Implemented HiViz dropslot big "drop here" feature
This commit is contained in:
parent
c88f132201
commit
4fd1e3a547
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
11912
amd/build/vue/vue.min.js
vendored
11912
amd/build/vue/vue.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
|
@ -119,7 +119,7 @@ export default {
|
|||
line_enrollable_1: 'line_enrollable:1',
|
||||
line_enrollable_2: 'line_enrollable:2',
|
||||
line_enrollable_3: 'line_enrollable:3',
|
||||
|
||||
drophere: 'drophere',
|
||||
},
|
||||
studyplan_advanced: {
|
||||
advanced_tools: 'advanced_tools',
|
||||
|
@ -1483,7 +1483,10 @@ export default {
|
|||
computed: {
|
||||
selectedpage() {
|
||||
return this.value.pages[this.selectedpageindex];
|
||||
}
|
||||
},
|
||||
hivizdrop() {
|
||||
return settings("hivizdropslots");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
premiumenabled,
|
||||
|
@ -1744,6 +1747,122 @@ export default {
|
|||
selectedpageChanged(newTabIndex,prevTabIndex) {
|
||||
const page = this.value.pages[newTabIndex];
|
||||
this.$emit('pagechanged',page);
|
||||
},
|
||||
sumLineLayers(idx,page) {
|
||||
if ( idx < 0 || page.studylines.count == 0 ) {
|
||||
return 0;
|
||||
} else {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < idx; i++) {
|
||||
sum += this.countLineLayers(page.studylines[i],page) + 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
},
|
||||
span (line, slot, layer) {
|
||||
let span = 1;
|
||||
for (const course of line.slots[slot].courses) {
|
||||
if (course.slot == slot && course.layer == layer) {
|
||||
span = course.span;
|
||||
}
|
||||
}
|
||||
return span;
|
||||
},
|
||||
onDrop(event, line, slot) {
|
||||
debug.info("dropping", event, line, slot);
|
||||
const self = this;
|
||||
if (event.type.component) { // Double check in case filter fails
|
||||
debug.info("Adding new component");
|
||||
if(event.type.type == "gradable"){
|
||||
// Determine first available layer;
|
||||
const lineslot = line.slots[slot].courses;
|
||||
let nextlayer = 0;
|
||||
for(const itm of lineslot){
|
||||
if (itm.layer >= nextlayer ) {
|
||||
nextlayer = itm.layer + 1;
|
||||
}
|
||||
}
|
||||
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_add_studyitem',
|
||||
args: {
|
||||
"line_id": line.id,
|
||||
"slot" : slot,
|
||||
"layer" : nextlayer,
|
||||
"type": 'course',
|
||||
"details": {
|
||||
"competency_id": null,
|
||||
'conditions':'',
|
||||
'course_id':event.data.id,
|
||||
'badge_id':null,
|
||||
'continuation_id':null,
|
||||
}
|
||||
}
|
||||
}])[0].then((response) => {
|
||||
let item = response;
|
||||
lineslot.push(item);
|
||||
self.$emit("input",self.value);
|
||||
|
||||
// call the validate period function on next tick,
|
||||
// since it paints the item in the slot first
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.timingChecker){
|
||||
this.$refs.timingChecker.validate_course_period();
|
||||
}
|
||||
});
|
||||
ItemEventBus.$emit('coursechange');
|
||||
}).catch(notification.exception);
|
||||
}
|
||||
else if(event.type.type == "filter") {
|
||||
debug.info("Adding new filter compenent");
|
||||
// Determine first available layer;
|
||||
const lineslot = line.slots[slot].filters;
|
||||
let nextlayer = 0;
|
||||
for(const itm of lineslot){
|
||||
if (itm.layer >= nextlayer ) {
|
||||
nextlayer = itm.layer + 1;
|
||||
}
|
||||
}
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_add_studyitem',
|
||||
args: {
|
||||
"line_id": line.id,
|
||||
"slot" : slot,
|
||||
"type": event.data.type,
|
||||
"layer" : nextlayer,
|
||||
"details":{
|
||||
"badge_id": event.data.badge?event.data.badge.id:undefined,
|
||||
}
|
||||
}
|
||||
}])[0].then((response) => {
|
||||
let item = response;
|
||||
lineslot.push(item);
|
||||
self.$emit("input",self.value);
|
||||
}).catch(notification.exception);
|
||||
}
|
||||
}
|
||||
},
|
||||
checkTypeCourse(type) {
|
||||
if(type.type == "gradable"){
|
||||
if ( settings("hivizdropslots") && !type.item) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
checkTypeFilter(type) {
|
||||
if(type.type == "filter"){
|
||||
if ( settings("hivizdropslots") && !type.item) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
|
@ -1880,6 +1999,8 @@ export default {
|
|||
<template v-for="(n,index) in (page.periods+1)">
|
||||
<s-studyline-header-period
|
||||
mode="edit"
|
||||
:x-index="index"
|
||||
:style="'grid-area: 1 / '+ ((2*index)) +';'"
|
||||
:identifier='Number(page.id)'
|
||||
v-if="index > 0"
|
||||
v-model="page.perioddesc[index-1]"
|
||||
|
@ -1892,7 +2013,10 @@ export default {
|
|||
:maxend="(index < page.periods) ? sub_day(page.perioddesc[index].enddate,2) : null"
|
||||
></t-period-edit
|
||||
></s-studyline-header-period>
|
||||
<div class="s-studyline-header-filter"></div>
|
||||
<div class="s-studyline-header-filter"
|
||||
:x-index="index"
|
||||
:style="'grid-area: 1 / '+ ((2*index)+1) +';'"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<!-- Line by line add the items -->
|
||||
|
@ -1902,6 +2026,10 @@ export default {
|
|||
><template v-for="(n,index) in (page.periods+1)"
|
||||
><t-studyline-slot
|
||||
v-if="index > 0 && showslot(page,line, index, layeridx, 'gradable')"
|
||||
:style="'grid-area: '+ (1+sumLineLayers(lineindex,page)+layernr)
|
||||
+ ' / ' + (2 * index)
|
||||
+ ' / ' + (1 + sumLineLayers(lineindex,page)+layernr)
|
||||
+ ' / ' + ( (2 * index) + (2*span(line,index,layeridx) - 1)) + ';'"
|
||||
type='gradable'
|
||||
v-model="line.slots[index].courses"
|
||||
:key="'c-'+lineindex+'-'+index+'-'+layernr"
|
||||
|
@ -1919,6 +2047,7 @@ export default {
|
|||
></t-studyline-slot
|
||||
><t-studyline-slot
|
||||
type='filter'
|
||||
:style="'grid-area: '+ (1+sumLineLayers(lineindex,page)+layernr) + ' / ' + (2*index+1) +';'"
|
||||
v-if="showslot(page,line, index, layeridx, 'filter')"
|
||||
v-model="line.slots[index].filters"
|
||||
:key="'f-'+lineindex+'-'+index+'-'+layernr"
|
||||
|
@ -1937,6 +2066,32 @@ export default {
|
|||
></template
|
||||
></template
|
||||
></template
|
||||
><template v-if="hivizdrop"
|
||||
><template v-for="(line,lineindex) in page.studylines"
|
||||
><template v-for="(n,index) in (page.periods+1)"
|
||||
><drop v-if="index > 0"
|
||||
:style="'grid-area: '+ (2 + sumLineLayers(lineindex,page))
|
||||
+ ' / ' + (2 * index)
|
||||
+ ' / ' + (1 + sumLineLayers(lineindex + 1,page))
|
||||
+ ' / ' + (2 * index) + '; overflow: hidden;'"
|
||||
:class="'t-slot-drop t-slot-linedrop course hiviz'"
|
||||
:accepts-type="checkTypeCourse"
|
||||
@drop="onDrop($event,line,index)"
|
||||
mode="cut"
|
||||
><span>{{text.drophere}}</span></drop
|
||||
><drop
|
||||
:style="'grid-area: '+ (2 + sumLineLayers(lineindex,page))
|
||||
+ ' / ' + ((2 * index) + 1)
|
||||
+ ' / ' + (1 + sumLineLayers(lineindex + 1,page))
|
||||
+ ' / ' + ( (2 * index) + 1 ) + '; overflow: hidden;'"
|
||||
:class="'t-slot-drop t-slot-linedrop filter hiviz'"
|
||||
:accepts-type="checkTypeFilter"
|
||||
@drop="onDrop($event,line,index)"
|
||||
mode="cut"
|
||||
><span>{{text.drophere}}</span></drop
|
||||
></template
|
||||
></template
|
||||
></template
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2284,20 +2439,13 @@ export default {
|
|||
listtype() {
|
||||
return this.type;
|
||||
},
|
||||
dragacceptlist(){
|
||||
if(this.type == "gradable"){
|
||||
return ["course", "gradable-item"];
|
||||
} else {
|
||||
return ["filter", "filter-item"];
|
||||
}
|
||||
},
|
||||
courseHoverDummy(){
|
||||
return {course: this.hover.component};
|
||||
},
|
||||
spanCss(){
|
||||
if(this.item && this.item.span > 1){
|
||||
const span = (2 * this.item.span) - 1;
|
||||
return `width: 100%; grid-column: span ${span};`;
|
||||
return `width: 100%; `;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -2306,6 +2454,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
text: strings.course_timing,
|
||||
plantext: strings.studyplan_text,
|
||||
resizeListener: null,
|
||||
hover: {
|
||||
component:null,
|
||||
|
@ -2448,6 +2597,9 @@ export default {
|
|||
},
|
||||
checkType(type) {
|
||||
if(type.type == this.type){
|
||||
if ( settings("hivizdropslots") && !type.item) {
|
||||
return false;
|
||||
} else {
|
||||
if(type == 'filter'){
|
||||
return true;
|
||||
} else if(type.span <= this.maxSpan()){
|
||||
|
@ -2455,6 +2607,7 @@ export default {
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -2509,7 +2662,7 @@ export default {
|
|||
class="t-slot-item feedback"
|
||||
:key="hover.type">--{{ hover.type }}--</div
|
||||
></template
|
||||
></drop>
|
||||
><span v-else-if="hivizdrop()">{{plantext.drophere}}</span></drop>
|
||||
<t-item-timing-checker hidden
|
||||
v-if="value && value[itemidx] && value[itemidx].course"
|
||||
ref="timingChecker"
|
||||
|
@ -3488,7 +3641,7 @@ export default {
|
|||
<a v-b-modal="'t-item-course-config-'+value.id"
|
||||
:id="'t-item-course-details-'+value.id"
|
||||
:href="wwwroot+'/course/view.php?id='+value.course.id"
|
||||
@click.prevent.stop="">>{{ value.course.displayname }}</a>
|
||||
@click.prevent.stop="">{{ value.course.displayname }}</a>
|
||||
</fittext>
|
||||
</div>
|
||||
<div class="h-100 t-item-course-indicator ">
|
||||
|
@ -4227,7 +4380,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
// TODO: Filtering like this doesn't work, since the courses are lazyloaded on opening
|
||||
hivizdrop() {
|
||||
return settings("hivizdropslots");
|
||||
},
|
||||
filterCategories(catlist) {
|
||||
const self = this;
|
||||
const list = [];
|
||||
|
|
11907
amd/src/vue/vue.js
11907
amd/src/vue/vue.js
File diff suppressed because one or more lines are too long
|
@ -411,7 +411,7 @@ class studyplan {
|
|||
global $CFG, $DB;
|
||||
|
||||
$addable = ['name', 'shortname', 'description', 'descriptionformat', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
|
||||
$info = ['enddate' => null ];
|
||||
$info = ['enddate' => null, "template" => 0, "suspended" => 0];
|
||||
foreach ($addable as $f) {
|
||||
if (array_key_exists($f, $fields)) {
|
||||
$info[$f] = $fields[$f];
|
||||
|
|
|
@ -65,7 +65,7 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
|||
|
||||
print $OUTPUT->header();
|
||||
?>
|
||||
<div id='root'>
|
||||
<div id='root' class="t-studyplan-limit-width">
|
||||
<div class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
--excellent: var(--blue);
|
||||
--pending: var(--gray);
|
||||
--incomplete: var(--gray);
|
||||
--highlight-dropslot: yellow;
|
||||
--highlight-dropslot-hover: var(--success);
|
||||
}
|
||||
|
||||
.path-local-treestudyplan div.tab-pane:target,
|
||||
|
@ -512,15 +514,41 @@ body.path-local-treestudyplan .editmode-switch-form > * {
|
|||
min-height: 32px;
|
||||
height: 100%;
|
||||
min-width: 50px;
|
||||
display: flex;
|
||||
opacity: 0.6;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop > span,
|
||||
.features-treestudyplan .t-slot-drop > span {
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed {
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
background-color: var(--highlight-dropslot);
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed > span,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed > span {
|
||||
display: inline;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in {
|
||||
background-color: var(--highlight-dropslot-hover);
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-linedrop,
|
||||
.features-treestudyplan .t-slot-linedrop {
|
||||
display: none;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed,
|
||||
.features-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed {
|
||||
display: block;
|
||||
z-index: 50;
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.competency,
|
||||
.features-treestudyplan .t-slot-drop.competency {
|
||||
|
@ -541,7 +569,6 @@ body.path-local-treestudyplan .editmode-switch-form > * {
|
|||
.path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz,
|
||||
.features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz {
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
}
|
||||
.path-local-treestudyplan .t-item-deletebox,
|
||||
.features-treestudyplan .t-item-deletebox {
|
||||
|
|
|
@ -633,6 +633,28 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
upgrade_plugin_savepoint(true, 2024041501, 'local', 'treestudyplan');
|
||||
}
|
||||
|
||||
if ($oldversion < 2024052400) {
|
||||
|
||||
// Changing the default of field template on table local_treestudyplan to 0.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('template', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'suspended');
|
||||
|
||||
// Launch change of default for field template.
|
||||
$dbman->change_field_default($table, $field);
|
||||
|
||||
// Changing the default of field suspended on table local_treestudyplan to 0.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('suspended', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'csync_flag');
|
||||
|
||||
// Launch change of default for field suspended.
|
||||
$dbman->change_field_default($table, $field);
|
||||
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2024052400, 'local', 'treestudyplan');
|
||||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ $string["settingdesc_hivizdropslots"] = 'Give the drop fields when editing a stu
|
|||
$string["setting_toolboxleft"] = 'Toolbar left side by default';
|
||||
$string["settingdesc_toolboxleft"] = 'Show the course and component toolbar on the left by default, instead of the right side of the screen';
|
||||
|
||||
$string["drophere"] = "Drop here";
|
||||
$string["infofield_position_above"] = 'Above course results';
|
||||
$string["infofield_position_below"] = 'Below course results';
|
||||
$string["infofield_position_header"] = 'Header ';
|
||||
|
|
|
@ -132,6 +132,7 @@ $string["settingdesc_hivizdropslots"] = 'Maak de velden waar cursussen heen gesl
|
|||
$string["setting_toolboxleft"] = 'Toolbox standaard links';
|
||||
$string["settingdesc_toolboxleft"] = 'Toon de toolbox met cursussen en componenten standaard links in plaats van rechts.';
|
||||
|
||||
$string["drophere"] = "Sleep hierheen";
|
||||
$string["infofield_position_above"] = 'Boven cursusresultaten';
|
||||
$string["infofield_position_below"] = 'Onder cursusresultaten';
|
||||
$string["infofield_position_header"] = 'Header ';
|
||||
|
|
|
@ -12,5 +12,7 @@
|
|||
--excellent: var(--blue);
|
||||
--pending: var(--gray);
|
||||
--incomplete: var(--gray);
|
||||
--highlight-dropslot: yellow;
|
||||
--highlight-dropslot-hover: var(--success);
|
||||
|
||||
}
|
|
@ -396,13 +396,37 @@
|
|||
min-height: 32px;
|
||||
height: 100%;
|
||||
min-width: 50px;
|
||||
display: flex;
|
||||
opacity: 0.6;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
> span {
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
&.hiviz.drop-allowed {
|
||||
> span {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
background-color: var(--highlight-dropslot);
|
||||
&.drop-in {
|
||||
background-color: var(--highlight-dropslot-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.t-slot-linedrop {
|
||||
display: none;
|
||||
&.type-allowed.drop-allowed {
|
||||
display: block;
|
||||
z-index: 50;
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,7 +446,6 @@
|
|||
min-height: 5px;
|
||||
&.hiviz {
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
33
styles.css
33
styles.css
|
@ -12,6 +12,8 @@
|
|||
--excellent: var(--blue);
|
||||
--pending: var(--gray);
|
||||
--incomplete: var(--gray);
|
||||
--highlight-dropslot: yellow;
|
||||
--highlight-dropslot-hover: var(--success);
|
||||
}
|
||||
|
||||
.path-local-treestudyplan div.tab-pane:target,
|
||||
|
@ -512,15 +514,41 @@ body.path-local-treestudyplan .editmode-switch-form > * {
|
|||
min-height: 32px;
|
||||
height: 100%;
|
||||
min-width: 50px;
|
||||
display: flex;
|
||||
opacity: 0.6;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop > span,
|
||||
.features-treestudyplan .t-slot-drop > span {
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed {
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
background-color: var(--highlight-dropslot);
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed > span,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed > span {
|
||||
display: inline;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in,
|
||||
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in {
|
||||
background-color: var(--highlight-dropslot-hover);
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-linedrop,
|
||||
.features-treestudyplan .t-slot-linedrop {
|
||||
display: none;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed,
|
||||
.features-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed {
|
||||
display: block;
|
||||
z-index: 50;
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.path-local-treestudyplan .t-slot-drop.competency,
|
||||
.features-treestudyplan .t-slot-drop.competency {
|
||||
|
@ -541,7 +569,6 @@ body.path-local-treestudyplan .editmode-switch-form > * {
|
|||
.path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz,
|
||||
.features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz {
|
||||
min-height: 44px;
|
||||
background-color: yellow;
|
||||
}
|
||||
.path-local-treestudyplan .t-item-deletebox,
|
||||
.features-treestudyplan .t-item-deletebox {
|
||||
|
|
|
@ -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 = 2024052200; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->version = 2024052400; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||
|
||||
$plugin->release = "1.3.0";
|
||||
|
|
Loading…
Reference in New Issue
Block a user