Fixes
This commit is contained in:
parent
97ce14fe20
commit
c938b994a1
2
amd/build/report-viewer-components.min.js
vendored
2
amd/build/report-viewer-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
amd/build/studyplan-editor-components.min.js
vendored
2
amd/build/studyplan-editor-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
amd/build/treestudyplan-components.min.js
vendored
2
amd/build/treestudyplan-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
amd/build/util/psidebar-vue.min.js
vendored
2
amd/build/util/psidebar-vue.min.js
vendored
|
@ -1,3 +1,3 @@
|
|||
define("local_treestudyplan/util/psidebar-vue",["exports","./css-calc"],(function(_exports,_cssCalc){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={install(Vue){Vue.component("p-sidebar",{props:{value:{type:Boolean,default:!0},right:{type:Boolean,default:!1},shadow:{type:Boolean,default:!1},target:{type:String,default:"body"},offsetRef:{type:String,default:""}},data:()=>({wrapper:null,contentwrapper:null,resizeobserver:null}),computed:{},methods:{initWrappers(target){this.wrapper=document.querySelector("#p-sidebar-wrapper"),this.wrapper||(this.wrapper=document.createElement("div"),this.wrapper.setAttribute("id","p-sidebar-wrapper")),this.contentwrapper=document.querySelector("#p-sidebar-contentwrapper"),this.contentwrapper||(this.contentwrapper=document.createElement("div"),this.contentwrapper.setAttribute("id","p-sidebar-contentwrapper"),this.wrapper.appendChild(this.contentwrapper));let targetEl=document.querySelector(target);for(console.info(`Targeting '${target}' to `,targetEl),targetEl&&"HTML"!=targetEl.nodeType||(targetEl=document.querySelector("body"));targetEl.childNodes.length>0;)this.contentwrapper.appendChild(targetEl.childNodes[0]);targetEl.appendChild(this.wrapper)},rePosition(right){const el=this.$refs.container;right?this.wrapper.insertBefore(el,this.contentwrapper.nextSibling):this.wrapper.insertBefore(el,this.contentwrapper)},setOffset(reference){const ref=reference?document.querySelector(reference):null;console.info(`Setting offset from '${reference}'`,ref);let offsetTop=ref?ref.offsetTop:0;offsetTop+=0!=offsetTop?"px":"";const el=this.$refs.container;el.style.height=`calc( 100vh - ${offsetTop})`,el.style.marginTop=offsetTop}},watch:{right(newVal){this.rePosition(newVal)},offsetRef(reference){this.setOffset(reference)}},mounted(){const el=this.$refs.container;this.initWrappers(this.target),this.setOffset(this.offsetRef),this.rePosition(this.right,this.besides),this.resizeObserver=new ResizeObserver((()=>{let wx=0-el.getBoundingClientRect().width;wx+=0!=wx?"px":"",el.style.setProperty("--p-sidebar-hideoffset",wx)})),this.resizeObserver.observe(el)},unmounted(){this.resizeObserver&&this.resizeObserver.disconnect()},template:"\n <div>\n <div ref='container' \n :class=\"'p-sidebar ' + (right?'p-sidebar-right ':'') + (shadow?'p-sidebar-shadow ':'') + (value?'shown ':'hidden ')\"\n ><slot></slot></div>\n </div>\n "})}};return _exports.default=_default,_exports.default}));
|
||||
define("local_treestudyplan/util/psidebar-vue",["exports","./debugger"],(function(_exports,_debugger){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;let debug=new(_debugger=(obj=_debugger)&&obj.__esModule?obj:{default:obj}).default("p-sidebar");var _default={install(Vue){Vue.component("p-sidebar",{props:{value:{type:Boolean,default:!0},right:{type:Boolean,default:!1},shadow:{type:Boolean,default:!1},target:{type:String,default:"body"},offsetRef:{type:String,default:""}},data:()=>({wrapper:null,contentwrapper:null,resizeobserver:null}),computed:{},methods:{initWrappers(target){let initializeWrapperContent=!1;if(this.wrapper=document.querySelector("#p-sidebar-wrapper"),this.wrapper||(initializeWrapperContent=!0,this.wrapper=document.createElement("div"),this.wrapper.setAttribute("id","p-sidebar-wrapper")),this.contentwrapper=document.querySelector("#p-sidebar-contentwrapper"),this.contentwrapper||(initializeWrapperContent=!0,this.contentwrapper=document.createElement("div"),this.contentwrapper.setAttribute("id","p-sidebar-contentwrapper"),this.wrapper.appendChild(this.contentwrapper)),initializeWrapperContent){let targetEl=document.querySelector(target);for(console.info(`Targeting '${target}' to `,targetEl),targetEl&&"HTML"!=targetEl.nodeType||(targetEl=document.querySelector("body")),debug.warn("Initializing wrappers with content of target ",targetEl);targetEl.childNodes.length>0;)this.contentwrapper.appendChild(targetEl.childNodes[0]);targetEl.appendChild(this.wrapper)}},rePosition(right){const el=this.$refs.container;right?this.wrapper.insertBefore(el,this.contentwrapper.nextSibling):this.wrapper.insertBefore(el,this.contentwrapper)},setOffset(reference){const ref=reference?document.querySelector(reference):null;console.info(`Setting offset from '${reference}'`,ref);let offsetTop=ref?ref.offsetTop:0;offsetTop+=0!=offsetTop?"px":"";const el=this.$refs.container;el.style.height=`calc( 100vh - ${offsetTop})`,el.style.marginTop=offsetTop}},watch:{right(newVal){this.rePosition(newVal)},offsetRef(reference){this.setOffset(reference)}},mounted(){const el=this.$refs.container;this.initWrappers(this.target),this.setOffset(this.offsetRef),this.rePosition(this.right,this.besides),this.resizeObserver=new ResizeObserver((()=>{let wx=0-el.getBoundingClientRect().width;wx+=0!=wx?"px":"",el.style.setProperty("--p-sidebar-hideoffset",wx)})),this.resizeObserver.observe(el)},unmounted(){this.resizeObserver&&this.resizeObserver.disconnect()},template:"\n <div>\n <div ref='container' \n :class=\"'p-sidebar ' + (right?'p-sidebar-right ':'') + (shadow?'p-sidebar-shadow ':'') + (value?'shown ':'hidden ')\"\n ><slot></slot></div>\n </div>\n "})}};return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=psidebar-vue.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -152,6 +152,7 @@ export default {
|
|||
required_goal: "required_goal",
|
||||
student_not_tracked: "student_not_tracked",
|
||||
not_enrolled: "not_enrolled",
|
||||
noenddate: "noenddate",
|
||||
},
|
||||
teachercourse: {
|
||||
select_conditions: "select_conditions",
|
||||
|
@ -164,6 +165,7 @@ export default {
|
|||
required_goal: "required_goal",
|
||||
student_from_plan_enrolled: "student_from_plan_enrolled",
|
||||
students_from_plan_enrolled: "students_from_plan_enrolled",
|
||||
noenddate: "noenddate",
|
||||
},
|
||||
competency: {
|
||||
competency_not_configured: "competency_not_configured",
|
||||
|
@ -1485,7 +1487,7 @@ export default {
|
|||
return format_date(this.value.course.startdate);
|
||||
},
|
||||
enddate(){
|
||||
if(this.value.course.enddate){
|
||||
if(this.value.course.enddate > 0){
|
||||
return format_date(this.value.course.enddate);
|
||||
}
|
||||
else {
|
||||
|
@ -2264,7 +2266,7 @@ export default {
|
|||
return format_date(this.value.course.startdate);
|
||||
},
|
||||
enddate(){
|
||||
if(this.value.course.enddate){
|
||||
if(this.value.course.enddate > 0){
|
||||
return format_date(this.value.course.enddate);
|
||||
}
|
||||
else {
|
||||
|
@ -2449,6 +2451,17 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
startdate(){
|
||||
return format_date(this.value.course.startdate);
|
||||
},
|
||||
enddate(){
|
||||
if(this.value.course.enddate > 0){
|
||||
return format_date(this.value.course.enddate);
|
||||
}
|
||||
else {
|
||||
return this.text.noenddate;
|
||||
}
|
||||
},
|
||||
wwwroot() {
|
||||
return Config.wwwroot;
|
||||
}
|
||||
|
@ -2495,10 +2508,9 @@ export default {
|
|||
<div class="r-course-detail-header-right">
|
||||
<div :class="'r-timing-'+value.course.timing">
|
||||
{{text['coursetiming_'+value.course.timing]}}<br>
|
||||
{{ value.course.startdate }} - {{ value.course.enddate }}
|
||||
{{ startdate }} - {{ enddate }}
|
||||
</div>
|
||||
</div>
|
||||
<s-
|
||||
</template>
|
||||
<b-form-group
|
||||
:label="text.select_grades"
|
||||
|
|
|
@ -223,6 +223,7 @@ export default {
|
|||
selected: 'selected',
|
||||
name: 'name',
|
||||
context: 'context',
|
||||
search: 'search',
|
||||
},
|
||||
item_text: {
|
||||
select_conditions: "select_conditions",
|
||||
|
@ -248,6 +249,7 @@ export default {
|
|||
ok: "ok@core",
|
||||
cancel: "cancel@core",
|
||||
delete: "delete@core",
|
||||
noenddate: "noenddate",
|
||||
},
|
||||
invalid: {
|
||||
error: 'error',
|
||||
|
@ -1111,7 +1113,7 @@ export default {
|
|||
<b-form-input
|
||||
type="text"
|
||||
@input="searchUsers($event)"
|
||||
placeholder="Search users"></b-form-input>
|
||||
:placeholder="text.search + ' ' + text.users"></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
|
@ -1144,7 +1146,7 @@ export default {
|
|||
</b-row>
|
||||
</b-container>
|
||||
</b-tab>
|
||||
<b-tab :title="text.coaches">
|
||||
<b-tab :title="text.coaches" v-if="premiumenabled()">
|
||||
<b-container>
|
||||
<b-row class='mb-2 mt-2'>
|
||||
<b-col>{{text.associated_coaches}}</b-col>
|
||||
|
@ -1157,7 +1159,7 @@ export default {
|
|||
<b-form-input
|
||||
type="text"
|
||||
@input="searchCoaches($event)"
|
||||
placeholder="Search coaches"></b-form-input>
|
||||
:placeholder="text.search + ' ' + text.coaches"></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
|
@ -1455,7 +1457,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
const self=this;
|
||||
if(this.value.pages[0].studylines.length == 0){
|
||||
if(this.value.pages[0].studylines.length == 0 && !this.coaching){
|
||||
// start in editmode if studylines on first page are empty
|
||||
this.edit.studyline.editmode = true;
|
||||
}
|
||||
|
@ -3402,7 +3404,7 @@ export default {
|
|||
return format_date(this.value.course.startdate);
|
||||
},
|
||||
enddate(){
|
||||
if(this.value.course.enddate){
|
||||
if(this.value.course.enddate > 0){
|
||||
return format_date(this.value.course.enddate);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -304,7 +304,8 @@ export default {
|
|||
<slot></slot
|
||||
><p v-if="value.startdate > 0"
|
||||
class="s-studyline-header-period-datespan small">
|
||||
<span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>
|
||||
<span class="date">{{ startdate }}</span>
|
||||
- <span class="date" v-if="this.value.enddate > 0">{{ enddate }}</span><span class="date" v-else>∞</span>
|
||||
</p>
|
||||
<div v-if="current && mode == 'view'" class='s-studyline-period-highlight'></div>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
/*eslint-disable no-console */
|
||||
/*eslint-env es6*/
|
||||
|
||||
import {calc} from "./css-calc";
|
||||
import Debugger from './debugger';
|
||||
let debug = new Debugger("p-sidebar");
|
||||
|
||||
export default {
|
||||
install(Vue/*,options*/){
|
||||
|
@ -42,9 +43,12 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
initWrappers(target) {
|
||||
let initializeWrapperContent = false;
|
||||
// First check if the sidebar wrapper already exists.
|
||||
// Creating the wrappers over and over again is a recipe for disaster.
|
||||
this.wrapper = document.querySelector("#p-sidebar-wrapper");
|
||||
if (!this.wrapper) {
|
||||
initializeWrapperContent = true;
|
||||
// Otherwise, create it.
|
||||
this.wrapper = document.createElement("div");
|
||||
this.wrapper.setAttribute("id","p-sidebar-wrapper");
|
||||
|
@ -52,31 +56,33 @@ export default {
|
|||
// First check if the contentwrapper already exists
|
||||
this.contentwrapper = document.querySelector("#p-sidebar-contentwrapper");
|
||||
if (!this.contentwrapper) {
|
||||
initializeWrapperContent = true;
|
||||
// Otherwise, create it.
|
||||
this.contentwrapper = document.createElement("div");
|
||||
this.contentwrapper.setAttribute("id","p-sidebar-contentwrapper");
|
||||
this.wrapper.appendChild(this.contentwrapper);
|
||||
}
|
||||
|
||||
if (initializeWrapperContent) {
|
||||
// Find containing target (otherwise use body)
|
||||
let targetEl = document.querySelector(target);
|
||||
console.info(`Targeting '${target}' to `,targetEl);
|
||||
if (!targetEl || targetEl.nodeType == "HTML") {
|
||||
targetEl = document.querySelector("body");
|
||||
}
|
||||
debug.warn(`Initializing wrappers with content of target `,targetEl);
|
||||
// Move all target content parts to content wrapper....
|
||||
while (targetEl.childNodes.length >0) {
|
||||
this.contentwrapper.appendChild(targetEl.childNodes[0]);
|
||||
}
|
||||
// Add sidebar wrapper to target Element
|
||||
targetEl.appendChild(this.wrapper);
|
||||
}
|
||||
|
||||
},
|
||||
rePosition(right) {
|
||||
// Place the container elsewhere in the DOM.
|
||||
const el = this.$refs.container;
|
||||
|
||||
|
||||
if(right) {
|
||||
this.wrapper.insertBefore(el,this.contentwrapper.nextSibling);
|
||||
} else {
|
||||
|
|
|
@ -365,7 +365,7 @@ class corecompletioninfo {
|
|||
$details = [
|
||||
"type" => get_string('manualcompletionby', 'completion'),
|
||||
"criteria" => $criteria->get_title(),
|
||||
"requirement" => get_string('markedcompleteby', 'completion', $criteria),
|
||||
"requirement" => get_string('markedcompleteby', 'completion', $criteria->get_title()),
|
||||
"status" => "",
|
||||
];
|
||||
} else if ($type == COMPLETION_CRITERIA_TYPE_COURSE) {
|
||||
|
|
|
@ -7,7 +7,7 @@ require_once($CFG->dirroot.'/repository/lib.php');
|
|||
use local_treestudyplan\aggregator;
|
||||
use local_treestudyplan\studyplan;
|
||||
use local_treestudyplan\premium;
|
||||
use local_treestudyplan\contextinfo;
|
||||
use local_treestudyplan\debug;
|
||||
use local_treestudyplan\studyplanservice;
|
||||
use local_treestudyplan\courseservice;
|
||||
use local_treestudyplan\form\text_integer;
|
||||
|
@ -384,7 +384,9 @@ class studyplan_editform extends formbase {
|
|||
Parse it through the clean_returnvalue function of exernal api (of which studyplanservice is a subclass)
|
||||
so we return it in a consistent way
|
||||
*/
|
||||
return studyplanservice::clean_returnvalue(studyplan::simple_structure(),$plan->simple_model());
|
||||
$response = studyplanservice::clean_returnvalue(studyplan::simple_structure(),$plan->simple_model());
|
||||
debug::dump($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class premium extends \external_api {
|
|||
|
||||
// Toggle the variable below to enable support for premium stuff.
|
||||
// If set to false, all premium features will be enabled and no premium settings panel will be visible.
|
||||
private static $premium_supported = true;
|
||||
private static $premium_supported = false;
|
||||
|
||||
private static $premiumcrt = "-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCFFlyhmKf1fN7U5lQL/dtlsyP24AQMA0GCSqGSIb3DQEBCwUAMGEx
|
||||
|
@ -67,7 +67,6 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
return self::$premium_supported;
|
||||
}
|
||||
|
||||
|
||||
private static function decrypt($encrypted) {
|
||||
// Get the public key.
|
||||
$key = \openssl_get_publickey(self::$premiumcrt);
|
||||
|
@ -161,6 +160,36 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
}
|
||||
}
|
||||
|
||||
public static function debuginfo() {
|
||||
$s = "<pre>";
|
||||
try {
|
||||
$activationkey = \get_config("local_treestudyplan","premium_key");
|
||||
if (strlen($activationkey) > 0) {
|
||||
$keydata = self::trim_headers($activationkey);
|
||||
$json = self::decrypt($keydata);
|
||||
$s .= "Decoded key data:\n----\n";
|
||||
$s .= $json . "\n----\n";
|
||||
$decoded = \json_decode($json,false);
|
||||
$s .= "Read as object:\n----\n";
|
||||
$s .= print_r($decoded,true) ."\n----\n";
|
||||
$status = self::premiumStatus();
|
||||
$s .= "Parsed premium status block:\n----\n";
|
||||
$s .= print_r($status,true) ."\n----\n";
|
||||
$s .= "Message: " . self::statusdescription() . "\n";
|
||||
} else {
|
||||
$s .= "Premium key empty";
|
||||
}
|
||||
} catch (\Throwable $x) {
|
||||
$s .= "!!! " . get_class($x) . ": " . $x->getCode() . " | " . $x->getMessage(). "\n";
|
||||
$stack = explode("\n",$x->getTraceAsString());
|
||||
foreach ($stack as $l) {
|
||||
$s .= " " . trim($l) ."\n";
|
||||
}
|
||||
}
|
||||
|
||||
$s.="</pre>";
|
||||
return $s;
|
||||
}
|
||||
/**
|
||||
* Determine, cache and retrieve premium status
|
||||
* @return object
|
||||
|
@ -177,13 +206,10 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
$o->expires = "";
|
||||
$o->expired = false;
|
||||
$o->issued = "";
|
||||
$o->message = \get_string("premium:notregistered","local_treestudyplan");
|
||||
|
||||
try {
|
||||
$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);
|
||||
|
@ -215,8 +241,6 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
} catch (\Exception $x) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( \in_array('treestudyplan',$o->intents)
|
||||
&& !empty($o->issued)
|
||||
&& self::website_match($o->website)
|
||||
|
@ -237,11 +261,10 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\ValueError $x) {
|
||||
$o->status = \get_string("premium:invalidactivationcontent","local_treestudyplan");
|
||||
}
|
||||
|
||||
}
|
||||
self::$cachedpremiumstatus = $o;
|
||||
|
||||
}
|
||||
|
@ -271,6 +294,11 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($keyurl->host == "*") {
|
||||
// * matches all
|
||||
return true;
|
||||
}
|
||||
|
||||
// First match the host part.
|
||||
$keyparts = \array_reverse(\explode(".",$keyurl->host));
|
||||
$siteparts = \array_reverse(\explode(".",$siteurl->host));
|
||||
|
@ -386,7 +414,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
if ($status->website != "*") {
|
||||
$msg->sitestatus = \get_string("premium:onsite",'local_treestudyplan',$status);
|
||||
} else {
|
||||
$msg->sitestatus = \get_string("premium:anywhere",'local_treestudyplan');;
|
||||
$msg->sitestatus = \get_string("premium:anywhere",'local_treestudyplan');
|
||||
}
|
||||
|
||||
if($status->enabled) {
|
||||
|
|
|
@ -412,10 +412,9 @@ class studyplan {
|
|||
}
|
||||
}
|
||||
|
||||
$page = studyplanpage::add($pageinfo);
|
||||
$page = studyplanpage::add($pageinfo,$bare);
|
||||
$plan->page_cache = [$page];
|
||||
}
|
||||
// End temporary skräpp code.
|
||||
|
||||
return $plan;
|
||||
}
|
||||
|
|
|
@ -266,8 +266,9 @@ class studyplanpage {
|
|||
/**
|
||||
* Add new study plan page
|
||||
* @param mixed $fields Parameter for new study plan page
|
||||
* @param bool $bare If true, do not create a first page with copy of studyplan names
|
||||
*/
|
||||
public static function add($fields) : self {
|
||||
public static function add($fields, $bare = false) : self {
|
||||
global $DB;
|
||||
|
||||
if (!isset($fields['studyplan_id'])) {
|
||||
|
@ -293,7 +294,19 @@ class studyplanpage {
|
|||
$id = $DB->insert_record(self::TABLE, $info);
|
||||
// Refresh the page cache for the studyplan
|
||||
$plan->pages(true);
|
||||
return self::find_by_id($id); // Make sure the new page is immediately cached.
|
||||
$page = self::find_by_id($id); // Make sure the new page is immediately cached.
|
||||
|
||||
if (!$bare) {
|
||||
// Add an empty study line
|
||||
$lineinfo = ['page_id' => $id,
|
||||
'name' => get_string("default_line_name","local_treestudyplan"),
|
||||
'shortname' => get_string("default_line_shortname","local_treestudyplan"),
|
||||
"color" => "#DDDDDD",
|
||||
];
|
||||
studyline::add($lineinfo);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -142,9 +142,7 @@ class utilityservice extends \external_api {
|
|||
* Return value description for webservice function submit_cm_editform
|
||||
*/
|
||||
public static function submit_mform_returns() : \external_description {
|
||||
return new \external_multiple_structure(new \external_single_structure([
|
||||
"id" => new \external_value(PARAM_INT, 'id of studyline'),
|
||||
]));
|
||||
return success::structure(); // Success structure includes data component which encodes date in json format.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,6 +163,7 @@ class utilityservice extends \external_api {
|
|||
$mform = self::load_mform($formname, $params, $ajaxformdata);
|
||||
|
||||
$return = $mform->process_submission();
|
||||
// Pass form processing result as success data component.
|
||||
return success::success($return)->model();
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ print $OUTPUT->header();
|
|||
v-if="displayedstudyplan.description"
|
||||
></s-studyplan-details>
|
||||
<div class="flex-grow-1"><!-- Spacer to align student selector right --></div>
|
||||
<div v-if="displayedstudyplan.description">
|
||||
<span><?php t('selectstudent_btn') ?></span>
|
||||
<s-prevnext-selector
|
||||
:options="associatedstudents"
|
||||
|
@ -99,7 +98,6 @@ print $OUTPUT->header();
|
|||
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
||||
<template #defaultlabel><span class='text-primary'><?php t("coacheditmode"); ?></span></template>
|
||||
</s-prevnext-selector>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='t-studyplan-container'>
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>local/treestudyplan:forcescales</td>
|
||||
<td>Allow studyplan managers with this right to push scales for grading to all selected grable activities (see above)<br>
|
||||
<td><b>(DEPRECATED)</b>Allow studyplan managers with this right to push scales for grading to all selected grable activities (see above)<br>
|
||||
(Effective only when Manual aggregation method is selected in the study plan)</td>
|
||||
<td>manager</td>
|
||||
<td>System and/or category</td>
|
||||
|
@ -125,13 +125,27 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>local/treestudyplan:configure</td>
|
||||
<td>Users with this right can configure global interpretation of scales and point grades </td>
|
||||
<td>Users with this right can configure global interpretation of scales and point grades <br>
|
||||
This is used when a per-activity passed grade is not set.
|
||||
(Effective only when Manual aggregation method is selected in the study plan)</td>
|
||||
<td>manager</td>
|
||||
<td>System</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>local/treestudyplan:viewuserreports</td>
|
||||
<td></td>
|
||||
<td>Users with this right can view the student's studyplans in all contexts in which this permission is granted</td>
|
||||
<td>manager</td>
|
||||
<td>System and/or category</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>local/treestudyplan:lineunenrol</td>
|
||||
<td>(Premium feature related) Users with this right in addition to <i>viewuserreports</i> can unregister students from an registerable line</td>
|
||||
<td>manager</td>
|
||||
<td>System and/or category</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>local/treestudyplan:coach</td>
|
||||
<td>(Premium feature related) Users with this right can be selected as coaches in the given context</td>
|
||||
<td>manager</td>
|
||||
<td>System and/or category</td>
|
||||
</tr>
|
||||
|
|
|
@ -61,6 +61,17 @@ Manage Study plans|/local/treestudyplan/edit-plan.php||en
|
|||
If you already have a role that you hand out to all teachers/faculty in specific categories, you may want to integrate this role with that one
|
||||
</div>
|
||||
</li>
|
||||
<li><b>Full Name:</b> <i>Studyplan Coach</i><br>
|
||||
<b>Short Name:</b> <i>studyplancoach</i><br>
|
||||
<b>Context types:</b> <i>System</i>, <i>Category</i><br>
|
||||
<b>Capabilities</b>
|
||||
<ul>
|
||||
<li><b>local/treestudyplan:coach</b> <i>Available as coach</i></li>
|
||||
</ul>
|
||||
<div class="generalbox alert alert-success"><b>TIP:</b><br>
|
||||
If you already have a role that you hand out to coaches of a limited group of students, you may want to integrate this role with that one
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p> Then assign the role <b>studyplanmanager</b> in a specific category context, or the system context to all users who should be able to create and edit studyplans
|
||||
in that specific context</p>
|
||||
|
|
|
@ -200,6 +200,8 @@ $string["studyline_shortname_ph"] = '';
|
|||
$string["studyline_color"] = 'Background color';
|
||||
$string["studyline_enrollable"] = 'Registration';
|
||||
$string["studyline_enrolroles"] = 'Allowed roles';
|
||||
$string["default_line_name"] = 'Courses';
|
||||
$string["default_line_shortname"] = 'Courses';
|
||||
|
||||
$string["studyitem_confirm_remove"] = 'Are you sure you want to remove module {$a}?';
|
||||
$string["editmode_modules_hidden"] = 'Modules hidden in edit mode';
|
||||
|
@ -291,6 +293,7 @@ $string["coaches"] = 'Coaches';
|
|||
$string["selected"] = 'Select';
|
||||
$string["name"] = 'Name';
|
||||
$string["context"] = 'Category';
|
||||
$string["search"] = "Search";
|
||||
|
||||
$string["error"] = "Error";
|
||||
$string["ungraded"] = 'Needs grading';
|
||||
|
@ -487,7 +490,9 @@ $string["settingdesc_premium_heading"] = 'To access premium features, you need a
|
|||
</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["settingdesc_premium_key"] = 'Paste the premium key you received in the box above.<br> <b>Attention!</b> The lines <i>----- BEGIN ACTIVATION KEY -----</i> and <i>----- END ACTIVATION KEY -----</i> are part of the key.';
|
||||
$string["setting_premium_debug"] = 'Debug info for premium key';
|
||||
|
||||
|
||||
$string["premiumfeature:morestudyplans"] = 'Creating more than 5 studyplans in a single category is a premium feature.';
|
||||
$string["premiumfeature:morecategories"] = 'Creating studyplans in more than 20 categories is a premium feature.';
|
||||
|
|
|
@ -129,7 +129,7 @@ $string["settingdesc_infofields_heading"] = 'Kies tot 5 extra velden om in het c
|
|||
$string["setting_hivizdropslots"] = 'Extra zichtbare sleepvelden';
|
||||
$string["settingdesc_hivizdropslots"] = 'Maak de velden waar cursussen heen gesleept kunnen worden extra goed zichtbaar.';
|
||||
|
||||
$string["setting_toolboxleft"] = 'Toolcox standaard links';
|
||||
$string["setting_toolboxleft"] = 'Toolbox standaard links';
|
||||
$string["settingdesc_toolboxleft"] = 'Toon de toolbox met cursussen en componenten standaard links in plaats van rechts.';
|
||||
|
||||
$string["infofield_position_above"] = 'Boven cursusresultaten';
|
||||
|
@ -201,6 +201,8 @@ $string["studyline_shortname_ph"] = '';
|
|||
$string["studyline_color"] = 'Achtergrondkleur';
|
||||
$string["studyline_enrollable"] = 'Inschrijven';
|
||||
$string["studyline_enrolroles"] = 'Rollen mogen inschrijven';
|
||||
$string["default_line_name"] = 'Cursussen';
|
||||
$string["default_line_shortname"] = 'Cursussen';
|
||||
|
||||
$string["studyitem_confirm_remove"] = 'Weet je zeker dat je module {$a} wilt verwijderen?';
|
||||
$string["editmode_modules_hidden"] = 'Modules verborgen tijdens bewerken';
|
||||
|
@ -292,6 +294,7 @@ $string["coaches"] = 'Coaches';
|
|||
$string["selected"] = 'Kies';
|
||||
$string["name"] = 'Naam';
|
||||
$string["context"] = 'Categorie';
|
||||
$string["search"] = "Zoek";
|
||||
|
||||
$string["error"] = "Fout";
|
||||
$string["ungraded"] = 'Nog beoordelen';
|
||||
|
@ -476,7 +479,7 @@ $string["premium:notregistered"] = 'Premium toegang staat <b>uitgeschakeld</n>';
|
|||
$string["premium:invalidactivationcontent"] = 'Premium activeringssleutel niet herkend';
|
||||
$string["premium:siteinvalid"] = 'Premium toegang staat <b>uitgeschakeld</n>. Activeringssleutel is alleen voor gebruik op <b>{$a}</b>';
|
||||
$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["settingspage_premium"] = 'Premium registratie';
|
||||
$string["setting_premium_heading"] = 'Premium features';
|
||||
$string["settingdesc_premium_heading"] = 'Voor premium toegang is een activeringssleutel vereist.
|
||||
<br>Premium toegang bevat onder andere:
|
||||
|
@ -487,8 +490,9 @@ $string["settingdesc_premium_heading"] = 'Voor premium toegang is een activering
|
|||
<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["setting_premium_key"] = 'Activatiesleutel';
|
||||
$string["settingdesc_premium_key"] = 'Plak de activatiesleutel in het tekstvak hierboven.<br> <b>Let op!</b> De regels <i>----- BEGIN ACTIVATION KEY -----</i> en <i>----- END ACTIVATION KEY -----</i> zijn onderdeel van de sleutel.';
|
||||
$string["setting_premium_debug"] = 'Debug info voor premium key';
|
||||
|
||||
$string["premiumfeature:morestudyplans"] = 'Meer dan 5 studieplannen in één categorie aanmaken kan alleen met premium toegang.';
|
||||
$string["premiumfeature:morecategories"] = 'In meer dan 20 categoriën een studieplan aanmaken kan alleen met premium toegang.';
|
||||
|
|
11
settings.php
11
settings.php
|
@ -373,7 +373,7 @@ if ($hassiteconfig) {
|
|||
* Settings page: Premium registration
|
||||
*
|
||||
*************************************/
|
||||
if (\local_treestudyplan\premium::supported()) {
|
||||
if (\local_treestudyplan\premium::supported() || $CFG->debugdeveloper) {
|
||||
$pagepremium = new admin_settingpage('local_treestudyplan_settings_premium',
|
||||
get_string('settingspage_premium', 'local_treestudyplan', null, true));
|
||||
|
||||
|
@ -396,6 +396,15 @@ if ($hassiteconfig) {
|
|||
PARAM_RAW
|
||||
));
|
||||
|
||||
if ($CFG->debugdeveloper) {
|
||||
// show decrypted key data
|
||||
$pagepremium->add(new admin_setting_description('local_treestudyplan/premium_debug',
|
||||
get_string('setting_premium_debug', 'local_treestudyplan'),
|
||||
premium::debuginfo() . "<br> <br>" // Add empty row at end.
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Add settings page2 to the admin settings category.
|
||||
$ADMIN->add('local_treestudyplan', $pagepremium);
|
||||
}
|
||||
|
|
83
test.php
Normal file
83
test.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
function sitematch($key,$site){
|
||||
// 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)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($keyurl->host == "*") {
|
||||
// * matches all
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
$tests = [
|
||||
["*", "https://www.miqra.nl"],
|
||||
["*/*", "https://www.miqra.nl"],
|
||||
["*", "https://clients.openedu.nl/fith"],
|
||||
["clients.openedu.nl/fith", "https://clients.openedu.nl/fith/"],
|
||||
["clients.openedu.nl/fith/", "https://clients.openedu.nl/fith"]
|
||||
];
|
||||
|
||||
foreach($tests as $test) {
|
||||
[$key, $site] = $test;
|
||||
print("Checking key '{$key}' on site '{$site}': " . (sitematch($key,$site)?"MATCH":"FAIL") . "\n");
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
||||
$plugin->version = 2024042100; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->version = 2024051000; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||
|
||||
$plugin->release = "1.2.0";
|
||||
|
|
Loading…
Reference in New Issue
Block a user