diff --git a/config.xml b/config.xml
index 211280ed8..744ea653e 100644
--- a/config.xml
+++ b/config.xml
@@ -147,6 +147,9 @@
+
+
+
diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts
index 37a806f43..69d6fdcbf 100644
--- a/src/addon/mod/data/components/index/index.ts
+++ b/src/addon/mod/data/components/index/index.ts
@@ -299,14 +299,14 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
if (!this.isEmpty) {
this.entries = entries.offlineEntries.concat(entries.entries);
- let entriesHTML = this.data.listtemplateheader || '';
+ let entriesHTML = this.dataHelper.getTemplate(this.data, 'listtemplateheader', this.fieldsArray);
// Get first entry from the whole list.
if (!this.search.searching || !this.firstEntry) {
this.firstEntry = this.entries[0].id;
}
- const template = this.data.listtemplate || this.dataHelper.getDefaultTemplate('list', this.fieldsArray);
+ const template = this.dataHelper.getTemplate(this.data, 'listtemplate', this.fieldsArray);
const entriesById = {};
this.entries.forEach((entry, index) => {
@@ -318,7 +318,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
entriesHTML += this.dataHelper.displayShowFields(template, this.fieldsArray, entry, offset, 'list', actions);
});
- entriesHTML += this.data.listtemplatefooter || '';
+ entriesHTML += this.dataHelper.getTemplate(this.data, 'listtemplatefooter', this.fieldsArray);
this.entriesRendered = entriesHTML;
diff --git a/src/addon/mod/data/pages/edit/edit.ts b/src/addon/mod/data/pages/edit/edit.ts
index 35e74cd06..52025cfa3 100644
--- a/src/addon/mod/data/pages/edit/edit.ts
+++ b/src/addon/mod/data/pages/edit/edit.ts
@@ -287,7 +287,7 @@ export class AddonModDataEditPage {
let replace,
render,
- template = this.data.addtemplate || this.dataHelper.getDefaultTemplate('add', this.fieldsArray);
+ template = this.dataHelper.getTemplate(this.data, 'addtemplate', this.fieldsArray);
// Replace the fields found on template.
this.fieldsArray.forEach((field) => {
diff --git a/src/addon/mod/data/pages/entry/entry.ts b/src/addon/mod/data/pages/entry/entry.ts
index 51e95ce9c..b8c7da5dd 100644
--- a/src/addon/mod/data/pages/entry/entry.ts
+++ b/src/addon/mod/data/pages/entry/entry.ts
@@ -163,7 +163,7 @@ export class AddonModDataEntryPage implements OnDestroy {
}).then(() => {
const actions = this.dataHelper.getActions(this.data, this.access, this.entry);
- const template = this.data.singletemplate || this.dataHelper.getDefaultTemplate('single', this.fieldsArray);
+ const template = this.dataHelper.getTemplate(this.data, 'singletemplate', this.fieldsArray);
this.entryHtml = this.dataHelper.displayShowFields(template, this.fieldsArray, this.entry, this.offset, 'show',
actions);
this.showComments = actions.comments;
diff --git a/src/addon/mod/data/pages/search/search.ts b/src/addon/mod/data/pages/search/search.ts
index 4ca20b5d0..036f517b7 100644
--- a/src/addon/mod/data/pages/search/search.ts
+++ b/src/addon/mod/data/pages/search/search.ts
@@ -89,7 +89,7 @@ export class AddonModDataSearchPage {
search: this.search.advanced
};
- let template = this.data.asearchtemplate || this.dataHelper.getDefaultTemplate('asearch', this.fieldsArray),
+ let template = this.dataHelper.getTemplate(this.data, 'asearchtemplate', this.fieldsArray),
replace, render;
// Replace the fields found on template.
diff --git a/src/addon/mod/data/providers/helper.ts b/src/addon/mod/data/providers/helper.ts
index b5aaadcec..a425a5cc6 100644
--- a/src/addon/mod/data/providers/helper.ts
+++ b/src/addon/mod/data/providers/helper.ts
@@ -410,10 +410,14 @@ export class AddonModDataHelperProvider {
* @param {any[]} fields List of database fields.
* @return {string} Template HTML.
*/
- getDefaultTemplate( type: 'add' | 'list' | 'single' | 'asearch', fields: any[]): string {
+ getDefaultTemplate(type: string, fields: any[]): string {
+ if (type == 'listtemplateheader' || type == 'listtemplatefooter') {
+ return '';
+ }
+
const html = [];
- if (type == 'list') {
+ if (type == 'listtemplate') {
html.push('##delcheck##
');
}
@@ -432,7 +436,7 @@ export class AddonModDataHelperProvider {
);
});
- if (type == 'list') {
+ if (type == 'listtemplate') {
html.push(
'
',
'',
@@ -440,7 +444,7 @@ export class AddonModDataHelperProvider {
' | ',
'
'
);
- } else if (type == 'single') {
+ } else if (type == 'singletemplate') {
html.push(
'',
'',
@@ -448,7 +452,7 @@ export class AddonModDataHelperProvider {
' | ',
'
'
);
- } else if (type == 'asearch') {
+ } else if (type == 'asearchtemplate') {
html.push(
'',
'Author first name: | ',
@@ -467,7 +471,7 @@ export class AddonModDataHelperProvider {
''
);
- if (type == 'list') {
+ if (type == 'listtemplate') {
html.push('
');
}
@@ -583,6 +587,28 @@ export class AddonModDataHelperProvider {
});
}
+ /**
+ * Returns the template of a certain type.
+ *
+ * @param {any} data Database object.
+ * @param {string} type Type of template.
+ * @param {any[]} fields List of database fields.
+ * @return {string} Template HTML.
+ */
+ getTemplate(data: any, type: string, fields: any[]): string {
+ let template = data[type] || this.getDefaultTemplate(type, fields);
+
+ // Try to fix syntax errors so the template can be parsed by Angular.
+ template = this.domUtils.fixHtml(template);
+
+ // Add core-link directive to links.
+ template = template.replace(/]*href="[^>]*)>/i, (match, attributes) => {
+ return '';
+ });
+
+ return template;
+ }
+
/**
* Check if data has been changed by the user.
*
diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts
index 24374f3cd..4a9ad7ed6 100644
--- a/src/providers/utils/dom.ts
+++ b/src/providers/utils/dom.ts
@@ -323,6 +323,33 @@ export class CoreDomUtilsProvider {
return urls;
}
+ /**
+ * Fix syntax errors in HTML.
+ *
+ * @param {string} html HTML text.
+ * @return {string} Fixed HTML text.
+ */
+ fixHtml(html: string): string {
+ this.template.innerHTML = html;
+
+ const attrNameRegExp = /[^\x00-\x20\x7F-\x9F"'>\/=]+/;
+
+ const fixElement = (element: Element): void => {
+ // Remove attributes with an invalid name.
+ Array.from(element.attributes).forEach((attr) => {
+ if (!attrNameRegExp.test(attr.name)) {
+ element.removeAttributeNode(attr);
+ }
+ });
+
+ Array.from(element.children).forEach(fixElement);
+ };
+
+ Array.from(this.template.content.children).forEach(fixElement);
+
+ return this.template.innerHTML;
+ }
+
/**
* Focus an element and open keyboard.
*