├── .travis.yml ├── CHANGELOG.md ├── README.md ├── api └── local.php ├── appinfo ├── app.php ├── application.php ├── database.xml ├── info.xml ├── remote.php └── routes.php ├── audio ├── ring.mp3 └── ring.ogg ├── caldav.php ├── controller ├── calendarcontroller.php ├── calendarsettingscontroller.php ├── eventcontroller.php ├── exportcontroller.php ├── importcontroller.php ├── pagecontroller.php ├── publiccontroller.php ├── repeatcontroller.php └── taskscontroller.php ├── css ├── 3rdparty │ ├── chosen-sprite.png │ ├── chosen.css │ ├── colorPicker.css │ ├── fontello │ │ ├── LICENSE.txt │ │ ├── README.txt │ │ ├── config.json │ │ ├── css │ │ │ ├── animation.css │ │ │ ├── fontello-codes.css │ │ │ ├── fontello-embedded.css │ │ │ ├── fontello-ie7-codes.css │ │ │ ├── fontello-ie7.css │ │ │ └── fontello.css │ │ ├── demo.html │ │ └── font │ │ │ ├── fontello.eot │ │ │ ├── fontello.svg │ │ │ ├── fontello.ttf │ │ │ └── fontello.woff │ ├── fullcalendar-1.5.4.css │ ├── fullcalendar.css │ ├── fullcalendar.print-1.5.4.css │ ├── fullcalendar.print.css │ ├── images │ │ ├── colors.png │ │ └── trigger.png │ ├── jquery.miniColors.css │ ├── jquery.tagit.css │ ├── jquery.timepicker.css │ └── jquery.webui-popover.css ├── 404.css ├── authenticate.css ├── import.css ├── mobile.css ├── share.css └── style.css ├── db ├── calendardao.php ├── eventdao.php └── repeatdao.php ├── img ├── arrow.gif ├── arrow.png ├── calendarplus.png ├── calendarplus.svg ├── favicon.png ├── loading.gif └── upload.svg ├── js ├── 3rdparty │ ├── chosen.jquery.min.js │ ├── datepair.js │ ├── fullcalendar.js │ ├── fullcalendar.min.js │ ├── gcal.js │ ├── jquery.colorPicker.js │ ├── jquery.colorPicker.min.js │ ├── jquery.datepair.js │ ├── jquery.timepicker.js │ ├── jquery.webui-popover.js │ ├── jstz-1.0.4.min.js │ ├── printThis.js │ └── tag-it.js ├── alarm.js ├── app.js ├── geo.js ├── jquery.combobox.js ├── jquery.multi-autocomplete.js ├── jquery.nicescroll.min.js ├── jquery.scrollTo.min.js ├── jquery.ui.touch-punch.min.js ├── loaderimport.js ├── on-event.js ├── settings.js ├── share.config.js ├── share.js └── timepicker.js ├── l10n ├── de.js ├── de.json ├── de_DE.js └── de_DE.json ├── lib ├── activity.php ├── activitydata.php ├── alarm.php ├── app.php ├── calendar.php ├── connector │ ├── calendarconnector.php │ └── sabre │ │ ├── backend.php │ │ ├── calendar.php │ │ ├── calendarobject.php │ │ ├── calendarroot.php │ │ ├── schedulingsupport.php │ │ └── usercalendars.php ├── export.php ├── hooks.php ├── import.php ├── object.php ├── search │ ├── provider.php │ └── result.php ├── share │ ├── backend │ │ ├── calendar.php │ │ └── event.php │ └── shareconnector.php ├── vobject.php └── vobject │ └── stringpropertycategories.php ├── service ├── contactsintegration.php ├── objectparser.php └── utility.php ├── share.php ├── shareevent.php ├── templates ├── calendar.php ├── navigationleft.php ├── part.editcalendar.php ├── part.editevent.php ├── part.eventform.php ├── part.import.php ├── part.newevent.php ├── part.showevent.php ├── part.subscriber.php ├── public.php └── publicevent.php └── tests ├── bootstrap.php ├── clover.xml ├── db └── calendardaotest.php └── phpunit.xml /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: php 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7 8 | - hhvm 9 | 10 | env: 11 | global: 12 | - CORE_BRANCH=master 13 | - APPNAME=calendarplus 14 | matrix: 15 | - DB=sqlite 16 | 17 | branches: 18 | only: 19 | - master 20 | - /^stable\d+(\.\d+)?$/ 21 | 22 | before_install: 23 | - wget https://raw.githubusercontent.com/owncloud/administration/master/travis-ci/before_install.sh 24 | - bash ./before_install.sh $APPNAME $CORE_BRANCH $DB 25 | - cd ../core 26 | - php occ app:enable $APPNAME 27 | 28 | before_script: 29 | - cd apps/$APPNAME 30 | 31 | script: 32 | # Test lint 33 | - find . -name \*.php -not -path './vendor/*' -exec php -l "{}" \; 34 | 35 | # Run phpunit tests 36 | - cd tests 37 | - phpunit --configuration phpunit.xml 38 | 39 | matrix: 40 | include: 41 | - php: 5.4 42 | env: DB=mysql 43 | - php: 5.4 44 | env: DB=pgsql 45 | 46 | allow_failures: 47 | - php: hhvm 48 | - php: 7 49 | fast_finish: true 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.1.0 4 | - fix for [issue 153](https://github.com/libasys/calendarplus/issues/153) 5 | - added import subscription dialog 6 | 7 | ## 1.0.9 8 | - fix for import progress & enhancement 9 | - birthday sync per caldav possible with contacts+ 1.0.6 or higher 10 | - nicer birthday events 11 | - tagmanager removed 12 | - added new,edit,delete tags directly on gui 13 | - smaller css & js fixes 14 | 15 | ## 1.0.8 16 | - Integration Contactsmanager for location autocomplete 17 | - fix for popover 18 | - several small css fixes 19 | - several small js fixes 20 | - several fixes client/server side 21 | ## 1.0.7 22 | - fix for colorchooser problem 23 | - fix for reminder description 24 | - fix for shared calendar with groups 25 | - fix for untranslated strings 26 | - fix for import check duplicate events 27 | 28 | ## 1.0.6 29 | - GUI Design enhancements 30 | - listview now for complete month 31 | - smoother dayview 32 | - settings now on left sidebar bottom 33 | - added settings field for date format choose (greetings to our american friends) 34 | - added more menu for calendars 35 | - better time slider for new/ edit events like google & icloud 36 | - remove headbar switch to left side 37 | - fix for reminder parser (alarm "just in time") 38 | - smaller js & css fixes 39 | - rework on serverside core calendar+ 40 | 41 | 42 | ## 1.0.2 43 | - several css bugfixe 44 | - reminder parsing fix 45 | - contactsplus birthday calendar support 46 | - toolTip added for better description 47 | 48 | ## 1.0.1 49 | - Bugfix for postgresql db 50 | - added sharing feature for subscribed calendars 51 | - some small Bugfixes on mobile view 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Calendar+ App 2 | ============= 3 | 4 | Maintainer: 5 | =========== 6 | Sebastian Döll 7 | 8 | Version Info: 9 | ============ 10 | 1.1.1 11 | 12 | Setup Info: 13 | =========== 14 | This version of the calendar app is only compatible with upcoming ownCloud Version 8.1 or later! 15 | 16 | The old sharees won't work if you export the database shema from an old installation of ownCloud! 17 | You can use *calendarplus* parallel to your current calendar application: it works on its own tables, so you can thoroughly test it without the risk of messing up your existing calendars. If you want to use your old calendar events, you have to export them using a desktop calendar application or the web interface, and import them to *calendarplus.* It won't work to simply export the database from an older installation of ownCloud. 18 | 19 | 20 | Installation: 21 | ============= 22 | Download the zip file and rename folder from calendarplus-master to calendarplus! Upload the app to your apps directory and activate it on the apps settings page! 23 | 24 | Import 25 | ====== 26 | - 1. Method 27 | ICS File per Drag & Drop on the calendar+ app, import dialog will prompt 28 | - 2. Method 29 | Upload the ICS File on the files app and then click on the uploaded ICS File and the import dialog will prompt 30 | 31 | Important: 32 | If the default calendar app is enabled, too, then you have to disable the calendar app for import from the files app! 33 | 34 | Caldav Addresses: 35 | ================== 36 | The syncing URL is shown up in the settings dialog (right sidebar top corner) 37 | 38 | New Features: 39 | ============= 40 | - Sharing calendar via public link 41 | - Sharing events via public link 42 | - Sharing of subscribed calendars 43 | - Customizing public sharings via link on share.config.js 44 | - Exdate for repeating events (means you can delete a single event of a series) 45 | - Calendar subscription 46 | - Birthday import from contacts+/ snyc per caldav 47 | - Reminder support 48 | - Repeat GUI changed 49 | - New/ Edit event GUI changed 50 | - Timezone support (daylight/ standard) for repeating events (if supported!) 51 | - Working Search function with additional search options like: today, tomorrow 52 | - activity log 53 | 54 | -------------------------------------------------------------------------------- /api/local.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | namespace OCA\CalendarPlus\API; 24 | 25 | class Local { 26 | 27 | /** 28 | * get all shares 29 | * 30 | * @param array $params option 'file' to limit the result to a specific file/folder 31 | * @return \OC_OCS_Result share information 32 | */ 33 | 34 | private static $sItems = array(); 35 | 36 | 37 | 38 | public static function getAllShares($params) { 39 | 40 | $sMode = 'calpl'; 41 | 42 | if(isset($_GET['mode'])){ 43 | $sMode=$_GET['mode']; 44 | } 45 | 46 | if (isset($_GET['shared_with_me']) && $_GET['shared_with_me'] !== 'false') { 47 | return self::getItemsSharedWithMe($sMode); 48 | } 49 | //Show all reshared options 50 | $bReshare=false; 51 | if(isset($_GET['reshares']) && $_GET['reshares'] === 'true'){ 52 | $bReshare=true; 53 | } 54 | 55 | //\OCP\Util::writeLog('share','Found MODE:'.$sMode,\OCP\Util::DEBUG); 56 | 57 | $shares = \OCP\Share::getItemShared($sMode, null); 58 | 59 | if ($shares === false) { 60 | return new \OC_OCS_Result(null, 404, 'could not get shares'); 61 | } else { 62 | $reshares=array(); 63 | foreach ($shares as &$share) { 64 | if($bReshare === true){ 65 | self::checkReShare($share['item_source'],$sMode); 66 | } 67 | } 68 | 69 | if(count(self::$sItems)>0){ 70 | $shares=array_merge($shares, self::$sItems); 71 | } 72 | 73 | return new \OC_OCS_Result($shares); 74 | } 75 | 76 | } 77 | /** 78 | * resolves reshares down to the last real share 79 | * @param array $linkItem 80 | * @return array file owner 81 | */ 82 | public static function checkReShare($itemsource,$sMode){ 83 | 84 | $getReshares = \OCP\DB::prepare("SELECT * FROM `*PREFIX*share` WHERE `item_source` = ? AND `uid_owner` != ? AND `item_type` = ? AND `parent` != 'NULL' "); 85 | //$item = $query->execute(array($id,$sMode))->fetchRow(); 86 | $items = $getReshares->execute(array($itemsource, \OCP\User::getUser(), $sMode))->fetchAll(); 87 | 88 | foreach($items as $reshare){ 89 | $reshare['share_type'] = (int) $reshare['share_type']; 90 | 91 | if (isset($reshare['share_with']) && $reshare['share_with'] !== '') { 92 | $reshare['share_with_displayname'] = \OCP\User::getDisplayName($reshare['share_with']); 93 | } 94 | self::$sItems[$reshare['id']]=$reshare; 95 | } 96 | 97 | 98 | } 99 | 100 | /** 101 | * get share information for a given share 102 | * 103 | * @param array $params which contains a 'id' 104 | * @return \OC_OCS_Result share information 105 | */ 106 | public static function getShare($params) { 107 | 108 | $s = self::getShareFromId($params['id']); 109 | 110 | //Show all reshared options 111 | $bReshare=false; 112 | if(isset($_GET['reshares']) && $_GET['reshares'] === 'true'){ 113 | $bReshare=true; 114 | } 115 | 116 | 117 | 118 | $params['itemSource'] = $s['item_source']; 119 | $params['itemType'] = $s['item_type']; 120 | $params['itemTarget'] = $s['item_target']; 121 | $params['specificShare'] = true; 122 | $params['reshare']=$bReshare; 123 | 124 | 125 | return self::collectShares($params); 126 | } 127 | 128 | /** 129 | * collect all share information, either of a specific share or all 130 | * shares for a given path 131 | * @param array $params 132 | * @return \OC_OCS_Result 133 | */ 134 | private static function collectShares($params) { 135 | 136 | $itemSource = $params['itemSource']; 137 | $itemType = $params['itemType']; 138 | $getSpecificShare = isset($params['specificShare']) ? $params['specificShare'] : false; 139 | 140 | if ($itemSource !== null) { 141 | $shares = \OCP\Share::getItemShared($itemType, $itemSource); 142 | $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $itemSource); 143 | // if a specific share was specified only return this one 144 | if ($getSpecificShare === true) { 145 | $shareEE=array(); 146 | foreach ($shares as $share) { 147 | if ($share['id'] === (int) $params['id']) { 148 | 149 | $shareEE[] = $share; 150 | 151 | break; 152 | } 153 | } 154 | 155 | if($params['reshare'] === true){ 156 | self::checkReShare($itemSource,$itemType); 157 | 158 | if(count(self::$sItems)>0){ 159 | $shares=array_merge($shareEE, self::$sItems); 160 | } 161 | } 162 | 163 | } 164 | 165 | if ($receivedFrom) { 166 | foreach ($shares as $key => $share) { 167 | $shares[$key]['received_from'] = $receivedFrom['uid_owner']; 168 | $shares[$key]['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); 169 | } 170 | } 171 | } else { 172 | $shares = null; 173 | } 174 | 175 | if ($shares === null || empty($shares)) { 176 | return new \OC_OCS_Result(null, 404, 'share doesn\'t exist'); 177 | } else { 178 | return new \OC_OCS_Result($shares); 179 | } 180 | } 181 | 182 | /** 183 | * get files shared with the user 184 | * @return \OC_OCS_Result 185 | */ 186 | private static function getItemsSharedWithMe($sMode) { 187 | try { 188 | $shares = \OCP\Share::getItemsSharedWith($sMode); 189 | 190 | $result = new \OC_OCS_Result($shares); 191 | } catch (\Exception $e) { 192 | $result = new \OC_OCS_Result(null, 403, $e->getMessage()); 193 | } 194 | 195 | return $result; 196 | 197 | } 198 | 199 | 200 | /** 201 | * get some information from a given share 202 | * @param int $shareID 203 | * @return array with: item_source, share_type, share_with, item_type, permissions 204 | */ 205 | private static function getShareFromId($shareID) { 206 | $sql = 'SELECT `id`,`item_source`, `share_type`, `share_with`, `item_type`, `item_target`, `permissions`, `stime` FROM `*PREFIX*share` WHERE `id` = ?'; 207 | $args = array($shareID); 208 | $query = \OCP\DB::prepare($sql); 209 | $result = $query->execute($args); 210 | 211 | if (\OCP\DB::isError($result)) { 212 | \OCP\Util::writeLog('calendarplus', \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); 213 | return null; 214 | } 215 | if ($share = $result->fetchRow()) { 216 | 217 | 218 | return $share; 219 | } 220 | 221 | return null; 222 | 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /appinfo/app.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | namespace OCA\CalendarPlus\AppInfo; 24 | 25 | $app = new Application(); 26 | $c = $app->getContainer(); 27 | 28 | $appName = (string)$c->getAppName(); 29 | // add an navigation entry 30 | $navigationEntry = function () use ($c) { 31 | return [ 32 | 'id' => $c->getAppName(), 33 | 'order' => 1, 34 | 'name' => $c->query('L10N')->t('Calendar+'), 35 | 'href' => $c->query('URLGenerator')->linkToRoute($c->getAppName().'.page.index'), 36 | 'icon' => $c->query('URLGenerator')->imagePath($c->getAppName(), 'calendarplus.svg'), 37 | ]; 38 | }; 39 | $c->getServer()->getNavigationManager()->add($navigationEntry); 40 | 41 | //upcoming version search for 8.2 perhaps patch https://github.com/owncloud/core/pull/17339/files 42 | //\OC::$server->getSearch()->registerProvider('OCA\CalendarPlus\Search\Provider', array('app' =>$appName,'apps' =>array('tasksplus'))); 43 | \OC::$server->getSearch()->registerProvider('OCA\CalendarPlus\Search\Provider', array('app' =>$appName)); 44 | 45 | if(\OC::$server->getAppManager()->isEnabledForUser('activity')){ 46 | \OC::$server->getActivityManager()->registerExtension(function() { 47 | return new \OCA\CalendarPlus\Activity(); 48 | }); 49 | } 50 | 51 | \OCA\CalendarPlus\Hooks::register(); 52 | 53 | \Sabre\VObject\Component\VCalendar::$propertyMap['CATEGORIES'] = '\OCA\CalendarPlus\VObject\StringPropertyCategories'; 54 | 55 | \OCP\Util::addScript($appName,'alarm'); 56 | 57 | if (\OCP\User::isLoggedIn() && !\OCP\App::isEnabled('calendar')) { 58 | $request = $c->query('Request'); 59 | if (isset($request->server['REQUEST_URI'])) { 60 | 61 | $url = $request->server['REQUEST_URI']; 62 | if (preg_match('%index.php/apps/files(/.*)?%', $url) || preg_match('%index.php/s/(/.*)?%', $url)) { 63 | \OCP\Util::addScript($appName,'loaderimport'); 64 | \OCP\Util::addStyle($appName, '3rdparty/colorPicker'); 65 | \OCP\Util::addscript($appName, '3rdparty/jquery.colorPicker'); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /appinfo/info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | calendarplus 4 | Calendar Plus 5 | AGPL 6 | Döll Sebastian, Georg Ehrke, Bart Visscher, Jakob Sack 7 | false 8 | Calendar Plus with CalDAV support and public sharing via link, activity log, reminder alerts and many more features 9 | CalendarPlus 10 | https://github.com/libasys/ownCloud-8.1-CalendarPlus 11 | 1.1.1 12 | productivity 13 | 14 | 15 | appinfo/remote.php 16 | 17 | 18 | share.php 19 | shareevent.php 20 | 21 | 22 | 23 | 24 | 25 | 170560 26 | 27 | -------------------------------------------------------------------------------- /appinfo/remote.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | 24 | /* 25 | if(substr(\OC::$server->getRequest()->getRequestUri(),0,strlen(OC_App::getAppWebPath('calendar').'/caldav.php')) == OC_App::getAppWebPath('calendar'). '/caldav.php') { 26 | $baseuri = OC_App::getAppWebPath('calendar').'/caldav.php'; 27 | }*/ 28 | 29 | // only need authentication apps 30 | 31 | 32 | 33 | // Backends 34 | //$principalBackend = new OCA\Calendar\Connector\Sabre\Principal(); 35 | 36 | $authBackend = new \OC\Connector\Sabre\Auth(); 37 | 38 | $principalBackend = new \OC\Connector\Sabre\Principal( 39 | \OC::$server->getConfig(), 40 | \OC::$server->getUserManager() 41 | ); 42 | 43 | 44 | 45 | $caldavBackend = new OCA\CalendarPlus\Connector\Sabre\Backend(); 46 | 47 | 48 | // Root nodes 49 | $Sabre_CalDAV_Principal_Collection = new \Sabre\CalDAV\Principal\Collection($principalBackend); 50 | $Sabre_CalDAV_Principal_Collection->disableListing = true; // Disable listening 51 | 52 | $calendarRoot = new OCA\CalendarPlus\Connector\Sabre\CalendarRoot($principalBackend, $caldavBackend); 53 | $calendarRoot->disableListing = true; // Disable listening 54 | 55 | $nodes = array( 56 | $Sabre_CalDAV_Principal_Collection, 57 | $calendarRoot, 58 | ); 59 | 60 | 61 | // Fire up server 62 | $server = new \Sabre\DAV\Server($nodes); 63 | $server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); 64 | $server->setBaseUri($baseuri); 65 | // Add plugins 66 | $defaults = new OC_Defaults(); 67 | $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin()); 68 | $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); 69 | $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend,$defaults->getName())); 70 | $server->addPlugin(new \Sabre\CalDAV\Plugin()); 71 | $server->addPlugin(new \Sabre\DAVACL\Plugin()); 72 | $server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); 73 | //$server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin()); 74 | //$server->addPlugin( 75 | // new \Sabre\CalDAV\Schedule\IMipPlugin('sebastian.doell@owncms.de') 76 | //); 77 | $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('caldav', \OC::$server->getLogger())); 78 | $server->addPlugin(new \OC\Connector\Sabre\AppEnabledPlugin( 79 | 'calendarplus', 80 | \OC::$server->getAppManager() 81 | )); 82 | 83 | // And off we go! 84 | $server->exec(); 85 | 86 | -------------------------------------------------------------------------------- /appinfo/routes.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | namespace OCA\CalendarPlus; 24 | 25 | 26 | use \OCA\CalendarPlus\AppInfo\Application; 27 | 28 | $application = new Application(); 29 | $application->registerRoutes($this, ['routes' => [ 30 | ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], 31 | ['name' => 'public#index', 'url' => '/s/{token}', 'verb' => 'GET'], 32 | ['name' => 'public#index','url' => '/s/{token}', 'verb' => 'POST', 'postfix' => 'auth'], 33 | ['name' => 'public#getGuestSettingsCalendar', 'url' => '/publicgetguestsettingscalendar', 'verb' => 'GET'], 34 | ['name' => 'public#getGuessTimeZone', 'url' => '/publicgetguesstimezone', 'verb' => 'POST'], 35 | ['name' => 'public#getDateTimeFormat', 'url' => '/publicgetdatetimeformat', 'verb' => 'POST'], 36 | ['name' => 'public#getEventsPublic', 'url' => '/geteventspublic', 'verb' => 'GET'], 37 | ['name' => 'event#getEvents', 'url' => '/getevents', 'verb' => 'GET'], 38 | ['name' => 'public#changeViewCalendarPublic', 'url' => '/changeviewcalendarpublic', 'verb' => 'POST'], 39 | ['name' => 'calendar#changeViewCalendar', 'url' => '/changeviewcalendar', 'verb' => 'POST'], 40 | ['name' => 'event#getReminderEvents', 'url' => '/getreminderevents', 'verb' => 'POST'], 41 | ['name' => 'event#getEventsDayView', 'url' => '/geteventsdayview', 'verb' => 'POST'], 42 | ['name' => 'event#addCategorieToEvent', 'url' => '/addcategorietoevent', 'verb' => 'POST'], 43 | ['name' => 'event#addSharedEvent', 'url' => '/addsharedevent', 'verb' => 'POST'], 44 | ['name' => 'event#addSubscriberEvent', 'url' => '/addsubscriberevent', 'verb' => 'POST'], 45 | ['name' => 'event#deleteExdateEvent', 'url' => '/deleteexdateevent', 'verb' => 'POST'], 46 | ['name' => 'event#deleteSingleRepeatingEvent', 'url' => '/deletesinglerepeatingevent', 'verb' => 'POST'], 47 | ['name' => 'event#deleteEvent', 'url' => '/deleteevent', 'verb' => 'POST'], 48 | ['name' => 'event#moveEvent', 'url' => '/moveevent', 'verb' => 'POST'], 49 | ['name' => 'event#resizeEvent', 'url' => '/resizeevent', 'verb' => 'POST'], 50 | ['name' => 'event#getShowEvent', 'url' => '/getshowevent', 'verb' => 'POST'], 51 | ['name' => 'event#getEditFormEvent', 'url' => '/geteditformevent', 'verb' => 'GET'], 52 | ['name' => 'event#getQuickInfoEvent', 'url' => '/getquickinfoevent', 'verb' => 'GET'], 53 | ['name' => 'event#editEvent', 'url' => '/editevent', 'verb' => 'POST'], 54 | ['name' => 'event#getNewFormEvent', 'url' => '/getnewformevent', 'verb' => 'GET'], 55 | ['name' => 'event#autoComplete', 'url' => '/autocompletelocation', 'verb' => 'GET'], 56 | ['name' => 'event#newEvent', 'url' => '/newevent', 'verb' => 'POST'], 57 | ['name' => 'event#sendEmailEventIcs', 'url' => '/sendemaileventics', 'verb' => 'POST'], 58 | ['name' => 'calendarSettings#index', 'url' => '/calendarsettingsindex', 'verb' => 'GET'], 59 | ['name' => 'calendarSettings#setTimeZone', 'url' => '/calendarsettingssettimezone', 'verb' => 'POST'], 60 | ['name' => 'calendarSettings#setTimeFormat', 'url' => '/calendarsettingssettimeformat', 'verb' => 'POST'], 61 | ['name' => 'calendarSettings#setDateFormat', 'url' => '/calendarsettingssetdateformat', 'verb' => 'POST'], 62 | ['name' => 'calendarSettings#setFirstDay', 'url' => '/calendarsettingssetfirstday', 'verb' => 'POST'], 63 | ['name' => 'calendarSettings#timeZoneDectection', 'url' => '/calendarsettingstimezonedetection', 'verb' => 'POST'], 64 | ['name' => 'calendarSettings#reScanCal', 'url' => '/calendarsettingsrescancal', 'verb' => 'GET'], 65 | ['name' => 'calendarSettings#setTaskNavActive', 'url' => '/calendarsettingssettasknavactive', 'verb' => 'POST'], 66 | ['name' => 'calendarSettings#setCalendarNavActive', 'url' => '/calendarsettingssetcalendarnavactive', 'verb' => 'POST'], 67 | ['name' => 'calendarSettings#getUserSettingsCalendar', 'url' => '/calendarsettingsgetusersettingscalendar', 'verb' => 'GET'], 68 | ['name' => 'calendarSettings#saveUserViewSettings', 'url' => '/calendarsettingssaveuserview', 'verb' => 'POST'], 69 | ['name' => 'calendarSettings#getGuessTimeZoneUser', 'url' => '/calendarsettingsgetguesstimezoneuser', 'verb' => 'POST'], 70 | ['name' => 'calendar#getNewFormCalendar', 'url' => '/getnewformcalendar', 'verb' => 'GET'], 71 | ['name' => 'calendar#getEditFormCalendar', 'url' => '/geteditformcalendar', 'verb' => 'POST'], 72 | ['name' => 'calendar#newCalendar', 'url' => '/newcalendar', 'verb' => 'POST'], 73 | ['name' => 'calendar#editCalendar', 'url' => '/editcalendar', 'verb' => 'POST'], 74 | ['name' => 'calendar#deleteCalendar', 'url' => '/deletecalendar', 'verb' => 'POST'], 75 | ['name' => 'calendar#setActiveCalendar', 'url' => '/setactivecalendar', 'verb' => 'POST'], 76 | ['name' => 'calendar#setMyActiveCalendar', 'url' => '/setmyactivecalendar', 'verb' => 'POST'], 77 | ['name' => 'calendar#touchCalendar', 'url' => '/touchcalendar', 'verb' => 'POST'], 78 | ['name' => 'calendar#rebuildLeftNavigation', 'url' => '/rebuildleftnavigationcalendar', 'verb' => 'POST'], 79 | ['name' => 'calendar#refreshSubscribedCalendar', 'url' => '/refreshsubscribedcalendar', 'verb' => 'POST'], 80 | ['name' => 'calendar#checkImportUrl', 'url' => '/checkimporturl', 'verb' => 'POST'], 81 | ['name' => 'calendar#updateTag', 'url' => '/updatetag', 'verb' => 'GET'], 82 | ['name' => 'tasks#rebuildTaskViewRight', 'url' => '/rebuildtaskviewrightcalendar', 'verb' => 'POST'], 83 | ['name' => 'tasks#setCompletedTask', 'url' => '/setcompletedtaskcalendar', 'verb' => 'POST'], 84 | ['name' => 'import#getImportDialogTpl', 'url' => '/getimportdialogtplcalendar', 'verb' => 'POST'], 85 | ['name' => 'import#checkCalendarExists', 'url' => '/checkcalendarexistsimport', 'verb' => 'POST'], 86 | ['name' => 'import#importEvents', 'url' => '/importeventscalendar', 'verb' => 'POST'], 87 | ['name' => 'import#importEventsPerDrop', 'url' => '/importeventsperdropcalendar', 'verb' => 'POST'], 88 | ['name' => 'export#exportEvents', 'url' => '/exporteventscalendar', 'verb' => 'GET'], 89 | ] 90 | ]); 91 | 92 | 93 | \OCP\API::register('get', 94 | '/apps/calendarplus/api/v1/shares', 95 | array('\OCA\CalendarPlus\API\Local', 'getAllShares'), 96 | 'calendarplus'); 97 | \OCP\API::register('get', 98 | '/apps/calendarplus/api/v1/shares/{id}', 99 | array('\OCA\CalendarPlus\API\Local', 'getShare'), 100 | 'calendarplus'); 101 | -------------------------------------------------------------------------------- /audio/ring.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/audio/ring.mp3 -------------------------------------------------------------------------------- /audio/ring.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/audio/ring.ogg -------------------------------------------------------------------------------- /caldav.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | namespace OCA\CalendarPlus\Controller; 24 | 25 | 26 | use \OCA\CalendarPlus\App as CalendarApp; 27 | use \OCA\CalendarPlus\Calendar as CalendarCalendar; 28 | use \OCA\CalendarPlus\VObject; 29 | use \OCA\CalendarPlus\Object; 30 | use \OCA\CalendarPlus\Export; 31 | 32 | use \OCP\AppFramework\Controller; 33 | use \OCP\AppFramework\Http\JSONResponse; 34 | use \OCP\AppFramework\Http\DataDownloadResponse; 35 | use \OCP\IRequest; 36 | use \OCP\Share; 37 | use \OCP\IConfig; 38 | 39 | class ExportController extends Controller { 40 | 41 | private $userId; 42 | private $l10n; 43 | private $configInfo; 44 | 45 | public function __construct($appName, IRequest $request, $userId, $l10n, IConfig $settings) { 46 | parent::__construct($appName, $request); 47 | $this -> userId = $userId; 48 | $this->l10n = $l10n; 49 | $this->configInfo = $settings; 50 | } 51 | 52 | 53 | 54 | /** 55 | *@PublicPage 56 | * @NoCSRFRequired 57 | * 58 | */ 59 | public function exportEvents(){ 60 | $token = $this -> params('t'); 61 | $calid = null; 62 | $eventid = null; 63 | 64 | if (isset($token)) { 65 | 66 | $linkItem = \OCP\Share::getShareByToken($token, false); 67 | if (is_array($linkItem) && isset($linkItem['uid_owner']) && !isset($linkItem['share_with'])) { 68 | $rootLinkItem = \OCP\Share::resolveReShare($linkItem); 69 | 70 | if (isset($rootLinkItem['uid_owner'])) { 71 | \OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); 72 | if($linkItem['item_type'] === CalendarApp::SHARECALENDAR){ 73 | $sPrefix =CalendarApp::SHARECALENDARPREFIX; 74 | } 75 | if($linkItem['item_type'] === CalendarApp::SHAREEVENT){ 76 | $sPrefix = CalendarApp::SHAREEVENTPREFIX; 77 | } 78 | if($linkItem['item_type'] === CalendarApp::SHARETODO){ 79 | $sPrefix = CalendarApp::SHARETODOPREFIX; 80 | } 81 | 82 | $itemSource =CalendarApp::validateItemSource($linkItem['item_source'],$sPrefix); 83 | if($linkItem['item_type'] === CalendarApp::SHARECALENDAR){ 84 | $calid=$itemSource; 85 | } 86 | if($linkItem['item_type'] === CalendarApp::SHAREEVENT || $linkItem['item_type'] === CalendarApp::SHARETODO){ 87 | $eventid=$itemSource; 88 | } 89 | } 90 | } 91 | 92 | } 93 | else{ 94 | if (\OCP\User::isLoggedIn()) { 95 | 96 | $calid = $this -> params('calid'); 97 | $eventid = $this -> params('eventid'); 98 | 99 | 100 | } 101 | } 102 | 103 | if(!is_null($calid)) { 104 | //check shared calendar also 105 | $calendar = CalendarApp::getCalendar($calid, true, true); 106 | if(!$calendar) { 107 | $params = [ 108 | 'status' => 'error', 109 | ]; 110 | $response = new JSONResponse($params); 111 | return $response; 112 | } 113 | 114 | $name = str_replace(' ', '_', $calendar['displayname']) . '.ics'; 115 | $calendarEvents = Export::export($calid, Export::CALENDAR); 116 | 117 | $response = new DataDownloadResponse($calendarEvents, $name, 'text/calendar'); 118 | $response->addHeader('last-modified',$calendar['lastmodifieddate']); 119 | return $response; 120 | 121 | } 122 | if(!is_null($eventid)) { 123 | $data = CalendarApp::getEventObject($eventid, false); 124 | if(!$data) { 125 | $params = [ 126 | 'status' => 'error', 127 | ]; 128 | $response = new JSONResponse($params); 129 | return $response; 130 | } 131 | 132 | $name = str_replace(' ', '_', $data['summary']) . '.ics'; 133 | $singleEvent = Export::export($eventid, Export::EVENT); 134 | 135 | $response = new DataDownloadResponse($singleEvent, $name, 'text/calendar'); 136 | 137 | return $response; 138 | 139 | } 140 | 141 | } 142 | 143 | 144 | 145 | } -------------------------------------------------------------------------------- /controller/taskscontroller.php: -------------------------------------------------------------------------------- 1 | . 20 | * 21 | */ 22 | 23 | namespace OCA\CalendarPlus\Controller; 24 | 25 | 26 | use \OCA\CalendarPlus\App as CalendarApp; 27 | use \OCA\CalendarPlus\Calendar as CalendarCalendar; 28 | use \OCA\CalendarPlus\VObject; 29 | use \OCA\CalendarPlus\Object; 30 | 31 | use \OCA\TasksPlus\App as TasksApp; 32 | use \OCA\TasksPlus\Timeline; 33 | 34 | use \OCP\AppFramework\Controller; 35 | use \OCP\AppFramework\Http\JSONResponse; 36 | use \OCP\AppFramework\Http\TemplateResponse; 37 | use \OCP\IRequest; 38 | use \OCP\Share; 39 | use \OCP\IConfig; 40 | 41 | class TasksController extends Controller { 42 | 43 | private $userId; 44 | private $l10n; 45 | private $configInfo; 46 | 47 | public function __construct($appName, IRequest $request, $userId, $l10n, IConfig $settings) { 48 | parent::__construct($appName, $request); 49 | $this -> userId = $userId; 50 | $this->l10n = $l10n; 51 | $this->configInfo = $settings; 52 | } 53 | 54 | 55 | /** 56 | * @NoAdminRequired 57 | */ 58 | public function rebuildTaskViewRight() { 59 | 60 | if(\OC::$server->getAppManager()->isEnabledForUser('tasksplus')){ 61 | $calendars = CalendarCalendar::allCalendars($this -> userId, true); 62 | 63 | if( count($calendars) > 0 ) { 64 | $allowedCals=[]; 65 | 66 | foreach($calendars as $calInfo){ 67 | $isAktiv=(int)$calInfo['active']; 68 | if($this->configInfo->getUserValue($this -> userId, $this->appName, 'calendar_'.$calInfo['id']) !== ''){ 69 | $isAktiv= (int) $this->configInfo->getUserValue($this -> userId, $this->appName, 'calendar_'.$calInfo['id']); 70 | } 71 | if($isAktiv === 1){ 72 | $allowedCals[]=$calInfo; 73 | } 74 | } 75 | 76 | $cDataTimeLine=new Timeline(); 77 | $cDataTimeLine->setCalendars($allowedCals); 78 | $taskOutPutbyTime=$cDataTimeLine->generateAddonCalendarTodo(); 79 | 80 | $params=[ 81 | 'taskOutPutbyTime' => $taskOutPutbyTime, 82 | ]; 83 | 84 | $response = new TemplateResponse('tasksplus', 'calendars.tasks.list',$params, ''); 85 | 86 | return $response; 87 | } 88 | } 89 | 90 | } 91 | /** 92 | * @NoAdminRequired 93 | */ 94 | public function setCompletedTask() { 95 | 96 | $id = $this -> params('id'); 97 | $checked = $this -> params('checked'); 98 | 99 | $vcalendar = CalendarApp::getVCalendar( $id ); 100 | $vtodo = $vcalendar->VTODO; 101 | 102 | TasksApp::setComplete($vtodo, $checked ? 100 : 0, null); 103 | Object::edit($id, $vcalendar->serialize()); 104 | $user_timezone = CalendarApp::getTimezone(); 105 | 106 | $aTask= TasksApp::getEventObject($id, true, true); 107 | $aCalendar = CalendarCalendar::find($aTask['calendarid']); 108 | 109 | $task_info[] = TasksApp::arrayForJSON($id, $vtodo, $user_timezone,$aCalendar,$aTask); 110 | 111 | $subTaskIds=''; 112 | if($aTask['relatedto'] === ''){ 113 | $subTaskIds = TasksApp::getSubTasks($aTask['eventuid']); 114 | if($subTaskIds !== ''){ 115 | $tempIds = explode(',',$subTaskIds); 116 | foreach($tempIds as $subIds){ 117 | $vcalendar = TasksApp::getVCalendar( $subIds, true, true ); 118 | $vtodo = $vcalendar->VTODO; 119 | TasksApp::setComplete($vtodo, $checked ? 100 : 0, null); 120 | TasksApp::edit($subIds, $vcalendar->serialize()); 121 | $task_info[] = TasksApp::arrayForJSON($subIds, $vtodo, $user_timezone,$aCalendar,$aTask); 122 | } 123 | } 124 | } 125 | $params = [ 126 | 'status' => 'success', 127 | 'data' =>$task_info 128 | ]; 129 | 130 | $response = new JSONResponse($params); 131 | 132 | return $response; 133 | 134 | } 135 | 136 | } -------------------------------------------------------------------------------- /css/3rdparty/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/css/3rdparty/chosen-sprite.png -------------------------------------------------------------------------------- /css/3rdparty/colorPicker.css: -------------------------------------------------------------------------------- 1 | div.colorPicker-picker { 2 | height: 22px; 3 | width: 22px; 4 | position:relative; 5 | float:left; 6 | top:7px; 7 | margin-right:2px; 8 | padding: 0 !important; 9 | border: 1px solid #ccc; 10 | background: url(../../img/arrow.png) no-repeat bottom right; 11 | cursor: pointer; 12 | line-height: 22px; 13 | font-size:0.75em; 14 | font-weight:bold; 15 | text-align: center; 16 | } 17 | 18 | div.colorPicker-palette { 19 | width: 200px; 20 | position: absolute; 21 | border: 1px solid #ccc; 22 | background-color: #f4f4f4; 23 | padding: 2px; 24 | z-index: 9999; 25 | } 26 | div.colorPicker_hexWrap {width: 100%; float:left } 27 | div.colorPicker_hexWrap label {font-size: 95%; color: #2F2F2F; margin: 5px 2px; width: 25%} 28 | div.colorPicker_hexWrap input {margin: 5px 2px; padding: 0; font-size: 95%; border: 1px solid #ccc; width: 65%; } 29 | 30 | div.colorPicker-swatch { 31 | height: 22px; 32 | width: 22px; 33 | border: 1px solid #ccc; 34 | margin: 2px; 35 | float: left; 36 | cursor: pointer; 37 | line-height: 22px; 38 | } 39 | -------------------------------------------------------------------------------- /css/3rdparty/fontello/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Font license info 2 | 3 | 4 | ## Elusive 5 | 6 | Copyright (C) 2013 by Aristeides Stathopoulos 7 | 8 | Author: Aristeides Stathopoulos 9 | License: SIL (http://scripts.sil.org/OFL) 10 | Homepage: http://aristeides.com/ 11 | 12 | 13 | ## Iconic 14 | 15 | Copyright (C) 2012 by P.J. Onori 16 | 17 | Author: P.J. Onori 18 | License: SIL (http://scripts.sil.org/OFL) 19 | Homepage: http://somerandomdude.com/work/iconic/ 20 | 21 | 22 | ## MFG Labs 23 | 24 | Copyright (C) 2012 by Daniel Bruce 25 | 26 | Author: MFG Labs 27 | License: SIL (http://scripts.sil.org/OFL) 28 | Homepage: http://www.mfglabs.com/ 29 | 30 | 31 | ## Font Awesome 32 | 33 | Copyright (C) 2012 by Dave Gandy 34 | 35 | Author: Dave Gandy 36 | License: SIL () 37 | Homepage: http://fortawesome.github.com/Font-Awesome/ 38 | 39 | 40 | ## Entypo 41 | 42 | Copyright (C) 2012 by Daniel Bruce 43 | 44 | Author: Daniel Bruce 45 | License: SIL (http://scripts.sil.org/OFL) 46 | Homepage: http://www.entypo.com 47 | 48 | 49 | -------------------------------------------------------------------------------- /css/3rdparty/fontello/README.txt: -------------------------------------------------------------------------------- 1 | This webfont is generated by http://fontello.com open source project. 2 | 3 | 4 | ================================================================================ 5 | Please, note, that you should obey original font licences, used to make this 6 | webfont pack. Details available in LICENSE.txt file. 7 | 8 | - Usually, it's enough to publish content of LICENSE.txt file somewhere on your 9 | site in "About" section. 10 | 11 | - If your project is open-source, usually, it will be ok to make LICENSE.txt 12 | file publically available in your repository. 13 | 14 | - Fonts, used in Fontello, don't require a clickable link on your site. 15 | But any kind of additional authors crediting is welcome. 16 | ================================================================================ 17 | 18 | 19 | Comments on archive content 20 | --------------------------- 21 | 22 | - /font/* - fonts in different formats 23 | 24 | - /css/* - different kinds of css, for all situations. Should be ok with 25 | twitter bootstrap. Also, you can skip style and assign icon classes 26 | directly to text elements, if you don't mind about IE7. 27 | 28 | - demo.html - demo file, to show your webfont content 29 | 30 | - LICENSE.txt - license info about source fonts, used to build your one. 31 | 32 | - config.json - keeps your settings. You can import it back into fontello 33 | anytime, to continue your work 34 | 35 | 36 | Why so many CSS files ? 37 | ----------------------- 38 | 39 | Because we like to fit all your needs :) 40 | 41 | - basic file, .css - is usually enough, it contains @font-face 42 | and character code definitions 43 | 44 | - *-ie7.css - if you need IE7 support, but still don't wish to put char codes 45 | directly into html 46 | 47 | - *-codes.css and *-ie7-codes.css - if you like to use your own @font-face 48 | rules, but still wish to benefit from css generation. That can be very 49 | convenient for automated asset build systems. When you need to update font - 50 | no need to manually edit files, just override old version with archive 51 | content. See fontello source code for examples. 52 | 53 | - *-embedded.css - basic css file, but with embedded WOFF font, to avoid 54 | CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain. 55 | We strongly recommend to resolve this issue by `Access-Control-Allow-Origin` 56 | server headers. But if you ok with dirty hack - this file is for you. Note, 57 | that data url moved to separate @font-face to avoid problems with tag. 6 | * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css. 7 | * 8 | * Copyright (c) 2011 Adam Shaw 9 | * Dual licensed under the MIT and GPL licenses, located in 10 | * MIT-LICENSE.txt and GPL-LICENSE.txt respectively. 11 | * 12 | * Date: Tue Sep 4 23:38:33 2012 -0700 13 | * 14 | */ 15 | 16 | body{ 17 | font-family:arial; 18 | font-size:12px; 19 | } 20 | 21 | 22 | /* Events 23 | -----------------------------------------------------*/ 24 | 25 | .fc-event-skin { 26 | background: none !important; 27 | color: #000 !important; 28 | } 29 | 30 | /* horizontal events */ 31 | 32 | .fc-event-hori { 33 | border-width: 0 0 1px 0 !important; 34 | border-bottom-style: dotted !important; 35 | border-bottom-color: #000 !important; 36 | padding: 1px 0 0 0 !important; 37 | } 38 | 39 | .fc-event-hori .fc-event-inner { 40 | border-width: 0 !important; 41 | padding: 0 1px !important; 42 | } 43 | 44 | /* vertical events */ 45 | 46 | .fc-event-vert { 47 | border-width: 0 0 0 1px !important; 48 | border-left-style: dotted !important; 49 | border-left-color: #000 !important; 50 | padding: 0 1px 0 0 !important; 51 | } 52 | 53 | .fc-event-vert .fc-event-inner { 54 | border-width: 0 !important; 55 | padding: 1px 0 !important; 56 | } 57 | 58 | .fc-event-bg { 59 | display: none !important; 60 | } 61 | 62 | .fc-event .ui-resizable-handle { 63 | display: none !important; 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /css/3rdparty/images/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/css/3rdparty/images/colors.png -------------------------------------------------------------------------------- /css/3rdparty/images/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/css/3rdparty/images/trigger.png -------------------------------------------------------------------------------- /css/3rdparty/jquery.miniColors.css: -------------------------------------------------------------------------------- 1 | input.miniColors { 2 | margin-right: 4px; 3 | } 4 | 5 | .miniColors-selector { 6 | position: absolute; 7 | width: 175px; 8 | height: 150px; 9 | background: white; 10 | border: solid 1px #bababa; 11 | -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); 12 | -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); 13 | box-shadow: 0 0 6px rgba(0, 0, 0, .25); 14 | -moz-border-radius: 5px; 15 | -webkit-border-radius: 5px; 16 | border-radius: 5px; 17 | padding: 5px; 18 | z-index: 999999; 19 | } 20 | 21 | .miniColors.opacity.miniColors-selector { 22 | width: 200px; 23 | } 24 | 25 | .miniColors-selector.black { 26 | background: black; 27 | border-color: black; 28 | } 29 | 30 | .miniColors-colors { 31 | position: absolute; 32 | top: 5px; 33 | left: 5px; 34 | width: 150px; 35 | height: 150px; 36 | background: url(images/colors.png) -40px 0 no-repeat; 37 | cursor: crosshair; 38 | } 39 | 40 | .miniColors.opacity .miniColors-colors { 41 | left: 30px; 42 | } 43 | 44 | .miniColors-hues { 45 | position: absolute; 46 | top: 5px; 47 | left: 160px; 48 | width: 20px; 49 | height: 150px; 50 | background: url(images/colors.png) 0 0 no-repeat; 51 | cursor: crosshair; 52 | } 53 | 54 | .miniColors.opacity .miniColors-hues { 55 | left: 185px; 56 | } 57 | 58 | .miniColors-opacity { 59 | position: absolute; 60 | top: 5px; 61 | left: 5px; 62 | width: 20px; 63 | height: 150px; 64 | background: url(images/colors.png) -20px 0 no-repeat; 65 | cursor: crosshair; 66 | } 67 | 68 | .miniColors-colorPicker { 69 | position: absolute; 70 | width: 11px; 71 | height: 11px; 72 | border: 1px solid black; 73 | -moz-border-radius: 11px; 74 | -webkit-border-radius: 11px; 75 | border-radius: 11px; 76 | } 77 | .miniColors-colorPicker-inner { 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | width: 7px; 82 | height: 7px; 83 | border: 2px solid white; 84 | -moz-border-radius: 9px; 85 | -webkit-border-radius: 9px; 86 | border-radius: 9px; 87 | } 88 | 89 | .miniColors-huePicker, 90 | .miniColors-opacityPicker { 91 | position: absolute; 92 | left: -2px; 93 | width: 22px; 94 | height: 2px; 95 | border: 1px solid black; 96 | background: white; 97 | margin-top: -1px; 98 | border-radius: 2px; 99 | } 100 | 101 | .miniColors-trigger, 102 | .miniColors-triggerWrap { 103 | width: 22px; 104 | height: 22px; 105 | display: inline-block; 106 | } 107 | 108 | .miniColors-triggerWrap { 109 | background: url(images/trigger.png) -22px 0 no-repeat; 110 | } 111 | 112 | .miniColors-triggerWrap.disabled { 113 | filter: alpha(opacity=50); 114 | opacity: .5; 115 | } 116 | 117 | .miniColors-trigger { 118 | vertical-align: middle; 119 | outline: none; 120 | background: url(images/trigger.png) 0 0 no-repeat; 121 | } 122 | 123 | .miniColors-triggerWrap.disabled .miniColors-trigger { 124 | cursor: default; 125 | } -------------------------------------------------------------------------------- /css/3rdparty/jquery.tagit.css: -------------------------------------------------------------------------------- 1 | .ui-autocomplete { 2 | background-color: #fff; 3 | position: absolute; 4 | cursor: default; 5 | list-style: none; 6 | margin: 25px 0 0; 7 | padding: 0; 8 | border: 1px solid #E5E5E5; 9 | border-top: none; 10 | -moz-border-radius: 0 0 10px 10px; 11 | border-radius: 0 0 10px 10px; 12 | -moz-box-shadow: 0 3px 5px #888; 13 | -webkit-box-shadow: 0 3px 5px #888; 14 | box-shadow: 0 3px 5px #888; 15 | font-size: 12px; 16 | } 17 | 18 | .ui-autocomplete .ui-menu-item { 19 | padding: 0; 20 | margin: 0; 21 | } 22 | 23 | .ui-autocomplete .ui-menu-item a { 24 | display: block; 25 | padding: 4px 6px; 26 | margin: 0; 27 | text-decoration: none; 28 | line-height: 12px; 29 | border-bottom: 1px solid #E5E5E5; 30 | border-top: 1px solid #fff; 31 | color: #78959D; 32 | 33 | } 34 | 35 | .ui-autocomplete .ui-menu-item a.ui-state-hover, 36 | .ui-autocomplete .ui-menu-item a.ui-state-active { 37 | background-color: #e3e3e3; 38 | color: #555; 39 | border-color: #f7f7f7; 40 | } 41 | 42 | .ui-autocomplete-loading { 43 | background: white url(ui-anim_basic_16x16.gif) right center no-repeat; 44 | } 45 | 46 | ul.tagit { 47 | cursor: text; 48 | overflow: auto; 49 | font-size: 14px; 50 | width: 97%; 51 | padding: 3px; 52 | margin-top: 2px; 53 | margin-right: 6px; 54 | margin-bottom: 16px; 55 | border: 1px solid #E5E5E5; 56 | background: #fff; 57 | color: #555; 58 | } 59 | 60 | ul.tagit li { 61 | -moz-border-radius: 5px; 62 | border-radius: 5px; 63 | display: block; 64 | float: left; 65 | margin: 2px 5px 2px 0; 66 | position: relative; 67 | } 68 | 69 | ul.tagit.ui-sortable li.tagit-choice a.ui-icon.ui-icon-grip-dotted-vertical{ 70 | float: left; 71 | width: 12px; 72 | margin-left: -5px; 73 | cursor: move; 74 | } 75 | 76 | ul.tagit li.tagit-choice { 77 | background-color: #f7f7f7; 78 | border: 1px solid #f2f2f2; 79 | padding: 2px 13px 3px 4px; 80 | height:20px; 81 | } 82 | 83 | ul.tagit li.tagit-choice.selected { 84 | background-color: #f5b0b0; 85 | border-color: #f76464; 86 | } 87 | 88 | ul.tagit li.tagit-choice:hover { 89 | background-color: #f2f2f2; 90 | border-color: #e0e0e0; 91 | } 92 | 93 | ul.tagit li.tagit-new { 94 | padding: 2px 4px 3px; 95 | padding: 2px 4px 1px; 96 | padding: 2px 4px 1px 0; 97 | } 98 | 99 | ul.tagit li.tagit-choice input { 100 | display: block; 101 | float: left; 102 | margin: 0 5px 0 0; 103 | padding-top: 2px; 104 | padding-bottom: 2px; 105 | } 106 | 107 | ul.tagit li.tagit-choice a.tagit-close { 108 | color: #777777; 109 | cursor: pointer; 110 | font-size: 12px; 111 | font-weight: bold; 112 | outline: medium none; 113 | padding: 2px 0 2px 3px; 114 | text-decoration: none; 115 | position: absolute; 116 | display: block; 117 | top: 0; 118 | right: 3px; 119 | 120 | text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.7); 121 | } 122 | 123 | ul.tagit li.tagit-choice a.tagit-close:hover { 124 | color: #535353; 125 | } 126 | 127 | ul.tagit input[type="text"] { 128 | -moz-box-sizing: border-box; 129 | border: none !important; 130 | margin: 0 !important; 131 | padding: 0 !important; 132 | width: inherit !important; 133 | outline: none; 134 | } 135 | 136 | .tagit-hiddenSelect { 137 | display:none; 138 | } -------------------------------------------------------------------------------- /css/3rdparty/jquery.timepicker.css: -------------------------------------------------------------------------------- 1 | .ui-timepicker-wrapper { 2 | overflow-y: auto; 3 | height: 150px; 4 | width: 6.5em; 5 | background: #fff; 6 | border: 1px solid #ddd; 7 | -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); 8 | -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); 9 | box-shadow:0 5px 10px rgba(0,0,0,0.2); 10 | outline: none; 11 | z-index: 10001; 12 | margin: 0; 13 | } 14 | 15 | .ui-timepicker-wrapper.ui-timepicker-with-duration { 16 | width: 13em; 17 | } 18 | 19 | .ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30, 20 | .ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 { 21 | width: 11em; 22 | } 23 | 24 | .ui-timepicker-list { 25 | margin: 0; 26 | padding: 0; 27 | list-style: none; 28 | } 29 | 30 | .ui-timepicker-duration { 31 | margin-left: 5px; color: #888; 32 | } 33 | 34 | .ui-timepicker-list:hover .ui-timepicker-duration { 35 | color: #888; 36 | } 37 | 38 | .ui-timepicker-list li { 39 | padding: 3px 0 3px 5px; 40 | cursor: pointer; 41 | white-space: nowrap; 42 | color: #000; 43 | list-style: none; 44 | margin: 0; 45 | } 46 | 47 | .ui-timepicker-list:hover .ui-timepicker-selected { 48 | background: #fff; color: #000; 49 | } 50 | 51 | li.ui-timepicker-selected, 52 | .ui-timepicker-list li:hover, 53 | .ui-timepicker-list .ui-timepicker-selected:hover { 54 | background: #1980EC; color: #fff; 55 | } 56 | 57 | li.ui-timepicker-selected .ui-timepicker-duration, 58 | .ui-timepicker-list li:hover .ui-timepicker-duration { 59 | color: #ccc; 60 | } 61 | 62 | .ui-timepicker-list li.ui-timepicker-disabled, 63 | .ui-timepicker-list li.ui-timepicker-disabled:hover, 64 | .ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { 65 | color: #888; 66 | cursor: default; 67 | } 68 | 69 | .ui-timepicker-list li.ui-timepicker-disabled:hover, 70 | .ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { 71 | background: #f2f2f2; 72 | } 73 | -------------------------------------------------------------------------------- /css/3rdparty/jquery.webui-popover.css: -------------------------------------------------------------------------------- 1 | /* webui popover */ 2 | .webui-popover { 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | z-index: 500; 7 | display: none; 8 | width: 276px; 9 | min-height: 32px; 10 | text-align: left; 11 | white-space: normal; 12 | background-color: #ffffff; 13 | background-clip: padding-box; 14 | background: white; 15 | color: #333333; 16 | border-radius: 3px; 17 | box-shadow: 0 0 7px #888888; 18 | padding: 15px; 19 | } 20 | .webui-popover.top, 21 | .webui-popover.top-left, 22 | .webui-popover.top-right { 23 | margin-top: -10px; 24 | } 25 | .webui-popover.right, 26 | .webui-popover.right-top, 27 | .webui-popover.right-bottom { 28 | margin-left: 10px; 29 | } 30 | .webui-popover.bottom, 31 | .webui-popover.bottom-left, 32 | .webui-popover.bottom-right { 33 | margin-top: 10px; 34 | } 35 | .webui-popover.left, 36 | .webui-popover.left-top, 37 | .webui-popover.left-bottom { 38 | margin-left: -10px; 39 | } 40 | .webui-popover.pop { 41 | -webkit-transform: scale(0.8); 42 | -o-transform: scale(0.8); 43 | transform: scale(0.8); 44 | transition: transform 0.15s cubic-bezier(0.3, 0, 0, 1.5); 45 | } 46 | .webui-popover.fade { 47 | transition: opacity .15s linear; 48 | } 49 | .webui-popover.in { 50 | -webkit-transform: none; 51 | -o-transform: none; 52 | transform: none; 53 | opacity: 1; 54 | } 55 | .webui-popover-inner .close { 56 | font-family: arial; 57 | margin: 5px 10px 0 0; 58 | float: right; 59 | font-size: 20px; 60 | font-weight: bold; 61 | line-height: 20px; 62 | color: #000000; 63 | text-shadow: 0 1px 0 #fff; 64 | opacity: 0.2; 65 | filter: alpha(opacity=20); 66 | text-decoration: none; 67 | } 68 | .webui-popover-inner .close:hover, 69 | .webui-popover-inner .close:focus { 70 | opacity: 0.5; 71 | filter: alpha(opacity=50); 72 | } 73 | .webui-popover-title { 74 | padding: 8px 14px; 75 | margin: 0; 76 | font-size: 14px; 77 | font-weight: bold; 78 | line-height: 18px; 79 | background-color: #ffffff; 80 | border-bottom: 1px solid #f2f2f2; 81 | border-radius: 5px 5px 0 0; 82 | } 83 | .webui-popover-content { 84 | padding: 9px 14px; 85 | overflow: auto; 86 | } 87 | .webui-popover-inverse { 88 | background-color: #333333; 89 | color: #eeeeee; 90 | } 91 | .webui-popover-inverse .webui-popover-title { 92 | background: #333333; 93 | border-bottom: 1px solid #3b3b3b; 94 | color: #eeeeee; 95 | } 96 | .webui-no-padding .webui-popover-content { 97 | padding: 0; 98 | } 99 | .webui-no-padding .list-group-item { 100 | border-right: none; 101 | border-left: none; 102 | } 103 | .webui-no-padding .list-group-item:first-child { 104 | border-top: 0; 105 | } 106 | .webui-no-padding .list-group-item:last-child { 107 | border-bottom: 0; 108 | } 109 | .webui-popover > .arrow, 110 | .webui-popover > .arrow:after { 111 | position: absolute; 112 | display: block; 113 | width: 0; 114 | height: 0; 115 | border-color: transparent; 116 | border-style: solid; 117 | } 118 | .webui-popover > .arrow { 119 | border-width: 11px; 120 | } 121 | .webui-popover > .arrow:after { 122 | border-width: 10px; 123 | content: ""; 124 | } 125 | .webui-popover.top > .arrow, 126 | .webui-popover.top-right > .arrow, 127 | .webui-popover.top-left > .arrow { 128 | bottom: -11px; 129 | left: 50%; 130 | margin-left: -11px; 131 | border-top-color: #ccc; 132 | 133 | border-bottom-width: 0; 134 | } 135 | .webui-popover.top > .arrow:after, 136 | .webui-popover.top-right > .arrow:after, 137 | .webui-popover.top-left > .arrow:after { 138 | content: " "; 139 | bottom: 1px; 140 | margin-left: -10px; 141 | border-top-color: #ffffff; 142 | border-bottom-width: 0; 143 | } 144 | .webui-popover.right > .arrow, 145 | .webui-popover.right-top > .arrow, 146 | .webui-popover.right-bottom > .arrow { 147 | top: 50%; 148 | left: -11px; 149 | margin-top: -11px; 150 | border-left-width: 0; 151 | border-right-color: #ccc; 152 | 153 | } 154 | .webui-popover.right > .arrow:after, 155 | .webui-popover.right-top > .arrow:after, 156 | .webui-popover.right-bottom > .arrow:after { 157 | content: " "; 158 | left: 1px; 159 | bottom: -10px; 160 | border-left-width: 0; 161 | border-right-color: #ffffff; 162 | } 163 | .webui-popover.bottom > .arrow, 164 | .webui-popover.bottom-right > .arrow, 165 | .webui-popover.bottom-left > .arrow { 166 | top: -11px; 167 | left: 50%; 168 | margin-left: -11px; 169 | border-bottom-color: #ccc; 170 | border-bottom-color: rgba(0, 0, 0, 0.25); 171 | border-top-width: 0; 172 | } 173 | .webui-popover.bottom > .arrow:after, 174 | .webui-popover.bottom-right > .arrow:after, 175 | .webui-popover.bottom-left > .arrow:after { 176 | content: " "; 177 | top: 1px; 178 | margin-left: -10px; 179 | border-bottom-color: #ffffff; 180 | border-top-width: 0; 181 | } 182 | .webui-popover.left > .arrow, 183 | .webui-popover.left-top > .arrow, 184 | .webui-popover.left-bottom > .arrow { 185 | top: 50%; 186 | right: -11px; 187 | margin-top: -11px; 188 | border-right-width: 0; 189 | border-left-color: #ccc; 190 | border-left-color: rgba(0, 0, 0, 0.25); 191 | } 192 | .webui-popover.left > .arrow:after, 193 | .webui-popover.left-top > .arrow:after, 194 | .webui-popover.left-bottom > .arrow:after { 195 | content: " "; 196 | right: 1px; 197 | border-right-width: 0; 198 | border-left-color: #ffffff; 199 | bottom: -10px; 200 | } 201 | .webui-popover-inverse.top > .arrow, 202 | .webui-popover-inverse.top-left > .arrow, 203 | .webui-popover-inverse.top-right > .arrow, 204 | .webui-popover-inverse.top > .arrow:after, 205 | .webui-popover-inverse.top-left > .arrow:after, 206 | .webui-popover-inverse.top-right > .arrow:after { 207 | border-top-color: #ccc; 208 | } 209 | .webui-popover-inverse.right > .arrow, 210 | .webui-popover-inverse.right-top > .arrow, 211 | .webui-popover-inverse.right-bottom > .arrow, 212 | .webui-popover-inverse.right > .arrow:after, 213 | .webui-popover-inverse.right-top > .arrow:after, 214 | .webui-popover-inverse.right-bottom > .arrow:after { 215 | border-right-color: #ccc; 216 | } 217 | .webui-popover-inverse.bottom > .arrow, 218 | .webui-popover-inverse.bottom-left > .arrow, 219 | .webui-popover-inverse.bottom-right > .arrow, 220 | .webui-popover-inverse.bottom > .arrow:after, 221 | .webui-popover-inverse.bottom-left > .arrow:after, 222 | .webui-popover-inverse.bottom-right > .arrow:after { 223 | border-bottom-color: #ccc; 224 | } 225 | .webui-popover-inverse.left > .arrow, 226 | .webui-popover-inverse.left-top > .arrow, 227 | .webui-popover-inverse.left-bottom > .arrow, 228 | .webui-popover-inverse.left > .arrow:after, 229 | .webui-popover-inverse.left-top > .arrow:after, 230 | .webui-popover-inverse.left-bottom > .arrow:after { 231 | border-left-color: #ccc; 232 | } 233 | .webui-popover i.icon-refresh:before { 234 | content: ""; 235 | } 236 | .webui-popover i.icon-refresh { 237 | display: block; 238 | width: 30px; 239 | height: 30px; 240 | font-size: 20px; 241 | top: 50%; 242 | left: 50%; 243 | position: absolute; 244 | margin-left: -15px; 245 | margin-right: -15px; 246 | background: url(../../img/loading.gif) no-repeat; 247 | } 248 | @-webkit-keyframes rotate { 249 | 100% { 250 | -webkit-transform: rotate(360deg); 251 | } 252 | } 253 | @keyframes rotate { 254 | 100% { 255 | transform: rotate(360deg); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /css/404.css: -------------------------------------------------------------------------------- 1 | 2 | #body-login .error-broken-link{ 3 | text-align:left;color:#fff; 4 | } 5 | 6 | #body-login .error-broken-link ul{ 7 | margin:10px 0 10px 0; 8 | } 9 | 10 | #body-login .error-broken-link ul li{ 11 | list-style: disc;list-style-position:inside;cursor:default; 12 | } 13 | 14 | #body-login ul{ 15 | width:80%; 16 | margin-left:auto; 17 | margin-right:auto; 18 | } 19 | -------------------------------------------------------------------------------- /css/authenticate.css: -------------------------------------------------------------------------------- 1 | #body-login form label.infield { 2 | width: 190px; 3 | padding: 10px; 4 | left: 8px; 5 | top: 8px; 6 | } 7 | 8 | #body-login form div.warning-info{ 9 | color:#000; 10 | } 11 | 12 | #password { 13 | width: 190px !important; 14 | padding: 10px; 15 | margin: 6px; 16 | } 17 | 18 | #body-login form{ 19 | width:300px; 20 | color:#000; 21 | } 22 | #body-login input[type="submit"] { 23 | 24 | font-size:14px; 25 | width:50px; 26 | padding:12px 4px; 27 | } 28 | -------------------------------------------------------------------------------- /css/import.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2012 Georg Ehrke 3 | * This file is licensed under the Affero General Public License version 3 or 4 | * later. 5 | * See the COPYING-README file. 6 | */ 7 | 8 | #calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:left;} 9 | #calendar_import_form_message{font-weight: bold;text-align:left;} 10 | #calendar_import_newcalendar{float:right;} 11 | #calendar_import_mergewarning{clear: both;} 12 | #calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;} 13 | 14 | .calendar_import_warning{border-color: #fc3333;} 15 | .calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;margin-top: 5px;} 16 | #calendar_import_dialog{ 17 | padding:0; 18 | margin:0; 19 | text-align:left; 20 | line-height:30px; 21 | } 22 | .import-select-cal, 23 | #calendar_import_checkbox, 24 | #calendar_import_process, 25 | #calendar_import_newcalform, 26 | .importdiv{ 27 | position:relative; 28 | float:left; 29 | display:block; 30 | width:90%; 31 | padding-left:5%; 32 | margin-left:auto; 33 | margin-right:auto; 34 | } 35 | #calendar_import_newcalform input[type="text"], 36 | .importdiv input[type="text"] { 37 | padding:0; 38 | padding-left:5px; 39 | width: 88%; 40 | height:30px; 41 | float: left; 42 | } 43 | .button.primary-button{ 44 | background-color: #35537a; 45 | color: #ddd; 46 | } 47 | .button.primary-button:hover{ 48 | color:#fff; 49 | background-color: #35537a; 50 | } 51 | 52 | #calendar_import_submit{ 53 | margin-left:5%; 54 | } 55 | 56 | .importdiv button, 57 | .importdiv input[type="submit"] { 58 | width: 36px; 59 | height: 32px; 60 | float: left; 61 | } 62 | #calendar_import_newcalform div.colorPicker-picker{ 63 | width: 36px; 64 | height: 28px; 65 | float: left; 66 | margin-top:-3px; 67 | } 68 | 69 | #calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;} 70 | -------------------------------------------------------------------------------- /css/mobile.css: -------------------------------------------------------------------------------- 1 | /** 2 | * ownCloud - CalendarPlus 3 | * 4 | * @author Sebastian Doell 5 | * @copyright 2015 sebastian doell sebastian@libasys.de 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 9 | * License as published by the Free Software Foundation; either 10 | * version 3 of the License, or any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public 18 | * License along with this library. If not, see . 19 | * 20 | */ 21 | @media only screen and (max-width: 420px) { 22 | #header .left-right-nav, 23 | #header .datenavigation{ 24 | display:none; 25 | } 26 | } 27 | 28 | @media only screen and (max-width: 768px) { 29 | 30 | 31 | /* do not show Deleted Files on mobile, not optimized yet and button too long */ 32 | #header .datenavigation{ 33 | width:160px; 34 | 35 | } 36 | 37 | #app-navigation{ 38 | display:block; 39 | } 40 | #rightCalendarNav{ 41 | display:none; 42 | } 43 | 44 | #header .datenavigation #datelabel{ 45 | display:none; 46 | } 47 | #content .isHiddenCal{ 48 | display:block; 49 | } 50 | /* proper notification area for multi line messages */ 51 | #notification-container { 52 | display: -webkit-box; 53 | display: -moz-box; 54 | display: -ms-flexbox; 55 | display: -webkit-flex; 56 | display: flex; 57 | } 58 | .ui-dialog .ui-widget-header button, .ui-dialog #actions button { 59 | min-width:30px; 60 | height:30px; 61 | line-height:24px; 62 | text-align:center; 63 | font-size:12px; 64 | padding:0; 65 | padding-left:5px; 66 | padding-right:5px; 67 | } 68 | .ui-dialog #actions #closeDialog{ 69 | display:none; 70 | } 71 | .ui-dialog #accordion{ 72 | width:100%; 73 | 74 | } 75 | .ui-dialog #actions .button-group.first{ 76 | width:79%; 77 | 78 | } 79 | .ui-dialog #actions .button-group.second{ 80 | width:21%; 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /css/share.css: -------------------------------------------------------------------------------- 1 | /** 2 | * ownCloud - CalendarPlus 3 | * 4 | * @author Sebastian Doell 5 | * @copyright 2015 sebastian doell sebastian@libasys.de 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 9 | * License as published by the Free Software Foundation; either 10 | * version 3 of the License, or any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public 18 | * License along with this library. If not, see . 19 | * 20 | */ 21 | 22 | body{ 23 | background:#fff; 24 | } 25 | #controls{ 26 | left:0; 27 | margin-left:-240px; 28 | background:#fff; 29 | width:95%; 30 | } 31 | #fullcalendar{ 32 | 33 | padding-left:10px; 34 | } 35 | #app-content{ 36 | top:50px; 37 | height:100%; 38 | float:left; 39 | position:relative; 40 | display:block; 41 | width:75%; 42 | } 43 | #timezoneDiv{ 44 | float:none; 45 | position:relative; 46 | display:block; 47 | width:98%; 48 | 49 | min-height:40px; 50 | } 51 | #app-navigation{ 52 | clear:both; 53 | width:250px; 54 | position:relative; 55 | float:left; 56 | display:block; 57 | top:50px; 58 | height:95%; 59 | left:0; 60 | } 61 | #view{ 62 | 63 | display:inline-block; 64 | } 65 | 66 | #datecontrol_today{ 67 | float:none; 68 | margin: 5px 3px; 69 | padding: 6px 7px; 70 | } 71 | 72 | .button-group button + button{ 73 | margin-left: -5px; 74 | } 75 | 76 | .button-group > button:first-child { 77 | margin-left: 0; 78 | border-top-left-radius: 3px; 79 | border-bottom-left-radius: 3px; 80 | } 81 | 82 | .button-group > button:last-child { 83 | 84 | border-top-right-radius: 3px; 85 | border-bottom-right-radius: 3px; 86 | } 87 | 88 | 89 | .fc-view-list{ 90 | overflow:auto; 91 | height:auto; 92 | } 93 | 94 | 95 | #view button.active{ 96 | background:#fff; 97 | } 98 | 99 | footer{ 100 | position:absolute; 101 | bottom:0; 102 | width:100%; 103 | text-align:center; 104 | } 105 | .leftControls{ 106 | position:relative; 107 | float:left; 108 | left:0; 109 | width:25%; 110 | display:none; 111 | 112 | } 113 | .centerControls{ 114 | position:relative; 115 | float:left; 116 | width:50%; 117 | text-align:center; 118 | display:inline-block; 119 | 120 | } 121 | .rightControls{ 122 | position:relative; 123 | float:left; 124 | min-width:23%; 125 | right:0; 126 | display:none; 127 | text-align:right; 128 | 129 | } 130 | .rightControls label{ 131 | text-align:right; 132 | position:relative; 133 | 134 | font-size:14px; 135 | display:inline-block; 136 | line-height:20px; 137 | width:auto; 138 | height:20px; 139 | 140 | } 141 | .chzn-container{ 142 | margin-top:10px; 143 | text-align:left; 144 | width:120px; 145 | } 146 | 147 | #eventPublic .noticeShareShow{ 148 | display:block; 149 | width:96%; 150 | float:left; 151 | min-height:50px; 152 | line-height:20px; 153 | border-radius:8px; 154 | border:1px solid #ccc; 155 | padding:5px; 156 | } 157 | #header{ 158 | display:none; 159 | } 160 | footer{ 161 | display:none; 162 | 163 | } 164 | .datenavigation{ 165 | padding-left:4px; 166 | } 167 | 168 | .timezonelabel{ 169 | padding-left:8px; 170 | font-weight:bold; 171 | } 172 | 173 | 174 | .header-right{ 175 | 176 | height:30px; 177 | margin:0; 178 | padding:0; 179 | position:relative; 180 | top:5px; 181 | right:10px; 182 | float:right; 183 | } 184 | 185 | .header-right span a.button{ 186 | border:1px solid #ccc; 187 | background:#fff; 188 | color:#000; 189 | } 190 | 191 | .header-right a:hover{ 192 | border:1px solid #ccc; 193 | background:black; 194 | color:#fff; 195 | } 196 | 197 | #eventPublic{ 198 | top:88px; 199 | position:relative; 200 | display:block; 201 | width:30%; 202 | margin-left:auto; 203 | margin-right:auto; 204 | font-size:14px; 205 | } 206 | 207 | #eventPublic i.ioc{ 208 | font-size:16px; 209 | margin-right:5px; 210 | } 211 | 212 | #eventPublicInner{ 213 | position:relative; 214 | display:block; 215 | width:90%; 216 | margin-left:auto; 217 | margin-right:auto; 218 | border:1px solid #ccc; 219 | padding:10px; 220 | border-radius:8px; 221 | line-height:22px; 222 | box-shadow:0 0 1em rgba(0, 0, 0, .5); 223 | min-height:300px; 224 | min-width:300px; 225 | } 226 | .shareevent tr:hover{ 227 | background-color:transparent; 228 | } 229 | 230 | th.leftDescr{ 231 | width:30px; 232 | line-height:24px; 233 | padding:0; 234 | margin:0; 235 | } 236 | 237 | .shareevent th.leftDescr{ 238 | width:120px; 239 | line-height:22px; 240 | padding:0; 241 | margin:0; 242 | } 243 | .shareevent a{ 244 | text-decoration:underline; 245 | } 246 | 247 | @media only screen and (max-width: 768px) { 248 | .rightControls{ 249 | display:none; 250 | } 251 | .leftControls{ 252 | width:10%; 253 | } 254 | 255 | .centerControls{ 256 | width:90%; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /db/repeatdao.php: -------------------------------------------------------------------------------- 1 | . 21 | * 22 | */ 23 | 24 | namespace OCA\CalendarPlus\Db; 25 | 26 | use \OCP\IDb; 27 | 28 | 29 | class RepeatDAO { 30 | 31 | private $db; 32 | private $userId; 33 | private $calendarRepeatTable = '*PREFIX*clndrplus_repeat'; 34 | 35 | public function __construct(IDb $db, $userId) { 36 | $this->db = $db; 37 | $this->userId = $userId; 38 | } 39 | 40 | /** 41 | * @brief returns the cache of an event 42 | * @param integer $id 43 | * @return array || null 44 | */ 45 | public function getEvent($id) { 46 | 47 | $stmt = $this->db->prepareQuery('SELECT * FROM `'.$this->calendarRepeatTable.'` WHERE `eventid` = ?'); 48 | $result = $stmt->execute(array($id)); 49 | 50 | if($result !== null && $result !== '' ){ 51 | $return = array(); 52 | while($row = $result->fetchRow()) { 53 | $return[] = $row; 54 | } 55 | 56 | return $return; 57 | }else{ 58 | return null; 59 | } 60 | 61 | } 62 | 63 | /** 64 | * @brief returns the cache of an event in a specific peroid 65 | * @param integer $id - id of the event 66 | * @param (DateTime) $from - start for period in UTC 67 | * @param (DateTime) $until - end for period in UTC 68 | * @param bool $bAlarm 69 | * @return array || null 70 | */ 71 | public function getEventInperiod($id, $from, $until,$bAlarm) { 72 | 73 | $values = array(); 74 | if($bAlarm === true){ 75 | $values = [ $id, $from, $until, $from, $until ]; 76 | }else{ 77 | $values = [$id, 78 | $this->getUTCforMDB($from), $this->getUTCforMDB($until), 79 | $this->getUTCforMDB($from), $this->getUTCforMDB($until) 80 | ]; 81 | } 82 | 83 | $stmt = $this->db->prepareQuery( 'SELECT * FROM `'.$this->calendarRepeatTable.'` WHERE `eventid` = ?' 84 | .' AND ((`startdate` >= ? AND `startdate` <= ?)' 85 | .' OR (`enddate` >= ? AND `enddate` <= ?))'); 86 | $result = $stmt->execute($values); 87 | 88 | if($result !== null && $result !==''){ 89 | return $result->fetchAll(); 90 | }else{ 91 | return null; 92 | } 93 | 94 | } 95 | 96 | 97 | /** 98 | * @brief returns the cache of all repeating events of a calendar 99 | * @param integer $id 100 | * @return array || null 101 | */ 102 | public function getCalendar($id) { 103 | 104 | $stmt = $this->db->prepareQuery('SELECT * FROM `'.$this->calendarRepeatTable.'` WHERE `calid` = ?'); 105 | $result = $stmt->execute(array($id)); 106 | 107 | if($result !== null && $result !== ''){ 108 | $return = array(); 109 | while($row = $result->fetchRow()) { 110 | $return[] = $row; 111 | } 112 | 113 | return $return; 114 | }else{ 115 | return null; 116 | } 117 | 118 | } 119 | 120 | /** 121 | * @brief returns the cache of all repeating events of a calendar in a specific period 122 | * @param integer $id - id of the event 123 | * @param string $from - start for period in UTC 124 | * @param string $until - end for period in UTC 125 | * @return array || null 126 | */ 127 | public function getCalendarInperiod($id, $from, $until) { 128 | 129 | $stmt = $this->db->prepareQuery( 'SELECT * FROM `'.$this->calendarRepeatTable.'` WHERE `calid` = ?' 130 | .' AND ((`startdate` >= ? AND `startdate` <= ?)' 131 | .' OR (`enddate` >= ? AND `enddate` <= ?))'); 132 | $result = $stmt->execute(array($id, $from, $until, $from, $until)); 133 | 134 | if($result !== null && $result !== ''){ 135 | $return = array(); 136 | while($row = $result->fetchRow()) { 137 | $return[] = $row; 138 | } 139 | 140 | return $return; 141 | }else{ 142 | return null; 143 | } 144 | } 145 | 146 | /** 147 | * @brief returns the cache of all repeating events of a calendar in a specific period 148 | * @param integer $id - id of the event 149 | *@param integer $calendarId - id of the calendar 150 | * @param string $start - start for period in UTC 151 | * @param string $end - end for period in UTC 152 | * @return true || null 153 | */ 154 | public function insertEvent($id, $calendarId, $start, $end) { 155 | 156 | $stmt = $this->db->prepareQuery('INSERT INTO `'.$this->calendarRepeatTable.'` (`eventid`,`calid`,`startdate`,`enddate`) VALUES(?,?,?,?)'); 157 | $result = $stmt->execute(array($id,$calendarId,$start,$end)); 158 | 159 | if($result !== null && $result !==''){ 160 | return true; 161 | }else{ 162 | return null; 163 | } 164 | 165 | } 166 | 167 | /** 168 | * @brief removes the cache of an event 169 | * @param integer $id 170 | * @return true || null 171 | */ 172 | public function cleanEvent($id) { 173 | $stmt = $this->db->prepareQuery('DELETE FROM `'.$this->calendarRepeatTable.'` WHERE `eventid` = ?'); 174 | $result = $stmt->execute(array($id)); 175 | 176 | if($result !== null && $result !==''){ 177 | return true; 178 | }else{ 179 | return null; 180 | } 181 | } 182 | 183 | 184 | /** 185 | * @brief removes the cache of all events of a calendar 186 | * @param integer $id 187 | * @return true || null 188 | */ 189 | public function cleanCalendar($id) { 190 | $stmt = $this->db->prepareQuery('DELETE FROM `'.$this->calendarRepeatTable.'` WHERE `calid` = ?'); 191 | $result = $stmt->execute(array($id)); 192 | 193 | if($result !== null && $result !== ''){ 194 | return true; 195 | }else{ 196 | return null; 197 | } 198 | } 199 | 200 | 201 | /** 202 | * @brief DateTime to UTC string 203 | * @param DateTime $datetime The date to convert 204 | * @returns date as YYYY-MM-DD hh:mm 205 | * 206 | * This function creates a date string that can be used by MDB2. 207 | * Furthermore it converts the time to UTC. 208 | */ 209 | private function getUTCforMDB($datetime) { 210 | return date('Y-m-d H:i', $datetime->format('U')); 211 | } 212 | 213 | 214 | } -------------------------------------------------------------------------------- /img/arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/img/arrow.gif -------------------------------------------------------------------------------- /img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/img/arrow.png -------------------------------------------------------------------------------- /img/calendarplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/img/calendarplus.png -------------------------------------------------------------------------------- /img/calendarplus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | ]> 6 | 10 | 11 | 12 | 16 | 18 | 24 | 26 | 27 | -------------------------------------------------------------------------------- /img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/img/favicon.png -------------------------------------------------------------------------------- /img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libasys/calendarplus/0cc8e37253c0f14ae466528f77d4663d51a5314a/img/loading.gif -------------------------------------------------------------------------------- /img/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/3rdparty/gcal.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * FullCalendar v1.6.4 Google Calendar Plugin 3 | * Docs & License: http://arshaw.com/fullcalendar/ 4 | * (c) 2013 Adam Shaw 5 | */ 6 | 7 | (function($) { 8 | 9 | 10 | var fc = $.fullCalendar; 11 | var formatDate = fc.formatDate; 12 | var parseISO8601 = fc.parseISO8601; 13 | var addDays = fc.addDays; 14 | var applyAll = fc.applyAll; 15 | 16 | 17 | fc.sourceNormalizers.push(function(sourceOptions) { 18 | if (sourceOptions.dataType == 'gcal' || 19 | sourceOptions.dataType === undefined && 20 | (sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) { 21 | sourceOptions.dataType = 'gcal'; 22 | if (sourceOptions.editable === undefined) { 23 | sourceOptions.editable = false; 24 | } 25 | } 26 | }); 27 | 28 | 29 | fc.sourceFetchers.push(function(sourceOptions, start, end) { 30 | if (sourceOptions.dataType == 'gcal') { 31 | return transformOptions(sourceOptions, start, end); 32 | } 33 | }); 34 | 35 | 36 | function transformOptions(sourceOptions, start, end) { 37 | 38 | var success = sourceOptions.success; 39 | var data = $.extend({}, sourceOptions.data || {}, { 40 | 'start-min': formatDate(start, 'u'), 41 | 'start-max': formatDate(end, 'u'), 42 | 'singleevents': true, 43 | 'max-results': 9999 44 | }); 45 | 46 | var ctz = sourceOptions.currentTimezone; 47 | if (ctz) { 48 | data.ctz = ctz = ctz.replace(' ', '_'); 49 | } 50 | 51 | return $.extend({}, sourceOptions, { 52 | url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?', 53 | dataType: 'jsonp', 54 | data: data, 55 | startParam: false, 56 | endParam: false, 57 | success: function(data) { 58 | var events = []; 59 | if (data.feed.entry) { 60 | $.each(data.feed.entry, function(i, entry) { 61 | var startStr = entry['gd$when'][0]['startTime']; 62 | var start = parseISO8601(startStr, true); 63 | var end = parseISO8601(entry['gd$when'][0]['endTime'], true); 64 | var allDay = startStr.indexOf('T') == -1; 65 | var url; 66 | $.each(entry.link, function(i, link) { 67 | if (link.type == 'text/html') { 68 | url = link.href; 69 | if (ctz) { 70 | url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz; 71 | } 72 | } 73 | }); 74 | if (allDay) { 75 | addDays(end, -1); // make inclusive 76 | } 77 | events.push({ 78 | id: entry['gCal$uid']['value'], 79 | title: entry['title']['$t'], 80 | url: url, 81 | start: start, 82 | end: end, 83 | allDay: allDay, 84 | location: entry['gd$where'][0]['valueString'], 85 | description: entry['content']['$t'] 86 | }); 87 | }); 88 | } 89 | var args = [events].concat(Array.prototype.slice.call(arguments, 1)); 90 | var res = applyAll(success, this, args); 91 | if ($.isArray(res)) { 92 | return res; 93 | } 94 | return events; 95 | } 96 | }); 97 | 98 | } 99 | 100 | 101 | // legacy 102 | fc.gcalFeed = function(url, sourceOptions) { 103 | return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' }); 104 | }; 105 | 106 | 107 | })(jQuery); 108 | -------------------------------------------------------------------------------- /js/3rdparty/jquery.colorPicker.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Really Simple Color Picker in jQuery 3 | * 4 | * Licensed under the MIT (MIT-LICENSE.txt) licenses. 5 | * 6 | * Copyright (c) 2008-2012 7 | * Lakshan Perera (www.laktek.com) & Daniel Lacy (daniellacy.com) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to 11 | * deal in the Software without restriction, including without limitation the 12 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | * sell copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | * IN THE SOFTWARE. 26 | */(function(a){var b,c,d=0,e={control:a('
 
'),palette:a('
'),swatch:a('
 
'),hexLabel:a(''),hexField:a('')},f="transparent",g;a.fn.colorPicker=function(b){return this.each(function(){var c=a(this),g=a.extend({},a.fn.colorPicker.defaults,b),h=a.fn.colorPicker.toHex(c.val().length>0?c.val():g.pickerDefault),i=e.control.clone(),j=e.palette.clone().attr("id","colorPicker_palette-"+d),k=e.hexLabel.clone(),l=e.hexField.clone(),m=j[0].id,n,o;a.each(g.colors,function(b){n=e.swatch.clone(),g.colors[b]===f?(n.addClass(f).text("X"),a.fn.colorPicker.bindPalette(l,n,f)):(n.css("background-color","#"+this),a.fn.colorPicker.bindPalette(l,n)),n.appendTo(j)}),k.attr("for","colorPicker_hex-"+d),l.attr({id:"colorPicker_hex-"+d,value:h}),l.bind("keydown",function(b){if(b.keyCode===13){var d=a.fn.colorPicker.toHex(a(this).val());a.fn.colorPicker.changeColor(d?d:c.val())}b.keyCode===27&&a.fn.colorPicker.hidePalette()}),l.bind("keyup",function(b){var d=a.fn.colorPicker.toHex(a(b.target).val());a.fn.colorPicker.previewColor(d?d:c.val())}),a('
').append(k).appendTo(j),j.find(".colorPicker_hexWrap").append(l),g.showHexField===!1&&(l.hide(),k.hide()),a("body").append(j),j.hide(),i.css("background-color",h),i.bind("click",function(){c.is(":not(:disabled)")&&a.fn.colorPicker.togglePalette(a("#"+m),a(this))}),b&&b.onColorChange?i.data("onColorChange",b.onColorChange):i.data("onColorChange",function(){}),(o=c.data("text"))&&i.html(o),c.after(i),c.bind("change",function(){c.next(".colorPicker-picker").css("background-color",a.fn.colorPicker.toHex(a(this).val()))}),c.val(h);if(c[0].tagName.toLowerCase()==="input")try{c.attr("type","hidden")}catch(p){c.css("visibility","hidden").css("position","absolute")}else c.hide();d++})},a.extend(!0,a.fn.colorPicker,{toHex:function(a){if(a.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i))return a.charAt(0)==="#"?a:"#"+a;if(!a.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/))return!1;var b=[parseInt(RegExp.$1,10),parseInt(RegExp.$2,10),parseInt(RegExp.$3,10)],c=function(a){if(a.length<2)for(var b=0,c=2-a.length;b0)return;a.fn.colorPicker.hidePalette()},hidePalette:function(){a(document).unbind("mousedown",a.fn.colorPicker.checkMouse),a(".colorPicker-palette").hide()},showPalette:function(c){var d=b.prev("input").val();c.css({top:b.offset().top+b.outerHeight(),left:b.offset().left}),a("#color_value").val(d),c.show(),a(document).bind("mousedown",a.fn.colorPicker.checkMouse)},togglePalette:function(d,e){e&&(b=e),c=d,c.is(":visible")?a.fn.colorPicker.hidePalette():a.fn.colorPicker.showPalette(d)},changeColor:function(c){b.css("background-color",c),b.prev("input").val(c).change(),a.fn.colorPicker.hidePalette(),b.data("onColorChange").call(b,a(b).prev("input").attr("id"),c)},previewColor:function(a){b.css("background-color",a)},bindPalette:function(c,d,e){e=e?e:a.fn.colorPicker.toHex(d.css("background-color")),d.bind({click:function(b){g=e,a.fn.colorPicker.changeColor(e)},mouseover:function(b){g=c.val(),a(this).css("border-color","#598FEF"),c.val(e),a.fn.colorPicker.previewColor(e)},mouseout:function(d){a(this).css("border-color","#000"),c.val(b.css("background-color")),c.val(g),a.fn.colorPicker.previewColor(g)}})}}),a.fn.colorPicker.defaults={pickerDefault:"FFFFFF",colors:["000000","993300","333300","000080","333399","333333","800000","FF6600","808000","008000","008080","0000FF","666699","808080","FF0000","FF9900","99CC00","339966","33CCCC","3366FF","800080","999999","FF00FF","FFCC00","FFFF00","00FF00","00FFFF","00CCFF","993366","C0C0C0","FF99CC","FFCC99","FFFF99","CCFFFF","99CCFF","FFFFFF"],addColors:[],showHexField:!0}})(jQuery); -------------------------------------------------------------------------------- /js/3rdparty/jquery.datepair.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | if(!$) { 4 | return; 5 | } 6 | 7 | //////////// 8 | // Plugin // 9 | //////////// 10 | 11 | $.fn.datepair = function(option) { 12 | var out; 13 | this.each(function() { 14 | var $this = $(this); 15 | var data = $this.data('datepair'); 16 | var options = typeof option === 'object' && option; 17 | 18 | if (!data) { 19 | data = new Datepair(this, options); 20 | $this.data('datepair', data); 21 | } 22 | 23 | if (typeof option === 'string') { 24 | out = data[option](); 25 | } 26 | }); 27 | 28 | return out || this; 29 | }; 30 | 31 | ////////////// 32 | // Data API // 33 | ////////////// 34 | 35 | $('[data-datepair]').each(function() { 36 | var $this = $(this); 37 | $this.datepair($this.data()); 38 | }); 39 | 40 | }(window.Zepto || window.jQuery)); 41 | -------------------------------------------------------------------------------- /js/3rdparty/jstz-1.0.4.min.js: -------------------------------------------------------------------------------- 1 | /*! jstz - v1.0.4 - 2012-12-12 */ 2 | (function(e){var t=function(){"use strict";var e="s",n=function(e){var t=-e.getTimezoneOffset();return t!==null?t:0},r=function(e,t,n){var r=new Date;return e!==undefined&&r.setFullYear(e),r.setDate(n),r.setMonth(t),r},i=function(e){return n(r(e,0,2))},s=function(e){return n(r(e,5,2))},o=function(e){var t=e.getMonth()>7?s(e.getFullYear()):i(e.getFullYear()),r=n(e);return t-r!==0},u=function(){var t=i(),n=s(),r=i()-s();return r<0?t+",1":r>0?n+",1,"+e:t+",0"},a=function(){var e=u();return new t.TimeZone(t.olson.timezones[e])};return{determine:a,date_is_dst:o}}();t.TimeZone=function(e){"use strict";var n=null,r=function(){return n},i=function(){var e=t.olson.ambiguity_list[n],r=e.length,i=0,s=e[0];for(;i. 19 | * 20 | */ 21 | //SEARCH 22 | OC.search.resultTypes['calendar']=t('calendarplus','Cal.'); 23 | OC.search.resultTypes['tasks']=t('aufgaben','Tasks'); 24 | OC.search.resultTypes['contacts']=t('kontakte','Contacts'); 25 | 26 | (function($){ 27 | 28 | $.extend({ 29 | playSound: function(){ 30 | 31 | return $('').prependTo('#reminderBox'); 32 | } 33 | }); 34 | 35 | })(jQuery); 36 | 37 | $(document).ready(function(){ 38 | 39 | liveReminderCheck(); 40 | 41 | }); 42 | 43 | var timerRefresher=null; 44 | 45 | /** 46 | * Calls the server periodically every 1 min to check live calendar events 47 | * 48 | */ 49 | function liveReminderCheck(){ 50 | 51 | var url = OC.generateUrl('/apps/calendarplus/getreminderevents'); 52 | var myRefChecker=''; 53 | if (timerRefresher !== null){ 54 | window.clearInterval(timerRefresher); 55 | } 56 | 57 | var timerRefresher = window.setInterval(function(){ 58 | 59 | if($('#fullcalendar').length === 1 && CalendarPlus.calendarConfig != null){ 60 | //calId = ctag 61 | myRefChecker=CalendarPlus.calendarConfig['myRefreshChecker']; 62 | //CalendarPlus.UI.loading(true); 63 | //shows all 5 minutes 64 | if(CalendarPlus.UI.timerRun === 5 ){ 65 | $.each(CalendarPlus.calendarConfig['mycalendars'], function(i, elem) { 66 | if(elem.issubscribe === 1 && elem.uri !== 'bdaycpltocal_'+oc_current_user){ 67 | 68 | CalendarPlus.UI.Calendar.autoRefreshCalendar(elem.id); 69 | 70 | } 71 | }); 72 | CalendarPlus.UI.timerRun = 0; 73 | if(CalendarPlus.UI.isRefresh === true) { 74 | $('#fullcalendar').fullCalendar('refetchEvents'); 75 | CalendarPlus.UI.isRefresh = false; 76 | } 77 | 78 | } 79 | 80 | 81 | } 82 | 83 | $.post(url,{EvSource:myRefChecker},function(jasondata){ 84 | 85 | if($('#fullcalendar').length === 1){ 86 | if(jasondata.refresh !== 'onlyTimeLine'){ 87 | 88 | //CalendarPlus.calendarConfig['myRefreshChecker'][jasondata.refresh.id]=jasondata.refresh.ctag; 89 | 90 | 91 | } 92 | 93 | 94 | } 95 | // 96 | if(jasondata.data != ''){ 97 | 98 | openReminderDialog(jasondata.data); 99 | } 100 | 101 | // 102 | }); 103 | 104 | if($('#fullcalendar').length === 1){ 105 | CalendarPlus.Util.setTimeline(); 106 | CalendarPlus.UI.timerRun ++; 107 | } 108 | 109 | 110 | }, 60000); 111 | 112 | 113 | 114 | } 115 | 116 | var openReminderDialog=function(data){ 117 | //var output=''; 118 | $('body').append('
'); 119 | 120 | var output=''; 121 | $.each(data, function(i, elem) { 122 | output+=''+elem.startdate+'
'; 123 | output+=' '+elem.summary+'
'; 124 | 125 | }); 126 | $.playSound(oc_webroot+'/apps/calendarplus/audio/ring'); 127 | $('#reminderBox').html(output).ocdialog({ 128 | modal: true, 129 | closeOnEscape: true, 130 | title : t('calendarplus', 'Reminder Alert'), 131 | height: 'auto', width: 'auto', 132 | buttons: 133 | [{ 134 | text:t('calendarplus', 'Ok'), click: function() { 135 | $(this).ocdialog("close"); 136 | }, 137 | defaultButton: true 138 | }], 139 | close: function(/*event, ui*/) { 140 | $(this).ocdialog('destroy').remove(); 141 | $('#reminderBox').remove(); 142 | 143 | }, 144 | }); 145 | 146 | 147 | return false; 148 | 149 | 150 | }; -------------------------------------------------------------------------------- /js/geo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011 Georg Ehrke 3 | * This file is licensed under the Affero General Public License version 3 or 4 | * later. 5 | * See the COPYING-README file. 6 | */ 7 | $(document).ready(function(){ 8 | var timezone = jstz.determine(); 9 | var timezoneName = timezone.name(); 10 | 11 | $.post(OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingsgetguesstimezoneuser'), {timezone: timezoneName}, 12 | function(data){ 13 | 14 | if (data.status == 'success' && typeof(data.message) != 'undefined'){ 15 | $('#notification').html(data.message); 16 | $('#notification').slideDown(); 17 | window.setTimeout(function(){$('#notification').slideUp();}, 5000); 18 | $('#fullcalendar').fullCalendar('refetchEvents'); 19 | } 20 | }); 21 | }); -------------------------------------------------------------------------------- /js/jquery.combobox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired by http://jqueryui.com/demos/autocomplete/#multiple 3 | */ 4 | 5 | (function($) { 6 | $.widget("custom.combobox", { 7 | _create : function() { 8 | this.wrapper = $("").addClass("custom-combobox").insertAfter(this.element); 9 | this.element.hide(); 10 | this._createAutocomplete(); 11 | this._createShowAllButton(); 12 | }, 13 | _createAutocomplete : function() { 14 | var selected = this.element.children(":selected"), value = selected.val() ? selected.text() : ""; 15 | this.input = $("").appendTo(this.wrapper).val(value).attr("title", "").addClass("custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left").autocomplete({ 16 | delay : 0, 17 | minLength : 0, 18 | source : $.proxy(this, "_source") 19 | }).tooltip({ 20 | tooltipClass : "ui-state-highlight" 21 | }); 22 | this._on(this.input, { 23 | autocompleteselect : function(event, ui) { 24 | ui.item.option.selected = true; 25 | this._trigger("select", event, { 26 | item : ui.item.option 27 | }); 28 | }, 29 | autocompletechange : "_removeIfInvalid" 30 | }); 31 | }, 32 | _createShowAllButton : function() { 33 | var input = this.input, wasOpen = false; 34 | $("").attr("tabIndex", -1).attr("title", "Show All Items").tooltip().appendTo(this.wrapper).button({ 35 | icons : { 36 | primary : "ui-icon-triangle-1-s" 37 | }, 38 | text : false 39 | }).removeClass("ui-corner-all").addClass("custom-combobox-toggle ui-corner-right").mousedown(function() { 40 | wasOpen = input.autocomplete("widget").is(":visible"); 41 | }).click(function() { 42 | input.focus(); 43 | // Close if already visible 44 | if (wasOpen) { 45 | return; 46 | } 47 | // Pass empty string as value to search for, displaying all results 48 | input.autocomplete("search", ""); 49 | }); 50 | }, 51 | _source : function(request, response) { 52 | var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); 53 | response(this.element.children("option").map(function() { 54 | var text = $(this).text(); 55 | if (this.value && (!request.term || matcher.test(text) )) 56 | return { 57 | label : text, 58 | value : text, 59 | option : this 60 | }; 61 | })); 62 | }, 63 | _removeIfInvalid : function(event, ui) { 64 | // Selected an item, nothing to do 65 | if (ui.item) { 66 | return; 67 | } 68 | // Search for a match (case-insensitive) 69 | var value = this.input.val(), valueLowerCase = value.toLowerCase(), valid = false; 70 | this.element.children("option").each(function() { 71 | if ($(this).text().toLowerCase() === valueLowerCase) { 72 | this.selected = valid = true; 73 | return false; 74 | } 75 | }); 76 | // Found a match, nothing to do 77 | if (valid) { 78 | return; 79 | } 80 | // Remove invalid value 81 | this.input.val("").attr("title", value + " didn't match any item").tooltip("open"); 82 | this.element.val(""); 83 | this._delay(function() { 84 | this.input.tooltip("close").attr("title", ""); 85 | }, 2500); 86 | this.input.data("ui-autocomplete").term = ""; 87 | }, 88 | _destroy : function() { 89 | this.wrapper.remove(); 90 | this.element.show(); 91 | } 92 | }); 93 | })(jQuery); -------------------------------------------------------------------------------- /js/jquery.multi-autocomplete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired by http://jqueryui.com/demos/autocomplete/#multiple 3 | */ 4 | 5 | (function( $ ) { 6 | $.widget('ui.multiple_autocomplete', { 7 | _create: function() { 8 | var self = this; 9 | function split( val ) { 10 | return val.split( /,\s*/ ); 11 | } 12 | function extractLast( term ) { 13 | return split( term ).pop(); 14 | } 15 | function showOptions() { 16 | if(!self.element.autocomplete('widget').is(':visible') && self.element.val().trim() == '') { 17 | self.element.autocomplete('search', ''); 18 | } 19 | } 20 | //console.log('_create: ' + this.options['id']); 21 | this.element.bind('click', function( event ) { 22 | showOptions(); 23 | }); 24 | this.element.bind('input', function( event ) { 25 | showOptions(); 26 | }); 27 | this.element.bind('blur', function( event ) { 28 | var tmp = self.element.val().trim(); 29 | if(tmp[tmp.length-1] == ',') { 30 | self.element.val(tmp.substring(0, tmp.length-1)); 31 | } else { 32 | self.element.val(tmp); 33 | } 34 | if(self.element.val().trim() != '') { 35 | self.element.trigger('change'); // Changes wasn't saved when only using the dropdown. 36 | } 37 | }); 38 | this.element.bind( "keydown", function( event ) { 39 | if ( event.keyCode === $.ui.keyCode.TAB && 40 | $( this ).data( "autocomplete" ).menu.active ) { 41 | event.preventDefault(); 42 | } 43 | }) 44 | .autocomplete({ 45 | minLength: 0, 46 | source: function( request, response ) { 47 | // delegate back to autocomplete, but extract the last term 48 | response( $.ui.autocomplete.filter( 49 | self.options.source, extractLast( request.term ) ) ); 50 | }, 51 | focus: function() { 52 | // prevent value inserted on focus 53 | return false; 54 | }, 55 | select: function( event, ui ) { 56 | var terms = split( this.value ); 57 | 58 | // remove the current input 59 | terms.pop(); 60 | // add the selected item 61 | terms.push( ui.item.name ); 62 | // add placeholder to get the comma-and-space at the end 63 | terms.push( "" ); 64 | this.value = terms.join( ", " ); 65 | return false; 66 | } 67 | }).data( "ui-autocomplete" )._renderItem = function( ul, item ) { 68 | 69 | return $( '
  • ' ) 70 | .append( '
     ' + item.name + '
    ' ) 71 | .appendTo( ul ); 72 | }; 73 | /*this.button = $( "" ) 74 | .attr( "tabIndex", -1 ) 75 | .attr( "title", "Show All Items" ) 76 | .insertAfter( this.element ) 77 | .addClass('svg') 78 | .addClass('action') 79 | .addClass('combo-button') 80 | .click(function() { 81 | // close if already visible 82 | if ( self.element.autocomplete( "widget" ).is( ":visible" ) ) { 83 | self.element.autocomplete( "close" ); 84 | return; 85 | } 86 | 87 | // work around a bug (likely same cause as #5265) 88 | $( this ).blur(); 89 | 90 | var tmp = self.element.val().trim(); 91 | if(tmp[tmp.length-1] != ',') { 92 | self.element.val(tmp+', '); 93 | } 94 | // pass empty string as value to search for, displaying all results 95 | self.element.autocomplete( "search", "" ); 96 | self.element.focus(); 97 | });*/ 98 | }, 99 | }); 100 | })( jQuery ); 101 | -------------------------------------------------------------------------------- /js/jquery.scrollTo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com 3 | * Licensed under MIT 4 | * @author Ariel Flesler 5 | * @version 1.4.11 6 | */ 7 | ;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var j=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};j.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};j.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(f,g,h){if(typeof g=='object'){h=g;g=0}if(typeof h=='function')h={onAfter:h};if(f=='max')f=9e9;h=$.extend({},j.defaults,h);g=g||h.duration;h.queue=h.queue&&h.axis.length>1;if(h.queue)g/=2;h.offset=both(h.offset);h.over=both(h.over);return this._scrollable().each(function(){if(f==null)return;var d=this,$elem=$(d),targ=f,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}var e=$.isFunction(h.offset)&&h.offset(d,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=j.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=e[pos]||0;if(h.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*h.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&h.queue){if(old!=attr[key])animate(h.onAfterFirst);delete attr[key]}});animate(h.onAfter);function animate(a){$elem.animate(attr,g,h.easing,a&&function(){a.call(this,targ,h)})}}).end()};j.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||typeof a=='object'?a:{top:a,left:a}};return j})); 8 | -------------------------------------------------------------------------------- /js/jquery.ui.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Touch Punch 0.2.3 3 | * 4 | * Copyright 2011–2014, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); -------------------------------------------------------------------------------- /js/on-event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ownCloud - CalendarPlus 3 | * 4 | * @author Sebastian Doell 5 | * @copyright 2015 sebastian doell sebastian@libasys.de 6 | * 7 | * Copyright (c) 2012 Georg Ehrke 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 10 | * License as published by the Free Software Foundation; either 11 | * version 3 of the License, or any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public 19 | * License along with this library. If not, see . 20 | * 21 | */ 22 | 23 | /**SETTINGS EVENTS**/ 24 | $(document).on('click', '.chooseCalendar-activeCalendar', function () { 25 | CalendarPlus.UI.Calendar.activation(this,$(this).data('id')); 26 | }); 27 | 28 | $(document).on('click', '.chooseCalendar-showCalDAVURL', function () { 29 | CalendarPlus.UI.showCalDAVUrl($(this).data('user'), $(this).data('caldav')); 30 | }); 31 | 32 | $(document).on('click', '.chooseCalendar-edit', function () { 33 | CalendarPlus.UI.Calendar.edit($(this), $(this).data('id')); 34 | }); 35 | 36 | $(document).on('click', '.chooseCalendar-delete', function () { 37 | CalendarPlus.UI.Calendar.deleteCalendar($(this).data('id')); 38 | }); 39 | 40 | $(document).on('click', '#caldav_url_close', function () { 41 | $('#caldav_url').hide();$('#caldav_url_close').hide(); 42 | }); 43 | 44 | $(document).on('mouseover', '#caldav_url', function () { 45 | $('#caldav_url').select(); 46 | }); 47 | 48 | $(document).on('click', '#newCalendar', function () { 49 | CalendarPlus.UI.Calendar.newCalendar(this); 50 | }); 51 | 52 | /**END**/ 53 | 54 | $(document).on('click', '#editCategories', function () { 55 | $(this).tipsy('hide');OC.Tags.edit('event'); 56 | }); 57 | 58 | $(document).on('click', '#allday_checkbox', function () { 59 | CalendarPlus.UI.lockTime(); 60 | }); 61 | 62 | /* 63 | $(document).on('click', '#submitNewEvent', function () { 64 | Calendar.UI.validateEventForm($(this).data('link')); 65 | }); 66 | 67 | $(document).on('click', '#editEvent-submit', function () { 68 | Calendar.UI.validateEventForm($(this).data('link')); 69 | });*/ 70 | 71 | $(document).on('click', '#allday_checkbox', function () { 72 | Calendar.UI.lockTime(); 73 | }); 74 | 75 | 76 | 77 | /**NEW**/ 78 | /* 79 | $(document).on('click', '#editEvent-delete-single', function () { 80 | Calendar.UI.submitDeleteEventSingleForm($(this).data('link')); 81 | });*/ 82 | 83 | $(document).on('click', '#editEvent-export', function () { 84 | window.location = $(this).data('link'); 85 | }); 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /js/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ownCloud - CalendarPlus 3 | * 4 | * @author Sebastian Doell 5 | * @copyright 2015 sebastian doell sebastian@libasys.de 6 | * 7 | * Copyright (c) 2012 Georg Ehrke 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 10 | * License as published by the Free Software Foundation; either 11 | * version 3 of the License, or any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public 19 | * License along with this library. If not, see . 20 | * 21 | */ 22 | 23 | 24 | $(document).ready(function(){ 25 | 26 | $('.viewsettings').change( function(){ 27 | $.post( OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingssaveuserview'), { 28 | 'checked' : $(this).is(':checked'), 29 | 'name' : $(this).attr('name') 30 | }, function(jsondata){ 31 | if(jsondata.status == 'success'){ 32 | CalendarPlus.calendarConfig['userconfig'][jsondata.data.name]= jsondata.data.checked; 33 | if(jsondata.data.checked === 'true'){ 34 | $('.view button[data-action="'+jsondata.data.name+'"]').show(); 35 | }else{ 36 | $('.view button[data-action="'+jsondata.data.name+'"]').hide(); 37 | } 38 | } 39 | //OC.msg.finishedSaving('.msgTzd', jsondata); 40 | }); 41 | return false; 42 | }); 43 | /* 44 | $('#timeformat').chosen(); 45 | $('#firstday').chosen(); 46 | $('#timezone').chosen();*/ 47 | 48 | $('#timezone').change( function(){ 49 | var post = $( '#timezone' ).serialize(); 50 | $.post( OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingssettimezone'), post, function(jsondata){ 51 | $('#fullcalendar').fullCalendar('destroy'); 52 | CalendarPlus.init(); 53 | OC.msg.finishedSaving('.msgTz', jsondata); 54 | }); 55 | return false; 56 | }); 57 | 58 | $('#timeformat').change( function(){ 59 | var data = $('#timeformat').serialize(); 60 | $.post( OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingssettimeformat'), data, function(jsondata){ 61 | OC.msg.finishedSaving('.msgTf', jsondata); 62 | CalendarPlus.calendarConfig['agendatime'] = jsondata.data.agendaTime; 63 | CalendarPlus.calendarConfig['defaulttime'] = jsondata.data.defaultTime; 64 | $('#fullcalendar').fullCalendar('destroy'); 65 | CalendarPlus.init(); 66 | }); 67 | return false; 68 | }); 69 | 70 | $('#firstday').change( function(){ 71 | var data = $('#firstday').serialize(); 72 | $.post( OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingssetfirstday'), data, function(jsondata){ 73 | OC.msg.finishedSaving('.msgFd', jsondata); 74 | CalendarPlus.calendarConfig['firstDay'] = jsondata.firstday; 75 | $("#datepickerNav").datepicker('option', 'firstDay', jsondata.firstday); 76 | $('#fullcalendar').fullCalendar('destroy'); 77 | CalendarPlus.init(); 78 | 79 | }); 80 | return false; 81 | }); 82 | 83 | $('#timezonedetection').change( function(){ 84 | var data = $('#timezonedetection').serialize(); 85 | $.post( OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingstimezonedetection'), data, function(jsondata){ 86 | OC.msg.finishedSaving('.msgTzd', jsondata); 87 | }); 88 | return false; 89 | }); 90 | 91 | $('#cleancalendarcache').click(function(){ 92 | $.getJSON(OC.generateUrl('apps/'+CalendarPlus.appname+'/calendarsettingsrescancal'), function(jsondata){ 93 | OC.msg.finishedSaving('.msgCcc', jsondata); 94 | }); 95 | }); 96 | 97 | 98 | 99 | }); 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /js/share.config.js: -------------------------------------------------------------------------------- 1 | /*Options 2 | 3 | defaultView: month, agendaDay, agendaThreeDays, agendaWorkWeek, agendaWeek, list 4 | firstDay: 0- 6 (0 = Sunday, 1 = Monday,...) 5 | agendatime: timeformat 24 hours HH:mm { - HH:mm} or for timeformat 12 hours hh:mm tt { - hh:mm tt} 6 | defaulttime: timeformat 24 hours HH:mm or for timeformat 12 hours hh:mm tt 7 | calendarViews: 'prev','agendaDay','agendaThreeDays','agendaWorkWeek','agendaWeek','month','year','list','next' //choosen buttons 8 | smallCalendarLeft: true / false //shows small calendar on the left side 9 | showTimeZone: true / false //shows the selectbox for timezone selection, if false it shows only the text 10 | header: false //Removes the header 11 | footer: false //removes the footer 12 | //Parameters for yearview 13 | yearColumns: 2, // Month per Row 14 | firstMonth:0, 15 | lastMonth:6, 16 | monthClickable: true/false 17 | hiddenMonths:[1,2], // disable some month 18 | * */ 19 | 20 | CalendarShare.defaultConfig ={}; 21 | 22 | CalendarShare.defaultConfig[0]={ 23 | 'defaultView' : 'month' , 24 | 'agendatime' : 'HH:mm { - HH:mm}', 25 | 'defaulttime' : 'HH:mm', 26 | 'dateformat' : 'd.m.Y', 27 | 'timeformat' : 'H:i', 28 | 'firstDay' : 1, 29 | 'calendarViews': ['agendaDay','agendaWeek','month'], 30 | 'smallCalendarLeft': true, 31 | 'showTimeZone': true, 32 | 'showTodayButton': true, 33 | 'header': true, 34 | 'footer' : true, 35 | 'yearColumns' : 2, 36 | 'monthClickable': true, 37 | 'firstMonth':0, 38 | 'lastMonth':12, 39 | 'hiddenMonths':null 40 | }; 41 | 42 | // the index is the id of the calendar created in the database for example calendar with ID = 9 43 | /* 44 | CalendarShare.defaultConfig[11]={ 45 | 'defaultView' : 'year' , 46 | 'agendatime' : 'HH:mm { - HH:mm}', 47 | 'defaulttime' : 'HH:mm', 48 | 'firstDay' : 1, 49 | 'calendarViews':null, 50 | 'smallCalendarLeft': false, 51 | 'showTimeZone': false, 52 | 'header': false, 53 | 'footer' : false, 54 | 'yearColumns' : 2, 55 | 'monthClickable': false, 56 | 'showTodayButton': false, 57 | 'firstMonth':0, 58 | 'lastMonth':6, 59 | 'hiddenMonths':[2,3] 60 | };*/ 61 | -------------------------------------------------------------------------------- /lib/activitydata.php: -------------------------------------------------------------------------------- 1 | . 21 | * 22 | */ 23 | 24 | 25 | namespace OCA\CalendarPlus; 26 | 27 | class ActivityData{ 28 | 29 | 30 | /* 31 | * Emit Hook add_event_activity on lib/object.php add() 32 | * 33 | * @params array link, trans_type, summary, cal_user, cal_displayname 34 | * **/ 35 | 36 | public static function logEventActivity($params,$syncedWithDav=false,$bCal=false){ 37 | if(\OC::$server->getAppManager()->isEnabledForUser('activity')){ 38 | $sncDescr=''; 39 | if($syncedWithDav){ 40 | $sncDescr='Syncing per CalDav -> '; 41 | } 42 | $prefixMode='event_'; 43 | $prefixMode1=''; 44 | if($bCal==true){ 45 | $prefixMode='calendar_'; 46 | $prefixMode1='_calendar'; 47 | } 48 | if ($params['cal_user'] !== \OCP\User::getUser()) { 49 | 50 | $subjParam=array($sncDescr.$params['trans_type'].' '.$params['summary'],\OCP\User::getUser(),$params['cal_displayname']); 51 | \OC::$server->getActivityManager()->publishActivity(App::$appname, $params['mode'].'_by_other', $subjParam, '', '','', $params['link'],$params['cal_user'], 'shared_event_'.$params['mode'], ''); 52 | } 53 | 54 | 55 | $subjParam=array($sncDescr.$params['trans_type'].' '.$params['summary'],$params['cal_displayname']); 56 | \OC::$server->getActivityManager()->publishActivity(App::$appname, $params['mode'].$prefixMode1.'_self', $subjParam, '', '','', $params['link'], \OCP\User::getUser(), $prefixMode.$params['mode'], ''); 57 | } 58 | 59 | } 60 | 61 | 62 | 63 | } -------------------------------------------------------------------------------- /lib/connector/sabre/calendar.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | /** 26 | * This class overrides Sabre_CalDAV_Calendar::getACL() to return read/write 27 | * permissions based on user and shared state and it overrides 28 | * Sabre_CalDAV_Calendar::getChild() and Sabre_CalDAV_Calendar::getChildren() 29 | * to instantiate OC_Connector_Sabre_CalDAV_CalendarObjects. 30 | */ 31 | 32 | namespace OCA\CalendarPlus\Connector\Sabre; 33 | 34 | use OCA\CalendarPlus\Connector\CalendarConnector; 35 | use OCA\CalendarPlus\Share\ShareConnector; 36 | 37 | class Calendar extends \Sabre\CalDAV\Calendar { 38 | 39 | /** 40 | * Returns a list of ACE's for this node. 41 | * 42 | * Each ACE has the following properties: 43 | * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 44 | * currently the only supported privileges 45 | * * 'principal', a url to the principal who owns the node 46 | * * 'protected' (optional), indicating that this ACE is not allowed to 47 | * be updated. 48 | * 49 | * @return array 50 | */ 51 | public function getACL() { 52 | 53 | $readprincipal = $this->getOwner(); 54 | $writeprincipal = $this->getOwner(); 55 | 56 | $calendarConnector = new CalendarConnector(); 57 | $shareConnector = new ShareConnector(); 58 | 59 | $uid = $calendarConnector->extractUserID($this->getOwner()); 60 | $calendar = $calendarConnector->getCalendar($this->calendarInfo['id'], false, false); 61 | $user = \OCP\USER::getUser(); 62 | 63 | if($uid === $user && (bool)$calendar['issubscribe'] === true) { 64 | $readprincipal = 'principals/' .$user; 65 | $writeprincipal =''; 66 | } 67 | 68 | if($uid !== $user) { 69 | $sharedCalendar = $shareConnector->getItemSharedWithBySourceCalendar($this->calendarInfo['id']); 70 | 71 | if ($sharedCalendar && ($sharedCalendar['permissions'] & $shareConnector->getReadAccess())) { 72 | $readprincipal = 'principals/' . $user; 73 | $writeprincipal = ''; 74 | 75 | } 76 | if ($sharedCalendar && ($sharedCalendar['permissions'] & $shareConnector->getUpdateAccess())) { 77 | $readprincipal = 'principals/' . $user; 78 | $writeprincipal = 'principals/' . $user; 79 | } 80 | } 81 | 82 | $acl = array( 83 | array( 84 | 'privilege' => '{DAV:}read', 85 | 'principal' => $readprincipal, 86 | 'protected' => true, 87 | ), 88 | array( 89 | 'privilege' => '{DAV:}write', 90 | 'principal' => $writeprincipal, 91 | 'protected' => true, 92 | ), 93 | array( 94 | 'privilege' => '{DAV:}read', 95 | 'principal' => $readprincipal . '/calendar-proxy-write', 96 | 'protected' => true, 97 | ), 98 | array( 99 | 'privilege' => '{DAV:}write', 100 | 'principal' => $writeprincipal . '/calendar-proxy-write', 101 | 'protected' => true, 102 | ), 103 | array( 104 | 'privilege' => '{DAV:}read', 105 | 'principal' => $readprincipal . '/calendar-proxy-read', 106 | 'protected' => true, 107 | ), 108 | array( 109 | 'privilege' => '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}read-free-busy', 110 | 'principal' => '{DAV:}authenticated', 111 | 'protected' => true, 112 | ), 113 | 114 | ); 115 | 116 | if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { 117 | $acl[] = [ 118 | 'privilege' => '{DAV:}write', 119 | 'principal' => $writeprincipal, 120 | 'protected' => true, 121 | ]; 122 | $acl[] = [ 123 | 'privilege' => '{DAV:}write', 124 | 'principal' => $writeprincipal . '/calendar-proxy-write', 125 | 'protected' => true, 126 | ]; 127 | } 128 | 129 | return $acl; 130 | } 131 | 132 | /** 133 | * Returns a calendar object 134 | * 135 | * The contained calendar objects are for example Events or Todo's. 136 | * 137 | * @param string $name 138 | * @return Sabre_DAV_ICalendarObject 139 | */ 140 | public function getChild($name) { 141 | 142 | $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); 143 | if (!$obj) throw new \Sabre\DAV\Exception\NotFound('Calendar object not found'); 144 | return new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); 145 | 146 | } 147 | 148 | /** 149 | * Returns the full list of calendar objects 150 | * 151 | * @return array 152 | */ 153 | public function getChildren() { 154 | 155 | $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); 156 | $children = array(); 157 | foreach($objs as $obj) { 158 | $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); 159 | } 160 | return $children; 161 | 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /lib/connector/sabre/calendarobject.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | /** 25 | * This class overrides Sabre_CalDAV_CalendarObject::getACL() 26 | * to return read/write permissions based on user and shared state. 27 | */ 28 | 29 | namespace OCA\CalendarPlus\Connector\Sabre; 30 | 31 | use OCA\CalendarPlus\Connector\CalendarConnector; 32 | use OCA\CalendarPlus\Share\ShareConnector; 33 | use OCA\CalendarPlus\Service\ObjectParser; 34 | 35 | class CalendarObject extends \Sabre\CalDAV\CalendarObject { 36 | 37 | /** 38 | * Returns a list of ACE's for this node. 39 | * 40 | * Each ACE has the following properties: 41 | * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 42 | * currently the only supported privileges 43 | * * 'principal', a url to the principal who owns the node 44 | * * 'protected' (optional), indicating that this ACE is not allowed to 45 | * be updated. 46 | * 47 | * @return array 48 | */ 49 | public function getACL() { 50 | 51 | $readprincipal = $this->getOwner(); 52 | $writeprincipal = $this->getOwner(); 53 | 54 | $user = \OCP\USER::getUser(); 55 | $calendarConnector = new CalendarConnector(); 56 | $shareConnector = new ShareConnector(); 57 | $objectParser = new ObjectParser($user); 58 | 59 | $uid = $calendarConnector->extractUserID($this->getOwner()); 60 | 61 | 62 | if($uid != $user) { 63 | $object = $objectParser->parse($this->objectData['calendardata']); 64 | $sharedCalendar = $shareConnector->getItemSharedWithBySourceCalendar($this->calendarInfo['id']); 65 | $sharedAccessClassPermissions = $objectParser->getAccessClassPermissions($object); 66 | 67 | if ($sharedCalendar && ($sharedCalendar['permissions'] & $shareConnector->getReadAccess()) && ($sharedAccessClassPermissions & $shareConnector->getReadAccess())) { 68 | $readprincipal = 'principals/' . $user; 69 | } 70 | if ($sharedCalendar && ($sharedCalendar['permissions'] & $shareConnector->getUpdateAccess()) && ($sharedAccessClassPermissions & $shareConnector->getUpdateAccess())) { 71 | $writeprincipal = 'principals/' . $user; 72 | }else{ 73 | $writeprincipal = ''; 74 | } 75 | } 76 | 77 | return array( 78 | array( 79 | 'privilege' => '{DAV:}read', 80 | 'principal' => $readprincipal, 81 | 'protected' => true, 82 | ), 83 | array( 84 | 'privilege' => '{DAV:}write', 85 | 'principal' => $writeprincipal, 86 | 'protected' => true, 87 | ), 88 | array( 89 | 'privilege' => '{DAV:}read', 90 | 'principal' => $readprincipal . '/calendar-proxy-write', 91 | 'protected' => true, 92 | ), 93 | array( 94 | 'privilege' => '{DAV:}write', 95 | 'principal' => $writeprincipal . '/calendar-proxy-write', 96 | 'protected' => true, 97 | ), 98 | array( 99 | 'privilege' => '{DAV:}read', 100 | 'principal' => $readprincipal . '/calendar-proxy-read', 101 | 'protected' => true, 102 | ), 103 | ); 104 | 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /lib/connector/sabre/calendarroot.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | /** 26 | * This class overrides Sabre_CalDAV_CalendarRootNode::getChildForPrincipal() 27 | * to instantiate OC_Connector_Sabre_CalDAV_UserCalendars. 28 | */ 29 | 30 | namespace OCA\CalendarPlus\Connector\Sabre; 31 | 32 | class CalendarRoot extends \Sabre\CalDAV\CalendarRootNode { 33 | 34 | /** 35 | * This method returns a node for a principal. 36 | * 37 | * The passed array contains principal information, and is guaranteed to 38 | * at least contain a uri item. Other properties may or may not be 39 | * supplied by the authentication backend. 40 | * 41 | * @param array $principal 42 | * @return Sabre_DAV_INode 43 | */ 44 | public function getChildForPrincipal(array $principal) { 45 | 46 | return new UserCalendars($this->caldavBackend, $principal); 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /lib/connector/sabre/schedulingsupport.php: -------------------------------------------------------------------------------- 1 | calendarConnector = new CalendarConnector(); 17 | $this->objectParser = new ObjectParser(\OCP\USER::getUser()); 18 | } 19 | /** 20 | * Returns a single scheduling object for the inbox collection. 21 | * 22 | * The returned array should contain the following elements: 23 | * * uri - A unique basename for the object. This will be used to 24 | * construct a full uri. 25 | * * calendardata - The iCalendar object 26 | * * lastmodified - The last modification date. Can be an int for a unix 27 | * timestamp, or a PHP DateTime object. 28 | * * etag - A unique token that must change if the object changed. 29 | * * size - The size of the object, in bytes. 30 | * 31 | * @param string $principalUri 32 | * @param string $objectUri 33 | * @return array 34 | */ 35 | function getSchedulingObject($principalUri, $objectUri){ 36 | $data = $this->calendarConnector->findObjectWhereDAVDataIs($calendarId,$objectUri); 37 | 38 | } 39 | 40 | /** 41 | * Returns all scheduling objects for the inbox collection. 42 | * 43 | * These objects should be returned as an array. Every item in the array 44 | * should follow the same structure as returned from getSchedulingObject. 45 | * 46 | * The main difference is that 'calendardata' is optional. 47 | * 48 | * @param string $principalUri 49 | * @return array 50 | */ 51 | function getSchedulingObjects($principalUri){ 52 | $raw = $this->calendarConnector->allCalendarsWherePrincipalURIIs($principalUri); 53 | 54 | } 55 | 56 | /** 57 | * Deletes a scheduling object from the inbox collection. 58 | * 59 | * @param string $principalUri 60 | * @param string $objectUri 61 | * @return void 62 | */ 63 | function deleteSchedulingObject($principalUri, $objectUri); 64 | 65 | 66 | /** 67 | * Creates a new scheduling object. This should land in a users' inbox. 68 | * 69 | * @param string $principalUri 70 | * @param string $objectUri 71 | * @param string $objectData 72 | * @return void 73 | */ 74 | function createSchedulingObject($principalUri, $objectUri, $objectData); 75 | 76 | /** 77 | * @brief Creates a etag 78 | * @param array $row Database result 79 | * @returns associative array 80 | * 81 | * Adds a key "etag" to the row 82 | */ 83 | private function OCAddETag($row) { 84 | $row['etag'] = '"'.md5($row['calendarid'].$row['uri'].$row['calendardata'].$row['lastmodified']).'"'; 85 | return $row; 86 | } 87 | } -------------------------------------------------------------------------------- /lib/connector/sabre/usercalendars.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | /** 26 | * This class overrides Sabre_CalDAV_UserCalendars::getChildren() 27 | * to instantiate OC_Connector_Sabre_CalDAV_Calendars. 28 | */ 29 | namespace OCA\CalendarPlus\Connector\Sabre; 30 | 31 | class UserCalendars extends \Sabre\CalDAV\CalendarHome { 32 | 33 | /** 34 | * Returns a list of calendars 35 | * 36 | * @return array 37 | */ 38 | public function getChildren() { 39 | 40 | $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); 41 | 42 | $objs = array(); 43 | 44 | foreach($calendars as $calendar) { 45 | $objs[] = new Calendar($this->caldavBackend, $calendar); 46 | } 47 | $objs[] = new \Sabre\CalDAV\Schedule\Outbox($this->principalInfo['uri']); 48 | return $objs; 49 | 50 | 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /lib/export.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | /** 25 | * This class does export and converts all times to UTC 26 | */ 27 | namespace OCA\CalendarPlus; 28 | 29 | class Export{ 30 | /** 31 | * @brief Use one of these constants as second parameter if you call Export::export() 32 | */ 33 | const CALENDAR = 'calendar'; 34 | const EVENT = 'event'; 35 | 36 | /** 37 | * @brief export a calendar or an event 38 | * @param integer $id id of calendar / event 39 | * @param string $type use Export constants 40 | * @return string 41 | */ 42 | public static function export($id, $type) { 43 | if($type === self::EVENT) { 44 | $return = self::event($id); 45 | }else{ 46 | $return = self::calendar($id); 47 | } 48 | return self::fixLineBreaks($return); 49 | } 50 | 51 | /** 52 | * @brief exports a calendar and convert all times to UTC 53 | * @param integer $id id of the calendar 54 | * @return string 55 | */ 56 | private static function calendar($id) { 57 | $events = Object::all($id); 58 | $calendar = Calendar::find($id); 59 | $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . \OCP\App::getAppVersion(App::$appname) . "\nX-WR-CALNAME:" . $calendar['displayname'] . "\n"; 60 | $return .= self::addVtimezone(); 61 | 62 | foreach($events as $event) { 63 | $return .= self::generateEvent($event); 64 | } 65 | $return .= "END:VCALENDAR"; 66 | return $return; 67 | } 68 | 69 | private static function addVtimezone(){ 70 | 71 | $tz=App::getTimezone(); 72 | $ex=explode('/', $tz, 2); 73 | $aTzTimes = App::getTzDaylightStandard(); 74 | 75 | if(isset($ex[1]) && array_key_exists($ex[0], $aTzTimes)){ 76 | $summerTime=$aTzTimes[$ex[0]]['daylight']; 77 | $winterTime=$aTzTimes[$ex[0]]['standard']; 78 | 79 | $dateOffsetSummer=new \DateTime($summerTime, new \DateTimeZone('UTC')); 80 | $dateOffsetSummer -> setTimezone(new \DateTimeZone($tz)); 81 | $offsetSummer= $dateOffsetSummer->format('O') ; 82 | $offsetSummerTZ= $dateOffsetSummer->format('T') ; 83 | $dateOffsetWinter=new \DateTime($winterTime, new \DateTimeZone('UTC')); 84 | $dateOffsetWinter -> setTimezone(new \DateTimeZone($tz)); 85 | $offsetWinter= $dateOffsetWinter->format('O') ; 86 | $offsetWinterTZ= $dateOffsetWinter->format('T') ; 87 | 88 | $sTimeZone = "BEGIN:VTIMEZONE\nTZID:".$tz."\n"; 89 | $sTimeZone .="BEGIN:DAYLIGHT\n"; 90 | $sTimeZone .="TZOFFSETFROM:".$offsetWinter."\n"; 91 | $sTimeZone .="RRULE:FREQ=YEARLY;BYMONTH=".$aTzTimes[$ex[0]]['daylightstart'].";BYDAY=-1SU\n"; 92 | $sTimeZone .="DTSTART:".$summerTime."\n"; 93 | $sTimeZone .="TZNAME:".$offsetSummerTZ."\n"; 94 | $sTimeZone .="TZOFFSETTO:".$offsetSummer."\n"; 95 | $sTimeZone .="END:DAYLIGHT\n"; 96 | $sTimeZone .="BEGIN:STANDARD\n"; 97 | $sTimeZone .="TZOFFSETFROM:".$offsetSummer."\n"; 98 | $sTimeZone .="RRULE:FREQ=YEARLY;BYMONTH=".$aTzTimes[$ex[0]]['daylightend'].";BYDAY=-1SU\n"; 99 | $sTimeZone .="DTSTART:".$winterTime."\n"; 100 | $sTimeZone .="TZNAME:".$offsetWinterTZ."\n"; 101 | $sTimeZone .="TZOFFSETTO:".$offsetWinter."\n"; 102 | $sTimeZone .="END:STANDARD\n"; 103 | $sTimeZone .="END:VTIMEZONE\n"; 104 | 105 | return $sTimeZone; 106 | } 107 | } 108 | /** 109 | * @brief exports an event and convert all times to UTC 110 | * @param integer $id id of the event 111 | * @return string 112 | */ 113 | private static function event($id) { 114 | $event = Object::find($id); 115 | $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . \OCP\App::getAppVersion(App::$appname) . "\nX-WR-CALNAME:" . $event['summary'] . "\n"; 116 | $object = VObject::parse($event['calendardata']); 117 | if($object->VTIMEZONE){ 118 | $return .= self::addVtimezone(); 119 | } 120 | 121 | $return .= self::generateEvent($event); 122 | $return .= "END:VCALENDAR"; 123 | return $return; 124 | } 125 | 126 | /** 127 | * @brief generates the VEVENT/VTODO/VJOURNAL with UTC dates 128 | * @param array $event 129 | * @return string 130 | */ 131 | private static function generateEvent($event) { 132 | $object = VObject::parse($event['calendardata']); 133 | $serializeObjects=''; 134 | if(!$object){ 135 | return false; 136 | } 137 | 138 | $sharedAccessClassPermissions = Object::getAccessClassPermissions($object); 139 | if(Object::getowner($event['id']) !== \OCP\User::getUser()){ 140 | if (!($sharedAccessClassPermissions & \OCP\PERMISSION_READ)) { 141 | return ''; 142 | } 143 | } 144 | $object = Object::cleanByAccessClass($event['id'], $object); 145 | 146 | if($object->VEVENT){ 147 | /* 148 | $dtstart = $object->VEVENT->DTSTART; 149 | $start_dt = $dtstart->getDateTime(); 150 | $dtend = Object::getDTEndFromVEvent($object->VEVENT); 151 | $end_dt = $dtend->getDateTime(); 152 | if($dtstart->getValueType() !== 'DATE') { 153 | $start_dt->setTimezone(new \DateTimeZone('UTC')); 154 | $end_dt->setTimezone(new \DateTimeZone('UTC')); 155 | $object->VEVENT->setDateTime('DTSTART', $start_dt); 156 | $object->VEVENT->setDateTime('DTEND', $end_dt); 157 | }*/ 158 | return $object->VEVENT->serialize(); 159 | 160 | } 161 | if($object->VTODO){ 162 | return $object->VTODO->serialize(); 163 | } 164 | if($object->VJOURNAL){ 165 | return $object->VJOURNAL->serialize(); 166 | } 167 | 168 | 169 | 170 | return ''; 171 | } 172 | 173 | /** 174 | * @brief fixes new line breaks 175 | * (fixes problems with Apple iCal) 176 | * @param string $string to fix 177 | * @return string 178 | */ 179 | private static function fixLineBreaks($string) { 180 | $string = str_replace("\r\n", "\n", $string); 181 | $string = str_replace("\r", "\n", $string); 182 | $string = str_replace("\n", "\r\n", $string); 183 | return $string; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /lib/search/provider.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | namespace OCA\CalendarPlus\Search; 26 | 27 | use \OCA\CalendarPlus\Calendar as CalendarCalendar; 28 | use \OCA\CalendarPlus\App as CalendarApp; 29 | use \OCA\CalendarPlus\Object; 30 | use \OCA\CalendarPlus\VObject; 31 | 32 | /** 33 | * Provide search results from the 'calendar' app 34 | */ 35 | class Provider extends \OCP\Search\Provider { 36 | 37 | /** 38 | * 39 | * @param string $query 40 | * @return \OCP\Search\Result 41 | */ 42 | function search($query) { 43 | 44 | $today= date('Y-m-d',time()); 45 | $allowedCommands=array('#ra'=>1,'#dt'=>1); 46 | 47 | $calendars = CalendarCalendar::allCalendars(\OCP\USER::getUser(), true); 48 | $activeCalendars = ''; 49 | $config = \OC::$server->getConfig(); 50 | 51 | foreach($calendars as $calendar) { 52 | $isAktiv = $calendar['active']; 53 | 54 | if($config -> getUserValue(\OCP\USER::getUser(), CalendarApp::$appname, 'calendar_'.$calendar['id'])!=''){ 55 | $isAktiv= (int)$config -> getUserValue(\OCP\USER::getUser(),CalendarApp::$appname, 'calendar_'.$calendar['id']); 56 | } 57 | if(!array_key_exists('active', $calendar)){ 58 | $isAktiv = 1; 59 | } 60 | if($isAktiv == 1 && (int) $calendar['issubscribe'] === 0) { 61 | $activeCalendars[] = $calendar; 62 | } 63 | } 64 | 65 | 66 | 67 | if(count($activeCalendars) === 0 || !\OCP\App::isEnabled(CalendarApp::$appname)) { 68 | //return false; 69 | } 70 | $results = array(); 71 | $searchquery = array(); 72 | if(substr_count($query, ' ') > 0) { 73 | $searchquery = explode(' ', $query); 74 | }else{ 75 | $searchquery[] = $query; 76 | } 77 | 78 | 79 | $user_timezone = CalendarApp::getTimezone(); 80 | $l = \OC::$server->getL10N(CalendarApp::$appname); 81 | 82 | $isDate=false; 83 | if(strlen($query) >= 5 && self::validateDate($query)){ 84 | $isDate=true; 85 | //\OCP\Util::writeLog('calendar','VALID DATE FOUND', \OCP\Util::DEBUG); 86 | } 87 | 88 | foreach($activeCalendars as $calendar) { 89 | $objects = Object::all($calendar['id']); 90 | foreach($objects as $object) { 91 | if($object['objecttype'] !== 'VEVENT') { 92 | continue; 93 | } 94 | 95 | 96 | $searchAdvanced=false; 97 | 98 | if($isDate === true && strlen($query)>= 5 ){ 99 | // \OCP\Util::writeLog('calendar','search: ->'.$query, \OCP\Util::DEBUG); 100 | $tempQuery = strtotime($query); 101 | $checkDate = date('Y-m-d',$tempQuery); 102 | if(substr_count($object['startdate'],$checkDate) > 0){ 103 | $searchAdvanced = true; 104 | } 105 | } 106 | 107 | if(array_key_exists($query,$allowedCommands) && $allowedCommands[$query]){ 108 | if($query === '#dt'){ 109 | $search=$object['startdate']; 110 | if(substr_count($search,$today) > 0){ 111 | $searchAdvanced = true; 112 | 113 | } 114 | } 115 | 116 | if($query=='#ra'){ 117 | if($object['isalarm'] === 1){ 118 | $searchAdvanced = true; 119 | } 120 | 121 | } 122 | } 123 | 124 | if(substr_count(strtolower($object['summary']), strtolower($query)) > 0 || $searchAdvanced === true) { 125 | $calendardata = VObject::parse($object['calendardata']); 126 | $vevent = $calendardata->VEVENT; 127 | if (Object::getowner($object['id']) !== \OCP\USER::getUser()) { 128 | if (isset($vevent -> CLASS) && $vevent -> CLASS -> getValue() === 'CONFIDENTIAL') { 129 | continue; 130 | } 131 | if (isset($vevent -> CLASS) && ($vevent -> CLASS -> getValue() === 'PRIVATE' || $vevent -> CLASS -> getValue() === '')) { 132 | continue; 133 | } 134 | } 135 | 136 | 137 | $dtstart = $vevent->DTSTART; 138 | $dtend = Object::getDTEndFromVEvent($vevent); 139 | $start_dt = $dtstart->getDateTime(); 140 | $start_dt->setTimezone(new \DateTimeZone($user_timezone)); 141 | $end_dt = $dtend->getDateTime(); 142 | $end_dt->setTimezone(new \DateTimeZone($user_timezone)); 143 | if ($dtstart->getValueType() =='DATE') { 144 | $end_dt->modify('-1 sec'); 145 | if($start_dt->format('d.m.Y') != $end_dt->format('d.m.Y')) { 146 | $info = $l->t('Date') . ': ' . $start_dt->format('d.m.Y') . ' - ' . $end_dt->format('d.m.Y'); 147 | }else{ 148 | $info = $l->t('Date') . ': ' . $start_dt->format('d.m.Y'); 149 | } 150 | }else{ 151 | $info = $l->t('Date') . ': ' . $start_dt->format('d.m.y H:i') . ' - ' . $end_dt->format('d.m.y H:i'); 152 | } 153 | $link = \OC::$server->getURLGenerator()->linkToRoute(CalendarApp::$appname.'.page.index').'#'.urlencode($object['id']); 154 | 155 | $returnData['id'] = $object['id']; 156 | $returnData['description'] = $object['summary'].' '.$info; 157 | $returnData['link'] = $link; 158 | $returnData['type'] = 'calendar'; 159 | //$results[]=$returnData; 160 | $results[]=new Result($returnData);//$name,$text,$link,$type 161 | } 162 | } 163 | } 164 | return $results; 165 | } 166 | 167 | public static function validateDate($Str){ 168 | $Stamp = strtotime( $Str ); 169 | $Month = date( 'm', $Stamp ); 170 | $Day = date( 'd', $Stamp ); 171 | $Year = date( 'Y', $Stamp ); 172 | 173 | return checkdate( $Month, $Day, $Year ); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /lib/search/result.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | namespace OCA\CalendarPlus\Search; 26 | 27 | /** 28 | * A found file 29 | */ 30 | class Result extends \OCP\Search\Result { 31 | 32 | /** 33 | * Type name; translated in templates 34 | * @var string 35 | */ 36 | public $type = 'calendarplus'; 37 | 38 | 39 | /** 40 | * Create a new file search result 41 | * @param array $data file data given by provider 42 | */ 43 | public function __construct(array $data = null) { 44 | $this->type = $data['type']; 45 | $this->id = $data['id']; 46 | $this->name = $data['description']; 47 | $this->link = $data['link']; 48 | $this->icon ='ioc ioc-calendar'; 49 | 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lib/share/backend/calendar.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | 25 | namespace OCA\CalendarPlus\Share\Backend; 26 | 27 | use OCA\CalendarPlus\Connector\CalendarConnector; 28 | use OCA\CalendarPlus\Share\ShareConnector; 29 | 30 | class Calendar implements \OCP\Share_Backend_Collection { 31 | 32 | const FORMAT_CALENDAR = 1; 33 | private $calendarConnector; 34 | private $shareConnector; 35 | 36 | public function __construct(){ 37 | $this->calendarConnector = new CalendarConnector(); 38 | $this->shareConnector = new ShareConnector(); 39 | } 40 | /** 41 | * @brief Get the source of the item to be stored in the database 42 | * @param string Item 43 | * @param string Owner of the item 44 | * @return mixed|array|false Source 45 | * 46 | * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' 47 | * Return false if the item does not exist for the user 48 | * 49 | * The formatItems() function will translate the source returned back into the item 50 | */ 51 | public function isValidSource($itemSource, $uidOwner) { 52 | 53 | $itemSource = $this->shareConnector->validateItemSource($itemSource, $this->shareConnector->getConstSharePrefixCalendar()); 54 | $calendar = $this->calendarConnector->getCalendar( $itemSource ); 55 | 56 | if ($calendar === false || $calendar['userid'] != $uidOwner) { 57 | return false; 58 | } 59 | return true; 60 | } 61 | 62 | public function isShareTypeAllowed($shareType) { 63 | return true; 64 | } 65 | 66 | 67 | 68 | /** 69 | * @brief Get a unique name of the item for the specified user 70 | * @param string Item 71 | * @param string|false User the item is being shared with 72 | * @param array|null List of similar item names already existing as shared items 73 | * @return string Target name 74 | * 75 | * This function needs to verify that the user does not already have an item with this name. 76 | * If it does generate a new name e.g. name_# 77 | */ 78 | public function generateTarget($itemSource, $shareWith, $exclude = null) { 79 | 80 | $itemSource = $this->shareConnector->validateItemSource($itemSource, $this->shareConnector->getConstSharePrefixCalendar()); 81 | $calendar = $this->calendarConnector->getCalendar( $itemSource ); 82 | 83 | $user_calendars = array(); 84 | foreach($this->calendarConnector->all($shareWith) as $user_calendar) { 85 | $user_calendars[] = $user_calendar['displayname']; 86 | } 87 | 88 | $l10n = \OC::$server->getL10N('calendarplus'); 89 | 90 | $name = $calendar['displayname'].' '.(string)$l10n->t('shared by').' '.$calendar['userid']; 91 | $suffix = ''; 92 | while (in_array($name.$suffix, $user_calendars)) { 93 | $suffix++; 94 | } 95 | 96 | return $name.$suffix; 97 | } 98 | 99 | /** 100 | * @brief Converts the shared item sources back into the item in the specified format 101 | * @param array Shared items 102 | * @param int Format 103 | * @return ? 104 | * 105 | * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info. 106 | * The key/value pairs included in the share info depend on the function originally called: 107 | * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source 108 | * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target 109 | * This function allows the backend to control the output of shared items with custom formats. 110 | * It is only called through calls to the public getItem(s)Shared(With) functions. 111 | */ 112 | public function formatItems($items, $format, $parameters = null) { 113 | $calendars = array(); 114 | if ($format == self::FORMAT_CALENDAR) { 115 | foreach ($items as $item) { 116 | $item['item_source'] = $this->shareConnector->validateItemSource($item['item_source'], $this->shareConnector->getConstSharePrefixCalendar()); 117 | $calendar = $this->calendarConnector->getCalendar( $item['item_source'], false ); 118 | 119 | if(!$calendar) { 120 | continue; 121 | } 122 | // TODO: really check $parameters['permissions'] == 'rw'/'r' 123 | if ($parameters['permissions'] == 'rw') { 124 | continue; // TODO 125 | } 126 | $calendar['displayname'] = $item['item_target']; 127 | $calendar['permissions'] = $item['permissions']; 128 | $calendar['calendarid'] = $calendar['id']; 129 | $calendar['owner'] = $calendar['userid']; 130 | $calendar['active'] = (int)$calendar['active']; 131 | $calendars[] = $calendar; 132 | } 133 | } 134 | return $calendars; 135 | } 136 | 137 | public function getChildren($itemSource) { 138 | 139 | $itemSource = $this->shareConnector->validateItemSource($itemSource, $this->shareConnector->getConstSharePrefixCalendar()); 140 | 141 | $aObjects = $this->calendarConnector->allObjects($itemSource); 142 | $children = array(); 143 | if($aObjects !== null){ 144 | foreach($aObjects as $object){ 145 | $children[] = array('source' => $object['id'], 'target' => $object['summary']); 146 | } 147 | } 148 | 149 | return $children; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /lib/share/backend/event.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | namespace OCA\CalendarPlus\Share\Backend; 25 | 26 | use OCA\CalendarPlus\Connector\CalendarConnector; 27 | use OCA\CalendarPlus\Share\ShareConnector; 28 | 29 | 30 | class Event implements \OCP\Share_Backend { 31 | 32 | const FORMAT_EVENT = 0; 33 | 34 | private static $event; 35 | 36 | public function __construct(){ 37 | $this->calendarConnector = new CalendarConnector(); 38 | $this->shareConnector = new ShareConnector(); 39 | } 40 | 41 | public function isValidSource($itemSource, $uidOwner) { 42 | 43 | $itemSource = $this->shareConnector->validateItemSource($itemSource, $this->shareConnector->getConstSharePrefixEvent()); 44 | 45 | self::$event = $this->calendarConnector->findObject($itemSource); 46 | if (self::$event) { 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | 53 | 54 | public function generateTarget($itemSource, $shareWith, $exclude = null) { 55 | 56 | $itemSource = $this->shareConnector->validateItemSource($itemSource, $this->shareConnector->getConstSharePrefixEvent()); 57 | 58 | if(!self::$event) { 59 | self::$event = $this->calendarConnector->findObject($itemSource); 60 | } 61 | return self::$event['summary']; 62 | } 63 | 64 | public function isShareTypeAllowed($shareType) { 65 | return true; 66 | } 67 | 68 | public function formatItems($items, $format, $parameters = null) { 69 | $events = array(); 70 | if ($format == self::FORMAT_EVENT) { 71 | 72 | foreach ($items as $item) { 73 | 74 | $item['item_source'] = $this->shareConnector->validateItemSource($item['item_source'], $this->shareConnector->getConstSharePrefixEvent()); 75 | 76 | if(!$this->calendarConnector->checkIfObjectIsShared($item['item_source'])){ 77 | 78 | $event = $this->calendarConnector->findObject($item['item_source']); 79 | 80 | $event['summary'] = $item['item_target']; 81 | $event['item_source'] = (int) $item['item_source']; 82 | $event['privat'] =false; 83 | $event['shared'] =false; 84 | $event['isalarm']=$event['isalarm']; 85 | $event['permissions'] = $item['permissions']; 86 | //$event['userid'] = $event['userid']; 87 | $event['orgevent'] =false; 88 | 89 | 90 | $events[] = $event; 91 | } 92 | } 93 | } 94 | return $events; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /lib/share/shareconnector.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | namespace OCA\CalendarPlus\Share; 25 | 26 | use \OCP\Share; 27 | use \OCA\CalendarPlus\Share\Backend\Calendar as ShareCalendar; 28 | 29 | class ShareConnector { 30 | const SHARECALENDAR = 'calpl'; 31 | const SHARECALENDARPREFIX = 'calendar-'; 32 | const SHAREEVENT = 'calplevent'; 33 | const SHAREEVENTPREFIX = 'event-'; 34 | const SHARETODO = 'calpltodo'; 35 | const SHARETODOPREFIX = 'todo-'; 36 | 37 | /** 38 | * @NoAdminRequired 39 | * 40 | * 41 | * @param integer $calendarid calendar id 42 | * @param string $userid 43 | *@return array || null 44 | */ 45 | public function getItemSharedWithByLinkCalendar($calendarid, $userid) { 46 | return Share::getItemSharedWithByLink(self::SHARECALENDAR, self::SHARECALENDARPREFIX . $calendarid, $userid); 47 | } 48 | 49 | public function getItemsSharedWithCalendar() { 50 | return Share::getItemsSharedWith(self::SHARECALENDAR, ShareCalendar::FORMAT_CALENDAR); 51 | } 52 | 53 | public function getConstShareCalendar() { 54 | return self::SHARECALENDAR; 55 | } 56 | 57 | public function getConstSharePrefixCalendar() { 58 | return self::SHARECALENDARPREFIX; 59 | } 60 | 61 | public function getConstShareEvent() { 62 | return self::SHAREEVENT; 63 | } 64 | 65 | public function getConstSharePrefixEvent() { 66 | return self::SHAREEVENTPREFIX; 67 | } 68 | 69 | public function getConstShareTodo() { 70 | return self::SHARETODO; 71 | } 72 | 73 | public function getConstSharePrefixTodo() { 74 | return self::SHARETODOPREFIX; 75 | } 76 | 77 | /** 78 | * 79 | * @param string $token 80 | *@return array || null 81 | */ 82 | public function getShareByToken($token) { 83 | return Share::getShareByToken($token, false); 84 | } 85 | 86 | /** 87 | * 88 | * @param array $linkItem 89 | *@return array || null 90 | */ 91 | public function resolveReShare($linkItem) { 92 | return Share::resolveReShare($linkItem); 93 | } 94 | 95 | /** 96 | * @NoAdminRequired 97 | * 98 | * 99 | * @param integer $calendarid calendar id 100 | *@return array || null 101 | */ 102 | public function getItemSharedWithBySourceCalendar($calendarid) { 103 | return Share::getItemSharedWithBySource(self::SHARECALENDAR, self::SHARECALENDARPREFIX . $calendarid); 104 | } 105 | 106 | /** 107 | * @NoAdminRequired 108 | * 109 | * 110 | * @param integer $eventid 111 | *@return array || null 112 | */ 113 | public function getItemSharedWithBySourceEvent($eventid) { 114 | return Share::getItemSharedWithBySource(self::SHAREEVENT, self::SHAREEVENTPREFIX . $eventid); 115 | } 116 | 117 | /** 118 | * @NoAdminRequired 119 | * 120 | * 121 | * @param integer $eventid 122 | *@return array || null 123 | */ 124 | public function getItemSharedWithBySourceTodo($eventid) { 125 | return Share::getItemSharedWithBySource(self::SHARETODO, self::SHARETODOPREFIX . $eventid); 126 | } 127 | 128 | /** 129 | * @NoAdminRequired 130 | * 131 | * 132 | * @param integer $eventid 133 | *@return array || null 134 | */ 135 | public function unshareAllEvent($eventid) { 136 | return Share::unshareAll(self::SHAREEVENT, self::SHAREEVENTPREFIX . $eventid); 137 | } 138 | 139 | /** 140 | * @NoAdminRequired 141 | * 142 | * 143 | * @param integer $eventid 144 | *@return array || null 145 | */ 146 | public function unshareAllTodos($eventid) { 147 | return Share::unshareAll(self::SHARETODO, self::SHARETODOPREFIX . $eventid); 148 | } 149 | 150 | /** 151 | * @NoAdminRequired 152 | * 153 | * 154 | * @param integer $calendarid 155 | *@return array || null 156 | */ 157 | public function unshareAllCalendar($calendarid) { 158 | return Share::unshareAll(self::SHARECALENDAR, self::SHARECALENDARPREFIX . $calendarid); 159 | } 160 | 161 | public function validateItemSource($itemSource, $itemType) { 162 | 163 | if (stristr($itemSource, $itemType)) { 164 | $iTempItemSource = explode($itemType, $itemSource); 165 | return (int)$iTempItemSource[1]; 166 | } else { 167 | return $itemSource; 168 | } 169 | 170 | } 171 | 172 | public function getShareTypeLink() { 173 | return Share::SHARE_TYPE_LINK; 174 | } 175 | 176 | public function getShareAccess() { 177 | return \OCP\Constants::PERMISSION_SHARE; 178 | } 179 | 180 | public function getReadAccess() { 181 | return \OCP\Constants::PERMISSION_READ; 182 | } 183 | 184 | public function getCreateAccess() { 185 | return \OCP\Constants::PERMISSION_CREATE; 186 | } 187 | 188 | public function getUpdateAccess() { 189 | return \OCP\Constants::PERMISSION_UPDATE; 190 | } 191 | 192 | public function getDeleteAccess() { 193 | return \OCP\Constants::PERMISSION_DELETE; 194 | } 195 | 196 | public function getAllAccess() { 197 | return \OCP\Constants::PERMISSION_ALL; 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /lib/vobject/stringpropertycategories.php: -------------------------------------------------------------------------------- 1 | name; 18 | if ($this->group) { 19 | $str = $this->group . '.' . $this->name; 20 | } 21 | 22 | 23 | $src = array( 24 | ';', 25 | ); 26 | $out = array( 27 | ',', 28 | ); 29 | 30 | if(is_array($this->value)){ 31 | $this->value = implode(',',$this->value); 32 | } 33 | 34 | $value = strtr($this->value, array('\,' => ',', '\;' => ';')); 35 | $str.=':' . str_replace($src, $out, $value); 36 | 37 | $out = ''; 38 | while(strlen($str) > 0) { 39 | if (strlen($str) > 75) { 40 | $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n"; 41 | $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8'); 42 | } else { 43 | $out .= $str . "\r\n"; 44 | $str = ''; 45 | break; 46 | } 47 | } 48 | 49 | return $out; 50 | 51 | } 52 | 53 | 54 | } -------------------------------------------------------------------------------- /service/contactsintegration.php: -------------------------------------------------------------------------------- 1 | . 11 | * 12 | */ 13 | 14 | 15 | namespace OCA\CalendarPlus\Service { 16 | 17 | class ContactsIntegration 18 | { 19 | /** 20 | * @var \OCP\Contacts\IManager 21 | */ 22 | private $contactsManager; 23 | 24 | public function __construct($contactsManager) { 25 | $this->contactsManager = $contactsManager; 26 | } 27 | 28 | 29 | 30 | /** 31 | * Extracts all matching contacts with email address and name 32 | * 33 | * @param string $term 34 | * @return array 35 | */ 36 | public function getMatchingLocaction($term) { 37 | if (!$this->contactsManager->isEnabled()) { 38 | return array(); 39 | } 40 | 41 | $result = $this->contactsManager->search($term, array('FN', 'ADR','N')); 42 | 43 | $addressDefArray=array('0'=>'','1'=>'','2'=>'street','3'=>'city','4'=>'','5'=>'postalcode','6'=>'country'); 44 | $aDefNArray=array('0'=>'lname','1'=>'fname','2'=>'anrede', '3'=>'title'); 45 | 46 | $receivers = array(); 47 | foreach ($result as $r) { 48 | $id = $r['id']; 49 | $fn = $r['FN']; 50 | 51 | $name = ''; 52 | if(isset($r['N'])){ 53 | list($lastname,$surename,$gender, $title) = $r['N']; 54 | $name = (!empty($surename)?$surename:''); 55 | $name .= (!empty($lastname)? ' '.$lastname:''); 56 | $name = '('.$name.')'; 57 | //$name .= (!empty($gender)?$gender:''); 58 | //$name .= (!empty($title)?' '.$title:''); 59 | } 60 | 61 | $address = $r['ADR']; 62 | 63 | if (!is_array($address)) { 64 | $address = array($address); 65 | } 66 | 67 | // loop through all email addresses of this contact 68 | foreach ($address as $e) { 69 | $aAddr=''; 70 | list($zero,$one,$street, $city, $four,$postalcode,$country) = $e; 71 | $aAddr = (!empty($street)?$street:''); 72 | $aAddr .= (!empty($postalcode)? ', '.$postalcode.' ':''); 73 | $aAddr .= (!empty($city)?$city:''); 74 | $aAddr .= (!empty($country)?', '.$country:''); 75 | 76 | 77 | 78 | $displayName = "\"$fn\" $name $aAddr"; 79 | $valueAddr = $aAddr; 80 | $receivers[] = array('id' => $id, 81 | 'label' => $displayName, 82 | 'value' => $valueAddr); 83 | } 84 | } 85 | 86 | return $receivers; 87 | } 88 | 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /share.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { 25 | header('HTTP/1.0 404 Not Found'); 26 | $tmpl = new OCP\Template('', '404', 'guest'); 27 | $tmpl->printPage(); 28 | exit(); 29 | } 30 | 31 | $urlGenerator = \OC::$server->getURLGenerator(); 32 | $token = isset($_GET['t']) ? $_GET['t'] : ''; 33 | 34 | if($token !== '') { 35 | OCP\Response::redirect($urlGenerator->linkToRoute('calendarplus.public.index', array('token' => $token))); 36 | } else { 37 | header('HTTP/1.0 404 Not Found'); 38 | $tmpl = new OCP\Template('', '404', 'guest'); 39 | $tmpl->printPage(); 40 | exit(); 41 | } -------------------------------------------------------------------------------- /shareevent.php: -------------------------------------------------------------------------------- 1 | 9 | * Copyright (c) 2012 Georg Ehrke 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public 21 | * License along with this library. If not, see . 22 | * 23 | */ 24 | if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { 25 | header('HTTP/1.0 404 Not Found'); 26 | $tmpl = new OCP\Template('', '404', 'guest'); 27 | $tmpl->printPage(); 28 | exit(); 29 | } 30 | 31 | $urlGenerator = \OC::$server->getURLGenerator(); 32 | $token = isset($_GET['t']) ? $_GET['t'] : ''; 33 | 34 | if($token !== '') { 35 | OCP\Response::redirect($urlGenerator->linkToRoute('calendarplus.public.index', array('token' => $token))); 36 | } else { 37 | header('HTTP/1.0 404 Not Found'); 38 | $tmpl = new OCP\Template('', '404', 'guest'); 39 | $tmpl->printPage(); 40 | exit(); 41 | } -------------------------------------------------------------------------------- /templates/calendar.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    > 8 | 9 | 10 |
    11 |
    12 | 17 |
    18 | 19 |
    > 20 | 21 | 22 | 23 | 24 |
    25 | 26 |
    27 |
    28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /templates/part.editcalendar.php: -------------------------------------------------------------------------------- 1 | 4 | * This file is licensed under the Affero General Public License version 3 or 5 | * later. 6 | * See the COPYING-README file. 7 | */ 8 | ?> 9 | t("Edit calendar")); ?>" colspan="7"> 10 |
    11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 |
    t('Displayname')) ?> 15 | 16 |
    Extern Link 22 | 23 |
    30 | > 31 | 34 |
    t('Calendar color')) ?> 40 | 41 | 42 | 43 |
    46 | " value="t("Save") : $l->t("Submit")); ?>"> 47 | " value="t("Cancel")); ?>"> 48 |
    49 | 50 | -------------------------------------------------------------------------------- /templates/part.editevent.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 |
    5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | inc("part.eventform")); ?> 13 |
    14 |
    15 |
    16 | 17 |
    18 | t("Delete"); 20 | if($_['addSingleDeleteButton'] ) { 21 | $DeleteButtonTitle=$l->t("Serie"); 22 | } 23 | ?> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
    35 |
    36 | 37 | 38 |
    39 | 40 |
    41 | 42 |
    43 | -------------------------------------------------------------------------------- /templates/part.import.php: -------------------------------------------------------------------------------- 1 | '404')); 20 | } 21 | if($bFile){ 22 | $import = new OCA\CalendarPlus\Import($file); 23 | $import->setUserID(OCP\User::getUser()); 24 | $newcalendarname = OCP\Util::sanitizeHTML($import->createCalendarName()); 25 | $guessedcalendarname = OCP\Util::sanitizeHTML($import->guessCalendarName()); 26 | $calendarcolor = OCP\Util::sanitizeHTML($import->createCalendarColor()); 27 | } 28 | 29 | //loading calendars for select box 30 | $calendar_options = OCA\CalendarPlus\Calendar::allCalendars(OCP\USER::getUser()); 31 | 32 | 33 | ?> 34 |
    "> 35 |
    36 | 37 | 38 |
    39 |
    40 |
    41 | 42 |
    43 | 44 | 45 | 46 | 47 |
    48 |
    t('Please choose a calendar')); ?>
    49 | 65 |
    66 |
    67 | 68 | 69 |
    70 | 71 | 72 |
    t('A Calendar with this name already exists. If you continue anyhow, these calendars will be merged.')); ?>
    73 |
    74 |
    75 |
    76 | 77 | 78 |
    79 |
    80 | 81 | 82 |
    83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    89 | 90 |
    91 |
    92 | -------------------------------------------------------------------------------- /templates/part.newevent.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | inc("part.eventform")); ?> 7 | 8 |
    9 |
    10 | 11 |
    12 | 13 | 14 |
    15 |
    16 | 17 |
    18 | -------------------------------------------------------------------------------- /templates/part.subscriber.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 |
    Organzier
    Attendees 15 |
      16 | 17 |
    • 18 | 19 |
    20 |
    24 | 25 |
    26 | Teilnehmer E-Mail: 27 |

    28 | -------------------------------------------------------------------------------- /templates/public.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 5 |
    6 | 7 |
    8 | 9 | 10 |
    11 |
    24 | 25 |
    26 |
    27 |
    28 |
    29 | 30 | 31 |
    32 |
    33 |
    34 | 35 |
    36 |
    37 | 38 |
    39 |    40 | 41 | 59 |
    60 |
    61 |
    62 | 63 |
    64 | 65 | 66 |
    67 |
    68 |

    69 | getLongFooter()); ?> 70 |

    71 |
    -------------------------------------------------------------------------------- /templates/publicevent.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 5 | 6 | 7 |
    19 | 20 |
    21 |
    22 |
    23 | 0 && $_['categories']!='' ) { 30 | if(is_array($_['categories'])){ 31 | $output=''; 32 | foreach($_['categories'] as $categorie) { 33 | $output.=''.substr(trim($categorie),0,1).''; 34 | } 35 | print_unescaped($output); 36 | } 37 | } 38 | print_unescaped(' '); 39 | ?> 40 | 41 | 42 |
    43 |

    44 | 45 | 46 | 49 | 50 | 51 | 55 | 56 |
    47 | 48 |
    52 | 53 | 54 |
    57 | 58 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 75 | 76 | 77 | 78 | 83 | 84 | 85 |
    t("All Day Event"));?> 63 | id="allday_checkbox" name="allday" disabled="disabled" class="regular-checkbox"> 64 | 65 |
    t("From"));?> 71 | 72 |    73 | 74 |
    t("To"));?> 79 | 80 |    81 | 82 |
    86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | 101 |
    t("Repeating"));?> 95 | 96 |
     
    97 | 98 | t("End"));?>:
    99 |
    102 | 103 | 104 | 105 | 106 | 113 | 114 |
    t("Exception"));?> 107 |
      108 | $value): ?> 109 |
    • 110 | 111 |
    112 |
    115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 128 | 129 |
    t("Notice"));?> 124 |
    125 | 126 |
    127 |
    130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 144 | 145 | 146 | 147 |
    t("URL"));?> 140 | t("Link"));?> 141 | 142 | 143 |
    148 |
    149 |
    150 | 151 | 152 |
    153 |

    154 | getLongFooter()); ?> 155 |

    156 |
    157 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | . 10 | * 11 | */ 12 | 13 | namespace OCA\CalendarPlus\Db; 14 | 15 | use OC\AppFramework\Db\Db; 16 | 17 | class CalendarDaoTest extends \PHPUnit_Framework_TestCase { 18 | 19 | /** 20 | * @var MailAccountMapper 21 | */ 22 | private $calendarPDO; 23 | 24 | /** 25 | * @var \OCP\IDBConnection; 26 | */ 27 | private $db; 28 | 29 | /** 30 | * @var OCP\User 31 | */ 32 | private $userId; 33 | 34 | /** 35 | * @var integer; 36 | */ 37 | private $calendarId; 38 | 39 | /** 40 | * Initialize calendar pdo 41 | */ 42 | public function setup(){ 43 | $db = \OC::$server->getDatabaseConnection(); 44 | $this->db = new Db($db); 45 | 46 | //$userid = '81stable'; 47 | $this->userId = '81stable'; 48 | $this->calendarPDO = new CalendarDao($this->db, $this->userId); 49 | 50 | } 51 | 52 | public function testFind(){ 53 | /* 54 | $name, 55 | $uri, 56 | $order, 57 | $color, 58 | $timezone, 59 | $components, 60 | $issubscribe, 61 | $externuri, 62 | $lastmodified 63 | 64 | */ 65 | $this->calendarId = $this->calendarPDO->add('Marco','marco',0,'#ff0000',null,'VEVENT,VTODO',0,'',time()); 66 | 67 | $result = $this->calendarPDO->find($this->calendarId); 68 | 69 | $this->assertEquals($this->calendarId, $result['id']); 70 | 71 | 72 | } 73 | 74 | public function testUpdate(){ 75 | /* 76 | $name, $order, $color, $timezone, $components, $transparent, $id*/ 77 | $allCalendars = $this->calendarPDO->all(true,false); 78 | foreach($allCalendars as $calendarInfo){ 79 | if($calendarInfo['uri'] === 'marco'){ 80 | $this->calendarId = $calendarInfo['id']; 81 | break; 82 | } 83 | } 84 | 85 | $bUpdate = $this->calendarPDO->update('Marco Polo',1,'#ff00ff',null,'VEVENT,VTODO',null,$this->calendarId); 86 | $this->assertEquals(true, $bUpdate); 87 | 88 | $result = $this->calendarPDO->find($this->calendarId); 89 | $this->assertEquals('#ff00ff', $result['calendarcolor']); 90 | $this->assertEquals(1, $result['calendarorder']); 91 | $this->assertEquals('Marco Polo', $result['displayname']); 92 | 93 | $bDelete = $this->calendarPDO->delete($this->calendarId); 94 | $this->assertEquals(true, $bDelete); 95 | 96 | } 97 | 98 | public function tearDown(){ 99 | 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | . 10 | 11 | 12 | 13 | 14 | ../calendarplus 15 | 16 | ../calendarplus/l10n 17 | ../calendarplus/tests 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | --------------------------------------------------------------------------------