Merge pull request #73 from CatoTH/master

CalDAV Calendar: version 0.2
This commit is contained in:
friendica 2012-07-28 16:12:37 -07:00
commit 1ff91aaf65
124 changed files with 8552 additions and 14222 deletions

View file

@ -1,3 +1,11 @@
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 v0.1.1
====== ======
[FEATURE] A "New Event" Button in the navigation bar of the calendar is added. [FEATURE] A "New Event" Button in the navigation bar of the calendar is added.
@ -5,6 +13,6 @@ v0.1.1
[BUGFIX] When editing a event, the start time cannot be set befor the end time anymore. [BUGFIX] When editing a event, the start time cannot be set befor the end time anymore.
[BUGFIX] Fixed some problems with Magic Quotes [BUGFIX] Fixed some problems with Magic Quotes
v0.1 v0.1.0
====== ======
Initial Release 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,63 @@
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. * New feature: Support for caldav notifications!
* Changed: The CalDAV PDO backend is now a lot faster for common calendar * Changed: Responsibility for dealing with the calendar-query is now
queries. moved from the CalDAV plugin to the CalDAV backends. This allows for
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8 encoded. 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: Workaround for 10.8 Mountain Lion vCards, as it needs \r line
endings to parse them correctly.
* 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.4-stable (2012-??-??)
* 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.
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 +75,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,6 +95,11 @@ 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";
switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql' :
$pdo->exec(<<<SQL $pdo->exec(<<<SQL
ALTER TABLE calendarobjects ALTER TABLE calendarobjects
ADD etag VARCHAR(32), ADD etag VARCHAR(32),
@ -104,6 +109,19 @@ 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 {

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

@ -3,45 +3,15 @@
/** /**
* 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 +55,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.
* *

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

@ -16,8 +16,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

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

@ -68,7 +68,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:');
} }

View file

@ -304,6 +304,7 @@ 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;
if ($expandedEvent->VALARM !== null) {
foreach($expandedEvent->VALARM as $expandedAlarm) { foreach($expandedEvent->VALARM as $expandedAlarm) {
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
@ -325,7 +326,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
$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

@ -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

@ -162,6 +162,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 +173,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 +198,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 +385,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 +442,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) {
@ -648,7 +676,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 +696,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 +748,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)) {
@ -704,6 +775,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 +791,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 +835,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 +844,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);
@ -813,9 +892,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 +905,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, Sabre_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); $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
$resultStatus = '2.0;Success';
}
$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

@ -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

@ -154,8 +154,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 +269,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) {
@ -358,6 +357,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.');
}
} }
@ -440,6 +443,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
$vcard = Sabre_VObject_Reader::read($vcardData); $vcard = Sabre_VObject_Reader::read($vcardData);
if (!$filters) return true;
foreach($filters as $filter) { foreach($filters as $filter) {
$isDefined = isset($vcard->{$filter['name']}); $isDefined = isset($vcard->{$filter['name']});

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,11 +96,12 @@ 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();
throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');

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

@ -30,7 +30,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element {
public $children = array(); public $children = array();
/** /**
* If coponents are added to this map, they will be automatically mapped * If components are added to this map, they will be automatically mapped
* to their respective classes, if parsed by the reader or constructed with * to their respective classes, if parsed by the reader or constructed with
* the 'create' method. * the 'create' method.
* *
@ -94,40 +94,54 @@ class Sabre_VObject_Component extends Sabre_VObject_Element {
* *
* This is solely used by the childrenSort method. * This is solely used by the childrenSort method.
* *
* A higher score means the item will be higher in the list * A higher score means the item will be lower in the list.
* To avoid score collisions, each "score category" has a reasonable
* space to accomodate elements. The $key is added to the $score to
* preserve the original relative order of elements.
* *
* @param Sabre_VObject_Node $n * @param int $key
* @param Sabre_VObject $array
* @return int * @return int
*/ */
$sortScore = function($n) { $sortScore = function($key, $array) {
if ($n instanceof Sabre_VObject_Component) { if ($array[$key] instanceof Sabre_VObject_Component) {
// We want to encode VTIMEZONE first, this is a personal // We want to encode VTIMEZONE first, this is a personal
// preference. // preference.
if ($n->name === 'VTIMEZONE') { if ($array[$key]->name === 'VTIMEZONE') {
return 1; $score=300000000;
return $score+$key;
} else { } else {
return 0; $score=400000000;
return $score+$key;
} }
} else { } else {
// Properties get encoded first
// VCARD version 4.0 wants the VERSION property to appear first // VCARD version 4.0 wants the VERSION property to appear first
if ($n->name === 'VERSION') { if ($array[$key] instanceof Sabre_VObject_Property) {
return 3; if ($array[$key]->name === 'VERSION') {
$score=100000000;
return $score+$key;
} else { } else {
return 2; // All other properties
$score=200000000;
return $score+$key;
} }
} }
}
next($children);
}; };
usort($this->children, function($a, $b) use ($sortScore) { $tmp = $this->children;
uksort($this->children, function($a, $b) use ($sortScore, $tmp) {
$sA = $sortScore($a); $sA = $sortScore($a, $tmp);
$sB = $sortScore($b); $sB = $sortScore($b, $tmp);
if ($sA === $sB) return 0; if ($sA === $sB) return 0;
return ($sA > $sB) ? -1 : 1; return ($sA < $sB) ? -1 : 1;
}); });
@ -250,6 +264,27 @@ class Sabre_VObject_Component extends Sabre_VObject_Element {
} }
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
$result = array();
foreach($this->children as $child) {
$result = array_merge($result, $child->validate());
}
return $result;
}
/* Magic property accessors {{{ */ /* Magic property accessors {{{ */
/** /**

View file

@ -129,5 +129,110 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component {
} }
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
$warnings = array();
$version = $this->select('VERSION');
if (count($version)!==1) {
$warnings[] = array(
'level' => 1,
'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
'node' => $this,
);
} else {
if ((string)$this->VERSION !== '2.0') {
$warnings[] = array(
'level' => 1,
'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
'node' => $this,
);
}
}
$version = $this->select('PRODID');
if (count($version)!==1) {
$warnings[] = array(
'level' => 2,
'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
'node' => $this,
);
}
if (count($this->CALSCALE) > 1) {
$warnings[] = array(
'level' => 2,
'message' => 'The CALSCALE property must not be specified more than once.',
'node' => $this,
);
}
if (count($this->METHOD) > 1) {
$warnings[] = array(
'level' => 2,
'message' => 'The METHOD property must not be specified more than once.',
'node' => $this,
);
}
$allowedComponents = array(
'VEVENT',
'VTODO',
'VJOURNAL',
'VFREEBUSY',
'VTIMEZONE',
);
$allowedProperties = array(
'PRODID',
'VERSION',
'CALSCALE',
'METHOD',
);
$componentsFound = 0;
foreach($this->children as $child) {
if($child instanceof Sabre_VObject_Component) {
$componentsFound++;
if (!in_array($child->name, $allowedComponents)) {
$warnings[] = array(
'level' => 1,
'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component",
'node' => $this,
);
}
}
if ($child instanceof Sabre_VObject_Property) {
if (!in_array($child->name, $allowedProperties)) {
$warnings[] = array(
'level' => 2,
'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component",
'node' => $this,
);
}
}
}
if ($componentsFound===0) {
$warnings[] = array(
'level' => 1,
'message' => 'An iCalendar object must have at least 1 component.',
'node' => $this,
);
}
return array_merge(
$warnings,
parent::validate()
);
}
} }

View file

@ -32,6 +32,23 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou
*/ */
public $parent = null; public $parent = null;
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
return array();
}
/* {{{ IteratorAggregator interface */ /* {{{ IteratorAggregator interface */
/** /**

40
dav/SabreDAV/lib/Sabre/VObject/Property/DateTime.php Executable file → Normal file
View file

@ -204,49 +204,17 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property {
); );
} }
try { // To look up the timezone, we must first find the VCALENDAR component.
// tzid an Olson identifier?
$tz = new DateTimeZone($tzid->value);
} catch (Exception $e) {
// Not an Olson id, we're going to try to find the information
// through the time zone name map.
$newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value);
if (is_null($newtzid)) {
// Not a well known time zone name either, we're going to try
// to find the information through the VTIMEZONE object.
// First we find the root object
$root = $property; $root = $property;
while($root->parent) { while($root->parent) {
$root = $root->parent; $root = $root->parent;
} }
if ($root->name === 'VCALENDAR') {
if (isset($root->VTIMEZONE)) { $tz = Sabre_VObject_TimeZoneUtil::getTimeZone((string)$tzid, $root);
foreach($root->VTIMEZONE as $vtimezone) {
if (((string)$vtimezone->TZID) == $tzid) {
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
$newtzid = (string)$vtimezone->{'X-LIC-LOCATION'};
} else { } else {
// No libical location specified. As a last resort we could $tz = Sabre_VObject_TimeZoneUtil::getTimeZone((string)$tzid);
// try matching $vtimezone's DST rules against all known
// time zones returned by DateTimeZone::list*
// TODO
}
}
}
}
} }
try {
$tz = new DateTimeZone($newtzid);
} catch (Exception $e) {
// If all else fails, we use the default PHP timezone
$tz = new DateTimeZone(date_default_timezone_get());
}
}
$dt = new DateTime($dateStr, $tz); $dt = new DateTime($dateStr, $tz);
$dt->setTimeZone($tz); $dt->setTimeZone($tz);

View file

@ -337,6 +337,8 @@ class Sabre_VObject_RecurrenceIterator implements Iterator {
$this->endDate = clone $this->startDate; $this->endDate = clone $this->startDate;
if (isset($this->baseEvent->DURATION)) { if (isset($this->baseEvent->DURATION)) {
$this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value));
} elseif ($this->baseEvent->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
$this->endDate->modify('+1 day');
} }
} }
$this->currentDate = clone $this->startDate; $this->currentDate = clone $this->startDate;
@ -565,7 +567,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator {
*/ */
public function fastForward(DateTime $dt) { public function fastForward(DateTime $dt) {
while($this->valid() && $this->getDTEnd() < $dt) { while($this->valid() && $this->getDTEnd() <= $dt) {
$this->next(); $this->next();
} }
@ -838,9 +840,40 @@ class Sabre_VObject_RecurrenceIterator implements Iterator {
*/ */
protected function nextYearly() { protected function nextYearly() {
$currentMonth = $this->currentDate->format('n');
$currentYear = $this->currentDate->format('Y');
$currentDayOfMonth = $this->currentDate->format('j');
// No sub-rules, so we just advance by year
if (!$this->byMonth) { if (!$this->byMonth) {
// Unless it was a leap day!
if ($currentMonth==2 && $currentDayOfMonth==29) {
$counter = 0;
do {
$counter++;
// Here we increase the year count by the interval, until
// we hit a date that's also in a leap year.
//
// We could just find the next interval that's dividable by
// 4, but that would ignore the rule that there's no leap
// year every year that's dividable by a 100, but not by
// 400. (1800, 1900, 2100). So we just rely on the datetime
// functions instead.
$nextDate = clone $this->currentDate;
$nextDate->modify('+ ' . ($this->interval*$counter) . ' years');
} while ($nextDate->format('n')!=2);
$this->currentDate = $nextDate;
return;
}
// The easiest form
$this->currentDate->modify('+' . $this->interval . ' years'); $this->currentDate->modify('+' . $this->interval . ' years');
return; return;
} }
$currentMonth = $this->currentDate->format('n'); $currentMonth = $this->currentDate->format('n');
@ -892,8 +925,8 @@ class Sabre_VObject_RecurrenceIterator implements Iterator {
} else { } else {
// no byDay or byMonthDay, so we can just loop through the // These are the 'byMonth' rules, if there are no byDay or
// months. // byMonthDay sub-rules.
do { do {
$currentMonth++; $currentMonth++;
@ -903,6 +936,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator {
} }
} while (!in_array($currentMonth, $this->byMonth)); } while (!in_array($currentMonth, $this->byMonth));
$this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
return; return;
} }

View file

@ -0,0 +1,351 @@
<?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)
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_VObject_TimeZoneUtil {
public 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',
// Microsoft exchange timezones
// Source:
// http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx
//
// Correct timezones deduced with help from:
// http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
'Universal Coordinated Time' => 'UTC',
'Casablanca, Monrovia' => 'Africa/Casablanca',
'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London',
'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
'Prague, Central Europe' => 'Europe/Prague',
'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
'West Central Africa' => 'Africa/Luanda', // This was a best guess
'Athens, Istanbul, Minsk' => 'Europe/Athens',
'Bucharest' => 'Europe/Bucharest',
'Cairo' => 'Africa/Cairo',
'Harare, Pretoria' => 'Africa/Harare',
'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
'Baghdad' => 'Asia/Baghdad',
'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
'East Africa, Nairobi' => 'Africa/Nairobi',
'Tehran' => 'Asia/Tehran',
'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
'Kabul' => 'Asia/Kabul',
'Ekaterinburg' => 'Asia/Yekaterinburg',
'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
'Kathmandu, Nepal' => 'Asia/Kathmandu',
'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
'Astana, Dhaka' => 'Asia/Dhaka',
'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
'Rangoon' => 'Asia/Rangoon',
'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
'Krasnoyarsk' => 'Asia/Krasnoyarsk',
'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
'Kuala Lumpur, Singapore' => 'Asia/Singapore',
'Perth, Western Australia' => 'Australia/Perth',
'Taipei' => 'Asia/Taipei',
'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
'Seoul, Korea Standard time' => 'Asia/Seoul',
'Yakutsk' => 'Asia/Yakutsk',
'Adelaide, Central Australia' => 'Australia/Adelaide',
'Darwin' => 'Australia/Darwin',
'Brisbane, East Australia' => 'Australia/Brisbane',
'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
'Guam, Port Moresby' => 'Pacific/Guam',
'Hobart, Tasmania' => 'Australia/Hobart',
'Vladivostok' => 'Asia/Vladivostok',
'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
'Auckland, Wellington' => 'Pacific/Auckland',
'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
'Azores' => 'Atlantic/Azores',
'Cape Verde Is.' => 'Atlantic/Cape_Verde',
'Mid-Atlantic' => 'America/Noronha',
'Brasilia' => 'America/Sao_Paulo', // Best guess
'Buenos Aires' => 'America/Argentina/Buenos_Aires',
'Greenland' => 'America/Godthab',
'Newfoundland' => 'America/St_Johns',
'Atlantic Time (Canada)' => 'America/Halifax',
'Caracas, La Paz' => 'America/Caracas',
'Santiago' => 'America/Santiago',
'Bogota, Lima, Quito' => 'America/Bogota',
'Eastern Time (US & Canada)' => 'America/New_York',
'Indiana (East)' => 'America/Indiana/Indianapolis',
'Central America' => 'America/Guatemala',
'Central Time (US & Canada)' => 'America/Chicago',
'Mexico City, Tegucigalpa' => 'America/Mexico_City',
'Saskatchewan' => 'America/Edmonton',
'Arizona' => 'America/Phoenix',
'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
'Alaska' => 'America/Anchorage',
'Hawaii' => 'Pacific/Honolulu',
'Midway Island, Samoa' => 'Pacific/Midway',
'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
);
public static $microsoftExchangeMap = array(
0 => 'UTC',
31 => 'Africa/Casablanca',
2 => 'Europe/Lisbon',
1 => 'Europe/London',
4 => 'Europe/Berlin',
6 => 'Europe/Prague',
3 => 'Europe/Paris',
69 => 'Africa/Luanda', // This was a best guess
7 => 'Europe/Athens',
5 => 'Europe/Bucharest',
49 => 'Africa/Cairo',
50 => 'Africa/Harare',
59 => 'Europe/Helsinki',
27 => 'Asia/Jerusalem',
26 => 'Asia/Baghdad',
74 => 'Asia/Kuwait',
51 => 'Europe/Moscow',
56 => 'Africa/Nairobi',
25 => 'Asia/Tehran',
24 => 'Asia/Muscat', // Best guess
54 => 'Asia/Baku',
48 => 'Asia/Kabul',
58 => 'Asia/Yekaterinburg',
47 => 'Asia/Karachi',
23 => 'Asia/Calcutta',
62 => 'Asia/Kathmandu',
46 => 'Asia/Almaty',
71 => 'Asia/Dhaka',
66 => 'Asia/Colombo',
61 => 'Asia/Rangoon',
22 => 'Asia/Bangkok',
64 => 'Asia/Krasnoyarsk',
45 => 'Asia/Shanghai',
63 => 'Asia/Irkutsk',
21 => 'Asia/Singapore',
73 => 'Australia/Perth',
75 => 'Asia/Taipei',
20 => 'Asia/Tokyo',
72 => 'Asia/Seoul',
70 => 'Asia/Yakutsk',
19 => 'Australia/Adelaide',
44 => 'Australia/Darwin',
18 => 'Australia/Brisbane',
76 => 'Australia/Sydney',
43 => 'Pacific/Guam',
42 => 'Australia/Hobart',
68 => 'Asia/Vladivostok',
41 => 'Asia/Magadan',
17 => 'Pacific/Auckland',
40 => 'Pacific/Fiji',
67 => 'Pacific/Tongatapu',
29 => 'Atlantic/Azores',
53 => 'Atlantic/Cape_Verde',
30 => 'America/Noronha',
8 => 'America/Sao_Paulo', // Best guess
32 => 'America/Argentina/Buenos_Aires',
69 => 'America/Godthab',
28 => 'America/St_Johns',
9 => 'America/Halifax',
33 => 'America/Caracas',
65 => 'America/Santiago',
35 => 'America/Bogota',
10 => 'America/New_York',
34 => 'America/Indiana/Indianapolis',
55 => 'America/Guatemala',
11 => 'America/Chicago',
37 => 'America/Mexico_City',
36 => 'America/Edmonton',
38 => 'America/Phoenix',
12 => 'America/Denver', // Best guess
13 => 'America/Los_Angeles', // Best guess
14 => 'America/Anchorage',
15 => 'Pacific/Honolulu',
16 => 'Pacific/Midway',
39 => 'Pacific/Kwajalein',
);
/**
* This method will try to find out the correct timezone for an iCalendar
* date-time value.
*
* You must pass the contents of the TZID parameter, as well as the full
* calendar.
*
* If the lookup fails, this method will return UTC.
*
* @param string $tzid
* @param Sabre_VObject_Component $vcalendar
* @return DateTimeZone
*/
static public function getTimeZone($tzid, Sabre_VObject_Component $vcalendar = null) {
// First we will just see if the tzid is a support timezone identifier.
try {
return new DateTimeZone($tzid);
} catch (\Exception $e) {
}
// Next, we check if the tzid is somewhere in our tzid map.
if (isset(self::$map[$tzid])) {
return new DateTimeZone(self::$map[$tzid]);
}
if ($vcalendar) {
// If that didn't work, we will scan VTIMEZONE objects
foreach($vcalendar->select('VTIMEZONE') as $vtimezone) {
if ((string)$vtimezone->TZID === $tzid) {
// Some clients add 'X-LIC-LOCATION' with the olson name.
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
try {
return new DateTimeZone($vtimezone->{'X-LIC-LOCATION'});
} catch (\Exception $e) {
}
}
// Microsoft may add a magic number, which we also have an
// answer for.
if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
if (isset(self::$microsoftExchangeMap[(int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value])) {
return new DateTimeZone(self::$microsoftExchangeMap[(int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value]);
}
}
}
}
}
// If we got all the way here, we default to UTC.
return new DateTimeZone(date_default_timezone_get());
}
}

View file

@ -14,7 +14,7 @@ class Sabre_VObject_Version {
/** /**
* Full version number * Full version number
*/ */
const VERSION = '1.3.3'; const VERSION = '1.3.4';
/** /**
* Stability : alpha, beta, stable * Stability : alpha, beta, stable

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

@ -24,8 +24,8 @@ include __DIR__ . '/Parameter.php';
include __DIR__ . '/ParseException.php'; include __DIR__ . '/ParseException.php';
include __DIR__ . '/Reader.php'; include __DIR__ . '/Reader.php';
include __DIR__ . '/RecurrenceIterator.php'; include __DIR__ . '/RecurrenceIterator.php';
include __DIR__ . '/TimeZoneUtil.php';
include __DIR__ . '/Version.php'; include __DIR__ . '/Version.php';
include __DIR__ . '/WindowsTimezoneMap.php';
include __DIR__ . '/Element.php'; include __DIR__ . '/Element.php';
include __DIR__ . '/Property.php'; include __DIR__ . '/Property.php';
include __DIR__ . '/Component.php'; include __DIR__ . '/Component.php';

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

@ -343,6 +343,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 +612,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 +757,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

@ -55,6 +55,56 @@ class Sabre_CalDAV_ICSExportPluginTest extends PHPUnit_Framework_TestCase {
$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 = Sabre_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));

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,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(
@ -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

@ -53,9 +53,14 @@ class Sabre_VObject_Component_VEventTest extends PHPUnit_Framework_TestCase {
$vevent6->DTEND['VALUE'] = 'DATE'; $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('2012-01-01'), true);
$tests[] = array($vevent6, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); $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);
// Added this test to ensure that recurrence rules with no DTEND also
// get checked for the entire day.
$vevent7 = clone $vevent;
$vevent7->DTSTART = '20120101';
$vevent7->DTSTART['VALUE'] = 'DATE';
$vevent7->RRULE = 'FREQ=MONTHLY';
$tests[] = array($vevent7, new DateTime('2012-02-01 15:00:00'), new DateTime('2012-02-02'), true);
return $tests; return $tests;
} }

View file

@ -340,11 +340,14 @@ class Sabre_VObject_ComponentTest extends PHPUnit_Framework_TestCase {
new Sabre_VObject_Component('VEVENT'), new Sabre_VObject_Component('VEVENT'),
new Sabre_VObject_Component('VTODO') new Sabre_VObject_Component('VTODO')
); );
$this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $comp->serialize());
$str = $comp->serialize();
$this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", $str);
} }
function testSerializeOrder() { function testSerializeOrderCompAndProp() {
$comp = new Sabre_VObject_Component('VCALENDAR'); $comp = new Sabre_VObject_Component('VCALENDAR');
$comp->add(new Sabre_VObject_Component('VEVENT')); $comp->add(new Sabre_VObject_Component('VEVENT'));
@ -358,4 +361,30 @@ class Sabre_VObject_ComponentTest extends PHPUnit_Framework_TestCase {
} }
function testAnotherSerializeOrderProp() {
$prop4s=array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10');
$comp = new Sabre_VObject_Component('VCARD');
$comp->__set('SOMEPROP','FOO');
$comp->__set('ANOTHERPROP','FOO');
$comp->__set('THIRDPROP','FOO');
foreach ($prop4s as $prop4) {
$comp->add('PROP4', 'FOO '.$prop4);
}
$comp->__set('PROPNUMBERFIVE', 'FOO');
$comp->__set('PROPNUMBERSIX', 'FOO');
$comp->__set('PROPNUMBERSEVEN', 'FOO');
$comp->__set('PROPNUMBEREIGHT', 'FOO');
$comp->__set('PROPNUMBERNINE', 'FOO');
$comp->__set('PROPNUMBERTEN', 'FOO');
$comp->__set('VERSION','2.0');
$comp->__set('UID', 'FOO');
$str = $comp->serialize();
$this->assertEquals("BEGIN:VCARD\r\nVERSION:2.0\r\nSOMEPROP:FOO\r\nANOTHERPROP:FOO\r\nTHIRDPROP:FOO\r\nPROP4:FOO 1\r\nPROP4:FOO 2\r\nPROP4:FOO 3\r\nPROP4:FOO 4\r\nPROP4:FOO 5\r\nPROP4:FOO 6\r\nPROP4:FOO 7\r\nPROP4:FOO 8\r\nPROP4:FOO 9\r\nPROP4:FOO 10\r\nPROPNUMBERFIVE:FOO\r\nPROPNUMBERSIX:FOO\r\nPROPNUMBERSEVEN:FOO\r\nPROPNUMBEREIGHT:FOO\r\nPROPNUMBERNINE:FOO\r\nPROPNUMBERTEN:FOO\r\nUID:FOO\r\nEND:VCARD\r\n", $str);
}
} }

View file

@ -6,9 +6,9 @@ class Sabre_VObject_Issue154Test extends PHPUnit_Framework_TestCase {
$vcard = new Sabre_VObject_Component('VCARD'); $vcard = new Sabre_VObject_Component('VCARD');
$vcard->VERSION = '3.0'; $vcard->VERSION = '3.0';
$vcard->UID = 'foo-bar';
$vcard->PHOTO = base64_encode('random_stuff'); $vcard->PHOTO = base64_encode('random_stuff');
$vcard->PHOTO->add('BASE64',null); $vcard->PHOTO->add('BASE64',null);
$vcard->UID = 'foo-bar';
$result = $vcard->serialize(); $result = $vcard->serialize();
$expected = array( $expected = array(

View file

@ -707,6 +707,51 @@ class Sabre_VObject_RecurrenceIteratorTest extends PHPUnit_Framework_TestCase {
} }
/**
* @depends testValues
*/
function testYearlyLeapYear() {
$ev = new Sabre_VObject_Component('VEVENT');
$ev->UID = 'bla';
$ev->RRULE = 'FREQ=YEARLY;COUNT=3';
$dtStart = new Sabre_VObject_Property_DateTime('DTSTART');
$dtStart->setDateTime(new DateTime('2012-02-29'),Sabre_VObject_Property_DateTime::UTC);
$ev->add($dtStart);
$vcal = Sabre_VObject_Component::create('VCALENDAR');
$vcal->add($ev);
$it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid);
$this->assertEquals('yearly', $it->frequency);
$this->assertEquals(3, $it->count);
$max = 20;
$result = array();
foreach($it as $k=>$item) {
$result[] = $item;
$max--;
if (!$max) break;
}
$tz = new DateTimeZone('UTC');
$this->assertEquals(
array(
new DateTime('2012-02-29', $tz),
new DateTime('2016-02-29', $tz),
new DateTime('2020-02-29', $tz),
),
$result
);
}
/** /**
* @depends testValues * @depends testValues
*/ */

View file

@ -0,0 +1,153 @@
<?php
class Sabre_VObject_TimeZoneUtilTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider getMapping
*/
function testCorrectTZ($timezoneName) {
$tz = new DateTimeZone($timezoneName);
}
function getMapping() {
// PHPUNit requires an array of arrays
return array_map(
function($value) {
return array($value);
},
Sabre_VObject_TimeZoneUtil::$map
);
}
function testExchangeMap() {
$vobj = <<<HI
BEGIN:VCALENDAR
METHOD:REQUEST
VERSION:2.0
BEGIN:VTIMEZONE
TZID:foo
X-MICROSOFT-CDO-TZID:2
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20120416T092149Z
DTSTART;TZID="foo":20120418T1
00000
SUMMARY:Begin Unterhaltsreinigung
UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
0100000008FECD2E607780649BE5A4C9EE6418CBC
DTEND;TZID="Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb":20120418T103
000
END:VEVENT
END:VCALENDAR
HI;
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone('foo', Sabre_VObject_Reader::read($vobj));
$this->assertEquals(new DateTimeZone('Europe/Sarajevo'), $tz);
}
function testUnknownExchangeId() {
$vobj = <<<HI
BEGIN:VCALENDAR
METHOD:REQUEST
VERSION:2.0
BEGIN:VTIMEZONE
TZID:foo
X-MICROSOFT-CDO-TZID:2000
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20120416T092149Z
DTSTART;TZID="foo":20120418T1
00000
SUMMARY:Begin Unterhaltsreinigung
UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
0100000008FECD2E607780649BE5A4C9EE6418CBC
DTEND;TZID="Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb":20120418T103
000
END:VEVENT
END:VCALENDAR
HI;
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone('foo', Sabre_VObject_Reader::read($vobj));
$this->assertEquals(new DateTimeZone(date_default_timezone_get()), $tz);
}
function testWindowsTimeZone() {
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone('Eastern Standard Time');
$this->assertEquals(new DateTimeZone('America/New_York'), $tz);
}
function testFallBack() {
$vobj = <<<HI
BEGIN:VCALENDAR
METHOD:REQUEST
VERSION:2.0
BEGIN:VTIMEZONE
TZID:foo
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20120416T092149Z
DTSTART;TZID="foo":20120418T1
00000
SUMMARY:Begin Unterhaltsreinigung
UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
0100000008FECD2E607780649BE5A4C9EE6418CBC
000
END:VEVENT
END:VCALENDAR
HI;
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone('foo', Sabre_VObject_Reader::read($vobj));
$this->assertEquals(new DateTimeZone(date_default_timezone_get()), $tz);
}
}

View file

@ -3,7 +3,7 @@
$a = get_app(); $a = get_app();
$uri = parse_url($a->get_baseurl()); $uri = parse_url($a->get_baseurl());
$path = "/"; $path = "/";
if (strlen($uri["path"]) > 1) { if (isset($uri["path"]) && strlen($uri["path"]) > 1) {
$path = $uri["path"] . "/"; $path = $uri["path"] . "/";
} }
@ -12,20 +12,22 @@ define("CALDAV_SQL_PREFIX", "dav_");
define("CALDAV_URL_PREFIX", $path . "dav/"); define("CALDAV_URL_PREFIX", $path . "dav/");
define("CALDAV_NAMESPACE_PRIVATE", 1); define("CALDAV_NAMESPACE_PRIVATE", 1);
define("CALDAV_NAMESPACE_FRIENDICA_NATIVE", 2);
define("CALDAV_FRIENDICA_MINE", 1); define("CALDAV_FRIENDICA_MINE", "friendica-mine");
define("CALDAV_FRIENDICA_CONTACTS", 2); define("CALDAV_FRIENDICA_CONTACTS", "friendica-contacts");
$GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"] = array(CALDAV_FRIENDICA_MINE, CALDAV_FRIENDICA_CONTACTS);
define("CARDDAV_NAMESPACE_COMMUNITYCONTACTS", 1); define("CARDDAV_NAMESPACE_COMMUNITYCONTACTS", 1);
define("CARDDAV_NAMESPACE_PHONECONTACTS", 2); define("CARDDAV_NAMESPACE_PHONECONTACTS", 2);
define("CALDAV_DB_VERSION", 1); define("CALDAV_MAX_YEAR", date("Y") + 5);
/** /**
* @return int * @return int
*/ */
function getCurMicrotime () { function getCurMicrotime()
{
list($usec, $sec) = explode(" ", microtime()); list($usec, $sec) = explode(" ", microtime());
return sprintf("%14.0f", $sec * 10000 + $usec * 10000); return sprintf("%14.0f", $sec * 10000 + $usec * 10000);
} // function getCurMicrotime } // function getCurMicrotime
@ -33,7 +35,8 @@ function getCurMicrotime () {
/** /**
* *
*/ */
function debug_time() { function debug_time()
{
$cur = getCurMicrotime(); $cur = getCurMicrotime();
if ($GLOBALS["debug_time_last"] > 0) { if ($GLOBALS["debug_time_last"] > 0) {
echo "Zeit: " . ($cur - $GLOBALS["debug_time_last"]) . "<br>\n"; echo "Zeit: " . ($cur - $GLOBALS["debug_time_last"]) . "<br>\n";
@ -48,7 +51,7 @@ function debug_time() {
*/ */
function dav_compat_username2id($username = "") function dav_compat_username2id($username = "")
{ {
$x = q("SELECT `uid` FROM user WHERE nickname='%s' AND account_removed = 0 AND account_expired = 0", dbesc($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"]; if (count($x) == 1) return $x[0]["uid"];
return null; return null;
} }
@ -59,7 +62,7 @@ function dav_compat_username2id($username = "")
*/ */
function dav_compat_id2username($id = 0) 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)); $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"]; if (count($x) == 1) return $x[0]["nickname"];
return ""; return "";
} }
@ -67,7 +70,8 @@ function dav_compat_id2username($id = 0)
/** /**
* @return int * @return int
*/ */
function dav_compat_get_curr_user_id() { function dav_compat_get_curr_user_id()
{
$a = get_app(); $a = get_app();
return IntVal($a->user["uid"]); return IntVal($a->user["uid"]);
} }
@ -86,13 +90,38 @@ function dav_compat_principal2uid($principalUri = "")
return dav_compat_username2id($username); return dav_compat_username2id($username);
} }
/**
* @param string $principalUri
* @return array|null
*/
function dav_compat_principal2namespace($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 array("namespace" => CALDAV_NAMESPACE_PRIVATE, "namespace_id" => dav_compat_username2id($username));
}
/**
* @return string
*/
function dav_compat_currentUserPrincipal()
{
$a = get_app();
return "principals/users/" . strtolower($a->user["nickname"]);
}
/** /**
* @param string $name * @param string $name
* @return null|string * @return null|string
*/ */
function dav_compat_getRequestVar($name = "") { function dav_compat_getRequestVar($name = "")
if (x($_REQUEST, $name)) return $_REQUEST[$name]; {
if (isset($_REQUEST[$name])) return $_REQUEST[$name];
else return null; else return null;
} }
@ -108,28 +137,73 @@ function dav_compat_parse_text_serverside($text)
/** /**
* @param string $uri * @param string $uri
*/ */
function dav_compat_redirect($uri = "") { function dav_compat_redirect($uri = "")
{
goaway($uri); goaway($uri);
} }
/**
* @return null|int
*/
function dav_compat_get_max_private_calendars()
{
return null;
}
/** /**
* @param int $user_id
* @param int $namespace * @param int $namespace
* @param int $namespace_id * @param int $namespace_id
* @return AnimexxCalSource * @param string $uri
* @param array $calendar
* @return Sabre_CalDAV_Backend_Common
* @throws Exception * @throws Exception
*/ */
function wdcal_calendar_factory($user_id, $namespace, $namespace_id) function wdcal_calendar_factory($namespace, $namespace_id, $uri, $calendar = null)
{ {
switch ($namespace) { switch ($namespace) {
case CALDAV_NAMESPACE_PRIVATE: case CALDAV_NAMESPACE_PRIVATE:
return new AnimexxCalSourcePrivate($user_id, $namespace_id); if ($uri == CALDAV_FRIENDICA_MINE || $uri == CALDAV_FRIENDICA_CONTACTS) return Sabre_CalDAV_Backend_Friendica::getInstance();
case CALDAV_NAMESPACE_FRIENDICA_NATIVE: else return Sabre_CalDAV_Backend_Private::getInstance();
return new FriendicaCalSourceEvents($user_id, $namespace_id);
} }
throw new Exception("Calendar Namespace not found"); throw new Exception("Calendar Namespace not found");
} }
/**
* @param int $calendar_id
* @return Sabre_CalDAV_Backend_Common
* @throws Exception
*/
function wdcal_calendar_factory_by_id($calendar_id)
{
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendar_id);
return wdcal_calendar_factory($calendar["namespace"], $calendar["namespace_id"], $calendar["uri"], $calendar);
}
/**
* @param int $user_id
* @param bool $withcheck
* @return array
*/
function wdcal_create_std_calendars_get_statements($user_id, $withcheck = true)
{
$stms = array();
$a = get_app();
$uris = array(
'private' => t("Private Calendar"),
CALDAV_FRIENDICA_MINE => t("Friendica Events: Mine"),
CALDAV_FRIENDICA_CONTACTS => t("Friendica Events: Contacts"),
);
foreach ($uris as $uri => $name) {
$cals = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, IntVal($user_id), dbesc($uri));
if (count($cals) == 0 || !$withcheck) $stms[] =
sprintf("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `displayname`, `timezone`, `ctag`, `uri`, `has_vevent`, `has_vtodo`)
VALUES (%d, %d, '%s', '%s', 1, '%s', 1, 0)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, IntVal($user_id), dbesc($name), dbesc($a->timezone), dbesc($uri));
}
return $stms;
}
/** /**
*/ */
@ -138,27 +212,9 @@ function wdcal_create_std_calendars()
$a = get_app(); $a = get_app();
if (!local_user()) return; 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); $privates = q("SELECT COUNT(*) num FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, IntVal($a->user["uid"]));
if (count($cals) == 0) { if ($privates[0]["num"] > 0) return;
$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); $stms = wdcal_create_std_calendars_get_statements($a->user["uid"]);
if (count($cals) < 2) { foreach ($stms as $stmt) q($stmt);
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,15 @@
<?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,7 +92,7 @@ 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 */
@ -136,41 +145,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 +187,192 @@ 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()
{
$caldavBackend_std = Sabre_CalDAV_Backend_Private::getInstance();
$caldavBackend_community = Sabre_CalDAV_Backend_Friendica::getInstance();
return new Sabre_CalDAV_AnimexxCalendarRootNode(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), array(
$caldavBackend_std,
$caldavBackend_community,
));
}
/**
* @return Sabre_CardDAV_AddressBookRootFriendica
*/
function dav_createRootContactsNode()
{
$carddavBackend_std = Sabre_CardDAV_Backend_Std::getInstance();
$carddavBackend_community = Sabre_CardDAV_Backend_FriendicaCommunity::getInstance();
return new Sabre_CardDAV_AddressBookRootFriendica(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), array(
$carddavBackend_std,
$carddavBackend_community,
));
}
/**
* @param bool $force_authentication
* @param bool $needs_caldav
* @param bool $needs_carddav
* @return Sabre_DAV_Server
*/
function dav_create_server($force_authentication = false, $needs_caldav = true, $needs_carddav = true)
{
$arr = array(
new Sabre_DAV_SimpleCollection('principals', array(
new Sabre_CalDAV_Principal_Collection(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), "principals/users"),
)),
);
if ($needs_caldav) $arr[] = dav_createRootCalendarNode();
if ($needs_carddav) $arr[] = dav_createRootContactsNode();
$tree = new Sabre_DAV_SimpleCollection('root', $arr);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($tree);
$server->setBaseUri(CALDAV_URL_PREFIX);
$authPlugin = new Sabre_DAV_Auth_Plugin(Sabre_DAV_Auth_Backend_Std::getInstance(), 'SabreDAV');
$server->addPlugin($authPlugin);
$aclPlugin = new Sabre_DAVACL_Plugin_Friendica();
$aclPlugin->defaultUsernamePath = "principals/users";
$server->addPlugin($aclPlugin);
if ($needs_caldav) {
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
}
if ($needs_carddav) {
$carddavPlugin = new Sabre_CardDAV_Plugin();
$server->addPlugin($carddavPlugin);
}
if ($force_authentication) $server->broadcastEvent('beforeMethod', array("GET", "/")); // Make it authenticate
return $server;
}
/**
* @param Sabre_DAV_Server $server
* @param string $with_privilege
* @return array|Sabre_CalDAV_Calendar[]
*/
function dav_get_current_user_calendars(&$server, $with_privilege = "")
{
if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
$a = get_app();
$calendar_path = "/calendars/" . strtolower($a->user["nickname"]) . "/";
/** @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")) {
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 dav_get_current_user_calendarobject(&$server, &$calendar, $calendarobject_uri, $with_privilege = "")
{
$obj = $calendar->getChild($calendarobject_uri);
if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
$a = get_app();
$uri = "/calendars/" . strtolower($a->user["nickname"]) . "/" . $calendar->getName() . "/" . $calendarobject_uri;
/** @var Sabre_DAVACL_Plugin $aclplugin */
$aclplugin = $server->getPlugin("acl");
if (!$aclplugin->checkPrivileges($uri, $with_privilege, Sabre_DAVACL_Plugin::R_PARENT, false)) return null;
$data = $obj->get();
$vObject = Sabre_VObject_Reader::read($data);
return $vObject;
}
/**
* @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 = "")
{ {
$a = get_app(); $a = get_app();
if ($uid == "") $uid = uniqid();
$v = new vcalendar(); return Sabre_VObject_Reader::read("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Friendica//DAV-Plugin//EN\r\nBEGIN:VEVENT\r\nUID:" . $uid . "@" . $a->get_hostname() .
$v->setConfig('unique_id', $a->get_hostname()); "\r\nDTSTAMP:" . date("Ymd") . "T" . date("His") . "Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
$v->parse($calendarobject->calendardata);
$v->sort();
$eventArray = $v->selectComponents(2009, 1, 1, date("Y") + 2, 12, 30);
$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 Sabre_VObject_Component_VCalendar $vObject
* @return Sabre_VObject_Component_VEvent|null
*/ */
function renderAllCalDavEntries() function dav_get_eventComponent(&$vObject)
{ {
q("DELETE FROM %s%sjqcalendar", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); $component = null;
q("DELETE FROM %s%snotifications", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); $componentType = "";
$calendars = q("SELECT * FROM %s%scalendars", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); foreach ($vObject->getComponents() as $component) {
$anz = count($calendars); if ($component->name !== 'VTIMEZONE') {
$i = 0; $componentType = $component->name;
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();
foreach ($d as $e) $cals[] = new DBClass_friendica_calendars($e);
return $cals;
}
/**
* @param mixed $obj
* @return string
*/
function wdcal_jsonp_encode($obj)
{
$str = json_encode($obj);
if (isset($_REQUEST["callback"])) {
$str = $_REQUEST["callback"] . "(" . $str . ")";
}
return $str;
}
/**
* @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);
}
/**
* @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 {
$start = $localization->date_parseLocal($_REQUEST["start_date"] . " " . $_REQUEST["start_time"]);
$end = $localization->date_parseLocal($_REQUEST["end_date"] . " " . $_REQUEST["end_time"]);
$isallday = false;
}
if ($uri == "new") {
$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 {
$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) {
$cs = wdcal_calendar_factory($uid, $c->namespace, $c->namespace_id);
$p = $cs->getPermissionsItem($uid, $uri, $recurr_uri);
if ($p["write"]) try {
$cs->updateItem($uri, $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);
}
}
}
/**
*
*/
function wdcal_print_feed($base_path = "")
{
$user_id = dav_compat_get_curr_user_id();
$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;
/** @var $cals array|AnimexxCalSource[] */
$method = $_GET["method"];
switch ($method) {
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) {
$ret = array(
'IsSuccess' => false,
'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"]);
$ret = array();
$ret['events'] = array();
$ret["issort"] = true;
$ret["start"] = $date[0];
$ret["end"] = $date[1];
$ret['error'] = null;
foreach ($cals as $c) {
$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; break;
} }
echo wdcal_jsonp_encode($ret);
killme();
} }
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,14 +1,11 @@
<?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',
@ -17,60 +14,166 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
); );
abstract public function getNamespace();
abstract public function getCalUrlPrefix();
/** /**
* @param int $namespace * @abstract
* @param int $namespace_id * @return int
*/ */
protected function increaseCalendarCtag($namespace, $namespace_id) { abstract public function getNamespace();
$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);
}
/**
* @static
* @abstract
* @return string
*/
abstract public static function getBackendTypeName();
/** /**
* Returns a list of calendars for a principal. * @param int $calendarId
* * @param string $sd
* Every project is an array with the following keys: * @param string $ed
* * id, a unique id that will be used by other functions to modify the * @param string $base_path
* 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 * @return array
*/ */
public function getCalendarsForUser($principalUri) 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)
{ {
list(,$name) = Sabre_DAV_URLUtil::splitPath($principalUri); if (!isset(self::$calendarCache[$calendarId])) {
$user_id = dav_compat_username2id($name); $c = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarCache[$calendarId] = $c[0];
$cals = q("SELECT * FROM %s%scalendars WHERE `uid`=%d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $user_id, $this->getNamespace()); }
$ret = array(); return self::$calendarCache[$calendarId];
foreach ($cals as $cal) {
$dat = array(
"id" => $cal["namespace"] . "-" . $cal["namespace_id"],
"uri" => $this->getCalUrlPrefix() . "-" . $cal["namespace_id"],
"principaluri" => $principalUri,
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag']?$cal['ctag']:'0',
"calendar_class" => "Sabre_CalDAV_Calendar",
);
foreach ($this->propertyMap as $key=>$field) $dat[$key] = $cal[$field];
$ret[] = $dat;
} }
return $ret; /**
* @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];
}
/**
* @static
* @param Sabre_VObject_Component_VEvent $component
* @return int
*/
public static function getDtEndTimeStamp(&$component)
{
/** @var Sabre_VObject_Property_DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
if ($component->__get("DTEND")) {
/** @var Sabre_VObject_Property_DateTime $dtend */
$dtend = $component->__get("DTEND");
return $dtend->getDateTime()->getTimeStamp();
} elseif ($component->__get("DURATION")) {
$endDate = clone $dtstart->getDateTime();
$endDate->add(Sabre_VObject_DateTimeParser::parse($component->__get("DURATION")->value));
return $endDate->getTimeStamp();
} elseif ($dtstart->getDateType() === Sabre_VObject_Property_DateTime::DATE) {
$endDate = clone $dtstart->getDateTime();
$endDate->modify('+1 day');
return $endDate->getTimeStamp();
} else {
return $dtstart->getDateTime()->getTimeStamp() + 3600;
}
}
/**
* 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,7 +212,8 @@ 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(
@ -151,20 +255,33 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
} }
$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",
);
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,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

@ -14,6 +14,23 @@
class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Abstract 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
*/ */

View file

@ -1,14 +1,6 @@
<?php <?php
/**
* The UserCalenders class contains all calendars associated to one user
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2011 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_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL { class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
/** /**
@ -21,7 +13,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
/** /**
* CalDAV backends * CalDAV backends
* *
* @var array|Sabre_CalDAV_Backend_Abstract[] * @var array|Sabre_CalDAV_Backend_Common[]
*/ */
protected $caldavBackends; protected $caldavBackends;
@ -36,7 +28,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
* Constructor * Constructor
* *
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param array|Sabre_CalDAV_Backend_Abstract $caldavBackends * @param array|Sabre_CalDAV_Backend_Common[] $caldavBackends
* @param mixed $userUri * @param mixed $userUri
*/ */
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $caldavBackends, $userUri) { public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $caldavBackends, $userUri) {
@ -130,7 +122,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
* Returns a single calendar, by name * Returns a single calendar, by name
* *
* @param string $name * @param string $name
* @throws Sabre_DAV_Exception_FileNotFound * @throws Sabre_DAV_Exception_NotFound
* @todo needs optimizing * @todo needs optimizing
* @return \Sabre_CalDAV_Calendar|\Sabre_DAV_INode * @return \Sabre_CalDAV_Calendar|\Sabre_DAV_INode
*/ */
@ -141,7 +133,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
return $child; return $child;
} }
throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found'); throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found');
} }

View file

@ -1,28 +0,0 @@
<?php
# Generated automatically - do not change!
class DBClass_friendica_calendarobjects extends DBClass_animexx {
/** @var $PRIMARY_KEY array */
public $PRIMARY_KEY = array("id");
protected $SRC_TABLE = 'calendarobjects';
/** @var $calendardata string|null */
/** @var $uri string */
/** @var $lastmodified string|null */
/** @var $etag string */
public $calendardata, $uri, $lastmodified, $etag;
/** @var $id int */
/** @var $namespace int */
/** @var $namespace_id int */
/** @var $size int */
public $id, $namespace, $namespace_id, $size;
protected $_string_fields = array('calendardata', 'uri', 'lastmodified', 'etag');
protected $_int_fields = array('id', 'namespace', 'namespace_id', 'size');
protected $_null_fields = array('calendardata', 'lastmodified');
}

View file

@ -1,29 +0,0 @@
<?php
# Generated automatically - do not change!
class DBClass_friendica_calendars extends DBClass_animexx {
/** @var $PRIMARY_KEY array */
public $PRIMARY_KEY = array("namespace", "namespace_id");
protected $SRC_TABLE = 'calendars';
/** @var $calendarcolor string */
/** @var $displayname string */
/** @var $timezone string */
/** @var $description string */
public $calendarcolor, $displayname, $timezone, $description;
/** @var $namespace int */
/** @var $namespace_id int */
/** @var $uid int */
/** @var $calendarorder int */
/** @var $ctag int */
public $namespace, $namespace_id, $uid, $calendarorder, $ctag;
protected $_string_fields = array('calendarcolor', 'displayname', 'timezone', 'description');
protected $_int_fields = array('namespace', 'namespace_id', 'uid', 'calendarorder', 'ctag');
protected $_null_fields = array();
}

View file

@ -1,35 +0,0 @@
<?php
# Generated automatically - do not change!
class DBClass_friendica_jqcalendar extends DBClass_animexx {
/** @var $PRIMARY_KEY array */
public $PRIMARY_KEY = array("id");
protected $SRC_TABLE = 'jqcalendar';
/** @var $ical_uri string */
/** @var $ical_recurr_uri string */
/** @var $Subject string|null */
/** @var $Location string|null */
/** @var $Description string|null */
/** @var $StartTime string|null */
/** @var $EndTime string|null */
/** @var $Color string|null */
/** @var $RecurringRule string|null */
public $ical_uri, $ical_recurr_uri, $Subject, $Location, $Description, $StartTime, $EndTime, $Color, $RecurringRule;
/** @var $id int */
/** @var $uid int */
/** @var $namespace int */
/** @var $namespace_id int */
/** @var $permission_edit int */
/** @var $IsAllDayEvent int */
public $id, $uid, $namespace, $namespace_id, $permission_edit, $IsAllDayEvent;
protected $_string_fields = array('ical_uri', 'ical_recurr_uri', 'Subject', 'Location', 'Description', 'StartTime', 'EndTime', 'Color', 'RecurringRule');
protected $_int_fields = array('id', 'uid', 'namespace', 'namespace_id', 'permission_edit', 'IsAllDayEvent');
protected $_null_fields = array('Subject', 'Location', 'Description', 'StartTime', 'EndTime', 'Color', 'RecurringRule');
}

View file

@ -1,40 +0,0 @@
<?php
# Generated automatically - do not change!
class DBClass_friendica_notifications extends DBClass_animexx {
/** @var $PRIMARY_KEY array */
public $PRIMARY_KEY = array("id");
protected $SRC_TABLE = 'notifications';
/** @var $ical_uri string */
/** @var $ical_recurr_uri string */
/** @var $alert_date string */
/** @var $rel_type string */
public $ical_uri, $ical_recurr_uri, $alert_date, $rel_type;
/** @var $id int */
/** @var $uid int */
/** @var $namespace int */
/** @var $namespace_id int */
/** @var $rel_value int */
/** @var $notified int */
public $id, $uid, $namespace, $namespace_id, $rel_value, $notified;
/** @var $REL_TYPE_VALUES array */
public static $REL_TYPE_VALUES = array('second', 'minute', 'hour', 'day', 'week', 'month', 'year');
public static $REL_TYPE_SECOND = 'second';
public static $REL_TYPE_MINUTE = 'minute';
public static $REL_TYPE_HOUR = 'hour';
public static $REL_TYPE_DAY = 'day';
public static $REL_TYPE_WEEK = 'week';
public static $REL_TYPE_MONTH = 'month';
public static $REL_TYPE_YEAR = 'year';
protected $_string_fields = array('ical_uri', 'ical_recurr_uri', 'alert_date', 'rel_type');
protected $_int_fields = array('id', 'uid', 'namespace', 'namespace_id', 'rel_value', 'notified');
protected $_null_fields = array();
}

View file

@ -1,50 +0,0 @@
<?php
class DBClass_animexx
{
protected $_string_fields = array();
protected $_int_fields = array();
protected $_float_fields = array();
protected $_null_fields = array();
public $PRIMARY_KEY = array();
protected $SRC_TABLE = "";
/**
* @param $dbarray_or_id
* @throws Exception
*/
function __construct($dbarray_or_id)
{
if (is_numeric($dbarray_or_id) && count($this->PRIMARY_KEY) == 1) {
$dbarray_or_id = q("SELECT * FROM %s%s%s WHERE %s=%d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->SRC_TABLE, $this->PRIMARY_KEY[0], IntVal($dbarray_or_id)
);
if (count($dbarray_or_id) == 0) throw new Exception("Not found");
$dbarray_or_id = $dbarray_or_id[0];
}
if (is_array($dbarray_or_id)) {
foreach ($this->_string_fields as $field) {
$this->$field = $dbarray_or_id[$field];
}
foreach ($this->_int_fields as $field) {
$this->$field = IntVal($dbarray_or_id[$field]);
}
foreach ($this->_float_fields as $field) {
$this->$field = FloatVal($dbarray_or_id[$field]);
}
} else throw new Exception("Not found");
}
/**
* @return array
*/
function toArray()
{
$arr = array();
foreach ($this->_string_fields as $field) $arr[$field] = $this->$field;
foreach ($this->_int_fields as $field) $arr[$field] = $this->$field;
foreach ($this->_float_fields as $field) $arr[$field] = $this->$field;
return $arr;
}
}

View file

@ -1,63 +0,0 @@
<?php
abstract class VirtualCalSourceBackend {
/**
* @static
* @param int $uid
* @param int $namespace
*/
static public function invalidateCache($uid = 0, $namespace = 0) {
q("DELETE FROM %s%scache_synchronized WHERE `uid` = %d AND `namespace` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid), IntVal($namespace));
}
/**
* @static
* @abstract
* @param int $uid
* @param int $namespace_id
*/
static abstract function createCache($uid = 0, $namespace_id = 0);
/**
* @static
* @param int $uid
* @param int $namespace
* @return array
*/
static public function getCachedItems($uid = 0, $namespace = 0) {
$uid = IntVal($uid);
$namespace = IntVal($namespace);
$r = q("SELECT COUNT(*) n FROM %s%scache_synchronized WHERE `uid` = %d AND `namespace` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid), $namespace);
if ($r[0]["n"] == 0) self::createCache();
$r = q("SELECT * FROM %s%scal_virtual_object_cache WHERE `uid` = %d AND `namespace` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $uid, $namespace);
return $r;
}
/**
* @static
* @abstract
* @param int $uid
* @param int $namespace_id
* @param string $date_from
* @param string $date_to
* @return array
*/
abstract static public function getItemsByTime($uid = 0, $namespace_id = 0, $date_from = "", $date_to = "");
/**
* @static
* @abstract
* @param int $uid
* @param string $uri
* @return array
*/
abstract static public function getItemsByUri($uid = 0, $uri);
}

View file

@ -16,16 +16,19 @@ function wdcal_edit_getStartEnd() {
function wdcal_edit_checktime_startChanged() { function wdcal_edit_checktime_startChanged() {
"use strict"; "use strict";
var time = wdcal_edit_getStartEnd(); var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) { if (time.start.getTime() >= time.end.getTime()) {
var newend = new Date(time.start.getTime() + 3600000); var newend = new Date(time.start.getTime() + 3600000);
$("#cal_end_date").datepicker("setDate", newend); $("#cal_end_date").datepicker("setDate", newend);
$.timePicker("#cal_end_time").setTime(newend); $.timePicker("#cal_end_time").setTime(newend);
} }
wdcal_edit_recur_recalc();
} }
function wdcal_edit_checktime_endChanged() { function wdcal_edit_checktime_endChanged() {
"use strict"; "use strict";
var time = wdcal_edit_getStartEnd(); var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) { if (time.start.getTime() >= time.end.getTime()) {
var newstart = new Date(time.end.getTime() - 3600000); var newstart = new Date(time.end.getTime() - 3600000);
@ -34,10 +37,34 @@ function wdcal_edit_checktime_endChanged() {
} }
} }
function wdcal_edit_init(dateFormat) { function wdcal_edit_recur_recalc() {
"use strict";
var start = $("#cal_start_date").datepicker("getDate");
$(".rec_month_name").text($.datepicker._defaults.monthNames[start.getMonth()]);
$("#rec_yearly_day option[value=bymonthday]").text($("#rec_yearly_day option[value=bymonthday]").data("orig").replace("#num#", start.getDate()));
$("#rec_monthly_day option[value=bymonthday]").text($("#rec_monthly_day option[value=bymonthday]").data("orig").replace("#num#", start.getDate()));
var month = new Date(start.getFullYear(), start.getMonth() + 1, 0);
var monthlast = month.getDate() - start.getDate() + 1;
$("#rec_yearly_day option[value=bymonthday_neg]").text($("#rec_yearly_day option[value=bymonthday_neg]").data("orig").replace("#num#", monthlast));
$("#rec_monthly_day option[value=bymonthday_neg]").text($("#rec_monthly_day option[value=bymonthday_neg]").data("orig").replace("#num#", monthlast));
var wk = Math.ceil(start.getDate() / 7);
var wkname = $.datepicker._defaults.dayNames[start.getDay()];
$("#rec_yearly_day option[value=byday]").text($("#rec_yearly_day option[value=byday]").data("orig").replace("#num#", wk).replace("#wkday#", wkname));
$("#rec_monthly_day option[value=byday]").text($("#rec_monthly_day option[value=byday]").data("orig").replace("#num#", wk).replace("#wkday#", wkname));
var wk_inv = Math.ceil(monthlast / 7);
$("#rec_yearly_day option[value=byday_neg]").text($("#rec_yearly_day option[value=byday_neg]").data("orig").replace("#num#", wk_inv).replace("#wkday#", wkname));
$("#rec_monthly_day option[value=byday_neg]").text($("#rec_monthly_day option[value=byday_neg]").data("orig").replace("#num#", wk_inv).replace("#wkday#", wkname));
}
function wdcal_edit_init(dateFormat, base_path) {
"use strict"; "use strict";
$("#cal_color").colorPicker(); $("#cal_color").colorPicker();
$("#color_override").on("click", function() {
if ($("#color_override").prop("checked")) $("#cal_color_holder").show();
else $("#cal_color_holder").hide();
});
$("#cal_start_time").timePicker({ step: 15 }).on("change", wdcal_edit_checktime_startChanged); $("#cal_start_time").timePicker({ step: 15 }).on("change", wdcal_edit_checktime_startChanged);
$("#cal_end_time").timePicker().on("change", wdcal_edit_checktime_endChanged); $("#cal_end_time").timePicker().on("change", wdcal_edit_checktime_endChanged);
@ -49,6 +76,8 @@ function wdcal_edit_init(dateFormat) {
"dateFormat": dateFormat "dateFormat": dateFormat
}).on("change", wdcal_edit_checktime_endChanged); }).on("change", wdcal_edit_checktime_endChanged);
$("#rec_until_date").datepicker({ "dateFormat": dateFormat });
$("#notification").on("click change", function() { $("#notification").on("click change", function() {
if ($(this).prop("checked")) $("#notification_detail").show(); if ($(this).prop("checked")) $("#notification_detail").show();
else ($("#notification_detail")).hide(); else ($("#notification_detail")).hide();
@ -58,4 +87,135 @@ function wdcal_edit_init(dateFormat) {
if ($(this).prop("checked")) $("#cal_end_time, #cal_start_time").hide(); if ($(this).prop("checked")) $("#cal_end_time, #cal_start_time").hide();
else $("#cal_end_time, #cal_start_time").show(); else $("#cal_end_time, #cal_start_time").show();
}).change(); }).change();
$("#rec_frequency").on("click change", function() {
var val = $("#rec_frequency").val();
if (val == "") $("#rec_details").hide();
else $("#rec_details").show();
if (val == "daily") $(".rec_daily").show();
else $(".rec_daily").hide();
if (val == "weekly") $(".rec_weekly").show();
else $(".rec_weekly").hide();
if (val == "monthly") $(".rec_monthly").show();
else $(".rec_monthly").hide();
if (val == "yearly") $(".rec_yearly").show();
else $(".rec_yearly").hide();
}).change();
$("#rec_until_type").on("click change", function() {
var val = $("#rec_until_type").val();
if (val == "count") $("#rec_until_count").show();
else $("#rec_until_count").hide();
if (val == "date") $("#rec_until_date").show();
else $("#rec_until_date").hide();
}).change();
$("#rec_yearly_day option, #rec_monthly_day option").each(function() {
$(this).data("orig", $(this).text());
});
$("#new_alarm_adder a").click(function(ev) {
$("#new_alarm").val("1");
$("#noti_new_row").show();
$("#new_alarm_adder").hide();
ev.preventDefault();
});
wdcal_edit_recur_recalc();
$(document).on("click", ".exception_remover", function(ev) {
ev.preventDefault();
var $this = $(this),
$par = $this.parents(".rec_exceptions");
$this.parents(".except").remove();
if ($par.find(".rec_exceptions_holder").children().length == 0) {
$par.find(".rec_exceptions_holder").hide();
$par.find(".rec_exceptions_none").show();
}
});
$(".exception_adder").click(function(ev) {
ev.preventDefault();
var exceptions = [];
$(".rec_exceptions .except input").each(function() {
exceptions.push($(this).val());
});
var rec_weekly_byday = [];
$(".rec_weekly_byday:checked").each(function() {
rec_weekly_byday.push($(this).val());
});
var rec_daily_byday = [];
$(".rec_daily_byday:checked").each(function() {
rec_daily_byday.push($(this).val());
});
var opts = {
"start_date": $("input[name=start_date]").val(),
"start_time": $("input[name=start_time]").val(),
"end_date": $("input[name=end_date]").val(),
"end_time": $("input[name=end_time]").val(),
"rec_frequency": $("#rec_frequency").val(),
"rec_interval": $("#rec_interval").val(),
"rec_until_type": $("#rec_until_type").val(),
"rec_until_count": $("#rec_until_count").val(),
"rec_until_date": $("#rec_until_date").val(),
"rec_weekly_byday": rec_weekly_byday,
"rec_daily_byday": rec_daily_byday,
"rec_weekly_wkst": $("input[name=rec_weekly_wkst]:checked").val(),
"rec_monthly_day": $("#rec_monthly_day").val(),
"rec_yearly_day": $("#rec_yearly_day").val(),
"rec_exceptions": exceptions
};
if ($("#cal_allday").prop("checked")) opts["allday"] = 1;
var $dial = $("<div id='exception_setter_dialog'>Loading...</div>");
$dial.appendTo("body");
$dial.dialog({
"width": 400,
"height": 300,
"title": "Exceptions"
});
$dial.load(base_path + "getExceptionDates/", opts, function() {
$dial.find(".exception_selector_link").click(function(ev2) {
ev2.preventDefault();
var ts = $(this).data("timestamp");
var str = $(this).html();
var $part = $("<div data-timestamp='" + ts + "' class='except'><input type='hidden' class='rec_exception' name='rec_exceptions[]' value='" + ts + "'><a href='#' class='exception_remover'>[remove]</a> " + str + "</div>");
var found = false;
$(".rec_exceptions_holder .except").each(function() {
if (!found && ts < $(this).data("timestamp")) {
found = true;
$part.insertBefore(this);
}
});
if (!found) $(".rec_exceptions_holder").append($part);
$(".rec_exceptions .rec_exceptions_holder").show();
$(".rec_exceptions .rec_exceptions_none").hide();
$dial.dialog("destroy").remove();
})
});
});
}
function wdcal_edit_calendars_start(dateFormat, base_path) {
"use strict";
$(".cal_color").colorPicker();
$(".delete_cal").click(function(ev) {
if (!confirm("Do you really want to delete this calendar? All events will be moved to another private calendar.")) ev.preventDefault();
});
$(".calendar_add_caller").click(function(ev) {
$(".cal_add_row").show();
$(this).parents("div").hide();
ev.preventDefault();
});
} }

View file

@ -5,8 +5,10 @@
(function ($) { (function ($) {
"use strict"; "use strict";
var __WDAY = new Array(i18n.xgcalendar.dateformat.sun, i18n.xgcalendar.dateformat.mon, i18n.xgcalendar.dateformat.tue, i18n.xgcalendar.dateformat.wed, i18n.xgcalendar.dateformat.thu, i18n.xgcalendar.dateformat.fri, i18n.xgcalendar.dateformat.sat); var __WDAY = $.datepicker._defaults.dayNamesShort;
var __MonthName = new Array(i18n.xgcalendar.dateformat.jan, i18n.xgcalendar.dateformat.feb, i18n.xgcalendar.dateformat.mar, i18n.xgcalendar.dateformat.apr, i18n.xgcalendar.dateformat.may, i18n.xgcalendar.dateformat.jun, i18n.xgcalendar.dateformat.jul, i18n.xgcalendar.dateformat.aug, i18n.xgcalendar.dateformat.sep, i18n.xgcalendar.dateformat.oct, i18n.xgcalendar.dateformat.nov, i18n.xgcalendar.dateformat.dec); //new Array(i18n.xgcalendar.dateformat.sun, i18n.xgcalendar.dateformat.mon, i18n.xgcalendar.dateformat.tue, i18n.xgcalendar.dateformat.wed, i18n.xgcalendar.dateformat.thu, i18n.xgcalendar.dateformat.fri, i18n.xgcalendar.dateformat.sat);
var __MonthName = $.datepicker._defaults.monthNamesShort;
//new Array(i18n.xgcalendar.dateformat.jan, i18n.xgcalendar.dateformat.feb, i18n.xgcalendar.dateformat.mar, i18n.xgcalendar.dateformat.apr, i18n.xgcalendar.dateformat.may, i18n.xgcalendar.dateformat.jun, i18n.xgcalendar.dateformat.jul, i18n.xgcalendar.dateformat.aug, i18n.xgcalendar.dateformat.sep, i18n.xgcalendar.dateformat.oct, i18n.xgcalendar.dateformat.nov, i18n.xgcalendar.dateformat.dec);
function dateFormat(format) { function dateFormat(format) {
@ -769,8 +771,8 @@
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
var $col = $container.find(".tgCol" + i); var $col = $container.find(".tgCol" + i);
for (var j = 0; j < events[i].length; j++) { for (var j = 0; j < events[i].length; j++) {
if (events[i][j].event["color"] && events[i][j].event["color"].match(/^#[0-9a-f]{6}$/i)) { if (events[i][j].event["color"] && events[i][j].event["color"].match(/^[0-9a-f]{6}$/i)) {
c = events[i][j].event["color"]; c = "#" + events[i][j].event["color"];
} }
else { else {
c = option.std_color; c = option.std_color;
@ -787,7 +789,7 @@
function getTitle(event) { function getTitle(event) {
var timeshow, eventshow; var timeshow, eventshow;
var showtime = event["is_allday"] != 1; var showtime = event["is_allday"] != 1;
eventshow = event["subject"]; eventshow = event["summary"];
var startformat = getymformat(event["start"], null, showtime, true); var startformat = getymformat(event["start"], null, showtime, true);
var endformat = getymformat(event["end"], event["start"], showtime, true); var endformat = getymformat(event["end"], event["start"], showtime, true);
timeshow = dateFormat.call(event["start"], startformat) + " - " + dateFormat.call(event["end"], endformat); timeshow = dateFormat.call(event["start"], startformat) + " - " + dateFormat.call(event["end"], endformat);
@ -819,7 +821,7 @@
var p = { bdcolor:theme[0], bgcolor2:theme[0], bgcolor1:theme[2], width:"70%", icon:"", title:"", data:"" }; var p = { bdcolor:theme[0], bgcolor2:theme[0], bgcolor1:theme[2], width:"70%", icon:"", title:"", data:"" };
p.starttime = pZero(e.st.hour) + ":" + pZero(e.st.minute); p.starttime = pZero(e.st.hour) + ":" + pZero(e.st.minute);
p.endtime = pZero(e.et.hour) + ":" + pZero(e.et.minute); p.endtime = pZero(e.et.hour) + ":" + pZero(e.et.minute);
p.content = e.event["subject"]; p.content = e.event["summary"];
p.title = getTitle(e.event); p.title = getTitle(e.event);
var icons = []; var icons = [];
if (e.event["has_notification"] == 1) icons.push("<I class=\"cic cic-tmr\">&nbsp;</I>"); if (e.event["has_notification"] == 1) icons.push("<I class=\"cic cic-tmr\">&nbsp;</I>");
@ -1146,12 +1148,12 @@
var p = { color:theme[2], title:"", extendClass:"", extendHTML:"", data:"" }; var p = { color:theme[2], title:"", extendClass:"", extendHTML:"", data:"" };
p.title = getTitle(e.event); p.title = getTitle(e.event);
p.id = "bbit_cal_event_" + e.event["uri"]; p.id = "bbit_cal_event_" + e.event["jq_id"];
if (option.enableDrag && e.event["is_editable_quick"] == 1) { if (option.enableDrag && e.event["is_editable_quick"] == 1) {
p.eclass = "drag"; p.eclass = "drag";
} }
else { else {
p.eclass = "cal_" + e.event["uri"]; p.eclass = "cal_" + e.event["jq_id"];
} }
p.eclass += " " + (e.event["is_editable"] ? "editable" : "not_editable"); p.eclass += " " + (e.event["is_editable"] ? "editable" : "not_editable");
var sp = "<span style=\"cursor: pointer\">{content}</span>"; var sp = "<span style=\"cursor: pointer\">{content}</span>";
@ -1175,10 +1177,10 @@
} }
var cen; var cen;
if (!e.allday && !sf) { if (!e.allday && !sf) {
cen = pZero(e.st.hour) + ":" + pZero(e.st.minute) + " " + e.event["subject"]; cen = pZero(e.st.hour) + ":" + pZero(e.st.minute) + " " + e.event["summary"];
} }
else { else {
cen = e.event["subject"]; cen = e.event["summary"];
} }
var content = []; var content = [];
if (cen.indexOf("Geburtstag:") == 0) { if (cen.indexOf("Geburtstag:") == 0) {
@ -1294,7 +1296,7 @@
} }
if (option.eventItems[i]["start"] >= es) { if (option.eventItems[i]["start"] >= es) {
for (var j = 0; j < jl; j++) { for (var j = 0; j < jl; j++) {
if (option.eventItems[i]["uri"] == events[j]["uri"] && option.eventItems[i]["start"] < start) { if (option.eventItems[i]["jq_id"] == events[j]["jq_id"] && option.eventItems[i]["start"] < start) {
events.splice(j, 1); //for duplicated event events.splice(j, 1); //for duplicated event
jl--; jl--;
break; break;
@ -1477,7 +1479,7 @@
$("#bbit-cs-buddle").css("visibility", "hidden"); $("#bbit-cs-buddle").css("visibility", "hidden");
var calid = $("#bbit-cs-id").val(); var calid = $("#bbit-cs-id").val();
var param = [ var param = [
{ "name":"calendarId", value:calid }, { "name":"jq_id", value:calid },
{ "name":"type", value:type} { "name":"type", value:type}
]; ];
var de = rebyKey(calid, true); var de = rebyKey(calid, true);
@ -1609,8 +1611,8 @@
var location = ""; var location = "";
if (data["location"] != "") location = data["location"] + ", "; if (data["location"] != "") location = data["location"] + ", ";
$("#bbit-cs-buddle-timeshow").html(location + ss.join("")); $("#bbit-cs-buddle-timeshow").html(location + ss.join(""));
$bud.find(".bbit-cs-what").html(data["subject"]).attr("href", data["url_detail"]); $bud.find(".bbit-cs-what").html(data["summary"]).attr("href", data["url_detail"]);
$("#bbit-cs-id").val(data["uri"]); $("#bbit-cs-id").val(data["jq_id"]);
$bud.data("cdata", data); $bud.data("cdata", data);
$bud.css({ "visibility":"visible", left:pos.left, top:pos.top }); $bud.css({ "visibility":"visible", left:pos.left, top:pos.top });
@ -1684,11 +1686,11 @@
return false; return false;
} }
option.isloading = true; option.isloading = true;
var id = data["uri"]; var id = data["jq_id"];
var os = data["start"]; var os = data["start"];
var od = data["end"]; var od = data["end"];
var param = [ var param = [
{ "name":"calendarId", value:id }, { "name":"jq_id", value:id },
{ "name":"CalendarStartTime", value:Math.floor(start.getTime() / 1000) }, { "name":"CalendarStartTime", value:Math.floor(start.getTime() / 1000) },
{ "name":"CalendarEndTime", value:Math.floor(end.getTime() / 1000) } { "name":"CalendarEndTime", value:Math.floor(end.getTime() / 1000) }
]; ];
@ -1744,7 +1746,7 @@
temparr.push('<table class="cb-table"><tbody><tr><th class="cb-key">'); temparr.push('<table class="cb-table"><tbody><tr><th class="cb-key">');
temparr.push(i18n.xgcalendar.time, ':</th><td class=cb-value><div id="bbit-cal-buddle-timeshow"></div></td></tr><tr><th class="cb-key">'); temparr.push(i18n.xgcalendar.time, ':</th><td class=cb-value><div id="bbit-cal-buddle-timeshow"></div></td></tr><tr><th class="cb-key">');
temparr.push(i18n.xgcalendar.content, ':</th><td class="cb-value"><div class="textbox-fill-wrapper"><div class="textbox-fill-mid"><input id="bbit-cal-what" class="textbox-fill-input"/></div></div><div class="cb-example">'); temparr.push(i18n.xgcalendar.content, ':</th><td class="cb-value"><div class="textbox-fill-wrapper"><div class="textbox-fill-mid"><input id="bbit-cal-what" class="textbox-fill-input"/></div></div><div class="cb-example">');
temparr.push(i18n.xgcalendar.example, '</div></td></tr></tbody></table><input id="bbit-cal-start" type="hidden"/><input id="bbit-cal-end" type="hidden"/><input id="bbit-cal-allday" type="hidden"/><input id="bbit-cal-quickAddBTN" value="'); temparr.push(i18n.xgcalendar.example, '</div></td></tr></tbody></table><input id="bbit-cal-start" type="hidden"/><input id="bbit-cal-end" type="hidden"/><input id="bbit-cal-allday" type="hidden"/><input value="');
temparr.push(i18n.xgcalendar.create_event, '" type="submit"/>&nbsp; <a href="" class="lk bbit-cal-editLink">'); temparr.push(i18n.xgcalendar.create_event, '" type="submit"/>&nbsp; <a href="" class="lk bbit-cal-editLink">');
temparr.push(i18n.xgcalendar.update_detail, ' <StrONG>&gt;&gt;</StrONG></SPAN></div></div></div><tr><td><div id="bl1" class="bubble-corner"><div class="bubble-sprite bubble-bl"></div></div><td><div class="bubble-bottom"></div><td><div id="br1" class="bubble-corner"><div class="bubble-sprite bubble-br"></div></div></tr></tbody></table><div id="bubbleClose1" class="bubble-closebutton"></div><div id="prong2" class="prong"><div class=bubble-sprite></div></div></div>'); temparr.push(i18n.xgcalendar.update_detail, ' <StrONG>&gt;&gt;</StrONG></SPAN></div></div></div><tr><td><div id="bl1" class="bubble-corner"><div class="bubble-sprite bubble-bl"></div></div><td><div class="bubble-bottom"></div><td><div id="br1" class="bubble-corner"><div class="bubble-sprite bubble-br"></div></div></tr></tbody></table><div id="bubbleClose1" class="bubble-closebutton"></div><div id="prong2" class="prong"><div class=bubble-sprite></div></div></div>');
temparr.push('</form>'); temparr.push('</form>');
@ -1789,7 +1791,6 @@
param[param.length] = option.extParam[pi]; param[param.length] = option.extParam[pi];
} }
} }
if (option.quickAddHandler && $.isFunction(option.quickAddHandler)) { if (option.quickAddHandler && $.isFunction(option.quickAddHandler)) {
option.quickAddHandler.call(this, param); option.quickAddHandler.call(this, param);
$("#bbit-cal-buddle").css("visibility", "hidden"); $("#bbit-cal-buddle").css("visibility", "hidden");
@ -1804,8 +1805,9 @@
ed = new Date(dateend), ed = new Date(dateend),
diff = DateDiff("d", sd, ed); diff = DateDiff("d", sd, ed);
var newdata = { var newdata = {
"uri":"", "jq_id":"",
"subject":what, "ev_id":"",
"summary":what,
"start":sd, "start":sd,
"end":ed, "end":ed,
"is_allday":(allday == "1" ? 1 : 0), "is_allday":(allday == "1" ? 1 : 0),
@ -1859,15 +1861,19 @@
$("#bbit-cal-start").val(start.getTime()); $("#bbit-cal-start").val(start.getTime());
$("#bbit-cal-end").val(end.getTime()); $("#bbit-cal-end").val(end.getTime());
var addurl = option.baseurl + "new/?start=" + Math.floor($("#bbit-cal-start").val() / 1000) + "&end=" + Math.floor($("#bbit-cal-end").val() / 1000) + "&isallday=" + (isallday ? "1" : "0"); var addurl = option.baseurl + "new/?start=" + Math.floor($("#bbit-cal-start").val() / 1000) + "&end=" + Math.floor($("#bbit-cal-end").val() / 1000) +
"&isallday=" + (isallday ? "1" : "0") + "&title=";
buddle.find(".bbit-cal-editLink").attr("href", addurl); buddle.find(".bbit-cal-editLink").attr("href", addurl);
buddle.css({ "visibility":"visible", left:off.left, top:off.top }); buddle.css({ "visibility":"visible", left:off.left, top:off.top });
calwhat.blur().focus(); //add 2010-01-26 blur() fixed chrome calwhat.blur().focus(); //add 2010-01-26 blur() fixed chrome
$(document).one("mousedown", function () { $(document).on("mousedown", function () {
$("#bbit-cal-buddle").css("visibility", "hidden"); $("#bbit-cal-buddle").css("visibility", "hidden");
releasedragevent(); releasedragevent();
}); });
$(document).on("keyup", "#bbit-cal-what", function() {
buddle.find(".bbit-cal-editLink").attr("href", addurl + encodeURIComponent($("#bbit-cal-what").val()));
});
return false; return false;
} }
@ -1886,7 +1892,7 @@
var sl = option.eventItems.length; var sl = option.eventItems.length;
var i = -1; var i = -1;
for (var j = 0; j < sl; j++) { for (var j = 0; j < sl; j++) {
if (option.eventItems[j]["uri"] == key) { if (option.eventItems[j]["jq_id"] == key) {
i = j; i = j;
break; break;
} }
@ -2352,7 +2358,7 @@
d.target.hide(); d.target.hide();
ny = gP(gh.sh, gh.sm); ny = gP(gh.sh, gh.sm);
d.top = ny; d.top = ny;
tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], false, false, data["color"]); tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["summary"], false, false, data["color"]);
cpwrap = $("<div class='ca-evpi drag-chip-wrapper' style='top:" + ny + "px'/>").html(tempdata); cpwrap = $("<div class='ca-evpi drag-chip-wrapper' style='top:" + ny + "px'/>").html(tempdata);
evid = ".tgOver" + d.target.parent().data("col"); evid = ".tgOver" + d.target.parent().data("col");
$gridcontainer.find(evid).append(cpwrap); $gridcontainer.find(evid).append(cpwrap);
@ -2389,7 +2395,7 @@
//log.info("ny=" + ny); //log.info("ny=" + ny);
gh = gW(ny, ny + d.h); gh = gW(ny, ny + d.h);
//log.info("sh=" + gh.sh + ",sm=" + gh.sm); //log.info("sh=" + gh.sh + ",sm=" + gh.sm);
tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], false, false, data["color"]); tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["summary"], false, false, data["color"]);
d.cpwrap.css("top", ny + "px").html(tempdata); d.cpwrap.css("top", ny + "px").html(tempdata);
} }
d.ny = ny; d.ny = ny;
@ -2415,7 +2421,7 @@
d.target.hide(); d.target.hide();
ny = gP(gh.sh, gh.sm); ny = gP(gh.sh, gh.sm);
d.top = ny; d.top = ny;
tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], "100%", true, data["color"]); tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["summary"], "100%", true, data["color"]);
cpwrap = $("<div class='ca-evpi drag-chip-wrapper' style='top:" + ny + "px'/>").html(tempdata); cpwrap = $("<div class='ca-evpi drag-chip-wrapper' style='top:" + ny + "px'/>").html(tempdata);
evid = ".tgOver" + d.target.parent().data("col"); evid = ".tgOver" + d.target.parent().data("col");
$gridcontainer.find(evid).append(cpwrap); $gridcontainer.find(evid).append(cpwrap);
@ -2427,7 +2433,7 @@
nh = pnh > 1 ? nh - pnh + Math.ceil(option.hour_height / 2) : nh - pnh; nh = pnh > 1 ? nh - pnh + Math.ceil(option.hour_height / 2) : nh - pnh;
if (d.nh != nh) { if (d.nh != nh) {
gh = gW(d.top, d.top + nh); gh = gW(d.top, d.top + nh);
tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], "100%", true, data["color"]); tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["summary"], "100%", true, data["color"]);
d.cpwrap.html(tempdata); d.cpwrap.html(tempdata);
} }
d.nh = nh; d.nh = nh;

View file

@ -7,26 +7,7 @@ var i18n = $.extend({}, i18n || {}, {
"year_index": 2, "year_index": 2,
"month_index": 1, "month_index": 1,
"day_index": 0, "day_index": 0,
"day": "d", "day": "d"
"sun": "So",
"mon": "Mo",
"tue": "Di",
"wed": "Mi",
"thu": "Do",
"fri": "Fr",
"sat": "Sa",
"jan": "Jan",
"feb": "Feb",
"mar": "Mär",
"apr": "Apr",
"may": "Mai",
"jun": "Jun",
"jul": "Jul",
"aug": "Aug",
"sep": "Sep",
"oct": "Okt",
"nov": "Nov",
"dec": "Dez"
}, },
"no_implemented": "Nicht eingebaut", "no_implemented": "Nicht eingebaut",
"to_date_view": "Zum aktuellen Datum gehen", "to_date_view": "Zum aktuellen Datum gehen",

View file

@ -7,26 +7,7 @@ var i18n = $.extend({}, i18n || {}, {
"year_index": 2, "year_index": 2,
"month_index": 1, "month_index": 1,
"day_index": 0, "day_index": 0,
"day": "d", "day": "d"
"sun": "Su",
"mon": "Mo",
"tue": "Tu",
"wed": "Mi",
"thu": "Th",
"fri": "Fr",
"sat": "Sa",
"jan": "Jan",
"feb": "Feb",
"mar": "Mar",
"apr": "Apr",
"may": "May",
"jun": "Jun",
"jul": "Jul",
"aug": "Aug",
"sep": "Sep",
"oct": "Oct",
"nov": "Nov",
"dec": "Dec"
}, },
"no_implemented": "Not implemented", "no_implemented": "Not implemented",
"to_date_view": "Go to today", "to_date_view": "Go to today",

View file

@ -0,0 +1,238 @@
<?php
/**
* @param mixed $obj
* @return string
*/
function wdcal_jsonp_encode($obj)
{
$str = json_encode($obj);
if (isset($_REQUEST["callback"])) {
$str = $_REQUEST["callback"] . "(" . $str . ")";
}
return $str;
}
/**
* @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);
}
/**
* @param Sabre_DAV_Server $server
* @param string $right
* @return null|Sabre_CalDAV_Calendar
*/
function wdcal_print_feed_getCal(&$server, $right)
{
$cals = dav_get_current_user_calendars($server, $right);
$calfound = null;
for ($i = 0; $i < count($cals) && $calfound === null; $i++) {
$prop = $cals[$i]->getProperties(array("id"));
if (isset($prop["id"]) && (!isset($_REQUEST["cal"]) || in_array($prop["id"], $_REQUEST["cal"]))) $calfound = $cals[$i];
}
return $calfound;
}
/**
*
*/
function wdcal_print_feed($base_path = "")
{
$server = dav_create_server(true, true, false);
$ret = null;
$method = $_GET["method"];
switch ($method) {
case "add":
$cs = wdcal_print_feed_getCal($server, DAV_ACL_WRITE);
if ($cs == null) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
try {
$item = dav_create_empty_vevent();
$component = dav_get_eventComponent($item);
$component->add("SUMMARY", icalendar_sanitize_string(dav_compat_parse_text_serverside("CalendarTitle")));
if (isset($_REQUEST["allday"])) $type = Sabre_VObject_Property_DateTime::DATE;
else $type = Sabre_VObject_Property_DateTime::LOCALTZ;
$datetime_start = new Sabre_VObject_Property_DateTime("DTSTART");
$datetime_start->setDateTime(new DateTime(date("Y-m-d H:i:s", IntVal($_REQUEST["CalendarStartTime"]))), $type);
$datetime_end = new Sabre_VObject_Property_DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date("Y-m-d H:i:s", IntVal($_REQUEST["CalendarEndTime"]))), $type);
$component->add($datetime_start);
$component->add($datetime_end);
$uid = $component->__get("UID");
$data = $item->serialize();
$cs->createFile($uid . ".ics", $data);
$ret = array(
'IsSuccess' => true,
'Msg' => 'add success',
'Data' => $uid . ".ics",
);
} catch (Exception $e) {
$ret = array(
'IsSuccess' => false,
'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"]);
$ret = array();
$ret['events'] = array();
$ret["issort"] = true;
$ret["start"] = $date[0];
$ret["end"] = $date[1];
$ret['error'] = null;
$cals = dav_get_current_user_calendars($server, DAV_ACL_READ);
foreach ($cals as $cal) {
$prop = $cal->getProperties(array("id"));
if (isset($prop["id"]) && (!isset($_REQUEST["cal"]) || in_array($prop["id"], $_REQUEST["cal"]))) {
$backend = wdcal_calendar_factory_by_id($prop["id"]);
$events = $backend->listItemsByRange($prop["id"], $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":
$r = q("SELECT `calendarobject_id`, `calendar_id` FROM %s%sjqcalendar WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($_REQUEST["jq_id"]));
if (count($r) != 1) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
try {
$cs = dav_get_current_user_calendar_by_id($server, $r[0]["calendar_id"], DAV_ACL_READ);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($r[0]["calendarobject_id"]);
$vObject = dav_get_current_user_calendarobject($server, $cs, $obj_uri["uri"], DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if (!$component) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
if (isset($_REQUEST["allday"])) $type = Sabre_VObject_Property_DateTime::DATE;
else $type = Sabre_VObject_Property_DateTime::LOCALTZ;
$datetime_start = new Sabre_VObject_Property_DateTime("DTSTART");
$datetime_start->setDateTime(new DateTime(date("Y-m-d H:i:s", IntVal($_REQUEST["CalendarStartTime"]))), $type);
$datetime_end = new Sabre_VObject_Property_DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date("Y-m-d H:i:s", IntVal($_REQUEST["CalendarEndTime"]))), $type);
$component->__unset("DTSTART");
$component->__unset("DTEND");
$component->add($datetime_start);
$component->add($datetime_end);
$data = $vObject->serialize();
/** @var Sabre_CalDAV_CalendarObject $child */
$child = $cs->getChild($obj_uri["uri"]);
$child->put($data);
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} catch (Exception $e) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
break;
case "remove":
$r = q("SELECT `calendarobject_id`, `calendar_id` FROM %s%sjqcalendar WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($_REQUEST["jq_id"]));
if (count($r) != 1) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
try {
$cs = dav_get_current_user_calendar_by_id($server, $r[0]["calendar_id"], DAV_ACL_WRITE);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($r[0]["calendarobject_id"]);
$child = $cs->getChild($obj_uri["uri"]);
$child->delete();
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} catch (Exception $e) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => t('No access')));
killme();
}
break;
}
echo wdcal_jsonp_encode($ret);
killme();
}

View file

@ -1,138 +0,0 @@
<?php
abstract class AnimexxCalSource
{
/**
* @var int $namespace_id
*/
protected $namespace_id;
/**
* @var DBClass_friendica_calendars $calendarDb
*/
protected $calendarDb;
/**
* @var int
*/
protected $user_id;
/**
* @param int $user_id
* @param int $namespace_id
* @throws Sabre_DAV_Exception_NotFound
*/
function __construct($user_id = 0, $namespace_id = 0)
{
$this->namespace_id = IntVal($namespace_id);
$this->user_id = IntVal($user_id);
$x = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uid` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, $this->user_id
);
if (count($x) != 1) throw new Sabre_DAV_Exception_NotFound("Not found");
try {
$this->calendarDb = new DBClass_friendica_calendars($x[0]);
} catch (Exception $e) {
throw new Sabre_DAV_Exception_NotFound("Not found");
}
}
/**
* @abstract
* @return int
*/
public static abstract function getNamespace();
/**
* @abstract
* @param int $user
* @return array
*/
public abstract function getPermissionsCalendar($user);
/**
* @abstract
* @param int $user
* @param string $item_uri
* @param string $recurrence_uri
* @param array|null $item_arr
* @return array
*/
public abstract function getPermissionsItem($user, $item_uri, $recurrence_uri, $item_arr = null);
/**
* @param string $uri
* @param array $start
* @param array $end
* @param string $subject
* @param bool $allday
* @param string $description
* @param string $location
* @param null $color
* @param string $timezone
* @param bool $notification
* @param null $notification_type
* @param null $notification_value
*/
public abstract function updateItem($uri, $start, $end, $subject = "", $allday = false, $description = "", $location = "", $color = null,
$timezone = "", $notification = true, $notification_type = null, $notification_value = null);
/**
* @abstract
* @param array $start
* @param array $end
* @param string $subject
* @param bool $allday
* @param string $description
* @param string $location
* @param null $color
* @param string $timezone
* @param bool $notification
* @param null $notification_type
* @param null $notification_value
* @return array
*/
public abstract function addItem($start, $end, $subject, $allday = false, $description = "", $location = "", $color = null,
$timezone = "", $notification = true, $notification_type = null, $notification_value = null);
/**
* @param string $uri
*/
public abstract function removeItem($uri);
/**
* @abstract
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
public abstract function listItemsByRange($sd, $ed, $base_path);
/**
* @abstract
* @param string $uri
* @return array
*/
public abstract function getItemByUri($uri);
/**
* @param string $uri
* @return null|string
*/
public function getItemDetailRedirect($uri) {
return null;
}
}

View file

@ -1,332 +0,0 @@
<?php
class AnimexxCalSourcePrivate extends AnimexxCalSource
{
/**
* @return int
*/
public static function getNamespace()
{
return CALDAV_NAMESPACE_PRIVATE;
}
/**
* @param int $user
* @return array
*/
public function getPermissionsCalendar($user)
{
if ($user == $this->calendarDb->uid) return array("read"=> true, "write"=> true);
return array("read"=> false, "write"=> false);
}
/**
* @param int $user
* @param string $item_uri
* @param string $recurrence_uri
* @param null|array $item_arr
* @return array
*/
public function getPermissionsItem($user, $item_uri, $recurrence_uri, $item_arr = null)
{
$cal_perm = $this->getPermissionsCalendar($user);
if (!$cal_perm["read"]) return array("read"=> false, "write"=> false);
if (!$cal_perm["write"]) array("read"=> true, "write"=> false);
if ($item_arr === null) {
$x = q("SELECT `permission_edit` FROM %s%sjqcalendar WHERE `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, dbesc($item_uri), dbesc($recurrence_uri)
);
if (!$x || count($x) == 0) return array("read"=> false, "write"=> false);
return array("read"=> true, "write"=> ($x[0]["permission_edit"]));
} else {
return array("read"=> true, "write"=> ($item_arr["permission_edit"]));
}
}
/**
* @param string $uri
* @throws Sabre_DAV_Exception_NotFound
*/
public function removeItem($uri){
$obj_alt = q("SELECT * FROM %s%sjqcalendar WHERE namespace = %d AND namespace_id = %d AND ical_uri = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, dbesc($uri));
if (count($obj_alt) == 0) throw new Sabre_DAV_Exception_NotFound("Not found");
$calendarBackend = new Sabre_CalDAV_Backend_Std();
$calendarBackend->deleteCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $obj_alt[0]["ical_uri"]);
}
/**
* @param string $uri
* @param array $start
* @param array $end
* @param string $subject
* @param bool $allday
* @param string $description
* @param string $location
* @param null $color
* @param string $timezone
* @param bool $notification
* @param null $notification_type
* @param null $notification_value
* @throws Sabre_DAV_Exception_NotFound
* @throws Sabre_DAV_Exception_Conflict
*/
public function updateItem($uri, $start, $end, $subject = "", $allday = false, $description = "", $location = "", $color = null, $timezone = "", $notification = true, $notification_type = null, $notification_value = null)
{
$a = get_app();
$usr_id = IntVal($this->calendarDb->uid);
$old = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $usr_id, $this->getNamespace(), $this->namespace_id, dbesc($uri));
if (count($old) == 0) throw new Sabre_DAV_Exception_NotFound("Not Found 1");
$old_obj = new DBClass_friendica_jqcalendar($old[0]);
$calendarBackend = new Sabre_CalDAV_Backend_Std();
$obj = $calendarBackend->getCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $old_obj->ical_uri);
if (!$obj) throw new Sabre_DAV_Exception_NotFound("Not Found 2");
$v = new vcalendar();
$v->setConfig('unique_id', $a->get_hostname());
$v->setMethod('PUBLISH');
$v->setProperty("x-wr-calname", "AnimexxCal");
$v->setProperty("X-WR-CALDESC", "Animexx Calendar");
$v->setProperty("X-WR-TIMEZONE", $a->timezone);
$obj["calendardata"] = icalendar_sanitize_string($obj["calendardata"]);
$v->parse($obj["calendardata"]);
/** @var $vevent vevent */
$vevent = $v->getComponent('vevent');
if (trim($vevent->getProperty('uid')) . ".ics" != $old_obj->ical_uri)
throw new Sabre_DAV_Exception_Conflict("URI != URI: " . $old_obj->ical_uri . " vs. " . trim($vevent->getProperty("uid")));
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;
if ($end["hour"] < 23) $end["hour"]++;
} // DTEND muss <= DTSTART
if ($start["hour"] == 0 && $start["minute"] == 0 && $end["hour"] == 23 && $end["minute"] == 59) {
$allday = true;
}
if ($allday) {
$vevent->setDtstart($start["year"], $start["month"], $start["day"], FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE"));
$end = mktime(0, 0, 0, $end["month"], $end["day"], $end["year"]) + 3600 * 24;
// If a DST change occurs on the current day
$end += 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"));
}
if ($subject != "") {
$vevent->setProperty('LOCATION', $location);
$vevent->setProperty('summary', $subject);
$vevent->setProperty('description', $description);
}
if (!is_null($color) && $color >= 0) $vevent->setProperty("X-ANIMEXX-COLOR", $color);
if (!$notification || $notification_type != null) {
$vevent->deleteComponent("VALARM");
if ($notification) {
$valarm = new valarm();
$valarm->setTrigger(
($notification_type == "year" ? $notification_value : 0),
($notification_type == "month" ? $notification_value : 0),
($notification_type == "day" ? $notification_value : 0),
($notification_type == "week" ? $notification_value : 0),
($notification_type == "hour" ? $notification_value : 0),
($notification_type == "minute" ? $notification_value : 0),
($notification_type == "minute" ? $notification_value : 0),
true,
($notification_value > 0)
);
$valarm->setProperty("ACTION", "DISPLAY");
$valarm->setProperty("DESCRIPTION", $subject);
$vevent->setComponent($valarm);
}
}
$v->deleteComponent("vevent");
$v->setComponent($vevent, trim($vevent->getProperty("uid")));
$ical = $v->createCalendar();
$calendarBackend->updateCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $old_obj->ical_uri, $ical);
}
/**
* @param array $start
* @param array $end
* @param string $subject
* @param bool $allday
* @param string $description
* @param string $location
* @param null $color
* @param string $timezone
* @param bool $notification
* @param null $notification_type
* @param null $notification_value
* @return array|string
*/
public function addItem($start, $end, $subject, $allday = false, $description = "", $location = "", $color = null,
$timezone = "", $notification = true, $notification_type = null, $notification_value = null)
{
$a = get_app();
$v = new vcalendar();
$v->setConfig('unique_id', $a->get_hostname());
$v->setProperty('method', 'PUBLISH');
$v->setProperty("x-wr-calname", "AnimexxCal");
$v->setProperty("X-WR-CALDESC", "Animexx Calendar");
$v->setProperty("X-WR-TIMEZONE", $a->timezone);
$vevent = dav_create_vevent($start, $end, $allday);
$vevent->setLocation(icalendar_sanitize_string($location));
$vevent->setSummary(icalendar_sanitize_string($subject));
$vevent->setDescription(icalendar_sanitize_string($description));
if (!is_null($color) && $color >= 0) $vevent->setProperty("X-ANIMEXX-COLOR", $color);
if ($notification && $notification_type == null) {
if ($allday) {
$notification_type = "hour";
$notification_value = 24;
} else {
$notification_type = "minute";
$notification_value = 60;
}
}
if ($notification) {
$valarm = new valarm();
$valarm->setTrigger(
($notification_type == "year" ? $notification_value : 0),
($notification_type == "month" ? $notification_value : 0),
($notification_type == "day" ? $notification_value : 0),
($notification_type == "week" ? $notification_value : 0),
($notification_type == "hour" ? $notification_value : 0),
($notification_type == "minute" ? $notification_value : 0),
($notification_type == "second" ? $notification_value : 0),
true,
($notification_value > 0)
);
$valarm->setAction("DISPLAY");
$valarm->setDescription($subject);
$vevent->setComponent($valarm);
}
$v->setComponent($vevent);
$ical = $v->createCalendar();
$obj_id = trim($vevent->getProperty("UID"));
$calendarBackend = new Sabre_CalDAV_Backend_Std();
$calendarBackend->createCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $obj_id . ".ics", $ical);
return $obj_id . ".ics";
}
private function jqcal2wdcal($row, $usr_id, $base_path) {
$evo = new DBClass_friendica_jqcalendar($row);
$not = q("SELECT COUNT(*) num FROM %s%snotifications WHERE `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($row["ical_uri"]), $row["ical_recurr_uri"]
);
$editable = $this->getPermissionsItem($usr_id, $row["ical_uri"], $row["ical_recurr_uri"], $row);
$recurring = (is_null($evo->RecurringRule) || $evo->RecurringRule == "" || $evo->RecurringRule == "NULL" ? 0 : 1);
$end = wdcal_mySql2PhpTime($evo->EndTime);
if ($evo->IsAllDayEvent) $end -= 1;
$arr = array(
"uri" => $evo->ical_uri,
"subject" => escape_tags($evo->Subject),
"start" => wdcal_mySql2PhpTime($evo->StartTime),
"end" => $end,
"is_allday" => $evo->IsAllDayEvent,
"is_moredays" => 0,
"is_recurring" => $recurring,
"color" => (is_null($evo->Color) || $evo->Color == "" ? $this->calendarDb->calendarcolor : $evo->Color),
"is_editable" => ($editable ? 1 : 0),
"is_editable_quick" => ($editable && !$recurring ? 1 : 0),
"location" => $evo->Location,
"attendees" => '',
"has_notification" => ($not[0]["num"] > 0 ? 1 : 0),
"url_detail" => $base_path . $evo->ical_uri . "/",
"url_edit" => $base_path . $evo->ical_uri . "/edit/",
"special_type" => "",
);
return $arr;
}
/**
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
public function listItemsByRange($sd, $ed, $base_path)
{
$usr_id = IntVal($this->calendarDb->uid);
$von = wdcal_php2MySqlTime($sd);
$bis = wdcal_php2MySqlTime($ed);
// @TODO Events, die früher angefangen haben, aber noch andauern
$evs = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `starttime` between '%s' and '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
$usr_id, $this->getNamespace(), $this->namespace_id, dbesc($von), dbesc($bis));
$events = array();
foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $usr_id, $base_path);
return $events;
}
/**
* @param string $uri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getItemByUri($uri)
{
$usr_id = IntVal($this->calendarDb->uid);
$evs = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
$usr_id, $this->getNamespace(), $this->namespace_id, dbesc($uri));
if (count($evs) == 0) throw new Sabre_DAV_Exception_NotFound();
return $this->jqcal2wdcal($evs[0], $usr_id);
}
/**
* @param string $uri
* @return string
*/
public function getItemDetailRedirect($uri) {
return "/dav/wdcal/$uri/edit/";
}
}

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