From fe98d0e14db6db59455f80caab70008660aa87f5 Mon Sep 17 00:00:00 2001 From: Florian Bogner <f.bogner@pcsg.de> Date: Mon, 31 Oct 2016 17:25:15 +0100 Subject: [PATCH] feat: WIP Cronservice support added. --- ajax/cronservice/getStatus.php | 16 ++ ajax/cronservice/revokeRegistration.php | 15 ++ ajax/cronservice/sendRegistration.php | 16 ++ bin/CronServiceWindow.css | 62 +++++++ bin/CronServiceWindow.html | 42 +++++ bin/CronServiceWindow.js | 215 ++++++++++++++++++++++++ bin/CronServiceWindowRegistration.html | 25 +++ bin/Manager.js | 25 +++ locale.xml | 94 ++++++++++- src/QUI/Cron/CronService.php | 162 ++++++++++++++++++ 10 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 ajax/cronservice/getStatus.php create mode 100644 ajax/cronservice/revokeRegistration.php create mode 100644 ajax/cronservice/sendRegistration.php create mode 100644 bin/CronServiceWindow.css create mode 100644 bin/CronServiceWindow.html create mode 100644 bin/CronServiceWindow.js create mode 100644 bin/CronServiceWindowRegistration.html create mode 100644 src/QUI/Cron/CronService.php diff --git a/ajax/cronservice/getStatus.php b/ajax/cronservice/getStatus.php new file mode 100644 index 0000000..93cc62a --- /dev/null +++ b/ajax/cronservice/getStatus.php @@ -0,0 +1,16 @@ +<?php + +/** + * Gets the current Status for this instance + * + * @return string - Returns the status + */ +QUI::$Ajax->registerFunction( + 'package_quiqqer_cron_ajax_cronservice_getStatus', + function () { + $CronService = new \QUI\Cron\CronService(); + return $CronService->getStatus(); + }, + array(), + '' +); diff --git a/ajax/cronservice/revokeRegistration.php b/ajax/cronservice/revokeRegistration.php new file mode 100644 index 0000000..99f6f62 --- /dev/null +++ b/ajax/cronservice/revokeRegistration.php @@ -0,0 +1,15 @@ +<?php + +/** + * Revokes a registration on the cronservice server. + * + */ +QUI::$Ajax->registerFunction( + 'package_quiqqer_cron_ajax_cronservice_revokeRegistration', + function () { + $CronService = new \QUI\Cron\CronService(); + $CronService->revokeRegistration(); + }, + array(), + '' +); diff --git a/ajax/cronservice/sendRegistration.php b/ajax/cronservice/sendRegistration.php new file mode 100644 index 0000000..91ab2b7 --- /dev/null +++ b/ajax/cronservice/sendRegistration.php @@ -0,0 +1,16 @@ +<?php + +/** + * Sends a registration to the cronservice server. + * + * + */ +QUI::$Ajax->registerFunction( + 'package_quiqqer_cron_ajax_cronservice_sendRegistration', + function ($email) { + $CronService = new \QUI\Cron\CronService(); + $CronService->register($email); + }, + array('email'), + '' +); diff --git a/bin/CronServiceWindow.css b/bin/CronServiceWindow.css new file mode 100644 index 0000000..bc79b1c --- /dev/null +++ b/bin/CronServiceWindow.css @@ -0,0 +1,62 @@ + +.quiqqer-cron-cronservicewindow-btn-openRegistration { + margin : 30px auto 0 auto; + float : right; +} + +.quiqqer-cron-cronserviceWindow h2 { + font-weight: normal; + margin-bottom: 10px; +} + +.quiqqer-cron-cronservicewindow-title { + padding-bottom : 10px; + text-align : center; +} + +.quiqqer-cron-cronservicewindow-title span { + clear : both; + display : block; +} + +.quiqqer-cron-cronservicewindow-title .fa { + font-size : 60px; +} + +.quiqqer-cron-cronservicewindow-section { + margin : 15px auto; +} + +.quiqqer-cron-cronservicewindow-buttons { + text-align: center; + margin-top: 40px; + width: 100%; +} + +/* Registration Sheet */ + +.quiqqer-cron-cronservicewindow-registration { + padding : 20px; +} + +.quiqqer-cron-cronservicewindow-registration-title { + border-bottom : 1px solid #eee; + padding-bottom : 10px; +} + +.quiqqer-cron-cronservicewindow-registration-lbl-email { + font-weight : bold; + padding : 2px; + width : 100%; +} + +.quiqqer-cron-cronservicewindow-registration-txt-email { + width : 100%; + margin-top : 6px; +} + +.quiqqer-cron-cronservicewindow-btn-register { + margin-top : 20px; + float : right; +} + diff --git a/bin/CronServiceWindow.html b/bin/CronServiceWindow.html new file mode 100644 index 0000000..27f3a47 --- /dev/null +++ b/bin/CronServiceWindow.html @@ -0,0 +1,42 @@ +<h1 class="quiqqer-cron-cronservicewindow-title"> + <span class="fa fa-cloud"></span> + <span>{{cron_window_cronservice_content_title}}</span> +</h1> + +<div class="quiqqer-cron-cronservicewindow-section"> + <h2>{{cron_window_cronservice_content_about_title}}</h2> + <p> + {{cron_window_cronservice_content_about_text}} + </p> +</div> + +<div class="quiqqer-cron-cronservicewindow-section"> + <h2>{{cron_window_cronservice_content_status_title}}</h2> + + {{cron_window_cronservice_content_status_text}} + + <div class="quiqqer-cron-cronservicewindow-stat"> + <span class="quiqqer-cron-cronservicewindow-stat-title"> + {{cron_window_cronservice_content_register_lbl_stats_status}} + </span> + {{statusText}} + </div> + + {{#registered}} + <div class="quiqqer-cron-cronservicewindow-stat"> + <span class="quiqqer-cron-cronservicewindow-stat-title"> + {{cron_window_cronservice_content_register_lbl_stats_errors}} + </span> + {{statusErrors}} + </div> + <div class="quiqqer-cron-cronservicewindow-stat"> + <span class="quiqqer-cron-cronservicewindow-stat-title"> + {{cron_window_cronservice_content_register_lbl_stats_lastExecution}} + </span> + {{statusLastExecution}} + </div> + {{/registered}} + +</div> + +<div class="quiqqer-cron-cronservicewindow-buttons"></div> \ No newline at end of file diff --git a/bin/CronServiceWindow.js b/bin/CronServiceWindow.js new file mode 100644 index 0000000..c8dbd8c --- /dev/null +++ b/bin/CronServiceWindow.js @@ -0,0 +1,215 @@ +/** + * + */ +define('package/quiqqer/cron/bin/CronServiceWindow', [ + + 'qui/QUI', + 'qui/controls/windows/Popup', + 'qui/controls/buttons/Button', + 'Mustache', + 'Locale', + 'Ajax', + 'qui/controls/desktop/panels/Sheet', + + 'text!package/quiqqer/cron/bin/CronServiceWindow.html', + 'text!package/quiqqer/cron/bin/CronServiceWindowRegistration.html', + 'css!package/quiqqer/cron/bin/CronServiceWindow.css' + +], function (QUI, QUIPopup, QUIButton, Mustache, QUILocale, QUIAjax, QUISheets, template, registrationTemplate) { + "use strict"; + + var lg = 'quiqqer/cron'; + + return new Class({ + + Extends: QUIPopup, + Type : 'package/quiqqer/cron/bin/CronServiceWindow', + + Binds: [ + '$onSubmit', + '$onOpen', + 'showRegistration' + ], + + options: { + title : QUILocale.get(lg, 'cron.window.cronservice.title'), + icon : 'fa fa-cloud', + maxWidth : 400, + maxHeight: 600, + autoclose: false, + buttons : false + }, + + initialize: function (options) { + this.parent(options); + + this.addEvents({ + onOpen: this.$onOpen + }); + }, + + $onOpen: function () { + var Content = this.getContent(); + + Content.set('html', ''); + Content.addClass('quiqqer-cron-cronserviceWindow'); + + this.refresh(); + }, + + /** + * refresh + */ + refresh: function () { + var self = this, + Content = this.getContent(); + + this.Loader.show(); + + QUIAjax.get('package_quiqqer_cron_ajax_cronservice_getStatus', function (result) { + console.log(result); + var status = result; + + var statusText = QUILocale.get(lg, 'cron.window.cronservice.status.text.unregistered'); + + if (status['status'] == 1) { + statusText = QUILocale.get(lg, 'cron.window.cronservice.status.text.registered'); + } + if (status['status'] == 2) { + statusText = QUILocale.get(lg, 'cron.window.cronservice.status.text.inactive'); + } + + Content.set('html', Mustache.render(template, { + cron_window_cronservice_content_title : QUILocale.get(lg, 'cron.window.cronservice.content.title'), + cron_window_cronservice_content_about_title : QUILocale.get(lg, 'cron.window.cronservice.content.about.title'), + cron_window_cronservice_content_about_text : QUILocale.get(lg, 'cron.window.cronservice.content.about.text'), + cron_window_cronservice_content_status_title : QUILocale.get(lg, 'cron.window.cronservice.content.status.title'), + cron_window_cronservice_content_status_text : QUILocale.get(lg, 'cron.window.cronservice.content.status.text'), + cron_window_cronservice_content_btn_unregister : QUILocale.get(lg, 'cron.window.cronservice.content.register.btn.unregister'), + cron_window_cronservice_content_btn_register : QUILocale.get(lg, 'cron.window.cronservice.content.btn.register'), + cron_window_cronservice_content_register_lbl_stats_status : QUILocale.get(lg, 'cron.window.cronservice.content.register.lbl.stats.status'), + cron_window_cronservice_content_register_lbl_stats_errors : QUILocale.get(lg, 'cron.window.cronservice.content.register.lbl.stats.errors'), + cron_window_cronservice_content_register_lbl_stats_lastExecution: QUILocale.get(lg, 'cron.window.cronservice.content.register.lbl.stats.lastExecution'), + statusText : statusText, + status : status['status'], + statusErrors : status['errors'], + statusLastExecution : status['last_execution'], + registered : (status['status'] != 0) + })); + + var Buttons = Content.getElement('.quiqqer-cron-cronservicewindow-buttons'); + + new QUIButton({ + text : QUILocale.get(lg, 'cron.window.cronservice.content.btn.register'), + textimage: 'fa fa-arrow-right', + events : { + onClick: function(Button) { + if (reg) { + self.showRegistration(); + return; + } + + Button.setAttribute('text', 'Sind Sie sicher?'); + + self.showUnRegistration() + } + }, + styles : { + 'float': 'none', + margin : '0 auto', + width : 200 + } + }).inject(Buttons); + + // + // Content.getElements('.quiqqer-cron-cronservicewindow-btn-openRegistration') + // .addEvent('click', function () { + // self.showRegistration(); + // }); + // + // Content.getElements('.quiqqer-cron-cronservicewindow-btn-unregister') + // .addEvent('click', function () { + // self.unregister(); + // }); + // + + self.Loader.hide(); + }, { + 'package': lg, + 'onError': function () { + self.Loader.hide(); + } + }); + }, + + /** + * Opens the registration sheet + */ + showRegistration: function () { + var self = this; + + new QUISheets({ + header : true, + icon: 'fa fa-cloud', + title: QUILocale.get(lg, 'cron.window.cronservice.title'), + buttons: false, + events : { + onOpen: function (Sheet) { + var Content = Sheet.getContent(); + + Content.set('html', Mustache.render(registrationTemplate, { + cron_window_cronservice_registration_title : QUILocale.get(lg, 'cron.window.cronservice.registration.title'), + cron_window_cronservice_content_register_txt_email_title : QUILocale.get(lg, 'cron.window.cronservice.content.register.txt.email.title'), + cron_window_cronservice_content_register_placeholder_email: QUILocale.get(lg, 'cron.window.cronservice.content.register.placeholder.email'), + cron_window_cronservice_content_btn_register : QUILocale.get(lg, 'cron.window.cronservice.registration.title') + })); + + var Email = Content.getElement('.quiqqer-cron-cronservicewindow-registration-txt-email'); + + Content.getElement('.quiqqer-cron-cronservicewindow-btn-register').addEvent('click', function () { + self.Loader.show(); + self.register(Email.value).then(function () { + self.refresh(); + Sheet.close(); + }); + }); + }, + + onClose: function (Sheet) { + Sheet.destroy(); + } + } + }).inject(this.$Elm).show(); + }, + + /** + * Register a email to the cron service + * + * @param {String} email + * @returns {Promise} + */ + register: function (email) { + return new Promise(function (resolve, reject) { + QUIAjax.post('package_quiqqer_cron_ajax_cronservice_sendRegistration', resolve, { + 'package': lg, + 'email' : email, + onError : reject + }); + }); + }, + + /** + * Unregister a email to the cron service + * + * @returns {Promise} + */ + unregister: function () { + return new Promise(function (resolve, reject) { + QUIAjax.get('package_quiqqer_cron_ajax_cronservice_revokeRegistration', resolve, { + 'package': lg, + onError : reject + }); + }); + } + }); +}); diff --git a/bin/CronServiceWindowRegistration.html b/bin/CronServiceWindowRegistration.html new file mode 100644 index 0000000..1382e6a --- /dev/null +++ b/bin/CronServiceWindowRegistration.html @@ -0,0 +1,25 @@ +<div class="quiqqer-cron-cronservicewindow-registration"> + <h1 class="quiqqer-cron-cronservicewindow-registration-title"> + {{cron_window_cronservice_registration_title}} + </h1> + <form action="" method="POST"> + <label + class="quiqqer-cron-cronservicewindow-registration-lbl-email" + for="quiqqer-cron-cronservicewindow-registration-txt-email" + > + {{cron_window_cronservice_content_register_txt_email_title}} + </label> + <input + id="quiqqer-cron-cronservicewindow-registration-txt-email" + type="text" + name="email" + required="required" + placeholder="{{cron_window_cronservice_content_register_placeholder_email}}" + class="quiqqer-cron-cronservicewindow-registration-txt-email" + /> + <div class="quiqqer-cron-cronservicewindow-btn-register qui-button"> + <span class="fa fa-check"></span> + <span>{{cron_window_cronservice_content_btn_register}}</span> + </div> + </form> +</div> \ No newline at end of file diff --git a/bin/Manager.js b/bin/Manager.js index a6403fb..b7ddb5b 100644 --- a/bin/Manager.js +++ b/bin/Manager.js @@ -176,6 +176,21 @@ define('package/quiqqer/cron/bin/Manager', [ this.getButtons('delete').disable(); + this.addButton(new QUIButtonSeperator()); + this.addButton( + new QUIButton({ + name : 'cronservice', + text : QUILocale.get(lg, 'cron.panel.manager.btn.cronservice.register'), + textimage: 'fa fa-cloud', + events : { + onClick: function () { + self.registerCronservice(); + } + } + }) + ); + + var Content = this.getContent(), Container = new Element('div', { @@ -465,6 +480,16 @@ define('package/quiqqer/cron/bin/Manager', [ require(['package/quiqqer/cron/bin/History'], function (Panel) { new Panel().inject(self.getParent()); }); + }, + + /** + * Opens the Cronservice registration + */ + registerCronservice: function () { + require(['package/quiqqer/cron/bin/CronServiceWindow'], function (CronServiceWindow) { + var csWindow = new CronServiceWindow(); + csWindow.open(); + }); } }); }); \ No newline at end of file diff --git a/locale.xml b/locale.xml index 128bd89..83524cf 100644 --- a/locale.xml +++ b/locale.xml @@ -146,6 +146,99 @@ <de><![CDATA[Aufgabenverlauf]]></de> <en><![CDATA[Cron history]]></en> </locale> + <locale name="cron.panel.manager.btn.cronservice.register"> + <de><![CDATA[Cronservice]]></de> + <en><![CDATA[Cronservice]]></en> + </locale> + + <locale name="cron.window.cronservice.title"> + <de><![CDATA[Cronservice]]></de> + <en><![CDATA[Cronservice]]></en> + </locale> + <locale name="cron.window.cronservice.btn.cancel"> + <de><![CDATA[Abbrechen]]></de> + <en><![CDATA[Cancel]]></en> + </locale> + <locale name="cron.window.cronservice.btn.register"> + <de><![CDATA[Registrieren]]></de> + <en><![CDATA[Register]]></en> + </locale> + <locale name="cron.window.cronservice.content.title"> + <de><![CDATA[Cronservice]]></de> + <en><![CDATA[Cronservice]]></en> + </locale> + <locale name="cron.window.cronservice.content.about.title"> + <de><![CDATA[Was ist das?]]></de> + <en><![CDATA[About]]></en> + </locale> + <locale name="cron.window.cronservice.content.about.text"> + <de><![CDATA[Der QUIQQER Cronservice ermöglicht es Crons auf Systemen auszuführen, die nativ keine Crons ausführen können. +Realisiert wird dies durch einen Server, an dem sich QUIQQER-SYteme anmelden können, welcher wiederrum regelmäßig die Crons der angemeldeten Systeme anstoßt. +Dies kann vorallem auf Shared-Hostings von großem Nutzen sein.]]></de> + <en><![CDATA[The QUIQQER cronservice enables systems withou native cronfunctionality to execute the QUIQQER crons anyway. +This is works with an external server on which QUIQQER systems can register themselves. This server will start the scheduled crons of all registered QUIQQER systems regularely. +]]></en> + </locale> + <locale name="cron.window.cronservice.content.status.title"> + <de><![CDATA[Status]]></de> + <en><![CDATA[State]]></en> + </locale> + <locale name="cron.window.cronservice.content.status.text"> + <de><![CDATA[Hier können Sie den Status Ihrer Registrierung verfolgen :]]></de> + <en><![CDATA[You can check the state of your registration here :]]></en> + </locale> + <locale name="cron.window.cronservice.content.btn.register"> + <de><![CDATA[Registrieren]]></de> + <en><![CDATA[Register]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.title"> + <de><![CDATA[Registrieren]]></de> + <en><![CDATA[Register]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.placeholder.email"> + <de><![CDATA[Email]]></de> + <en><![CDATA[Email]]></en> + </locale> + <locale name="cron.window.cronservice.registration.title"> + <de><![CDATA[Registration]]></de> + <en><![CDATA[Registration]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.txt.email.title"> + <de><![CDATA[Email]]></de> + <en><![CDATA[Email]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.btn.register"> + <de><![CDATA[Registrieren]]></de> + <en><![CDATA[Register]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.btn.unregister"> + <de><![CDATA[Abmelden]]></de> + <en><![CDATA[Unregister]]></en> + </locale> + <locale name="cron.window.cronservice.status.text.inactive"> + <de><![CDATA[Inaktiv]]></de> + <en><![CDATA[Inactive]]></en> + </locale> + <locale name="cron.window.cronservice.status.text.registered"> + <de><![CDATA[Aktiv]]></de> + <en><![CDATA[Active]]></en> + </locale> + <locale name="cron.window.cronservice.status.text.unregistered"> + <de><![CDATA[Nicht registriert]]></de> + <en><![CDATA[Not registered]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.lbl.stats.status"> + <de><![CDATA[Status: ]]></de> + <en><![CDATA[Status: ]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.lbl.stats.errors"> + <de><![CDATA[Fehlerzahl: ]]></de> + <en><![CDATA[Errorcount: ]]></en> + </locale> + <locale name="cron.window.cronservice.content.register.lbl.stats.lastExecution"> + <de><![CDATA[Letzte Ausführung: ]]></de> + <en><![CDATA[Last execution: ]]></en> + </locale> <locale name="permission.quiqqer.cron._header"> <de><![CDATA[Aufgaben / Tasks]]></de> @@ -175,6 +268,5 @@ <de><![CDATA[Darf Aufgaben / Tasks deaktivieren.]]></de> <en><![CDATA[Can deactivate tasks.]]></en> </locale> - </groups> </locales> \ No newline at end of file diff --git a/src/QUI/Cron/CronService.php b/src/QUI/Cron/CronService.php new file mode 100644 index 0000000..c698c73 --- /dev/null +++ b/src/QUI/Cron/CronService.php @@ -0,0 +1,162 @@ +<?php + +namespace QUI\Cron; + +use QUI; +use QUI\System\Log; + +class CronService +{ + + const CRONSERVICE_URL = "http://server.local"; + + private $domain; + private $https; + private $packageDir; + + + public function __construct() + { + $host = QUI::$Conf->get("globals", "host"); + $cms_dir = QUI::$Conf->get("globals", "cms_dir"); + $opt_dir = QUI::$Conf->get("globals", "opt_dir"); + $url_dir = QUI::$Conf->get("globals", "url_dir"); + + // Parse Domain and protocol + if (strpos($host, "https://") !== false) { + $this->https = true; + $this->domain = str_replace("https://", "", $host); + } elseif (strpos($host, "http://") !== false) { + $this->https = false; + $this->domain = str_replace("http://", "", $host); + } else { + $this->https = false; + $this->domain = $host; + } + + // Parse Package dir + $this->packageDir = $url_dir . str_replace($cms_dir, "", $opt_dir); + } + + /** + * Will register this quiqqer instance. + * + * @param $email - Email used for communication. Must be valid. + */ + public function register($email) + { + $this->sendRegistrationRequest($this->domain, $email, $this->packageDir, $this->https); + } + + /** + * Gets the status of the given domain. + * + * Returnformat : + * array( + * 'status' => 0, (0=unregistered; 1=active; 2=inactive) + * 'current_failures' => int, + * 'total_failures' => int, + * 'last_execution' => string (mysql dateformat) + * @return mixed + */ + public function getStatus() + { + $status = $this->makeServerAjaxCall('package_pcsg_cronservice_ajax_getStatus', array( + 'domain' => $this->domain + )); + + return $status; + } + + public function revokeRegistration() + { + $token = "0VamlwcIlNUgE79ocOgpUKTvjhS8I4cr"; + + $this->makeServerAjaxCall('package_pcsg_cronservice_ajax_revokeRegistration', array( + 'domain' => $this->domain, + 'token' => $token + )); + } + + /** + * Sends an ajax request to the cronservice server. + * + * @param $domain - The domain to be registered. Example : example.org + * @param $email - The Email that should be used for communication. + * @param $packageDir - The package url dir + * @param $https - wether or not http secure should be used to call the cron.php + */ + private function sendRegistrationRequest($domain, $email, $packageDir, $https) + { + $url = self::CRONSERVICE_URL . "/admin/ajax.php?" . + "_rf=" . urlencode("[\"package_pcsg_cronservice_ajax_register\"]") . + "&package=" . urlencode("pcsg/cronservice") . + "&lang=de" . // TODO Detect language + "&domain=" . urlencode($domain) . + "&email=" . urlencode($email) . + "&packageDir=" . urlencode($packageDir) . + "&https=" . ($https ? "1" : "0"); + + Log::addDebug($url); + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $url, + CURLOPT_USERAGENT => 'QUIQQER' + )); + + $response = curl_exec($curl); + curl_close($curl); + + Log::addDebug($response); + } + + /** + * Calls the given ajax function on the Cronservice server and returns its output + * @param $function - Ajax function name + * @param $params - Params to pass + * @return mixed + * @throws QUI\Exception + */ + private function makeServerAjaxCall($function, $params) + { + $url = self::CRONSERVICE_URL . "/admin/ajax.php?" . + "_rf=" . urlencode('["' . $function . '"]') . + "&package=" . urlencode("pcsg/cronservice") . + "&lang=de";// TODO Detect language + + foreach ($params as $param => $value) { + $url .= '&' . $param . '=' . urlencode($value); + } + + Log::addDebug("Ajax Request : " . $url); + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $url, + CURLOPT_USERAGENT => 'QUIQQER' + )); + + $response = curl_exec($curl); + + Log::addDebug($response); + + curl_close($curl); + + // Process raw ajax response + $response = substr($response, 9, -10); + $response = json_decode($response, true); + + + if (isset($response[$function]['Exception'])) { + throw new QUI\Exception($response[$function]['Exception']['message']); + } + + Log::writeRecursive($response); + + return $response[$function]['result']; + } + +} \ No newline at end of file -- GitLab