3 Commits

32 changed files with 383 additions and 506 deletions

0
.gitignore vendored Executable file → Normal file
View File

0
DOCUMENTATION.md Executable file → Normal file
View File

0
LICENCE.md Executable file → Normal file
View File

3
Plugin.php Executable file → Normal file
View File

@@ -17,7 +17,8 @@ class Plugin extends PluginBase {
public function registerComponents() {
return [
'NicoSt\GCalendar\Components\Upcoming' => 'upcoming',
'NicoSt\GCalendar\Components\EmbeddedCalendar' => 'embeddedCalendar'
'NicoSt\GCalendar\Components\EmbeddedCalendar' => 'embeddedCalendar',
'NicoSt\GCalendar\Components\Download' => 'download'
];
}

0
README.md Executable file → Normal file
View File

0
classes/EventEntity.php Executable file → Normal file
View File

24
classes/GoogleCalendarClient.php Executable file → Normal file
View File

@@ -1,7 +1,7 @@
<?php namespace NicoSt\GCalendar\Classes;
use Google\Client as Google_Client;
use Google\Service\Calendar as Google_Service_Calendar;
use Google_Client;
use Google_Service_Calendar;
use Log;
use NicoSt\GCalendar\Models\Settings;
@@ -19,25 +19,26 @@ class GoogleCalendarClient {
$this->client->setAccessType('offline');
$this->client->setRedirectUri(url('/gcalendar/oauth2callback'));
$this->client->addScope([Google_Service_Calendar::CALENDAR_READONLY , Google_Service_Calendar::CALENDAR_EVENTS_READONLY]);
$this->client->addScope(Google_Service_Calendar::CALENDAR);
// Set access toke on client if exist.
$accessToken = Settings::get('access_token', []);
$accessToken = Settings::get('access_token');
if (!empty($accessToken)) {
$this->client->setAccessToken($accessToken);
}
// Ensure valid access token.
// If there is no previous token or it's expired.
if ($tryRefreshToken && $this->client->isAccessTokenExpired()) {
if (!empty($this->client->getRefreshToken())) {
// Refresh the token if possible.
if ($this->client->getRefreshToken()) {
$this->client->fetchAccessTokenWithRefreshToken($this->client->getRefreshToken());
// Merge access token
Settings::set('access_token', array_merge($accessToken, $this->client->getAccessToken()));
// Save new access token
Settings::set('access_token', $this->client->getAccessToken());
} else {
Log::warning('G-Calendar: No valid refresh token given.');
Log::warning('G-Calendar: No valid access token given.');
}
}
}
public function getClient() {
@@ -48,4 +49,5 @@ class GoogleCalendarClient {
return new Google_Service_Calendar($this->client);
}
}
}

23
classes/GoogleCalendarService.php Executable file → Normal file
View File

@@ -2,12 +2,11 @@
use Log;
use Cache;
use Carbon\Carbon;
use NicoSt\GCalendar\Models\Settings;
class GoogleCalendarService {
private $cacheTimeMinutesDefault = 15;
private $cacheTimeDefault = 15;
private $cacheKeyCalendarList = 'GCalendar-CalendarList';
private $cacheKeyCalendarGet = 'GCalendar-CalendarGet#';
@@ -21,9 +20,9 @@ class GoogleCalendarService {
public function getCalendar($calendarId, $maxEvents = '10') {
$cachedCalendarGet = Cache::get($this->cacheKeyCalendarGet.$calendarId);
$cacheTimeMinutes = Settings::get('cache_time', $this->cacheTimeMinutesDefault);
$cacheTime = Settings::get('cache_time', $this->cacheTimeDefault);
if(!isset($cachedCalendarGet) || $cacheTimeMinutes == 0) {
if(!isset($cachedCalendarGet) || $cacheTime == 0) {
// "timeMin" -> Set param to only get upcoming events
// "singleEvents" -> Split recurring events into single events
// "orderBy" -> Order events by startTime
@@ -39,8 +38,7 @@ class GoogleCalendarService {
Log::debug('G-Calendar: Call google with #getCalendar for calendar with id: ' . $calendarId);
$response = $this->service->events->listEvents($calendarId, $params);
$expiresAt = Carbon::now()->addMinutes($cacheTimeMinutes);
Cache::put($this->cacheKeyCalendarGet.$calendarId, $response, $expiresAt);
Cache::put($this->cacheKeyCalendarGet.$calendarId, $response, $cacheTime);
return $response;
} catch (\Exception $e) {
$json = json_decode($e->getMessage(), true);
@@ -54,15 +52,14 @@ class GoogleCalendarService {
public function calendarList() {
$cachedCalendarList = Cache::get($this->cacheKeyCalendarList);
$cacheTimeMinutes = Settings::get('cache_time', $this->cacheTimeMinutesDefault);
if(!isset($cachedCalendarList) || $cacheTimeMinutes == 0) {
$cacheTime = Settings::get('cache_time', $this->cacheTimeDefault);
if(!isset($cachedCalendarList) || $cacheTime == 0) {
try {
Log::debug('G-Calendar: Call google with #calendarList.');
$response = $this->service->calendarList->listCalendarList()->getItems();
$expiresAt = Carbon::now()->addMinutes($cacheTimeMinutes);
Cache::put($this->cacheKeyCalendarList, $response, $expiresAt);
Cache::put($this->cacheKeyCalendarList, $response, $cacheTime);
return $response;
} catch (\Exception $e) {
$json = json_decode($e->getMessage(), true);
@@ -103,7 +100,7 @@ class GoogleCalendarService {
}
array_push($events, $calendarEvents);
}
return $events;
}
}
}

95
components/Download.php Normal file
View File

@@ -0,0 +1,95 @@
<?php namespace NicoSt\GCalendar\Components;
use Cms\Classes\ComponentBase;
use NicoSt\GCalendar\Classes\GoogleCalendarService;
class Download extends ComponentBase {
private $calPropSeparator = '§§';
private $googleIcalUrl = 'https://calendar.google.com/calendar/ical/%s/public/basic.ics';
/**
* Returns information about this component, including name and description.
*/
public function componentDetails() {
return [
'name' => 'nicost.gcalendar::lang.component.download.name',
'description' => 'nicost.gcalendar::lang.component.download.description'
];
}
public function defineProperties() {
$calendars = $this->calendarProperties();
$customProperties = [];
$result = array_merge($customProperties, $calendars);
return $result;
}
public function getCalendarDownloads() {
// Calenders from config
$calendarList = $this->getCalendarsFromProperties();
$data = [];
foreach($calendarList as $calendarData) {
$calendar = [];
$calendar['link'] = sprintf($this->googleIcalUrl, $calendarData['id']);
$calendar['title'] = $calendarData['title'];
array_push($data, $calendar);
}
return $data;
}
private function getCalendarsFromProperties() {
$calendars = [];
foreach($this->properties as $key => $value){
$exp_key = explode($this->calPropSeparator, $key);
if($exp_key[0] == 'cal' && $this->property($key) == true){
$calendar = [];
$calendar['id'] = $exp_key[1];
$calendar['title'] = $exp_key[2];
array_push($calendars, $calendar);
}
}
return $calendars;
}
private function calendarProperties() {
$service = new GoogleCalendarService();
$calendars = $service->calendarList();
if (isset($calendars['error'])) {
$calendars = '';
}
$calendarProperties = [];
if (!empty($calendars)) {
foreach ($calendars as &$calendar) {
$calendarData = [];
$calendarData['id'] = $calendar->id;
$calendarData['title'] = $calendar->summary;
$property = $this->createProperty($calendarData);
$calendarProperties = array_merge($calendarProperties, $property);
}
}
return $calendarProperties;
}
private function createProperty(array $calendarData) {
return [
'cal'.$this->calPropSeparator.$calendarData['id'].$this->calPropSeparator.$calendarData['title'] => [
'title' => $calendarData['title'],
'type' => 'checkbox',
'group' => 'nicost.gcalendar::lang.component.download.groups.calendars',
// Default to setting from config
'default' => 0,
]
];
}
}

12
components/EmbeddedCalendar.php Executable file → Normal file
View File

@@ -2,7 +2,9 @@
use Cms\Classes\ComponentBase;
use NicoSt\GCalendar\Classes\GoogleCalendarService;
use Detection\MobileDetect;
use Mobile_Detect;
use Log;
class EmbeddedCalendar extends ComponentBase {
private $calPropSeparator = '§§';
@@ -156,7 +158,7 @@ class EmbeddedCalendar extends ComponentBase {
} else if($viewMode == 'week') {
$data += ['mode' => 'WEEK'];
} else if($viewMode == 'dynamic') {
$detect = new MobileDetect();
$detect = new Mobile_Detect;
if($detect->isMobile()) {
$data += ['mode' => 'AGENDA'];
} else {
@@ -174,6 +176,7 @@ class EmbeddedCalendar extends ComponentBase {
$data += ['wkst' => 2];
}
// Calenders from config
$calendarList = $this->getCalendarsFromProperties();
$dataParams = http_build_query($data);
@@ -218,9 +221,9 @@ class EmbeddedCalendar extends ComponentBase {
$exp_key = explode($this->calPropSeparator, $key);
if($exp_key[0] == 'cal' && $this->property($key) == true){
$calendar = [];
$calendar['src'] = $exp_key[2];
$calendar['color'] = $exp_key[1];
$calendars[] = $calendar;
$calendar['src'] = $exp_key[2];
array_push($calendars, $calendar);
}
}
@@ -228,7 +231,6 @@ class EmbeddedCalendar extends ComponentBase {
}
private function calendarProperties() {
// ToDo: Add cache => https://octobercms.com/docs/services/cache
$service = new GoogleCalendarService();
$calendars = $service->calendarList();

2
components/Upcoming.php Executable file → Normal file
View File

@@ -41,7 +41,7 @@ class Upcoming extends ComponentBase {
];
}
public function events() {
public function getEvents() {
$connector = new GoogleCalendarService();
$maxEvents = $this->property('maxEvents');
$calendarEvents = $connector->allEventList($maxEvents);

View File

@@ -0,0 +1,5 @@
<h3>Click on a calendar to download it and import it to you're device.</h3>
{% set calendars = __SELF__.calendarDownloads %}
{% for calendar in calendars %}
<a href="{{ calendar.link }}" target="_blank">{{ calendar.title }}</a><br />
{% endfor %}

0
components/embeddedcalendar/default.htm Executable file → Normal file
View File

2
components/upcoming/default.htm Executable file → Normal file
View File

@@ -1,4 +1,4 @@
{% set events = upcoming.events %}
{% set events = __SELF__.events %}
{% if not events.error and events is not empty %}
<table>

20
composer.json Executable file → Normal file
View File

@@ -10,23 +10,9 @@
}
],
"require": {
"php": ">=8.1",
"php": ">=7.0",
"composer/installers": "~1.0",
"google/apiclient": "^2.0",
"mobiledetect/mobiledetectlib": "^4.8"
},
"config": {
"allow-plugins": {
"composer/installers": true
},
"cache-read-only": true
},
"scripts": {
"pre-autoload-dump": "Google\\Task\\Composer::cleanup"
},
"extra": {
"google/apiclient-services": [
"Calendar"
]
"mobiledetect/mobiledetectlib": "^2.8"
}
}
}

641
composer.lock generated Executable file → Normal file

File diff suppressed because it is too large Load Diff

2
controllers/GoogleCallback.php Executable file → Normal file
View File

@@ -31,7 +31,7 @@ class GoogleCallback extends Controller {
$accessToken = $client->fetchAccessTokenWithAuthCode($code);
$client->setAccessToken($accessToken);
Log::info('G-Calendar: Set access_token ['. print_r($client->getAccessToken(), true) .'].');
Log::info('G-Calendar: Set access-token ['. print_r($client->getAccessToken(), true) .'].');
Settings::set('access_token', $client->getAccessToken());
return Redirect::to('/backend/system/settings/update/nicost/gcalendar/settings');

0
formwidgets/CalendarSelector.php Executable file → Normal file
View File

0
formwidgets/ClearCacheButton.php Executable file → Normal file
View File

21
formwidgets/OAuth.php Executable file → Normal file
View File

@@ -21,6 +21,7 @@ class OAuth extends FormWidgetBase {
$this->vars['redirectUrl'] = $client->getRedirectUri();
$this->vars['isAccessTokenExpired'] = $client->isAccessTokenExpired() ? '1' : '0';
$this->vars['clientIdExist'] = null != Settings::get('client_id', null) ? '1' : '0';
$this->vars['accessToken'] = print_r(Settings::get('access_token'), true);
return $this->makePartial('oauth');
}
@@ -29,34 +30,28 @@ class OAuth extends FormWidgetBase {
$class = new GoogleCalendarClient(true);
$client = $class->getClient();
$client->setPrompt('consent');
if ($client->isAccessTokenExpired()) {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
//Log::info('G-Calendar: Request authorization with URL ['. $authUrl .'].');
Log::info('G-Calendar: Request authorization with URL ['. $authUrl .'].');
$this->vars['auth_url'] = $authUrl;
return $this->makePartial('gaccess');
}
Flash::info(Lang::get('nicost.gcalendar::lang.message.accessTokenNotExpired'));
Log::info('G-Calendar: Access token not expired.');
Flash::error(Lang::get('nicost.gcalendar::lang.message.accessTokenNotExpired'));
}
public function onClearAccessToken() {
Settings::set('access_token', []);
Settings::set('access_token', '');
Flash::success(Lang::get('nicost.gcalendar::lang.message.accessTokenRemoved'));
return $this->refreshTokenStatus();
}
private function refreshTokenStatus() {
$class = new GoogleCalendarClient(true);
$client = $class->getClient();
$this->vars['isAccessTokenExpired'] = $client->isAccessTokenExpired() ? '1' : '0';
$this->vars['isAccessTokenExpired'] = '1';
return Redirect::refresh();
}
}
}

6
formwidgets/calendarselector/partials/_selector.htm Executable file → Normal file
View File

@@ -3,7 +3,7 @@
<thead>
<tr>
<th class="list-checkbox">
<div class="checkbox nolabel">
<div class="checkbox custom-checkbox nolabel">
<input type="checkbox" id="checkboxAll" />
<label for="checkboxAll"></label>
</div>
@@ -27,7 +27,7 @@
<?php foreach ($calendars as $key=>$calendar): ?>
<tr>
<td class="list-checkbox nolink">
<div class="checkbox nolabel">
<div class="checkbox custom-checkbox nolabel">
<input id="checkbox_<?= $key ?>"
type="checkbox"
name="Settings[calendar_selector][<?= $key ?>][checked]"
@@ -69,4 +69,4 @@
$("#checkboxAll").click(function () {
$('input:checkbox').not(this).prop('checked', this.checked);
});
</script>
</script>

View File

0
formwidgets/oauth/partials/_gaccess.htm Executable file → Normal file
View File

0
formwidgets/oauth/partials/_oauth.htm Executable file → Normal file
View File

10
lang/de/lang.php Executable file → Normal file
View File

@@ -74,8 +74,14 @@
],
'showCalendarList' => [
'title' => 'Kalender Liste Anzeigen'
],
]
]
],
'download' => [
'name' => 'Download Kalender',
'description' => 'Kalender als "ICS-Datei" downloaden.',
'groups' => [
'calendars' => 'Kalender'
]
]
],

10
lang/en/lang.php Executable file → Normal file
View File

@@ -74,8 +74,14 @@
],
'showCalendarList' => [
'title' => 'Show Calendar List'
],
]
]
],
'download' => [
'name' => 'Download Calendar',
'description' => 'Download calendar as "ICS-File".',
'groups' => [
'calendars' => 'Calendars'
]
]
],

0
models/Settings.php Executable file → Normal file
View File

0
models/settings/fields.yaml Executable file → Normal file
View File

0
resources/calendar_language.json Executable file → Normal file
View File

0
resources/calendar_timezone.json Executable file → Normal file
View File

1
routes.php Executable file → Normal file
View File

@@ -6,3 +6,4 @@ Route::group(['prefix' => 'gcalendar'], function () {
'uses' => 'NicoSt\GCalendar\Controllers\GoogleCallback@oauth2callback'
]);
});

12
updates/version.yaml Executable file → Normal file
View File

@@ -1,13 +1,9 @@
1.0.0:
- Initialize plugin.
- Prepare for October marketplace.
1.0.1:
- Compatibility with October CMS 2
1.0.2:
- Fix missing checkboxes at the calendar selector in the backend
1.1.0:
- Update scopes for Calendar API to read only.
- Fix bug about missing refresh token after the update to the latest google client api version
- Remove unnecessary logging
- Add new "Download" component to display a list of calendars to be downloaded as ics-file.
- Update dependencies
- Fix some minor bugs
1.1.1:
- Updated dependencies
- Compatibility with October CMS 2