Added premium key option
This commit is contained in:
parent
dadbd6331f
commit
3cf6459ce8
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n// You can call it anything you like\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\nimport {ProcessStudyplan} from './studyplan-processor';\nimport {studyplanTiming} from './util/date-helper';\n\nimport TSComponents from './treestudyplan-components';\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} period The id of the studyplan we need to view \n */\nexport function init(studyplanid,period) {\n // Make sure the id's are numeric and integer\n if (undefined === studyplanid || !Number.isInteger(Number(studyplanid)) ){ \n studyplanid = 0;\n } else {\n studyplanid = Number(studyplanid);\n } // ensure a numeric value instead of string.\n\n const app = new Vue({\n el: '#root',\n data: {\n\n },\n async mounted() {\n \n },\n computed: {\n \n },\n methods: {\n \n },\n });\n}\n"],"names":["studyplanid","period","undefined","Number","isInteger","Vue","el","data","computed","methods","use","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"onBAyCqBA,YAAYC,QAKzBD,iBAHAE,IAAcF,aAAgBG,OAAOC,UAAUD,OAAOH,cAGxCG,OAAOH,aAFP,EAKN,IAAIK,aAAI,CAChBC,GAAI,QACJC,KAAM,qBAMNC,SAAU,GAGVC,QAAS,qXAxCbC,IAAIC,oCAGJD,IAAIE,iCAEJF,IAAIG,uBAGI,IAAIC,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"}
|
{"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\nimport {ProcessStudyplan} from './studyplan-processor';\nimport {studyplanTiming} from './util/date-helper';\n\nimport TSComponents from './treestudyplan-components';\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} period The id of the studyplan we need to view \n */\nexport function init(studyplanid,period) {\n // Make sure the id's are numeric and integer\n if (undefined === studyplanid || !Number.isInteger(Number(studyplanid)) ){ \n studyplanid = 0;\n } else {\n studyplanid = Number(studyplanid);\n } // ensure a numeric value instead of string.\n\n const app = new Vue({\n el: '#root',\n data: {\n\n },\n async mounted() {\n \n },\n computed: {\n \n },\n methods: {\n \n },\n });\n}\n"],"names":["studyplanid","period","undefined","Number","isInteger","Vue","el","data","computed","methods","use","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"onBAuCqBA,YAAYC,QAKzBD,iBAHAE,IAAcF,aAAgBG,OAAOC,UAAUD,OAAOH,cAGxCG,OAAOH,aAFP,EAKN,IAAIK,aAAI,CAChBC,GAAI,QACJC,KAAM,qBAMNC,SAAU,GAGVC,QAAS,qXAxCbC,IAAIC,oCAGJD,IAAIE,iCAEJF,IAAIG,uBAGI,IAAIC,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"}
|
3
amd/build/util/premium.min.js
vendored
Normal file
3
amd/build/util/premium.min.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
define("local_treestudyplan/util/premium",["exports","core/ajax"],(function(_exports,_ajax){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.enabled=function(){return!!premiumstatus.enabled};let premiumstatus={enabled:!1,website:"",name:"",expires:""}}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=premium.min.js.map
|
1
amd/build/util/premium.min.js.map
Normal file
1
amd/build/util/premium.min.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"premium.min.js","sources":["../../src/util/premium.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\n\n// Prepare default value.\nlet premiumstatus = {\n enabled: false,\n website: \"\",\n name: \"\",\n expires: \"\",\n};\n\n/**\n * Check if premium status is enabled.\n * @returns {Object} The map with strings loaded in\n */\nexport function enabled (){\n return !!premiumstatus.enabled;\n}\n"],"names":["premiumstatus","enabled","website","name","expires"],"mappings":"wLAqBaA,cAAcC,aAZvBD,cAAgB,CAChBC,SAAS,EACTC,QAAS,GACTC,KAAM,GACNC,QAAS"}
|
|
@ -3,8 +3,6 @@
|
||||||
/*eslint linebreak-style: "off" */
|
/*eslint linebreak-style: "off" */
|
||||||
/*eslint no-trailing-spaces: "off" */
|
/*eslint no-trailing-spaces: "off" */
|
||||||
/*eslint-env es6*/
|
/*eslint-env es6*/
|
||||||
// Put this file in path/to/plugin/amd/src
|
|
||||||
// You can call it anything you like
|
|
||||||
|
|
||||||
import {call} from 'core/ajax';
|
import {call} from 'core/ajax';
|
||||||
import notification from 'core/notification';
|
import notification from 'core/notification';
|
||||||
|
|
23
amd/src/util/premium.js
Normal file
23
amd/src/util/premium.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*eslint no-var: "error" */
|
||||||
|
/*eslint no-unused-vars: "off" */
|
||||||
|
/*eslint linebreak-style: "off" */
|
||||||
|
/*eslint no-trailing-spaces: "off" */
|
||||||
|
/*eslint-env es6*/
|
||||||
|
|
||||||
|
import {call} from 'core/ajax';
|
||||||
|
|
||||||
|
// Prepare default value.
|
||||||
|
let premiumstatus = {
|
||||||
|
enabled: false,
|
||||||
|
website: "",
|
||||||
|
name: "",
|
||||||
|
expires: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if premium status is enabled.
|
||||||
|
* @returns {Object} The map with strings loaded in
|
||||||
|
*/
|
||||||
|
export function enabled (){
|
||||||
|
return !!premiumstatus.enabled;
|
||||||
|
}
|
345
classes/premium.php
Normal file
345
classes/premium.php
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* Determine premium status
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace local_treestudyplan;
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
require_once($CFG->libdir.'/externallib.php');
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use moodle_url;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle badge information in the same style as the other classes
|
||||||
|
*/
|
||||||
|
class premium extends \external_api {
|
||||||
|
|
||||||
|
private static $premiumcrt = "-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDSzCCAjMCFFlyhmKf1fN7U5lQL/dtlsyP24AQMA0GCSqGSIb3DQEBCwUAMGEx
|
||||||
|
CzAJBgNVBAYTAk5MMRYwFAYDVQQIDA1Ob29yZC1Ib2xsYW5kMRowGAYDVQQKDBFN
|
||||||
|
aXFyYSBFbmdpbmVlcmluZzEeMBwGA1UEAwwVVHJlZVN0dWR5cGxhbiBQcmVtaXVt
|
||||||
|
MCAXDTI0MDIxMDE2MDQwM1oYDzIxMjQwMTE3MTYwNDAzWjBhMQswCQYDVQQGEwJO
|
||||||
|
TDEWMBQGA1UECAwNTm9vcmQtSG9sbGFuZDEaMBgGA1UECgwRTWlxcmEgRW5naW5l
|
||||||
|
ZXJpbmcxHjAcBgNVBAMMFVRyZWVTdHVkeXBsYW4gUHJlbWl1bTCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAOD7+Nf5UBYGmIadI+kRM7vSPGA12F6cyZuZ
|
||||||
|
O/JsdCWzZx3cCgVYt29DxHRvFVGrhGGLsoaMY9iXc9LdeO02jKqL3RoPo2kc5moT
|
||||||
|
SNarsKZcGZXgqo5NATmdMLqQpKAy41H0ybgXZDLq5XKs9YIRlkwSpzQTNeP49mOl
|
||||||
|
48giVX3icbpMw1TdQotalKXAtcs62o+guQJNANpjBRxPXssrmDoNXrJcAtUjNOjx
|
||||||
|
8M+8tCmwkKwBoK8F3wWxIo04kZ9KILtybMmn4VJJ6SwLEf4StphTIoru8zS7XUt8
|
||||||
|
3HbV3PsiyYErPlwIcobfcjwZJpub23bzetvxRvhpeIpLhrTGrPMCAwEAATANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAQEAQwkbP6m3sdQgXEK3mYYZvvs6R/FI9QPu/9ICA+dgfj4y
|
||||||
|
7wvL0toYYR5oXdhO9At3MYmS+0bFUmqoTS+cxsC4COpEKFbRBWwbJ3NXAw14Hx2U
|
||||||
|
ELLqMZGJNOwNV+3ZdhADrwA++AjUqu144ObrcNUqo4+A4h9R8qj+o0J50Gvwja9R
|
||||||
|
Uh67LsF4Ls8fUtqzpqct94bUl6MPMHlH4qpZlgndmQdgOwLWeQEmM8X3WtSJH90S
|
||||||
|
n8FqBInMBhGu1uz0Qeo09ke0RHRnghP9EXfig/veMegASZeEhFqmS2Bdiy6gqeZ5
|
||||||
|
Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
|
-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
private static $cachedpremiumstatus = null;
|
||||||
|
|
||||||
|
private static function decrypt($encrypted) {
|
||||||
|
// Get the public key.
|
||||||
|
$key = \openssl_get_publickey(self::$premiumcrt);
|
||||||
|
if ($key === false ){
|
||||||
|
throw new \ValueError("Error parsing public key data)");
|
||||||
|
}
|
||||||
|
// Determine the key size.
|
||||||
|
$keysize = \openssl_pkey_get_details($key)["bits"];
|
||||||
|
//
|
||||||
|
$blocksize = ($keysize / 8); // Bits / 8. Whether padded or not.
|
||||||
|
|
||||||
|
// Decode data in
|
||||||
|
$b64 = \base64_decode($encrypted);
|
||||||
|
if ($b64 === false) {
|
||||||
|
throw new \ValueError("Error in base64 decoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = \str_split($b64,$blocksize);
|
||||||
|
$decrypted = "";
|
||||||
|
$i = 0;
|
||||||
|
foreach($data as $chunk) {
|
||||||
|
if (\openssl_public_decrypt($chunk,$dchunk,$key, \OPENSSL_PKCS1_PADDING)) {
|
||||||
|
$decrypted .= $dchunk;
|
||||||
|
} else {
|
||||||
|
throw new \ValueError("Error decrypting chunk $i ({$blocksize} bytes)");
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated in PHP 8.0 and up, but included to be compatible with 7.4.
|
||||||
|
// Wrap in a try/catch in case the function is removed in a later version.
|
||||||
|
try {
|
||||||
|
\openssl_pkey_free($key);
|
||||||
|
} catch (\Exception $x) {}
|
||||||
|
|
||||||
|
return $decrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function trim_headers($data) {
|
||||||
|
// Headers are repeated in this function for easier testing and copy-pasting into other projects.
|
||||||
|
$START_HEADER = "----- BEGIN ACTIVATION KEY -----";
|
||||||
|
$END_HEADER = "----- END ACTIVATION KEY -----";
|
||||||
|
|
||||||
|
$parts = preg_split("/\r?\n/",\trim($data));
|
||||||
|
if (count($parts) > 2) {
|
||||||
|
$start = -1;
|
||||||
|
$end = -1;
|
||||||
|
for($i = 0; $i < count($parts); $i++) {
|
||||||
|
if ( $parts[$i] == $START_HEADER ) {
|
||||||
|
$start = $i+1;
|
||||||
|
}
|
||||||
|
if ($start > 0 && $parts[$i] == $END_HEADER) {
|
||||||
|
$end = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($start < 0 || $end < 0 || $end - $start <= 0) {
|
||||||
|
throw new \ValueError("Invalid activation key wrappers");
|
||||||
|
} else {
|
||||||
|
$keyslice = array_slice($parts, $start, $end - $start);
|
||||||
|
return implode("\n",$keyslice);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \ValueError("Invalid activation key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function enabled() {
|
||||||
|
$status = self::premiumStatus();
|
||||||
|
return $status->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function premiumStatus() {
|
||||||
|
if (!isset(self::$cachedpremiumstatus)) {
|
||||||
|
// Initialize default object.
|
||||||
|
$o = new \stdClass;
|
||||||
|
$o->enabled = false;
|
||||||
|
$o->intent = "";
|
||||||
|
$o->name = "";
|
||||||
|
$o->website = "";
|
||||||
|
$o->expires = "";
|
||||||
|
$o->expired = false;
|
||||||
|
$o->issued = "";
|
||||||
|
$o->message = \get_string("premium:notregistered","local_treestudyplan");
|
||||||
|
|
||||||
|
$activationkey = \get_config("local_treestudyplan","premium_key");
|
||||||
|
if (strlen($activationkey) > 0) {
|
||||||
|
$activationkey;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$keydata = self::trim_headers($activationkey);
|
||||||
|
$json = self::decrypt($keydata);
|
||||||
|
$decoded = \json_decode($json,false);
|
||||||
|
|
||||||
|
if (is_object($decoded)) {
|
||||||
|
|
||||||
|
$keys = ["intent","name","website","expires","issued"];
|
||||||
|
foreach ( $keys as $k) {
|
||||||
|
if (isset($decoded->$k)) {
|
||||||
|
$o->$k = $decoded->$k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert dates to user dates
|
||||||
|
$now = new \DateTime();
|
||||||
|
$issuedate = new \DateTime($o->issued);
|
||||||
|
$expirydate = new \DateTime(); // Default to now
|
||||||
|
if ($o->expires == 'never') {
|
||||||
|
// If expiry date == never
|
||||||
|
$expirydate->add(new \DateInterval("P1Y"));
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$expirydate = new \DateTime($o->expires);
|
||||||
|
} catch (\Exception $x) {}
|
||||||
|
}
|
||||||
|
if ($o->intent == 'treestudyplan'
|
||||||
|
&& !empty($o->issued)
|
||||||
|
&& self::website_match($o->website)
|
||||||
|
) {
|
||||||
|
if ($expirydate > $now ) {
|
||||||
|
$o->enabled = true;
|
||||||
|
$o->expired = false;
|
||||||
|
} else {
|
||||||
|
$o->expired = true;
|
||||||
|
$o->enabled = false;
|
||||||
|
}
|
||||||
|
// Format dates localized.
|
||||||
|
$o->issued = \userdate($issuedate->getTimestamp(),\get_string('strftimedate','langconfig'));
|
||||||
|
if ($o->expires == "never") {
|
||||||
|
$o->expires = \get_string("premium:never",'local_treestudyplan');
|
||||||
|
} else {
|
||||||
|
$o->expires = \userdate($expirydate->getTimestamp(),\get_string('strftimedate','langconfig'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\ValueError $x) {
|
||||||
|
$o->status = \get_string("premium:invalidactivationcontent","local_treestudyplan");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
self::$cachedpremiumstatus = $o;
|
||||||
|
|
||||||
|
}
|
||||||
|
return self::$cachedpremiumstatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function website_match($key) {
|
||||||
|
global $CFG;
|
||||||
|
$site = $CFG->wwwroot;
|
||||||
|
// Add double slashes to key and site if no scheme is set.
|
||||||
|
// Basically: if no double slashes present before any dots,shashes or @s.
|
||||||
|
if(!preg_match_all('#^[^./@]*?//#',$key )) {
|
||||||
|
$key = "//".$key;
|
||||||
|
}
|
||||||
|
if(!preg_match_all('#^[^./@]*?//#',$site)) {
|
||||||
|
$site = "//".$site;
|
||||||
|
}
|
||||||
|
// Use parse_url() to split path and host.
|
||||||
|
$keyurl = (object)\parse_url($key);
|
||||||
|
$siteurl = (object)\parse_url($site);
|
||||||
|
|
||||||
|
// No match if host is empty on key or site
|
||||||
|
if (empty($keyurl->host) || empty($siteurl->host)) {
|
||||||
|
if(empty($keyurl->host)){
|
||||||
|
print "\e[91mError: no host in keyurl '{$key}'\n";
|
||||||
|
print_r($keyurl);
|
||||||
|
print "\e[0m";
|
||||||
|
}
|
||||||
|
if(empty($siteurl->host)){
|
||||||
|
print "\e[91mError: no host in siteurl '{$site}'\n";
|
||||||
|
print_r($siteurl);
|
||||||
|
print "\e[0m";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First match the host part.
|
||||||
|
$keyparts = array_reverse(explode(".",$keyurl->host));
|
||||||
|
$siteparts = array_reverse(explode(".",$siteurl->host));
|
||||||
|
|
||||||
|
// Trim starting www from both parts, since site.domain and www.site.domain should be treated as the same.
|
||||||
|
if (($x = array_pop($keyparts)) != "www") {array_push($keyparts,$x);}
|
||||||
|
if (($x = array_pop($siteparts)) != "www") {array_push($siteparts,$x);}
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($keyparts); $i++) {
|
||||||
|
// No match if the site does not have a part, but the key does. Unless the key part is *
|
||||||
|
if (!isset($siteparts[$i]) ) {
|
||||||
|
if($keyparts[$i] != "*") {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$i++; //increment $i by one before break, to make sure the comparison following this loop holds.
|
||||||
|
break; // Stop comparison. Host part matches.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do a proper case insensitive check for matching.
|
||||||
|
// Uses fnmatch to easily handle shell type wildcards.
|
||||||
|
if ( ! \fnmatch($keyparts[$i],$siteparts[$i],\FNM_CASEFOLD)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fail if the site has a deeper subdomain than the key, unless the deepest key subdomain is *
|
||||||
|
if ($keyparts[$i-1] != '*' && count($siteparts) > ($i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we made it here then the host part matches. Now check the path.
|
||||||
|
// If path is /*, matches all subpaths including /
|
||||||
|
$keypath = empty($keyurl->path)?"/":$keyurl->path;
|
||||||
|
$sitepath = empty($siteurl->path)?"/":$siteurl->path;
|
||||||
|
|
||||||
|
// Trim trailing / from both paths before comparison
|
||||||
|
if (strlen($sitepath) > 1) {
|
||||||
|
$sitepath = \rtrim($sitepath,"/");
|
||||||
|
}
|
||||||
|
if (strlen($keypath) > 1) {
|
||||||
|
$keypath = \rtrim($keypath,"/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a case insensitive fnmatch on the site so wildcards are matched too.
|
||||||
|
return \fnmatch($keypath,$sitepath,\FNM_CASEFOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_premiumstatus_parameters() : \external_function_parameters {
|
||||||
|
return new \external_function_parameters([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value description for webservice function get_premiumstatus
|
||||||
|
*/
|
||||||
|
public static function get_premiumstatus_returns() : \external_description {
|
||||||
|
return new \external_single_structure([
|
||||||
|
"enabled" => new \external_value(PARAM_BOOL, 'premium status enabled'),
|
||||||
|
"website" => new \external_value(PARAM_TEXT, 'premium registration website'),
|
||||||
|
"name" => new \external_value(PARAM_TEXT, 'premium registration name'),
|
||||||
|
"expires" => new \external_value(PARAM_TEXT, 'premium registration expiry date'),
|
||||||
|
"expired" => new \external_value(PARAM_BOOL, 'premium status expired'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get premium status information for webservice
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static function get_premiumstatus() {
|
||||||
|
$status = self::premiumStatus();
|
||||||
|
$keys = [
|
||||||
|
"enabled",
|
||||||
|
"website",
|
||||||
|
"name",
|
||||||
|
"expires",
|
||||||
|
"expired",
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ( $keys as $param) {
|
||||||
|
$result[$param] = $status->$param;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function statusdescription() {
|
||||||
|
$status = self::premiumStatus();
|
||||||
|
|
||||||
|
$msg = new \stdClass;
|
||||||
|
$msg->name = $status->name;
|
||||||
|
$msg->issued = $status->issued;
|
||||||
|
$msg->expires = $status->expires;
|
||||||
|
if ($status->website != "*") {
|
||||||
|
$msg->sitestatus = \get_string("premium:onsite",'local_treestudyplan',$status);
|
||||||
|
} else {
|
||||||
|
$msg->sitestatus = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if($status->enabled) {
|
||||||
|
return \get_string("premium:active",'local_treestudyplan',$msg);
|
||||||
|
} else if ($status->expired) {
|
||||||
|
return \get_string("premium:expired",'local_treestudyplan',$msg);
|
||||||
|
} else {
|
||||||
|
return \get_string("premium:notregistered",'local_treestudyplan',$msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -710,4 +710,16 @@ $functions = [
|
||||||
'capabilities' => 'local/treestudyplan:viewuserreports',
|
'capabilities' => 'local/treestudyplan:viewuserreports',
|
||||||
'loginrequired' => true,
|
'loginrequired' => true,
|
||||||
],
|
],
|
||||||
|
/***************************
|
||||||
|
* Premium status functions
|
||||||
|
***************************/
|
||||||
|
'local_treestudyplan_premiumstatus' => [ // Web service function name.
|
||||||
|
'classname' => '\local_treestudyplan\premium', // Class containing the external function.
|
||||||
|
'methodname' => 'get_premiumstatus', // External function name.
|
||||||
|
'description' => 'Retrieve premium status info',
|
||||||
|
'type' => 'read', // Database rights of the web service function (read, write).
|
||||||
|
'ajax' => true,
|
||||||
|
'capabilities' => '',
|
||||||
|
'loginrequired' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -433,4 +433,25 @@ $string["individuals"] = 'Individuals';
|
||||||
$string["error:cannotviewcategory"] = 'Error: You do not have access to view this category or context: {$a}';
|
$string["error:cannotviewcategory"] = 'Error: You do not have access to view this category or context: {$a}';
|
||||||
$string["error:nostudyplanviewaccess"] = 'Error: You do not have access to view study plans in this category or context: {$a}';
|
$string["error:nostudyplanviewaccess"] = 'Error: You do not have access to view study plans in this category or context: {$a}';
|
||||||
$string["error:nostudyplaneditaccess"] = 'Error: You do not have access to manage study plans in this category or context: {$a}';
|
$string["error:nostudyplaneditaccess"] = 'Error: You do not have access to manage study plans in this category or context: {$a}';
|
||||||
$string["error:nocategoriesvisible"] = 'Error: You have no viewing permissions in any category. Therefore the course list remains empty.';
|
$string["error:nocategoriesvisible"] = 'Error: You have no viewing permissions in any category. Therefore the course list remains empty.';
|
||||||
|
|
||||||
|
$string["premium:never"] = 'never';
|
||||||
|
$string["premium:onsite"] = 'for use on site <b>{$a->website}</b>';
|
||||||
|
$string["premium:active"] = 'Premium access <b class="text-success">enabled</b>.<br> Registered to <b>{$a->name}</b> {$a->sitestatus}<br>Expires <b>{$a->expires}</b>';
|
||||||
|
$string["premium:notregistered"] = 'Premium access <b>disabled</b> ';
|
||||||
|
$string["premium:invalidactivationcontent"] = 'Premium activation key not recognized';
|
||||||
|
$string["premium:expired"] = 'Premium access <b class="text-danger">expired</b> on <b>{$a->expires}</b><br>Was registered to <b>{$a->name}</b> {$a->sitestatus}';
|
||||||
|
$string["settingspage_premium"] = 'Premium registration';
|
||||||
|
$string["setting_premium_heading"] = 'Premium features';
|
||||||
|
$string["settingdesc_premium_heading"] = 'To access premium features, you need a registration key.
|
||||||
|
<br>Premium features include:
|
||||||
|
<ul>
|
||||||
|
<li>Creating more than 5 studyplans per category</li>
|
||||||
|
<li>Overview of grades for all students in a studyplan</li>
|
||||||
|
<li>Rows in a studplan which a student can choose to unlock</li>
|
||||||
|
</ul>';
|
||||||
|
$string["setting_premium_status"] = 'Current premium status';
|
||||||
|
$string["setting_premium_key"] = 'Activation key';
|
||||||
|
$string["settingdesc_premium_key"] = 'Paste the premium key you received in the box above.';
|
||||||
|
|
||||||
|
$string["premiumfeature:morestudyplans"] = 'Creating more than 5 studyplans in a single category is a premium feature.';
|
|
@ -433,4 +433,25 @@ $string["individuals"] = 'Individueel';
|
||||||
$string["error:cannotviewcategory"] = 'Fout: Je hebt geen rechten om deze category of context te bekijken: {$a}';
|
$string["error:cannotviewcategory"] = 'Fout: Je hebt geen rechten om deze category of context te bekijken: {$a}';
|
||||||
$string["error:nostudyplanviewaccess"] = 'Fout: Je hebt geen rechten om studieplannen in deze categorie of context te bekijken: {$a}';
|
$string["error:nostudyplanviewaccess"] = 'Fout: Je hebt geen rechten om studieplannen in deze categorie of context te bekijken: {$a}';
|
||||||
$string["error:nostudyplaneditaccess"] = 'Fout: Je hebt geen rechten om studieplannen in deze categorie of context te beheren: {$a}';
|
$string["error:nostudyplaneditaccess"] = 'Fout: Je hebt geen rechten om studieplannen in deze categorie of context te beheren: {$a}';
|
||||||
$string["error:nocategoriesvisible"] = 'Fout: Je kunt geen cursussen in een categorie bekijken. Daarom blijft de cursuslijst leeg';
|
$string["error:nocategoriesvisible"] = 'Fout: Je kunt geen cursussen in een categorie bekijken. Daarom blijft de cursuslijst leeg';
|
||||||
|
|
||||||
|
$string["premium:never"] = 'nooit';
|
||||||
|
$string["premium:onsite"] = 'voor site {$a->website}';
|
||||||
|
$string["premium:active"] = 'Premium toegang <b class="text-success">ingeschakeld</b>.<br>Uitgegeven aan <b>{$a->name}</b> {$a->sitestatus}<br>Verloopt op<b>{$a->expires}</b>';
|
||||||
|
$string["premium:notregistered"] = 'Premium toegang staat <b>uitgeschakeld</n>';
|
||||||
|
$string["premium:invalidactivationcontent"] = 'Premium activeringssleutel niet herkend';
|
||||||
|
$string["premium:expired"] = 'Premium toegang is <b class="text-danger">verlopen</b> op <b>{$a->expires}</b><br>Was uitgegeven aan <b>{$a->name}</b> {$a->sitestatus}';
|
||||||
|
$string["settingspage_premium"] = 'Premium registration';
|
||||||
|
$string["setting_premium_heading"] = 'Premium features';
|
||||||
|
$string["settingdesc_premium_heading"] = 'Voor premium toegang is een activeringssleutel vereist.
|
||||||
|
<br>Premium toegang bevat onder andere:
|
||||||
|
<ul>
|
||||||
|
<li>Meer dan 5 studieplannen per categorie aan kunnen maken</li>
|
||||||
|
<li>Overzichtsrapport van alle resultaten van studenten in een studieplan</li>
|
||||||
|
<li>Leerlijnen die student kan ontsluiten</li>
|
||||||
|
</ul>';
|
||||||
|
$string["setting_premium_status"] = 'Premium status';
|
||||||
|
$string["setting_premium_key"] = 'Activation key';
|
||||||
|
$string["settingdesc_premium_key"] = 'Premium activation key';
|
||||||
|
|
||||||
|
$string["premiumfeature:morestudyplans"] = 'Meer dan 5 studieplannen in één categorie aanmaken kan alleen met premium toegang.';
|
33
settings.php
33
settings.php
|
@ -25,6 +25,7 @@
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
use local_treestudyplan\aggregator;
|
use local_treestudyplan\aggregator;
|
||||||
|
use local_treestudyplan\premium;
|
||||||
|
|
||||||
if ($hassiteconfig) {
|
if ($hassiteconfig) {
|
||||||
|
|
||||||
|
@ -329,4 +330,36 @@ if ($hassiteconfig) {
|
||||||
get_string('cfg_help', 'local_treestudyplan', null, true),
|
get_string('cfg_help', 'local_treestudyplan', null, true),
|
||||||
$CFG->wwwroot . '/local/treestudyplan/doc.php/index.htm'));
|
$CFG->wwwroot . '/local/treestudyplan/doc.php/index.htm'));
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* Settings page: Cohort sync
|
||||||
|
*
|
||||||
|
*************************************/
|
||||||
|
|
||||||
|
$pagepremium = new admin_settingpage('local_treestudyplan_settings_premium',
|
||||||
|
get_string('settingspage_premium', 'local_treestudyplan', null, true));
|
||||||
|
|
||||||
|
// Description heading.
|
||||||
|
$pagepremium->add(new admin_setting_heading('local_treestudyplan/premium_heading',
|
||||||
|
get_string('setting_premium_heading', 'local_treestudyplan'),
|
||||||
|
get_string('settingdesc_premium_heading', 'local_treestudyplan')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Description heading.
|
||||||
|
$pagepremium->add(new admin_setting_description('local_treestudyplan/premium_status',
|
||||||
|
get_string('setting_premium_status', 'local_treestudyplan'),
|
||||||
|
premium::statusdescription() . "<br> <br>" // Add empty row at end.
|
||||||
|
));
|
||||||
|
|
||||||
|
$pagepremium->add(new admin_setting_configtextarea('local_treestudyplan/premium_key',
|
||||||
|
get_string('setting_premium_key', 'local_treestudyplan'),
|
||||||
|
get_string('settingdesc_premium_key', 'local_treestudyplan'),
|
||||||
|
"",
|
||||||
|
PARAM_RAW
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add settings page2 to the admin settings category.
|
||||||
|
$ADMIN->add('local_treestudyplan', $pagepremium);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
||||||
$plugin->version = 2024020900; // YYYYMMDDHH (year, month, day, iteration).
|
$plugin->version = 2024020901; // YYYYMMDDHH (year, month, day, iteration).
|
||||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||||
|
|
||||||
$plugin->release = "1.1.0";
|
$plugin->release = "1.1.0";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user