Heavily refactored, including multiple calendars per user and recurring events. Not in an installable state yet, though

This commit is contained in:
Tobias Hößl 2012-07-08 17:12:58 +00:00
parent 4a5e30ec84
commit fefee23e90
78 changed files with 8026 additions and 1205 deletions

15
dav/Changelog.txt Normal file
View file

@ -0,0 +1,15 @@
v0.2.0
======
[FEATURE] Multiple private Calendars can be created.
[COMPATIBILITY] When creating or updating an event using CalDAV, the etag is returned.
v0.1.1
======
[FEATURE] A "New Event" Button in the navigation bar of the calendar is added.
[FEATURE] When creating an event by dragging in the calendar, the "Edit Details"-Link leads to a page where the details can be added before actually creating the event.
[BUGFIX] When editing a event, the start time cannot be set befor the end time anymore.
[BUGFIX] Fixed some problems with Magic Quotes
v0.1.0
======
Initial Release

View file

@ -6,19 +6,24 @@ 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:
- A web-based drag&drop interface for managing events
- All-Day-Events, Multi-Day-Events, and time-based events
- Recurrences (not the whole set of options given in the iCalendar spec, but the most important ones)
- 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)
- 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)
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.
- The basic design of the system is aware of timezones; however this is not reflected in the UI yet. It currently assumes that the timezone set in the friendica-installation matches the user's local time and matches the local time set in the user's operating system.
CalDAV device compatibility:
- iOS (iPhone/iPodTouch) works
- Thunderbird Lightning should work, not tested yet
- Android: http://dmfs.org/caldav/ seems to work, not much tested yet, though
- Thunderbird Lightning works
- 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
After activating, serveral tables in the database have to be created. The admin-interface of the plugin will try to do this automatically.
@ -26,10 +31,9 @@ In case of errors, the SQL-statement to create the tables manually are shown in
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 yet a complete replacement for the friendica-native events
- Sharing events; all events are private at the moment, therefore this system is not a complete replacement for the friendica-native events
- Attendees / Collaboration
- ICS Export and Import
Used libraries
@ -46,10 +50,6 @@ jQueryUI
http://jqueryui.com/
Dual-licenced: MIT and GPL licenses
iCalCreator
http://kigkonsult.se/iCalcreator/
GNU Lesser General Public License
TimePicker
http://www.texotela.co.uk/code/jquery/timepicker/
Dual-licenced: MIT and GPL licenses

View file

@ -1,31 +1,47 @@
1.7.0-alpha (2012-??-??)
* BC Break: The calendarobjects database table has a bunch of new fields,
and a migration script is required to ensure everything will keep
working. Read the wiki for more details.
* BC Break: The calendarobjects database table has a bunch of new
fields, and a migration script is required to ensure everything will
keep working. Read the wiki for more details.
* BC Break: The iCalendar interface now has a new method: calendarQuery.
* BC Break: In this version a number of classes have been deleted, that
have been previously deprecated. Namely:
- Sabre_DAV_Directory (now: Sabre_DAV_Collection)
- Sabre_DAV_SimpleDirectory (now: Sabre_DAV_SimpleCollection)
- Sabre_VObject_Element_DateTime (now: Sabre_VObject_Property_DateTime)
- Sabre_VObject_Element_MultiDateTime (now .._Property_MultiDateTime)
- Sabre_VObject_Element_DateTime (now: .._Property_DateTime)
- Sabre_VObject_Element_MultiDateTime (-> .._Property_MultiDateTime)
* BC Break: Sabre_CalDAV_Schedule_IMip::sendMessage now has an extra
argument. If you extended this class, you should fix this method. It's
only used for informational purposes.
* Changed: Responsibility for dealing with the calendar-query is now moved
from the CalDAV plugin to the CalDAV backends. This allows for heavy
optimizations.
* Changed: The CalDAV PDO backend is now a lot faster for common calendar
queries.
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8 encoded.
* New feature: Support for caldav notifications!
* Changed: Responsibility for dealing with the calendar-query is now
moved from the CalDAV plugin to the CalDAV backends. This allows for
heavy optimizations.
* Changed: The CalDAV PDO backend is now a lot faster for common
calendar queries.
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8
encoded.
* Fixed: Workaround for the SOGO connector, as it doesn't understand
receiving "text/x-vcard; charset=utf-8" for a contenttype.
* Added: Sabre_DAV_Client now throws more specific exceptions in cases
where we already has an exception class.
* Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the PATCH
method to update parts of a file.
* Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the
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!)
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.
1.6.3-stable (2012-06-12)
* Added: It's now possible to specify in Sabre_DAV_Client which type of
authentication is to be used.
* Fixed: Issue 206: Sabre_DAV_Client PUT requests are fixed.
@ -43,6 +59,7 @@
compatibility.
* Fixed: Added a workaround for a bug in KDE 4.8.2 contact syncing. See
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)
* Fixed: Sabre_VObject_Node::$parent should have been public.

View file

@ -95,6 +95,11 @@ foreach($fields17 as $field) {
if ($found === 0) {
echo "The database had the 1.6 schema. Table will now be altered.\n";
echo "This may take some time for large tables\n";
switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql' :
$pdo->exec(<<<SQL
ALTER TABLE calendarobjects
ADD etag VARCHAR(32),
@ -103,7 +108,20 @@ ADD componenttype VARCHAR(8),
ADD firstoccurence INT(11) UNSIGNED,
ADD lastoccurence INT(11) UNSIGNED
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";
} elseif ($found === 5) {

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

@ -3,45 +3,15 @@
/**
* Abstract Calendaring backend. Extend this class to create your own backends.
*
* Checkout the BackendInterface for all the methods that must be implemented.
*
* @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
*/
abstract class Sabre_CalDAV_Backend_Abstract {
/**
* 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);
abstract class Sabre_CalDAV_Backend_Abstract implements Sabre_CalDAV_Backend_BackendInterface {
/**
* 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.
*

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

@ -24,7 +24,7 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
/**
* CalDAV backend
*
* @var Sabre_CalDAV_Backend_Abstract
* @var Sabre_CalDAV_Backend_BackendInterface
*/
protected $caldavBackend;
@ -39,10 +39,10 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
* Constructor
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @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->principalBackend = $principalBackend;

View file

@ -12,7 +12,7 @@
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
*/
@ -35,11 +35,11 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
/**
* Constructor
*
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @param array $calendarInfo
* @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;

View file

@ -304,6 +304,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
// one is the first to trigger. Based on this, we can
// determine if we can 'give up' expanding events.
$firstAlarm = null;
if ($expandedEvent->VALARM !== null) {
foreach($expandedEvent->VALARM as $expandedAlarm) {
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
@ -325,7 +326,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
$firstAlarm = $effectiveTrigger;
}
}
}
}
if (is_null($firstAlarm)) {
// No alarm was found.

View file

@ -17,7 +17,7 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
/**
* CalDAV backend
*
* @var Sabre_CalDAV_Backend_Abstract
* @var Sabre_CalDAV_Backend_BackendInterface
*/
protected $caldavBackend;
@ -33,10 +33,10 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
*
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @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);
$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('beforeWriteContent', array($this, 'beforeWriteContent'));
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
$server->subscribeEvent('beforeMethod', array($this,'beforeMethod'));
$server->xmlNamespaces[self::NS_CALDAV] = 'cal';
$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_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_Notifications_ICollection'] = '{' . self::NS_CALENDARSERVER . '}notifications';
$server->resourceTypeMapping['Sabre_CalDAV_Notifications_INode'] = '{' . self::NS_CALENDARSERVER . '}notification';
array_push($server->protectedProperties,
@ -195,7 +198,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
// CalendarServer extensions
'{' . self::NS_CALENDARSERVER . '}getctag',
'{' . 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
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) {
// The calendar-data property is not supposed to be a 'real'
@ -648,7 +676,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
if (!$node instanceof Sabre_CalDAV_ICalendarObject)
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)
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.
*
* @param resource|string $data
* @param string $path
* @return void
*/
protected function validateICalendar(&$data) {
protected function validateICalendar(&$data, $path) {
// If it's a stream, we convert it to a string first.
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.');
}
// 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;
$foundUID = null;
foreach($vobj->getComponents() as $component) {
@ -715,6 +791,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
case 'VJOURNAL' :
if (is_null($foundType)) {
$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)) {
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');
}
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');
}
$originator = substr($originator,7);
@ -765,7 +844,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
foreach($recipients as $k=>$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');
}
$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') {
$this->iMIPMessage($originator, $recipients, $vObject, $principal);
$result = $this->iMIPMessage($originator, $recipients, $vObject, $principal);
$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 {
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.
*
* 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 array $recipients
* @param Sabre_VObject_Component $vObject
* @param string $principal Principal url
* @return void
* @return array
*/
protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) {
if (!$this->imipHandler) {
throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.');
}
$resultStatus = '5.2;This server does not support this operation';
} else {
$this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
$resultStatus = '2.0;Success';
}
$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
*
* @var Sabre_CalDAV_Backend_Abstract
* @var Sabre_CalDAV_Backend_BackendInterface
*/
protected $caldavBackend;
@ -36,10 +36,10 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
* Constructor
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
* @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->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_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;
}

View file

@ -238,7 +238,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
* Creates a new card.
*
* 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
* cardData argument is the vcard body, and is passed as a string.

View file

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

View file

@ -293,7 +293,10 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
$this->server->tree->getNodeForPath($uri);
// 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) {

View file

@ -11,7 +11,7 @@
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @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);

View file

@ -138,7 +138,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA
if (is_scalar($propertyValue)) {
$text = $document->createTextNode($propertyValue);
$currentProperty->appendChild($text);
} elseif ($propertyValue instanceof Sabre_DAV_Property) {
} elseif ($propertyValue instanceof Sabre_DAV_PropertyInterface) {
$propertyValue->serialize($server,$currentProperty);
} elseif (!is_null($propertyValue)) {
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) {
try {
$this->broadcastEvent('exception', array($e));
} catch (Exception $ignore) {
}
$DOM = new DOMDocument('1.0','utf-8');
$DOM->formatOutput = true;
@ -508,7 +512,7 @@ class Sabre_DAV_Server {
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();
// Converting string into stream, if needed.

View file

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

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 {
// 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
// To look up the timezone, we must first find the VCALENDAR component.
$root = $property;
while($root->parent) {
$root = $root->parent;
}
if (isset($root->VTIMEZONE)) {
foreach($root->VTIMEZONE as $vtimezone) {
if (((string)$vtimezone->TZID) == $tzid) {
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
$newtzid = (string)$vtimezone->{'X-LIC-LOCATION'};
if ($root->name === 'VCALENDAR') {
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone((string)$tzid, $root);
} else {
// No libical location specified. As a last resort we could
// try matching $vtimezone's DST rules against all known
// time zones returned by DateTimeZone::list*
// TODO
}
}
}
}
$tz = Sabre_VObject_TimeZoneUtil::getTimeZone((string)$tzid);
}
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->setTimeZone($tz);

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

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

View file

@ -1,14 +1,16 @@
<?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 $calendars;
private $notifications;
function __construct(array $calendars, array $calendarData) {
function __construct(array $calendars, array $calendarData, array $notifications = array()) {
$this->calendars = $calendars;
$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) {
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) {
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

@ -55,6 +55,56 @@ class Sabre_CalDAV_ICSExportPluginTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(1,count($obj->VERSION));
$this->assertEquals(1,count($obj->CALSCALE));
$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->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));
$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');
$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(
array(

View file

@ -23,8 +23,34 @@ class Sabre_CalDAV_PluginTest extends PHPUnit_Framework_TestCase {
function setup() {
if (!SABRE_HASSQLITE) $this->markTestSkipped('No PDO SQLite support');
$this->caldavBackend = Sabre_CalDAV_TestUtil::getBackend();
$this->caldavBackend = new Sabre_CalDAV_Backend_Mock(array(
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->setGroupMemberSet('principals/admin/calendar-proxy-read',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',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for',
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}notification-URL',
));
$this->assertArrayHasKey(0,$props);
@ -414,6 +441,12 @@ END:VCALENDAR';
$this->assertTrue($prop instanceof Sabre_DAV_Property_Href);
$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]);
$prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'];
$this->assertTrue($prop instanceof Sabre_DAV_Property_HrefList);
@ -429,6 +462,7 @@ END:VCALENDAR';
$this->assertInstanceOf('Sabre_DAV_Property_HrefList', $prop);
$this->assertEquals(array('principals/admin'), $prop->getHrefs());
}
function testSupportedReportSetPropertyNonCalendar() {
@ -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);
}
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',
'principaluri' => 'principals/admin',
'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'));
}
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_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);
$this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
$expected = array(
'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'));
}
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() {
$request = new Sabre_HTTP_Request(array(
@ -114,7 +129,7 @@ class Sabre_CardDAV_ValidateVCardTest extends PHPUnit_Framework_TestCase {
'REQUEST_METHOD' => 'PUT',
'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);
$response = $this->request($request);

View file

@ -6,6 +6,8 @@ class Sabre_DAV_ServerEventsTest extends Sabre_DAV_AbstractServer {
private $tempPath;
private $exception;
function testAfterBind() {
$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

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

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();
$uri = parse_url($a->get_baseurl());
$path = "/";
if (strlen($uri["path"]) > 1) {
if (isset($uri["path"]) && strlen($uri["path"]) > 1) {
$path = $uri["path"] . "/";
}
@ -12,20 +12,24 @@ define("CALDAV_SQL_PREFIX", "dav_");
define("CALDAV_URL_PREFIX", $path . "dav/");
define("CALDAV_NAMESPACE_PRIVATE", 1);
define("CALDAV_NAMESPACE_FRIENDICA_NATIVE", 2);
define("CALDAV_FRIENDICA_MINE", 1);
define("CALDAV_FRIENDICA_CONTACTS", 2);
define("CALDAV_FRIENDICA_MINE", "friendica-mine");
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_PHONECONTACTS", 2);
define("CALDAV_DB_VERSION", 1);
define("CALDAV_DB_VERSION", 2);
define("CALDAV_MAX_YEAR", date("Y") + 5);
/**
* @return int
*/
function getCurMicrotime () {
function getCurMicrotime()
{
list($usec, $sec) = explode(" ", microtime());
return sprintf("%14.0f", $sec * 10000 + $usec * 10000);
} // function getCurMicrotime
@ -33,7 +37,8 @@ function getCurMicrotime () {
/**
*
*/
function debug_time() {
function debug_time()
{
$cur = getCurMicrotime();
if ($GLOBALS["debug_time_last"] > 0) {
echo "Zeit: " . ($cur - $GLOBALS["debug_time_last"]) . "<br>\n";
@ -48,7 +53,7 @@ function debug_time() {
*/
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"];
return null;
}
@ -59,7 +64,7 @@ function dav_compat_username2id($username = "")
*/
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"];
return "";
}
@ -67,7 +72,8 @@ function dav_compat_id2username($id = 0)
/**
* @return int
*/
function dav_compat_get_curr_user_id() {
function dav_compat_get_curr_user_id()
{
$a = get_app();
return IntVal($a->user["uid"]);
}
@ -86,13 +92,34 @@ function dav_compat_principal2uid($principalUri = "")
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));
}
function dav_compat_currentUserPrincipal() {
$a = get_app();
return "principals/users/" . strtolower($a->user["nickname"]);
}
/**
* @param string $name
* @return null|string
*/
function dav_compat_getRequestVar($name = "") {
if (x($_REQUEST, $name)) return $_REQUEST[$name];
function dav_compat_getRequestVar($name = "")
{
if (isset($_REQUEST[$name])) return $_REQUEST[$name];
else return null;
}
@ -108,28 +135,49 @@ function dav_compat_parse_text_serverside($text)
/**
* @param string $uri
*/
function dav_compat_redirect($uri = "") {
function dav_compat_redirect($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_id
* @return AnimexxCalSource
* @param string $uri
* @param array $calendar
* @return Sabre_CalDAV_Backend_Common
* @throws Exception
*/
function wdcal_calendar_factory($user_id, $namespace, $namespace_id)
function wdcal_calendar_factory($namespace, $namespace_id, $uri, $calendar = null)
{
switch ($namespace) {
case CALDAV_NAMESPACE_PRIVATE:
return new AnimexxCalSourcePrivate($user_id, $namespace_id);
case CALDAV_NAMESPACE_FRIENDICA_NATIVE:
return new FriendicaCalSourceEvents($user_id, $namespace_id);
if ($uri == CALDAV_FRIENDICA_MINE || $uri == CALDAV_FRIENDICA_CONTACTS) return Sabre_CalDAV_Backend_Friendica::getInstance();
else return Sabre_CalDAV_Backend_Private::getInstance();
}
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);
}
/**
*/
@ -138,27 +186,19 @@ function wdcal_create_std_calendars()
$a = get_app();
if (!local_user()) return;
$cals = q("SELECT * FROM %s%scalendars WHERE `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_PRIVATE);
if (count($cals) == 0) {
$maxid = q("SELECT MAX(`namespace_id`) maxid FROM %s%scalendars WHERE `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE);
if (!$maxid) {
notification("Something went wrong when trying to create your calendar.");
goaway("/");
killme();
}
$nextid = IntVal($maxid[0]["maxid"]) + 1;
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $nextid, $a->user["uid"], dbesc(t("Private Calendar")), dbesc($a->timezone)
$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 `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_FRIENDICA_NATIVE);
if (count($cals) < 2) {
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_MINE, $a->user["uid"], dbesc(t("Friendica Events: Mine")), dbesc($a->timezone)
);
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_CONTACTS, $a->user["uid"], dbesc(t("Friendica Events: Contacts")), dbesc($a->timezone)
$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, $a->user["uid"], dbesc($uri));
if (count($cals) == 0) {
q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `displayname`, `timezone`, `ctag`, `uri`, `has_vevent`, `has_vtodo`) VALUES (%d, %d, %d, '%s', '%s', 1, '%s', 1, 0)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, IntVal($a->user["uid"]), dbesc($name), dbesc($a->timezone), dbesc($uri)
);
}
}
}

View file

@ -6,8 +6,8 @@
<title>Really Simple Color Picker</title>
<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 language="javascript" type="text/javascript" src="jquery.colorPicker.min.js"/></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="jquery.colorPicker.min.js"></script>
<script type="text/javascript">
//Run the code when document ready

View file

@ -1,6 +1,11 @@
<?php
define("DAV_ACL_READ", "{DAV:}read");
define("DAV_ACL_WRITE", "{DAV:}write");
define("DAV_DISPLAYNAME", "{DAV:}displayname");
class vcard_source_data_email
{
public $email, $type;
@ -83,7 +88,7 @@ class vcard_source_data
/** @var array|vcard_source_data_email[] $email */
public $emails;
/** @var array|vcard_source_data_addresses[] $addresses */
/** @var array|vcard_source_data_address[] $addresses */
public $addresses;
/** @var vcard_source_data_photo */
@ -136,41 +141,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)
* @return string (Lokalzeit)
@ -216,457 +186,196 @@ function icalendar_sanitize_string($str = "")
$str = str_replace("\r\n", "\n", $str);
$str = str_replace("\n\r", "\n", $str);
$str = str_replace("\r", "\n", $str);
$str = str_replace("\n\n", "\n", $str);
$str = str_replace("\n\n", "\n", $str);
return $str;
}
/**
* @param DBClass_friendica_calendars $calendar
* @param DBClass_friendica_calendarobjects $calendarobject
* @return Sabre_CalDAV_AnimexxCalendarRootNode
*/
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_VEvent
*/
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_VEvent $vObject
*/
function dav_create_empty_vevent($uid = "")
{
$a = get_app();
$v = new vcalendar();
$v->setConfig('unique_id', $a->get_hostname());
$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);
}
}
}
if ($uid == "") $uid = uniqid();
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() .
"\r\nDTSTAMP:" . date("Ymd") . "T" . date("His") . "Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
}
/**
*
*/
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) {
$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
* @param Sabre_VObject_Component_VEvent $vObject
* @return Sabre_VObject_Component_VEvent|null
*/
function wdcal_postEditPage($uri, $recurr_uri = "", $uid = 0, $timezone = "", $goaway_url = "")
function dav_get_eventComponent(&$vObject)
{
$uid = IntVal($uid);
$localization = wdcal_local::getInstanceByUser($uid);
$component = null;
$componentType = "";
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if ($componentType != "VEVENT") return null;
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);
}
}
return $component;
}
/**
*
*/
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;
}
echo wdcal_jsonp_encode($ret);
killme();
}

View file

@ -0,0 +1,185 @@
<?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);
$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');
}
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', '%s', '%s', %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, '%s', %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,14 +1,11 @@
<?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
*/
public $propertyMap = array(
protected $propertyMap = array(
'{DAV:}displayname' => 'displayname',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
@ -17,60 +14,158 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
);
abstract public function getNamespace();
abstract public function getCalUrlPrefix();
/**
* @param int $namespace
* @param int $namespace_id
* @abstract
* @return int
*/
protected function increaseCalendarCtag($namespace, $namespace_id) {
$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);
}
abstract public function getNamespace();
/**
* 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
* @param int $calendarId
* @param string $sd
* @param string $ed
* @param string $base_path
* @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);
$user_id = dav_compat_username2id($name);
$cals = q("SELECT * FROM %s%scalendars WHERE `uid`=%d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $user_id, $this->getNamespace());
$ret = array();
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;
if (!isset(self::$calendarCache[$calendarId])) {
$c = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarCache[$calendarId] = $c[0];
}
return self::$calendarCache[$calendarId];
}
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 +204,8 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
* @param array $mutations
* @return bool|array
*/
public function updateCalendar($calendarId, array $mutations) {
public function updateCalendar($calendarId, array $mutations)
{
$newValues = array();
$result = array(
@ -120,7 +216,7 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
$hasError = false;
foreach($mutations as $propertyName=>$propertyValue) {
foreach ($mutations as $propertyName=> $propertyValue) {
// We don't know about this property.
if (!isset($this->propertyMap[$propertyName])) {
@ -138,33 +234,46 @@ abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
// If there were any errors we need to fail the request
if ($hasError) {
// Properties has the remaining properties
foreach($mutations as $propertyName=>$propertyValue) {
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]);
foreach ($result as $status=> $properties) {
if (is_array($properties) && count($properties) === 0) unset($result[$status]);
}
return $result;
}
$x = explode("-", $calendarId);
$this->increaseCalendarCtag($x[0], $x[1]);
$this->increaseCalendarCtag($calendarId);
$valuesSql = array();
foreach($newValues as $fieldName=>$value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
if (count($valuesSql) > 0) {
q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `namespace` = %d AND `namespace_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1])
);
q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
}
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,495 @@
<?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;
}
/**
* @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);
// @TODO Events, die früher angefangen haben, aber noch andauern
$evs = q("SELECT * 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
* @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 (in_array($cal["uri"], $GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"])) continue;
$dat = array(
"id" => $cal["id"],
"uri" => $cal["uri"],
"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;
}
/**
* 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
* @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("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
{
/**
* @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
*/

View file

@ -1,14 +1,6 @@
<?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 {
/**
@ -21,7 +13,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
/**
* CalDAV backends
*
* @var array|Sabre_CalDAV_Backend_Abstract[]
* @var array|Sabre_CalDAV_Backend_Common[]
*/
protected $caldavBackends;
@ -36,7 +28,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
* Constructor
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param array|Sabre_CalDAV_Backend_Abstract $caldavBackends
* @param array|Sabre_CalDAV_Backend_Common[] $caldavBackends
* @param mixed $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
*
* @param string $name
* @throws Sabre_DAV_Exception_FileNotFound
* @throws Sabre_DAV_Exception_NotFound
* @todo needs optimizing
* @return \Sabre_CalDAV_Calendar|\Sabre_DAV_INode
*/
@ -141,7 +133,7 @@ class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection
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

@ -16,16 +16,19 @@ function wdcal_edit_getStartEnd() {
function wdcal_edit_checktime_startChanged() {
"use strict";
var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) {
var newend = new Date(time.start.getTime() + 3600000);
$("#cal_end_date").datepicker("setDate", newend);
$.timePicker("#cal_end_time").setTime(newend);
}
wdcal_edit_recur_recalc();
}
function wdcal_edit_checktime_endChanged() {
"use strict";
var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) {
var newstart = new Date(time.end.getTime() - 3600000);
@ -34,7 +37,27 @@ 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], #rec_monthly_day option[value=bymonthday]").text($("#rec_yearly_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], #rec_monthly_day option[value=bymonthday_neg]").text($("#rec_yearly_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], #rec_monthly_day option[value=byday]").text(
$("#rec_yearly_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], #rec_monthly_day option[value=byday_neg]").text(
$("#rec_yearly_day option[value=byday_neg]").data("orig").replace("#num#", wk_inv).replace("#wkday#", wkname)
);
}
function wdcal_edit_init(dateFormat, base_path) {
"use strict";
$("#cal_color").colorPicker();
@ -49,6 +72,8 @@ function wdcal_edit_init(dateFormat) {
"dateFormat": dateFormat
}).on("change", wdcal_edit_checktime_endChanged);
$("#rec_until_date").datepicker({ "dateFormat": dateFormat });
$("#notification").on("click change", function() {
if ($(this).prop("checked")) $("#notification_detail").show();
else ($("#notification_detail")).hide();
@ -58,4 +83,111 @@ function wdcal_edit_init(dateFormat) {
if ($(this).prop("checked")) $("#cal_end_time, #cal_start_time").hide();
else $("#cal_end_time, #cal_start_time").show();
}).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());
});
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();
})
});
});
}

View file

@ -5,8 +5,10 @@
(function ($) {
"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 __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);
var __WDAY = $.datepicker._defaults.dayNamesShort;
//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) {
@ -769,8 +771,8 @@
for (i = 0; i < l; i++) {
var $col = $container.find(".tgCol" + i);
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)) {
c = events[i][j].event["color"];
if (events[i][j].event["color"] && events[i][j].event["color"].match(/^[0-9a-f]{6}$/i)) {
c = "#" + events[i][j].event["color"];
}
else {
c = option.std_color;
@ -787,7 +789,7 @@
function getTitle(event) {
var timeshow, eventshow;
var showtime = event["is_allday"] != 1;
eventshow = event["subject"];
eventshow = event["summary"];
var startformat = getymformat(event["start"], null, showtime, true);
var endformat = getymformat(event["end"], event["start"], showtime, true);
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:"" };
p.starttime = pZero(e.st.hour) + ":" + pZero(e.st.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);
var icons = [];
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:"" };
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) {
p.eclass = "drag";
}
else {
p.eclass = "cal_" + e.event["uri"];
p.eclass = "cal_" + e.event["jq_id"];
}
p.eclass += " " + (e.event["is_editable"] ? "editable" : "not_editable");
var sp = "<span style=\"cursor: pointer\">{content}</span>";
@ -1175,10 +1177,10 @@
}
var cen;
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 {
cen = e.event["subject"];
cen = e.event["summary"];
}
var content = [];
if (cen.indexOf("Geburtstag:") == 0) {
@ -1294,7 +1296,7 @@
}
if (option.eventItems[i]["start"] >= es) {
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
jl--;
break;
@ -1477,7 +1479,7 @@
$("#bbit-cs-buddle").css("visibility", "hidden");
var calid = $("#bbit-cs-id").val();
var param = [
{ "name":"calendarId", value:calid },
{ "name":"jq_id", value:calid },
{ "name":"type", value:type}
];
var de = rebyKey(calid, true);
@ -1609,8 +1611,8 @@
var location = "";
if (data["location"] != "") location = data["location"] + ", ";
$("#bbit-cs-buddle-timeshow").html(location + ss.join(""));
$bud.find(".bbit-cs-what").html(data["subject"]).attr("href", data["url_detail"]);
$("#bbit-cs-id").val(data["uri"]);
$bud.find(".bbit-cs-what").html(data["summary"]).attr("href", data["url_detail"]);
$("#bbit-cs-id").val(data["jq_id"]);
$bud.data("cdata", data);
$bud.css({ "visibility":"visible", left:pos.left, top:pos.top });
@ -1684,11 +1686,11 @@
return false;
}
option.isloading = true;
var id = data["uri"];
var id = data["jq_id"];
var os = data["start"];
var od = data["end"];
var param = [
{ "name":"calendarId", value:id },
{ "name":"jq_id", value:id },
{ "name":"CalendarStartTime", value:Math.floor(start.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(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.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.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>');
@ -1789,7 +1791,6 @@
param[param.length] = option.extParam[pi];
}
}
if (option.quickAddHandler && $.isFunction(option.quickAddHandler)) {
option.quickAddHandler.call(this, param);
$("#bbit-cal-buddle").css("visibility", "hidden");
@ -1804,8 +1805,9 @@
ed = new Date(dateend),
diff = DateDiff("d", sd, ed);
var newdata = {
"uri":"",
"subject":what,
"jq_id":"",
"ev_id":"",
"summary":what,
"start":sd,
"end":ed,
"is_allday":(allday == "1" ? 1 : 0),
@ -1886,7 +1888,7 @@
var sl = option.eventItems.length;
var i = -1;
for (var j = 0; j < sl; j++) {
if (option.eventItems[j]["uri"] == key) {
if (option.eventItems[j]["jq_id"] == key) {
i = j;
break;
}
@ -2352,7 +2354,7 @@
d.target.hide();
ny = gP(gh.sh, gh.sm);
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);
evid = ".tgOver" + d.target.parent().data("col");
$gridcontainer.find(evid).append(cpwrap);
@ -2389,7 +2391,7 @@
//log.info("ny=" + ny);
gh = gW(ny, ny + d.h);
//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.ny = ny;
@ -2415,7 +2417,7 @@
d.target.hide();
ny = gP(gh.sh, gh.sm);
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);
evid = ".tgOver" + d.target.parent().data("col");
$gridcontainer.find(evid).append(cpwrap);
@ -2427,7 +2429,7 @@
nh = pnh > 1 ? nh - pnh + Math.ceil(option.hour_height / 2) : nh - pnh;
if (d.nh != 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.nh = nh;

View file

@ -7,26 +7,7 @@ var i18n = $.extend({}, i18n || {}, {
"year_index": 2,
"month_index": 1,
"day_index": 0,
"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"
"day": "d"
},
"no_implemented": "Nicht eingebaut",
"to_date_view": "Zum aktuellen Datum gehen",

View file

@ -7,26 +7,7 @@ var i18n = $.extend({}, i18n || {}, {
"year_index": 2,
"month_index": 1,
"day_index": 0,
"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"
"day": "d"
},
"no_implemented": "Not implemented",
"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

@ -40,6 +40,13 @@ abstract class wdcal_local
return $format;
}
/**
* @static
* @abstract
* @return string
*/
abstract static function getLanguageCode();
/**
* @abstract
* @static
@ -77,6 +84,13 @@ abstract class wdcal_local
*/
abstract function date_timestamp2local($ts);
/**
* @abstract
* @param int $ts
* @return string
*/
abstract function date_timestamp2localDate($ts);
/**
* @abstract
* @return int
@ -119,6 +133,14 @@ abstract class wdcal_local
class wdcal_local_us extends wdcal_local {
/**
* @static
* @return string
*/
static function getLanguageCode() {
return "en";
}
/**
* @return string
*/
@ -152,6 +174,14 @@ class wdcal_local_us extends wdcal_local {
return date("m/d/Y H:i", $ts);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2localDate($ts) {
return date("l, F jS Y", $ts);
}
/**
* @return int
*/
@ -198,6 +228,14 @@ class wdcal_local_us extends wdcal_local {
class wdcal_local_de extends wdcal_local {
/**
* @static
* @return string
*/
static function getLanguageCode() {
return "de";
}
/**
* @return string
*/
@ -231,6 +269,14 @@ class wdcal_local_de extends wdcal_local {
return date("d.m.Y H:i", $ts);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2localDate($ts) {
return date("l, j. F Y", $ts);
}
/**
* @return int
*/

View file

@ -0,0 +1,510 @@
<?php
/**
* @param wdcal_local $localization
* @param string $baseurl
* @param int $uid
* @param int $calendar_id
* @param int $uri
* @param string $recurr_uri
* @return string
*/
function wdcal_getEditPage_str(&$localization, $baseurl, $uid, $calendar_id, $uri, $recurr_uri = "")
{
$server = dav_create_server(true, true, false);
if ($uri > 0) {
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
if (!$calendar) {
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_READ);
$calendars = array();
} else {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
}
if ($calendar == null) return "Calendar not found";
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($uri);
$vObject = dav_get_current_user_calendarobject($server, $calendar, $obj_uri["uri"], DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if ($component == null) return t('Could not open component for editing');
/** @var Sabre_VObject_Property_DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$event = array(
"id" => IntVal($uri),
"Summary" => ($component->__get("SUMMARY") ? $component->__get("SUMMARY")->value : null),
"StartTime" => $dtstart->getDateTime()->getTimeStamp(),
"EndTime" => Sabre_CalDAV_Backend_Common::getDtEndTimeStamp($component),
"IsAllDayEvent" => (strlen($dtstart->value) == 8),
"Description" => ($component->__get("DESCRIPTION") ? $component->__get("DESCRIPTION")->value : null),
"Location" => ($component->__get("LOCATION") ? $component->__get("LOCATION")->value : null),
"Color" => ($component->__get("X-ANIMEXX-COLOR") ? $component->__get("X-ANIMEXX-COLOR")->value : null),
);
$exdates = $component->select("EXDATE");
$recurrentce_exdates = array();
/** @var Sabre_VObject_Property_MultiDateTime $x */
foreach ($exdates as $x) {
/** @var DateTime $y */
$z = $x->getDateTimes();
foreach ($z as $y) $recurrentce_exdates[] = $y->getTimeStamp();
}
if ($component->select("RRULE")) $recurrence = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->__get("UID"));
else $recurrence = null;
} elseif (isset($_REQUEST["start"]) && $_REQUEST["start"] > 0) {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
$event = array(
"id" => 0,
"Summary" => $_REQUEST["title"],
"StartTime" => InTVal($_REQUEST["start"]),
"EndTime" => IntVal($_REQUEST["end"]),
"IsAllDayEvent" => $_REQUEST["isallday"],
"Description" => "",
"Location" => "",
"Color" => "#5858ff",
);
if ($_REQUEST["isallday"]) {
$notifications = array(array("rel" => "start", "type" => "duration", "period" => "hour", "period_val" => 24));
} else {
$notifications = array(array("rel" => "start", "type" => "duration", "period" => "hour", "period_val" => 1));
}
$recurrence = null;
$recurrentce_exdates = array();
} else {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
$event = array(
"id" => 0,
"Summary" => "",
"StartTime" => time(),
"EndTime" => time() + 3600,
"IsAllDayEvent" => "0",
"Description" => "",
"Location" => "",
"Color" => "#5858ff",
);
$notifications = array(array("rel" => "start", "type" => "duration", "period" => "hour", "period_val" => 1));
$recurrence = null;
$recurrentce_exdates = array();
}
$postto = $baseurl . "/dav/wdcal/" . ($uri == 0 ? "new/" : $calendar_id . "/" . $uri . "/edit/");
$out = "<a href='" . $baseurl . "/dav/wdcal/'>" . t("Go back to the calendar") . "</a><br><br>";
$out .= "<form method='POST' action='$postto'>
<input type='hidden' name='form_security_token' value='" . get_form_security_token('caledit') . "'>\n";
$out .= "<h2>" .t("Event data") . "</h2>";
$out .= "<label for='calendar'>" . t("Calendar") . ":</label><select name='calendar' size='1'>";
foreach ($calendars as $cal) {
$prop = $cal->getProperties(array("id", DAV_DISPLAYNAME));
$out .= "<option value='" . $prop["id"] . "' ";
if ($prop["id"] == $calendar_id) $out .= "selected";
$out .= ">" . escape_tags($prop[DAV_DISPLAYNAME]) . "</option>\n";
}
$out .= "</select><br>\n";
$out .= "<label class='block' for='cal_summary'>" . t("Subject") . ":</label>
<input name='color' id='cal_color' value='" . (strlen($event["Color"]) != 7 ? "#5858ff" : escape_tags($event["Color"])) . "'>
<input name='summary' id='cal_summary' value='" . escape_tags($event["Summary"]) . "'><br>\n";
$out .= "<label class='block' for='cal_allday'>Is All-Day event:</label><input type='checkbox' name='allday' id='cal_allday' " . ($event["IsAllDayEvent"] ? "checked" : "") . "><br>\n";
$out .= "<label class='block' for='cal_startdate'>" . t("Starts") . ":</label>";
$out .= "<input name='start_date' value='" . $localization->dateformat_datepicker_php($event["StartTime"]) . "' id='cal_start_date'>";
$out .= "<input name='start_time' value='" . date("H:i", $event["StartTime"]) . "' id='cal_start_time'>";
$out .= "<br>\n";
$out .= "<label class='block' for='cal_enddate'>" . t("Ends") . ":</label>";
$out .= "<input name='end_date' value='" . $localization->dateformat_datepicker_php($event["EndTime"]) . "' id='cal_end_date'>";
$out .= "<input name='end_time' value='" . date("H:i", $event["EndTime"]) . "' id='cal_end_time'>";
$out .= "<br>\n";
$out .= "<label class='block' for='cal_location'>" . t("Location") . ":</label><input name='location' id='cal_location' value='" . escape_tags($event["Location"]) . "'><br>\n";
$out .= "<label class='block' for='event-desc-textarea'>" . t("Description") . ":</label> <textarea id='event-desc-textarea' name='wdcal_desc' style='vertical-align: top; width: 400px; height: 100px;'>" . escape_tags($event["Description"]) . "</textarea>";
$out .= "<br style='clear: both;'>";
$out .= "<h2>" .t("Recurrence") . "</h2>";
$out .= "<label class='block' for='rec_frequency'>" . t("Frequency") . ":</label> <select id='rec_frequency' name='rec_frequency' size='1'>";
$out .= "<option value=''>" . t("None") . "</option>\n";
$out .= "<option value='daily' "; if ($recurrence && $recurrence->frequency == "daily") $out .= "selected"; $out .= ">" . t("Daily") . "</option>\n";
$out .= "<option value='weekly' "; if ($recurrence && $recurrence->frequency == "weekly") $out .= "selected"; $out .= ">" . t("Weekly") . "</option>\n";
$out .= "<option value='monthly' "; if ($recurrence && $recurrence->frequency == "monthly") $out .= "selected"; $out .= ">" . t("Monthly") . "</option>\n";
$out .= "<option value='yearly' "; if ($recurrence && $recurrence->frequency == "yearly") $out .= "selected"; $out .= ">" . t("Yearly") . "</option>\n";
$out .="</select><br>\n";
$out .= "<div id='rec_details'>";
$select = "<select id='rec_interval' name='rec_interval' size='1'>";
for ($i = 1; $i < 50; $i++) {
$select .= "<option value='$i' ";
if ($recurrence && $i == $recurrence->interval) $select .= "selected";
$select .= ">$i</option>\n";
}
$select .= "</select>";
$time = "<span class='rec_daily'>" . t("days") . "</span>";
$time .= "<span class='rec_weekly'>" . t("weeks") . "</span>";
$time .= "<span class='rec_monthly'>" . t("months") . "</span>";
$time .= "<span class='rec_yearly'>" . t("years") . "</span>";
$out .= "<label class='block' for='rev_interval'>" . t("Interval") . ":</label> " . str_replace(array("%select%", "%time%"), array($select, $time), t("All %select% %time%")) . "<br>";
$out .= "<div class='rec_daily'>";
$out .= "<label class='block'>" . t("Days") . ":</label>";
if ($recurrence && $recurrence->byDay) {
$byday = $recurrence->byDay;
} else {
$byday = array("MO", "TU", "WE", "TH", "FR", "SA", "SU");
}
if ($localization->getFirstDayOfWeek() == 0) {
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' "; if (in_array("SU", $byday)) $out .= "checked"; $out .= ">" . t("Sunday") . "</label> &nbsp; ";
}
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='MO' "; if (in_array("MO", $byday)) $out .= "checked"; $out .= ">" . t("Monday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TU' "; if (in_array("TU", $byday)) $out .= "checked"; $out .= ">" . t("Tuesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='WE' "; if (in_array("WE", $byday)) $out .= "checked"; $out .= ">" . t("Wednesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TH' "; if (in_array("TH", $byday)) $out .= "checked"; $out .= ">" . t("Thursday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='FR' "; if (in_array("FR", $byday)) $out .= "checked"; $out .= ">" . t("Friday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SA' "; if (in_array("SA", $byday)) $out .= "checked"; $out .= ">" . t("Saturday") . "</label> &nbsp; ";
if ($localization->getFirstDayOfWeek() != 0) {
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' "; if (in_array("SU", $byday)) $out .= "checked"; $out .= ">" . t("Sunday") . "</label> &nbsp; ";
}
$out .= "</div>";
$out .= "<div class='rec_weekly'>";
$out .= "<label class='block'>" . t("Days") . ":</label>";
if ($recurrence && $recurrence->byDay) {
$byday = $recurrence->byDay;
} else {
$byday = array("MO", "TU", "WE", "TH", "FR", "SA", "SU");
}
if ($localization->getFirstDayOfWeek() == 0) {
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' "; if (in_array("SU", $byday)) $out .= "checked"; $out .= ">" . t("Sunday") . "</label> &nbsp; ";
}
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='MO' "; if (in_array("MO", $byday)) $out .= "checked"; $out .= ">" . t("Monday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TU' "; if (in_array("TU", $byday)) $out .= "checked"; $out .= ">" . t("Tuesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='WE' "; if (in_array("WE", $byday)) $out .= "checked"; $out .= ">" . t("Wednesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TH' "; if (in_array("TH", $byday)) $out .= "checked"; $out .= ">" . t("Thursday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='FR' "; if (in_array("FR", $byday)) $out .= "checked"; $out .= ">" . t("Friday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SA' "; if (in_array("SA", $byday)) $out .= "checked"; $out .= ">" . t("Saturday") . "</label> &nbsp; ";
if ($localization->getFirstDayOfWeek() != 0) {
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' "; if (in_array("SU", $byday)) $out .= "checked"; $out .= ">" . t("Sunday") . "</label> &nbsp; ";
}
$out .= "<br>";
$out .= "<label class='block'>" . t("First day of week:") . "</label>";
if ($recurrence && $recurrence->weekStart != "") $wkst = $recurrence->weekStart;
else {
if ($localization->getFirstDayOfWeek() == 0) $wkst = "SU";
else $wkst = "MO";
}
$out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='SU' "; if ($wkst == "SU") $out .= "checked"; $out .= ">" . t("Sunday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='MO' "; if ($wkst == "MO") $out .= "checked"; $out .= ">" . t("Monday") . "</label><br>\n";
$out .= "</div>";
$monthly_rule = "bymonthday"; // @TODO
$out .= "<div class='rec_monthly'>";
$out .= "<label class='block' name='rec_monthly_day'>" . t("Day of month") . ":</label>";
$out .= "<select id='rec_monthly_day' name='rec_monthly_day' size='1'>";
$out .= "<option value='bymonthday' "; if ($monthly_rule == "bymonthday") $out .= "selected"; $out .= ">" . t("#num#th of each month") . "</option>\n";
$out .= "<option value='bymonthday_neg' "; if ($monthly_rule == "bymonthday_neg") $out .= "selected"; $out .= ">" . t("#num#th-last of each month") . "</option>\n";
$out .= "<option value='byday' "; if ($monthly_rule == "byday") $out .= "selected"; $out .= ">" . t("#num#th #wkday# of each month") . "</option>\n";
$out .= "<option value='byday_neg' "; if ($monthly_rule == "byday_neg") $out .= "selected"; $out .= ">" . t("#num#th-last #wkday# of each month") . "</option>\n";
$out .= "</select>";
$out .= "</div>\n";
$out .= "<div class='rec_yearly'>";
$out .= "<label class='block' name='rec_yearly_day'>" . t("Month") . ":</label> <span class='rec_month_name'>#month#</span><br>\n";
$out .= "<label class='block' name='rec_yearly_day'>" . t("Day of month") . ":</label>";
$out .= "<select id='rec_yearly_day' name='rec_yearly_day' size='1'>";
$out .= "<option value='bymonthday' "; if ($monthly_rule == "bymonthday") $out .= "selected"; $out .= ">" . t("#num#th of each month") . "</option>\n";
$out .= "<option value='bymonthday_neg' "; if ($monthly_rule == "bymonthday_neg") $out .= "selected"; $out .= ">" . t("#num#th-last of each month") . "</option>\n";
$out .= "<option value='byday' "; if ($monthly_rule == "byday") $out .= "selected"; $out .= ">" . t("#num#th #wkday# of each month") . "</option>\n";
$out .= "<option value='byday_neg' "; if ($monthly_rule == "byday_neg") $out .= "selected"; $out .= ">" . t("#num#th-last #wkday# of each month") . "</option>\n";
$out .= "</select>";
$out .= "</div>\n";
if ($recurrence) {
$until = $recurrence->until;
$count = $recurrence->count;
if (is_a($until, "DateTime")) {
/** @var DateTime $until */
$rule_type = "date";
$rule_until_date = $until->getTimestamp();
$rule_until_count = 1;
} elseif ($count > 0) {
$rule_type = "count";
$rule_until_date = time();
$rule_until_count = $count;
} else {
$rule_type = "infinite";
$rule_until_date = time();
$rule_until_count = 1;
}
} else {
$rule_type = "infinite";
$rule_until_date = time();
$rule_until_count = 1;
}
$out .= "<label class='block' for='rec_until_type'>" . t("Repeat until") . ":</label> ";
$out .= "<select name='rec_until_type' id='rec_until_type' size='1'>";
$out .= "<option value='infinite' "; if ($rule_type == "infinite") $out .= "selected"; $out .= ">" . t("Infinite") . "</option>\n";
$out .= "<option value='date' "; if ($rule_type == "date") $out .= "selected"; $out .= ">" . t("Until the following date") . ":</option>\n";
$out .= "<option value='count' "; if ($rule_type == "count") $out .= "selected"; $out .= ">" . t("Number of times") . ":</option>\n";
$out .= "</select>";
$out .= "<input name='rec_until_date' value='" . $localization->dateformat_datepicker_php($rule_until_date) . "' id='rec_until_date'>";
$out .= "<input name='rec_until_count' value='$rule_until_count' id='rec_until_count'><br>";
$out .= "<label class='block'>" . t("Exceptions") . ":</label><div class='rec_exceptions'>";
$out .= "<div class='rec_exceptions_none' ";
if (count($recurrentce_exdates) > 0) $out .= "style='display: none;'";
$out .= ">" . t("none") . "</div>";
$out .= "<div class='rec_exceptions_holder' ";
if (count($recurrentce_exdates) == 0) $out .= "style='display: none;'";
$out .= ">";
foreach ($recurrentce_exdates as $exdate) {
$out .= "<div data-timestamp='$exdate' class='except'><input type='hidden' class='rec_exception' name='rec_exceptions[]' value='$exdate'>";
$out .= "<a href='#' class='exception_remover'>[remove]</a> ";
$out .= $localization->date_timestamp2localDate($exdate);
$out .= "</div>\n";
}
$out .= "</div><div><a href='#' class='exception_adder'>[add]</a></div>";
$out .= "</div>\n";
$out .= "<br>\n";
$out .= "</div><br>";
$out .= "<h2>" .t("Notification") . "</h2>";
/*
$out .= '<input type="checkbox" name="notification" id="notification" ';
if ($notification) $out .= "checked";
$out .= '> ';
$out .= '<span id="notification_detail" style="display: none;">
<input name="notification_value" value="' . $notification_value . '" size="3">
<select name="notification_type" size="1">
<option value="minute" ';
if ($notification_type == "minute") $out .= "selected";
$out .= '> ' . t('Minutes') . '</option>
<option value="hour" ';
if ($notification_type == "hour") $out .= "selected";
$out .= '> ' . t('Hours') . '</option>
<option value="day" ';
if ($notification_type == "day") echo "selected";
$out .= '> ' . t('Days') . '</option>
</select> ' . t('before') . '
</span><br><br>';
*/
$out .= "<script>\$(function() {
wdcal_edit_init('" . $localization->dateformat_datepicker_js() . "', '${baseurl}/dav/');
});</script>";
$out .= "<input type='submit' name='save' value='Save'></form>";
return $out;
}
/**
* @param Sabre_VObject_Component_VEvent $component
* @param wdcal_local $localization
*/
function wdcal_set_component_date(&$component, &$localization) {
if (isset($_REQUEST["allday"])) {
$ts_start = $localization->date_local2timestamp($_REQUEST["start_date"] . " 00:00");
$ts_end = $localization->date_local2timestamp($_REQUEST["end_date"] . " 00:00");
$type = Sabre_VObject_Property_DateTime::DATE;
} else {
$ts_start = $localization->date_local2timestamp($_REQUEST["start_date"] . " " . $_REQUEST["start_time"]);
$ts_end = $localization->date_local2timestamp($_REQUEST["end_date"] . " " . $_REQUEST["end_time"]);
$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", $ts_start)), $type);
$datetime_end = new Sabre_VObject_Property_DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date("Y-m-d H:i:s", $ts_end)), $type);
$component->__unset("DTSTART");
$component->__unset("DTEND");
$component->add($datetime_start);
$component->add($datetime_end);
}
/**
* @param Sabre_VObject_Component_VEvent $component
* @param wdcal_local $localization
*/
function wdcal_set_component_recurrence(&$component, &$localization) {
$component->__unset("RRULE");
$component->__unset("EXRULE");
$component->__unset("EXDATE");
$component->__unset("RDATE");
$part_until = "";
switch ($_REQUEST["rec_until_type"]) {
case "date":
$date = $localization->date_local2timestamp($_REQUEST["rec_until_date"]);
$part_until = ";UNTIL=" . date("Ymd", $date);
$datetime_until = new Sabre_VObject_Property_DateTime("UNTIL");
$datetime_until->setDateTime(new DateTime(date("Y-m-d H:i:s", $date)), Sabre_VObject_Property_DateTime::DATE);
break;
case "count":
$part_until = ";COUNT=" . IntVal($_REQUEST["rec_until_count"]);
break;
}
switch ($_REQUEST["rec_frequency"]) {
case "daily":
$part_freq = "FREQ=DAILY";
if (isset($_REQUEST["rec_daily_byday"])) {
$days = array();
foreach ($_REQUEST["rec_daily_byday"] as $x) if (in_array($x, array("MO", "TU", "WE", "TH", "FR", "SA", "SU"))) $days[] = $x;
if (count($days) > 0) $part_freq .= ";BYDAY=" . implode(",", $days);
}
break;
case "weekly":
$part_freq = "FREQ=WEEKLY";
if (isset($_REQUEST["rec_weekly_wkst"]) && in_array($_REQUEST["rec_weekly_wkst"], array("MO", "SU"))) $part_freq .= ";WKST=" . $_REQUEST["rec_weekly_wkst"];
if (isset($_REQUEST["rec_weekly_byday"])) {
$days = array();
foreach ($_REQUEST["rec_weekly_byday"] as $x) if (in_array($x, array("MO", "TU", "WE", "TH", "FR", "SA", "SU"))) $days[] = $x;
if (count($days) > 0) $part_freq .= ";BYDAY=" . implode(",", $days);
}
break;
case "monthly":
$part_freq = "FREQ=MONTHLY";
break;
case "FREQ=yearly":
$part_freq = "FREQ=YEARLY";
break;
default:
$part_freq = "";
}
if ($part_freq == "") return;
if (isset($_REQUEST["rec_interval"])) $part_freq .= ";INTERVAL=" . InTVal($_REQUEST["rec_interval"]);
if (isset($_REQUEST["rec_exceptions"])) {
$arr = array();
foreach ($_REQUEST["rec_exceptions"] as $except) {
$arr[] = new DateTime(date("Y-m-d H:i:s", $except));
}
/** @var Sabre_VObject_Property_MultiDateTime $prop */
$prop = Sabre_VObject_Property::create("EXDATE");
$prop->setDateTimes($arr);
$component->add($prop);
}
$rrule = $part_freq . $part_until;
$component->add(new Sabre_VObject_Property("RRULE", $rrule));
}
/**
* @param string $uri
* @param string $recurr_uri
* @param int $uid
* @param string $timezone
* @param string $goaway_url
* @return array
*/
function wdcal_postEditPage($uri, $recurr_uri = "", $uid = 0, $timezone = "", $goaway_url = "")
{
$uid = IntVal($uid);
$localization = wdcal_local::getInstanceByUser($uid);
$server = dav_create_server(true, true, false);
if ($uri > 0) {
$calendar = dav_get_current_user_calendar_by_id($server, $_REQUEST["calendar"], DAV_ACL_READ);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($uri);
$obj_uri = $obj_uri["uri"];
$vObject = dav_get_current_user_calendarobject($server, $calendar, $obj_uri, DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if ($component == null) return array("ok" => false, "msg" => t('Could not open component for editing'));
} else {
$calendar = dav_get_current_user_calendar_by_id($server, $_REQUEST["calendar"], DAV_ACL_WRITE);
$vObject = dav_create_empty_vevent();
$component = dav_get_eventComponent($vObject);
$obj_uri = $component->__get("UID");
}
wdcal_set_component_date($component, $localization);
wdcal_set_component_recurrence($component, $localization);
$component->__unset("LOCATION");
$component->__unset("SUMMARY");
$component->__unset("DESCRIPTION");
$component->__unset("X-ANIMEXXCOLOR");
$component->add("SUMMARY", icalendar_sanitize_string(dav_compat_parse_text_serverside("summary")));
$component->add("LOCATION", icalendar_sanitize_string(dav_compat_parse_text_serverside("location")));
$component->add("DESCRIPTION", icalendar_sanitize_string(dav_compat_parse_text_serverside("wdcal_desc")));
$component->add("X-ANIMEXX-COLOR", $_REQUEST["color"]);
$data = $vObject->serialize();
if ($uri == 0) {
$calendar->createFile($obj_uri . ".ics", $data);
} else {
$obj = $calendar->getChild($obj_uri);
$obj->put($data);
}
return array("ok" => false, "msg" => t("Saved"));
}
/**
* @return string
*/
function wdcal_getEditPage_exception_selector() {
header("Content-type: application/json");
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
$vObject = wdcal_create_empty_vevent();
foreach($vObject->getComponents() as $component) {
if ($component->name!=='VTIMEZONE') break;
}
/** @var Sabre_VObject_Component_VEvent $component */
wdcal_set_component_date($component, $localization);
wdcal_set_component_recurrence($component, $localization);
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->__get("UID"));
$max_ts = mktime(0, 0, 0, 1, 1, CALDAV_MAX_YEAR + 1);
$last_start = 0;
$o = "<ul>";
$i = 0;
while($it->valid() && $last_start < $max_ts && $i++ < 1000) {
$last_start = $it->getDtStart()->getTimestamp();
$o .= "<li><a href='#' class='exception_selector_link' data-timestamp='$last_start'>" . $localization->date_timestamp2localDate($last_start) . "</a></li>\n";
$it->next();
}
$o .= "</ul>\n";
return $o;
}

View file

@ -1,10 +1,80 @@
<?php
/**
* @param int $from_version
* @return array|string[]
*/
function dav_get_update_statements($from_version)
{
$stms = array();
if ($from_version <= 0) {
$stms[] = "ALTER TABLE `dav_calendars` ADD `uri` VARCHAR( 50 ) NULL DEFAULT NULL AFTER `description` , ADD `has_vevent` TINYINT NOT NULL DEFAULT '1' AFTER `uri` , ADD `has_vtodo` TINYINT NOT NULL DEFAULT '1' AFTER `has_vevent`";
$stms[] = "UPDATE `dav_calendars` SET `uri` = 'private' WHERE `namespace` = 1";
$stms[] = "UPDATE `dav_calendars` SET `uri` = 'friendica-mine' WHERE `namespace` = 2 AND `namespace_id` = 1";
$stms[] = "UPDATE `dav_calendars` SET `uri` = 'friendica-contacts' WHERE `namespace` = 2 AND `namespace_id` = 2";
$stms[] = "ALTER TABLE `dav_calendars` DROP PRIMARY KEY ";
$stms[] = "ALTER TABLE `dav_calendars` ADD `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST ";
$stms[] = "ALTER TABLE `dav_calendarobjects` ADD `calendar_id` INT NOT NULL AFTER `id` ";
$stms[] = "UPDATE `dav_calendarobjects` a JOIN `dav_calendars` b ON a.`namespace` = b.`namespace` AND a.`namespace_id` = b.`namespace_id` SET a.`calendar_id` = b.`id`";
$stms[] = "ALTER TABLE `dav_calendarobjects` DROP `namespace` , DROP `namespace_id` ;";
$stms[] = "ALTER TABLE `dav_calendarobjects` ADD INDEX ( `calendar_id` ) ";
$stms[] = "ALTER TABLE `dav_calendarobjects` ADD `componentType` ENUM( 'VEVENT', 'VTODO' ) NOT NULL DEFAULT 'VEVENT' AFTER `calendardata` ,
ADD `firstOccurence` TIMESTAMP NOT NULL AFTER `lastmodified` ,
ADD `lastOccurence` TIMESTAMP NOT NULL AFTER `firstOccurence`";
$stms[] = "DROP TABLE `dav_jqcalendar`";
$stms[] = "CREATE TABLE IF NOT EXISTS `dav_jqcalendar` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ical_recurr_uri` varchar(100) DEFAULT NULL,
`calendar_id` int(10) unsigned NOT NULL,
`calendarobject_id` int(10) unsigned NOT NULL,
`Subject` varchar(1000) NOT NULL,
`StartTime` timestamp NULL DEFAULT NULL,
`EndTime` timestamp NULL DEFAULT NULL,
`IsEditable` tinyint(3) unsigned NOT NULL,
`IsAllDayEvent` tinyint(4) NOT NULL,
`IsRecurring` tinyint(4) NOT NULL,
`Color` CHAR(6) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `calendarByStart` (`calendar_id`,`StartTime`),
KEY `calendarobject_id` (`calendarobject_id`,`ical_recurr_uri`)
) DEFAULT CHARSET=utf8 ";
$stms[] = "ALTER TABLE `dav_notifications` ADD `calendar_id` INT NOT NULL AFTER `ical_recurr_uri` ";
$stms[] = "ALTER TABLE `dav_notifications` DROP INDEX `ical_uri` , ADD INDEX `ical_uri` ( `calendar_id` , `ical_uri` , `ical_recurr_uri` ) ";
$stms[] = "TRUNCATE TABLE `dav_notifications`";
$stms[] = "ALTER TABLE `dav_notifications` DROP `namespace` , DROP `namespace_id`";
$stms[] = "TRUNCATE TABLE `dav_cal_virtual_object_cache`";
$stme[] = "ALTER TABLE `dav_cal_virtual_object_cache` ADD `calendar_id` INT UNSIGNED NOT NULL AFTER `id` ";
$stms[] = "ALTER TABLE `dav_cal_virtual_object_cache` DROP INDEX `ref_type` , ADD INDEX `ref_type` ( `calendar_id` , `data_end` ) ";
$stms[] = "ALTER TABLE `dav_cal_virtual_object_cache` DROP `uid`, DROP `namespace` , DROP `namespace_id` ";
$stms[] = "CREATE TABLE `friendica`.`dav_cal_virtual_object_sync` (
`calendar_id` INT UNSIGNED NOT NULL ,
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( `calendar_id` )
) ENGINE=MyISAM DEFAULT CHARSET=utf8";
$stms[] = "DROP TABLE `dav_cache_synchronized` ";
$stms[] = "UPDATE `dav_calendars` SET `namespace` = 1, `namespace_id` = `uid`"; // last
$stms[] = "ALTER TABLE `dav_calendars` DROP `uid`";
}
return $stms;
}
/**
* @return array
*/
function dav_get_create_statements() {
function dav_get_create_statements()
{
$arr = array();
$arr[] = "CREATE TABLE IF NOT EXISTS `dav_addressbooks_community` (
@ -69,6 +139,9 @@ function dav_get_create_statements() {
`displayname` varchar(200) NOT NULL,
`timezone` text NOT NULL,
`description` varchar(500) NOT NULL,
`uri` varchar(50) DEFAULT NULL,
`has_vevent` tinyint(4) NOT NULL DEFAULT '1',
`has_vtodo` tinyint(4) NOT NULL DEFAULT '1',
`ctag` int(10) unsigned NOT NULL,
PRIMARY KEY (`namespace`,`namespace_id`),
KEY `uid` (`uid`)
@ -171,10 +244,11 @@ function dav_get_create_statements() {
/**
* @return int
*/
function dav_check_tables() {
function dav_check_tables()
{
$dbv = get_config("dav", "db_version");
if ($dbv == CALDAV_DB_VERSION) return 0; // Correct
if (is_numeric($dbv)) return 1; // Older version (update needed)
if (is_numeric($dbv) || $dbv == "CALDAV_DB_VERSION") return 1; // Older version (update needed)
return -1; // Not installed
}
@ -197,3 +271,25 @@ function dav_create_tables()
return $errors;
}
/**
* @return array
*/
function dav_upgrade_tables()
{
$dbv = get_config("dav", "db_version");
if ($dbv == "CALDAV_DB_VERSION") $ver = 0;
else $ver = IntVal($dbv);
$stms = dav_get_update_statements($ver);
$errors = array();
global $db;
foreach ($stms as $st) {
$db->q($st);
if ($db->error) $errors[] = $db->error;
}
if (count($errors) == 0) set_config("dav", "db_version", CALDAV_DB_VERSION);
return $errors;
}

View file

@ -2,7 +2,7 @@
/**
* Name: Calendar with CalDAV Support
* Description: A web-based calendar system with CalDAV-support. Also brings your Friendica-Contacts to your CardDAV-capable mobile phone. Requires PHP >= 5.3.
* Version: 0.1.1
* Version: 0.2.0
* Author: Tobias Hößl <https://github.com/CatoTH/>
*/

View file

@ -0,0 +1,245 @@
<?php
class Sabre_CalDAV_Backend_Friendica extends Sabre_CalDAV_Backend_Virtual
{
/**
* @var null|Sabre_CalDAV_Backend_Friendica
*/
private static $instance = null;
/**
* @static
* @return Sabre_CalDAV_Backend_Friendica
*/
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new Sabre_CalDAV_Backend_Friendica();
}
return self::$instance;
}
/**
* @return int
*/
public function getNamespace()
{
return CALDAV_NAMESPACE_PRIVATE;
}
/**
* @static
* @param int $calendarId
* @throws Sabre_DAV_Exception_NotFound
* @return void
*/
protected static function createCache_internal($calendarId)
{
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
switch ($calendar["uri"]) {
case CALDAV_FRIENDICA_MINE:
$sql_where = " AND cid = 0";
break;
case CALDAV_FRIENDICA_CONTACTS:
$sql_where = " AND cid > 0";
break;
default:
throw new Sabre_DAV_Exception_NotFound();
}
$r = q("SELECT * FROM `event` WHERE `uid` = %d " . $sql_where . " ORDER BY `start`", IntVal($calendar["namespace_id"]));
foreach ($r as $row) {
$uid = $calendar["uri"] . "-" . $row["id"];
$vevent = dav_create_empty_vevent($uid);
$component = dav_get_eventComponent($vevent);
if ($row["adjust"]) {
$start = datetime_convert('UTC', date_default_timezone_get(), $row["start"]);
$finish = datetime_convert('UTC', date_default_timezone_get(), $row["finish"]);
} else {
$start = $row["start"];
$finish = $row["finish"];
}
$summary = ($row["summary"] != "" ? $row["summary"] : $row["desc"]);
$desc = ($row["summary"] != "" ? $row["desc"] : "");
$component->add("SUMMARY", icalendar_sanitize_string($summary));
$component->add("LOCATION", icalendar_sanitize_string($row["location"]));
$component->add("DESCRIPTION", icalendar_sanitize_string($desc));
$ts_start = wdcal_mySql2PhpTime($start);
$ts_end = wdcal_mySql2PhpTime($start);
$allday = (strpos($start, "00:00:00") !== false && strpos($finish, "00:00:00") !== false);
$type = ($allday ? Sabre_VObject_Property_DateTime::DATE : 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", $ts_start)), $type);
$datetime_end = new Sabre_VObject_Property_DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date("Y-m-d H:i:s", $ts_end)), $type);
$component->add($datetime_start);
$component->add($datetime_end);
$data = $vevent->serialize();
q("INSERT INTO %s%scal_virtual_object_cache (`calendar_id`, `data_uri`, `data_summary`, `data_location`, `data_start`, `data_end`, `data_allday`, `data_type`,
`calendardata`, `size`, `etag`) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', %d, '%s')",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId, dbesc($uid), dbesc($summary), dbesc($row["location"]), dbesc($row["start"]), dbesc($row["finish"]),
($allday ? 1 : 0), dbesc(($row["type"] == "birthday" ? "birthday" : "")), dbesc($data), strlen($data), md5($data));
}
}
/**
* @param array $row
* @param array $calendar
* @param string $base_path
* @return array
*/
private function jqcal2wdcal($row, $calendar, $base_path)
{
if ($row["adjust"]) {
$start = datetime_convert('UTC', date_default_timezone_get(), $row["start"]);
$finish = datetime_convert('UTC', date_default_timezone_get(), $row["finish"]);
} else {
$start = $row["start"];
$finish = $row["finish"];
}
$allday = (strpos($start, "00:00:00") !== false && strpos($finish, "00:00:00") !== false);
$summary = (($row["summary"]) ? $row["summary"] : substr(preg_replace("/\[[^\]]*\]/", "", $row["desc"]), 0, 100));
return array(
"jq_id" => $row["id"],
"ev_id" => $row["id"],
"summary" => escape_tags($summary),
"start" => wdcal_mySql2PhpTime($start),
"end" => wdcal_mySql2PhpTime($finish),
"is_allday" => ($allday ? 1 : 0),
"is_moredays" => (substr($start, 0, 10) != substr($finish, 0, 10)),
"is_recurring" => ($row["type"] == "birthday"),
"color" => "#f8f8ff",
"is_editable" => 0,
"is_editable_quick" => 0,
"location" => $row["location"],
"attendees" => '',
"has_notification" => 0,
"url_detail" => $base_path . "/events/event/" . $row["id"],
"url_edit" => "",
"special_type" => ($row["type"] == "birthday" ? "birthday" : ""),
);
}
/**
* @param int $calendarId
* @param string $date_from
* @param string $date_to
* @param string $base_path
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function listItemsByRange($calendarId, $date_from, $date_to, $base_path)
{
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
if ($calendar["namespace"] != CALDAV_NAMESPACE_PRIVATE) throw new Sabre_DAV_Exception_NotFound();
switch ($calendar["uri"]) {
case CALDAV_FRIENDICA_MINE:
$sql_where = " AND cid = 0";
break;
case CALDAV_FRIENDICA_CONTACTS:
$sql_where = " AND cid > 0";
break;
default:
throw new Sabre_DAV_Exception_NotFound();
}
if ($date_from != "") {
if (is_numeric($date_from)) $sql_where .= " AND `finish` >= '" . date("Y-m-d H:i:s", $date_from) . "'";
else $sql_where .= " AND `finish` >= '" . dbesc($date_from) . "'";
}
if ($date_to != "") {
if (is_numeric($date_to)) $sql_where .= " AND `start` <= '" . date("Y-m-d H:i:s", $date_to) . "'";
else $sql_where .= " AND `start` <= '" . dbesc($date_to) . "'";
}
$ret = array();
$r = q("SELECT * FROM `event` WHERE `uid` = %d " . $sql_where . " ORDER BY `start`", IntVal($calendar["namespace_id"]));
$a = get_app();
foreach ($r as $row) {
$r = $this->jqcal2wdcal($row, $calendar, $a->get_baseurl());
$r["calendar_id"] = $calendar["id"];
$ret[] = $r;
}
return $ret;
}
/**
* 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)
{
$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 (!in_array($cal["uri"], $GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"])) continue;
$dat = array(
"id" => $cal["id"],
"uri" => $cal["uri"],
"principaluri" => $principalUri,
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag'] ? $cal['ctag'] : '0',
"calendar_class" => "Sabre_CalDAV_Calendar_Virtual",
);
foreach ($this->propertyMap as $key=> $field) $dat[$key] = $cal[$field];
$ret[] = $dat;
}
return $ret;
}
/**
* @param int $calendar_id
* @param int $calendarobject_id
* @return string
*/
function getItemDetailRedirect($calendar_id, $calendarobject_id)
{
$a = get_app();
$item = q("SELECT `id` FROM `item` WHERE `event-id` = %d AND `uid` = %d AND deleted = 0", IntVal($calendarobject_id), $a->user["uid"]);
if (count($item) == 0) return "/events/";
return "/display/" . $a->user["nickname"] . "/" . IntVal($item[0]["id"]);
}
}

View file

@ -0,0 +1,336 @@
<?php
class Sabre_CardDAV_Backend_FriendicaCommunity extends Sabre_CardDAV_Backend_Abstract
{
/**
* @var null|Sabre_CardDAV_Backend_FriendicaCommunity
*/
private static $instance = null;
/**
* @static
* @return Sabre_CardDAV_Backend_FriendicaCommunity
*/
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new Sabre_CardDAV_Backend_FriendicaCommunity();
}
return self::$instance;
}
/**
* Sets up the object
*/
public function __construct()
{
}
/**
* Returns the list of addressbooks for a specific user.
*
* @param string $principalUri
* @return array
*/
public function getAddressBooksForUser($principalUri)
{
$uid = dav_compat_principal2uid($principalUri);
$addressBooks = array();
$books = q("SELECT ctag FROM %s%saddressbooks_community WHERE uid = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid));
if (count($books) == 0) {
q("INSERT INTO %s%saddressbooks_community (uid, ctag) VALUES (%d, 1)", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid));
$ctag = 1;
} else {
$ctag = $books[0]["ctag"];
}
$addressBooks[] = array(
'id' => CARDDAV_NAMESPACE_COMMUNITYCONTACTS . "-" . $uid,
'uri' => "friendica",
'principaluri' => $principalUri,
'{DAV:}displayname' => t("Friendica-Contacts"),
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => t("Your Friendica-Contacts"),
'{http://calendarserver.org/ns/}getctag' => $ctag,
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' =>
new Sabre_CardDAV_Property_SupportedAddressData(),
);
return $addressBooks;
}
/**
* Updates an addressbook's properties
*
* See Sabre_DAV_IProperties for a description of the mutations array, as
* well as the return value.
*
* @param string $addressBookId
* @param array $mutations
* @throws Sabre_DAV_Exception_Forbidden
* @see Sabre_DAV_IProperties::updateProperties
* @return bool|array
*/
public function updateAddressBook($addressBookId, array $mutations)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Creates a new address book
*
* @param string $principalUri
* @param string $url Just the 'basename' of the url.
* @param array $properties
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function createAddressBook($principalUri, $url, array $properties)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Deletes an entire addressbook and all its contents
*
* @param int $addressBookId
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function deleteAddressBook($addressBookId)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* @param array $contact
* @return array
*/
private function dav_contactarr2vcardsource($contact)
{
$name = explode(" ", $contact["name"]);
$first_name = $last_name = "";
$middle_name = array();
$num = count($name);
for ($i = 0; $i < $num && $first_name == ""; $i++) if ($name[$i] != "") {
$first_name = $name[$i];
unset($name[$i]);
}
for ($i = $num - 1; $i >= 0 && $last_name == ""; $i--) if (isset($name[$i]) && $name[$i] != "") {
$last_name = $name[$i];
unset($name[$i]);
}
foreach ($name as $n) if ($n != "") $middle_name[] = $n;
$vcarddata = new vcard_source_data($first_name, implode(" ", $middle_name), $last_name);
$vcarddata->homepages[] = new vcard_source_data_homepage("pref", $contact["url"]);
$vcarddata->last_update = ($contact["last-update"] > 0 ? $contact["last-update"] : $contact["created"]);
$photo = q("SELECT * FROM photo WHERE `contact-id` = %d ORDER BY scale DESC", $contact["id"]); //prefer size 80x80
if ($photo && count($photo) > 0) {
$photodata = new vcard_source_data_photo();
$photodata->width = $photo[0]["width"];
$photodata->height = $photo[0]["height"];
$photodata->type = "JPEG";
$photodata->binarydata = $photo[0]["data"];
$vcarddata->photo = $photodata;
}
switch ($contact["network"]) {
case "face":
$vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("facebook", $contact["notify"], "http://www.facebook.com/" . $contact["notify"]);
break;
case "dfrn":
$vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("dfrn", $contact["nick"], $contact["url"]);
break;
case "twitter":
$vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("twitter", $contact["nick"], "http://twitter.com/" . $contact["nick"]); // @TODO Stimmt das?
break;
}
$vcard = vcard_source_compile($vcarddata);
return array(
"id" => $contact["id"],
"carddata" => $vcard,
"uri" => $contact["id"] . ".vcf",
"lastmodified" => wdcal_mySql2PhpTime($vcarddata->last_update),
"etag" => md5($vcard),
"size" => strlen($vcard),
);
}
/**
* @param int $uid
* @param array|int[] $exclude_ids
* @return array
*/
private function dav_getCommunityContactsVCards($uid = 0, $exclude_ids = array())
{
$notin = (count($exclude_ids) > 0 ? " AND id NOT IN (" . implode(", ", $exclude_ids) . ") " : "");
$uid = IntVal($uid);
$contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 $notin ORDER BY `name` ASC", $uid);
$retdata = array();
foreach ($contacts as $contact) {
$x = $this->dav_contactarr2vcardsource($contact);
$x["contact"] = $contact["id"];
$retdata[] = $x;
}
return $retdata;
}
/**
* Returns all cards for a specific addressbook id.
*
* This method should return the following properties for each card:
* * carddata - raw vcard data
* * uri - Some unique url
* * lastmodified - A unix timestamp
*
* It's recommended to also return the following properties:
* * etag - A unique etag. This must change every time the card changes.
* * size - The size of the card in bytes.
*
* If these last two properties are provided, less time will be spent
* calculating them. If they are specified, you can also ommit carddata.
* This may speed up certain requests, especially with large cards.
*
* @param string $addressbookId
* @return array
*/
public function getCards($addressbookId)
{
$add = explode("-", $addressbookId);
$indb = q('SELECT id, carddata, uri, lastmodified, etag, size, contact, manually_deleted FROM %s%scards WHERE namespace = %d AND namespace_id = %d',
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($add[0]), IntVal($add[1])
);
$found_contacts = array();
$contacts = array();
foreach ($indb as $x) {
if ($x["manually_deleted"] == 0) $contacts[] = $x;
$found_contacts[] = IntVal($x["contact"]);
}
$new_found = $this->dav_getCommunityContactsVCards($add[1], $found_contacts);
foreach ($new_found as $new) {
q("INSERT INTO %s%scards (namespace, namespace_id, contact, carddata, uri, lastmodified, manually_edited, manually_deleted, etag, size)
VALUES (%d, %d, %d, '%s', '%s', %d, 0, 0, '%s', %d)", CALDAV_SQL_DB, CALDAV_SQL_PREFIX,
IntVal($add[0]), IntVal($add[1]), IntVal($new["contact"]), dbesc($new["carddata"]), dbesc($new["uri"]), time(), md5($new["carddata"]), strlen($new["carddata"])
);
}
return array_merge($contacts, $new_found);
}
/**
* Returns a specfic card.
*
* The same set of properties must be returned as with getCards. The only
* exception is that 'carddata' is absolutely required.
*
* @param mixed $addressBookId
* @param string $cardUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getCard($addressBookId, $cardUri)
{
$x = explode("-", $addressBookId);
$x = q("SELECT id, carddata, uri, lastmodified, etag, size FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri));
if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound();
return $x[0];
}
/**
* Creates a new card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* 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.
*
* It is possible to return an ETag from this method. This ETag is for the
* newly created resource, and must be enclosed with double quotes (that
* is, the string itself must contain the double quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param string $addressBookId
* @param string $cardUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return string
*/
public function createCard($addressBookId, $cardUri, $cardData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Updates a card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* 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.
*
* It is possible to return an ETag from this method. This ETag should
* match that of the updated resource, and must be enclosed with double
* quotes (that is: the string itself must contain the actual quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param string $addressBookId
* @param string $cardUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return string|null
*/
public function updateCard($addressBookId, $cardUri, $cardData)
{
$x = explode("-", $addressBookId);
$etag = md5($cardData);
q("UPDATE %s%scards SET carddata = '%s', lastmodified = %d, etag = '%s', size = %d, manually_edited = 1 WHERE uri = '%s' AND namespace = %d AND namespace_id =%d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), time(), $etag, strlen($cardData), dbesc($cardUri), IntVal($x[10]), IntVal($x[1])
);
q('UPDATE %s%saddressbooks_community SET ctag = ctag + 1 WHERE uid = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1]));
return '"' . $etag . '"';
}
/**
* Deletes a card
*
* @param string $addressBookId
* @param string $cardUri
* @throws Sabre_DAV_Exception_Forbidden
* @return bool
*/
public function deleteCard($addressBookId, $cardUri)
{
$x = explode("-", $addressBookId);
q("UPDATE %s%scards SET manually_deleted = 1 WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri));
q('UPDATE %s%saddressbooks_community SET ctag = ctag + 1 WHERE uid = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1]));
return true;
}
}

View file

@ -1,15 +1,38 @@
<?php
class Sabre_DAV_Auth_Backend_Friendica extends Sabre_DAV_Auth_Backend_AbstractBasic {
class Sabre_DAV_Auth_Backend_Std extends Sabre_DAV_Auth_Backend_AbstractBasic {
public function __construct() {
}
/**
* @var Sabre_DAV_Auth_Backend_Std|null
*/
private static $intstance = null;
/**
* @static
* @return Sabre_DAV_Auth_Backend_Std
*/
public static function &getInstance() {
if (is_null(self::$intstance)) {
self::$intstance = new Sabre_DAV_Auth_Backend_Std();
}
return self::$intstance;
}
/**
* @return array
*/
public function getUsers() {
return array($this->currentUser);
}
/**
* @return null|string
*/
public function getCurrentUser() {
return $this->currentUser;
}
@ -27,6 +50,12 @@ class Sabre_DAV_Auth_Backend_Friendica extends Sabre_DAV_Auth_Backend_AbstractBa
*/
public function authenticate(Sabre_DAV_Server $server, $realm) {
$a = get_app();
if (isset($a->user["uid"])) {
$this->currentUser = strtolower($a->user["nickname"]);
return true;
}
$auth = new Sabre_HTTP_BasicAuth();
$auth->setHTTPRequest($server->httpRequest);
$auth->setHTTPResponse($server->httpResponse);
@ -47,12 +76,18 @@ class Sabre_DAV_Auth_Backend_Friendica extends Sabre_DAV_Auth_Backend_AbstractBa
}
/**
* @param string $username
* @param string $password
* @return bool
*/
protected function validateUserPass($username, $password) {
$user = array(
'uri' => "/" . 'principals/users/' . strtolower($username),
$encrypted = hash('whirlpool',trim($password));
$r = q("SELECT COUNT(*) anz FROM `user` WHERE `nickname` = '%s' AND `password` = '%s' AND `blocked` = 0 AND `account_expired` = 0 AND `verified` = 1 LIMIT 1",
dbesc(trim($username)),
dbesc($encrypted)
);
return $user;
return ($r[0]["anz"] == 1);
}
}

View file

@ -1,7 +1,7 @@
<?php
class Sabre_DAVACL_PrincipalBackend_Friendica implements Sabre_DAVACL_IPrincipalBackend
class Sabre_DAVACL_PrincipalBackend_Std implements Sabre_DAVACL_IPrincipalBackend
{
/**
@ -24,6 +24,23 @@ class Sabre_DAVACL_PrincipalBackend_Friendica implements Sabre_DAVACL_IPrincipal
}
/**
* @var Sabre_DAVACL_IPrincipalBackend|null
*/
private static $intstance = null;
/**
* @static
* @return Sabre_DAVACL_IPrincipalBackend
*/
public static function &getInstance() {
if (is_null(self::$intstance)) {
$authBackend = Sabre_DAV_Auth_Backend_Std::getInstance();
self::$intstance = new Sabre_DAVACL_PrincipalBackend_Std($authBackend);
}
return self::$intstance;
}
/**
* Returns a list of principals based on a prefix.
*

375
dav/jqueryui/jquery-ui-1.8.21.custom.css vendored Normal file
View file

@ -0,0 +1,375 @@
/*!
* jQuery UI CSS Framework 1.8.21
*
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
.ui-helper-clearfix:after { clear: both; }
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*!
* jQuery UI CSS Framework 1.8.21
*
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
.ui-widget-content a { color: #333333; }
.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*!
* jQuery UI Dialog 1.8.21
*
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*!
* jQuery UI Datepicker 1.8.21
*
* Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}

File diff suppressed because one or more lines are too long

23
dav/jqueryui/jquery.ui.datepicker-de.js vendored Normal file
View file

@ -0,0 +1,23 @@
/* German initialisation for the jQuery UI date picker plugin. */
/* Written by Milian Wolff (mail@milianw.de). */
jQuery(function($){
$.datepicker.regional['de'] = {
closeText: 'schließen',
prevText: '&#x3c;zurück',
nextText: 'Vor&#x3e;',
currentText: 'heute',
monthNames: ['Januar','Februar','März','April','Mai','Juni',
'Juli','August','September','Oktober','November','Dezember'],
monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun',
'Jul','Aug','Sep','Okt','Nov','Dez'],
dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
weekHeader: 'Wo',
dateFormat: 'dd.mm.yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''};
$.datepicker.setDefaults($.datepicker.regional['de']);
});

View file

@ -8,8 +8,8 @@ function wdcal_addRequiredHeaders()
{
$a = get_app();
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.20.custom.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.20.custom.min.js"></script>' . "\r\n";
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.21.custom.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.21.custom.min.js"></script>' . "\r\n";
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/wdcal.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal.js"></script>' . "\r\n";
@ -20,6 +20,7 @@ function wdcal_addRequiredHeaders()
switch (get_config("system", "language")) {
case "de":
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal/js/wdCalendar_lang_DE.js"></script>' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery.ui.datepicker-de.js"></script>' . "\r\n";
break;
default:
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal/js/wdCalendar_lang_EN.js"></script>' . "\r\n";
@ -34,10 +35,12 @@ function wdcal_addRequiredHeaders()
*/
function wdcal_addRequiredHeadersEdit()
{
$a = get_app();
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.20.custom.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.20.custom.min.js"></script>' . "\r\n";
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.21.custom.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery-ui-1.8.21.custom.min.js"></script>' . "\r\n";
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/colorpicker/colorPicker.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/colorpicker/jquery.colorPicker.min.js"></script>' . "\r\n";
@ -48,12 +51,56 @@ function wdcal_addRequiredHeadersEdit()
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' . $a->get_baseurl() . '/addon/dav/wdcal.css' . '" media="all" />' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal.js"></script>' . "\r\n";
switch ($localization->getLanguageCode()) {
case "de":
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal/js/wdCalendar_lang_DE.js"></script>' . "\r\n";
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/jqueryui/jquery.ui.datepicker-de.js"></script>' . "\r\n";
break;
default:
$a->page['htmlhead'] .= '<script type="text/javascript" src="' . $a->get_baseurl() . '/addon/dav/common/wdcal/js/wdCalendar_lang_EN.js"></script>' . "\r\n";
}
}
/**
* @param array|int[] $calendars
*/
function wdcal_print_user_ics($calendars = array())
{
$add = "";
if (count($calendars) > 0) {
$c = array();
foreach ($calendars as $i) $c[] = IntVal($i);
$add = " AND `id` IN (" . implode(", ", $c) . ")";
}
$a = get_app();
header("Content-type: text/plain");
$str = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Friendica//DAV-Plugin//EN\r\n";
$cals = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d %s", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $a->user["uid"], $add);
if (count($cals) > 0) {
$ids = array();
foreach ($cals as $c) $ids[] = IntVal($c["id"]);
$objs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` IN (" . implode(", ", $ids) . ") ORDER BY `firstOccurence`", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
foreach ($objs as $obj) {
preg_match("/BEGIN:VEVENT(.*)END:VEVENT/siu", $obj["calendardata"], $matches);
$str2 = preg_replace("/([^\\r])\\n/siu", "\\1\r\n", $matches[0]);
$str2 = preg_replace("/MAILTO:.*[^:a-z0-9_\+äöüß\\n\\n@-]+.*(:|\\r\\n[^ ])/siU", "\\1", $str2);
$str .= $str2 . "\r\n";
}
}
$str .= "END:VCALENDAR\r\n";
echo $str;
killme();
}
/**
* @param array|DBClass_friendica_calendars[] $calendars
* @param array $calendar_preselected
* @param array|Sabre_CalDAV_Calendar[] $calendars
* @param array|int[] $calendars_selected
* @param string $data_feed_url
* @param string $view
* @param int $theme
@ -64,14 +111,17 @@ function wdcal_addRequiredHeadersEdit()
* @param bool $show_nav
* @return string
*/
function wdcal_printCalendar($calendars, $calendar_preselected, $data_feed_url, $view = "week", $theme = 0, $height_diff = 175, $readonly = false, $curr_day = "", $add_params = array(), $show_nav = true)
function wdcal_printCalendar($calendars, $calendars_selected, $data_feed_url, $view = "week", $theme = 0, $height_diff = 175, $readonly = false, $curr_day = "", $add_params = array(), $show_nav = true)
{
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
$cals_avail = array();
foreach ($calendars as $c) $cals_avail[] = array("ns" => $c->namespace, "id" => $c->namespace_id, "displayname" => $c->displayname);
if (count($calendars_selected) == 0) foreach ($calendars as $c) {
$prop = $c->getProperties(array("id"));
$calendars_selected[] = $prop["id"];
}
$opts = array(
"view" => $view,
"theme" => $theme,
@ -96,12 +146,13 @@ function wdcal_printCalendar($calendars, $calendar_preselected, $data_feed_url,
<div id="animexxcalendar" class="animexxcalendar">
<div class="calselect"><strong>Available Calendars:</strong>';
foreach ($cals_avail as $cal) {
$x .= '<label style="margin-left: 10px; margin-right: 10px;"><input type="checkbox" name="cals[]" value="' . $cal["ns"] . '-' . $cal["id"] . '"';
foreach ($calendars as $cal) {
$cal_id = $cal->getProperties(array("id", DAV_DISPLAYNAME));
$x .= '<label style="margin-left: 10px; margin-right: 10px;"><input type="checkbox" name="cals[]" value="' . $cal_id["id"] . '"';
$found = false;
foreach ($calendar_preselected as $pre) if ($pre["ns"] == $cal["ns"] && $pre["id"] == $cal["id"]) $found = true;
foreach ($calendars_selected as $pre) if ($pre["id"] == $cal_id["id"]) $found = true;
if ($found) $x .= ' checked';
$x .= '> ' . escape_tags($cal["displayname"]) . '</label> ';
$x .= '> ' . escape_tags($cal_id[DAV_DISPLAYNAME]) . '</label> ';
}
$x .= '</div>
@ -179,166 +230,57 @@ function wdcal_printCalendar($calendars, $calendar_preselected, $data_feed_url,
/**
* @param string $uri
* @param int $calendar_id
* @param int $calendarobject_id
* @param string $recurr_uri
* @return string
*/
function wdcal_getDetailPage($uri, $recurr_uri)
function wdcal_getDetailPage($calendar_id, $calendarobject_id, $recurr_uri)
{
$a = get_app();
try {
$details = null;
$cals = dav_getMyCals($a->user["uid"]);
foreach ($cals as $c) {
$cs = wdcal_calendar_factory($a->user["uid"], $c->namespace, $c->namespace_id);
$p = $cs->getPermissionsItem($a->user["uid"], $uri, $recurr_uri);
if ($p["read"]) try {
$redirect = $cs->getItemDetailRedirect($uri);
if ($redirect !== null) goaway($redirect);
$details = $cs->getItemByUri($uri);
$server = dav_create_server(true, true, false);
$cal = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_READ);
$obj = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($calendarobject_id);
dav_get_current_user_calendarobject($server, $cal, $obj["uri"], DAV_ACL_READ); // Check permissions
$calbackend = wdcal_calendar_factory_by_id($calendar_id);
$redirect = $calbackend->getItemDetailRedirect($calendar_id, $calendarobject_id);
if ($redirect !== null) goaway($a->get_baseurl() . $redirect);
$details = $obj;
} catch (Exception $e) {
notification(t("Error") . ": " . $e);
info(t("Error") . ": " . $e);
goaway($a->get_baseurl() . "/dav/wdcal/");
}
}
return $uri . " / " . $recurr_uri . "<br>" . print_r($details, true);
return print_r($details, true);
}
/**
* @param string $uri
* @param int $calendar_id
* @param int $uri
* @param string $recurr_uri
* @return string
*/
function wdcal_getEditPage($uri, $recurr_uri = "")
function wdcal_getEditPage($calendar_id, $uri, $recurr_uri = "")
{
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
if ($uri != "" && $uri != "new") {
$o = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], dbesc($uri), dbesc($recurr_uri)
);
if (count($o) != 1) return t('Not found');
$event = $o[0];
return wdcal_getEditPage_str($localization, $a->get_baseurl(), $a->user["uid"], $calendar_id, $uri, $recurr_uri);
}
$calendarSource = wdcal_calendar_factory($a->user["uid"], $event["namespace"], $event["namespace_id"]);
function wdcal_getNewPage()
{
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
$permissions = $calendarSource->getPermissionsItem($a->user["uid"], $uri, $recurr_uri, $event);
if (!$permissions["write"]) return t('No access');
$n = q("SELECT * FROM %s%snotifications WHERE `uid` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], dbesc($uri), dbesc($recurr_uri)
);
if (count($n) > 0) {
$notification_type = $n[0]["rel_type"];
$notification_value = -1 * $n[0]["rel_value"];
$notification = true;
} else {
if ($event["IsAllDayEvent"]) {
$notification_type = "hour";
$notification_value = 24;
} else {
$notification_type = "minute";
$notification_value = 60;
}
$notification = false;
}
} elseif (isset($_REQUEST["start"]) && $_REQUEST["start"] > 0) {
$event = array(
"id" => 0,
"Subject" => $_REQUEST["title"],
"Location" => "",
"Description" => "",
"StartTime" => wdcal_php2MySqlTime($_REQUEST["start"]),
"EndTime" => wdcal_php2MySqlTime($_REQUEST["end"]),
"IsAllDayEvent" => $_REQUEST["isallday"],
"Color" => null,
"RecurringRule" => null,
);
if ($_REQUEST["isallday"]) {
$notification_type = "hour";
$notification_value = 24;
} else {
$notification_type = "hour";
$notification_value = 1;
}
$notification = true;
} else {
$event = array(
"id" => 0,
"Subject" => "",
"Location" => "",
"Description" => "",
"StartTime" => date("Y-m-d H:i:s"),
"EndTime" => date("Y-m-d H:i:s", time() + 3600),
"IsAllDayEvent" => "0",
"Color" => "#5858ff",
"RecurringRule" => null,
);
$notification_type = "hour";
$notification_value = 1;
$notification = true;
}
$postto = $a->get_baseurl() . "/dav/wdcal/" . ($uri == "new" ? "new/" : $uri . "/edit/");
$out = "<a href='" . $a->get_baseurl() . "/dav/wdcal/'>" . t("Go back to the calendar") . "</a><br><br>";
$out .= "<form method='POST' action='$postto'><input type='hidden' name='form_security_token' value='" . get_form_security_token('caledit') . "'>\n";
$out .= "<label for='cal_subject'>Subject:</label>
<input name='color' id='cal_color' value='" . (strlen($event["Color"]) != 7 ? "#5858ff" : escape_tags($event["Color"])) . "'>
<input name='subject' id='cal_subject' value='" . escape_tags($event["Subject"]) . "'><br>\n";
$out .= "<label for='cal_allday'>Is All-Day event:</label><input type='checkbox' name='allday' id='cal_allday' " . ($event["IsAllDayEvent"] ? "checked" : "") . "><br>\n";
$out .= "<label for='cal_startdate'>" . t("Starts") . ":</label>";
$out .= "<input name='start_date' value='" . $localization->dateformat_datepicker_php(wdcal_mySql2PhpTime($event["StartTime"])) . "' id='cal_start_date'>";
$out .= "<input name='start_time' value='" . substr($event["StartTime"], 11, 5) . "' id='cal_start_time'>";
$out .= "<br>\n";
$out .= "<label for='cal_enddate'>" . t("Ends") . ":</label>";
$out .= "<input name='end_date' value='" . $localization->dateformat_datepicker_php(wdcal_mySql2PhpTime($event["EndTime"])) . "' id='cal_end_date'>";
$out .= "<input name='end_time' value='" . substr($event["EndTime"], 11, 5) . "' id='cal_end_time'>";
$out .= "<br>\n";
$out .= "<label for='cal_location'>" . t("Location") . ":</label><input name='location' id='cal_location' value='" . escape_tags($event["Location"]) . "'><br>\n";
$out .= "<label for='event-desc-textarea'>" . t("Description") . ":</label> <textarea id='event-desc-textarea' name='wdcal_desc' style='vertical-align: top; width: 400px; height: 100px;'>" . escape_tags($event["Description"]) . "</textarea>";
$out .= "<br style='clear: both;'>";
$out .= "<label for='notification'>" . t('Notification') . ":</label>";
$out .= '<input type="checkbox" name="notification" id="notification" ';
if ($notification) $out .= "checked";
$out .= '> ';
$out .= '<span id="notification_detail" style="display: none;">
<input name="notification_value" value="' . $notification_value . '" size="3">
<select name="notification_type" size="1">
<option value="minute" ';
if ($notification_type == "minute") $out .= "selected";
$out .= '> ' . t('Minutes') . '</option>
<option value="hour" ';
if ($notification_type == "hour") $out .= "selected";
$out .= '> ' . t('Hours') . '</option>
<option value="day" ';
if ($notification_type == "day") echo "selected";
$out .= '> ' . t('Days') . '</option>
</select> ' . t('before') . '
</span><br><br>';
$out .= "<script>\$(function() {
wdcal_edit_init('" . $localization->dateformat_datepicker_js() . "');
});</script>";
$out .= "<input type='submit' name='save' value='Save'></form>";
return $out;
return wdcal_getEditPage_str($localization, $a->get_baseurl(), $a->user["uid"], 0, 0);
}

View file

@ -24,12 +24,6 @@ function dav_module()
function dav_include_files()
{
require_once (__DIR__ . "/common/dbclasses/dbclass_animexx.class.php");
require_once (__DIR__ . "/common/dbclasses/dbclass.friendica.calendars.class.php");
require_once (__DIR__ . "/common/dbclasses/dbclass.friendica.jqcalendar.class.php");
require_once (__DIR__ . "/common/dbclasses/dbclass.friendica.notifications.class.php");
require_once (__DIR__ . "/common/dbclasses/dbclass.friendica.calendarobjects.class.php");
/*
require_once (__DIR__ . "/SabreDAV/lib/Sabre.includes.php");
require_once (__DIR__ . "/SabreDAV/lib/Sabre/VObject/includes.php");
@ -38,31 +32,27 @@ function dav_include_files()
*/
require_once (__DIR__ . "/SabreDAV/lib/Sabre/autoload.php");
$tz_before = date_default_timezone_get();
require_once (__DIR__ . "/iCalcreator/iCalcreator.class.php");
date_default_timezone_set($tz_before);
require_once (__DIR__ . "/common/calendar.fnk.php");
require_once (__DIR__ . "/common/calendar_rendering.fnk.php");
require_once (__DIR__ . "/common/dav_caldav_backend_common.inc.php");
require_once (__DIR__ . "/common/dav_caldav_backend.inc.php");
require_once (__DIR__ . "/common/dav_caldav_backend_private.inc.php");
require_once (__DIR__ . "/common/dav_caldav_backend_virtual.inc.php");
require_once (__DIR__ . "/common/dav_caldav_root.inc.php");
require_once (__DIR__ . "/common/dav_user_calendars.inc.php");
require_once (__DIR__ . "/common/dav_carddav_root.inc.php");
require_once (__DIR__ . "/common/dav_carddav_backend_std.inc.php");
require_once (__DIR__ . "/common/dav_user_addressbooks.inc.php");
require_once (__DIR__ . "/common/virtual_cal_source_backend.inc.php");
require_once (__DIR__ . "/common/dav_caldav_calendar_virtual.inc.php");
require_once (__DIR__ . "/common/wdcal_configuration.php");
require_once (__DIR__ . "/common/wdcal_cal_source.inc.php");
require_once (__DIR__ . "/common/wdcal_cal_source_private.inc.php");
require_once (__DIR__ . "/common/wdcal_backend.inc.php");
require_once (__DIR__ . "/dav_friendica_principal.inc.php");
require_once (__DIR__ . "/dav_friendica_auth.inc.php");
require_once (__DIR__ . "/dav_carddav_backend_friendica_community.inc.php");
require_once (__DIR__ . "/dav_caldav_backend_friendica.inc.php");
require_once (__DIR__ . "/virtual_cal_source_friendica.inc.php");
require_once (__DIR__ . "/wdcal_cal_source_friendicaevents.inc.php");
require_once (__DIR__ . "/dav_carddav_backend_virtual_friendica.inc.php");
require_once (__DIR__ . "/dav_caldav_backend_virtual_friendica.inc.php");
require_once (__DIR__ . "/FriendicaACLPlugin.inc.php");
require_once (__DIR__ . "/common/wdcal_edit.inc.php");
require_once (__DIR__ . "/calendar.friendica.fnk.php");
require_once (__DIR__ . "/layout.fnk.php");
}
@ -81,7 +71,7 @@ function dav_init(&$a)
dav_include_files();
if (false) {
if (true) {
dbg(true);
error_reporting(E_ALL);
ini_set("display_errors", 1);
@ -102,58 +92,24 @@ function dav_init(&$a)
}
return;
}
if ($a->argc >= 2 && $a->argv[1] == "getExceptionDates") {
echo wdcal_getEditPage_exception_selector();
killme();
}
if ($a->argc >= 2 && $a->argv[1] == "settings") {
return;
}
$authBackend = new Sabre_DAV_Auth_Backend_Friendica();
$principalBackend = new Sabre_DAVACL_PrincipalBackend_Friendica($authBackend);
$caldavBackend_std = new Sabre_CalDAV_Backend_Std();
$caldavBackend_community = new Sabre_CalDAV_Backend_Friendica();
$carddavBackend_std = new Sabre_CardDAV_Backend_Std();
$carddavBackend_community = new Sabre_CardDAV_Backend_FriendicaCommunity();
if (isset($_SERVER["PHP_AUTH_USER"])) {
$tree = new Sabre_DAV_SimpleCollection('root', array(
new Sabre_DAV_SimpleCollection('principals', array(
new Sabre_CalDAV_Principal_Collection($principalBackend, "principals/users"),
)),
new Sabre_CalDAV_AnimexxCalendarRootNode($principalBackend, array(
$caldavBackend_std,
$caldavBackend_community,
)),
new Sabre_CardDAV_AddressBookRootFriendica($principalBackend, array(
$carddavBackend_std,
$carddavBackend_community,
)),
));
} else {
$tree = new Sabre_DAV_SimpleCollection('root', array());
if (isset($_REQUEST["test"])) {
renderAllCalDavEntries();
}
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($tree);
$url = parse_url($a->get_baseurl());
$server->setBaseUri(CALDAV_URL_PREFIX);
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend, 'SabreDAV');
$server->addPlugin($authPlugin);
$aclPlugin = new Sabre_DAVACL_Plugin_Friendica();
$aclPlugin->defaultUsernamePath = "principals/users";
$server->addPlugin($aclPlugin);
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
$carddavPlugin = new Sabre_CardDAV_Plugin();
$server->addPlugin($carddavPlugin);
$server = dav_create_server();
$browser = new Sabre_DAV_Browser_Plugin();
$server->addPlugin($browser);
$server->exec();
killme();
@ -174,36 +130,44 @@ function dav_content()
if ($a->argv[1] == "settings") {
return wdcal_getSettingsPage($a);
} elseif ($a->argv[1] == "wdcal") {
if ($a->argc >= 3 && strlen($a->argv[2]) > 0) {
$uri = $a->argv[2];
if ($uri == "new") {
if (isset($a->argv[2]) && strlen($a->argv[2]) > 0) {
if ($a->argv[2] == "ics") {
wdcal_print_user_ics();
} elseif ($a->argv[2] == "new") {
$o = "";
if (isset($_REQUEST["save"])) {
check_form_security_token_redirectOnErr($a->get_baseurl() . "/dav/wdcal/", "caledit");
$o .= wdcal_postEditPage("new", "", $a->user["uid"], $a->timezone, $a->get_baseurl() . "/dav/wdcal/");
$ret = wdcal_postEditPage("new", "", $a->user["uid"], $a->timezone, $a->get_baseurl() . "/dav/wdcal/");
if ($ret["ok"]) notice($ret["msg"]);
else info($ret["msg"]);
}
$o .= wdcal_getEditPage("new");
$o .= wdcal_getNewPage();
return $o;
} else {
$calendar_id = IntVal($a->argv[2]);
if (isset($a->argv[3]) && $a->argv[3] > 0) {
$recurr_uri = ""; // @TODO
if (isset($a->argv[3]) && $a->argv[3] == "edit") {
if (isset($a->argv[4]) && $a->argv[4] == "edit") {
$o = "";
if (isset($_REQUEST["save"])) {
check_form_security_token_redirectOnErr($a->get_baseurl() . "/dav/wdcal/", "caledit");
$o .= wdcal_postEditPage($uri, $recurr_uri, $a->user["uid"], $a->timezone, $a->get_baseurl() . "/dav/wdcal/");
$ret = wdcal_postEditPage($a->argv[3], $recurr_uri, $a->user["uid"], $a->timezone, $a->get_baseurl() . "/dav/wdcal/");
if ($ret["ok"]) notice($ret["msg"]);
else info($ret["msg"]);
}
$o .= wdcal_getEditPage($uri, $recurr_uri);
$o .= wdcal_getEditPage($calendar_id, $a->argv[3], $recurr_uri);
return $o;
} else {
return wdcal_getDetailPage($uri, $recurr_uri);
return wdcal_getDetailPage($calendar_id, $a->argv[3], $recurr_uri);
}
} else {
// @TODO Edit Calendar
}
}
} else {
$cals = dav_getMyCals($a->user["uid"]);
$cals_show = array();
foreach ($cals as $e) $cals_show[] = array("ns" => $e->namespace, "id" => $e->namespace_id, "displayname" => $e->displayname);
$x = wdcal_printCalendar($cals, $cals_show, $a->get_baseurl() . "/dav/wdcal/feed/", "week", 0, 200);
$server = dav_create_server(true, true, false);
$cals = dav_get_current_user_calendars($server, DAV_ACL_READ);
$x = wdcal_printCalendar($cals, array(), $a->get_baseurl() . "/dav/wdcal/feed/", "week", 0, 200);
}
}
return $x;
@ -218,8 +182,8 @@ function dav_event_created_hook(&$a, &$b)
{
dav_include_files();
// @TODO Updating the cache instead of completely invalidating and rebuilding it
FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS);
FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE);
Sabre_CalDAV_Backend_Friendica::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS);
Sabre_CalDAV_Backend_Friendica::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE);
}
/**
@ -230,8 +194,8 @@ function dav_event_updated_hook(&$a, &$b)
{
dav_include_files();
// @TODO Updating the cache instead of completely invalidating and rebuilding it
FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS);
FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE);
Sabre_CalDAV_Backend_Friendica::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS);
Sabre_CalDAV_Backend_Friendica::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE);
}
/**
@ -256,6 +220,7 @@ function dav_plugin_admin_post(&$a = null, &$o = null)
{
check_form_security_token_redirectOnErr('/admin/plugins/dav', 'dav_admin_save');
dav_include_files();
require_once(__DIR__ . "/database-init.inc.php");
if (isset($_REQUEST["install"])) {
@ -263,15 +228,20 @@ function dav_plugin_admin_post(&$a = null, &$o = null)
if (count($errs) == 0) info(t('The database tables have been installed.') . EOL);
else notice(t("An error occurred during the installation.") . EOL);
}
if (isset($_REQUEST["upgrade"])) {
$errs = dav_upgrade_tables();
if (count($errs) == 0) info(t('The database tables have been updated.') . EOL);
else notice(t("An error occurred during the update.") . EOL);
}
}
/**
* @param App $a
* @param null|object $o
* @param string $o
*/
function dav_plugin_admin(&$a, &$o)
{
dav_include_files();
require_once(__DIR__ . "/database-init.inc.php");
$dbstatus = dav_check_tables();

View file

@ -11,3 +11,33 @@ div.colorPicker-picker { display: inline-block; }
.ui-datepicker select.ui-datepicker-year { min-width: 0; width: 50px !important; }
#cal_start_time, #cal_end_time { width: 5em; margin-left: 1em; }
#cal_start_date, #cal_end_date { width: 6em;}
label.block {
background: none repeat scroll 0 0 #CCCCCC;
border: 1px solid #EEEEEC;
box-shadow: 3px 3px 5px 0 #111111;
color: #111111;
display: inline-block;
font-size: small;
margin: 0 10px 1em 0;
padding: 3px 5px;
width: 38%;
vertical-align: top;
}
label.plain {
background: none;
border: none;
box-shadow: none;
color: black;
display: inline;
margin: 0;
padding: 0;
}
.rec_exceptions { display: inline-block; }
.rec_exceptions_holder { display: inline-block; }
.rec_exceptions .remover { }
#rec_until_count { width: 50px; }
#rec_until_date { width: 100px; }