Merge remote branch 'friendica/master'

This commit is contained in:
fabrixxm 2012-08-31 09:03:27 -04:00
commit 7b71fec622
222 changed files with 12201 additions and 16389 deletions

Binary file not shown.

View file

@ -157,9 +157,9 @@ function blockem_item_photo_menu(&$a,&$b) {
} }
} }
if($blocked) if($blocked)
$b['menu'][ t('Unblock Author')] = 'javascript:blockemUnblock("' . $author . '");'; $b['menu'][ t('Unblock Author')] = 'javascript:blockemUnblock(\'' . $author . '\');';
else else
$b['menu'][ t('Block Author')] = 'javascript:blockemBlock("' . $author . '");'; $b['menu'][ t('Block Author')] = 'javascript:blockemBlock(\'' . $author . '\');';
} }
function blockem_module() {} function blockem_module() {}

23
dav/Changelog.txt Normal file
View file

@ -0,0 +1,23 @@
v0.3
[REFACTOR] The new version of the VObject Library is used
[REFACTOR] The addressbook part has beed heavily refactored
[REFACTOR] Remove some Friendica-specific code out of the "common"-folder
v0.2.0
======
[FEATURE] Multiple private Calendars can be created. Each calendar can have its own default color; single events of a calendar can override this setting.
[FEATURE] Support for recurring events.
[FEATURE] ICS files can be imported to and exported from a calendar.
[FEATURE] Notification by e-mail is supported.
[COMPATIBILITY] When creating or updating an event using CalDAV, the etag is returned.
v0.1.1
======
[FEATURE] A "New Event" Button in the navigation bar of the calendar is added.
[FEATURE] When creating an event by dragging in the calendar, the "Edit Details"-Link leads to a page where the details can be added before actually creating the event.
[BUGFIX] When editing a event, the start time cannot be set befor the end time anymore.
[BUGFIX] Fixed some problems with Magic Quotes
v0.1.0
======
Initial Release

View file

@ -6,10 +6,15 @@ It's still in a very early stage, so expect major bugs. Please feel free to repo
At the moment, the calendar system supports the following features: At the moment, the calendar system supports the following features:
- A web-based drag&drop interface for managing events - A web-based drag&drop interface for managing events
- All-Day-Events, Multi-Day-Events, and time-based events - All-Day-Events, Multi-Day-Events, and time-based events
- Access to the events using CalDAV (using iPhone, Thunderbird Lightning etc., see below)
- read-only access to the friendica-native events (also using CalDAV)
- The friendica-contacts are made available using CardDAV (confirmed to work with iOS)
- Giving the subject, a description, a location and a color for the event (the color is not available through CalDAV, though) - Giving the subject, a description, a location and a color for the event (the color is not available through CalDAV, though)
- Recurrences (not the whole set of options given in the iCalendar spec, but the most important ones)
- Notification by e-mail. Multiple notifications can be set per event
- Multiple calendars per user
- Access to the events using CalDAV (using iPhone, Thunderbird Lightning etc., see below)
- Read-only access to the friendica-native events (also using CalDAV)
- The friendica-contacts are made available using CardDAV (confirmed to work with iOS)
- The events of a calendar can be exported as ICS file. ICS files can be imported into a calendar
Internationalization: Internationalization:
- At the moment, settings for the US and the german systems are selectable (regarding the date format and the first day of the week). More will be added on request. - At the moment, settings for the US and the german systems are selectable (regarding the date format and the first day of the week). More will be added on request.
@ -17,8 +22,10 @@ Internationalization:
CalDAV device compatibility: CalDAV device compatibility:
- iOS (iPhone/iPodTouch) works - iOS (iPhone/iPodTouch) works
- Thunderbird Lightning should work, not tested yet - Thunderbird Lightning works
- Android: http://dmfs.org/caldav/ seems to work, not much tested yet, though - Android:
- aCal (http://andrew.mcmillan.net.nz/projects/aCal) works, available in F-Droid and Google Play
- CalDAV-Sync (http://dmfs.org/caldav/) works, non-free
Installation Installation
After activating, serveral tables in the database have to be created. The admin-interface of the plugin will try to do this automatically. After activating, serveral tables in the database have to be created. The admin-interface of the plugin will try to do this automatically.
@ -26,12 +33,10 @@ In case of errors, the SQL-statement to create the tables manually are shown in
Functuality missing: (a.k.a. "Roadmap") Functuality missing: (a.k.a. "Roadmap")
- Recurrence of events (this is only supported using the CalDAV-interface; recurring events saved using CalDAV will appear correctly multiple times in the web-based frontend; hovever those events will be read-only at the web-based frondend) - Sharing events; all events are private at the moment, therefore this system is not a complete replacement for the friendica-native events
- Sharing events; all events are private at the moment, therefore this system is not yet a complete replacement for the friendica-native events
- Attendees / Collaboration - Attendees / Collaboration
Used libraries Used libraries
SabreDAV SabreDAV
@ -46,10 +51,6 @@ jQueryUI
http://jqueryui.com/ http://jqueryui.com/
Dual-licenced: MIT and GPL licenses Dual-licenced: MIT and GPL licenses
iCalCreator
http://kigkonsult.se/iCalcreator/
GNU Lesser General Public License
TimePicker TimePicker
http://www.texotela.co.uk/code/jquery/timepicker/ http://www.texotela.co.uk/code/jquery/timepicker/
Dual-licenced: MIT and GPL licenses Dual-licenced: MIT and GPL licenses

View file

@ -1,31 +1,67 @@
1.7.0-alpha (2012-??-??) 1.7.0-alpha (2012-??-??)
* BC Break: The calendarobjects database table has a bunch of new fields, * BC Break: The calendarobjects database table has a bunch of new
and a migration script is required to ensure everything will keep fields, and a migration script is required to ensure everything will
working. Read the wiki for more details. keep working. Read the wiki for more details.
* BC Break: The iCalendar interface now has a new method: calendarQuery. * BC Break: The iCalendar interface now has a new method: calendarQuery.
* BC Break: In this version a number of classes have been deleted, that * BC Break: In this version a number of classes have been deleted, that
have been previously deprecated. Namely: have been previously deprecated. Namely:
- Sabre_DAV_Directory (now: Sabre_DAV_Collection) - Sabre_DAV_Directory (now: Sabre_DAV_Collection)
- Sabre_DAV_SimpleDirectory (now: Sabre_DAV_SimpleCollection) - Sabre_DAV_SimpleDirectory (now: Sabre_DAV_SimpleCollection)
- Sabre_VObject_Element_DateTime (now: Sabre_VObject_Property_DateTime) - Sabre_VObject_Element_DateTime (now: .._Property_DateTime)
- Sabre_VObject_Element_MultiDateTime (now .._Property_MultiDateTime) - Sabre_VObject_Element_MultiDateTime (-> .._Property_MultiDateTime)
* BC Break: Sabre_CalDAV_Schedule_IMip::sendMessage now has an extra * BC Break: Sabre_CalDAV_Schedule_IMip::sendMessage now has an extra
argument. If you extended this class, you should fix this method. It's argument. If you extended this class, you should fix this method. It's
only used for informational purposes. only used for informational purposes.
* Changed: Responsibility for dealing with the calendar-query is now moved * BC Break: The DAV: namespace is no longer converted to urn:DAV. This was
from the CalDAV plugin to the CalDAV backends. This allows for heavy a workaround for a bug in older PHP versions (pre-5.3).
optimizations. * Changed: The Sabre_VObject library now spawned into it's own project!
* Changed: The CalDAV PDO backend is now a lot faster for common calendar * New feature: Support for caldav notifications!
queries. * Changed: Responsibility for dealing with the calendar-query is now
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8 encoded. moved from the CalDAV plugin to the CalDAV backends. This allows for
heavy optimizations.
* Changed: The CalDAV PDO backend is now a lot faster for common
calendar queries.
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8
encoded.
* Fixed: Workaround for the SOGO connector, as it doesn't understand * Fixed: Workaround for the SOGO connector, as it doesn't understand
receiving "text/x-vcard; charset=utf-8" for a contenttype. receiving "text/x-vcard; charset=utf-8" for a contenttype.
* Added: Sabre_DAV_Client now throws more specific exceptions in cases * Added: Sabre_DAV_Client now throws more specific exceptions in cases
where we already has an exception class. where we already has an exception class.
* Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the PATCH * Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the
method to update parts of a file. PATCH method to update parts of a file.
* Added: Tons of timezone name mappings for Microsoft Exchange.
* Added: Support for an 'exception' event.
* Fixed: Uploaded VCards without a UID are now rejected. (thanks Dominik!)
* Fixed: Rejecting calendar objects if they are not in the
supported-calendar-component list. (thanks Armin!)
* Fixed: Issue 219: serialize() now reorders correctly.
* Fixed: Sabre_DAV_XMLUtil no longer returns empty $dom->childNodes
if there is whitespace in $dom.
1.6.3-stable (2012-??-??) 1.6.5-stable (2012-??-??)
* Fixed: Workaround for line-ending bug OS X 10.8 addressbook has.
1.6.4-stable (2012-08-02)
* Fixed: Issue 220: Calendar-query filters may fail when filtering on
alarms, if an overridden event has it's alarm removed.
* Fixed: Compatibility for OS/X 10.8 iCal in the IMipHandler.
* Fixed: Issue 222: beforeWriteContent shouldn't be called for lock
requests.
* Fixed: Problem with POST requests to the outbox if mailto: was not lower
cased.
* Fixed: Yearly recurrence rule expansion on leap-days no behaves
correctly.
* Fixed: Correctly checking if recurring, all-day events with no dtstart
fall in a timerange if the start of the time-range exceeds the start of
the instance of an event, but not the end.
* Fixed: All-day recurring events wouldn't match if an occurence ended
exactly on the start of a time-range.
* Fixed: HTTP basic auth did not correctly deal with passwords containing
colons on some servers.
* Fixed: Issue 228: DTEND is now non-inclusive for all-day events in the
calendar-query REPORT and free-busy calculations.
1.6.3-stable (2012-06-12)
* Added: It's now possible to specify in Sabre_DAV_Client which type of * Added: It's now possible to specify in Sabre_DAV_Client which type of
authentication is to be used. authentication is to be used.
* Fixed: Issue 206: Sabre_DAV_Client PUT requests are fixed. * Fixed: Issue 206: Sabre_DAV_Client PUT requests are fixed.
@ -43,6 +79,7 @@
compatibility. compatibility.
* Fixed: Added a workaround for a bug in KDE 4.8.2 contact syncing. See * Fixed: Added a workaround for a bug in KDE 4.8.2 contact syncing. See
https://bugs.kde.org/show_bug.cgi?id=300047 https://bugs.kde.org/show_bug.cgi?id=300047
* Fixed: Issue 217: Sabre_DAV_Tree_FileSystem was pretty broken.
1.6.2-stable (2012-04-16) 1.6.2-stable (2012-04-16)
* Fixed: Sabre_VObject_Node::$parent should have been public. * Fixed: Sabre_VObject_Node::$parent should have been public.

42
dav/SabreDAV/README.md Normal file
View file

@ -0,0 +1,42 @@
# What is SabreDAV
SabreDAV allows you to easily add WebDAV support to a PHP application. SabreDAV is meant to cover the entire standard, and attempts to allow integration using an easy to understand API.
### Feature list:
* Fully WebDAV compliant
* Supports Windows XP, Windows Vista, Mac OS/X, DavFSv2, Cadaver, Netdrive, Open Office, and probably more.
* Passing all Litmus tests.
* Supporting class 1, 2 and 3 Webdav servers.
* Locking support.
* Custom property support.
* CalDAV (tested with [Evolution](http://code.google.com/p/sabredav/wiki/Evolution), [iCal](http://code.google.com/p/sabredav/wiki/ICal), [iPhone](http://code.google.com/p/sabredav/wiki/IPhone) and [Lightning](http://code.google.com/p/sabredav/wiki/Lightning)).
* CardDAV (tested with [OS/X addressbook](http://code.google.com/p/sabredav/wiki/OSXAddressbook), the [iOS addressbook](http://code.google.com/p/sabredav/wiki/iOSCardDAV) and [Evolution](http://code.google.com/p/sabredav/wiki/Evolution)).
* Over 97% unittest code coverage.
### Supported RFC's:
* [RFC2617](http://www.ietf.org/rfc/rfc2617.txt): Basic/Digest auth.
* [RFC2518](http://www.ietf.org/rfc/rfc2518.txt): First WebDAV spec.
* [RFC3744](http://www.ietf.org/rfc/rfc3744.txt): ACL (some features missing).
* [RFC4709](http://www.ietf.org/rfc/rfc4709.txt): [DavMount](http://code.google.com/p/sabredav/wiki/DavMount).
* [RFC4791](http://www.ietf.org/rfc/rfc4791.txt): CalDAV.
* [RFC4918](http://www.ietf.org/rfc/rfc4918.txt): WebDAV revision.
* [RFC5397](http://www.ietf.org/rfc/rfc5689.txt): current-user-principal.
* [RFC5689](http://www.ietf.org/rfc/rfc5689.txt): Extended MKCOL.
* [RFC5789](http://tools.ietf.org/html/rfc5789): PATCH method for HTTP.
* [RFC6352](http://www.ietf.org/rfc/rfc6352.txt): CardDAV
* [draft-daboo-carddav-directory-gateway](http://tools.ietf.org/html/draft-daboo-carddav-directory-gateway): CardDAV directory gateway
* CalDAV ctag, CalDAV-proxy.
## Live Demo
### Head over to:
* Url: [http://demo.sabredav.org/public/](http://demo.sabredav.org/public/)
* Username: testuser
* Password: test
**Please note:** Due to the webserver stack (nginx with varnish) some clients will not work correctly. At the very least this includes Finder and Cyberduck. Any client using chunked transfer encoding or expect *100-Continue* will fail.
The demo site is kindly hosted by sourceforge, so take it easy with the diskspace. It's limited!

View file

@ -95,7 +95,12 @@ foreach($fields17 as $field) {
if ($found === 0) { if ($found === 0) {
echo "The database had the 1.6 schema. Table will now be altered.\n"; echo "The database had the 1.6 schema. Table will now be altered.\n";
echo "This may take some time for large tables\n"; echo "This may take some time for large tables\n";
$pdo->exec(<<<SQL
switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql' :
$pdo->exec(<<<SQL
ALTER TABLE calendarobjects ALTER TABLE calendarobjects
ADD etag VARCHAR(32), ADD etag VARCHAR(32),
ADD size INT(11) UNSIGNED, ADD size INT(11) UNSIGNED,
@ -103,7 +108,20 @@ ADD componenttype VARCHAR(8),
ADD firstoccurence INT(11) UNSIGNED, ADD firstoccurence INT(11) UNSIGNED,
ADD lastoccurence INT(11) UNSIGNED ADD lastoccurence INT(11) UNSIGNED
SQL SQL
); );
break;
case 'sqlite' :
$pdo->exec('ALTER TABLE calendarobjects ADD etag text');
$pdo->exec('ALTER TABLE calendarobjects ADD size integer');
$pdo->exec('ALTER TABLE calendarobjects ADD componenttype TEXT');
$pdo->exec('ALTER TABLE calendarobjects ADD firstoccurence integer');
$pdo->exec('ALTER TABLE calendarobjects ADD lastoccurence integer');
break;
default :
die('This upgrade script does not support this driver (' . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . ")\n");
}
echo "Database schema upgraded.\n"; echo "Database schema upgraded.\n";
} elseif ($found === 5) { } elseif ($found === 5) {
@ -205,7 +223,7 @@ function getDenormalizedData($calendarData) {
} }
} else { } else {
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
$maxDate = new DateTime(self::MAX_DATE); $maxDate = new DateTime(Sabre_CalDAV_Backend_PDO::MAX_DATE);
if ($it->isInfinite()) { if ($it->isInfinite()) {
$lastOccurence = $maxDate->getTimeStamp(); $lastOccurence = $maxDate->getTimeStamp();
} else { } else {

View file

@ -1,21 +1,30 @@
{ {
"name": "evert/sabredav", "name": "sabre/dav",
"type": "library", "type": "library",
"description": "WebDAV Framework for PHP", "description": "WebDAV Framework for PHP",
"keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"], "keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"],
"homepage": "http://code.google.com/p/sabredav/", "homepage": "http://code.google.com/p/sabredav/",
"license": "New BSD License", "license" : "BSD-3-Clause",
"authors": [ "authors": [
{ {
"name": "Evert Pot", "name": "Evert Pot",
"email": "evert@rooftopsolutions.nl", "email": "evert@rooftopsolutions.nl",
"homepage" : "http://www.rooftopsolutions.nl/" "homepage" : "http://www.rooftopsolutions.nl/",
"role" : "Developer"
} }
], ],
"require": { "require": {
"php": ">=5.3.1" "php": ">=5.3.1",
"sabre/vobject" : "master-dev"
},
"provide" : {
"evert/sabredav" : "2.0.0"
}, },
"autoload": { "autoload": {
"psr-0": { "Sabre": "lib/" } "psr-0": { "Sabre": "lib/" }
},
"support" : {
"forum" : "https://groups.google.com/group/sabredav-discuss",
"source" : "https://github.com/evert/sabredav"
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,451 @@
Internet Engineering Task Force (IETF) M. Nottingham
Request for Comments: 5785 E. Hammer-Lahav
Updates: 2616, 2818 April 2010
Category: Standards Track
ISSN: 2070-1721
Defining Well-Known Uniform Resource Identifiers (URIs)
Abstract
This memo defines a path prefix for "well-known locations",
"/.well-known/", in selected Uniform Resource Identifier (URI)
schemes.
Status of This Memo
This is an Internet Standards Track document.
This document is a product of the Internet Engineering Task Force
(IETF). It represents the consensus of the IETF community. It has
received public review and has been approved for publication by the
Internet Engineering Steering Group (IESG). Further information on
Internet Standards is available in Section 2 of RFC 5741.
Information about the current status of this document, any errata,
and how to provide feedback on it may be obtained at
http://www.rfc-editor.org/info/rfc5785.
Copyright Notice
Copyright (c) 2010 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect
to this document. Code Components extracted from this document must
include Simplified BSD License text as described in Section 4.e of
the Trust Legal Provisions and are provided without warranty as
described in the Simplified BSD License.
Nottingham & Hammer-Lahav Standards Track [Page 1]
RFC 5785 Defining Well-Known URIs April 2010
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1. Appropriate Use of Well-Known URIs . . . . . . . . . . . . 3
2. Notational Conventions . . . . . . . . . . . . . . . . . . . . 3
3. Well-Known URIs . . . . . . . . . . . . . . . . . . . . . . . . 3
4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4
5. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 4
5.1. The Well-Known URI Registry . . . . . . . . . . . . . . . . 4
5.1.1. Registration Template . . . . . . . . . . . . . . . . . 5
6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 5
6.1. Normative References . . . . . . . . . . . . . . . . . . . 5
6.2. Informative References . . . . . . . . . . . . . . . . . . 5
Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 7
Appendix B. Frequently Asked Questions . . . . . . . . . . . . . . 7
1. Introduction
It is increasingly common for Web-based protocols to require the
discovery of policy or other information about a host ("site-wide
metadata") before making a request. For example, the Robots
Exclusion Protocol <http://www.robotstxt.org/> specifies a way for
automated processes to obtain permission to access resources;
likewise, the Platform for Privacy Preferences [W3C.REC-P3P-20020416]
tells user-agents how to discover privacy policy beforehand.
While there are several ways to access per-resource metadata (e.g.,
HTTP headers, WebDAV's PROPFIND [RFC4918]), the perceived overhead
(either in terms of client-perceived latency and/or deployment
difficulties) associated with them often precludes their use in these
scenarios.
When this happens, it is common to designate a "well-known location"
for such data, so that it can be easily located. However, this
approach has the drawback of risking collisions, both with other such
designated "well-known locations" and with pre-existing resources.
To address this, this memo defines a path prefix in HTTP(S) URIs for
these "well-known locations", "/.well-known/". Future specifications
that need to define a resource for such site-wide metadata can
register their use to avoid collisions and minimise impingement upon
sites' URI space.
Nottingham & Hammer-Lahav Standards Track [Page 2]
RFC 5785 Defining Well-Known URIs April 2010
1.1. Appropriate Use of Well-Known URIs
There are a number of possible ways that applications could use Well-
known URIs. However, in keeping with the Architecture of the World-
Wide Web [W3C.REC-webarch-20041215], well-known URIs are not intended
for general information retrieval or establishment of large URI
namespaces on the Web. Rather, they are designed to facilitate
discovery of information on a site when it isn't practical to use
other mechanisms; for example, when discovering policy that needs to
be evaluated before a resource is accessed, or when using multiple
round-trips is judged detrimental to performance.
As such, the well-known URI space was created with the expectation
that it will be used to make site-wide policy information and other
metadata available directly (if sufficiently concise), or provide
references to other URIs that provide such metadata.
2. Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [RFC2119].
3. Well-Known URIs
A well-known URI is a URI [RFC3986] whose path component begins with
the characters "/.well-known/", and whose scheme is "HTTP", "HTTPS",
or another scheme that has explicitly been specified to use well-
known URIs.
Applications that wish to mint new well-known URIs MUST register
them, following the procedures in Section 5.1.
For example, if an application registers the name 'example', the
corresponding well-known URI on 'http://www.example.com/' would be
'http://www.example.com/.well-known/example'.
Registered names MUST conform to the segment-nz production in
[RFC3986].
Note that this specification defines neither how to determine the
authority to use for a particular context, nor the scope of the
metadata discovered by dereferencing the well-known URI; both should
be defined by the application itself.
Typically, a registration will reference a specification that defines
the format and associated media type to be obtained by dereferencing
the well-known URI.
Nottingham & Hammer-Lahav Standards Track [Page 3]
RFC 5785 Defining Well-Known URIs April 2010
It MAY also contain additional information, such as the syntax of
additional path components, query strings and/or fragment identifiers
to be appended to the well-known URI, or protocol-specific details
(e.g., HTTP [RFC2616] method handling).
Note that this specification does not define a format or media-type
for the resource located at "/.well-known/" and clients should not
expect a resource to exist at that location.
4. Security Considerations
This memo does not specify the scope of applicability of metadata or
policy obtained from a well-known URI, and does not specify how to
discover a well-known URI for a particular application. Individual
applications using this mechanism must define both aspects.
Applications minting new well-known URIs, as well as administrators
deploying them, will need to consider several security-related
issues, including (but not limited to) exposure of sensitive data,
denial-of-service attacks (in addition to normal load issues), server
and client authentication, vulnerability to DNS rebinding attacks,
and attacks where limited access to a server grants the ability to
affect how well-known URIs are served.
5. IANA Considerations
5.1. The Well-Known URI Registry
This document establishes the well-known URI registry.
Well-known URIs are registered on the advice of one or more
Designated Experts (appointed by the IESG or their delegate), with a
Specification Required (using terminology from [RFC5226]). However,
to allow for the allocation of values prior to publication, the
Designated Expert(s) may approve registration once they are satisfied
that such a specification will be published.
Registration requests should be sent to the
wellknown-uri-review@ietf.org mailing list for review and comment,
with an appropriate subject (e.g., "Request for well-known URI:
example").
Before a period of 14 days has passed, the Designated Expert(s) will
either approve or deny the registration request, communicating this
decision both to the review list and to IANA. Denials should include
an explanation and, if applicable, suggestions as to how to make the
Nottingham & Hammer-Lahav Standards Track [Page 4]
RFC 5785 Defining Well-Known URIs April 2010
request successful. Registration requests that are undetermined for
a period longer than 21 days can be brought to the IESG's attention
(using the iesg@iesg.org mailing list) for resolution.
5.1.1. Registration Template
URI suffix: The name requested for the well-known URI, relative to
"/.well-known/"; e.g., "example".
Change controller: For Standards-Track RFCs, state "IETF". For
others, give the name of the responsible party. Other details
(e.g., postal address, e-mail address, home page URI) may also be
included.
Specification document(s): Reference to the document that specifies
the field, preferably including a URI that can be used to retrieve
a copy of the document. An indication of the relevant sections
may also be included, but is not required.
Related information: Optionally, citations to additional documents
containing further relevant information.
6. References
6.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, January 2005.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 5226,
May 2008.
6.2. Informative References
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter,
L., Leach, P., and T. Berners-Lee, "Hypertext Transfer
Protocol -- HTTP/1.1", RFC 2616, June 1999.
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
Nottingham & Hammer-Lahav Standards Track [Page 5]
RFC 5785 Defining Well-Known URIs April 2010
[W3C.REC-P3P-20020416]
Marchiori, M., "The Platform for Privacy Preferences 1.0
(P3P1.0) Specification", World Wide Web Consortium
Recommendation REC-P3P-20020416, April 2002,
<http://www.w3.org/TR/2002/ REC-P3P-20020416>.
[W3C.REC-webarch-20041215]
Jacobs, I. and N. Walsh, "Architecture of the World Wide
Web, Volume One", World Wide Web Consortium
Recommendation REC- webarch-20041215, December 2004,
<http:// www.w3.org/TR/2004/REC-webarch-20041215>.
Nottingham & Hammer-Lahav Standards Track [Page 6]
RFC 5785 Defining Well-Known URIs April 2010
Appendix A. Acknowledgements
We would like to acknowledge the contributions of everyone who
provided feedback and use cases for this document; in particular,
Phil Archer, Dirk Balfanz, Adam Barth, Tim Bray, Brian Eaton, Brad
Fitzpatrick, Joe Gregorio, Paul Hoffman, Barry Leiba, Ashok Malhotra,
Breno de Medeiros, John Panzer, and Drummond Reed. However, they are
not responsible for errors and omissions.
Appendix B. Frequently Asked Questions
1. Aren't well-known locations bad for the Web?
They are, but for various reasons -- both technical and social --
they are commonly used and their use is increasing. This memo
defines a "sandbox" for them, to reduce the risks of collision and
to minimise the impact upon pre-existing URIs on sites.
2. Why /.well-known?
It's short, descriptive, and according to search indices, not
widely used.
3. What impact does this have on existing mechanisms, such as P3P and
robots.txt?
None, until they choose to use this mechanism.
4. Why aren't per-directory well-known locations defined?
Allowing every URI path segment to have a well-known location
(e.g., "/images/.well-known/") would increase the risks of
colliding with a pre-existing URI on a site, and generally these
solutions are found not to scale well, because they're too
"chatty".
Nottingham & Hammer-Lahav Standards Track [Page 7]
RFC 5785 Defining Well-Known URIs April 2010
Authors' Addresses
Mark Nottingham
EMail: mnot@mnot.net
URI: http://www.mnot.net/
Eran Hammer-Lahav
EMail: eran@hueniverse.com
URI: http://hueniverse.com/
Nottingham & Hammer-Lahav Standards Track [Page 8]

View file

@ -6,5 +6,8 @@ CREATE TABLE locks (
token VARCHAR(100), token VARCHAR(100),
scope TINYINT, scope TINYINT,
depth TINYINT, depth TINYINT,
uri text uri VARCHAR(1000),
INDEX(token),
INDEX(uri)
); );

View file

@ -7,27 +7,27 @@
# settings as well. # settings as well.
<VirtualHost *:*> <VirtualHost *:*>
# Don't forget to change the server name # Don't forget to change the server name
# ServerName dav.example.org # ServerName dav.example.org
# The DocumentRoot is also required # The DocumentRoot is also required
# DocumentRoot /home/sabredav/ # DocumentRoot /home/sabredav/
RewriteEngine On RewriteEngine On
# This makes every request go to server.php # This makes every request go to server.php
RewriteRule ^/(.*)$ /server.php [L] RewriteRule ^/(.*)$ /server.php [L]
# Output buffering needs to be off, to prevent high memory usage # Output buffering needs to be off, to prevent high memory usage
php_flag output_buffering off php_flag output_buffering off
# This is also to prevent high memory usage # This is also to prevent high memory usage
php_flag always_populate_raw_post_data off php_flag always_populate_raw_post_data off
# This is almost a given, but magic quotes is *still* on on some # This is almost a given, but magic quotes is *still* on on some
# linux distributions # linux distributions
php_flag magic_quotes_gpc off php_flag magic_quotes_gpc off
# SabreDAV is not compatible with mbstring function overloading # SabreDAV is not compatible with mbstring function overloading
php_flag mbstring.func_overload off php_flag mbstring.func_overload off
</VirtualHost *:*> </VirtualHost *:*>

View file

@ -6,16 +6,16 @@
# This configuration assumes CGI or FastCGI is used. # This configuration assumes CGI or FastCGI is used.
<VirtualHost *:*> <VirtualHost *:*>
# Don't forget to change the server name # Don't forget to change the server name
# ServerName dav.example.org # ServerName dav.example.org
# The DocumentRoot is also required # The DocumentRoot is also required
# DocumentRoot /home/sabredav/ # DocumentRoot /home/sabredav/
# This makes every request go to server.php. This also makes sure # This makes every request go to server.php. This also makes sure
# the Authentication information is available. If your server script is # the Authentication information is available. If your server script is
# not called server.php, be sure to change it. # not called server.php, be sure to change it.
RewriteEngine On RewriteEngine On
RewriteRule ^/(.*)$ /server.php [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteRule ^/(.*)$ /server.php [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</VirtualHost *:*> </VirtualHost *:*>

View file

@ -1,47 +1,19 @@
<?php <?php
use Sabre\VObject;
/** /**
* Abstract Calendaring backend. Extend this class to create your own backends. * Abstract Calendaring backend. Extend this class to create your own backends.
* *
* Checkout the BackendInterface for all the methods that must be implemented.
*
* @package Sabre * @package Sabre
* @subpackage CalDAV * @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/) * @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/ */
abstract class Sabre_CalDAV_Backend_Abstract { abstract class Sabre_CalDAV_Backend_Abstract implements Sabre_CalDAV_Backend_BackendInterface {
/**
* Returns a list of calendars for a principal.
*
* Every project is an array with the following keys:
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
* * uri, which the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
*
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
* @param string $principalUri
* @return array
*/
abstract function getCalendarsForUser($principalUri);
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @return void
*/
abstract function createCalendar($principalUri,$calendarUri,array $properties);
/** /**
* Updates properties for a calendar. * Updates properties for a calendar.
@ -85,102 +57,6 @@ abstract class Sabre_CalDAV_Backend_Abstract {
} }
/**
* Delete a calendar and all it's objects
*
* @param mixed $calendarId
* @return void
*/
abstract function deleteCalendar($calendarId);
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
* * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
* ' "abcdef"')
* * calendarid - The calendarid as it was passed to this function.
* * size - The size of the calendar objects, in bytes.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
*
* The calendardata is also optional. If it's not returned
* 'getCalendarObject' will be called later, which *is* expected to return
* calendardata.
*
* If neither etag or size are specified, the calendardata will be
* used/fetched to determine these numbers. If both are specified the
* amount of times this is needed is reduced by a great degree.
*
* @param mixed $calendarId
* @return array
*/
abstract function getCalendarObjects($calendarId);
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param mixed $calendarId
* @param string $objectUri
* @return array
*/
abstract function getCalendarObject($calendarId,$objectUri);
/**
* Creates a new calendar object.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
/**
* Updates an existing calendarobject, based on it's uri.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
/**
* Deletes an existing calendar object.
*
* @param mixed $calendarId
* @param string $objectUri
* @return void
*/
abstract function deleteCalendarObject($calendarId,$objectUri);
/** /**
* Performs a calendar-query on the contents of this calendar. * Performs a calendar-query on the contents of this calendar.
* *
@ -266,7 +142,7 @@ abstract class Sabre_CalDAV_Backend_Abstract {
$object = $this->getCalendarObject($object['calendarid'], $object['uri']); $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
} }
$vObject = Sabre_VObject_Reader::read($object['calendardata']); $vObject = VObject\Reader::read($object['calendardata']);
$validator = new Sabre_CalDAV_CalendarQueryValidator(); $validator = new Sabre_CalDAV_CalendarQueryValidator();
return $validator->validate($vObject, $filters); return $validator->validate($vObject, $filters);

View file

@ -0,0 +1,231 @@
<?php
/**
* Every CalDAV backend must at least implement this interface.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_CalDAV_Backend_BackendInterface {
/**
* Returns a list of calendars for a principal.
*
* Every project is an array with the following keys:
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
* * uri, which the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
*
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
* @param string $principalUri
* @return array
*/
public function getCalendarsForUser($principalUri);
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @return void
*/
public function createCalendar($principalUri,$calendarUri,array $properties);
/**
* Updates properties for a calendar.
*
* The mutations array uses the propertyName in clark-notation as key,
* and the array value for the property value. In the case a property
* should be deleted, the property value will be null.
*
* This method must be atomic. If one property cannot be changed, the
* entire operation must fail.
*
* If the operation was successful, true can be returned.
* If the operation failed, false can be returned.
*
* Deletion of a non-existent property is always successful.
*
* Lastly, it is optional to return detailed information about any
* failures. In this case an array should be returned with the following
* structure:
*
* array(
* 403 => array(
* '{DAV:}displayname' => null,
* ),
* 424 => array(
* '{DAV:}owner' => null,
* )
* )
*
* In this example it was forbidden to update {DAV:}displayname.
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
* (424 Failed Dependency) because the request needs to be atomic.
*
* @param mixed $calendarId
* @param array $mutations
* @return bool|array
*/
public function updateCalendar($calendarId, array $mutations);
/**
* Delete a calendar and all it's objects
*
* @param mixed $calendarId
* @return void
*/
public function deleteCalendar($calendarId);
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
* * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
* ' "abcdef"')
* * calendarid - The calendarid as it was passed to this function.
* * size - The size of the calendar objects, in bytes.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
*
* The calendardata is also optional. If it's not returned
* 'getCalendarObject' will be called later, which *is* expected to return
* calendardata.
*
* If neither etag or size are specified, the calendardata will be
* used/fetched to determine these numbers. If both are specified the
* amount of times this is needed is reduced by a great degree.
*
* @param mixed $calendarId
* @return array
*/
public function getCalendarObjects($calendarId);
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param mixed $calendarId
* @param string $objectUri
* @return array
*/
public function getCalendarObject($calendarId,$objectUri);
/**
* Creates a new calendar object.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
public function createCalendarObject($calendarId,$objectUri,$calendarData);
/**
* Updates an existing calendarobject, based on it's uri.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
public function updateCalendarObject($calendarId,$objectUri,$calendarData);
/**
* Deletes an existing calendar object.
*
* @param mixed $calendarId
* @param string $objectUri
* @return void
*/
public function deleteCalendarObject($calendarId,$objectUri);
/**
* Performs a calendar-query on the contents of this calendar.
*
* The calendar-query is defined in RFC4791 : CalDAV. Using the
* calendar-query it is possible for a client to request a specific set of
* object, based on contents of iCalendar properties, date-ranges and
* iCalendar component types (VTODO, VEVENT).
*
* This method should just return a list of (relative) urls that match this
* query.
*
* The list of filters are specified as an array. The exact array is
* documented by Sabre_CalDAV_CalendarQueryParser.
*
* Note that it is extremely likely that getCalendarObject for every path
* returned from this method will be called almost immediately after. You
* may want to anticipate this to speed up these requests.
*
* This method provides a default implementation, which parses *all* the
* iCalendar objects in the specified calendar.
*
* This default may well be good enough for personal use, and calendars
* that aren't very large. But if you anticipate high usage, big calendars
* or high loads, you are strongly adviced to optimize certain paths.
*
* The best way to do so is override this method and to optimize
* specifically for 'common filters'.
*
* Requests that are extremely common are:
* * requests for just VEVENTS
* * requests for just VTODO
* * requests with a time-range-filter on either VEVENT or VTODO.
*
* ..and combinations of these requests. It may not be worth it to try to
* handle every possible situation and just rely on the (relatively
* easy to use) CalendarQueryValidator to handle the rest.
*
* Note that especially time-range-filters may be difficult to parse. A
* time-range filter specified on a VEVENT must for instance also handle
* recurrence rules correctly.
* A good example of how to interprete all these filters can also simply
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
* as possible, so it gives you a good idea on what type of stuff you need
* to think of.
*
* @param mixed $calendarId
* @param array $filters
* @return array
*/
public function calendarQuery($calendarId, array $filters);
}

View file

@ -0,0 +1,44 @@
<?php
/**
* Adds caldav notification support to a backend.
*
* Notifications are defined at:
* http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-notifications.txt
*
* These notifications are basically a list of server-generated notifications
* displayed to the user. Users can dismiss notifications by deleting them.
*
* The primary usecase is to allow for calendar-sharing.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_CalDAV_Backend_NotificationSupport extends Sabre_CalDAV_Backend_BackendInterface {
/**
* Returns a list of notifications for a given principal url.
*
* The returned array should only consist of implementations of
* Sabre_CalDAV_Notifications_INotificationType.
*
* @param string $principalUri
* @return array
*/
public function getNotificationsForPrincipal($principalUri);
/**
* This deletes a specific notifcation.
*
* This may be called by a client once it deems a notification handled.
*
* @param string $principalUri
* @param Sabre_CalDAV_Notifications_INotificationType $notification
* @return void
*/
public function deleteNotification($principalUri, Sabre_CalDAV_Notifications_INotificationType $notification);
}

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* PDO CalDAV backend * PDO CalDAV backend
* *
@ -16,8 +18,13 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
/** /**
* We need to specify a max date, because we need to stop *somewhere* * We need to specify a max date, because we need to stop *somewhere*
*
* On 32 bit system the maximum for a signed integer is 2147483647, so
* MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
* in 2038-01-19 to avoid problems when the date is converted
* to a unix timestamp.
*/ */
const MAX_DATE = '2040-01-01'; const MAX_DATE = '2038-01-01';
/** /**
* pdo * pdo
@ -457,7 +464,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
*/ */
protected function getDenormalizedData($calendarData) { protected function getDenormalizedData($calendarData) {
$vObject = Sabre_VObject_Reader::read($calendarData); $vObject = VObject\Reader::read($calendarData);
$componentType = null; $componentType = null;
$component = null; $component = null;
$firstOccurence = null; $firstOccurence = null;
@ -479,9 +486,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
} elseif (isset($component->DURATION)) { } elseif (isset($component->DURATION)) {
$endDate = clone $component->DTSTART->getDateTime(); $endDate = clone $component->DTSTART->getDateTime();
$endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value)); $endDate->add(VObject\DateTimeParser::parse($component->DURATION->value));
$lastOccurence = $endDate->getTimeStamp(); $lastOccurence = $endDate->getTimeStamp();
} elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { } elseif ($component->DTSTART->getDateType()===VObject\Property\DateTime::DATE) {
$endDate = clone $component->DTSTART->getDateTime(); $endDate = clone $component->DTSTART->getDateTime();
$endDate->modify('+1 day'); $endDate->modify('+1 day');
$lastOccurence = $endDate->getTimeStamp(); $lastOccurence = $endDate->getTimeStamp();
@ -489,7 +496,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
$lastOccurence = $firstOccurence; $lastOccurence = $firstOccurence;
} }
} else { } else {
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); $it = new VObject\RecurrenceIterator($vObject, (string)$component->UID);
$maxDate = new DateTime(self::MAX_DATE); $maxDate = new DateTime(self::MAX_DATE);
if ($it->isInfinite()) { if ($it->isInfinite()) {
$lastOccurence = $maxDate->getTimeStamp(); $lastOccurence = $maxDate->getTimeStamp();

View file

@ -24,7 +24,7 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
/** /**
* CalDAV backend * CalDAV backend
* *
* @var Sabre_CalDAV_Backend_Abstract * @var Sabre_CalDAV_Backend_BackendInterface
*/ */
protected $caldavBackend; protected $caldavBackend;
@ -39,10 +39,10 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
* Constructor * Constructor
* *
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @param array $calendarInfo * @param array $calendarInfo
*/ */
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) { public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $calendarInfo) {
$this->caldavBackend = $caldavBackend; $this->caldavBackend = $caldavBackend;
$this->principalBackend = $principalBackend; $this->principalBackend = $principalBackend;

View file

@ -12,7 +12,7 @@
class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL { class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
/** /**
* Sabre_CalDAV_Backend_Abstract * Sabre_CalDAV_Backend_BackendInterface
* *
* @var array * @var array
*/ */
@ -35,11 +35,11 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
/** /**
* Constructor * Constructor
* *
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @param array $calendarInfo * @param array $calendarInfo
* @param array $objectData * @param array $objectData
*/ */
public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) { public function __construct(Sabre_CalDAV_Backend_BackendInterface $caldavBackend,array $calendarInfo,array $objectData) {
$this->caldavBackend = $caldavBackend; $this->caldavBackend = $caldavBackend;

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* Parses the calendar-query report request body. * Parses the calendar-query report request body.
* *
@ -68,7 +70,7 @@ class Sabre_CalDAV_CalendarQueryParser {
$this->xpath = new DOMXPath($dom); $this->xpath = new DOMXPath($dom);
$this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
$this->xpath->registerNameSpace('dav','urn:DAV'); $this->xpath->registerNameSpace('dav','DAV:');
} }
@ -241,12 +243,12 @@ class Sabre_CalDAV_CalendarQueryParser {
$timeRangeNode = $timeRangeNodes->item(0); $timeRangeNode = $timeRangeNodes->item(0);
if ($start = $timeRangeNode->getAttribute('start')) { if ($start = $timeRangeNode->getAttribute('start')) {
$start = Sabre_VObject_DateTimeParser::parseDateTime($start); $start = VObject\DateTimeParser::parseDateTime($start);
} else { } else {
$start = null; $start = null;
} }
if ($end = $timeRangeNode->getAttribute('end')) { if ($end = $timeRangeNode->getAttribute('end')) {
$end = Sabre_VObject_DateTimeParser::parseDateTime($end); $end = VObject\DateTimeParser::parseDateTime($end);
} else { } else {
$end = null; $end = null;
} }
@ -274,13 +276,13 @@ class Sabre_CalDAV_CalendarQueryParser {
if(!$start) { if(!$start) {
throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
} }
$start = Sabre_VObject_DateTimeParser::parseDateTime($start); $start = VObject\DateTimeParser::parseDateTime($start);
$end = $parentNode->getAttribute('end'); $end = $parentNode->getAttribute('end');
if(!$end) { if(!$end) {
throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
} }
$end = Sabre_VObject_DateTimeParser::parseDateTime($end); $end = VObject\DateTimeParser::parseDateTime($end);
if ($end <= $start) { if ($end <= $start) {
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* CalendarQuery Validator * CalendarQuery Validator
* *
@ -22,11 +24,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
* *
* The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser * The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser
* *
* @param Sabre_VObject_Component $vObject * @param VObject\Component $vObject
* @param array $filters * @param array $filters
* @return bool * @return bool
*/ */
public function validate(Sabre_VObject_Component $vObject,array $filters) { public function validate(VObject\Component $vObject,array $filters) {
// The top level object is always a component filter. // The top level object is always a component filter.
// We'll parse it manually, as it's pretty simple. // We'll parse it manually, as it's pretty simple.
@ -48,11 +50,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
* component we're checking should be specified, not the component to check * component we're checking should be specified, not the component to check
* itself. * itself.
* *
* @param Sabre_VObject_Component $parent * @param VObject\Component $parent
* @param array $filters * @param array $filters
* @return bool * @return bool
*/ */
protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) { protected function validateCompFilters(VObject\Component $parent, array $filters) {
foreach($filters as $filter) { foreach($filters as $filter) {
@ -117,11 +119,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
* property we're checking should be specified, not the property to check * property we're checking should be specified, not the property to check
* itself. * itself.
* *
* @param Sabre_VObject_Component $parent * @param VObject\Component $parent
* @param array $filters * @param array $filters
* @return bool * @return bool
*/ */
protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) { protected function validatePropFilters(VObject\Component $parent, array $filters) {
foreach($filters as $filter) { foreach($filters as $filter) {
@ -187,11 +189,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
* parameter we're checking should be specified, not the parameter to check * parameter we're checking should be specified, not the parameter to check
* itself. * itself.
* *
* @param Sabre_VObject_Property $parent * @param VObject\Property $parent
* @param array $filters * @param array $filters
* @return bool * @return bool
*/ */
protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) { protected function validateParamFilters(VObject\Property $parent, array $filters) {
foreach($filters as $filter) { foreach($filters as $filter) {
@ -243,11 +245,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
* A single text-match should be specified as well as the specific property * A single text-match should be specified as well as the specific property
* or parameter we need to validate. * or parameter we need to validate.
* *
* @param Sabre_VObject_Node $parent * @param VObject\Node $parent
* @param array $textMatch * @param array $textMatch
* @return bool * @return bool
*/ */
protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { protected function validateTextMatch(VObject\Node $parent, array $textMatch) {
$value = (string)$parent; $value = (string)$parent;
@ -263,12 +265,12 @@ class Sabre_CalDAV_CalendarQueryValidator {
* This is all based on the rules specified in rfc4791, which are quite * This is all based on the rules specified in rfc4791, which are quite
* complex. * complex.
* *
* @param Sabre_VObject_Node $component * @param VObject\Node $component
* @param DateTime $start * @param DateTime $start
* @param DateTime $end * @param DateTime $end
* @return bool * @return bool
*/ */
protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { protected function validateTimeRange(VObject\Node $component, $start, $end) {
if (is_null($start)) { if (is_null($start)) {
$start = new DateTime('1900-01-01'); $start = new DateTime('1900-01-01');
@ -296,7 +298,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
// Fire up the iterator! // Fire up the iterator!
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); $it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
while($it->valid()) { while($it->valid()) {
$expandedEvent = $it->getEventObject(); $expandedEvent = $it->getEventObject();
@ -304,28 +306,29 @@ class Sabre_CalDAV_CalendarQueryValidator {
// one is the first to trigger. Based on this, we can // one is the first to trigger. Based on this, we can
// determine if we can 'give up' expanding events. // determine if we can 'give up' expanding events.
$firstAlarm = null; $firstAlarm = null;
foreach($expandedEvent->VALARM as $expandedAlarm) { if ($expandedEvent->VALARM !== null) {
foreach($expandedEvent->VALARM as $expandedAlarm) {
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
if ($expandedAlarm->isInTimeRange($start, $end)) { if ($expandedAlarm->isInTimeRange($start, $end)) {
return true; return true;
} }
if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
// This is an alarm with a non-relative trigger // This is an alarm with a non-relative trigger
// time, likely created by a buggy client. The // time, likely created by a buggy client. The
// implication is that every alarm in this // implication is that every alarm in this
// recurring event trigger at the exact same // recurring event trigger at the exact same
// time. It doesn't make sense to traverse // time. It doesn't make sense to traverse
// further. // further.
} else { } else {
// We store the first alarm as a means to // We store the first alarm as a means to
// figure out when we can stop traversing. // figure out when we can stop traversing.
if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
$firstAlarm = $effectiveTrigger; $firstAlarm = $effectiveTrigger;
}
} }
} }
} }
if (is_null($firstAlarm)) { if (is_null($firstAlarm)) {
// No alarm was found. // No alarm was found.

View file

@ -17,7 +17,7 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
/** /**
* CalDAV backend * CalDAV backend
* *
* @var Sabre_CalDAV_Backend_Abstract * @var Sabre_CalDAV_Backend_BackendInterface
*/ */
protected $caldavBackend; protected $caldavBackend;
@ -33,10 +33,10 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
* *
* *
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @param string $principalPrefix * @param string $principalPrefix
*/ */
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') { public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $principalPrefix = 'principals') {
parent::__construct($principalBackend, $principalPrefix); parent::__construct($principalBackend, $principalPrefix);
$this->caldavBackend = $caldavBackend; $this->caldavBackend = $caldavBackend;

View file

@ -0,0 +1,32 @@
<?php
/**
* Sabre_CalDAV_Exception_InvalidComponentType
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Exception_InvalidComponentType extends Sabre_DAV_Exception_Forbidden {
/**
* Adds in extra information in the xml response.
*
* This method adds the {CALDAV:}supported-calendar-component as defined in rfc4791
*
* @param Sabre_DAV_Server $server
* @param DOMElement $errorNode
* @return void
*/
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
$np = $doc->createElementNS(Sabre_CalDAV_Plugin::NS_CALDAV,'cal:supported-calendar-component');
$errorNode->appendChild($np);
}
}

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* ICS Exporter * ICS Exporter
* *
@ -82,7 +84,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
*/ */
public function generateICS(array $nodes) { public function generateICS(array $nodes) {
$calendar = new Sabre_VObject_Component('vcalendar'); $calendar = new VObject\Component('vcalendar');
$calendar->version = '2.0'; $calendar->version = '2.0';
if (Sabre_DAV_Server::$exposeVersion) { if (Sabre_DAV_Server::$exposeVersion) {
$calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
@ -103,7 +105,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
} }
$nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'];
$nodeComp = Sabre_VObject_Reader::read($nodeData); $nodeComp = VObject\Reader::read($nodeData);
foreach($nodeComp->children() as $child) { foreach($nodeComp->children() as $child) {

View file

@ -0,0 +1,80 @@
<?php
/**
* This node represents a list of notifications.
*
* It provides no additional functionality, but you must implement this
* interface to allow the Notifications plugin to mark the collection
* as a notifications collection.
*
* This collection should only return Sabre_CalDAV_Notifications_INode nodes as
* its children.
*
* @package Sabre
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Notifications_Collection extends Sabre_DAV_Collection implements Sabre_CalDAV_Notifications_ICollection {
/**
* The notification backend
*
* @var Sabre_CalDAV_Backend_NotificationSupport
*/
protected $caldavBackend;
/**
* Principal uri
*
* @var string
*/
protected $principalUri;
/**
* Constructor
*
* @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend
* @param string $principalUri
*/
public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, $principalUri) {
$this->caldavBackend = $caldavBackend;
$this->principalUri = $principalUri;
}
/**
* Returns all notifications for a principal
*
* @return array
*/
public function getChildren() {
$children = array();
$notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri);
foreach($notifications as $notification) {
$children[] = new Sabre_CalDAV_Notifications_Node(
$this->caldavBackend,
$notification
);
}
return $children;
}
/**
* Returns the name of this object
*
* @return string
*/
public function getName() {
return 'notifications';
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* This node represents a list of notifications.
*
* It provides no additional functionality, but you must implement this
* interface to allow the Notifications plugin to mark the collection
* as a notifications collection.
*
* This collection should only return Sabre_CalDAV_Notifications_INode nodes as
* its children.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_CalDAV_Notifications_ICollection extends Sabre_DAV_ICollection {
}

View file

@ -0,0 +1,29 @@
<?php
/**
* This node represents a single notification.
*
* The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method
* MUST return an xml document that matches the requirements of the
* 'caldav-notifications.txt' spec.
*
* For a complete example, check out the Notification class, which contains
* some helper functions.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_CalDAV_Notifications_INode {
/**
* This method must return an xml element, using the
* Sabre_CalDAV_Notifications_INotificationType classes.
*
* @return Sabre_DAVNotification_INotificationType
*/
function getNotificationType();
}

View file

@ -0,0 +1,46 @@
<?php
/**
* This interface reflects a single notification type.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_CalDAV_Notifications_INotificationType extends Sabre_DAV_PropertyInterface {
/**
* Serializes the notification as a single property.
*
* You should usually just encode the single top-level element of the
* notification.
*
* @param Sabre_DAV_Server $server
* @param DOMElement $node
* @return void
*/
function serialize(Sabre_DAV_Server $server, \DOMElement $node);
/**
* This method serializes the entire notification, as it is used in the
* response body.
*
* @param Sabre_DAV_Server $server
* @param DOMElement $node
* @return void
*/
function serializeBody(Sabre_DAV_Server $server, \DOMElement $node);
/**
* Returns a unique id for this notification
*
* This is just the base url. This should generally be some kind of unique
* id.
*
* @return string
*/
function getId();
}

View file

@ -0,0 +1,68 @@
<?php
/**
* This node represents a single notification.
*
* The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method
* MUST return an xml document that matches the requirements of the
* 'caldav-notifications.txt' spec.
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Notifications_Node extends Sabre_DAV_Node implements Sabre_CalDAV_Notifications_INode {
/**
* The notification backend
*
* @var Sabre_CalDAV_Backend_NotificationSupport
*/
protected $caldavBackend;
/**
* The actual notification
*
* @var Sabre_CalDAV_Notifications_INotificationType
*/
protected $notification;
/**
* Constructor
*
* @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend
* @param Sabre_CalDAV_Notifications_INotificationType $notification
*/
public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, Sabre_CalDAV_Notifications_INotificationType $notification) {
$this->caldavBackend = $caldavBackend;
$this->notification = $notification;
}
/**
* Returns the path name for this notification
*
* @return id
*/
public function getName() {
return $this->notification->getId();
}
/**
* This method must return an xml element, using the
* Sabre_CalDAV_Notifications_INotificationType classes.
*
* @return Sabre_DAVNotification_INotificationType
*/
public function getNotificationType() {
return $this->notification;
}
}

View file

@ -0,0 +1,158 @@
<?php
/**
* SystemStatus notification
*
* This notification can be used to indicate to the user that the system is
* down.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Notifications_Notification_SystemStatus extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType {
const TYPE_LOW = 1;
const TYPE_MEDIUM = 2;
const TYPE_HIGH = 3;
/**
* A unique id
*
* @var string
*/
protected $id;
/**
* The type of alert. This should be one of the TYPE_ constants.
*
* @var int
*/
protected $type;
/**
* A human-readable description of the problem.
*
* @var string
*/
protected $description;
/**
* A url to a website with more information for the user.
*
* @var string
*/
protected $href;
/**
* Creates the notification.
*
* Some kind of unique id should be provided. This is used to generate a
* url.
*
* @param string $id
* @param int $type
* @param string $description
* @param string $href
*/
public function __construct($id, $type = self::TYPE_HIGH, $description = null, $href = null) {
$this->id = $id;
$this->type = $type;
$this->description = $description;
$this->href = $href;
}
/**
* Serializes the notification as a single property.
*
* You should usually just encode the single top-level element of the
* notification.
*
* @param Sabre_DAV_Server $server
* @param DOMElement $node
* @return void
*/
public function serialize(Sabre_DAV_Server $server, \DOMElement $node) {
switch($this->type) {
case self::TYPE_LOW :
$type = 'low';
break;
case self::TYPE_MEDIUM :
$type = 'medium';
break;
default :
case self::TYPE_HIGH :
$type = 'high';
break;
}
$prop = $node->ownerDocument->createElement('cs:systemstatus');
$prop->setAttribute('type', $type);
$node->appendChild($prop);
}
/**
* This method serializes the entire notification, as it is used in the
* response body.
*
* @param Sabre_DAV_Server $server
* @param DOMElement $node
* @return void
*/
public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) {
switch($this->type) {
case self::TYPE_LOW :
$type = 'low';
break;
case self::TYPE_MEDIUM :
$type = 'medium';
break;
default :
case self::TYPE_HIGH :
$type = 'high';
break;
}
$prop = $node->ownerDocument->createElement('cs:systemstatus');
$prop->setAttribute('type', $type);
if ($this->description) {
$text = $node->ownerDocument->createTextNode($this->description);
$desc = $node->ownerDocument->createElement('cs:description');
$desc->appendChild($text);
$prop->appendChild($desc);
}
if ($this->href) {
$text = $node->ownerDocument->createTextNode($this->href);
$href = $node->ownerDocument->createElement('d:href');
$href->appendChild($text);
$prop->appendChild($href);
}
$node->appendChild($prop);
}
/**
* Returns a unique id for this notification
*
* This is just the base url. This should generally be some kind of unique
* id.
*
* @return string
*/
public function getId() {
return $this->id;
}
}

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* CalDAV plugin * CalDAV plugin
* *
@ -162,6 +164,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
$server->subscribeEvent('beforeMethod', array($this,'beforeMethod'));
$server->xmlNamespaces[self::NS_CALDAV] = 'cal'; $server->xmlNamespaces[self::NS_CALDAV] = 'cal';
$server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
@ -172,6 +175,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
$server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; $server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
$server->resourceTypeMapping['Sabre_CalDAV_Notifications_ICollection'] = '{' . self::NS_CALENDARSERVER . '}notifications';
$server->resourceTypeMapping['Sabre_CalDAV_Notifications_INode'] = '{' . self::NS_CALENDARSERVER . '}notification';
array_push($server->protectedProperties, array_push($server->protectedProperties,
@ -195,7 +200,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
// CalendarServer extensions // CalendarServer extensions
'{' . self::NS_CALENDARSERVER . '}getctag', '{' . self::NS_CALENDARSERVER . '}getctag',
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for',
'{' . self::NS_CALENDARSERVER . '}notification-URL',
'{' . self::NS_CALENDARSERVER . '}notificationtype'
); );
} }
@ -380,8 +387,31 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
} }
// notification-URL property
$notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL';
if (($index = array_search($notificationUrl, $requestedProperties)) !== false) {
$principalId = $node->getName();
$calendarHomePath = 'calendars/' . $principalId . '/notifications/';
unset($requestedProperties[$index]);
$returnedProperties[200][$notificationUrl] = new Sabre_DAV_Property_Href($calendarHomePath);
}
} // instanceof IPrincipal } // instanceof IPrincipal
if ($node instanceof Sabre_CalDAV_Notifications_INode) {
$propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype';
if (($index = array_search($propertyName, $requestedProperties)) !== false) {
$returnedProperties[200][$propertyName] =
$node->getNotificationType();
unset($requestedProperties[$index]);
}
} // instanceof Notifications_INode
if ($node instanceof Sabre_CalDAV_ICalendarObject) { if ($node instanceof Sabre_CalDAV_ICalendarObject) {
// The calendar-data property is not supposed to be a 'real' // The calendar-data property is not supposed to be a 'real'
@ -414,11 +444,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
public function calendarMultiGetReport($dom) { public function calendarMultiGetReport($dom) {
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); $hrefElems = $dom->getElementsByTagNameNS('DAV:','href');
$xpath = new DOMXPath($dom); $xpath = new DOMXPath($dom);
$xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
$xpath->registerNameSpace('dav','urn:DAV'); $xpath->registerNameSpace('dav','DAV:');
$expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
if ($expand->length>0) { if ($expand->length>0) {
@ -428,8 +458,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if(!$start || !$end) { if(!$start || !$end) {
throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
} }
$start = Sabre_VObject_DateTimeParser::parseDateTime($start); $start = VObject\DateTimeParser::parseDateTime($start);
$end = Sabre_VObject_DateTimeParser::parseDateTime($end); $end = VObject\DateTimeParser::parseDateTime($end);
if ($end <= $start) { if ($end <= $start) {
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
@ -448,7 +478,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
list($objProps) = $this->server->getPropertiesForPath($uri,$properties); list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
$vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
$vObject->expand($start, $end); $vObject->expand($start, $end);
$objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
} }
@ -515,7 +545,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
$validator = new Sabre_CalDAV_CalendarQueryValidator(); $validator = new Sabre_CalDAV_CalendarQueryValidator();
$vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
if ($validator->validate($vObject,$parser->filters)) { if ($validator->validate($vObject,$parser->filters)) {
// If the client didn't require the calendar-data property, // If the client didn't require the calendar-data property,
@ -549,7 +579,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if ($parser->expand) { if ($parser->expand) {
// We need to do some post-processing // We need to do some post-processing
$vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
$vObject->expand($parser->expand['start'], $parser->expand['end']); $vObject->expand($parser->expand['start'], $parser->expand['end']);
$properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
} }
@ -589,10 +619,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
} }
if ($start) { if ($start) {
$start = Sabre_VObject_DateTimeParser::parseDateTime($start); $start = VObject\DateTimeParser::parseDateTime($start);
} }
if ($end) { if ($end) {
$end = Sabre_VObject_DateTimeParser::parseDateTime($end); $end = VObject\DateTimeParser::parseDateTime($end);
} }
if (!$start && !$end) { if (!$start && !$end) {
@ -619,7 +649,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
return $obj; return $obj;
}, $calendar->getChildren()); }, $calendar->getChildren());
$generator = new Sabre_VObject_FreeBusyGenerator(); $generator = new VObject\FreeBusyGenerator();
$generator->setObjects($objects); $generator->setObjects($objects);
$generator->setTimeRange($start, $end); $generator->setTimeRange($start, $end);
$result = $generator->getResult(); $result = $generator->getResult();
@ -648,7 +678,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if (!$node instanceof Sabre_CalDAV_ICalendarObject) if (!$node instanceof Sabre_CalDAV_ICalendarObject)
return; return;
$this->validateICalendar($data); $this->validateICalendar($data, $path);
} }
@ -668,7 +698,49 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if (!$parentNode instanceof Sabre_CalDAV_Calendar) if (!$parentNode instanceof Sabre_CalDAV_Calendar)
return; return;
$this->validateICalendar($data); $this->validateICalendar($data, $path);
}
/**
* This event is triggered before any HTTP request is handled.
*
* We use this to intercept GET calls to notification nodes, and return the
* proper response.
*
* @param string $method
* @param string $path
* @return void
*/
public function beforeMethod($method, $path) {
if ($method!=='GET') return;
try {
$node = $this->server->tree->getNodeForPath($path);
} catch (Sabre_DAV_Exception_NotFound $e) {
return;
}
if (!$node instanceof Sabre_CalDAV_Notifications_INode)
return;
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$root = $dom->createElement('cs:notification');
foreach($this->server->xmlNamespaces as $namespace => $prefix) {
$root->setAttribute('xmlns:' . $prefix, $namespace);
}
$dom->appendChild($root);
$node->getNotificationType()->serializeBody($this->server, $root);
$this->server->httpResponse->setHeader('Content-Type','application/xml');
$this->server->httpResponse->sendStatus(200);
$this->server->httpResponse->sendBody($dom->saveXML());
return false;
} }
@ -678,9 +750,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
* An exception is thrown if it's not. * An exception is thrown if it's not.
* *
* @param resource|string $data * @param resource|string $data
* @param string $path
* @return void * @return void
*/ */
protected function validateICalendar(&$data) { protected function validateICalendar(&$data, $path) {
// If it's a stream, we convert it to a string first. // If it's a stream, we convert it to a string first.
if (is_resource($data)) { if (is_resource($data)) {
@ -692,9 +765,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
try { try {
$vobj = Sabre_VObject_Reader::read($data); $vobj = VObject\Reader::read($data);
} catch (Sabre_VObject_ParseException $e) { } catch (VObject\ParseException $e) {
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
@ -704,6 +777,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
} }
// Get the Supported Components for the target calendar
list($parentPath,$object) = Sabre_Dav_URLUtil::splitPath($path);
$calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'));
$supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue();
$foundType = null; $foundType = null;
$foundUID = null; $foundUID = null;
foreach($vobj->getComponents() as $component) { foreach($vobj->getComponents() as $component) {
@ -715,6 +793,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
case 'VJOURNAL' : case 'VJOURNAL' :
if (is_null($foundType)) { if (is_null($foundType)) {
$foundType = $component->name; $foundType = $component->name;
if (!in_array($foundType, $supportedComponents)) {
throw new Sabre_CalDAV_Exception_InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType);
}
if (!isset($component->UID)) { if (!isset($component->UID)) {
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
} }
@ -756,7 +837,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests');
} }
if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { if (!preg_match('/^mailto:(.*)@(.*)$/i', $originator)) {
throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address');
} }
$originator = substr($originator,7); $originator = substr($originator,7);
@ -765,7 +846,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
foreach($recipients as $k=>$recipient) { foreach($recipients as $k=>$recipient) {
$recipient = trim($recipient); $recipient = trim($recipient);
if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) {
throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address');
} }
$recipient = substr($recipient, 7); $recipient = substr($recipient, 7);
@ -789,8 +870,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
} }
try { try {
$vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); $vObject = VObject\Reader::read($this->server->httpRequest->getBody(true));
} catch (Sabre_VObject_ParseException $e) { } catch (VObject\ParseException $e) {
throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
} }
@ -813,9 +894,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
} }
if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') {
$this->iMIPMessage($originator, $recipients, $vObject, $principal); $result = $this->iMIPMessage($originator, $recipients, $vObject, $principal);
$this->server->httpResponse->sendStatus(200); $this->server->httpResponse->sendStatus(200);
$this->server->httpResponse->sendBody('Messages sent'); $this->server->httpResponse->setHeader('Content-Type','application/xml');
$this->server->httpResponse->sendBody($this->generateScheduleResponse($result));
} else { } else {
throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented');
} }
@ -825,18 +907,81 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
/** /**
* Sends an iMIP message by email. * Sends an iMIP message by email.
* *
* This method must return an array with status codes per recipient.
* This should look something like:
*
* array(
* 'user1@example.org' => '2.0;Success'
* )
*
* Formatting for this status code can be found at:
* https://tools.ietf.org/html/rfc5545#section-3.8.8.3
*
* A list of valid status codes can be found at:
* https://tools.ietf.org/html/rfc5546#section-3.6
*
* @param string $originator * @param string $originator
* @param array $recipients * @param array $recipients
* @param Sabre_VObject_Component $vObject * @param Sabre\VObject\Component $vObject
* @param string $principal Principal url * @return array
* @return void
*/ */
protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) { protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
if (!$this->imipHandler) { if (!$this->imipHandler) {
throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); $resultStatus = '5.2;This server does not support this operation';
} else {
$this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
$resultStatus = '2.0;Success';
} }
$this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
$result = array();
foreach($recipients as $recipient) {
$result[$recipient] = $resultStatus;
}
return $result;
}
/**
* Generates a schedule-response XML body
*
* The recipients array is a key->value list, containing email addresses
* and iTip status codes. See the iMIPMessage method for a description of
* the value.
*
* @param array $recipients
* @return string
*/
public function generateScheduleResponse(array $recipients) {
$dom = new DOMDocument('1.0','utf-8');
$dom->formatOutput = true;
$xscheduleResponse = $dom->createElement('cal:schedule-response');
$dom->appendChild($xscheduleResponse);
foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
$xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace);
}
foreach($recipients as $recipient=>$status) {
$xresponse = $dom->createElement('cal:response');
$xrecipient = $dom->createElement('cal:recipient');
$xrecipient->appendChild($dom->createTextNode($recipient));
$xresponse->appendChild($xrecipient);
$xrequestStatus = $dom->createElement('cal:request-status');
$xrequestStatus->appendChild($dom->createTextNode($status));
$xresponse->appendChild($xrequestStatus);
$xscheduleResponse->appendChild($xresponse);
}
return $dom->saveXML();
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* iMIP handler. * iMIP handler.
* *
@ -44,11 +46,11 @@ class Sabre_CalDAV_Schedule_IMip {
* *
* @param string $originator Originator Email * @param string $originator Originator Email
* @param array $recipients Array of email addresses * @param array $recipients Array of email addresses
* @param Sabre_VObject_Component $vObject * @param Sabre\VObject\Component $vObject
* @param string $principal Principal Url of the originator * @param string $principal Principal Url of the originator
* @return void * @return void
*/ */
public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) { public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
foreach($recipients as $recipient) { foreach($recipients as $recipient) {

View file

@ -21,7 +21,7 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
/** /**
* CalDAV backend * CalDAV backend
* *
* @var Sabre_CalDAV_Backend_Abstract * @var Sabre_CalDAV_Backend_BackendInterface
*/ */
protected $caldavBackend; protected $caldavBackend;
@ -36,10 +36,10 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
* Constructor * Constructor
* *
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @param mixed $userUri * @param mixed $userUri
*/ */
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) { public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $userUri) {
$this->principalBackend = $principalBackend; $this->principalBackend = $principalBackend;
$this->caldavBackend = $caldavBackend; $this->caldavBackend = $caldavBackend;
@ -171,6 +171,11 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
$objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
} }
$objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']);
// We're adding a notifications node, if it's supported by the backend.
if ($this->caldavBackend instanceof Sabre_CalDAV_Backend_NotificationSupport) {
$objs[] = new Sabre_CalDAV_Notifications_Collection($this->caldavBackend, $this->principalInfo['uri']);
}
return $objs; return $objs;
} }

View file

@ -238,7 +238,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
* Creates a new card. * Creates a new card.
* *
* The addressbook id will be passed as the first argument. This is the * The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getCards method. * same id as it is returned from the getAddressbooksForUser method.
* *
* The cardUri is a base uri, and doesn't include the full path. The * The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string. * cardData argument is the vcard body, and is passed as a string.

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* CardDAV plugin * CardDAV plugin
* *
@ -154,8 +156,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
if (is_resource($val)) if (is_resource($val))
$val = stream_get_contents($val); $val = stream_get_contents($val);
// Taking out \r to not screw up the xml output $returnedProperties[200][$addressDataProp] = $val;
$returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
} }
} }
@ -270,7 +271,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); $hrefElems = $dom->getElementsByTagNameNS('DAV:','href');
$propertyList = array(); $propertyList = array();
foreach($hrefElems as $elem) { foreach($hrefElems as $elem) {
@ -346,9 +347,9 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
try { try {
$vobj = Sabre_VObject_Reader::read($data); $vobj = VObject\Reader::read($data);
} catch (Sabre_VObject_ParseException $e) { } catch (VObject\ParseException $e) {
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
@ -358,6 +359,10 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.'); throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
} }
if (!isset($vobj->UID)) {
throw new Sabre_DAV_Exception_BadRequest('Every vcard must have an UID.');
}
} }
@ -438,7 +443,9 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
*/ */
public function validateFilters($vcardData, array $filters, $test) { public function validateFilters($vcardData, array $filters, $test) {
$vcard = Sabre_VObject_Reader::read($vcardData); $vcard = VObject\Reader::read($vcardData);
if (!$filters) return true;
foreach($filters as $filter) { foreach($filters as $filter) {

View file

@ -485,19 +485,17 @@ class Sabre_DAV_Client {
*/ */
public function parseMultiStatus($body) { public function parseMultiStatus($body) {
$body = Sabre_DAV_XMLUtil::convertDAVNamespace($body);
$responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
if ($responseXML===false) { if ($responseXML===false) {
throw new InvalidArgumentException('The passed data is not valid XML'); throw new InvalidArgumentException('The passed data is not valid XML');
} }
$responseXML->registerXPathNamespace('d', 'urn:DAV'); $responseXML->registerXPathNamespace('d', 'DAV:');
$propResult = array(); $propResult = array();
foreach($responseXML->xpath('d:response') as $response) { foreach($responseXML->xpath('d:response') as $response) {
$response->registerXPathNamespace('d', 'urn:DAV'); $response->registerXPathNamespace('d', 'DAV:');
$href = $response->xpath('d:href'); $href = $response->xpath('d:href');
$href = (string)$href[0]; $href = (string)$href[0];
@ -505,7 +503,7 @@ class Sabre_DAV_Client {
foreach($response->xpath('d:propstat') as $propStat) { foreach($response->xpath('d:propstat') as $propStat) {
$propStat->registerXPathNamespace('d', 'urn:DAV'); $propStat->registerXPathNamespace('d', 'DAV:');
$status = $propStat->xpath('d:status'); $status = $propStat->xpath('d:status');
list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);

View file

@ -293,7 +293,10 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
$this->server->tree->getNodeForPath($uri); $this->server->tree->getNodeForPath($uri);
// We need to call the beforeWriteContent event for RFC3744 // We need to call the beforeWriteContent event for RFC3744
$this->server->broadcastEvent('beforeWriteContent',array($uri)); // Edit: looks like this is not used, and causing problems now.
//
// See Issue 222
// $this->server->broadcastEvent('beforeWriteContent',array($uri));
} catch (Sabre_DAV_Exception_NotFound $e) { } catch (Sabre_DAV_Exception_NotFound $e) {

View file

@ -11,9 +11,7 @@
* @author Evert Pot (http://www.rooftopsolutions.nl/) * @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/ */
abstract class Sabre_DAV_Property { abstract class Sabre_DAV_Property implements Sabre_DAV_PropertyInterface {
abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop);
static function unserialize(DOMElement $prop) { static function unserialize(DOMElement $prop) {

View file

@ -138,7 +138,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA
if (is_scalar($propertyValue)) { if (is_scalar($propertyValue)) {
$text = $document->createTextNode($propertyValue); $text = $document->createTextNode($propertyValue);
$currentProperty->appendChild($text); $currentProperty->appendChild($text);
} elseif ($propertyValue instanceof Sabre_DAV_Property) { } elseif ($propertyValue instanceof Sabre_DAV_PropertyInterface) {
$propertyValue->serialize($server,$currentProperty); $propertyValue->serialize($server,$currentProperty);
} elseif (!is_null($propertyValue)) { } elseif (!is_null($propertyValue)) {
throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);

View file

@ -0,0 +1,21 @@
<?php
/**
* PropertyInterface
*
* Implement this interface to create new complex properties
*
* @package Sabre
* @subpackage DAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
interface Sabre_DAV_PropertyInterface {
public function serialize(Sabre_DAV_Server $server, DOMElement $prop);
static function unserialize(DOMElement $prop);
}

View file

@ -207,6 +207,10 @@ class Sabre_DAV_Server {
} catch (Exception $e) { } catch (Exception $e) {
try {
$this->broadcastEvent('exception', array($e));
} catch (Exception $ignore) {
}
$DOM = new DOMDocument('1.0','utf-8'); $DOM = new DOMDocument('1.0','utf-8');
$DOM->formatOutput = true; $DOM->formatOutput = true;
@ -508,7 +512,7 @@ class Sabre_DAV_Server {
if (!$this->checkPreconditions(true)) return false; if (!$this->checkPreconditions(true)) return false;
if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); if (!$node instanceof Sabre_DAV_IFile) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
$body = $node->get(); $body = $node->get();
// Converting string into stream, if needed. // Converting string into stream, if needed.
@ -1995,7 +1999,7 @@ class Sabre_DAV_Server {
if (!$body) return array(); if (!$body) return array();
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
$elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); $elem = $dom->getElementsByTagNameNS('DAV:','propfind')->item(0);
return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
} }

View file

@ -42,9 +42,9 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree {
$realPath = $this->getRealPath($path); $realPath = $this->getRealPath($path);
if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found'); if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found');
if (is_dir($realPath)) { if (is_dir($realPath)) {
return new Sabre_DAV_FS_Directory($path); return new Sabre_DAV_FS_Directory($realPath);
} else { } else {
return new Sabre_DAV_FS_File($path); return new Sabre_DAV_FS_File($realPath);
} }
} }

View file

@ -20,9 +20,6 @@ class Sabre_DAV_XMLUtil {
* {http://www.example.org}myelem * {http://www.example.org}myelem
* *
* This format is used throughout the SabreDAV sourcecode. * This format is used throughout the SabreDAV sourcecode.
* Elements encoded with the urn:DAV namespace will
* be returned as if they were in the DAV: namespace. This is to avoid
* compatibility problems.
* *
* This function will return null if a nodetype other than an Element is passed. * This function will return null if a nodetype other than an Element is passed.
* *
@ -33,8 +30,7 @@ class Sabre_DAV_XMLUtil {
if ($dom->nodeType !== XML_ELEMENT_NODE) return null; if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
// Mapping back to the real namespace, in case it was dav $ns = $dom->namespaceURI;
if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
// Mapping to clark notation // Mapping to clark notation
return '{' . $ns . '}' . $dom->localName; return '{' . $ns . '}' . $dom->localName;
@ -64,29 +60,11 @@ class Sabre_DAV_XMLUtil {
} }
/**
* This method takes an XML document (as string) and converts all instances of the
* DAV: namespace to urn:DAV
*
* This is unfortunately needed, because the DAV: namespace violates the xml namespaces
* spec, and causes the DOM to throw errors
*
* @param string $xmlDocument
* @return array|string|null
*/
static function convertDAVNamespace($xmlDocument) {
// This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
// namespace is actually a violation of the XML namespaces specification, and will cause errors
return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
}
/** /**
* This method provides a generic way to load a DOMDocument for WebDAV use. * This method provides a generic way to load a DOMDocument for WebDAV use.
* *
* This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors. * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
* It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. * It does not preserve whitespace.
* *
* @param string $xml * @param string $xml
* @throws Sabre_DAV_Exception_BadRequest * @throws Sabre_DAV_Exception_BadRequest
@ -118,10 +96,11 @@ class Sabre_DAV_XMLUtil {
libxml_clear_errors(); libxml_clear_errors();
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
// We don't generally care about any whitespace // We don't generally care about any whitespace
$dom->preserveWhiteSpace = false; $dom->preserveWhiteSpace = false;
$dom->loadXML($xml,LIBXML_NOWARNING | LIBXML_NOERROR);
if ($error = libxml_get_last_error()) { if ($error = libxml_get_last_error()) {
libxml_clear_errors(); libxml_clear_errors();

View file

@ -88,11 +88,11 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property {
static public function unserialize(DOMElement $dom) { static public function unserialize(DOMElement $dom) {
$privileges = array(); $privileges = array();
$xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); $xaces = $dom->getElementsByTagNameNS('DAV:','ace');
for($ii=0; $ii < $xaces->length; $ii++) { for($ii=0; $ii < $xaces->length; $ii++) {
$xace = $xaces->item($ii); $xace = $xaces->item($ii);
$principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); $principal = $xace->getElementsByTagNameNS('DAV:','principal');
if ($principal->length !== 1) { if ($principal->length !== 1) {
throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
} }
@ -116,17 +116,17 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property {
$protected = false; $protected = false;
if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { if ($xace->getElementsByTagNameNS('DAV:','protected')->length > 0) {
$protected = true; $protected = true;
} }
$grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); $grants = $xace->getElementsByTagNameNS('DAV:','grant');
if ($grants->length < 1) { if ($grants->length < 1) {
throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
} }
$grant = $grants->item(0); $grant = $grants->item(0);
$xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); $xprivs = $grant->getElementsByTagNameNS('DAV:','privilege');
for($jj=0; $jj<$xprivs->length; $jj++) { for($jj=0; $jj<$xprivs->length; $jj++) {
$xpriv = $xprivs->item($jj); $xpriv = $xprivs->item($jj);

View file

@ -46,7 +46,7 @@ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth {
if (strpos(strtolower($auth),'basic')!==0) return false; if (strpos(strtolower($auth),'basic')!==0) return false;
return explode(':', base64_decode(substr($auth, 6))); return explode(':', base64_decode(substr($auth, 6)),2);
} }

View file

@ -14,7 +14,7 @@ class Sabre_HTTP_Version {
/** /**
* Full version number * Full version number
*/ */
const VERSION = '1.6.2'; const VERSION = '1.6.4';
/** /**
* Stability : alpha, beta, stable * Stability : alpha, beta, stable

View file

@ -1,133 +0,0 @@
<?php
/**
* The VCalendar component
*
* This component adds functionality to a component, specific for a VCALENDAR.
*
* @package Sabre
* @subpackage VObject
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component {
/**
* Returns a list of all 'base components'. For instance, if an Event has
* a recurrence rule, and one instance is overridden, the overridden event
* will have the same UID, but will be excluded from this list.
*
* VTIMEZONE components will always be excluded.
*
* @param string $componentName filter by component name
* @return array
*/
public function getBaseComponents($componentName = null) {
$components = array();
foreach($this->children as $component) {
if (!$component instanceof Sabre_VObject_Component)
continue;
if (isset($component->{'RECURRENCE-ID'}))
continue;
if ($componentName && $component->name !== strtoupper($componentName))
continue;
if ($component->name === 'VTIMEZONE')
continue;
$components[] = $component;
}
return $components;
}
/**
* If this calendar object, has events with recurrence rules, this method
* can be used to expand the event into multiple sub-events.
*
* Each event will be stripped from it's recurrence information, and only
* the instances of the event in the specified timerange will be left
* alone.
*
* In addition, this method will cause timezone information to be stripped,
* and normalized to UTC.
*
* This method will alter the VCalendar. This cannot be reversed.
*
* This functionality is specifically used by the CalDAV standard. It is
* possible for clients to request expand events, if they are rather simple
* clients and do not have the possibility to calculate recurrences.
*
* @param DateTime $start
* @param DateTime $end
* @return void
*/
public function expand(DateTime $start, DateTime $end) {
$newEvents = array();
foreach($this->select('VEVENT') as $key=>$vevent) {
if (isset($vevent->{'RECURRENCE-ID'})) {
unset($this->children[$key]);
continue;
}
if (!$vevent->rrule) {
unset($this->children[$key]);
if ($vevent->isInTimeRange($start, $end)) {
$newEvents[] = $vevent;
}
continue;
}
$uid = (string)$vevent->uid;
if (!$uid) {
throw new LogicException('Event did not have a UID!');
}
$it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid);
$it->fastForward($start);
while($it->valid() && $it->getDTStart() < $end) {
if ($it->getDTEnd() > $start) {
$newEvents[] = $it->getEventObject();
}
$it->next();
}
unset($this->children[$key]);
}
foreach($newEvents as $newEvent) {
foreach($newEvent->children as $child) {
if ($child instanceof Sabre_VObject_Property_DateTime &&
$child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) {
$child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC);
}
}
$this->add($newEvent);
}
// Removing all VTIMEZONE components
unset($this->VTIMEZONE);
}
}

View file

@ -1,128 +0,0 @@
<?php
/**
* Time zone name translation
*
* This file translates well-known time zone names into "Olson database" time zone names.
*
* @package Sabre
* @subpackage VObject
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Frank Edelhaeuser (fedel@users.sourceforge.net)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_VObject_WindowsTimezoneMap {
protected static $map = array(
// from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
// snapshot taken on 2012/01/16
// windows
'AUS Central Standard Time'=>'Australia/Darwin',
'AUS Eastern Standard Time'=>'Australia/Sydney',
'Afghanistan Standard Time'=>'Asia/Kabul',
'Alaskan Standard Time'=>'America/Anchorage',
'Arab Standard Time'=>'Asia/Riyadh',
'Arabian Standard Time'=>'Asia/Dubai',
'Arabic Standard Time'=>'Asia/Baghdad',
'Argentina Standard Time'=>'America/Buenos_Aires',
'Armenian Standard Time'=>'Asia/Yerevan',
'Atlantic Standard Time'=>'America/Halifax',
'Azerbaijan Standard Time'=>'Asia/Baku',
'Azores Standard Time'=>'Atlantic/Azores',
'Bangladesh Standard Time'=>'Asia/Dhaka',
'Canada Central Standard Time'=>'America/Regina',
'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
'Caucasus Standard Time'=>'Asia/Yerevan',
'Cen. Australia Standard Time'=>'Australia/Adelaide',
'Central America Standard Time'=>'America/Guatemala',
'Central Asia Standard Time'=>'Asia/Almaty',
'Central Brazilian Standard Time'=>'America/Cuiaba',
'Central Europe Standard Time'=>'Europe/Budapest',
'Central European Standard Time'=>'Europe/Warsaw',
'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
'Central Standard Time'=>'America/Chicago',
'Central Standard Time (Mexico)'=>'America/Mexico_City',
'China Standard Time'=>'Asia/Shanghai',
'Dateline Standard Time'=>'Etc/GMT+12',
'E. Africa Standard Time'=>'Africa/Nairobi',
'E. Australia Standard Time'=>'Australia/Brisbane',
'E. Europe Standard Time'=>'Europe/Minsk',
'E. South America Standard Time'=>'America/Sao_Paulo',
'Eastern Standard Time'=>'America/New_York',
'Egypt Standard Time'=>'Africa/Cairo',
'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
'FLE Standard Time'=>'Europe/Kiev',
'Fiji Standard Time'=>'Pacific/Fiji',
'GMT Standard Time'=>'Europe/London',
'GTB Standard Time'=>'Europe/Istanbul',
'Georgian Standard Time'=>'Asia/Tbilisi',
'Greenland Standard Time'=>'America/Godthab',
'Greenwich Standard Time'=>'Atlantic/Reykjavik',
'Hawaiian Standard Time'=>'Pacific/Honolulu',
'India Standard Time'=>'Asia/Calcutta',
'Iran Standard Time'=>'Asia/Tehran',
'Israel Standard Time'=>'Asia/Jerusalem',
'Jordan Standard Time'=>'Asia/Amman',
'Kamchatka Standard Time'=>'Asia/Kamchatka',
'Korea Standard Time'=>'Asia/Seoul',
'Magadan Standard Time'=>'Asia/Magadan',
'Mauritius Standard Time'=>'Indian/Mauritius',
'Mexico Standard Time'=>'America/Mexico_City',
'Mexico Standard Time 2'=>'America/Chihuahua',
'Mid-Atlantic Standard Time'=>'Etc/GMT+2',
'Middle East Standard Time'=>'Asia/Beirut',
'Montevideo Standard Time'=>'America/Montevideo',
'Morocco Standard Time'=>'Africa/Casablanca',
'Mountain Standard Time'=>'America/Denver',
'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
'Myanmar Standard Time'=>'Asia/Rangoon',
'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
'Namibia Standard Time'=>'Africa/Windhoek',
'Nepal Standard Time'=>'Asia/Katmandu',
'New Zealand Standard Time'=>'Pacific/Auckland',
'Newfoundland Standard Time'=>'America/St_Johns',
'North Asia East Standard Time'=>'Asia/Irkutsk',
'North Asia Standard Time'=>'Asia/Krasnoyarsk',
'Pacific SA Standard Time'=>'America/Santiago',
'Pacific Standard Time'=>'America/Los_Angeles',
'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
'Pakistan Standard Time'=>'Asia/Karachi',
'Paraguay Standard Time'=>'America/Asuncion',
'Romance Standard Time'=>'Europe/Paris',
'Russian Standard Time'=>'Europe/Moscow',
'SA Eastern Standard Time'=>'America/Cayenne',
'SA Pacific Standard Time'=>'America/Bogota',
'SA Western Standard Time'=>'America/La_Paz',
'SE Asia Standard Time'=>'Asia/Bangkok',
'Samoa Standard Time'=>'Pacific/Apia',
'Singapore Standard Time'=>'Asia/Singapore',
'South Africa Standard Time'=>'Africa/Johannesburg',
'Sri Lanka Standard Time'=>'Asia/Colombo',
'Syria Standard Time'=>'Asia/Damascus',
'Taipei Standard Time'=>'Asia/Taipei',
'Tasmania Standard Time'=>'Australia/Hobart',
'Tokyo Standard Time'=>'Asia/Tokyo',
'Tonga Standard Time'=>'Pacific/Tongatapu',
'US Eastern Standard Time'=>'America/Indianapolis',
'US Mountain Standard Time'=>'America/Phoenix',
'UTC'=>'Etc/GMT',
'UTC+12'=>'Etc/GMT-12',
'UTC-02'=>'Etc/GMT+2',
'UTC-11'=>'Etc/GMT+11',
'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
'Venezuela Standard Time'=>'America/Caracas',
'Vladivostok Standard Time'=>'Asia/Vladivostok',
'W. Australia Standard Time'=>'Australia/Perth',
'W. Central Africa Standard Time'=>'Africa/Lagos',
'W. Europe Standard Time'=>'Europe/Berlin',
'West Asia Standard Time'=>'Asia/Tashkent',
'West Pacific Standard Time'=>'Pacific/Port_Moresby',
'Yakutsk Standard Time'=>'Asia/Yakutsk',
);
static public function lookup($tzid) {
return isset(self::$map[$tzid]) ? self::$map[$tzid] : null;
}
}

View file

@ -1,39 +0,0 @@
<?php
/**
* Sabre_VObject includes file
*
* Including this file will automatically include all files from the VObject
* package.
*
* This often allows faster loadtimes, as autoload-speed is often quite slow.
*
* @package Sabre
* @subpackage VObject
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
// Begin includes
include __DIR__ . '/DateTimeParser.php';
include __DIR__ . '/ElementList.php';
include __DIR__ . '/FreeBusyGenerator.php';
include __DIR__ . '/Node.php';
include __DIR__ . '/Parameter.php';
include __DIR__ . '/ParseException.php';
include __DIR__ . '/Reader.php';
include __DIR__ . '/RecurrenceIterator.php';
include __DIR__ . '/Version.php';
include __DIR__ . '/WindowsTimezoneMap.php';
include __DIR__ . '/Element.php';
include __DIR__ . '/Property.php';
include __DIR__ . '/Component.php';
include __DIR__ . '/Property/DateTime.php';
include __DIR__ . '/Property/MultiDateTime.php';
include __DIR__ . '/Component/VAlarm.php';
include __DIR__ . '/Component/VCalendar.php';
include __DIR__ . '/Component/VEvent.php';
include __DIR__ . '/Component/VJournal.php';
include __DIR__ . '/Component/VTodo.php';
// End includes

View file

@ -1,14 +1,16 @@
<?php <?php
class Sabre_CalDAV_Backend_Mock extends Sabre_CalDAV_Backend_Abstract { class Sabre_CalDAV_Backend_Mock extends Sabre_CalDAV_Backend_Abstract implements Sabre_CalDAV_Backend_NotificationSupport {
private $calendarData; private $calendarData;
private $calendars; private $calendars;
private $notifications;
function __construct(array $calendars, array $calendarData) { function __construct(array $calendars, array $calendarData, array $notifications = array()) {
$this->calendars = $calendars; $this->calendars = $calendars;
$this->calendarData = $calendarData; $this->calendarData = $calendarData;
$this->notifications = $notifications;
} }
@ -58,7 +60,15 @@ class Sabre_CalDAV_Backend_Mock extends Sabre_CalDAV_Backend_Abstract {
*/ */
function createCalendar($principalUri,$calendarUri,array $properties) { function createCalendar($principalUri,$calendarUri,array $properties) {
throw new Exception('Not implemented'); $id = Sabre_DAV_UUIDUtil::getUUID();
$this->calendars[] = array_merge(array(
'id' => $id,
'principaluri' => $principalUri,
'uri' => $calendarUri,
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT','VTODO')),
), $properties);
return $id;
} }
@ -112,7 +122,11 @@ class Sabre_CalDAV_Backend_Mock extends Sabre_CalDAV_Backend_Abstract {
*/ */
public function deleteCalendar($calendarId) { public function deleteCalendar($calendarId) {
throw new Exception('Not implemented'); foreach($this->calendars as $k=>$calendar) {
if ($calendar['id'] === $calendarId) {
unset($this->calendars[$k]);
}
}
} }
@ -227,4 +241,37 @@ class Sabre_CalDAV_Backend_Mock extends Sabre_CalDAV_Backend_Abstract {
} }
/**
* Returns a list of notifications for a given principal url.
*
* The returned array should only consist of implementations of
* Sabre_CalDAV_Notifications_INotificationType.
*
* @param string $principalUri
* @return array
*/
public function getNotificationsForPrincipal($principalUri) {
if (isset($this->notifications[$principalUri])) {
return $this->notifications[$principalUri];
}
return array();
}
/**
* This deletes a specific notifcation.
*
* This may be called by a client once it deems a notification handled.
*
* @param string $principalUri
* @param Sabre_CalDAV_Notifications_INotificationType $notification
* @return void
*/
public function deleteNotification($principalUri, Sabre_CalDAV_Notifications_INotificationType $notification) {
throw new Sabre_DAV_Exception_NotImplemented('This doesn\'t work!');
}
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
class Sabre_CalDAV_CalendarQueryVAlarmTest extends PHPUnit_Framework_TestCase { class Sabre_CalDAV_CalendarQueryVAlarmTest extends PHPUnit_Framework_TestCase {
/** /**
@ -8,16 +10,16 @@ class Sabre_CalDAV_CalendarQueryVAlarmTest extends PHPUnit_Framework_TestCase {
*/ */
function testValarm() { function testValarm() {
$vevent = Sabre_VObject_Component::create('VEVENT'); $vevent = VObject\Component::create('VEVENT');
$vevent->RRULE = 'FREQ=MONTHLY'; $vevent->RRULE = 'FREQ=MONTHLY';
$vevent->DTSTART = '20120101T120000Z'; $vevent->DTSTART = '20120101T120000Z';
$vevent->UID = 'bla'; $vevent->UID = 'bla';
$valarm = Sabre_VObject_Component::create('VALARM'); $valarm = VObject\Component::create('VALARM');
$valarm->TRIGGER = '-P15D'; $valarm->TRIGGER = '-P15D';
$vevent->add($valarm); $vevent->add($valarm);
$vcalendar = Sabre_VObject_Component::create('VCALENDAR'); $vcalendar = VObject\Component::create('VCALENDAR');
$vcalendar->add($vevent); $vcalendar->add($vevent);
$filter = array( $filter = array(
@ -52,16 +54,16 @@ class Sabre_CalDAV_CalendarQueryVAlarmTest extends PHPUnit_Framework_TestCase {
// A limited recurrence rule, should return false // A limited recurrence rule, should return false
$vevent = Sabre_VObject_Component::create('VEVENT'); $vevent = VObject\Component::create('VEVENT');
$vevent->RRULE = 'FREQ=MONTHLY;COUNT=1'; $vevent->RRULE = 'FREQ=MONTHLY;COUNT=1';
$vevent->DTSTART = '20120101T120000Z'; $vevent->DTSTART = '20120101T120000Z';
$vevent->UID = 'bla'; $vevent->UID = 'bla';
$valarm = Sabre_VObject_Component::create('VALARM'); $valarm = VObject\Component::create('VALARM');
$valarm->TRIGGER = '-P15D'; $valarm->TRIGGER = '-P15D';
$vevent->add($valarm); $vevent->add($valarm);
$vcalendar = Sabre_VObject_Component::create('VCALENDAR'); $vcalendar = VObject\Component::create('VCALENDAR');
$vcalendar->add($vevent); $vcalendar->add($vevent);
$this->assertFalse($validator->validate($vcalendar, $filter)); $this->assertFalse($validator->validate($vcalendar, $filter));
@ -69,15 +71,15 @@ class Sabre_CalDAV_CalendarQueryVAlarmTest extends PHPUnit_Framework_TestCase {
function testAlarmWayBefore() { function testAlarmWayBefore() {
$vevent = Sabre_VObject_Component::create('VEVENT'); $vevent = VObject\Component::create('VEVENT');
$vevent->DTSTART = '20120101T120000Z'; $vevent->DTSTART = '20120101T120000Z';
$vevent->UID = 'bla'; $vevent->UID = 'bla';
$valarm = Sabre_VObject_Component::create('VALARM'); $valarm = VObject\Component::create('VALARM');
$valarm->TRIGGER = '-P2W1D'; $valarm->TRIGGER = '-P2W1D';
$vevent->add($valarm); $vevent->add($valarm);
$vcalendar = Sabre_VObject_Component::create('VCALENDAR'); $vcalendar = VObject\Component::create('VCALENDAR');
$vcalendar->add($vevent); $vcalendar->add($vevent);
$filter = array( $filter = array(

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
class Sabre_CalDAV_CalendarQueryValidatorTest extends PHPUnit_Framework_TestCase { class Sabre_CalDAV_CalendarQueryValidatorTest extends PHPUnit_Framework_TestCase {
/** /**
@ -19,7 +21,7 @@ class Sabre_CalDAV_CalendarQueryValidatorTest extends PHPUnit_Framework_TestCase
'time-range' => null, 'time-range' => null,
); );
$vObject = Sabre_VObject_Reader::read($icalObject); $vObject = VObject\Reader::read($icalObject);
switch($outcome) { switch($outcome) {
case 0 : case 0 :
@ -31,7 +33,7 @@ class Sabre_CalDAV_CalendarQueryValidatorTest extends PHPUnit_Framework_TestCase
case -1 : case -1 :
try { try {
$validator->validate($vObject, $filters); $validator->validate($vObject, $filters);
} catch (Sabre_DAV_Exception $e) { } catch (Exception $e) {
// Success // Success
} }
break; break;
@ -343,6 +345,14 @@ DURATION:PT1H
RRULE:FREQ=YEARLY RRULE:FREQ=YEARLY
END:VEVENT END:VEVENT
END:VCALENDAR END:VCALENDAR
yow;
$blob33 = <<<yow
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART;VALUE=DATE:20120628
RRULE:FREQ=DAILY
END:VEVENT
END:VCALENDAR
yow; yow;
$filter1 = array( $filter1 = array(
@ -604,8 +614,16 @@ yow;
'time-range' => null, 'time-range' => null,
); );
// Time-range with RRULE $filter38 = array(
'name' => 'VEVENT',
'comp-filters' => array(),
'prop-filters' => array(),
'is-not-defined' => false,
'time-range' => array(
'start' => new DateTime('2012-07-01 00:00:00', new DateTimeZone('UTC')),
'end' => new DateTime('2012-08-01 00:00:00', new DateTimeZone('UTC')),
)
);
return array( return array(
// Component check // Component check
@ -741,6 +759,9 @@ yow;
array($blob31, $filter20, 1), array($blob31, $filter20, 1),
array($blob32, $filter20, 0), array($blob32, $filter20, 0),
// Bug reported on mailing list, related to all-day events.
array($blob33, $filter38, 1),
); );
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittests is created to find out why recurring events have wrong DTSTART value * This unittests is created to find out why recurring events have wrong DTSTART value
* *
@ -83,13 +85,13 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
// check if DTSTARTs and DTENDs are correct // check if DTSTARTs and DTENDs are correct
foreach ($vObject->VEVENT as $vevent) { foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre_VObject_Component_VEvent */ /** @var $vevent Sabre\VObject\Component_VEvent */
foreach ($vevent->children as $child) { foreach ($vevent->children as $child) {
/** @var $child Sabre_VObject_Property */ /** @var $child Sabre\VObject\Property */
if ($child->name == 'DTSTART') { if ($child->name == 'DTSTART') {
// DTSTART has to be one of three valid values // DTSTART has to be one of three valid values

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittests is created to find out why recurring events have wrong DTSTART value * This unittests is created to find out why recurring events have wrong DTSTART value
* *
@ -75,15 +77,15 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
$this->assertEquals(2, count($vObject->VEVENT)); $this->assertEquals(2, count($vObject->VEVENT));
// check if DTSTARTs and DTENDs are correct // check if DTSTARTs and DTENDs are correct
foreach ($vObject->VEVENT as $vevent) { foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre_VObject_Component_VEvent */ /** @var $vevent Sabre\VObject\Component\VEvent */
foreach ($vevent->children as $child) { foreach ($vevent->children as $child) {
/** @var $child Sabre_VObject_Property */ /** @var $child Sabre\VObject\Property */
if ($child->name == 'DTSTART') { if ($child->name == 'DTSTART') {
// DTSTART has to be one of two valid values // DTSTART has to be one of two valid values

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittests is created to find out why certain events show up twice. * This unittests is created to find out why certain events show up twice.
* *
@ -85,7 +87,7 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
// We only expect 3 events // We only expect 3 events
$this->assertEquals(3, count($vObject->VEVENT),'We got 6 events instead of 3. Output: ' . $body); $this->assertEquals(3, count($vObject->VEVENT),'We got 6 events instead of 3. Output: ' . $body);

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittest is created to check if queries for time-range include the start timestamp or not * This unittest is created to check if queries for time-range include the start timestamp or not
* *
@ -82,7 +84,7 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
// We expect 1 event // We expect 1 event
$this->assertEquals(1, count($vObject->VEVENT), 'We got 0 events instead of 1. Output: ' . $body); $this->assertEquals(1, count($vObject->VEVENT), 'We got 0 events instead of 1. Output: ' . $body);

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
require_once 'Sabre/CalDAV/TestUtil.php'; require_once 'Sabre/CalDAV/TestUtil.php';
require_once 'Sabre/DAV/Auth/MockBackend.php'; require_once 'Sabre/DAV/Auth/MockBackend.php';
require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/HTTP/ResponseMock.php';
@ -49,12 +51,62 @@ class Sabre_CalDAV_ICSExportPluginTest extends PHPUnit_Framework_TestCase {
'Content-Type' => 'text/calendar', 'Content-Type' => 'text/calendar',
), $s->httpResponse->headers); ), $s->httpResponse->headers);
$obj = Sabre_VObject_Reader::read($s->httpResponse->body); $obj = VObject\Reader::read($s->httpResponse->body);
$this->assertEquals(5,count($obj->children())); $this->assertEquals(5,count($obj->children()));
$this->assertEquals(1,count($obj->VERSION)); $this->assertEquals(1,count($obj->VERSION));
$this->assertEquals(1,count($obj->CALSCALE)); $this->assertEquals(1,count($obj->CALSCALE));
$this->assertEquals(1,count($obj->PRODID)); $this->assertEquals(1,count($obj->PRODID));
$this->assertTrue(strpos((string)$obj->PRODID, Sabre_DAV_Version::VERSION)!==false);
$this->assertEquals(1,count($obj->VTIMEZONE));
$this->assertEquals(1,count($obj->VEVENT));
}
function testBeforeMethodNoVersion() {
if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$cbackend = Sabre_CalDAV_TestUtil::getBackend();
$pbackend = new Sabre_DAVACL_MockPrincipalBackend();
$props = array(
'uri'=>'UUID-123467',
'principaluri' => 'admin',
'id' => 1,
);
$tree = array(
new Sabre_CalDAV_Calendar($pbackend,$cbackend,$props),
);
$p = new Sabre_CalDAV_ICSExportPlugin();
$s = new Sabre_DAV_Server($tree);
$s->addPlugin($p);
$s->addPlugin(new Sabre_CalDAV_Plugin());
$h = new Sabre_HTTP_Request(array(
'QUERY_STRING' => 'export',
));
$s->httpRequest = $h;
$s->httpResponse = new Sabre_HTTP_ResponseMock();
Sabre_DAV_Server::$exposeVersion = false;
$this->assertFalse($p->beforeMethod('GET','UUID-123467?export'));
Sabre_DAV_Server::$exposeVersion = true;
$this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status);
$this->assertEquals(array(
'Content-Type' => 'text/calendar',
), $s->httpResponse->headers);
$obj = VObject\Reader::read($s->httpResponse->body);
$this->assertEquals(5,count($obj->children()));
$this->assertEquals(1,count($obj->VERSION));
$this->assertEquals(1,count($obj->CALSCALE));
$this->assertEquals(1,count($obj->PRODID));
$this->assertFalse(strpos((string)$obj->PRODID, Sabre_DAV_Version::VERSION)!==false);
$this->assertEquals(1,count($obj->VTIMEZONE)); $this->assertEquals(1,count($obj->VTIMEZONE));
$this->assertEquals(1,count($obj->VEVENT)); $this->assertEquals(1,count($obj->VEVENT));
@ -161,7 +213,7 @@ class Sabre_CalDAV_ICSExportPluginTest extends PHPUnit_Framework_TestCase {
'Content-Type' => 'text/calendar', 'Content-Type' => 'text/calendar',
), $s->httpResponse->headers); ), $s->httpResponse->headers);
$obj = Sabre_VObject_Reader::read($s->httpResponse->body); $obj = VObject\Reader::read($s->httpResponse->body);
$this->assertEquals(5,count($obj->children())); $this->assertEquals(5,count($obj->children()));
$this->assertEquals(1,count($obj->VERSION)); $this->assertEquals(1,count($obj->VERSION));

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
class Sabre_CalDAV_Issue166Test extends PHPUnit_Framework_TestCase { class Sabre_CalDAV_Issue166Test extends PHPUnit_Framework_TestCase {
function testFlaw() { function testFlaw() {
@ -51,7 +53,7 @@ HI;
'is-not-defined' => false, 'is-not-defined' => false,
'time-range' => null, 'time-range' => null,
); );
$input = Sabre_VObject_Reader::read($input); $input = VObject\Reader::read($input);
$this->assertTrue($validator->validate($input,$filters)); $this->assertTrue($validator->validate($input,$filters));
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
class Sabre_CalDAV_Issue172Test extends PHPUnit_Framework_TestCase { class Sabre_CalDAV_Issue172Test extends PHPUnit_Framework_TestCase {
// DateTimeZone() native name: America/Los_Angeles (GMT-8 in January) // DateTimeZone() native name: America/Los_Angeles (GMT-8 in January)
@ -30,7 +32,7 @@ HI;
), ),
'prop-filters' => array(), 'prop-filters' => array(),
); );
$input = Sabre_VObject_Reader::read($input); $input = VObject\Reader::read($input);
$this->assertTrue($validator->validate($input,$filters)); $this->assertTrue($validator->validate($input,$filters));
} }
@ -77,7 +79,7 @@ HI;
), ),
'prop-filters' => array(), 'prop-filters' => array(),
); );
$input = Sabre_VObject_Reader::read($input); $input = VObject\Reader::read($input);
$this->assertTrue($validator->validate($input,$filters)); $this->assertTrue($validator->validate($input,$filters));
} }
@ -125,7 +127,7 @@ HI;
), ),
'prop-filters' => array(), 'prop-filters' => array(),
); );
$input = Sabre_VObject_Reader::read($input); $input = VObject\Reader::read($input);
$this->assertTrue($validator->validate($input,$filters)); $this->assertTrue($validator->validate($input,$filters));
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittest is created to find out why an overwritten DAILY event has wrong DTSTART, DTEND, SUMMARY and RECURRENCEID * This unittest is created to find out why an overwritten DAILY event has wrong DTSTART, DTEND, SUMMARY and RECURRENCEID
* *
@ -88,7 +90,7 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
$this->assertEquals(2, count($vObject->VEVENT)); $this->assertEquals(2, count($vObject->VEVENT));
@ -113,10 +115,10 @@ END:VCALENDAR
$matching = false; $matching = false;
foreach ($vObject->VEVENT as $vevent) { foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre_VObject_Component_VEvent */ /** @var $vevent Sabre\VObject\Component\VEvent */
foreach ($vevent->children as $child) { foreach ($vevent->children as $child) {
/** @var $child Sabre_VObject_Property */ /** @var $child Sabre\VObject\Property */
if (isset($expectedEvent[$child->name])) { if (isset($expectedEvent[$child->name])) {
if ($expectedEvent[$child->name] != $child->value) { if ($expectedEvent[$child->name] != $child->value) {

View file

@ -1,5 +1,7 @@
<?php <?php
use Sabre\VObject;
/** /**
* This unittest is created to check if a VALARM TRIGGER of PT0S is supported * This unittest is created to check if a VALARM TRIGGER of PT0S is supported
* *
@ -86,7 +88,7 @@ END:VCALENDAR
); );
$body = str_replace('&#13;','',$body); $body = str_replace('&#13;','',$body);
$vObject = Sabre_VObject_Reader::read($body); $vObject = VObject\Reader::read($body);
$this->assertEquals(1, count($vObject->VEVENT)); $this->assertEquals(1, count($vObject->VEVENT));

View file

@ -0,0 +1,96 @@
<?php
/**
* This unittest is created to check for an endless loop in Sabre_CalDAV_CalendarQueryValidator
*
*
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Issue220Test extends Sabre_DAVServerTest {
protected $setupCalDAV = true;
protected $caldavCalendars = array(
array(
'id' => 1,
'name' => 'Calendar',
'principaluri' => 'principals/user1',
'uri' => 'calendar1',
)
);
protected $caldavCalendarObjects = array(
1 => array(
'event.ics' => array(
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART;TZID=Europe/Berlin:20120601T180000
SUMMARY:Brot backen
RRULE:FREQ=DAILY;INTERVAL=1;WKST=MO
TRANSP:OPAQUE
DURATION:PT20M
LAST-MODIFIED:20120601T064634Z
CREATED:20120601T064634Z
DTSTAMP:20120601T064634Z
UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd
BEGIN:VALARM
TRIGGER;VALUE=DURATION:-PT5M
ACTION:DISPLAY
DESCRIPTION:Default Event Notification
X-WR-ALARMUID:cd952c1b-b3d6-41fb-b0a6-ec3a1a5bdd58
END:VALARM
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Berlin:20120606T180000
SUMMARY:Brot backen
TRANSP:OPAQUE
STATUS:CANCELLED
DTEND;TZID=Europe/Berlin:20120606T182000
LAST-MODIFIED:20120605T094310Z
SEQUENCE:1
RECURRENCE-ID:20120606T160000Z
UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd
END:VEVENT
END:VCALENDAR
',
),
),
);
function testIssue220() {
$request = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
'HTTP_DEPTH' => '1',
));
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<C:calendar-data/>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:comp-filter name="VALARM">
<C:time-range start="20120607T161646Z" end="20120612T161646Z"/>
</C:comp-filter>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>');
$response = $this->request($request);
$this->assertFalse(strpos($response->body, '<s:exception>PHPUnit_Framework_Error_Warning</s:exception>'), 'Error Warning occurred: ' . $response->body);
$this->assertFalse(strpos($response->body, 'Invalid argument supplied for foreach()'), 'Invalid argument supplied for foreach(): ' . $response->body);
$this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status);
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* This unittest is created to check if the time-range filter is working correctly with all-day-events
*
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_Issue228Test extends Sabre_DAVServerTest {
protected $setupCalDAV = true;
protected $caldavCalendars = array(
array(
'id' => 1,
'name' => 'Calendar',
'principaluri' => 'principals/user1',
'uri' => 'calendar1',
)
);
protected $caldavCalendarObjects = array(
1 => array(
'event.ics' => array(
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:20120730T113415CEST-6804EGphkd@xxxxxx.de
DTSTAMP:20120730T093415Z
DTSTART;VALUE=DATE:20120729
DTEND;VALUE=DATE:20120730
SUMMARY:sunday event
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR
',
),
),
);
function testIssue228() {
$request = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
'HTTP_DEPTH' => '1',
));
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<C:calendar-data>
<C:expand start="20120730T095609Z"
end="20120813T095609Z"/>
</C:calendar-data>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:time-range start="20120730T095609Z" end="20120813T095609Z"/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>');
$response = $this->request($request);
// We must check if absolutely nothing was returned from this query.
$this->assertFalse(strpos($response->body, 'BEGIN:VCALENDAR'));
}
}

View file

@ -0,0 +1,27 @@
<?php
class Sabre_CalDAV_Notifications_CollectionTest extends \PHPUnit_Framework_TestCase {
function testGetChildren() {
$principalUri = 'principals/user1';
$systemStatus = new Sabre_CalDAV_Notifications_Notification_SystemStatus(1);
$caldavBackend = new Sabre_CalDAV_Backend_Mock(array(),array(), array(
'principals/user1' => array(
$systemStatus
)
));
$col = new Sabre_CalDAV_Notifications_Collection($caldavBackend, $principalUri);
$this->assertEquals('notifications', $col->getName());
$this->assertEquals(array(
new Sabre_CalDAV_Notifications_Node($caldavBackend, $systemStatus)
), $col->getChildren());
}
}

View file

@ -0,0 +1,23 @@
<?php
class Sabre_CalDAV_Notifications_NodeTest extends \PHPUnit_Framework_TestCase {
function testGetId() {
$principalUri = 'principals/user1';
$systemStatus = new Sabre_CalDAV_Notifications_Notification_SystemStatus(1);
$caldavBackend = new Sabre_CalDAV_Backend_Mock(array(),array(), array(
'principals/user1' => array(
$systemStatus
)
));
$node = new Sabre_CalDAV_Notifications_Node($caldavBackend, $systemStatus);
$this->assertEquals($systemStatus->getId(), $node->getName());
}
}

View file

@ -0,0 +1,55 @@
<?php
class Sabre_CalDAV_Notifications_Notification extends \PHPUnit_Framework_TestCase {
/**
* @dataProvider dataProvider
*/
function testSerializers($notification, $expected1, $expected2) {
$this->assertEquals('foo', $notification->getId());
$dom = new DOMDocument('1.0','UTF-8');
$elem = $dom->createElement('cs:root');
$elem->setAttribute('xmlns:cs',Sabre_CalDAV_Plugin::NS_CALENDARSERVER);
$dom->appendChild($elem);
$notification->serialize(new Sabre_DAV_Server(), $elem);
$this->assertEquals($expected1, $dom->saveXML());
$dom = new DOMDocument('1.0','UTF-8');
$elem = $dom->createElement('cs:root');
$elem->setAttribute('xmlns:cs',Sabre_CalDAV_Plugin::NS_CALENDARSERVER);
$dom->appendChild($elem);
$notification->serializeBody(new Sabre_DAV_Server(), $elem);
$this->assertEquals($expected2, $dom->saveXML());
}
function dataProvider() {
return array(
array(
new Sabre_CalDAV_Notifications_Notification_SystemStatus('foo'),
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="high"/></cs:root>' . "\n",
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="high"/></cs:root>' . "\n",
),
array(
new Sabre_CalDAV_Notifications_Notification_SystemStatus('foo',Sabre_CalDAV_Notifications_Notification_SystemStatus::TYPE_MEDIUM,'bar'),
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="medium"/></cs:root>' . "\n",
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="medium"><cs:description>bar</cs:description></cs:systemstatus></cs:root>' . "\n",
),
array(
new Sabre_CalDAV_Notifications_Notification_SystemStatus('foo',Sabre_CalDAV_Notifications_Notification_SystemStatus::TYPE_LOW,null,'http://example.org/'),
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="low"/></cs:root>' . "\n",
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="low"><d:href>http://example.org/</d:href></cs:systemstatus></cs:root>' . "\n",
)
);
}
}

View file

@ -191,7 +191,17 @@ class Sabre_CalDAV_OutboxPostTest extends Sabre_DAVServerTest {
$req->setBody(implode("\r\n",$body)); $req->setBody(implode("\r\n",$body));
$this->assertHTTPStatus(501, $req);
$response = $this->request($req);
$this->assertEquals('HTTP/1.1 200 OK', $response->status);
$this->assertEquals(array(
'Content-Type' => 'application/xml',
), $response->headers);
// Lazily checking the body for a few expected values.
$this->assertTrue(strpos($response->body, '5.2;')!==false);
$this->assertTrue(strpos($response->body,'user2@example.org')!==false);
} }
@ -218,7 +228,66 @@ class Sabre_CalDAV_OutboxPostTest extends Sabre_DAVServerTest {
$handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org'); $handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org');
$this->caldavPlugin->setIMIPhandler($handler); $this->caldavPlugin->setIMIPhandler($handler);
$this->assertHTTPStatus(200, $req);
$response = $this->request($req);
$this->assertEquals('HTTP/1.1 200 OK', $response->status);
$this->assertEquals(array(
'Content-Type' => 'application/xml',
), $response->headers);
// Lazily checking the body for a few expected values.
$this->assertTrue(strpos($response->body, '2.0;')!==false);
$this->assertTrue(strpos($response->body,'user2@example.org')!==false);
$this->assertEquals(array(
array(
'to' => 'user2@example.org',
'subject' => 'Invitation for: An invitation',
'body' => implode("\r\n", $body) . "\r\n",
'headers' => array(
'Reply-To: user1.sabredav@sabredav.org',
'From: server@example.org',
'Content-Type: text/calendar; method=REQUEST; charset=utf-8',
'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY,
),
)
), $handler->getSentEmails());
}
function testSuccessRequestUpperCased() {
$req = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/outbox',
'HTTP_ORIGINATOR' => 'MAILTO:user1.sabredav@sabredav.org',
'HTTP_RECIPIENT' => 'MAILTO:user2@example.org',
));
$body = array(
'BEGIN:VCALENDAR',
'METHOD:REQUEST',
'BEGIN:VEVENT',
'SUMMARY:An invitation',
'END:VEVENT',
'END:VCALENDAR',
);
$req->setBody(implode("\r\n",$body));
$handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org');
$this->caldavPlugin->setIMIPhandler($handler);
$response = $this->request($req);
$this->assertEquals('HTTP/1.1 200 OK', $response->status);
$this->assertEquals(array(
'Content-Type' => 'application/xml',
), $response->headers);
// Lazily checking the body for a few expected values.
$this->assertTrue(strpos($response->body, '2.0;')!==false);
$this->assertTrue(strpos($response->body,'user2@example.org')!==false);
$this->assertEquals(array( $this->assertEquals(array(
array( array(

View file

@ -23,8 +23,34 @@ class Sabre_CalDAV_PluginTest extends PHPUnit_Framework_TestCase {
function setup() { function setup() {
if (!SABRE_HASSQLITE) $this->markTestSkipped('No PDO SQLite support'); $this->caldavBackend = new Sabre_CalDAV_Backend_Mock(array(
$this->caldavBackend = Sabre_CalDAV_TestUtil::getBackend(); array(
'id' => 1,
'uri' => 'UUID-123467',
'principaluri' => 'principals/user1',
'{DAV:}displayname' => 'user1 calendar',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description',
'{http://apple.com/ns/ical/}calendar-order' => '1',
'{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT','VTODO')),
),
array(
'id' => 2,
'uri' => 'UUID-123468',
'principaluri' => 'principals/user1',
'{DAV:}displayname' => 'user1 calendar2',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description',
'{http://apple.com/ns/ical/}calendar-order' => '1',
'{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT','VTODO')),
)
), array(
1 => array(
'UUID-2345' => array(
'calendardata' => Sabre_CalDAV_TestUtil::getTestCalendarData(),
)
)
));
$principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); $principalBackend = new Sabre_DAVACL_MockPrincipalBackend();
$principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read',array('principals/user1')); $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read',array('principals/user1'));
$principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write',array('principals/user1')); $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write',array('principals/user1'));
@ -398,6 +424,7 @@ END:VCALENDAR';
'{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for', '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for', '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}notification-URL',
)); ));
$this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(0,$props);
@ -414,6 +441,12 @@ END:VCALENDAR';
$this->assertTrue($prop instanceof Sabre_DAV_Property_Href); $this->assertTrue($prop instanceof Sabre_DAV_Property_Href);
$this->assertEquals('calendars/user1/outbox',$prop->getHref()); $this->assertEquals('calendars/user1/outbox',$prop->getHref());
$this->assertArrayHasKey('{'.Sabre_CalDAV_Plugin::NS_CALENDARSERVER .'}notification-URL',$props[0][200]);
$prop = $props[0][200]['{'.Sabre_CalDAV_Plugin::NS_CALENDARSERVER .'}notification-URL'];
$this->assertTrue($prop instanceof Sabre_DAV_Property_Href);
$this->assertEquals('calendars/user1/notifications/',$prop->getHref());
$this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]);
$prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'];
$this->assertTrue($prop instanceof Sabre_DAV_Property_HrefList); $this->assertTrue($prop instanceof Sabre_DAV_Property_HrefList);
@ -429,6 +462,7 @@ END:VCALENDAR';
$this->assertInstanceOf('Sabre_DAV_Property_HrefList', $prop); $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $prop);
$this->assertEquals(array('principals/admin'), $prop->getHrefs()); $this->assertEquals(array('principals/admin'), $prop->getHrefs());
} }
function testSupportedReportSetPropertyNonCalendar() { function testSupportedReportSetPropertyNonCalendar() {
@ -502,9 +536,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -564,9 +598,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -629,9 +663,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -688,9 +722,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -755,7 +789,7 @@ END:VCALENDAR';
'<d:prop>' . '<d:prop>' .
' <c:calendar-data>' . ' <c:calendar-data>' .
' <c:expand start="20000101T000000Z" end="20101231T235959Z" />' . ' <c:expand start="20000101T000000Z" end="20101231T235959Z" />' .
' </c:calendar-data>' . ' </c:calendar-data>' .
' <d:getetag />' . ' <d:getetag />' .
'</d:prop>' . '</d:prop>' .
'<c:filter>' . '<c:filter>' .
@ -777,9 +811,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -837,9 +871,9 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
$xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); $xml = simplexml_load_string($this->response->body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
$check = array( $check = array(
@ -991,4 +1025,60 @@ END:VCALENDAR';
$this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
} }
function testNotificationProperties() {
$request = array(
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}notificationtype',
);
$result = array();
$notification = new Sabre_CalDAV_Notifications_Node(
$this->caldavBackend,
new Sabre_CalDAV_Notifications_Notification_SystemStatus('foo')
);
$this->plugin->beforeGetProperties('foo', $notification, $request, $result);
$this->assertEquals(
array(
200 => array(
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}notificationtype' => $notification->getNotificationType()
)
), $result);
}
function testNotificationGet() {
$notification = new Sabre_CalDAV_Notifications_Node(
$this->caldavBackend,
new Sabre_CalDAV_Notifications_Notification_SystemStatus('foo')
);
$server = new Sabre_DAV_Server(array($notification));
$caldav = new Sabre_CalDAV_Plugin();
$httpResponse = new Sabre_HTTP_ResponseMock();
$server->httpResponse = $httpResponse;
$server->addPlugin($caldav);
$caldav->beforeMethod('GET','foo');
$this->assertEquals('HTTP/1.1 200 OK', $httpResponse->status);
$this->assertEquals(array(
'Content-Type' => 'application/xml',
), $httpResponse->headers);
$expected =
'<?xml version="1.0" encoding="UTF-8"?>
<cs:notification xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">
<cs:systemstatus type="high"/>
</cs:notification>
';
$this->assertEquals($expected, $httpResponse->body);
}
} }

View file

@ -22,6 +22,13 @@ class Sabre_CalDAV_ValidateICalTest extends PHPUnit_Framework_TestCase {
'id' => 'calendar1', 'id' => 'calendar1',
'principaluri' => 'principals/admin', 'principaluri' => 'principals/admin',
'uri' => 'calendar1', 'uri' => 'calendar1',
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet( array('VEVENT','VTODO','VJOURNAL') ),
),
array(
'id' => 'calendar2',
'principaluri' => 'principals/admin',
'uri' => 'calendar2',
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet( array('VTODO','VJOURNAL') ),
) )
); );
@ -207,4 +214,33 @@ class Sabre_CalDAV_ValidateICalTest extends PHPUnit_Framework_TestCase {
$this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics')); $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics'));
} }
function testCreateFileInvalidComponent() {
$request = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics',
));
$request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
$response = $this->request($request);
$this->assertEquals('HTTP/1.1 403 Forbidden', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testUpdateFileInvalidComponent() {
$this->calBackend->createCalendarObject('calendar2','blabla.ics','foo');
$request = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics',
));
$request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
$response = $this->request($request);
$this->assertEquals('HTTP/1.1 403 Forbidden', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
} }

View file

@ -65,20 +65,35 @@ class Sabre_CardDAV_ValidateVCardTest extends PHPUnit_Framework_TestCase {
'REQUEST_METHOD' => 'PUT', 'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
)); ));
$request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); $request->setBody("BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n");
$response = $this->request($request); $response = $this->request($request);
$this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
$expected = array( $expected = array(
'uri' => 'blabla.vcf', 'uri' => 'blabla.vcf',
'carddata' => "BEGIN:VCARD\r\nEND:VCARD\r\n", 'carddata' => "BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n",
); );
$this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf')); $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf'));
} }
function testCreateFileNoUID() {
$request = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
$request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n");
$response = $this->request($request);
$this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFileVCalendar() { function testCreateFileVCalendar() {
$request = new Sabre_HTTP_Request(array( $request = new Sabre_HTTP_Request(array(
@ -114,7 +129,7 @@ class Sabre_CardDAV_ValidateVCardTest extends PHPUnit_Framework_TestCase {
'REQUEST_METHOD' => 'PUT', 'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
)); ));
$body = "BEGIN:VCARD\r\nEND:VCARD\r\n"; $body = "BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n";
$request->setBody($body); $request->setBody($body);
$response = $this->request($request); $response = $this->request($request);

View file

@ -92,9 +92,9 @@ class Sabre_DAV_Locks_PluginTest extends Sabre_DAV_AbstractServer {
$this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$elements = array( $elements = array(
'/d:prop', '/d:prop',

View file

@ -60,7 +60,7 @@ class Sabre_DAV_Property_HrefListTest extends PHPUnit_Framework_TestCase {
function testUnserialize() { function testUnserialize() {
$xml = '<?xml version="1.0"?> $xml = '<?xml version="1.0"?>
<d:anything xmlns:d="urn:DAV"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything> <d:anything xmlns:d="DAV:"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything>
'; ';
$dom = new DOMDocument(); $dom = new DOMDocument();
@ -74,7 +74,7 @@ class Sabre_DAV_Property_HrefListTest extends PHPUnit_Framework_TestCase {
function testUnserializeIncompatible() { function testUnserializeIncompatible() {
$xml = '<?xml version="1.0"?> $xml = '<?xml version="1.0"?>
<d:anything xmlns:d="urn:DAV"><d:href2>/bla/foo</d:href2></d:anything> <d:anything xmlns:d="DAV:"><d:href2>/bla/foo</d:href2></d:anything>
'; ';
$dom = new DOMDocument(); $dom = new DOMDocument();

View file

@ -60,7 +60,7 @@ class Sabre_DAV_Property_HrefTest extends PHPUnit_Framework_TestCase {
function testUnserialize() { function testUnserialize() {
$xml = '<?xml version="1.0"?> $xml = '<?xml version="1.0"?>
<d:anything xmlns:d="urn:DAV"><d:href>/bla/path</d:href></d:anything> <d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything>
'; ';
$dom = new DOMDocument(); $dom = new DOMDocument();
@ -74,7 +74,7 @@ class Sabre_DAV_Property_HrefTest extends PHPUnit_Framework_TestCase {
function testUnserializeIncompatible() { function testUnserializeIncompatible() {
$xml = '<?xml version="1.0"?> $xml = '<?xml version="1.0"?>
<d:anything xmlns:d="urn:DAV"><d:href2>/bla/path</d:href2></d:anything> <d:anything xmlns:d="DAV:"><d:href2>/bla/path</d:href2></d:anything>
'; ';
$dom = new DOMDocument(); $dom = new DOMDocument();

View file

@ -37,9 +37,9 @@ class Sabre_DAV_Property_SupportedReportSetTest extends Sabre_DAV_AbstractServer
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');
$this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element');
@ -74,9 +74,9 @@ class Sabre_DAV_Property_SupportedReportSetTest extends Sabre_DAV_AbstractServer
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('x','http://www.rooftopsolutions.nl/testnamespace'); $xml->registerXPathNamespace('x','http://www.rooftopsolutions.nl/testnamespace');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');

View file

@ -6,6 +6,8 @@ class Sabre_DAV_ServerEventsTest extends Sabre_DAV_AbstractServer {
private $tempPath; private $tempPath;
private $exception;
function testAfterBind() { function testAfterBind() {
$this->server->subscribeEvent('afterBind',array($this,'afterBindHandler')); $this->server->subscribeEvent('afterBind',array($this,'afterBindHandler'));
@ -47,5 +49,25 @@ class Sabre_DAV_ServerEventsTest extends Sabre_DAV_AbstractServer {
} }
function testException() {
$this->server->subscribeEvent('exception', array($this, 'exceptionHandler'));
$req = new Sabre_HTTP_Request(array(
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/not/exisitng',
));
$this->server->httpRequest = $req;
$this->server->exec();
$this->assertInstanceOf('Sabre_DAV_Exception_NotFound', $this->exception);
}
function exceptionHandler(Exception $exception) {
$this->exception = $exception;
}
} }

View file

@ -192,7 +192,6 @@ class Sabre_DAV_ServerMKCOLTest extends Sabre_DAV_AbstractServer {
} }
/** /**
* @depends testMKCOLIncorrectResourceType2 * @depends testMKCOLIncorrectResourceType2
*/ */
@ -224,6 +223,38 @@ class Sabre_DAV_ServerMKCOLTest extends Sabre_DAV_AbstractServer {
} }
/**
* @depends testMKCOLIncorrectResourceType2
*/
function testMKCOLWhiteSpaceResourceType() {
$serverVars = array(
'REQUEST_URI' => '/testcol',
'REQUEST_METHOD' => 'MKCOL',
'HTTP_CONTENT_TYPE' => 'application/xml',
);
$request = new Sabre_HTTP_Request($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
<prop>
<resourcetype>
<collection />
</resourcetype>
</prop>
</set>
</mkcol>');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
'Content-Length' => '0',
),$this->response->headers);
$this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/** /**
* @depends testMKCOLIncorrectResourceType2 * @depends testMKCOLIncorrectResourceType2

View file

@ -58,9 +58,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
$this->response->headers $this->response->headers
); );
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href');
$this->assertEquals('/',(string)$data,'href element should have been /'); $this->assertEquals('/',(string)$data,'href element should have been /');
@ -81,9 +81,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
$this->sendRequest($xml); $this->sendRequest($xml);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry');
$this->assertEquals(2,count($data),'We expected two \'d:lockentry\' tags'); $this->assertEquals(2,count($data),'We expected two \'d:lockentry\' tags');
@ -115,9 +115,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
$this->sendRequest($xml); $this->sendRequest($xml);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery');
$this->assertEquals(1,count($data),'We expected a \'d:lockdiscovery\' tag'); $this->assertEquals(1,count($data),'We expected a \'d:lockdiscovery\' tag');
@ -134,9 +134,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
</d:propfind>'; </d:propfind>';
$this->sendRequest($xml); $this->sendRequest($xml);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$pathTests = array( $pathTests = array(
'/d:multistatus', '/d:multistatus',
'/d:multistatus/d:response', '/d:multistatus/d:response',
@ -312,9 +312,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
$this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We got the wrong status. Full XML response: ' . $this->response->body); $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We got the wrong status. Full XML response: ' . $this->response->body);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');
@ -345,9 +345,9 @@ class Sabre_DAV_ServerPropsTest extends Sabre_DAV_AbstractServer {
$this->sendRequest($xml); $this->sendRequest($xml);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
$xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace');
$xpath='//bla:someprop'; $xpath='//bla:someprop';

View file

@ -233,9 +233,9 @@ class Sabre_DAV_TemporaryFileFilterTest extends Sabre_DAV_AbstractServer {
'Content-Type' => 'application/xml; charset=utf-8', 'Content-Type' => 'application/xml; charset=utf-8',
),$this->response->headers); ),$this->response->headers);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"DAV:\"",$this->response->body);
$xml = simplexml_load_string($body); $xml = simplexml_load_string($body);
$xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('d','DAV:');
list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href');
$this->assertEquals('/._testput.txt',(string)$data,'href element should have been /._testput.txt'); $this->assertEquals('/._testput.txt',(string)$data,'href element should have been /._testput.txt');

View file

@ -37,6 +37,8 @@ class Sabre_DAV_Tree_FilesystemTest extends PHPUnit_Framework_TestCase {
$fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR); $fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR);
$node = $fs->getNodeForPath('dir'); $node = $fs->getNodeForPath('dir');
$this->assertTrue($node instanceof Sabre_DAV_FS_Directory); $this->assertTrue($node instanceof Sabre_DAV_FS_Directory);
$this->assertEquals('dir', $node->getName());
$this->assertInternalType('array', $node->getChildren());
} }

View file

@ -29,7 +29,7 @@ class Sabre_DAV_XMLUtilTest extends PHPUnit_Framework_TestCase {
function testToClarkNotationDAVNamespace() { function testToClarkNotationDAVNamespace() {
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>'); $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="DAV:">Testdoc</s:test1>');
$this->assertEquals( $this->assertEquals(
'{DAV:}test1', '{DAV:}test1',
@ -41,7 +41,7 @@ class Sabre_DAV_XMLUtilTest extends PHPUnit_Framework_TestCase {
function testToClarkNotationNoElem() { function testToClarkNotationNoElem() {
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>'); $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="DAV:">Testdoc</s:test1>');
$this->assertNull( $this->assertNull(
Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild->firstChild) Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild->firstChild)
@ -49,59 +49,6 @@ class Sabre_DAV_XMLUtilTest extends PHPUnit_Framework_TestCase {
} }
function testConvertDAVNamespace() {
$xml='<?xml version="1.0"?><document xmlns="DAV:">blablabla</document>';
$this->assertEquals(
'<?xml version="1.0"?><document xmlns="urn:DAV">blablabla</document>',
Sabre_DAV_XMLUtil::convertDAVNamespace($xml)
);
}
function testConvertDAVNamespace2() {
$xml='<?xml version="1.0"?><s:document xmlns:s="DAV:">blablabla</s:document>';
$this->assertEquals(
'<?xml version="1.0"?><s:document xmlns:s="urn:DAV">blablabla</s:document>',
Sabre_DAV_XMLUtil::convertDAVNamespace($xml)
);
}
function testConvertDAVNamespace3() {
$xml='<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="DAV:" xmlns:z="http://othernamespace">blablabla</s:document>';
$this->assertEquals(
'<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="urn:DAV" xmlns:z="http://othernamespace">blablabla</s:document>',
Sabre_DAV_XMLUtil::convertDAVNamespace($xml)
);
}
function testConvertDAVNamespace4() {
$xml='<?xml version="1.0"?><document xmlns=\'DAV:\'>blablabla</document>';
$this->assertEquals(
'<?xml version="1.0"?><document xmlns=\'urn:DAV\'>blablabla</document>',
Sabre_DAV_XMLUtil::convertDAVNamespace($xml)
);
}
function testConvertDAVNamespaceMixedQuotes() {
$xml='<?xml version="1.0"?><document xmlns=\'DAV:" xmlns="Another attribute\'>blablabla</document>';
$this->assertEquals(
$xml,
Sabre_DAV_XMLUtil::convertDAVNamespace($xml)
);
}
/**
* @depends testConvertDAVNamespace
*/
function testLoadDOMDocument() { function testLoadDOMDocument() {
$xml='<?xml version="1.0"?><document></document>'; $xml='<?xml version="1.0"?><document></document>';
@ -121,7 +68,6 @@ class Sabre_DAV_XMLUtilTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @depends testConvertDAVNamespace
* @expectedException Sabre_DAV_Exception_BadRequest * @expectedException Sabre_DAV_Exception_BadRequest
*/ */
function testLoadDOMDocumentInvalid() { function testLoadDOMDocumentInvalid() {

View file

@ -60,6 +60,25 @@ class Sabre_HTTP_BasicAuthTest extends PHPUnit_Framework_TestCase {
} }
function testGetUserPassWithColon() {
$server = array(
'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234:5678'),
);
$request = new Sabre_HTTP_Request($server);
$this->basicAuth->setHTTPRequest($request);
$userPass = $this->basicAuth->getUserPass();
$this->assertEquals(
array('admin','1234:5678'),
$userPass,
'We did not get the username and password we expected'
);
}
function testGetUserPassApacheEdgeCase() { function testGetUserPassApacheEdgeCase() {
$server = array( $server = array(

View file

@ -1,64 +0,0 @@
<?php
class Sabre_VObject_Component_VEventTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider timeRangeTestData
*/
public function testInTimeRange(Sabre_VObject_Component_VEvent $vevent,$start,$end,$outcome) {
$this->assertEquals($outcome, $vevent->isInTimeRange($start, $end));
}
public function timeRangeTestData() {
$tests = array();
$vevent = new Sabre_VObject_Component_VEvent('VEVENT');
$vevent->DTSTART = '20111223T120000Z';
$tests[] = array($vevent, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vevent2 = clone $vevent;
$vevent2->DTEND = '20111225T120000Z';
$tests[] = array($vevent2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vevent3 = clone $vevent;
$vevent3->DURATION = 'P1D';
$tests[] = array($vevent3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vevent4 = clone $vevent;
$vevent4->DTSTART = '20111225';
$vevent4->DTSTART['VALUE'] = 'DATE';
$tests[] = array($vevent4, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent4, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
// Event with no end date should be treated as lasting the entire day.
$tests[] = array($vevent4, new DateTime('2011-12-25 16:00:00'), new DateTime('2011-12-25 17:00:00'), true);
$vevent5 = clone $vevent;
$vevent5->DURATION = 'P1D';
$vevent5->RRULE = 'FREQ=YEARLY';
$tests[] = array($vevent5, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent5, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$tests[] = array($vevent5, new DateTime('2013-12-01'), new DateTime('2013-12-31'), true);
$vevent6 = clone $vevent;
$vevent6->DTSTART = '20111225';
$vevent6->DTSTART['VALUE'] = 'DATE';
$vevent6->DTEND = '20111225';
$vevent6->DTEND['VALUE'] = 'DATE';
$tests[] = array($vevent6, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vevent6, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
// Event with no end date should be treated as lasting the entire day.
$tests[] = array($vevent6, new DateTime('2011-12-25 16:00:00'), new DateTime('2011-12-25 17:00:00'), true);
return $tests;
}
}

View file

@ -1,37 +0,0 @@
<?php
class Sabre_VObject_Component_VJournalTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider timeRangeTestData
*/
public function testInTimeRange(Sabre_VObject_Component_VJournal $vtodo,$start,$end,$outcome) {
$this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
}
public function timeRangeTestData() {
$tests = array();
$vjournal = Sabre_VObject_Component::create('VJOURNAL');
$vjournal->DTSTART = '20111223T120000Z';
$tests[] = array($vjournal, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vjournal, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vjournal2 = Sabre_VObject_Component::create('VJOURNAL');
$vjournal2->DTSTART = '20111223';
$vjournal2->DTSTART['VALUE'] = 'DATE';
$tests[] = array($vjournal2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vjournal2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vjournal3 = Sabre_VObject_Component::create('VJOURNAL');
$tests[] = array($vjournal3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), false);
$tests[] = array($vjournal3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
return $tests;
}
}

View file

@ -1,63 +0,0 @@
<?php
class Sabre_VObject_Component_VTodoTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider timeRangeTestData
*/
public function testInTimeRange(Sabre_VObject_Component_VTodo $vtodo,$start,$end,$outcome) {
$this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
}
public function timeRangeTestData() {
$tests = array();
$vtodo = Sabre_VObject_Component::create('VTODO');
$vtodo->DTSTART = '20111223T120000Z';
$tests[] = array($vtodo, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo2 = clone $vtodo;
$vtodo2->DURATION = 'P1D';
$tests[] = array($vtodo2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo3 = clone $vtodo;
$vtodo3->DUE = '20111225';
$tests[] = array($vtodo3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo4 = Sabre_VObject_Component::create('VTODO');
$vtodo4->DUE = '20111225';
$tests[] = array($vtodo4, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo4, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo5 = Sabre_VObject_Component::create('VTODO');
$vtodo5->COMPLETED = '20111225';
$tests[] = array($vtodo5, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo5, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo6 = Sabre_VObject_Component::create('VTODO');
$vtodo6->CREATED = '20111225';
$tests[] = array($vtodo6, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo6, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo7 = Sabre_VObject_Component::create('VTODO');
$vtodo7->CREATED = '20111225';
$vtodo7->COMPLETED = '20111226';
$tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false);
$vtodo7 = Sabre_VObject_Component::create('VTODO');
$tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true);
$tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2011-11-01'), true);
return $tests;
}
}

View file

@ -1,117 +0,0 @@
<?php
require_once 'Sabre/CalDAV/TestUtil.php';
class Sabre_VObject_DateTimeParserTest extends PHPUnit_Framework_TestCase {
function testParseICalendarDuration() {
$this->assertEquals('+1 weeks', Sabre_VObject_DateTimeParser::parseDuration('P1W',true));
$this->assertEquals('+5 days', Sabre_VObject_DateTimeParser::parseDuration('P5D',true));
$this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', Sabre_VObject_DateTimeParser::parseDuration('P5DT3H50M12S',true));
$this->assertEquals('-1 weeks 50 minutes', Sabre_VObject_DateTimeParser::parseDuration('-P1WT50M',true));
$this->assertEquals('+50 days 3 hours 2 seconds', Sabre_VObject_DateTimeParser::parseDuration('+P50DT3H2S',true));
$this->assertEquals(new DateInterval('PT0S'), Sabre_VObject_DateTimeParser::parseDuration('PT0S'));
}
function testParseICalendarDurationDateInterval() {
$expected = new DateInterval('P7D');
$this->assertEquals($expected, Sabre_VObject_DateTimeParser::parseDuration('P1W'));
$this->assertEquals($expected, Sabre_VObject_DateTimeParser::parse('P1W'));
$expected = new DateInterval('PT3M');
$expected->invert = true;
$this->assertEquals($expected, Sabre_VObject_DateTimeParser::parseDuration('-PT3M'));
}
/**
* @expectedException Sabre_DAV_Exception_BadRequest
*/
function testParseICalendarDurationFail() {
Sabre_VObject_DateTimeParser::parseDuration('P1X',true);
}
function testParseICalendarDateTime() {
$dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405');
$compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
$this->assertEquals($compare, $dateTime);
}
/**
* @depends testParseICalendarDateTime
* @expectedException Sabre_DAV_Exception_BadRequest
*/
function testParseICalendarDateTimeBadFormat() {
$dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405 ');
}
/**
* @depends testParseICalendarDateTime
*/
function testParseICalendarDateTimeUTC() {
$dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405Z');
$compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
$this->assertEquals($compare, $dateTime);
}
/**
* @depends testParseICalendarDateTime
*/
function testParseICalendarDateTimeUTC2() {
$dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20101211T160000Z');
$compare = new DateTime('2010-12-11 16:00:00',new DateTimeZone('UTC'));
$this->assertEquals($compare, $dateTime);
}
/**
* @depends testParseICalendarDateTime
*/
function testParseICalendarDateTimeCustomTimeZone() {
$dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam'));
$compare = new DateTime('2010-03-16 13:14:05',new DateTimeZone('UTC'));
$this->assertEquals($compare, $dateTime);
}
function testParseICalendarDate() {
$dateTime = Sabre_VObject_DateTimeParser::parseDate('20100316');
$expected = new DateTime('2010-03-16 00:00:00',new DateTimeZone('UTC'));
$this->assertEquals($expected, $dateTime);
$dateTime = Sabre_VObject_DateTimeParser::parse('20100316');
$this->assertEquals($expected, $dateTime);
}
/**
* @depends testParseICalendarDate
* @expectedException Sabre_DAV_Exception_BadRequest
*/
function testParseICalendarDateBadFormat() {
$dateTime = Sabre_VObject_DateTimeParser::parseDate('20100316T141405');
}
}

View file

@ -1,12 +0,0 @@
<?php
class Sabre_VObject_Issue153Test extends PHPUnit_Framework_TestCase {
function testRead() {
$obj = Sabre_VObject_Reader::read(file_get_contents(dirname(__FILE__) . '/issue153.vcf'));
$this->assertEquals('Test Benutzer', (string)$obj->fn);
}
}

View file

@ -4,9 +4,9 @@ define('SABRE_MYSQLDSN','mysql:host=127.0.0.1;dbname=sabredav');
define('SABRE_MYSQLUSER','root'); define('SABRE_MYSQLUSER','root');
define('SABRE_MYSQLPASS',''); define('SABRE_MYSQLPASS','');
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . dirname(__FILE__) . '/../lib/' . PATH_SEPARATOR . get_include_path()); set_include_path(__DIR__ . '/../lib/' . PATH_SEPARATOR . __DIR__ . PATH_SEPARATOR . get_include_path());
include 'Sabre/autoload.php'; include __DIR__ . '/../vendor/autoload.php';
include 'Sabre/DAVServerTest.php'; include 'Sabre/DAVServerTest.php';
date_default_timezone_set('GMT'); date_default_timezone_set('GMT');

View file

@ -1,164 +0,0 @@
<?php
$a = get_app();
$uri = parse_url($a->get_baseurl());
$path = "/";
if (strlen($uri["path"]) > 1) {
$path = $uri["path"] . "/";
}
define("CALDAV_SQL_DB", "");
define("CALDAV_SQL_PREFIX", "dav_");
define("CALDAV_URL_PREFIX", $path . "dav/");
define("CALDAV_NAMESPACE_PRIVATE", 1);
define("CALDAV_NAMESPACE_FRIENDICA_NATIVE", 2);
define("CALDAV_FRIENDICA_MINE", 1);
define("CALDAV_FRIENDICA_CONTACTS", 2);
define("CARDDAV_NAMESPACE_COMMUNITYCONTACTS", 1);
define("CARDDAV_NAMESPACE_PHONECONTACTS", 2);
define("CALDAV_DB_VERSION", 1);
/**
* @return int
*/
function getCurMicrotime () {
list($usec, $sec) = explode(" ", microtime());
return sprintf("%14.0f", $sec * 10000 + $usec * 10000);
} // function getCurMicrotime
/**
*
*/
function debug_time() {
$cur = getCurMicrotime();
if ($GLOBALS["debug_time_last"] > 0) {
echo "Zeit: " . ($cur - $GLOBALS["debug_time_last"]) . "<br>\n";
}
$GLOBALS["debug_time_last"] = $cur;
}
/**
* @param string $username
* @return int|null
*/
function dav_compat_username2id($username = "")
{
$x = q("SELECT `uid` FROM user WHERE nickname='%s' AND account_removed = 0 AND account_expired = 0", dbesc($username));
if (count($x) == 1) return $x[0]["uid"];
return null;
}
/**
* @param int $id
* @return string
*/
function dav_compat_id2username($id = 0)
{
$x = q("SELECT `nickname` FROM user WHERE uid = %i AND account_removed = 0 AND account_expired = 0", IntVal($id));
if (count($x) == 1) return $x[0]["nickname"];
return "";
}
/**
* @return int
*/
function dav_compat_get_curr_user_id() {
$a = get_app();
return IntVal($a->user["uid"]);
}
/**
* @param string $principalUri
* @return int|null
*/
function dav_compat_principal2uid($principalUri = "")
{
if (strlen($principalUri) == 0) return null;
if ($principalUri[0] == "/") $principalUri = substr($principalUri, 1);
if (strpos($principalUri, "principals/users/") !== 0) return null;
$username = substr($principalUri, strlen("principals/users/"));
return dav_compat_username2id($username);
}
/**
* @param string $name
* @return null|string
*/
function dav_compat_getRequestVar($name = "") {
if (x($_REQUEST, $name)) return $_REQUEST[$name];
else return null;
}
/**
* @param $text
* @return null|string
*/
function dav_compat_parse_text_serverside($text)
{
return dav_compat_getRequestVar($text);
}
/**
* @param string $uri
*/
function dav_compat_redirect($uri = "") {
goaway($uri);
}
/**
* @param int $user_id
* @param int $namespace
* @param int $namespace_id
* @return AnimexxCalSource
* @throws Exception
*/
function wdcal_calendar_factory($user_id, $namespace, $namespace_id)
{
switch ($namespace) {
case CALDAV_NAMESPACE_PRIVATE:
return new AnimexxCalSourcePrivate($user_id, $namespace_id);
case CALDAV_NAMESPACE_FRIENDICA_NATIVE:
return new FriendicaCalSourceEvents($user_id, $namespace_id);
}
throw new Exception("Calendar Namespace not found");
}
/**
*/
function wdcal_create_std_calendars()
{
$a = get_app();
if (!local_user()) return;
$cals = q("SELECT * FROM %s%scalendars WHERE `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_PRIVATE);
if (count($cals) == 0) {
$maxid = q("SELECT MAX(`namespace_id`) maxid FROM %s%scalendars WHERE `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE);
if (!$maxid) {
notification("Something went wrong when trying to create your calendar.");
goaway("/");
killme();
}
$nextid = IntVal($maxid[0]["maxid"]) + 1;
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $nextid, $a->user["uid"], dbesc(t("Private Calendar")), dbesc($a->timezone)
);
}
$cals = q("SELECT * FROM %s%scalendars WHERE `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_FRIENDICA_NATIVE);
if (count($cals) < 2) {
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_MINE, $a->user["uid"], dbesc(t("Friendica Events: Mine")), dbesc($a->timezone)
);
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_CONTACTS, $a->user["uid"], dbesc(t("Friendica Events: Contacts")), dbesc($a->timezone)
);
}
}

View file

@ -6,8 +6,8 @@
<title>Really Simple Color Picker</title> <title>Really Simple Color Picker</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script language="javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="jquery.colorPicker.min.js"/></script> <script type="text/javascript" src="jquery.colorPicker.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
//Run the code when document ready //Run the code when document ready

View file

@ -1,6 +1,17 @@
<?php <?php
define("DAV_ACL_READ", "{DAV:}read");
define("DAV_ACL_WRITE", "{DAV:}write");
define("DAV_DISPLAYNAME", "{DAV:}displayname");
define("DAV_CALENDARCOLOR", "{http://apple.com/ns/ical/}calendar-color");
class DAVVersionMismatchException extends Exception
{
}
class vcard_source_data_email class vcard_source_data_email
{ {
public $email, $type; public $email, $type;
@ -83,16 +94,13 @@ class vcard_source_data
/** @var array|vcard_source_data_email[] $email */ /** @var array|vcard_source_data_email[] $email */
public $emails; public $emails;
/** @var array|vcard_source_data_addresses[] $addresses */ /** @var array|vcard_source_data_address[] $addresses */
public $addresses; public $addresses;
/** @var vcard_source_data_photo */ /** @var vcard_source_data_photo */
public $photo; public $photo;
} }
;
/** /**
* @param vcard_source_data $vcardsource * @param vcard_source_data $vcardsource
* @return string * @return string
@ -136,41 +144,6 @@ function vcard_source_compile($vcardsource)
} }
/**
* @param array $start
* @param array $end
* @param bool $allday
* @return vevent
*/
function dav_create_vevent($start, $end, $allday)
{
if ($end["year"] < $start["year"] ||
($end["year"] == $start["year"] && $end["month"] < $start["month"]) ||
($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] < $start["day"]) ||
($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] < $start["hour"]) ||
($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] < $start["minute"]) ||
($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] == $start["minute"] && $end["second"] < $start["second"])
) {
$end = $start;
} // DTEND muss <= DTSTART
$vevent = new vevent();
if ($allday) {
$vevent->setDtstart($start["year"], $start["month"], $start["day"], FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE"));
$end = IntVal(mktime(0, 0, 0, $end["month"], $end["day"], $end["year"]) + 3600 * 24);
// If a DST change occurs on the current day
$end += IntVal(date("Z", ($end - 3600 * 24)) - date("Z", $end));
$vevent->setDtend(date("Y", $end), date("m", $end), date("d", $end), FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE"));
} else {
$vevent->setDtstart($start["year"], $start["month"], $start["day"], $start["hour"], $start["minute"], $start["second"], FALSE, array("VALUE"=> "DATE-TIME"));
$vevent->setDtend($end["year"], $end["month"], $end["day"], $end["hour"], $end["minute"], $end["second"], FALSE, array("VALUE"=> "DATE-TIME"));
}
return $vevent;
}
/** /**
* @param int $phpDate (UTC) * @param int $phpDate (UTC)
* @return string (Lokalzeit) * @return string (Lokalzeit)
@ -213,460 +186,190 @@ function wdcal_mySql2icalTime($myqlDate)
*/ */
function icalendar_sanitize_string($str = "") function icalendar_sanitize_string($str = "")
{ {
$str = str_replace("\r\n", "\n", $str); return preg_replace("/[\\r\\n]+/siu", "\r\n", $str);
$str = str_replace("\n\r", "\n", $str);
$str = str_replace("\r", "\n", $str);
return $str;
} }
/** /**
* @param DBClass_friendica_calendars $calendar * @return Sabre_CalDAV_AnimexxCalendarRootNode
* @param DBClass_friendica_calendarobjects $calendarobject
*/ */
function renderCalDavEntry_data(&$calendar, &$calendarobject) function dav_createRootCalendarNode()
{ {
$a = get_app(); $backends = array(Sabre_CalDAV_Backend_Private::getInstance());
foreach ($GLOBALS["CALDAV_PRIVATE_SYSTEM_BACKENDS"] as $backendclass) $backends[] = $backendclass::getInstance();
return new Sabre_CalDAV_AnimexxCalendarRootNode(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), $backends);
}
$v = new vcalendar(); /**
$v->setConfig('unique_id', $a->get_hostname()); * @return Sabre_CardDAV_AddressBookRootFriendica
$v->parse($calendarobject->calendardata); */
$v->sort(); function dav_createRootContactsNode()
{
$backends = array(Sabre_CardDAV_Backend_Std::getInstance());
foreach ($GLOBALS["CARDDAV_PRIVATE_SYSTEM_BACKENDS"] as $backendclass) $backends[] = $backendclass::getInstance();
$eventArray = $v->selectComponents(2009, 1, 1, date("Y") + 2, 12, 30); return new Sabre_CardDAV_AddressBookRootFriendica(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), $backends);
$start_min = $end_max = "";
$allday = $summary = $vevent = $rrule = $color = $start = $end = null;
$location = $description = "";
foreach ($eventArray as $yearArray) {
foreach ($yearArray as $monthArray) {
foreach ($monthArray as $day => $dailyEventsArray) {
foreach ($dailyEventsArray as $vevent) {
/** @var $vevent vevent */
$start = "";
$rrule = "NULL";
$allday = 0;
$dtstart = $vevent->getProperty('X-CURRENT-DTSTART');
if (is_array($dtstart)) {
$start = "'" . $dtstart[1] . "'";
if (strpos($dtstart[1], ":") === false) $allday = 1;
} else {
$dtstart = $vevent->getProperty('dtstart');
if (isset($dtstart["day"]) && $dtstart["day"] == $day) { // Mehrtägige Events nur einmal rein
if (isset($dtstart["hour"])) $start = "'" . $dtstart["year"] . "-" . $dtstart["month"] . "-" . $dtstart["day"] . " " . $dtstart["hour"] . ":" . $dtstart["minute"] . ":" . $dtstart["secont"] . "'";
else {
$start = "'" . $dtstart["year"] . "-" . $dtstart["month"] . "-" . $dtstart["day"] . " 00:00:00'";
$allday = 1;
}
}
}
$dtend = $vevent->getProperty('X-CURRENT-DTEND');
if (is_array($dtend)) {
$end = "'" . $dtend[1] . "'";
if (strpos($dtend[1], ":") === false) $allday = 1;
} else {
$dtend = $vevent->getProperty('dtend');
if (isset($dtend["hour"])) $end = "'" . $dtend["year"] . "-" . $dtend["month"] . "-" . $dtend["day"] . " " . $dtend["hour"] . ":" . $dtend["minute"] . ":" . $dtend["second"] . "'";
else {
$end = "'" . $dtend["year"] . "-" . $dtend["month"] . "-" . $dtend["day"] . " 00:00:00' - INTERVAL 1 SECOND";
$allday = 1;
}
}
$summary = $vevent->getProperty('summary');
$description = $vevent->getProperty('description');
$location = $vevent->getProperty('location');
$rrule_prob = $vevent->getProperty('rrule');
if ($rrule_prob != null) {
$rrule = $vevent->createRrule();
$rrule = "'" . dbesc($rrule) . "'";
}
$color_ = $vevent->getProperty("X-ANIMEXX-COLOR");
$color = (is_array($color_) ? $color_[1] : "NULL");
if ($start_min == "" || preg_replace("/[^0-9]/", "", $start) < preg_replace("/[^0-9]/", "", $start_min)) $start_min = $start;
if ($end_max == "" || preg_replace("/[^0-9]/", "", $end) > preg_replace("/[^0-9]/", "", $start_min)) $end_max = $end;
}
}
}
}
if ($start_min != "") {
if ($allday && mb_strlen($end_max) == 12) {
$x = explode("-", str_replace("'", "", $end_max));
$time = mktime(0, 0, 0, IntVal($x[1]), IntVal($x[2]), IntVal($x[0]));
$end_max = date("'Y-m-d H:i:s'", ($time - 1));
}
q("INSERT INTO %s%sjqcalendar (`uid`, `namespace`, `namespace_id`, `ical_uri`, `Subject`, `Location`, `Description`, `StartTime`, `EndTime`, `IsAllDayEvent`, `RecurringRule`, `Color`)
VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', %s, %s, %d, '%s', '%s')",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
IntVal($calendar->uid), IntVal($calendarobject->namespace), IntVal($calendarobject->namespace_id), dbesc($calendarobject->uri), dbesc($summary),
dbesc($location), dbesc(str_replace("\\n", "\n", $description)), $start_min, $end_max, IntVal($allday), dbesc($rrule), dbesc($color)
);
foreach ($vevent->components as $comp) {
/** @var $comp calendarComponent */
$trigger = $comp->getProperty("TRIGGER");
$sql_field = ($trigger["relatedStart"] ? $start : $end);
$sql_op = ($trigger["before"] ? "DATE_SUB" : "DATE_ADD");
$num = "";
$rel_type = "";
$rel_value = 0;
if (isset($trigger["second"])) {
$num = IntVal($trigger["second"]) . " SECOND";
$rel_type = "second";
$rel_value = IntVal($trigger["second"]);
}
if (isset($trigger["minute"])) {
$num = IntVal($trigger["minute"]) . " MINUTE";
$rel_type = "minute";
$rel_value = IntVal($trigger["minute"]);
}
if (isset($trigger["hour"])) {
$num = IntVal($trigger["hour"]) . " HOUR";
$rel_type = "hour";
$rel_value = IntVal($trigger["hour"]);
}
if (isset($trigger["day"])) {
$num = IntVal($trigger["day"]) . " DAY";
$rel_type = "day";
$rel_value = IntVal($trigger["day"]);
}
if (isset($trigger["week"])) {
$num = IntVal($trigger["week"]) . " WEEK";
$rel_type = "week";
$rel_value = IntVal($trigger["week"]);
}
if (isset($trigger["month"])) {
$num = IntVal($trigger["month"]) . " MONTH";
$rel_type = "month";
$rel_value = IntVal($trigger["month"]);
}
if (isset($trigger["year"])) {
$num = IntVal($trigger["year"]) . " YEAR";
$rel_type = "year";
$rel_value = IntVal($trigger["year"]);
}
if ($trigger["before"]) $rel_value *= -1;
if ($rel_type != "") {
$not_date = "$sql_op($sql_field, INTERVAL $num)";
q("INSERT INTO %s%snotifications (`uid`, `ical_uri`, `rel_type`, `rel_value`, `alert_date`, `notified`) VALUES ('%s', '%s', '%s', '%s', %s, IF(%s < NOW(), 1, 0))",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
IntVal($calendar->uid), dbesc($calendarobject->uri), dbesc($rel_type), IntVal($rel_value), $not_date, $not_date);
}
}
}
} }
/** /**
* * @param bool $force_authentication
* @param bool $needs_caldav
* @param bool $needs_carddav
* @return Sabre_DAV_Server
*/ */
function renderAllCalDavEntries() function dav_create_server($force_authentication = false, $needs_caldav = true, $needs_carddav = true)
{ {
q("DELETE FROM %s%sjqcalendar", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); $arr = array(
q("DELETE FROM %s%snotifications", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); new Sabre_DAV_SimpleCollection('principals', array(
$calendars = q("SELECT * FROM %s%scalendars", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); new Sabre_CalDAV_Principal_Collection(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), "principals/users"),
$anz = count($calendars); )),
$i = 0;
foreach ($calendars as $calendar) {
$cal = new DBClass_friendica_calendars($calendar);
$i++;
if (($i % 100) == 0) echo "$i / $anz\n";
$calobjs = q("SELECT * FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["namespace"]), IntVal($calendar["namespace_id"]));
foreach ($calobjs as $calobj) {
$obj = new DBClass_friendica_calendarobjects($calobj);
renderCalDavEntry_data($cal, $obj);
}
}
}
/**
* @param string $uri
* @return bool
*/
function renderCalDavEntry_uri($uri)
{
q("DELETE FROM %s%sjqcalendar WHERE `ical_uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri));
q("DELETE FROM %s%snotifications WHERE `ical_uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri));
$calobj = q("SELECT * FROM %s%scalendarobjects WHERE `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri));
if (count($calobj) == 0) return false;
$cal = new DBClass_friendica_calendarobjects($calobj[0]);
$calendars = q("SELECT * FROM %s%scalendars WHERE `namespace`=%d AND `namespace_id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($cal->namespace), IntVal($cal->namespace_id));
$calendar = new DBClass_friendica_calendars($calendars[0]);
renderCalDavEntry_data($calendar, $cal);
return true;
}
/**
* @param $user_id
* @return array|DBClass_friendica_calendars[]
*/
function dav_getMyCals($user_id)
{
$d = q("SELECT * FROM %s%scalendars WHERE `uid` = %d ORDER BY `calendarorder` ASC",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($user_id), CALDAV_NAMESPACE_PRIVATE
); );
$cals = array(); if ($needs_caldav) $arr[] = dav_createRootCalendarNode();
foreach ($d as $e) $cals[] = new DBClass_friendica_calendars($e); if ($needs_carddav) $arr[] = dav_createRootContactsNode();
return $cals;
}
/** $tree = new Sabre_DAV_SimpleCollection('root', $arr);
* @param mixed $obj
* @return string // The object tree needs in turn to be passed to the server class
*/ $server = new Sabre_DAV_Server($tree);
function wdcal_jsonp_encode($obj)
{ if (CALDAV_URL_PREFIX != "") $server->setBaseUri(CALDAV_URL_PREFIX);
$str = json_encode($obj);
if (isset($_REQUEST["callback"])) { $authPlugin = new Sabre_DAV_Auth_Plugin(Sabre_DAV_Auth_Backend_Std::getInstance(), DAV_APPNAME);
$str = $_REQUEST["callback"] . "(" . $str . ")"; $server->addPlugin($authPlugin);
if ($needs_caldav) {
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
} }
return $str; if ($needs_carddav) {
} $carddavPlugin = new Sabre_CardDAV_Plugin();
$server->addPlugin($carddavPlugin);
/**
* @param string $day
* @param int $weekstartday
* @param int $num_days
* @param string $type
* @return array
*/
function wdcal_get_list_range_params($day, $weekstartday, $num_days, $type)
{
$phpTime = IntVal($day);
switch ($type) {
case "month":
$st = mktime(0, 0, 0, date("m", $phpTime), 1, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime) + 1, 1, date("Y", $phpTime));
break;
case "week":
//suppose first day of a week is monday
$monday = date("d", $phpTime) - date('N', $phpTime) + 1;
//echo date('N', $phpTime);
$st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), $monday + 7, date("Y", $phpTime));
break;
case "multi_days":
//suppose first day of a week is monday
$monday = date("d", $phpTime) - date('N', $phpTime) + $weekstartday;
//echo date('N', $phpTime);
$st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), $monday + $num_days, date("Y", $phpTime));
break;
case "day":
$st = mktime(0, 0, 0, date("m", $phpTime), date("d", $phpTime), date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), date("d", $phpTime) + 1, date("Y", $phpTime));
break;
default:
return array(0, 0);
} }
return array($st, $et);
}
if ($GLOBALS["CALDAV_ACL_PLUGIN_CLASS"] != "") {
$aclPlugin = new $GLOBALS["CALDAV_ACL_PLUGIN_CLASS"]();
$aclPlugin->defaultUsernamePath = "principals/users";
$server->addPlugin($aclPlugin);
/**
* @param string $uri
* @param string $recurr_uri
* @param int $uid
* @param string $timezone
* @param string $goaway_url
* @return string
*/
function wdcal_postEditPage($uri, $recurr_uri = "", $uid = 0, $timezone = "", $goaway_url = "")
{
$uid = IntVal($uid);
$localization = wdcal_local::getInstanceByUser($uid);
if (isset($_REQUEST["allday"])) {
$start = $localization->date_parseLocal($_REQUEST["start_date"] . " 00:00");
$end = $localization->date_parseLocal($_REQUEST["end_date"] . " 20:00");
$isallday = true;
} else { } else {
$start = $localization->date_parseLocal($_REQUEST["start_date"] . " " . $_REQUEST["start_time"]); $aclPlugin = new Sabre_DAVACL_Plugin();
$end = $localization->date_parseLocal($_REQUEST["end_date"] . " " . $_REQUEST["end_time"]); $aclPlugin->defaultUsernamePath = "principals/users";
$isallday = false; $server->addPlugin($aclPlugin);
} }
if ($uri == "new") { if ($force_authentication) $server->broadcastEvent('beforeMethod', array("GET", "/")); // Make it authenticate
$cals = dav_getMyCals($uid);
foreach ($cals as $c) {
$cs = wdcal_calendar_factory($uid, $c->namespace, $c->namespace_id);
$p = $cs->getPermissionsCalendar($uid);
if ($p["write"]) try { return $server;
$cs->addItem($start, $end, dav_compat_getRequestVar("subject"), $isallday, dav_compat_parse_text_serverside("wdcal_desc"), }
dav_compat_getRequestVar("location"), dav_compat_getRequestVar("color"), $timezone,
isset($_REQUEST["notification"]), $_REQUEST["notification_type"], $_REQUEST["notification_value"]);
} catch (Exception $e) {
notification(t("Error") . ": " . $e);
}
dav_compat_redirect($goaway_url);
}
} else {
$cals = dav_getMyCals($uid); /**
foreach ($cals as $c) { * @param Sabre_DAV_Server $server
$cs = wdcal_calendar_factory($uid, $c->namespace, $c->namespace_id); * @param string $with_privilege
$p = $cs->getPermissionsItem($uid, $uri, $recurr_uri); * @return array|Sabre_CalDAV_Calendar[]
if ($p["write"]) try { */
$cs->updateItem($uri, $start, $end, function dav_get_current_user_calendars(&$server, $with_privilege = "")
dav_compat_getRequestVar("subject"), $isallday, dav_compat_parse_text_serverside("wdcal_desc"), {
dav_compat_getRequestVar("location"), dav_compat_getRequestVar("color"), $timezone, if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
isset($_REQUEST["notification"]), $_REQUEST["notification_type"], $_REQUEST["notification_value"]);
} catch (Exception $e) { $a = get_app();
notification(t("Error") . ": " . $e); $calendar_path = "/calendars/" . strtolower($a->user["nickname"]) . "/";
}
dav_compat_redirect($goaway_url); /** @var Sabre_CalDAV_AnimexxUserCalendars $tree */
$tree = $server->tree->getNodeForPath($calendar_path);
/** @var array|Sabre_CalDAV_Calendar[] $calendars */
$children = $tree->getChildren();
$calendars = array();
/** @var Sabre_DAVACL_Plugin $aclplugin */
$aclplugin = $server->getPlugin("acl");
foreach ($children as $child) if (is_a($child, "Sabre_CalDAV_Calendar") || is_subclass_of($child, "Sabre_CalDAV_Calendar")) {
if ($with_privilege != "") {
$caluri = $calendar_path . $child->getName();
if ($aclplugin->checkPrivileges($caluri, $with_privilege, Sabre_DAVACL_Plugin::R_PARENT, false)) $calendars[] = $child;
} else {
$calendars[] = $child;
} }
} }
return $calendars;
} }
/** /**
* * @param Sabre_DAV_Server $server
* @param Sabre_CalDAV_Calendar $calendar
* @param string $calendarobject_uri
* @param string $with_privilege
* @return null|Sabre\VObject\Component\VCalendar
*/ */
function wdcal_print_feed($base_path = "") function dav_get_current_user_calendarobject(&$server, &$calendar, $calendarobject_uri, $with_privilege = "")
{ {
$user_id = dav_compat_get_curr_user_id(); $obj = $calendar->getChild($calendarobject_uri);
$cals = array();
if (isset($_REQUEST["cal"])) foreach ($_REQUEST["cal"] as $c) {
$x = explode("-", $c);
$calendarSource = wdcal_calendar_factory($user_id, $x[0], $x[1]);
$calp = $calendarSource->getPermissionsCalendar($user_id);
if ($calp["read"]) $cals[] = $calendarSource;
}
$ret = null; if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
/** @var $cals array|AnimexxCalSource[] */
$method = $_GET["method"]; $a = get_app();
switch ($method) { $uri = "/calendars/" . strtolower($a->user["nickname"]) . "/" . $calendar->getName() . "/" . $calendarobject_uri;
case "add":
$cs = null;
foreach ($cals as $c) if ($cs == null) {
$x = $c->getPermissionsCalendar($user_id);
if ($x["read"]) $cs = $c;
}
if ($cs == null) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
try {
$start = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarStartTime"]));
$end = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarEndTime"]));
$newuri = $cs->addItem($start, $end, $_REQUEST["CalendarTitle"], $_REQUEST["IsAllDayEvent"]);
$ret = array(
'IsSuccess' => true,
'Msg' => 'add success',
'Data' => $newuri,
);
} catch (Exception $e) { /** @var Sabre_DAVACL_Plugin $aclplugin */
$ret = array( $aclplugin = $server->getPlugin("acl");
'IsSuccess' => false, if (!$aclplugin->checkPrivileges($uri, $with_privilege, Sabre_DAVACL_Plugin::R_PARENT, false)) return null;
'Msg' => $e->__toString(),
);
}
break;
case "list":
$weekstartday = (isset($_REQUEST["weekstartday"]) ? IntVal($_REQUEST["weekstartday"]) : 1); // 1 = Monday
$num_days = (isset($_REQUEST["num_days"]) ? IntVal($_REQUEST["num_days"]) : 7);
$ret = null;
$date = wdcal_get_list_range_params($_REQUEST["showdate"], $weekstartday, $num_days, $_REQUEST["viewtype"]); $data = $obj->get();
$ret = array(); $vObject = Sabre\VObject\Reader::read($data);
$ret['events'] = array();
$ret["issort"] = true;
$ret["start"] = $date[0];
$ret["end"] = $date[1];
$ret['error'] = null;
foreach ($cals as $c) { return $vObject;
$events = $c->listItemsByRange($date[0], $date[1], $base_path);
$ret["events"] = array_merge($ret["events"], $events);
}
$tmpev = array();
foreach ($ret["events"] as $e) {
if (!isset($tmpev[$e["start"]])) $tmpev[$e["start"]] = array();
$tmpev[$e["start"]][] = $e;
}
ksort($tmpev);
$ret["events"] = array();
foreach ($tmpev as $e) foreach ($e as $f) $ret["events"][] = $f;
break;
case "update":
$found = false;
$start = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarStartTime"]));
$end = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarEndTime"]));
foreach ($cals as $c) try {
$permissions_item = $c->getPermissionsItem($user_id, $_REQUEST["calendarId"], "");
if ($permissions_item["write"]) {
$c->updateItem($_REQUEST["calendarId"], $start, $end);
$found = true;
}
} catch (Exception $e) {
}
;
if ($found) {
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} else {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
try {
} catch (Exception $e) {
$ret = array(
'IsSuccess' => false,
'Msg' => $e->__toString(),
);
}
break;
case "remove":
$found = false;
foreach ($cals as $c) try {
$permissions_item = $c->getPermissionsItem($user_id, $_REQUEST["calendarId"], "");
if ($permissions_item["write"]) $c->removeItem($_REQUEST["calendarId"]);
} catch (Exception $e) {
}
if ($found) {
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} else {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
break;
}
echo wdcal_jsonp_encode($ret);
killme();
} }
/**
* @param Sabre_DAV_Server $server
* @param int $id
* @param string $with_privilege
* @return null|Sabre_CalDAV_Calendar
*/
function dav_get_current_user_calendar_by_id(&$server, $id, $with_privilege = "")
{
$calendars = dav_get_current_user_calendars($server, $with_privilege);
$calendar = null;
foreach ($calendars as $cal) {
$prop = $cal->getProperties(array("id"));
if (isset($prop["id"]) && $prop["id"] == $id) $calendar = $cal;
}
return $calendar;
}
/**
* @param string $uid
* @return Sabre\VObject\Component\VCalendar $vObject
*/
function dav_create_empty_vevent($uid = "")
{
if ($uid == "") $uid = uniqid();
return Sabre\VObject\Reader::read("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//" . DAV_APPNAME . "//DAV-Plugin//EN\r\nBEGIN:VEVENT\r\nUID:" . $uid . "@" . dav_compat_get_hostname() .
"\r\nDTSTAMP:" . date("Ymd") . "T" . date("His") . "Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
}
/**
* @param Sabre\VObject\Component\VCalendar $vObject
* @return Sabre\VObject\Component\VEvent|null
*/
function dav_get_eventComponent(&$vObject)
{
$component = null;
$componentType = "";
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if ($componentType != "VEVENT") return null;
return $component;
}

View file

@ -0,0 +1,187 @@
<?php
/**
* @param Sabre\VObject\Component\VAlarm $alarm
* @param Sabre\VObject\Component\VEvent|Sabre\VObject\Component\VTodo $parent
* @return DateTime|null
* @throws Sabre_DAV_Exception
*/
function renderCalDavEntry_calcalarm(&$alarm, &$parent)
{
$trigger = $alarm->__get("TRIGGER");
if (!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
$triggerDuration = Sabre\VObject\DateTimeParser::parseDuration($trigger->value);
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
if ($related === 'START') {
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $parent->__get("DTSTART");
$effectiveTrigger = $dtstart->getDateTime();
$effectiveTrigger->add($triggerDuration);
} else {
if ($parent->name === 'VTODO') {
$endProp = 'DUE';
} else {
$endProp = 'DTEND';
}
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $parent->__get("DTSTART");
if (isset($parent->$endProp)) {
$effectiveTrigger = clone $parent->$endProp->getDateTime();
$effectiveTrigger->add($triggerDuration);
} elseif ($parent->__get("DURATION") != "") {
$effectiveTrigger = clone $dtstart->getDateTime();
$duration = Sabre\VObject\DateTimeParser::parseDuration($parent->__get("DURATION"));
$effectiveTrigger->add($duration);
$effectiveTrigger->add($triggerDuration);
} else {
$effectiveTrigger = clone $dtstart->getDateTime();
$effectiveTrigger->add($triggerDuration);
}
}
} else {
// ??? @TODO
$effectiveTrigger = $trigger->getDateTime();
}
return $effectiveTrigger;
}
/**
* @param array $calendar
* @param array $calendarobject
* @throws Sabre_DAV_Exception_BadRequest
* @return void
*/
function renderCalDavEntry_data(&$calendar, &$calendarobject)
{
/** @var Sabre\VObject\Component\VCalendar $vObject */
$vObject = Sabre\VObject\Reader::read($calendarobject["calendardata"]);
$componentType = null;
/** @var Sabre\VObject\Component\VEvent $component */
$component = null;
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if (!$componentType) {
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
}
$timezoneOffset = date("P"); // @TODO Get the actual timezone from the event
if ($componentType !== 'VEVENT') return;
$event = array(
"description" => ($component->__get("DESCRIPTION") ? $component->__get("DESCRIPTION")->value : null),
"summary" => ($component->__get("SUMMARY") ? $component->__get("SUMMARY")->value : null),
"location" => ($component->__get("LOCATION") ? $component->__get("LOCATION")->value : null),
"color" => ($component->__get("X-ANIMEXX-COLOR") ? $component->__get("X-ANIMEXX-COLOR")->value : null),
);
$recurring = ($component->__get("RRULE") ? 1 : 0);
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$allday = ($dtstart->getDateType() == Sabre\VObject\Property\DateTime::DATE ? 1 : 0);
/** @var array|Sabre\VObject\Component\VAlarm[] $alarms */
$alarms = array();
foreach ($component->getComponents() as $a_component) if ($a_component->name == "VALARM") {
/** var Sabre\VObject\Component\VAlarm $component */
$alarms[] = $a_component;
}
$it = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
$last_end = 0;
$max_ts = mktime(0, 0, 0, 1, 1, CALDAV_MAX_YEAR * 1);
$first = true;
while ($it->valid() && $last_end < $max_ts && ($recurring || $first)) {
$first = false;
$last_end = $it->getDtEnd()->getTimestamp();
$start = $it->getDtStart()->getTimestamp();
q("INSERT INTO %s%sjqcalendar (`calendar_id`, `calendarobject_id`, `Summary`, `StartTime`, `EndTime`, `IsEditable`, `IsAllDayEvent`, `IsRecurring`, `Color`) VALUES
(%d, %d, '%s', CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d, %d, %d, '%s')",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), dbesc($event["summary"]), date("Y-m-d H:i:s", $start),
date("Y-m-d H:i:s", $last_end), 1, $allday, $recurring, dbesc(substr($event["color"], 1))
);
foreach ($alarms as $alarm) {
$alarm = renderCalDavEntry_calcalarm($alarm, $component);
$notified = ($alarm->getTimestamp() < time() ? 1 : 0);
q("INSERT INTO %s%snotifications (`calendar_id`, `calendarobject_id`, `alert_date`, `notified`) VALUES (%d, %d, CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), $alarm->format("Y-m-d H:i:s"), $notified
);
}
$it->next();
}
return;
}
/**
*
*/
function renderAllCalDavEntries()
{
q("DELETE FROM %s%sjqcalendar", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
q("DELETE FROM %s%snotifications", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
$calendars = q("SELECT * FROM %s%scalendars", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
$anz = count($calendars);
$i = 0;
foreach ($calendars as $calendar) {
$i++;
if (($i % 100) == 0) echo "$i / $anz\n";
$calobjs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]));
foreach ($calobjs as $calobj) renderCalDavEntry_data($calendar, $calobj);
}
}
/**
* @param string $uri
* @return bool
*/
function renderCalDavEntry_uri($uri)
{
$calobj = q("SELECT * FROM %s%scalendarobjects WHERE `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri));
if (count($calobj) == 0) return false;
q("DELETE FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]), IntVal($calobj[0]["id"]));
q("DELETE FROM %s%snotifications WHERE `calendar_id` = %d AND `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]), IntVal($calobj[0]["id"]));
$calendars = q("SELECT * FROM %s%scalendars WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]));
renderCalDavEntry_data($calendars[0], $calobj[0]);
return true;
}
/**
* @param int $calobj_id
* @return bool
*/
function renderCalDavEntry_calobj_id($calobj_id)
{
$calobj_id = IntVal($calobj_id);
q("DELETE FROM %s%sjqcalendar WHERE `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
q("DELETE FROM %s%snotifications WHERE `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
$calobj = q("SELECT * FROM %s%scalendarobjects WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
if (count($calobj) == 0) return false;
$calendars = q("SELECT * FROM %s%scalendars WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]));
renderCalDavEntry_data($calendars[0], $calobj[0]);
return true;
}

View file

@ -1,174 +0,0 @@
<?php
class Sabre_CalDAV_Backend_Std extends Sabre_CalDAV_Backend_Common
{
public function getNamespace()
{
return CALDAV_NAMESPACE_PRIVATE;
}
public function getCalUrlPrefix()
{
return "private";
}
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @return void
*/
public function createCalendar($principalUri, $calendarUri, array $properties)
{
// TODO: Implement createCalendar() method.
}
/**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @return void
*/
public function deleteCalendar($calendarId)
{
// TODO: Implement deleteCalendar() method.
}
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
* * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
* ' "abcdef"')
* * calendarid - The calendarid as it was passed to this function.
* * size - The size of the calendar objects, in bytes.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
*
* The calendardata is also optional. If it's not returned
* 'getCalendarObject' will be called later, which *is* expected to return
* calendardata.
*
* If neither etag or size are specified, the calendardata will be
* used/fetched to determine these numbers. If both are specified the
* amount of times this is needed is reduced by a great degree.
*
* @param string $calendarId
* @return array
*/
function getCalendarObjects($calendarId)
{
$x = explode("-", $calendarId);
$objs = q("SELECT * FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]));
$ret = array();
foreach ($objs as $obj) {
$ret[] = array(
"id" => IntVal($obj["id"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["uri"],
"lastmodified" => $obj["lastmodified"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
}
return $ret;
}
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_FileNotFound
* @return array
*/
function getCalendarObject($calendarId, $objectUri)
{
$x = explode("-", $calendarId);
$o = q("SELECT * FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($objectUri));
if (count($o) > 0) {
$o[0]["calendarid"] = $calendarId;
$o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]);
return $o[0];
} else throw new Sabre_DAV_Exception_FileNotFound($calendarId . " / " . $objectUri);
}
/**
* Creates a new calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @return null|string|void
*/
function createCalendarObject($calendarId, $objectUri, $calendarData)
{
$x = explode("-", $calendarId);
q("INSERT INTO %s%scalendarobjects (`namespace`, `namespace_id`, `uri`, `calendardata`, `lastmodified`, `etag`, `size`) VALUES (%d, %d, '%s', '%s', NOW(), '%s', %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
IntVal($x[0]), IntVal($x[1]), dbesc($objectUri), addslashes($calendarData), md5($calendarData), strlen($calendarData)
);
$this->increaseCalendarCtag($x[0], $x[1]);
renderCalDavEntry_uri($objectUri);
}
/**
* Updates an existing calendarobject, based on it's uri.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @return null|string|void
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData)
{
$x = explode("-", $calendarId);
q("UPDATE %s%scalendarobjects SET `calendardata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($calendarData), md5($calendarData), strlen($calendarData), IntVal($x[0]), IntVal($x[1]), dbesc($objectUri));
$this->increaseCalendarCtag($x[0], $x[1]);
renderCalDavEntry_uri($objectUri);
}
/**
* Deletes an existing calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @return void
*/
function deleteCalendarObject($calendarId, $objectUri)
{
$x = explode("-", $calendarId);
q("DELETE FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($objectUri)
);
$this->increaseCalendarCtag($x[0], $x[1]);
renderCalDavEntry_uri($objectUri);
}
}

View file

@ -1,76 +1,179 @@
<?php <?php
abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract { abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
{
/** /**
* List of CalDAV properties, and how they map to database fieldnames
*
* Add your own properties by simply adding on to this array
*
* @var array * @var array
*/ */
public $propertyMap = array( protected $propertyMap = array(
'{DAV:}displayname' => 'displayname', '{DAV:}displayname' => 'displayname',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
); );
/**
* @abstract
* @return int
*/
abstract public function getNamespace(); abstract public function getNamespace();
abstract public function getCalUrlPrefix();
/** /**
* @param int $namespace * @static
* @param int $namespace_id * @abstract
* @return string
*/ */
protected function increaseCalendarCtag($namespace, $namespace_id) { abstract public static function getBackendTypeName();
$namespace = IntVal($namespace);
$namespace_id = IntVal($namespace_id);
q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $namespace, $namespace_id);
/**
* @param int $calendarId
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
abstract public function listItemsByRange($calendarId, $sd, $ed, $base_path);
/**
* @var array
*/
static private $calendarCache = array();
/**
* @var array
*/
static private $calendarObjectCache = array();
/**
* @static
* @param int $calendarId
* @return array
*/
static public function loadCalendarById($calendarId)
{
if (!isset(self::$calendarCache[$calendarId])) {
$c = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarCache[$calendarId] = $c[0];
}
return self::$calendarCache[$calendarId];
}
/**
* @static
* @param int $obj_id
* @return array
*/
static public function loadCalendarobjectById($obj_id)
{
if (!isset(self::$calendarObjectCache[$obj_id])) {
$o = q("SELECT * FROM %s%scalendarobjects WHERE `id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
);
self::$calendarObjectCache[$obj_id] = $o[0];
}
return self::$calendarObjectCache[$obj_id];
} }
/** /**
* Returns a list of calendars for a principal. * @static
* * @param Sabre\VObject\Component\VEvent $component
* Every project is an array with the following keys: * @return int
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
* * uri, which the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
*
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
* @param string $principalUri
* @return array
*/ */
public function getCalendarsForUser($principalUri) public static function getDtEndTimeStamp(&$component)
{ {
list(,$name) = Sabre_DAV_URLUtil::splitPath($principalUri); /** @var Sabre\VObject\Property\DateTime $dtstart */
$user_id = dav_compat_username2id($name); $dtstart = $component->__get("DTSTART");
if ($component->__get("DTEND")) {
$cals = q("SELECT * FROM %s%scalendars WHERE `uid`=%d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $user_id, $this->getNamespace()); /** @var Sabre\VObject\Property\DateTime $dtend */
$ret = array(); $dtend = $component->__get("DTEND");
foreach ($cals as $cal) { return $dtend->getDateTime()->getTimeStamp();
$dat = array( } elseif ($component->__get("DURATION")) {
"id" => $cal["namespace"] . "-" . $cal["namespace_id"], $endDate = clone $dtstart->getDateTime();
"uri" => $this->getCalUrlPrefix() . "-" . $cal["namespace_id"], $endDate->add(Sabre\VObject\DateTimeParser::parse($component->__get("DURATION")->value));
"principaluri" => $principalUri, return $endDate->getTimeStamp();
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag']?$cal['ctag']:'0', } elseif ($dtstart->getDateType() === Sabre\VObject\Property\DateTime::DATE) {
"calendar_class" => "Sabre_CalDAV_Calendar", $endDate = clone $dtstart->getDateTime();
); $endDate->modify('+1 day');
foreach ($this->propertyMap as $key=>$field) $dat[$key] = $cal[$field]; return $endDate->getTimeStamp();
} else {
$ret[] = $dat; return $dtstart->getDateTime()->getTimeStamp() + 3600;
} }
return $ret; }
/**
* Parses some information from calendar objects, used for optimized
* calendar-queries.
*
* Returns an array with the following keys:
* * etag
* * size
* * componentType
* * firstOccurence
* * lastOccurence
*
* @param string $calendarData
* @throws Sabre_DAV_Exception_BadRequest
* @return array
*/
protected function getDenormalizedData($calendarData)
{
/** @var Sabre\VObject\Component\VEvent $vObject */
$vObject = Sabre\VObject\Reader::read($calendarData);
$componentType = null;
$component = null;
$firstOccurence = null;
$lastOccurence = null;
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if (!$componentType) {
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
}
if ($componentType === 'VEVENT') {
/** @var Sabre\VObject\Component\VEvent $component */
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$firstOccurence = $dtstart->getDateTime()->getTimeStamp();
// Finding the last occurence is a bit harder
if (!$component->__get("RRULE")) {
$lastOccurence = self::getDtEndTimeStamp($component);
} else {
$it = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
$maxDate = new DateTime(CALDAV_MAX_YEAR . "-01-01");
if ($it->isInfinite()) {
$lastOccurence = $maxDate->getTimeStamp();
} else {
$end = $it->getDtEnd();
while ($it->valid() && $end < $maxDate) {
$end = $it->getDtEnd();
$it->next();
}
$lastOccurence = $end->getTimeStamp();
}
}
}
return array(
'etag' => md5($calendarData),
'size' => strlen($calendarData),
'componentType' => $componentType,
'firstOccurence' => $firstOccurence,
'lastOccurence' => $lastOccurence,
);
} }
/** /**
@ -109,10 +212,11 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
* @param array $mutations * @param array $mutations
* @return bool|array * @return bool|array
*/ */
public function updateCalendar($calendarId, array $mutations) { public function updateCalendar($calendarId, array $mutations)
{
$newValues = array(); $newValues = array();
$result = array( $result = array(
200 => array(), // Ok 200 => array(), // Ok
403 => array(), // Forbidden 403 => array(), // Forbidden
424 => array(), // Failed Dependency 424 => array(), // Failed Dependency
@ -120,17 +224,17 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
$hasError = false; $hasError = false;
foreach($mutations as $propertyName=>$propertyValue) { foreach ($mutations as $propertyName=> $propertyValue) {
// We don't know about this property. // We don't know about this property.
if (!isset($this->propertyMap[$propertyName])) { if (!isset($this->propertyMap[$propertyName])) {
$hasError = true; $hasError = true;
$result[403][$propertyName] = null; $result[403][$propertyName] = null;
unset($mutations[$propertyName]); unset($mutations[$propertyName]);
continue; continue;
} }
$fieldName = $this->propertyMap[$propertyName]; $fieldName = $this->propertyMap[$propertyName];
$newValues[$fieldName] = $propertyValue; $newValues[$fieldName] = $propertyValue;
} }
@ -138,33 +242,46 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
// If there were any errors we need to fail the request // If there were any errors we need to fail the request
if ($hasError) { if ($hasError) {
// Properties has the remaining properties // Properties has the remaining properties
foreach($mutations as $propertyName=>$propertyValue) { foreach ($mutations as $propertyName=> $propertyValue) {
$result[424][$propertyName] = null; $result[424][$propertyName] = null;
} }
// Removing unused statuscodes for cleanliness // Removing unused statuscodes for cleanliness
foreach($result as $status=>$properties) { foreach ($result as $status=> $properties) {
if (is_array($properties) && count($properties)===0) unset($result[$status]); if (is_array($properties) && count($properties) === 0) unset($result[$status]);
} }
return $result; return $result;
} }
$x = explode("-", $calendarId); $this->increaseCalendarCtag($calendarId);
$this->increaseCalendarCtag($x[0], $x[1]);
$valuesSql = array(); $valuesSql = array();
foreach($newValues as $fieldName=>$value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'"; foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
if (count($valuesSql) > 0) { if (count($valuesSql) > 0) {
q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `namespace` = %d AND `namespace_id` = %d", q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1])
);
} }
return true; return true;
} }
/**
* @param int $calendarId
*/
protected function increaseCalendarCtag($calendarId)
{
q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarObjectCache = array();
}
/**
* @abstract
* @param int $calendar_id
* @param int $calendarobject_id
* @return string
*/
abstract function getItemDetailRedirect($calendar_id, $calendarobject_id);
} }

View file

@ -0,0 +1,510 @@
<?php
class Sabre_CalDAV_Backend_Private extends Sabre_CalDAV_Backend_Common
{
/**
* @var null|Sabre_CalDAV_Backend_Private
*/
private static $instance = null;
/**
* @static
* @return Sabre_CalDAV_Backend_Private
*/
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new Sabre_CalDAV_Backend_Private();
}
return self::$instance;
}
/**
* @return int
*/
public function getNamespace()
{
return CALDAV_NAMESPACE_PRIVATE;
}
/**
* @static
* @return string
*/
public static function getBackendTypeName()
{
return t("Private Events");
}
/**
* @obsolete
* @param array $calendar
* @param int $user
* @return array
*/
public function getPermissionsCalendar($calendar, $user)
{
if ($calendar["namespace"] == CALDAV_NAMESPACE_PRIVATE && $user == $calendar["namespace_id"]) return array("read"=> true, "write"=> true);
return array("read"=> false, "write"=> false);
}
/**
* @obsolete
* @param array $calendar
* @param int $user
* @param string $calendarobject_id
* @param null|array $item_arr
* @return array
*/
public function getPermissionsItem($calendar, $user, $calendarobject_id, $item_arr = null)
{
return $this->getPermissionsCalendar($calendar, $user);
}
/**
* @param array $row
* @param array $calendar
* @param string $base_path
* @return array
*/
private function jqcal2wdcal($row, $calendar, $base_path)
{
$not = q("SELECT COUNT(*) num FROM %s%snotifications WHERE `calendar_id` = %d AND `calendarobject_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($row["calendar_id"]), IntVal($row["calendarobject_id"])
);
$editable = $this->getPermissionsItem($calendar["namespace_id"], $row["calendarobject_id"], $row);
$end = wdcal_mySql2PhpTime($row["EndTime"]);
if ($row["IsAllDayEvent"]) $end -= 1;
return array(
"jq_id" => $row["id"],
"ev_id" => $row["calendarobject_id"],
"summary" => escape_tags($row["Summary"]),
"start" => wdcal_mySql2PhpTime($row["StartTime"]),
"end" => $end,
"is_allday" => $row["IsAllDayEvent"],
"is_moredays" => 0,
"is_recurring" => $row["IsRecurring"],
"color" => (is_null($row["Color"]) || $row["Color"] == "" ? $calendar["calendarcolor"] : $row["Color"]),
"is_editable" => ($editable ? 1 : 0),
"is_editable_quick" => ($editable && !$row["IsRecurring"] ? 1 : 0),
"location" => "Loc.",
"attendees" => '',
"has_notification" => ($not[0]["num"] > 0 ? 1 : 0),
"url_detail" => $base_path . $row["calendarobject_id"] . "/",
"url_edit" => $base_path . $row["calendarobject_id"] . "/edit/",
"special_type" => "",
);
}
/**
* @param int $calendarId
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
public function listItemsByRange($calendarId, $sd, $ed, $base_path)
{
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
$von = wdcal_php2MySqlTime($sd);
$bis = wdcal_php2MySqlTime($ed);
$timezoneOffset = date("P");
// @TODO Events, die früher angefangen haben, aber noch andauern
$evs = q("SELECT *, CONVERT_TZ(`StartTime`, @@session.time_zone, '$timezoneOffset') StartTime, CONVERT_TZ(`EndTime`, @@session.time_zone, '$timezoneOffset') EndTime
FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `StartTime` between '%s' and '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($von), dbesc($bis));
$events = array();
foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/");
return $events;
}
/**
* @param int $calendar_id
* @param int $calendarobject_id
* @return string
*/
public function getItemDetailRedirect($calendar_id, $calendarobject_id)
{
return "/dav/wdcal/$calendar_id/$calendarobject_id/edit/";
}
/**
* Returns a list of calendars for a principal.
*
* Every project is an array with the following keys:
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
* * uri, which the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
*
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
* @param string $principalUri
* @throws DAVVersionMismatchException
* @return array
*/
public function getCalendarsForUser($principalUri)
{
$n = dav_compat_principal2namespace($principalUri);
if ($n["namespace"] != $this->getNamespace()) return array();
$cals = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), IntVal($n["namespace_id"]));
$ret = array();
foreach ($cals as $cal) {
if (!isset($cal["uri"])) throw new DAVVersionMismatchException();
if (in_array($cal["uri"], $GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"])) continue;
$components = array();
if ($cal["has_vevent"]) $components[] = "VEVENT";
if ($cal["has_vtodo"]) $components[] = "VTODO";
$dat = array(
"id" => $cal["id"],
"uri" => $cal["uri"],
"principaluri" => $principalUri,
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag'] ? $cal['ctag'] : '0',
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
"calendar_class" => "Sabre_CalDAV_Calendar_Private",
);
foreach ($this->propertyMap as $key=> $field) $dat[$key] = $cal[$field];
$ret[] = $dat;
}
return $ret;
}
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @throws Sabre_DAV_Exception|Sabre_DAV_Exception_Conflict
* @return string|void
*/
public function createCalendar($principalUri, $calendarUri, array $properties)
{
$uid = dav_compat_principal2uid($principalUri);
$r = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, dbesc($calendarUri));
if (count($r) > 0) throw new Sabre_DAV_Exception_Conflict("A calendar with this URI already exists");
$keys = array("`namespace`", "`namespace_id`", "`ctag`", "`uri`");
$vals = array(CALDAV_NAMESPACE_PRIVATE, IntVal($uid), 1, "'" . dbesc($calendarUri) . "'");
// Default value
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
$has_vevent = $has_vtodo = 1;
if (isset($properties[$sccs])) {
if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
}
$v = $properties[$sccs]->getValue();
$has_vevent = $has_vtodo = 0;
foreach ($v as $w) {
if (mb_strtolower($w) == "vevent") $has_vevent = 1;
if (mb_strtolower($w) == "vtodo") $has_vtodo = 1;
}
}
$keys[] = "`has_vevent`";
$keys[] = "`has_vtodo`";
$vals[] = $has_vevent;
$vals[] = $has_vtodo;
foreach ($this->propertyMap as $xmlName=> $dbName) {
if (isset($properties[$xmlName])) {
$keys[] = "`$dbName`";
$vals[] = "'" . dbesc($properties[$xmlName]) . "'";
}
}
$sql = sprintf("INSERT INTO %s%scalendars (" . implode(', ', $keys) . ") VALUES (" . implode(', ', $vals) . ")", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
q($sql);
$x = q("SELECT id FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, $calendarUri
);
return $x[0]["id"];
}
/**
* Updates properties for a calendar.
*
* The mutations array uses the propertyName in clark-notation as key,
* and the array value for the property value. In the case a property
* should be deleted, the property value will be null.
*
* This method must be atomic. If one property cannot be changed, the
* entire operation must fail.
*
* If the operation was successful, true can be returned.
* If the operation failed, false can be returned.
*
* Deletion of a non-existent property is always successful.
*
* Lastly, it is optional to return detailed information about any
* failures. In this case an array should be returned with the following
* structure:
*
* array(
* 403 => array(
* '{DAV:}displayname' => null,
* ),
* 424 => array(
* '{DAV:}owner' => null,
* )
* )
*
* In this example it was forbidden to update {DAV:}displayname.
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
* (424 Failed Dependency) because the request needs to be atomic.
*
* @param string $calendarId
* @param array $mutations
* @return bool|array
*/
public function updateCalendar($calendarId, array $mutations)
{
$newValues = array();
$result = array(
200 => array(), // Ok
403 => array(), // Forbidden
424 => array(), // Failed Dependency
);
$hasError = false;
foreach ($mutations as $propertyName=> $propertyValue) {
// We don't know about this property.
if (!isset($this->propertyMap[$propertyName])) {
$hasError = true;
$result[403][$propertyName] = null;
unset($mutations[$propertyName]);
continue;
}
$fieldName = $this->propertyMap[$propertyName];
$newValues[$fieldName] = $propertyValue;
}
// If there were any errors we need to fail the request
if ($hasError) {
// Properties has the remaining properties
foreach ($mutations as $propertyName=> $propertyValue) {
$result[424][$propertyName] = null;
}
// Removing unused statuscodes for cleanliness
foreach ($result as $status=> $properties) {
if (is_array($properties) && count($properties) === 0) unset($result[$status]);
}
return $result;
}
$sql = "`ctag` = `ctag` + 1";
foreach ($newValues as $key=> $val) $sql .= ", `" . $key . "` = '" . dbesc($val) . "'";
$sql = sprintf("UPDATE %s%scalendars SET $sql WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
q($sql);
return true;
}
/**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @return void
*/
public function deleteCalendar($calendarId)
{
q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
q("DELETE FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
}
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
* * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
* ' "abcdef"')
* * calendarid - The calendarid as it was passed to this function.
* * size - The size of the calendar objects, in bytes.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
*
* The calendardata is also optional. If it's not returned
* 'getCalendarObject' will be called later, which *is* expected to return
* calendardata.
*
* If neither etag or size are specified, the calendardata will be
* used/fetched to determine these numbers. If both are specified the
* amount of times this is needed is reduced by a great degree.
*
* @param mixed $calendarId
* @return array
*/
function getCalendarObjects($calendarId)
{
$objs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
$ret = array();
foreach ($objs as $obj) {
$ret[] = array(
"id" => IntVal($obj["id"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["uri"],
"lastmodified" => $obj["lastmodified"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
}
return $ret;
}
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
function getCalendarObject($calendarId, $objectUri)
{
$o = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
if (count($o) > 0) {
$o[0]["calendarid"] = $calendarId;
$o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]);
return $o[0];
} else throw new Sabre_DAV_Exception_NotFound($calendarId . " / " . $objectUri);
}
/**
* Creates a new calendar object.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
function createCalendarObject($calendarId, $objectUri, $calendarData)
{
$calendarData = icalendar_sanitize_string($calendarData);
$extraData = $this->getDenormalizedData($calendarData);
q("INSERT INTO %s%scalendarobjects (`calendar_id`, `uri`, `calendardata`, `lastmodified`, `componentType`, `firstOccurence`, `lastOccurence`, `etag`, `size`)
VALUES (%d, '%s', '%s', NOW(), '%s', '%s', '%s', '%s', %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri), addslashes($calendarData), dbesc($extraData['componentType']),
dbesc(wdcal_php2MySqlTime($extraData['firstOccurence'])), dbesc(wdcal_php2MySqlTime($extraData['lastOccurence'])), dbesc($extraData["etag"]), IntVal($extraData["size"])
);
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_uri($objectUri);
return '"' . $extraData['etag'] . '"';
}
/**
* Updates an existing calendarobject, based on it's uri.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData)
{
$calendarData = icalendar_sanitize_string($calendarData);
$extraData = $this->getDenormalizedData($calendarData);
q("UPDATE %s%scalendarobjects SET `calendardata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d, `componentType` = '%s', `firstOccurence` = '%s', `lastOccurence` = '%s'
WHERE `calendar_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($calendarData), dbesc($extraData["etag"]), IntVal($extraData["size"]), dbesc($extraData["componentType"]),
dbesc(wdcal_php2MySqlTime($extraData["firstOccurence"])), dbesc(wdcal_php2MySqlTime($extraData["lastOccurence"])), IntVal($calendarId), dbesc($objectUri));
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_uri($objectUri);
return '"' . $extraData['etag'] . '"';
}
/**
* Deletes an existing calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return void
*/
function deleteCalendarObject($calendarId, $objectUri)
{
$r = q("SELECT `id` FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_calobj_id($r[0]["id"]);
}
}

View file

@ -0,0 +1,186 @@
<?php
abstract class Sabre_CalDAV_Backend_Virtual extends Sabre_CalDAV_Backend_Common
{
/**
* @static
* @abstract
* @param int $calendarId
* @param string $uri
* @return array
*/
/*
abstract public function getItemsByUri($calendarId, $uri);
*/
/**
* @static
* @param int $uid
* @param int $namespace
*/
static public function invalidateCache($uid = 0, $namespace = 0) {
q("DELETE FROM %s%scal_virtual_object_sync WHERE `uid` = %d AND `namespace` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid), IntVal($namespace));
}
/**
* @static
* @abstract
* @param int $calendarId
*/
static abstract protected function createCache_internal($calendarId);
/**
* @static
* @param int $calendarId
*/
static protected function createCache($calendarId) {
$calendarId = IntVal($calendarId);
q("DELETE FROM %s%scal_virtual_object_cache WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
static::createCache_internal($calendarId);
q("REPLACE INTO %s%scal_virtual_object_sync (`calendar_id`, `date`) VALUES (%d, NOW())", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
}
/**
* @param string $calendarId
* @return array
*/
public function getCalendarObjects($calendarId)
{
$calendarId = IntVal($calendarId);
$r = q("SELECT COUNT(*) n FROM %s%scal_virtual_object_sync WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
if ($r[0]["n"] == 0) static::createCache($calendarId);
$r = q("SELECT * FROM %s%scal_virtual_object_cache WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
$ret = array();
foreach ($r as $obj) {
$ret[] = array(
"id" => IntVal($obj["data_uri"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["data_uri"],
"lastmodified" => $obj["date"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
}
return $ret;
}
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getCalendarObject($calendarId, $objectUri)
{
$calendarId = IntVal($calendarId);
$r = q("SELECT COUNT(*) n FROM %s%scal_virtual_object_sync WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
if ($r[0]["n"] == 0) static::createCache($calendarId);
$r = q("SELECT * FROM %s%scal_virtual_object_cache WHERE `data_uri` = '%s' AND `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($objectUri), IntVal($calendarId));
if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
$obj = $r[0];
$ret = array(
"id" => IntVal($obj["data_uri"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["data_uri"],
"lastmodified" => $obj["date"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
return $ret;
}
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function createCalendar($principalUri, $calendarUri, array $properties)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function deleteCalendar($calendarId)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Creates a new calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @throws Sabre_DAV_Exception_Forbidden
* @return null|string|void
*/
function createCalendarObject($calendarId, $objectUri, $calendarData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Updates an existing calendarobject, based on it's uri.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @throws Sabre_DAV_Exception_Forbidden
* @return null|string|void
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Deletes an existing calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
function deleteCalendarObject($calendarId, $objectUri)
{
throw new Sabre_DAV_Exception_Forbidden();
}
}

View file

@ -0,0 +1,50 @@
<?php
class Sabre_CalDAV_Calendar_Private extends Sabre_CalDAV_Calendar
{
public function getACL()
{
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
/*
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
'protected' => true,
),
array(
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
'principal' => '{DAV:}authenticated',
'protected' => true,
),
*/
);
}
}

View file

@ -0,0 +1,44 @@
<?php
class Sabre_CalDAV_Calendar_Virtual extends Sabre_CalDAV_Calendar {
/**
* Returns a list of ACE's for this node.
*
* Each ACE has the following properties:
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
* currently the only supported privileges
* * 'principal', a url to the principal who owns the node
* * 'protected' (optional), indicating that this ACE is not allowed to
* be updated.
*
* @return array
*/
public function getACL() {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
'protected' => true,
),
array(
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
'principal' => '{DAV:}authenticated',
'protected' => true,
),
);
}
}

View file

@ -0,0 +1,116 @@
<?php
abstract class Sabre_CardDAV_Backend_Common extends Sabre_CardDAV_Backend_Abstract
{
/**
* @abstract
* @return int
*/
abstract public function getNamespace();
/**
* @static
* @abstract
* @return string
*/
abstract public static function getBackendTypeName();
/**
* @var array
*/
static private $addressbookCache = array();
/**
* @var array
*/
static private $addressbookObjectCache = array();
/**
* @static
* @param int $addressbookId
* @return array
*/
static public function loadCalendarById($addressbookId)
{
if (!isset(self::$addressbookCache[$addressbookId])) {
$c = q("SELECT * FROM %s%saddressbooks WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId));
self::$addressbookCache[$addressbookId] = $c[0];
}
return self::$addressbookCache[$addressbookId];
}
/**
* @static
* @param int $obj_id
* @return array
*/
static public function loadAddressbookobjectById($obj_id)
{
if (!isset(self::$addressbookObjectCache[$obj_id])) {
$o = q("SELECT * FROM %s%saddressbookobjects WHERE `id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
);
self::$addressbookObjectCache[$obj_id] = $o[0];
}
return self::$addressbookObjectCache[$obj_id];
}
/**
* Updates an addressbook's properties
*
* See Sabre_DAV_IProperties for a description of the mutations array, as
* well as the return value.
*
* @param mixed $addressBookId
* @param array $mutations
* @throws Sabre_DAV_Exception_Forbidden
* @see Sabre_DAV_IProperties::updateProperties
* @return bool|array
*/
public function updateAddressBook($addressBookId, array $mutations)
{
$updates = array();
foreach ($mutations as $property=> $newValue) {
switch ($property) {
case '{DAV:}displayname' :
$updates['displayname'] = $newValue;
break;
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
$updates['description'] = $newValue;
break;
default :
// If any unsupported values were being updated, we must
// let the entire request fail.
return false;
}
}
// No values are being updated?
if (!$updates) {
return false;
}
$query = 'UPDATE ' . CALDAV_SQL_DB . CALDAV_SQL_PREFIX . 'addressbooks SET ctag = ctag + 1 ';
foreach ($updates as $key=> $value) {
$query .= ', `' . dbesc($key) . '` = ' . dbesc($key) . ' ';
}
$query .= ' WHERE id = ' . IntVal($addressBookId);
q($query);
return true;
}
/**
* @param int $addressbookId
*/
protected function increaseAddressbookCtag($addressbookId)
{
q("UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId));
self::$addressbookCache = array();
}
}

View file

@ -1,19 +1,25 @@
<?php <?php
/** class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Common
* PDO CardDAV backend
*
* This CardDAV backend uses PDO to store addressbooks
*
* @package Sabre
* @subpackage CardDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
{ {
/**
* @var null|Sabre_CardDAV_Backend_Std
*/
private static $instance = null;
/**
* @static
* @return Sabre_CardDAV_Backend_Std
*/
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new Sabre_CardDAV_Backend_Std();
}
return self::$instance;
}
/** /**
* Sets up the object * Sets up the object
*/ */
@ -22,6 +28,24 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
} }
/**
* @return int
*/
public function getNamespace()
{
return CARDDAV_NAMESPACE_PRIVATE;
}
/**
* @static
* @return string
*/
public static function getBackendTypeName()
{
return t("Private Addressbooks");
}
/** /**
* Returns the list of addressbooks for a specific user. * Returns the list of addressbooks for a specific user.
* *
@ -30,22 +54,19 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function getAddressBooksForUser($principalUri) public function getAddressBooksForUser($principalUri)
{ {
$uid = dav_compat_principal2uid($principalUri); $n = dav_compat_principal2namespace($principalUri);
if ($n["namespace"] != $this->getNamespace()) return array();
$addressBooks = array(); $addressBooks = array();
$books = q("SELECT id, uri, displayname, principaluri, description, ctag FROM %s%saddressbooks_phone WHERE principaluri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($principalUri)); $books = q("SELECT * FROM %s%saddressbooks WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($n["namespace"]), IntVal($n["namespace_id"]));
if (count($books) == 0) {
q("INSERT INTO %s%saddressbooks_phone (uid, principaluri, displayname, uri, description, ctag) VALUES (%d, '%s', '%s', '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $uid, dbesc($principalUri), 'Other', 'phone', 'Manually added contacts'
);
$books = q("SELECT id, uri, displayname, principaluri, description, ctag FROM %s%saddressbooks_phone WHERE principaluri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($principalUri));
}
foreach ($books as $row) { foreach ($books as $row) {
if (in_array($row["uri"], $GLOBALS["CARDDAV_PRIVATE_SYSTEM_ADDRESSBOOKS"])) continue;
$addressBooks[] = array( $addressBooks[] = array(
'id' => CARDDAV_NAMESPACE_PHONECONTACTS . "-" . $row['id'], 'id' => $row['id'],
'uri' => $row['uri'], 'uri' => $row['uri'],
'principaluri' => $row['principaluri'], 'principaluri' => $principalUri,
'{DAV:}displayname' => $row['displayname'], '{DAV:}displayname' => $row['displayname'],
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
'{http://calendarserver.org/ns/}getctag' => $row['ctag'], '{http://calendarserver.org/ns/}getctag' => $row['ctag'],
@ -59,57 +80,6 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
} }
/**
* Updates an addressbook's properties
*
* See Sabre_DAV_IProperties for a description of the mutations array, as
* well as the return value.
*
* @param mixed $addressBookId
* @param array $mutations
* @throws Sabre_DAV_Exception_Forbidden
* @see Sabre_DAV_IProperties::updateProperties
* @return bool|array
*/
public function updateAddressBook($addressBookId, array $mutations)
{
$x = explode("-", $addressBookId);
$updates = array();
foreach ($mutations as $property=> $newValue) {
switch ($property) {
case '{DAV:}displayname' :
$updates['displayname'] = $newValue;
break;
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
$updates['description'] = $newValue;
break;
default :
// If any unsupported values were being updated, we must
// let the entire request fail.
return false;
}
}
// No values are being updated?
if (!$updates) {
return false;
}
$query = 'UPDATE ' . CALDAV_SQL_DB . CALDAV_SQL_PREFIX . 'addressbooks_phone SET ctag = ctag + 1 ';
foreach ($updates as $key=> $value) {
$query .= ', `' . dbesc($key) . '` = ' . dbesc($key) . ' ';
}
$query .= ' WHERE id = ' . IntVal($x[1]);
q($query);
return true;
}
/** /**
* Creates a new address book * Creates a new address book
* *
@ -121,6 +91,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function createAddressBook($principalUri, $url, array $properties) public function createAddressBook($principalUri, $url, array $properties)
{ {
$uid = dav_compat_principal2uid($principalUri);
$values = array( $values = array(
'displayname' => null, 'displayname' => null,
'description' => null, 'description' => null,
@ -143,8 +115,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
} }
q("INSERT INTO %s%saddressbooks_phone (uri, displayname, description, principaluri, ctag) VALUES ('%s', '%s', '%s', '%s', 1)", q("INSERT INTO %s%saddressbooks (`uri`, `displayname`, `description`, `namespace`, `namespace_id`, `ctag`) VALUES ('%s', '%s', '%s', %d, %d, 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($values["uri"]), dbesc($values["displayname"]), dbesc($values["description"]), dbesc($values["principaluri"]) CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($values["uri"]), dbesc($values["displayname"]), dbesc($values["description"]), CARDDAV_NAMESPACE_PRIVATE, IntVal($uid)
); );
} }
@ -157,9 +129,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function deleteAddressBook($addressBookId) public function deleteAddressBook($addressBookId)
{ {
$x = explode("-", $addressBookId); q("DELETE FROM %s%saddressbookobjects WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
q("DELETE FROM %s%scards WHERE namespace = %d AND namespace_id = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1])); q("DELETE FROM %s%saddressbooks WHERE `addressbook_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
q("DELETE FROM %s%saddressbooks_phone WHERE id = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1]));
} }
/** /**
@ -183,10 +154,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function getCards($addressbookId) public function getCards($addressbookId)
{ {
$x = explode("-", $addressbookId); $r = q('SELECT `id`, `carddata`, `uri`, `lastmodified`, `etag`, `size`, `contact` FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `manually_deleted` = 0',
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId)
$r = q('SELECT id, carddata, uri, lastmodified, etag, size, contact FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND manually_deleted = 0',
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1])
); );
if ($r) return $r; if ($r) return $r;
return array(); return array();
@ -205,9 +174,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function getCard($addressBookId, $cardUri) public function getCard($addressBookId, $cardUri)
{ {
$x = explode("-", $addressBookId); $x = q("SELECT `id`, `carddata`, `uri`, `lastmodified`, `etag`, `size` FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `uri` = '%s'",
$x = q("SELECT id, carddata, uri, lastmodified, etag, size FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId), dbesc($cardUri));
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri));
if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound(); if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound();
return $x[0]; return $x[0];
} }
@ -240,14 +208,12 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function createCard($addressBookId, $cardUri, $cardData) public function createCard($addressBookId, $cardUri, $cardData)
{ {
$x = explode("-", $addressBookId);
$etag = md5($cardData); $etag = md5($cardData);
q("INSERT INTO %s%scards (carddata, uri, lastmodified, namespace, namespace_id, etag, size) VALUES ('%s', '%s', %d, %d, '%s', %d)", q("INSERT INTO %s%saddressbookobjects (`carddata`, `uri`, `lastmodified`, `addressbook_id`, `etag`, `size`) VALUES ('%s', '%s', NOW(), %d, '%s', %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($cardUri), time(), IntVal($x[0]), IntVal($x[1]), $etag, strlen($cardData) CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($cardUri), IntVal($addressBookId), dbesc($etag), strlen($cardData)
); );
q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return '"' . $etag . '"'; return '"' . $etag . '"';
@ -281,14 +247,12 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function updateCard($addressBookId, $cardUri, $cardData) public function updateCard($addressBookId, $cardUri, $cardData)
{ {
$x = explode("-", $addressBookId);
$etag = md5($cardData); $etag = md5($cardData);
q("UPDATE %s%scards SET carddata = '%s', lastmodified = %d, etag = '%s', size = %d, manually_edited = 1 WHERE uri = '%s' AND namespace = %d AND namespace_id =%d", q("UPDATE %s%saddressbookobjects SET `carddata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d, `manually_edited` = 1 WHERE `uri` = '%s' AND `addressbook_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), time(), $etag, strlen($cardData), dbesc($cardUri), IntVal($x[10]), IntVal($x[1]) CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($etag), strlen($cardData), dbesc($cardUri), IntVal($addressBookId)
); );
q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return '"' . $etag . '"'; return '"' . $etag . '"';
} }
@ -303,10 +267,8 @@ class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract
*/ */
public function deleteCard($addressBookId, $cardUri) public function deleteCard($addressBookId, $cardUri)
{ {
$x = explode("-", $addressBookId); q("DELETE FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId), dbesc($cardUri));
q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
q("DELETE FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri));
q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1]));
return true; return true;
} }

Some files were not shown because too many files have changed in this diff Show more