Completed
Push — master ( e92e87...f20844 )
by Paul
09:47
created

Func_CalendarBackend_Caldav::CalendarPeriod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
//-------------------------------------------------------------------------
3
// OVIDENTIA http://www.ovidentia.org
4
// Ovidentia is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation; either version 2, or (at your option)
7
// any later version.
8
//
9
// This program is distributed in the hope that it will be useful, but
10
// WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
// See the GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program; if not, write to the Free Software
16
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17
// USA.
18
//-------------------------------------------------------------------------
19
/**
20
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
21
 * @copyright Copyright (c) 2010 by CANTICO ({@link http://www.cantico.fr})
22
 */
23
24
25
require_once dirname(__FILE__) . '/functions.php';
26
require_once dirname(__FILE__) . '/caldav/caldav-client.php';
27
require_once dirname(__FILE__) . '/sessionfile.class.php';
28
29
bab_functionality::includefile('CalendarBackend');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class bab_functionality as the method includefile() does only exist in the following sub-classes of bab_functionality: Func_Archive, Func_Archive_Zip, Func_Archive_Zip_ZipArchive, Func_Archive_Zip_Zlib, Func_CalendarBackend, Func_CalendarBackend_Caldav, Func_CalendarBackend_Ovi, Func_Icons, Func_Icons_Default, Func_Ovml, Func_Ovml_Container, Func_Ovml_Container_Addon, Func_Ovml_Container_Article, Func_Ovml_Container_ArticleCategories, Func_Ovml_Container_ArticleCategory, Func_Ovml_Container_ArticleCategoryNext, Func_Ovml_Container_ArticleCategoryPrevious, Func_Ovml_Container_ArticleFiles, Func_Ovml_Container_ArticleNext, Func_Ovml_Container_ArticlePrevious, Func_Ovml_Container_ArticleTopic, Func_Ovml_Container_ArticleTopicNext, Func_Ovml_Container_ArticleTopicPrevious, Func_Ovml_Container_ArticleTopics, Func_Ovml_Container_Articles, Func_Ovml_Container_ArticlesHomePages, Func_Ovml_Container_CalendarCategories, Func_Ovml_Container_CalendarEventDomains, Func_Ovml_Container_CalendarEvents, Func_Ovml_Container_CalendarGroupEvents, Func_Ovml_Container_CalendarResourceEvents, Func_Ovml_Container_CalendarUserEvents, Func_Ovml_Container_Calendars, Func_Ovml_Container_DbDirectories, Func_Ovml_Container_DbDirectory, Func_Ovml_Container_DbDirectoryAcl, Func_Ovml_Container_DbDirectoryEntry, Func_Ovml_Container_DbDirectoryEntryFields, Func_Ovml_Container_DbDirectoryFields, Func_Ovml_Container_DbDirectoryMemberFields, Func_Ovml_Container_DbDirectoryMembers, Func_Ovml_Container_Delegation, Func_Ovml_Container_DelegationAdministrators, Func_Ovml_Container_DelegationItems, Func_Ovml_Container_DelegationManaged, Func_Ovml_Container_Delegations, Func_Ovml_Container_DelegationsCategories, Func_Ovml_Container_DelegationsCategory, Func_Ovml_Container_DelegationsManaged, Func_Ovml_Container_Faq, Func_Ovml_Container_FaqNext, Func_Ovml_Container_FaqPrevious, Func_Ovml_Container_FaqQuestion, Func_Ovml_Container_FaqQuestionNext, Func_Ovml_Container_FaqQuestionPrevious, Func_Ovml_Container_FaqQuestions, Func_Ovml_Container_FaqSubCategories, Func_Ovml_Container_FaqSubCategory, Func_Ovml_Container_Faqs, Func_Ovml_Container_File, Func_Ovml_Container_FileFields, Func_Ovml_Container_FileNext, Func_Ovml_Container_FilePrevious, Func_Ovml_Container_Files, Func_Ovml_Container_Folder, Func_Ovml_Container_FolderNext, Func_Ovml_Container_FolderPrevious, Func_Ovml_Container_Folders, Func_Ovml_Container_Forum, Func_Ovml_Container_ForumNext, Func_Ovml_Container_ForumPrevious, Func_Ovml_Container_Forums, Func_Ovml_Container_IfEqual, Func_Ovml_Container_IfGreaterThan, Func_Ovml_Container_IfGreaterThanOrEqual, Func_Ovml_Container_IfIsSet, Func_Ovml_Container_IfLessThan, Func_Ovml_Container_IfLessThanOrEqual, Func_Ovml_Container_IfNotEqual, Func_Ovml_Container_IfNotIsSet, Func_Ovml_Container_IfUserMemberOfGroups, Func_Ovml_Container_Multipages, Func_Ovml_Container_ObjectsInfo, Func_Ovml_Container_OrgPathToEntity, Func_Ovml_Container_OrgUserEntities, Func_Ovml_Container_OvmlArray, Func_Ovml_Container_OvmlArrayFields, Func_Ovml_Container_OvmlSoap, Func_Ovml_Container_ParentsArticleCategory, Func_Ovml_Container_Post, Func_Ovml_Container_PostFiles, Func_Ovml_Container_RecentArticles, Func_Ovml_Container_RecentComments, Func_Ovml_Container_RecentFaqQuestions, Func_Ovml_Container_RecentFiles, Func_Ovml_Container_RecentPosts, Func_Ovml_Container_RecentThreads, Func_Ovml_Container_SitemapEntries, Func_Ovml_Container_SitemapEntry, Func_Ovml_Container_SitemapPath, Func_Ovml_Container_Soap, Func_Ovml_Container_SubFolders, Func_Ovml_Container_Tags, Func_Ovml_Container_Thread, Func_Ovml_Container_TmProjects, Func_Ovml_Container_TmSpaces, Func_Ovml_Container_TmTaskFields, Func_Ovml_Container_TmTasks, Func_Ovml_Container_WaitingArticles, Func_Ovml_Container_WaitingComments, Func_Ovml_Container_WaitingFiles, Func_Ovml_Container_WaitingPosts, Func_Ovml_Function, Func_Ovml_Function_AOAddition, Func_Ovml_Function_AODivision, Func_Ovml_Function_AOModulus, Func_Ovml_Function_AOMultiplication, Func_Ovml_Function_AOSubtraction, Func_Ovml_Function_AddStyleSheet, Func_Ovml_Function_Addon, Func_Ovml_Function_Ajax, Func_Ovml_Function_ArticleTree, Func_Ovml_Function_FileTree, Func_Ovml_Function_Get, Func_Ovml_Function_GetCookie, Func_Ovml_Function_GetPageTitle, Func_Ovml_Function_GetPath, Func_Ovml_Function_GetSessionVar, Func_Ovml_Function_GetVar, Func_Ovml_Function_Header, Func_Ovml_Function_IfNotIsSet, Func_Ovml_Function_Include, Func_Ovml_Function_NextArticle, Func_Ovml_Function_Post, Func_Ovml_Function_PreviousArticle, Func_Ovml_Function_PreviousOrNextArticle, Func_Ovml_Function_PutArray, Func_Ovml_Function_PutSoapArray, Func_Ovml_Function_PutVar, Func_Ovml_Function_Recurse, Func_Ovml_Function_Request, Func_Ovml_Function_SetCookie, Func_Ovml_Function_SetSessionVar, Func_Ovml_Function_SitemapCustomNodeId, Func_Ovml_Function_SitemapMenu, Func_Ovml_Function_SitemapPosition, Func_Ovml_Function_SitemapUrl, Func_Ovml_Function_Translate, Func_Ovml_Function_UrlContent, Func_Ovml_Function_WebStat, Func_PortalAuthentication, Func_PortalAuthentication_AuthOvidentia, Func_SearchUi, Func_SitemapDynamicNode, Func_SitemapDynamicNode_Topic, Func_UserEditor, Func_WorkingHours, Func_WorkingHours_Ovidentia, Ovml_Container_Sitemap, bab_ArithmeticOperator, bab_Ovml_Container_Operator, bab_rgp. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
30
31
32
33
34
class Func_CalendarBackend_Caldav extends Func_CalendarBackend
35
{
36
37
38
	/**
39
	 * @var CalDAVClient
40
	 */
41
	protected $caldavClient;
42
43
44
	/**
45
	 * @var string
46
	 */
47
	private $serverUrl = null;
48
49
50
51
52
	/**
53
	 * @return string
54
	 * @static
55
	 */
56
	public function getDescription()
57
	{
58
		return caldav_translate('CalDAV server');
59
	}
60
61
62
	/**
63
	 * The backend can be used as a storage backend for the existing calendars (personal only for now)
64
	 * @return bool
65
	 */
66 1
	public function StorageBackend()
67
	{
68 1
		return $this->getStorageBackend();
69
	}
70
71
72
	/**
73
	 * Register myself as a functionality.
74
	 * @static
75
	 */
76
	public function register()
77
	{
78
		require_once $GLOBALS['babInstallPath'].'utilit/functionalityincl.php';
79
		$functionalities = new bab_functionalities();
80
		$functionalities->registerClass(__CLASS__, __FILE__);
81
	}
82
83
84
85
86 7
	public function includeEventCalendar()
87
	{
88 7
		require_once dirname(__FILE__).'/eventcalendar.class.php';
89 7
	}
90
91
92
93
94
	/**
95
	 *
96
	 * @param	int	$userId		owner of calendar
97
	 *
98
	 * @return caldav_PersonalCalendar
99
	 */
100 7
	public function PersonalCalendar($userId)
101
	{
102 7
		$this->includeEventCalendar();
103 7
		$calendar = new caldav_PersonalCalendar($this);
104 7
		$calendar->setIdUser($userId);
105 7
		return $calendar;
106
	}
107
108
	
109
	
110
	public function ResourceCalendar($id, Array $configuration)
111
	{
112
		$this->includeEventCalendar();
113
		$calendar = new caldav_ResourceCalendar($this);
114
		$calendar->setUid($id);
115
		$calendar->setConfiguration($configuration);
116
		return $calendar;
117
	}
118
	
119
120
121
122 7
	public function includeCalendarPeriod()
123
	{
124 7
		require_once dirname(__FILE__).'/calendarperiod.class.php';
125 7
	}
126
127
128
129
130
	/**
131
	 * Create new calendar period
132
	 *
133
	 * @param int $begin	Timestamp
134
	 * @param int $end		Timestamp
135
	 *
136
	 * @return caldav_CalendarPeriod
137
	 */
138 5
	public function CalendarPeriod($begin = null, $end = null)
139
	{
140 5
		$this->includeCalendarPeriod();
141 5
		return new caldav_CalendarPeriod();
142
	}
143
144
145
146
	public function includeCalendarAlarm()
147
	{
148
		require_once dirname(__FILE__).'/calendarperiod.class.php';
149
	}
150
151
	/**
152
	 * Create new calendar alarm
153
	 * VALARM object item, store rules for reminder on event
154
	 * @see bab_CalendarPeriod::setAlarm()
155
	 *
156
	 * @return caldav_CalendarAlarm
157
	 */
158
	public function CalendarAlarm()
159
	{
160
		$this->includeCalendarAlarm();
161
		return new caldav_CalendarAlarm();
162
	}
163
164
165
166 3
	public function getProdId()
167
	{
168 3
		return 'PRODID:-//Cantico//NONSGML Ovidentia CalDAV Client//EN';
169
	}
170
171
172
	/**
173
	 * Returns the timezone name. E.g. 'Europe/Paris'.
174
	 *
175
	 * @return string
176
	 */
177 3
	static public function getTimezoneName()
178
	{
179 3
		$timezone = date_default_timezone_get();
180 3
	    if (strtolower($timezone) === 'system/localtime') {
181
	    	$timezone = 'Europe/Paris';
182
	    }
183
184 3
	    return $timezone;
185
	}
186
187
188
	/**
189
	 * Returns the timezone's icalendar definition.
190
	 *  BEGIN:VTIMEZONE
191
	 *  ...
192
	 *  END:VTIMEZONE
193
	 *
194
	 * @return string
195
	 */
196 3
	public function getTimeZone()
197
	{
198 3
	    static $vtimezone = null;
199 3
	    if (isset($vtimezone)) {
200 2
	        return $vtimezone;
201
	    }
202
203 1
	    $timezone = self::getTimezoneName();
204
205 1
	    if ($addon = bab_getAddonInfosInstance('LibCaldav')) {
206
	        $timezoneFilename = $addon->getTemplatePath() . 'zoneinfo/' . $timezone . '.ics';
207
	    } else {
208 1
	        $timezoneFilename = dirname(__FILE__).'/../skins/ovidentia/templates/zoneinfo/' . $timezone . '.ics';
209
	    }
210
211 1
	    $vcalendar = @file_get_contents($timezoneFilename);
212 1
	    if ($vcalendar !== false) {
213
214 1
	        $m = null;
215 1
	        preg_match('/(BEGIN:VTIMEZONE.*END:VTIMEZONE)/Us', $vcalendar, $m);
216
217 1
	        $vtimezone = $m[0];
218 1
	        $vtimezone = str_replace("\n", "\r\n", $vtimezone);
219 1
	        $vtimezone .= "\r\n";
220 1
	        return $vtimezone;
221
	    }
222
223
 		$vtimezone = 'BEGIN:VTIMEZONE' . "\r\n"
224
                    . 'TZID:Europe/Paris' . "\r\n"
225
                    . 'X-LIC-LOCATION:Europe/Paris' . "\r\n"
226
                    . 'BEGIN:DAYLIGHT' . "\r\n"
227
                    . 'TZOFFSETFROM:+0100' . "\r\n"
228
                    . 'TZOFFSETTO:+0200' . "\r\n"
229
                    . 'TZNAME:CEST' . "\r\n"
230
                    . 'DTSTART:19700329T020000' . "\r\n"
231
                    . 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3' . "\r\n"
232
                    . 'END:DAYLIGHT' . "\r\n"
233
                    . 'BEGIN:STANDARD' . "\r\n"
234
                    . 'TZOFFSETFROM:+0200' . "\r\n"
235
                    . 'TZOFFSETTO:+0100' . "\r\n"
236
                    . 'TZNAME:CET' . "\r\n"
237
                    . 'DTSTART:19701025T030000' . "\r\n"
238
                    . 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10' . "\r\n"
239
                    . 'END:STANDARD' . "\r\n"
240
                    . 'END:VTIMEZONE' . "\r\n";
241
 		return $vtimezone;
242
	}
243
244
245
246
247
248
249
250
	/**
251
	 * Sets the base url to access the CalDAV server.
252
	 *
253
	 * @param string $url  The URL for the calendar server
254
	 *
255
	 * @return Func_CalendarBackend_Caldav
256
	 */
257
	public function setServerUrl($url)
258
	{
259
		$this->serverUrl = $url;
260
261
		return $this;
262
	}
263
	
264
	
265
	/**
266
	 * 
267
	 * @param int $userId
268
	 * @return int | null
269
	 */
270 8
	public function getUserServerId($userId)
271
	{
272 8
		$registry = bab_getRegistryInstance();
273 8
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
274 8
		return $registry->getValue('server');
275
	}
276
277
	
278
	/**
279
	 * Get server configuration
280
	 * @param int $id_server
281
	 * @return array
282
	 */
283 8
	public function getServer($id_server)
284
	{
285 8
		global $babDB;
286
		
287 8
		$res = $babDB->db_query('SELECT * FROM libcaldav_servers WHERE id='.$babDB->quote($id_server));
288 8
		while($arr = $babDB->db_fetch_assoc($res))
289
		{
290 8
			return $arr;
291
		}
292
		
293
		return null;
294
	}
295
296
297
298
	/**
299
	 * returns the base url to access the CalDAV server.
300
	 * @param	int	$userId
301
	 * @return string
302
	 */
303 8
	public function getServerUrl($userId)
304
	{
305 8
		if (!isset($this->serverUrl)) {
306 8
			$id_server = $this->getUserServerId($userId);
307 8
			if (!isset($id_server))
308 8
			{
309
				return null;
310
			}
311
			
312 8
			$server = $this->getServer($id_server);
313
			
314 8
			return $server['server_url'];
315
		}
316
		return $this->serverUrl;
317
	}
318
319
320
321
322
	/**
323
	 * Returns the href identifier of a caldav user corresponding to an
324
	 * ovidentia user.
325
	 *
326
	 * @param int	$userId		An ovidentia user id.
327
	 */
328
	public function getUserHref($userId)
329
	{
330
		if ($this->configByUser()) {
331
		
332
			return $this->getUserIdentifier($userId);
333
		}
334
		
335
		return bab_getUserNickname($userId);
336
	}
337
338
339
340
341
	/**
342
	 * check a personnal calendar with user configuration
343
	 *
344
	 * @param int	$userId
345
	 *
346
	 * @return bool
347
	 */
348 1
	public function checkCalendar($userId)
349
	{
350 1
		$path = $this->getPersonnalCalendarUrl($userId);
351 1
		list($nickname, $password) = $this->getPersonalCalendarCredentials($userId);
352
		
353 1
		$this->caldavClient = new CalDAVClient($path, $nickname, $password, 'calendar');
354
		
355
356
		$xml = '<?xml version="1.0" encoding="utf-8" ?>
357
		<D:propfind xmlns:D="DAV:">
358
		<D:prop>
359
		<D:current-user-privilege-set/>
360
		</D:prop>
361 1
		</D:propfind>';
362
363
364
		try {
365 1
			$this->caldavClient->SetDepth(0);
366 1
			$response = $this->caldavClient->DoXMLRequest('PROPFIND', $xml, '');
367
368 1
		} catch (Exception $e) {
369
			return false;
370
		}
371
372 1
		return !empty($response);
373
	}
374
375
	/**
376
	 *
377
	 * @param caldav_EventCalendar $calendar
378
	 * @param $relativePath
379
	 *
380
	 * @return Func_CalendarBackend_Caldav
381
	 */
382 7
	public function init(caldav_EventCalendar $calendar)
383
	{	
384 7
		$this->caldavClient = $calendar->getCaldavClient();
385 7
		return $this;
386
	}
387
388
389
390
	/**
391
	 * The list of calendars recorded with the sharing access form
392
	 * to use theses calendars, the user must have a personal calendar or $babBody->babsite['iPersonalCalendarAccess'] == 'Y'
393
	 *
394
	 * @param int		$access_user		in most case, the current user
395
	 * @param string	$calendartype		optional filter by calendar type
396
	 * @return array	<int>				array of id_user
397
	 */
398
	public function getAccessiblePersonalCalendars($access_user = null, $calendartype = null)
399
	{
400
		/*@var $backend Func_CalendarBackend_Ovi */
401
		$backend = bab_functionality::get('CalendarBackend/Ovi');
402
		if (!isset($backend)) {
403
			return array();
404
		}
405
406
		return $backend->getAccessiblePersonalCalendars($access_user, $calendartype);
407
	}
408
	
409
	
410
	
411
	/**
412
	 * @return array
413
	 */
414
	public function getAccessibleResourceCalendars()
415
	{
416
		
417
		$arr = bab_getUserIdObjects('libcaldav_resource_groups');
418
		
419
		if (empty($arr))
420
		{
421
			return array();
422
		}
423
		
424
		global $babDB;
425
		
426
		$res = $babDB->db_query('SELECT * FROM libcaldav_resources WHERE id IN('.$babDB->quote($arr).')');
427
		$return = array();
428
		while ($arr = $babDB->db_fetch_assoc($res))
429
		{
430
			$return[$arr['id']] = array(
431
				'name' => $arr['name'],
432
				'url' => $arr['url'],
433
				'nickname' => $arr['nickname'],
434
				'password' => $arr['password']	
435
			);
436
		}
437
		
438
		return $return;
439
	}
440
441
442
443
	public function iCalObjectToVcalendarInvitation(bab_ICalendarObject $icalObject, bab_PersonalCalendar $calendar)
0 ignored issues
show
Unused Code introduced by
The parameter $calendar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
444
	{
445
		$this->includeCalendarPeriod();
446
		$vcalendar = 'BEGIN:VCALENDAR' . "\n"
447
		. $this->getProdId() . "\n"
448
		. 'VERSION:2.0' . "\n"
449
		. 'METHOD:REQUEST' . "\n"
450
		. $this->getTimeZone();
451
452
		$vcalendar .= caldav_CalendarPeriod::toIcal($icalObject) . "\n";
0 ignored issues
show
Compatibility introduced by
$icalObject of type object<bab_ICalendarObject> is not a sub-type of object<bab_CalendarPeriod>. It seems like you assume a child class of the class bab_ICalendarObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
453
454
		$vcalendar .= 'END:VCALENDAR';
455
456
		return $vcalendar;
457
	}
458
459
460
461
462
	/**
463
	 * Creates or updates a calendar event.
464
	 *
465
	 * @param 	caldav_CalendarPeriod 	$period
466
	 * @param	string					$method		iCalendar Transport-Independent Interoperability Protocol (iTIP) (RFC 5546)
467
	 * 												PUBLISH | REQUEST | REPLY | ADD | CANCEL | REFRESH | COUNTER | DECLINECOUNTER
468
	 *
469
	 *
470
	 * @return bool		true if the period was correctly saved.
471
	 */
472 3
	public function savePeriod(bab_CalendarPeriod $period, $method = null)
473
	{
474 3
		$this->includeCalendarPeriod();
475 3
		$periodCollection = $period->getCollection();
476 3
		$calendar = $periodCollection->getCalendar();
477
478 3
		if (!($calendar instanceof caldav_EventCalendar))
479 3
		{
480
			throw new ErrorException(sprintf('failed to save the event %s, wrong calendar type %s', $period->getProperty('UID'), $calendar->getUrlIdentifier()));
481
		}
482
483 3
		$this->init($calendar);
484
485
486
		// Kerio specific :
487
		// when events selected from KERIO caldav server,
488
		// the ORGANIZER of an event is removed from the list of attendees
489
		// to fix this problem, we add X-CTO-ORGANIZER-PARTSTAT to store the PARTSTAT of the ORGANIZER
490
		// only if the organizer is in the list of attendees
491
492 3
		if ($organizer = $period->getOrganizer())
493 3
		{
494
			foreach($period->getAttendees() as $attendee)
495
			{
496
				if ($attendee['email'] === $organizer['email'] && $organizer['name'] === $attendee['CN'])
497
				{
498
					$period->setProperty('X-CTO-ORGANIZER-PARTSTAT', $attendee['PARTSTAT']);
499
					break;
500
				}
501
			}
502
		}
503
504
505 3
		$uid = $period->getProperty('UID');
506
507 3
		if (empty($uid)) {
508
			// create the event with generated ID
509
			$uid = uniqid();
510
			$period->setProperty('UID', $uid);
511
			$period->setProperty('SEQUENCE', 1);
512
			$period->setProperty('DTSTAMP', BAB_DateTime::now()->getICal(true));
513
			$period->setProperty('CREATED', BAB_DateTime::now()->getICal(true));
514
			$newEvent = true;
515
			$originalPeriods = array(clone $period);
516
517
		} else {
518
		    
519
		    
520
		    // optional delete on cancel
521
		    
522 3
		    $registry = bab_getRegistryInstance();
523 3
		    $registry->changeDirectory('/LibCaldav/');
524
		    
525 3
		    if ('CANCEL' === $method && $registry->getValue('deleteOnCancel')) {
526
		        return $this->deletePeriod($period);
527
		    }
528
		    
529
530
			// try to update SEQUENCE
531
532 3
			$sequence = 1 + (int) $period->getProperty('SEQUENCE');
533 3
			$period->setProperty('SEQUENCE', $sequence);
534
535 3
			$icalEvents = $this->caldavClient->GetEntryByUid($uid);
536 3
			$originalPeriods = null;
537
538 3
			foreach ($icalEvents as $icalEvent) {
539
			    
540 1
			    if (!isset($icalEvent['data'])) {
541
			        continue;
542
			    }
543
			    
544 1
				$originalPeriods = $this->expandPeriods($icalEvent['data'], $periodCollection, false);
545 3
			}
546
547
548 3
			if (isset($originalPeriods))
549 3
			{
550
				// Modification of an existing event.
551 1
				$newEvent = false;
552 1
			}
553
			else
554
			{
555
				// create the event with given ID
556 2
				$originalPeriods = array(clone $period);
557 2
				$newEvent = true;
558
			}
559
560
			/* @var $replacedPeriod bab_ICalendarObject */
561 3
			$replacedPeriod = null;
0 ignored issues
show
Unused Code introduced by
$replacedPeriod is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
562
563 3
			$recurrenceId = $period->getProperty('RECURRENCE-ID');
564 3
			if ($recurrenceId !== '') {
565
566
				// Here we know we change only one recurrence period in the event.
567
				if (is_array($recurrenceId)) {
568
					$recurrenceId = array_pop($recurrenceId);
569
				}
570
				if (isset($originalPeriods[$recurrenceId])) {
571
					// The recurrence id already exist, we replace the period.
572
					$replacedPeriod = $originalPeriods[$recurrenceId];
573
				} else {
574
					// The recurrence id does not already exist, we create a new period recurrence.
575
					$replacedPeriod = null;
576
					$originalPeriods[$recurrenceId] = $period;
577
				}
578
579
			} else {
580
581
				// Here we know we change all the periods in the event.
582 3
				$replacedPeriod = reset($originalPeriods);
583
			}
584
585 3
			if (isset($replacedPeriod) && $replacedPeriod) {
586
587
				try {
588 3
					$replacedPeriod->removeAttendees();
589 3
					$replacedPeriod->removeRelations();
590 3
				} catch(Exception $e){
591
					// relation could not be removed if there is an ongoing approbation
592
				}
593
594 3
				foreach ($period->getProperties() as $icalProperty) {
595
596 3
					$colonPos = mb_strpos($icalProperty, ':');
597 3
					$propName = substr($icalProperty, 0, $colonPos);
598 3
					$propValue = substr($icalProperty, $colonPos + 1);
599
					
600
601 3
					if (!empty($propValue)) {
602 3
						$replacedPeriod->setProperty($propName, $propValue);
603 3
					}
604 3
				}
605
606 3
				foreach ($period->getRelations() as $relation) {
607 1
					$replacedPeriod->addRelation($relation['reltype'], $relation['calendar']);
608 3
				}
609
610 3
				foreach ($period->getAllAttendees() as $attendee) {
611 2
					if (isset($attendee['calendar']))
612 2
					{
613
						// the calendar of attendee is accessible
614 2
						$replacedPeriod->addAttendee($attendee['calendar'], $attendee['ROLE'], $attendee['PARTSTAT'], $attendee['RSVP']);
615 2
					} else {
616
						// the calendar is not accessible
617
						// try to get the non accessible calendar :
618
						
619
						if ($id_user = bab_getUserIdByEmailAndName($attendee['email'], $attendee['CN']))
620
						{
621
							// use the icalendars object of attendee to bypass the access rights tests
622
							$att_cals = bab_getICalendars($id_user);
623
							
624
							if( $id_calendar = $att_cals->getPersonalCalendarUid($id_user))
625
							{
626
								$reftype = $att_cals->getUserReferenceType($id_user);
627
								$usercal = $att_cals->getEventCalendar("$reftype/$id_calendar");
628
								
629
								if (isset($usercal))
630
								{
631
									$replacedPeriod->addAttendee($usercal, $attendee['ROLE'], $attendee['PARTSTAT'], $attendee['RSVP']);
0 ignored issues
show
Documentation introduced by
$usercal is of type object<bab_EventCalendar>, but the function expects a object<bab_PersonalCalendar>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
632
								}
633
							}
634
						}
635
						
636
						
637
						
638
					}
639 3
				}
640
641 3
				if ($alarm = $period->getAlarm()) {
642
					$replacedPeriod->setAlarm($alarm);
643
				}
644 3
			}
645
		}
646
		
647
		
648 3
		if (empty($originalPeriods)) {
649
		    throw new ErrorException('No periods to save, put request canceled');
650
		}
651
		
652
653
		$xml = 'BEGIN:VCALENDAR' . "\n"
654 3
		. $this->getProdId() . "\n"
655 3
		. 'VERSION:2.0' . "\n"
656 3
		. $this->getTimeZone();
657
658
659 3
		if (null !== $method)
660 3
		{
661 1
			$xml .= "METHOD:$method\n";
662 1
		}
663
664
665 3
		foreach ($originalPeriods as $p) {
666 3
			$xml .= caldav_CalendarPeriod::toIcal($p) . "\n";
667 3
		}
668
669 3
		$xml .= 'END:VCALENDAR';
670
671
672 3
		if ($newEvent) {
673 2
			$this->caldavClient->setMatch(false);
674 2
		} else {
675
			// Check ETAG with If-Match (setMatch method)
676
		}
677
		
678
		try {
679 3
			$this->caldavClient->DoPUTRequest($uid . '.ics', $xml);
680 3
		} catch (Exception $e) {
681
		    // only ErrorException are captured are error messages in ovidentia
682
            throw new ErrorException($e->getMessage());
683
		}
684
685
		// caldav_SessionFile($uid . '.ics', $xml);
686
687 3
		return true;
688
	}
689
690
691
	/**
692
	 * Process coma separated values from multiples properties or one property
693
	 * 
694
	 * T6775 : probablement plusieurs EXDATE dans le meme evenement
695
	 * 
696
	 * @param	string | array	$properties
697
	 * @return array
698
	 */
699
	private function explodeRules($properties)
700
	{
701
		if (is_array($properties))
702
		{
703
			$values = array();
704
			foreach($properties as $property)
705
			{
706
				$values = array_merge($values, explode(',', $property));
707
			}
708
			
709
			return $values;
710
		}
711
		
712
		return explode(',', $properties);
713
	}
714
715
716
	/**
717
	 * Expands an icalendar-formatted data string $icalEventData to an array of <bab_CalendarPeriod>.
718
	 * The data string must correspond to a single caldav event (only one UID) that can however be
719
	 * made of several recurrences (hence several VEVENT).
720
	 *
721
	 * @param string						$icalEventData		The icalendar-formatted event data.
722
	 * @param bab_CalendarEventCollection	$collection			If specified, periods will be associated to this collection.
723
	 * @param bool							$expandRecurrence	True to have VEVENTs with RRULES expanded.
724
	 * 															If set to false, only one bab_CalendarPariod is created for a
725
	 * 															VEVENT with an RRULE, VEVENTs with a RECURRENCE-ID are still
726
	 * 															returned as additional bab_CalendarPeriods.
727
	 *
728
	 * @return bab_CalendarPeriod[]
729
	 */
730 5
	protected function expandPeriods($icalEventData, bab_CalendarEventCollection $collection = null, $expandRecurrence = true, $expandStart = null, $expandEnd = null)
731
	{
732 5
		$periods = array();
733 5
		$eventsData = array();
734 5
		preg_match_all('/BEGIN:VEVENT(.*)END:VEVENT/Us', $icalEventData, $eventsData);
735
736 5
		if ($expandRecurrence) {
737
			// If recurrent events should be expanded and no expand range has been specified, we
738
			// limit to +/- 5 years.
739 5 View Code Duplication
			if (!isset($expandStart)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
740 4
				require_once $GLOBALS['babInstallPath'] . 'utilit/dateTime.php';
741 4
				$expandStart = BAB_DateTime::now();
742 4
				$expandStart->add(-5, BAB_DATETIME_YEAR);
743 4
				$expandStart = $expandStart->getICal();
744 4
			}
745 5 View Code Duplication
			if (!isset($expandEnd)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
746 4
				require_once $GLOBALS['babInstallPath'] . 'utilit/dateTime.php';
747 4
				$expandEnd = BAB_DateTime::now();
748 4
				$expandEnd->add(5, BAB_DATETIME_YEAR);
749 4
				$expandEnd = $expandEnd->getICal();
750 4
			}
751 5
		}
752
753 5
		foreach ($eventsData[1] as $eventData) {
754
			
755
756 5
			$period = $this->CalendarPeriod();
757
758
			try {
759 5
				$period->fromIcal($eventData);
760
			}
761 5
			catch(Exception $e)
762
			{
763
				// bab_debug($e->getMessage(), DBG_ERROR, 'caldav');
764
				continue;
765
			}
766
767
			if ($expandRecurrence
768 5
				&& ($period->getProperty('RRULE') !== '' || $period->getProperty('RDATE') !== '')) {
769
770
				// If the period has a recurrence rule, we create as much periods as necessary.
771
772
				require_once dirname(__FILE__) . '/caldav/RRule.php';
773
774
				$recurrenceDates = array();
775
776
				// First we use the RDATE and RRULE parameters to determine the list
777
				// of dates.
778
779
				$icalStart = new LibCaldav_iCalDate($period->getProperty('DTSTART'));
780
				$icalEnd = new LibCaldav_iCalDate($period->getProperty('DTEND'));
781
				$icalDuration = $icalStart->DateDifference($icalEnd);
782
783 View Code Duplication
				if ($period->getProperty('RDATE') !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
784
					$rdates = $this->explodeRules($period->getProperty('RDATE'));
785
					foreach ($rdates as $rdate) {
786
						if ($rdate >= $expandStart && $rdate < $expandEnd) {
787
							$recurrenceDates[$rdate] = $rdate;
788
						}
789
					}
790
				}
791
792 View Code Duplication
				if ($period->getProperty('RRULE') !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
793
					$rrule = new RRule($icalStart, $period->getProperty('RRULE'));
794
795
					// We add an element to $recurrenceDates for each date defined by the recurrence rule.
796
					while (($dtStart = $rrule->GetNext()) && ($dtStart->_text < $expandEnd)) {
797
						if ($dtStart->_text >= $expandStart) {
798
							$recurrenceDates[$dtStart->_text] = $dtStart->_text;
799
						}
800
					}
801
				}
802
803
				// Now we use EXDATE and EXRULE parameters to determine a list
804
				// of date excluded from the recurrence rule.
805
806 View Code Duplication
				if ($period->getProperty('EXDATE') !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
807
					$exdates = $this->explodeRules($period->getProperty('EXDATE'));
808
					foreach ($exdates as $exdate) {
809
						if ($exdate >= $expandStart && $exdate < $expandEnd) {
810
							unset($recurrenceDates[$exdate]);
811
						}
812
					}
813
				}
814
815 View Code Duplication
				if ($period->getProperty('EXRULE') !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
816
					$exrule = new RRule($icalStart, $period->getProperty('EXRULE'));
817
818
					// We remove an element from $recurrenceDates for each date defined by the recurrence rule.
819
					while (($dtStart = $exrule->GetNext()) && ($dtStart->_text < $expandEnd)) {
820
						if ($dtStart->_text >= $expandStart) {
821
							unset($recurrenceDates[$dtStart->_text]);
822
						}
823
					}
824
				}
825
826
				// Finally we create a period for each date remaining in $recurrenceDates.
827
828
				foreach ($recurrenceDates as $startDate) {
829
					$dtStart = new LibCaldav_iCalDate($startDate);
830
					$dtEnd = new LibCaldav_iCalDate($dtStart);
831
					$dtEnd->addDuration($icalDuration);
832
					
833
					// en utilisant un clone de $period c'est probablement plus rapide
834
					// mais il semble que le lien avec la collection soit casse, cela declanche un probleme dans bab_CalAttendeeBackend::getRealAttendee()
835
					// $date_period = clone $period;
836
					$date_period = $this->CalendarPeriod();
837
					$date_period->fromIcal($eventData);
838
					
839
					$date_period->setDates(BAB_DateTime::fromICal($dtStart->_text), BAB_DateTime::fromICal($dtEnd->_text));
0 ignored issues
show
Bug introduced by
It seems like \BAB_DateTime::fromICal($dtStart->_text) can be null; however, setDates() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like \BAB_DateTime::fromICal($dtEnd->_text) can be null; however, setDates() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
840 View Code Duplication
					if (isset($collection)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
841
						$collection->addPeriod($date_period);
842
						$relations = $date_period->getRelations('PARENT');
843
						if (count($relations) === 0) {
844
							$date_period->addRelation('PARENT', $collection->getCalendar());
845
						}
846
					}
847
					$periods[$date_period->getProperty('DTSTART')] = $date_period;
848
				}
849
850
851
			} else {
852
853 5 View Code Duplication
				if (isset($collection)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
854 5
					$collection->addPeriod($period);
855 5
					$relations = $period->getRelations('PARENT');
856 5
					if (count($relations) === 0) {
857 5
						$period->addRelation('PARENT', $collection->getCalendar());
858 5
					}
859 5
				}
860 5
				if (!$expandRecurrence || ($period->getProperty('DTSTART') < $expandEnd && $period->getProperty('DTEND') > $expandStart) ) {
861 5
					$recurrenceId = $period->getProperty('RECURRENCE-ID');
862 5
					if ($recurrenceId !== '') {
863
						if (is_array($recurrenceId)) {
864
							$recurrenceId = array_pop($recurrenceId);
865
						}
866
						$periods[$recurrenceId] = $period;
867
					} else {
868 5
						$periods[$period->getProperty('DTSTART')] = $period;
869
					}
870 5
				}
871
872
			}
873 5
		}
874
875 5
		return $periods;
876
	}
877
878
879
	/**
880
	 * Returns the period corresponding to the specified identifier (and optionally a particular occurence at the specified start date).
881
	 *
882
	 * @param	bab_PeriodCollection	$periodCollection		where to search for event
883
	 * @param 	string 					$identifier				The UID property of event
884
	 * @param 	string 					$start					The icalendar-formatted start date (useful when editing a particular iteration of a recurrent event).
885
	 *
886
	 * @return caldav_CalendarPeriod	Or null if no match
887
	 */
888 4
	public function getPeriod(bab_PeriodCollection $periodCollection, $identifier, $start = null)
889
	{
890 4
		$calendar = $periodCollection->getCalendar();
891
892 4
		if (!$calendar)
893 4
		{
894
			throw new Exception(sprintf('try to search for event in collection %s but the collection is not in a calendar', get_class($periodCollection)));
895
		}
896
897 4
		$this->init($calendar);
0 ignored issues
show
Compatibility introduced by
$calendar of type object<bab_EventCalendar> is not a sub-type of object<caldav_EventCalendar>. It seems like you assume a child class of the class bab_EventCalendar to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
898
899
		try {
900 4
			$events = $this->caldavClient->GetEntryByUid($identifier);
901 4
		} catch (Exception $e) {
902
			bab_debug($e->getMessage());
903
			$events = array();
904
		}
905
906 4
		$periods = array();
907
908
		//		var_dump($events);
909 4
		foreach ($events as $icalEvent) {
910
		    
911 4
		    if (!isset($icalEvent['data'])) {
912
		        continue;
913
		    }
914
915
//			echo $icalEvent['data'] . "\n\n\n";
916 4
			$periods = $this->expandPeriods($icalEvent['data'], $periodCollection, true);
0 ignored issues
show
Documentation introduced by
$periodCollection is of type object<bab_PeriodCollection>, but the function expects a null|object<bab_CalendarEventCollection>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
917 4
			if (isset($start) && isset($periods[$start])) {
918
				return $periods[$start];
919
			}
920
921
922 4
			if ($identifier.'.ics' === $icalEvent['href']) {
923 4
				return array_pop($periods);
924
			}
925 1
		}
926
927 1
		if (count($periods) > 0)
928 1
		{
929
			return array_pop($periods);
930
		}
931
932
933 1
		return null;
934
	}
935
936
937
	/**
938
	 * Returns the period corresponding to the specified identifier (and optionally a particular occurence at the specified start date).
939
	 *
940
	 * @param bab_PeriodCollection	$periodCollection		where to search for event
941
	 * @param string 				$identifier				The UID property of event
942
	 * @param bool					$expandRecurrence		True to have VEVENTs with RRULES expanded.
943
	 * 														If set to false, only one bab_CalendarPariod is created for a
944
	 * 														VEVENT with an RRULE, VEVENTs with a RECURRENCE-ID are still
945
	 * 														returned as additional bab_CalendarPeriods.
946
	 * @param BAB_DateTime			$expandStart			Lower limit for date expansion, null for no lower limit.
947
	 * @param BAB_DateTime			$expandEnd				Upper limit for date expansion, null for no upper limit.
948
	 *
949
	 * @return array<caldav_CalendarPeriod>	Or null if no match
950
	 */
951
	public function getAllPeriods(bab_PeriodCollection $periodCollection, $identifier, $expandRecurrence = true, BAB_DateTime $expandStart = null, BAB_DateTime $expandEnd = null)
952
	{
953
		$calendar = $periodCollection->getCalendar();
954
955
		$this->init($calendar);
0 ignored issues
show
Compatibility introduced by
$calendar of type object<bab_EventCalendar> is not a sub-type of object<caldav_EventCalendar>. It seems like you assume a child class of the class bab_EventCalendar to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
956
		$events = $this->caldavClient->GetEntryByUid($identifier);
957
958
		if (isset($expandStart)) {
959
			$expandStart = $expandStart->getICal();
960
		}
961
		if (isset($expandEnd)) {
962
			$expandEnd = $expandEnd->getICal();
963
		}
964
		$periods = array();
0 ignored issues
show
Unused Code introduced by
$periods is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
965
966
		foreach ($events as $icalEvent) {
967
968
			$periods = $this->expandPeriods($icalEvent['data'], $periodCollection, $expandRecurrence, $expandStart, $expandEnd);
0 ignored issues
show
Documentation introduced by
$periodCollection is of type object<bab_PeriodCollection>, but the function expects a null|object<bab_CalendarEventCollection>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
969
			return $periods;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $periods; (bab_CalendarPeriod[]) is incompatible with the return type of the parent method Func_CalendarBackend::getAllPeriods of type Iterator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
970
		}
971
		return null;
972
	}
973
974
975
976 1
	private static function flattenCriteria(bab_PeriodCriteria $criteria, &$criteriaArray)
977
	{
978 1
		$criteriaArray[] = $criteria;
979 1
		$subCriteria = $criteria->getCriterions();
980 1
		foreach($subCriteria as $subCriterion) {
981 1
			self::flattenCriteria($subCriterion, $criteriaArray);
982 1
		}
983 1
	}
984
985
986
987
988
	/**
989
	 * @param bab_PeriodCriteria $criteria
990
	 *
991
	 * @return iterator <caldav_CalendarPeriod>
992
	 */
993 1
	public function selectPeriods(bab_PeriodCriteria $criteria = null)
994
	{
995 1
		$criteriaArray = array();
996 1
		self::flattenCriteria($criteria, $criteriaArray);
0 ignored issues
show
Bug introduced by
It seems like $criteria defined by parameter $criteria on line 993 can be null; however, Func_CalendarBackend_Caldav::flattenCriteria() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
997
998 1
		$filteredProperties = array();
999
1000 1
		$begin = null;
1001 1
		$end = null;
1002 1
		$uid_values = array();
1003 1
		$uid_criterion = null;
1004
1005 1
		foreach ($criteriaArray as $criterion) {
1006
			//			echo get_class($criterion) . "\n";
1007 1
			if ($criterion instanceof bab_PeriodCriteriaCalendar) {
1008 1
				$selectedCalendars = array();
1009 1
				foreach ($criterion->getCalendar() as $calendarId => $calendar) {
1010 1
					if (($calendar instanceof caldav_PersonalCalendar) || ($calendar instanceof caldav_ResourceCalendar)) {
1011 1
						$selectedCalendars[$calendarId] = $calendar;
1012 1
					}
1013 1
				}
1014 1
			} else if ($criterion instanceof bab_PeriodCritieraBeginDateLessThanOrEqual) {
1015 1
				$end = $criterion->getDate();
1016 1
				$end = isset($end) ? $end->getICal(true) : null;
1017 1
			} else if ($criterion instanceof bab_PeriodCritieraEndDateGreaterThanOrEqual) {
1018 1
				$begin = $criterion->getDate();
1019 1
				$begin = isset($begin) ? $begin->getICal(true) : null;
1020
1021 1
			} else if ($criterion instanceof bab_PeriodCritieraUid) {
1022
1023
				$uid_criterion = clone $criterion;
1024
				$uid_values = $criterion->getUidValues();
1025
1026
				foreach($uid_values as $uid)
1027
				{
1028
					$filteredProperties[] = array(
1029
						'name' => 'UID',
1030
						'value' => $uid,
1031
						'contain' => false
1032
					);
1033
				}
1034
1035
			} else if ($criterion instanceof bab_PeriodCritieraProperty) {
1036
1037
				foreach ($criterion->getValue() as $value) {
1038
					$filteredProperties[] = array(
1039
						'name' => $criterion->getProperty(),
1040
						'value' => $value,
1041
						'contain' => $criterion->getContain()
1042
					);
1043
				}
1044
			}
1045 1
		}
1046
1047
1048 1
		if (empty($selectedCalendars))
1049 1
		{
1050
			require_once $GLOBALS['babInstallPath'].'utilit/devtools.php';
1051
			bab_debug('Warning : query in LibCaldav without compatible calendars', DBG_WARNING, 'Caldav');
1052
			return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type of the parent method Func_CalendarBackend::selectPeriods of type Iterator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1053
		}
1054
1055
1056 1
		$periods = array();
1057
1058 1
		foreach ($selectedCalendars as $selectedCalendar) {
1059
1060
			try {
1061 1
				$this->init($selectedCalendar);
1062
				
1063 1
				if (null === $begin && null === $end && $uid_values)
0 ignored issues
show
Bug Best Practice introduced by
The expression $uid_values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1064 1
				{
1065
					// getUidValuesByCalendar use the parent_calendar column in the inbox
1066
					$uid_list = $uid_criterion->getUidValuesByCalendar($selectedCalendar);
1067
					if (!$uid_list)
1068
					{
1069
						continue;
1070
					}
1071
					
1072
					$events = $this->caldavClient->GetEntriesByUid($uid_list);
1073
				} else {
1074 1
					$this->caldavClient->SetDepth(1);
1075 1
					$events = $this->caldavClient->GetEvents($begin, $end);
1076
				}
1077
				
1078 1
			} catch (caldav_HttpException $e) // 403, 404
0 ignored issues
show
Bug introduced by
The class caldav_HttpException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1079
			{
1080
				global $babBody;
1081
				$babBody->addError($e->getMessage());
1082
				return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type of the parent method Func_CalendarBackend::selectPeriods of type Iterator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1083
				
1084
			}
1085
			 catch (caldav_AccessException $e) // 401
0 ignored issues
show
Bug introduced by
The class caldav_AccessException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1086
			{
1087
				global $babBody;
1088
				$babBody->addError($e->getMessage());
1089
				return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type of the parent method Func_CalendarBackend::selectPeriods of type Iterator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1090
				
1091
			} catch (Exception $e) {
1092
				bab_debug($e->getMessage());
1093
				continue; 	// exemple: the inbox of a ovidentia user contain a reference to a caldav event in a broken
1094
							// user configuration, the continue allow to process other caldav calendars in the same inbox
1095
			}
1096
1097
1098 1
			$defaultCollection = $this->CalendarEventCollection($selectedCalendar);
1099
			//		$defaultCollection->setCalendar($calendar[0]);
1100
1101 1
			foreach ($events as $event) {
1102
1103 1
				if (!isset($event['data']))
1104 1
				{
1105
					continue;
1106
				}
1107
1108
1109 1
				$ps = $this->expandPeriods($event['data'], $defaultCollection, true, $begin, $end);
1110 1
				foreach ($ps as $p) {
1111 1
					$periods[] = $p;
1112 1
					$this->caldavClient->setHrefForUid($p->getProperty('UID'), $event['hrefpath']);
1113 1
				}
1114
1115
				
1116 1
			}
1117
1118 1
		}
1119
1120
1121 1
		if (!empty($filteredProperties)) {
1122
1123
			$filteredPeriods = array();
1124
			foreach ($periods as $period) {
1125
				foreach ($filteredProperties as $property) {
1126
					if (false === $property['contain'] && (((string) $property['value']) === (string) $period->getProperty($property['name'])))
1127
					{
1128
						$filteredPeriods[] = $period;
1129
						break;
1130
1131
					} else if (stripos($period->getProperty($property['name']), $property['value']) !== false) {
1132
						$filteredPeriods[] = $period;
1133
						break;
1134
					}
1135
				}
1136
			}
1137
1138
1139
			$periods = $filteredPeriods;
1140
		}
1141
1142
1143
		/*
1144
		foreach ($periods as $period) {
1145
			bab_debug($period->getProperties(), 2, 'caldav');
1146
		}
1147
1148
1149
		require_once $GLOBALS['babInstallPath'].'utilit/devtools.php';
1150
		bab_debug_print_backtrace();
1151
		*/
1152
1153 1
		return $periods;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $periods; (array) is incompatible with the return type of the parent method Func_CalendarBackend::selectPeriods of type Iterator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1154
	}
1155
1156
1157
1158
1159
	/**
1160
	 * Deletes the period corresponding to the specified identifier.
1161
	 *
1162
	 * @param	bab_CalendarPeriod	$period		The period to delete
1163
	 *
1164
	 */
1165 1
	public function deletePeriod(bab_CalendarPeriod $period)
1166
	{
1167
1168 1
		$periodCollection = $period->getCollection();
1169 1
		$calendar = $periodCollection->getCalendar();
1170
1171 1
		if ($calendar instanceof caldav_PersonalCalendar)
1172 1
		{
1173 1
			$this->deletePeriodForUser($calendar, $period);
1174 1
		}
1175
		else
1176
		{
1177
			// the main calendar is not caldav
1178
			// try to get a caldav calendar from the attendees or throw an error
1179
			foreach($period->getAttendees() as $attendee)
1180
			{
1181
				if ($attendee['calendar'] instanceof caldav_PersonalCalendar)
1182
				{
1183
					
1184
					$this->deletePeriodForUser($attendee['calendar'], $period);
1185
				}
1186
			}
1187
		}
1188 1
	}
1189
1190
1191
1192
1193
1194
1195
1196
	/**
1197
	 *
1198
	 * @param caldav_PersonalCalendar $calendar
1199
	 * @param bab_CalendarPeriod $period
1200
	 * @return unknown_type
1201
	 */
1202 1
	private function deletePeriodForUser(caldav_PersonalCalendar $calendar, bab_CalendarPeriod $period)
1203
	{
1204 1
		$uid = $period->getProperty('UID');
1205 1
		$periodCollection = $period->getCollection();
1206 1
		$this->init($calendar);
1207
1208 1
		$recurrenceId = $period->getProperty('RECURRENCE-ID');
1209
1210 1
		if ($recurrenceId === '') {
1211
1212
			// No RECURRENCE-ID specified so we delete the whole event.
1213
			try {
1214 1
				$this->caldavClient->DoDELETERequest($uid . '.ics');
1215 1
			} catch (Exception $e) {
1216
				bab_debug($e->getMessage());
1217
			}
1218
1219 1
			return true;
1220
1221
		}
1222
1223
1224
1225
1226
		require_once $GLOBALS['babInstallPath'] . 'utilit/dateTime.php';
1227
1228
		// Here we know we delete recurrence period(s) in the event.
1229
1230
		if (is_array($recurrenceId)) {
1231
			list($params , $recurrenceId) = each($recurrenceId);
1232
			$params = explode(';',$params);
1233
			array_shift($params);
1234
			$range = null;
1235
1236
			foreach($params as $param)
1237
			{
1238
				$arr = explode('=', $param);
1239
				if (2 === count($arr))
1240
				{
1241
					list($name, $value) = $arr;
1242
					$name = trim($name);
1243
					$value = trim($value);
1244
1245
					switch($name)
1246
					{
1247
						case 'RANGE':
1248
							$range = $value;
1249
							break;
1250
					}
1251
				}
1252
			}
1253
		}
1254
1255
		$originalPeriods = array();
1256
		$icalEvents = $this->caldavClient->GetEntryByUid($uid);
1257
1258
		foreach ($icalEvents as $icalEvent) {
1259
			$originalPeriods = $this->expandPeriods($icalEvent['data'], $periodCollection, false);
1260
		}
1261
1262
		if ((!isset($originalPeriods[0])) || ($originalPeriods[0]->getProperty('RRULE') === '')) {
1263
			$this->init($calendar);
1264
1265
			// no recurence rule, delete full event
1266
1267
			try {
1268
				$this->caldavClient->DoDELETERequest($uid . '.ics');
1269
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1270
1271
			}
1272
			return true;
1273
		}
1274
1275
1276
		foreach($originalPeriods as $dtstart => $period)
1277
		{
1278
			if (0 !== $dtstart && ($dtstart === $recurrenceId || ($dtstart < $recurrenceId && 'THISANDPRIOR' === $range) || ($dtstart > $recurrenceId && 'THISANDFUTURE' === $range)) )
0 ignored issues
show
Bug introduced by
The variable $range does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1279
			{
1280
				// A period with this recurrence id already exist in the list of modified periods, we remove the period.
1281
				unset($originalPeriods[$dtstart]);
1282
			}
1283
		}
1284
1285
1286
		// search the list of periods to add as exception
1287
1288
		$rrulePeriods = $this->expandPeriods($icalEvent['data'], $periodCollection, true);
0 ignored issues
show
Bug introduced by
The variable $icalEvent seems to be defined by a foreach iteration on line 1258. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
1289
		$deleteRuleExceptions = array();
1290
		foreach($rrulePeriods as $dtstart => $period)
1291
		{
1292
			if ($dtstart === $recurrenceId || ($dtstart < $recurrenceId && 'THISANDPRIOR' === $range) )
1293
			{
1294
				$deleteRuleExceptions[$dtstart] = $dtstart;
1295
			}
1296
		}
1297
1298
1299
		if (isset($originalPeriods[0]))
1300
		{
1301
			$originalPeriod = $originalPeriods[0];
1302
1303
1304
			if ($deleteRuleExceptions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deleteRuleExceptions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1305
				// add exceptions dates
1306
1307
				if (($exdate = $originalPeriod->getProperty('EXDATE')) === '') {
1308
					$exdate = implode(',', $deleteRuleExceptions);
1309
				} else {
1310
					$exdate = explode(',', $exdate);
1311
					$exdate = array_merge($exdate, array_values($deleteRuleExceptions));
1312
					$exdate = implode(',', $exdate);
1313
				}
1314
				$originalPeriod->setProperty('EXDATE', $exdate);
1315
			}
1316
1317
1318
			if ('THISANDFUTURE' === $range) {
1319
				// update the RRULE with an UNTIL value (UTC is mandatory)
1320
1321
				if ($rrule = $originalPeriod->getProperty('RRULE'))
1322
				{
1323
					$until = BAB_DateTime::fromICal($recurrenceId);
1324
					$until = $until->getICal(true);
1325
					$rrule = preg_replace('/UNTIL=\s*[0-9TZ]+/', '', $rrule);
1326
					$rrule = trim($rrule, ' ;');
1327
					$rrule .= ';UNTIL='.$until;
1328
1329
					bab_debug($rrule);
1330
					$originalPeriod->setProperty('RRULE', $rrule);
1331
				}
1332
			}
1333
1334
			$originalPeriod->setProperty('LAST-MODIFIED', BAB_DateTime::now()->getIcal(true));
1335
			$originalPeriod->setProperty('DTSTAMP', BAB_DateTime::now()->getIcal(true));
1336
		}
1337
1338
1339
		if (count($originalPeriods) == 0) {
1340
			return;
1341
		}
1342
1343
		$xml = 'BEGIN:VCALENDAR' . "\n"
1344
		. $this->getProdId() . "\n"
1345
		. 'VERSION:2.0' . "\n"
1346
		. $this->getTimeZone();
1347
1348
		foreach ($originalPeriods as $p) {
1349
			$xml .= caldav_CalendarPeriod::toIcal($p) . "\n";
1350
		}
1351
1352
		$xml .= 'END:VCALENDAR';
1353
1354
		$this->init($calendar);
1355
1356
1357
		// TODO : Check ETAG with If-Match (setMatch method)
1358
1359
		try {
1360
			$this->caldavClient->DoPUTRequest($uid . '.ics', $xml);
1361
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1362
		}
1363
1364
		return true;
1365
	}
1366
1367
1368
1369
	/**
1370
	 * Update an attendee PARTSTAT value of a calendar event
1371
	 * a user can modifiy his participation status without modifing the full event, before triggering this method, the access right will be checked with the
1372
	 * canUpdateAttendeePARTSTAT method of the calendar
1373
	 *
1374
	 * @see bab_EventCalendar::canUpdateAttendeePARTSTAT()
1375
	 *
1376
	 * @param bab_CalendarPeriod 	$period		the event
1377
	 * @param bab_PersonalCalendar 	$calendar	the personal calendar used as an attendee
1378
	 * @param string 				$partstat	ACCEPTED | DECLINED
1379
	 * @param string				$comment	comment given when changing PARTSTAT (optional)
1380
	 * @return bool
1381
	 */
1382
	public function updateAttendeePartstat(bab_CalendarPeriod $period, bab_PersonalCalendar $calendar, $partstat, $comment = '')
1383
	{
1384
		$period->addAttendee($calendar, 'REQ-PARTICIPANT', $partstat);
1385
1386
		$return = $this->savePeriod($period, 'REPLY');
1387
		$period->commitEvent();
1388
1389
		return $return;
1390
	}
1391
1392
1393
1394
1395
	/**
1396
	 * @param	int	$accessType		BAB_CAL_ACCESS_VIEW | BAB_CAL_ACCESS_UPDATE | BAB_CAL_ACCESS_FULL | BAB_CAL_ACCESS_SHARED_UPDATE
1397
	 *
1398
	 * return string		The dav xml privilege corresponding to the ovidentia access type.
1399
	 */
1400
	public function getDavPrivilege($accessType)
1401
	{
1402
		switch ($accessType) {
1403
1404
			// TODO : NEED to update right here.
1405
			case BAB_CAL_ACCESS_VIEW:
1406
				return '<D:privilege><D:read/></D:privilege>';
1407
1408
			case BAB_CAL_ACCESS_UPDATE:
1409
				return '<D:privilege><D:read/></D:privilege><D:privilege><D:write/></D:privilege>';
1410
1411
			case BAB_CAL_ACCESS_FULL:
1412
				return '<D:privilege><D:read/></D:privilege><D:privilege><D:write/></D:privilege>';
1413
1414
			case BAB_CAL_ACCESS_SHARED_UPDATE:
1415
				return '<D:privilege><D:read/></D:privilege><D:privilege><D:write/></D:privilege>';
1416
		}
1417
		return '';
1418
	}
1419
1420
1421
1422
1423
	/**
1424
	 * @param $object
1425
	 * @param array			$privileges
0 ignored issues
show
Bug introduced by
There is no parameter named $privileges. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1426
	 * @param array<int>	$users			Ovidentia user ids
1427
	 * @param string		$changeType		'grant' or 'deny'
1428
	 */
1429
	protected function changeAccess($object, $accessTypes, $users, $changeType)
1430
	{
1431
		if (!is_array($users)) {
1432
			$users = array($users);
1433
		}
1434
		$xmlPrincipals = '<D:principal>';
1435
		foreach ($users as $user) {
1436
			$xmlPrincipals .= '<D:href>' . $this->getUserHref($user) . '</D:href>';
1437
		}
1438
		$xmlPrincipals .= '</D:principal>';
1439
1440
		if (!is_array($accessTypes)) {
1441
			$accessTypes = array($accessTypes);
1442
		}
1443
		$xmlPrivileges = '<D:' . $changeType . '>';
1444
		foreach ($accessTypes as $accessType) {
1445
			$xmlPrivileges .= $this->getDavPrivilege($accessType);
1446
		}
1447
		$xmlPrivileges .= '</D:' . $changeType . '>';
1448
1449
		$xml =
1450
			'<?xml version="1.0" encoding="utf-8" ?>
1451
   			<D:acl xmlns:D="DAV:">
1452
			 <D:ace>' .
1453
		$xmlPrincipals .
1454
		$xmlPrivileges .
1455
	  	   '</D:ace>
1456
	 	  </D:acl>';
1457
1458
		if (!is_string($object)) {
1459
			$objectUrl = $object->getUrl();
1460
		} else {
1461
			$objectUrl = $object;
1462
		}
1463
1464
		$userId = $GLOBALS['BAB_SESS_USERID'];
1465
		$caldavClient = new CalDAVClient($this->getServerUrl(), $this->getUserIdentifier($userId),$this->getUserPassword($userId), null);
0 ignored issues
show
Bug introduced by
The call to getServerUrl() misses a required argument $userId.

This check looks for function calls that miss required arguments.

Loading history...
1466
		try {
1467
			return $caldavClient->DoXMLRequest('ACL', $xml, $objectUrl);
1468
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1469
		}
1470
	}
1471
1472
1473
1474
1475
	/**
1476
	 */
1477
	public function grantAccess($object, $accessTypes, $users)
1478
	{
1479
		$this->changeAccess($object, $accessTypes, $users, 'grant');
1480
	}
1481
1482
1483
1484
1485
	/**
1486
	 */
1487
	public function revokeAccess($object, $accessTypes, $users)
1488
	{
1489
		$this->changeAccess($object, $accessTypes, $users, 'deny');
1490
	}
1491
1492
1493
1494
	/**
1495
	 * Returns all access privileges granted to the specified user on the specified object.
1496
	 *
1497
	 * @return array
1498
	 */
1499
	public function getAccesses($object, $userId)
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $userId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1500
	{
1501
		return;
1502
		
1503
		
1504
		$xml = '<?xml version="1.0" encoding="utf-8" ?>
0 ignored issues
show
Unused Code introduced by
$xml = '<?xml version="1...op> </D:propfind>'; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1505
				<D:propfind xmlns:D="DAV:">
1506
				<D:prop>
1507
				<D:current-user-privilege-set/>
1508
				</D:prop>
1509
				</D:propfind>';
1510
		if (!is_string($object)) {
1511
			$objectUrl = $object->getUrl();
1512
		} else {
1513
			$objectUrl = $object;
1514
		}
1515
		try {
1516
			$this->caldavClient->SetDepth(0);
1517
			$this->caldavClient->DoXMLRequest('PROPFIND', $xml, $objectUrl);
1518
		} catch(Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1519
1520
		}
1521
1522
1523
		$dom = new DOMDocument();
1524
		$dom->loadXML($this->caldavClient->xmlResponse);
1525
1526
1527
		//		HTTP/1.1 207 Multi-Status
1528
		//		Date: Thu, 09 Sep 2010 09:58:33 GMT
1529
		//		Server: Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny8 with Suhosin-Patch
1530
		//		X-Powered-By: PHP/5.2.6-1+lenny8
1531
		//		DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy, bind, calendar-auto-schedule
1532
		//		ETag: "b4449ea2bf72f5e850ec928688e197b7"
1533
		//		X-DAViCal-Version: DAViCal/0.9.9; DB/1.2.8
1534
		//		Content-Length: 1541
1535
		//		Vary: Accept-Encoding
1536
		//		Connection: close
1537
		//		Content-Type: text/xml; charset="utf-8"
1538
		//
1539
		//		<?xml version="1.0" encoding="utf-8" ? >
1540
		//		<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
1541
		//		 <response>
1542
		//		  <href>/davical/caldav.php/laucho/home/</href>
1543
		//		  <propstat>
1544
		//		   <prop>
1545
		//		    <current-user-privilege-set>
1546
		//		     <privilege>
1547
		//		      <all/>
1548
		//		     </privilege>
1549
		//		     <privilege>
1550
		//		      <read/>
1551
		//		     </privilege>
1552
		//		     <privilege>
1553
		//		      <unlock/>
1554
		//		     </privilege>
1555
		//		     <privilege>
1556
		//		      <read-acl/>
1557
		//		     </privilege>
1558
		//		     <privilege>
1559
		//		      <read-current-user-privilege-set/>
1560
		//		     </privilege>
1561
		//		     <privilege>
1562
		//		      <write-acl/>
1563
		//		     </privilege>
1564
		//		     <privilege>
1565
		//		      <C:read-free-busy/>
1566
		//		     </privilege>
1567
		//		     <privilege>
1568
		//		      <write/>
1569
		//		     </privilege>
1570
		//		     <privilege>
1571
		//		      <write-properties/>
1572
		//		     </privilege>
1573
		//		     <privilege>
1574
		//		      <write-content/>
1575
		//		     </privilege>
1576
		//		     <privilege>
1577
		//		      <bind/>
1578
		//		     </privilege>
1579
		//		     <privilege>
1580
		//		      <unbind/>
1581
		//		     </privilege>
1582
		//		     <privilege>
1583
		//		      <C:schedule-deliver/>
1584
		//		     </privilege>
1585
		//		     <privilege>
1586
		//		      <C:schedule-deliver-invite/>
1587
		//		     </privilege>
1588
		//		     <privilege>
1589
		//		      <C:schedule-deliver-reply/>
1590
		//		     </privilege>
1591
		//		     <privilege>
1592
		//		      <C:schedule-query-freebusy/>
1593
		//		     </privilege>
1594
		//		     <privilege>
1595
		//		      <C:schedule-send/>
1596
		//		     </privilege>
1597
		//		     <privilege>
1598
		//		      <C:schedule-send-invite/>
1599
		//		     </privilege>
1600
		//		     <privilege>
1601
		//		      <C:schedule-send-reply/>
1602
		//		     </privilege>
1603
		//		     <privilege>
1604
		//		      <C:schedule-send-freebusy/>
1605
		//		     </privilege>
1606
		//		    </current-user-privilege-set>
1607
		//		   </prop>
1608
		//		   <status>HTTP/1.1 200 OK</status>
1609
		//		  </propstat>
1610
		//		 </response>
1611
		//		</multistatus>
1612
1613
		$xpath = new DOMXPath($dom);
1614
1615
		$xpath->registerNamespace('D', 'DAV:');
1616
1617
		$entries = $xpath->query('//D:multistatus/D:response/D:propstat/D:prop/D:current-user-privilege-set/D:privilege/*');
1618
1619
1620
		$accesses = array();
1621
		//BAB_CAL_ACCESS_VIEW | BAB_CAL_ACCESS_UPDATE | BAB_CAL_ACCESS_FULL | BAB_CAL_ACCESS_SHARED_UPDATE
1622
		foreach ($entries as $entry) {
1623
1624
    		switch ($entry->tagName) {
1625
    			case 'all':
1626
    				$accesses[BAB_CAL_ACCESS_FULL] = BAB_CAL_ACCESS_FULL;
1627
    				break;
1628
    			case 'write':
1629
    				$accesses[BAB_CAL_ACCESS_UPDATE] = BAB_CAL_ACCESS_UPDATE;
1630
    				break;
1631
    			case 'read':
1632
    				$accesses[BAB_CAL_ACCESS_VIEW] = BAB_CAL_ACCESS_VIEW;
1633
    				break;
1634
    		}
1635
		}
1636
1637
		return $accesses;
1638
	}
1639
1640
	/**
1641
	 * The default caldav server url if no other is explicitely specified.
1642
	 * @deprecated
1643
	 * @return string
1644
	 */
1645
	public function getDefaultServerUrl()
1646
	{
1647
		return '';
1648
	}
1649
1650
1651
1652
1653
	/**
1654
	 *
1655
	 * @param int	$userId
1656
	 * @param int	$serverId
1657
	 *
1658
	 * @return Func_CalendarBackend_Caldav
1659
	 */
1660 1
	public function setServerId($userId, $serverId)
1661
	{
1662 1
		$registry = bab_getRegistryInstance();
1663 1
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1664 1
		$registry->setKeyValue('server', $serverId);
1665 1
	}
1666
	
1667
	
1668
	
1669
	
1670
1671
1672
1673
1674
	/**
1675
	 * The default url for any user's personal calendar
1676
	 * %u will be replaced by ovidentia nickname
1677
	 *
1678
	 * @return string
1679
	 */
1680 8
	public function getDefaultUserCalendarUrl()
1681
	{
1682 8
		$registry = bab_getRegistryInstance();
1683 8
		$registry->changeDirectory('/LibCaldav');
1684 8
		$path = $registry->getValue('defaultUserCalendarUrl', '');
1685 8
		return $path;
1686
	}
1687
1688
1689
1690
1691
	/**
1692
	 *
1693
	 *
1694
	 * @param string	$path
1695
	 *
1696
	 * @return Func_CalendarBackend_Caldav
1697
	 */
1698
	public function setDefaultUserCalendarUrl($path)
1699
	{
1700
		$registry = bab_getRegistryInstance();
1701
		$registry->changeDirectory('/LibCaldav');
1702
		$registry->setKeyValue('defaultUserCalendarUrl', $path);
1703
	}
1704
1705
1706
1707
1708
	/**
1709
	 * The caldav admin identifier
1710
	 *
1711
	 * @return string
1712
	 */
1713
	public function getAdminIdentifier()
1714
	{
1715
		$registry = bab_getRegistryInstance();
1716
		$registry->changeDirectory('/LibCaldav');
1717
		$identifier = $registry->getValue('adminIdentifier', '');
1718
		return $identifier;
1719
	}
1720
1721
1722
1723
1724
	/**
1725
	 *
1726
	 *
1727
	 * @param string	$identifier
1728
	 *
1729
	 * @return Func_CalendarBackend_Caldav
1730
	 */
1731
	public function setAdminIdentifier($identifier)
1732
	{
1733
		$registry = bab_getRegistryInstance();
1734
		$registry->changeDirectory('/LibCaldav');
1735
		$registry->setKeyValue('adminIdentifier', $identifier);
1736
	}
1737
1738
1739
1740
1741
	/**
1742
	 * The caldav admin password
1743
	 *
1744
	 * @return string
1745
	 */
1746
	public function getAdminPassword()
1747
	{
1748
		$registry = bab_getRegistryInstance();
1749
		$registry->changeDirectory('/LibCaldav');
1750
		$password = $registry->getValue('adminPassword', '');
1751
		return $password;
1752
	}
1753
1754
1755
1756
1757
	/**
1758
	 *
1759
	 *
1760
	 * @param string	$password
1761
	 *
1762
	 * @return Func_CalendarBackend_Caldav
1763
	 */
1764
	public function setAdminPassword($password)
1765
	{
1766
		$registry = bab_getRegistryInstance();
1767
		$registry->changeDirectory('/LibCaldav');
1768
		$registry->setKeyValue('adminPassword', $password);
1769
	}
1770
	
1771
	
1772
	
1773
	/**
1774
	 * Set if backend is used as a storage backend (allow user switching)
1775
	 *
1776
	 * @param bool		$status
1777
	 *
1778
	 * @return Func_CalendarBackend_Caldav
1779
	 */
1780
	public function setStorageBackend($status)
1781
	{
1782
		$registry = bab_getRegistryInstance();
1783
		$registry->changeDirectory('/LibCaldav');
1784
		$registry->setKeyValue('StorageBackend', (bool) $status);
1785
	}
1786
1787
	
1788
	
1789 1
	public function getStorageBackend()
1790
	{
1791 1
		$registry = bab_getRegistryInstance();
1792 1
		$registry->changeDirectory('/LibCaldav');
1793 1
		return $registry->getValue('StorageBackend', true);
1794
	}
1795
	
1796
	
1797
	
1798
	public function setVerifyPeer($status)
1799
	{
1800
	    $registry = bab_getRegistryInstance();
1801 8
	    $registry->changeDirectory('/LibCaldav');
1802
	    $registry->setKeyValue('verify_peer', (bool) $status);
1803 8
	}
1804
	
1805
	
1806
	
1807
	public function getVerifyPeer()
1808
	{
1809
	    $registry = bab_getRegistryInstance();
1810
	    $registry->changeDirectory('/LibCaldav');
1811 8
	    return $registry->getValue('verify_peer', true);
1812
	}
1813 8
	
1814 8
	/**
1815 8
	 * test if personnal calendar use a per user configuration
1816 8
	 * @return bool
1817 8
	 */
1818
	public function configByUser()
1819
	{
1820
		return ('' === $this->getDefaultUserCalendarUrl());
1821
	}
1822 8
	
1823
	
1824
	/**
1825
	 * Get username and password to use for personnal calendar
1826
	 * @return array
1827 8
	 */
1828
	public function getPersonalCalendarCredentials($userId)
1829 8
	{
1830 8
		if ($this->configByUser())
1831 8
		{
1832
			$nickname = $this->getUserIdentifier($userId);
1833 8
			$password = $this->getUserPassword($userId);
1834
		} else {
1835 8
			$nickname = $this->getAdminIdentifier();
1836 8
			$password = $this->getAdminPassword();
1837
		}
1838
		
1839
		return array($nickname, $password);
1840 8
	}
1841
	
1842
	
1843
	
1844 8
	public function getPersonnalCalendarUrl($userId)
1845
	{
1846
		if ($this->configByUser())
1847
		{
1848
			$relativePath = $this->getUserCalendarPath($userId);
1849
			
1850
			$url = $this->getServerUrl($userId) . $relativePath;
1851
			
1852
			if (empty($url))
1853
			{
1854
				throw new Exception(sprintf('Empty calendar url for user %s', bab_getUserName($userId)));
1855
			}
1856
			
1857
			if (substr($url, -1, 1) !== '/') {
1858
				$url .= '/';
1859
			}
1860
			
1861
			return $url;
1862
			
1863 8
			
1864
		} else {
1865 8
			
1866 8
			$calendarPathTemplate = $this->getDefaultUserCalendarUrl();
1867 8
			return $this->processCalendarPathTemplate($calendarPathTemplate, bab_getUserNickname($userId));
1868 8
		}
1869
	}
1870
1871 8
1872
1873
	/**
1874
	 * Returns the stored caldav identifier for ovidentia user $userId.
1875
	 *
1876
	 * @param int $userId The ovidentia user id.
1877
	 *
1878
	 * @return string
1879
	 */
1880
	public function getUserIdentifier($userId)
1881
	{
1882
		$registry = bab_getRegistryInstance();
1883
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1884
		$identifier = $registry->getValue('identifier');
1885
		if (!isset($identifier)) {
1886
			return bab_getUserNickname($userId);
1887 1
		}
1888
		return $identifier;
1889 1
	}
1890 1
1891 1
1892 1
1893
1894
	/**
1895
	 * Sets the caldav identifier for ovidentia user $userId.
1896
	 *
1897
	 * @param string	$identifier
1898
	 *
1899
	 * @param int    $userId     The ovidentia user id.
1900
	 * @param string $identifier The caldav user identifier.
1901
	 *
1902
	 * @return Func_CalendarBackend_Caldav
1903
	 */
1904 8
	public function setUserIdentifier($userId, $identifier)
1905
	{
1906 8
		$registry = bab_getRegistryInstance();
1907 8
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1908 8
		$registry->setKeyValue('identifier', $identifier);
1909 8
	}
1910
1911
1912
1913
1914
	/**
1915
	 * Returns the stored caldav password for ovidentia user $userId.
1916
	 *
1917
	 * @param int $userId The ovidentia user id.
1918
	 *
1919
	 * @return string
1920
	 */
1921
	public function getUserPassword($userId)
1922
	{
1923
		$registry = bab_getRegistryInstance();
1924
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1925 1
		$password = $registry->getValue('password', '');
1926
		return $password;
1927 1
	}
1928 1
1929 1
1930 1
1931
1932
	/**
1933
	 * Sets the caldav password for ovidentia user $userId.
1934
	 *
1935
	 * @param string	$password
1936
	 *
1937
	 * @param int    $userId     The ovidentia user id.
1938
	 * @param string $identifier The caldav user identifier.
0 ignored issues
show
Bug introduced by
There is no parameter named $identifier. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1939
	 *
1940
	 * @return Func_CalendarBackend_Caldav
1941
	 */
1942 8
	public function setUserPassword($userId, $password)
1943
	{
1944 8
		$registry = bab_getRegistryInstance();
1945 8
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1946 8
		$registry->setKeyValue('password', $password);
1947
	}
1948 8
1949
1950 8
1951 8
1952 8
	/**
1953
	 * Returns the relative caldav path to the specified user's personal calendar.
1954
	 *
1955 8
	 * @param int $userId The ovidentia user id.
1956 8
	 *
1957 8
	 * @return string
1958 8
	 */
1959 8
	public function getUserCalendarPath($userId)
1960
	{
1961
		$registry = bab_getRegistryInstance();
1962
		$registry->changeDirectory('/LibCaldav/Users/' . $userId);
1963
		$path = $registry->getValue('calendarPath');
1964
1965
		if (!isset($path)) {
1966
			
1967
			$id_server = $this->getUserServerId($userId);
1968
			if (!isset($id_server))
1969
			{
1970 8
				return null;
1971
			}
1972 8
			$server = $this->getServer($id_server);
1973 8
			$nickname = $this->getUserIdentifier($userId);
1974
			$path = $this->processCalendarPathTemplate($server['user_calendar_path'], $nickname);
1975
		}
1976 8
		return $path;
1977
	}
1978
	
1979 8
	/**
1980
	 * Process replacements in calendar path
1981
	 * 
1982
	 * @param	string	$calendarPathTemplate	ex : /%u/home
1983
	 * @param	string	$nickname				Nickname as typed by the user
1984
	 * 
1985
	 * @return string
1986
	 */
1987
	public function processCalendarPathTemplate($calendarPathTemplate, $nickname)
1988
	{
1989
		if (false !== $p = mb_strpos($nickname, '@'))
1990
		{
1991
			$n = mb_substr($nickname, 0, $p);
1992
		} else {
1993
			$n = $nickname;
1994
		}
1995
		
1996
		return str_replace(array('%u', '%n'), array($nickname, $n), $calendarPathTemplate);
1997
	}
1998
1999
2000
2001
2002
	/**
2003
	 * Get url of an option page for the backend, the page will be displayed in a popup window accessible for each user
2004
	 * from the calendar options.
2005
	 *
2006
	 * @return string
2007
	 */
2008
	public function getOptionsUrl()
2009
	{
2010
		$addon = bab_getAddonInfosInstance('LibCaldav');
2011
2012
		if (!$addon || !$this->configByUser())
2013
		{
2014
			return '';
2015
		}
2016
2017
		return $addon->getUrl().'userconfiguration';
2018
	}
2019
}
2020