diff --git a/src/addon/mod/data/classes/field-plugin-component.ts b/src/addon/mod/data/classes/field-plugin-component.ts
index dcd853b82..bcff4da64 100644
--- a/src/addon/mod/data/classes/field-plugin-component.ts
+++ b/src/addon/mod/data/classes/field-plugin-component.ts
@@ -23,6 +23,4 @@ export class AddonModDataFieldPluginComponent {
@Input() database?: any; // Database object.
@Input() error?: string; // Error when editing.
@Input() viewAction: string; // Action to perform.
-
- constructor() { }
}
diff --git a/src/addon/mod/data/components/action/action.html b/src/addon/mod/data/components/action/action.html
index 9588699a2..53f5096d7 100644
--- a/src/addon/mod/data/components/action/action.html
+++ b/src/addon/mod/data/components/action/action.html
@@ -1,12 +1,12 @@
-
+
-
+
-
+
@@ -14,11 +14,11 @@
-
+
-
+
diff --git a/src/addon/mod/data/components/field-plugin/field-plugin.ts b/src/addon/mod/data/components/field-plugin/field-plugin.ts
index 8fcf975f0..0dbf7c501 100644
--- a/src/addon/mod/data/components/field-plugin/field-plugin.ts
+++ b/src/addon/mod/data/components/field-plugin/field-plugin.ts
@@ -71,13 +71,4 @@ export class AddonModDataFieldPluginComponent implements OnInit {
}
});
}
-
- /**
- * Invalidate the plugin data.
- *
- * @return {Promise} Promise resolved when done.
- */
- invalidate(): Promise {
- return Promise.resolve(this.dynamicComponent && this.dynamicComponent.callComponentFunction('invalidate', []));
- }
}
diff --git a/src/addon/mod/data/data.module.ts b/src/addon/mod/data/data.module.ts
index 12f6eb73b..6231d8e79 100644
--- a/src/addon/mod/data/data.module.ts
+++ b/src/addon/mod/data/data.module.ts
@@ -21,6 +21,10 @@ import { AddonModDataComponentsModule } from './components/components.module';
import { AddonModDataModuleHandler } from './providers/module-handler';
import { AddonModDataProvider } from './providers/data';
import { AddonModDataLinkHandler } from './providers/link-handler';
+import { AddonModDataApproveLinkHandler } from './providers/approve-link-handler';
+import { AddonModDataDeleteLinkHandler } from './providers/delete-link-handler';
+import { AddonModDataShowLinkHandler } from './providers/show-link-handler';
+import { AddonModDataEditLinkHandler } from './providers/edit-link-handler';
import { AddonModDataHelperProvider } from './providers/helper';
import { AddonModDataPrefetchHandler } from './providers/prefetch-handler';
import { AddonModDataSyncProvider } from './providers/sync';
@@ -43,6 +47,10 @@ import { AddonModDataFieldModule } from './fields/field.module';
AddonModDataPrefetchHandler,
AddonModDataHelperProvider,
AddonModDataLinkHandler,
+ AddonModDataApproveLinkHandler,
+ AddonModDataDeleteLinkHandler,
+ AddonModDataShowLinkHandler,
+ AddonModDataEditLinkHandler,
AddonModDataSyncCronHandler,
AddonModDataSyncProvider,
AddonModDataOfflineProvider,
@@ -54,10 +62,16 @@ export class AddonModDataModule {
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModDataModuleHandler,
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModDataPrefetchHandler,
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModDataLinkHandler,
- cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler) {
+ cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler,
+ approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler,
+ showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler) {
moduleDelegate.registerHandler(moduleHandler);
prefetchDelegate.registerHandler(prefetchHandler);
contentLinksDelegate.registerHandler(linkHandler);
+ contentLinksDelegate.registerHandler(approveLinkHandler);
+ contentLinksDelegate.registerHandler(deleteLinkHandler);
+ contentLinksDelegate.registerHandler(showLinkHandler);
+ contentLinksDelegate.registerHandler(editLinkHandler);
cronDelegate.register(syncHandler);
}
}
diff --git a/src/addon/mod/data/fields/url/component/url.html b/src/addon/mod/data/fields/url/component/url.html
index ddd31d55f..9212b3aae 100644
--- a/src/addon/mod/data/fields/url/component/url.html
+++ b/src/addon/mod/data/fields/url/component/url.html
@@ -4,4 +4,4 @@
-{{field.name}}
\ No newline at end of file
+{{field.name}}
\ No newline at end of file
diff --git a/src/addon/mod/data/lang/en.json b/src/addon/mod/data/lang/en.json
index 0e0dcd235..fdd3f402a 100644
--- a/src/addon/mod/data/lang/en.json
+++ b/src/addon/mod/data/lang/en.json
@@ -1,3 +1,41 @@
{
-
+ "addentries": "Add entries",
+ "advancedsearch": "Advanced search",
+ "alttext": "Alternative text",
+ "approve": "Approve",
+ "approved": "Approved",
+ "ascending": "Ascending",
+ "authorfirstname": "Author first name",
+ "authorlastname": "Author surname",
+ "confirmdeleterecord": "Are you sure you want to delete this entry?",
+ "descending": "Descending",
+ "disapprove": "Undo approval",
+ "emptyaddform": "You did not fill out any fields!",
+ "entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity",
+ "entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.",
+ "errorapproving": "Error approving or unapproving entry.",
+ "errordeleting": "Error deleting entry.",
+ "errormustsupplyvalue": "You must supply a value here.",
+ "expired": "Sorry, this activity closed on {{$a}} and is no longer available",
+ "fields": "Fields",
+ "latlongboth": "Both latitude and longitude are required.",
+ "menuchoose": "Choose...",
+ "more": "More",
+ "nomatch": "No matching entries found!",
+ "norecords": "No entries in database",
+ "notapproved": "Entry is not approved yet.",
+ "notopenyet": "Sorry, this activity is not available until {{$a}}",
+ "numrecords": "{{$a}} entries",
+ "other": "Other",
+ "recordapproved": "Entry approved",
+ "recorddeleted": "Entry deleted",
+ "recorddisapproved": "Entry unapproved",
+ "resetsettings": "Reset filters",
+ "search": "Search",
+ "single": "View single",
+ "selectedrequired": "All selected required",
+ "single": "View single",
+ "timeadded": "Time added",
+ "timemodified": "Time modified",
+ "usedate": "Include in search."
}
\ No newline at end of file
diff --git a/src/addon/mod/data/providers/approve-link-handler.ts b/src/addon/mod/data/providers/approve-link-handler.ts
new file mode 100644
index 000000000..bd3c9c71a
--- /dev/null
+++ b/src/addon/mod/data/providers/approve-link-handler.ts
@@ -0,0 +1,122 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { AddonModDataProvider } from './data';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { CoreEventsProvider } from '@providers/events';
+
+/**
+ * Content links handler for database approve/disapprove entry.
+ * Match mod/data/view.php?d=6&approve=5 with a valid data id and entryid.
+ */
+@Injectable()
+export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase {
+ name = 'AddonModDataApproveLinkHandler';
+ featureName = 'CoreCourseModuleDelegate_AddonModData';
+ pattern = /\/mod\/data\/view\.php.*([\?\&](d|approve|disapprove)=\d+)/;
+
+ constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
+ private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider) {
+ super();
+ }
+
+ /**
+ * Convenience function to help get courseId.
+ *
+ * @param {number} dataId Database Id.
+ * @param {string} siteId Site Id, if not set, current site will be used.
+ * @param {number} courseId Course Id if already set.
+ * @return {Promise} Resolved with course Id when done.
+ */
+ protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise {
+ if (courseId) {
+ return Promise.resolve(courseId);
+ }
+
+ return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
+ return module.course;
+ });
+ }
+
+ /**
+ * Get the list of actions for a link (url).
+ *
+ * @param {string[]} siteIds List of sites the URL belongs to.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {CoreContentLinksAction[]|Promise} List of (or promise resolved with list of) actions.
+ */
+ getActions(siteIds: string[], url: string, params: any, courseId?: number):
+ CoreContentLinksAction[] | Promise {
+ return [{
+ action: (siteId, navCtrl?): void => {
+ const modal = this.domUtils.showModalLoading(),
+ dataId = parseInt(params.d, 10),
+ entryId = parseInt(params.approve, 10) || parseInt(params.disapprove, 10),
+ approve = parseInt(params.approve, 10) ? true : false;
+
+ this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
+ courseId = cId;
+
+ // Approve/disapprove entry.
+ return this.dataProvider.approveEntry(dataId, entryId, approve, courseId, siteId).catch((message) => {
+ modal.dismiss();
+ this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errorapproving', true);
+
+ return Promise.reject(null);
+ });
+ }).then(() => {
+ const promises = [];
+ promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
+ promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
+
+ return Promise.all(promises);
+ }).then(() => {
+ this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, siteId);
+
+ modal.dismiss();
+ this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true,
+ 3000);
+ }).finally(() => {
+ // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+ modal.dismiss();
+ });
+ }
+ }];
+ }
+
+ /**
+ * Check if the handler is enabled for a certain site (site + user) and a URL.
+ * If not defined, defaults to true.
+ *
+ * @param {string} siteId The site ID.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {boolean|Promise} Whether the handler is enabled for the URL and site.
+ */
+ isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise {
+ if (typeof params.d == 'undefined' || (typeof params.approve == 'undefined' && typeof params.disapprove == 'undefined')) {
+ // Required fields not defined. Cannot treat the URL.
+ return false;
+ }
+
+ return this.dataProvider.isPluginEnabled(siteId);
+ }
+}
diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts
index 5ffa8fdaa..b4beec32b 100644
--- a/src/addon/mod/data/providers/data.ts
+++ b/src/addon/mod/data/providers/data.ts
@@ -18,6 +18,7 @@ import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreFilepoolProvider } from '@providers/filepool';
import { AddonModDataOfflineProvider } from './offline';
+import { CoreAppProvider } from '@providers/app';
/**
* Service that provides some features for databases.
@@ -32,7 +33,8 @@ export class AddonModDataProvider {
protected logger;
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
- private filepoolProvider: CoreFilepoolProvider, private dataOffline: AddonModDataOfflineProvider) {
+ private filepoolProvider: CoreFilepoolProvider, private dataOffline: AddonModDataOfflineProvider,
+ private appProvider: CoreAppProvider) {
this.logger = logger.getInstance('AddonModDataProvider');
}
@@ -60,6 +62,51 @@ export class AddonModDataProvider {
});
}
+ /**
+ * Approves or unapproves an entry.
+ *
+ * @param {number} dataId Database ID.
+ * @param {number} entryId Entry ID.
+ * @param {boolean} approve Whether to approve (true) or unapprove the entry.
+ * @param {number} courseId Course ID.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resolved when the action is done.
+ */
+ approveEntry(dataId: number, entryId: number, approve: boolean, courseId: number, siteId?: string): Promise {
+ siteId = siteId || this.sitesProvider.getCurrentSiteId();
+
+ // Convenience function to store a data to be synchronized later.
+ const storeOffline = (): Promise => {
+ const action = approve ? 'approve' : 'disapprove';
+
+ return this.dataOffline.saveEntry(dataId, entryId, action, courseId, null, null, null, siteId);
+ };
+
+ // Get if the opposite action is not synced.
+ const oppositeAction = approve ? 'disapprove' : 'approve';
+
+ return this.dataOffline.getEntry(dataId, entryId, oppositeAction, siteId).then(() => {
+ // Found. Just delete the action.
+ return this.dataOffline.deleteEntry(dataId, entryId, oppositeAction, siteId);
+ }).catch(() => {
+
+ if (!this.appProvider.isOnline()) {
+ // App is offline, store the action.
+ return storeOffline();
+ }
+
+ return this.approveEntryOnline(entryId, approve, siteId).catch((error) => {
+ if (this.utils.isWebServiceError(error)) {
+ // The WebService has thrown an error, this means that responses cannot be submitted.
+ return Promise.reject(error);
+ }
+
+ // Couldn't connect to server, store in offline.
+ return storeOffline();
+ });
+ });
+ }
+
/**
* Approves or unapproves an entry. It does not cache calls. It will fail if offline or cannot connect.
*
@@ -79,6 +126,62 @@ export class AddonModDataProvider {
});
}
+ /**
+ * Deletes an entry.
+ *
+ * @param {number} dataId Database ID.
+ * @param {number} entryId Entry ID.
+ * @param {number} courseId Course ID.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resolved when the action is done.
+ */
+ deleteEntry(dataId: number, entryId: number, courseId: number, siteId?: string): Promise {
+ siteId = siteId || this.sitesProvider.getCurrentSiteId();
+
+ // Convenience function to store a data to be synchronized later.
+ const storeOffline = (): Promise => {
+ return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, null, null, null, siteId);
+ };
+
+ let justAdded = false;
+
+ // Check if the opposite action is not synced and just delete it.
+ return this.dataOffline.getEntryActions(dataId, entryId, siteId).then((entries) => {
+ if (entries && entries.length) {
+ // Found. Delete other actions first.
+ const proms = entries.map((entry) => {
+ if (entry.action == 'add') {
+ justAdded = true;
+ }
+
+ return this.dataOffline.deleteEntry(dataId, entryId, entry.action, siteId);
+ });
+
+ return Promise.all(proms);
+ }
+ }).then(() => {
+ if (justAdded) {
+ // The field was added offline, delete and stop.
+ return;
+ }
+
+ if (!this.appProvider.isOnline()) {
+ // App is offline, store the action.
+ return storeOffline();
+ }
+
+ return this.deleteEntryOnline(entryId, siteId).catch((error) => {
+ if (this.utils.isWebServiceError(error)) {
+ // The WebService has thrown an error, this means that responses cannot be submitted.
+ return Promise.reject(error);
+ }
+
+ // Couldn't connect to server, store in offline.
+ return storeOffline();
+ });
+ });
+ }
+
/**
* Deletes an entry. It does not cache calls. It will fail if offline or cannot connect.
*
@@ -494,6 +597,20 @@ export class AddonModDataProvider {
});
}
+ /**
+ * Invalidates database entry data.
+ *
+ * @param {number} dataId Data ID for caching purposes.
+ * @param {number} entryId Entry ID.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resolved when the data is invalidated.
+ */
+ invalidateEntryData(dataId: number, entryId: number, siteId?: string): Promise {
+ return this.sitesProvider.getSite(siteId).then((site) => {
+ return site.invalidateWsCacheForKey(this.getEntryCacheKey(dataId, entryId));
+ });
+ }
+
/**
* Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the database WS are available.
*
diff --git a/src/addon/mod/data/providers/delete-link-handler.ts b/src/addon/mod/data/providers/delete-link-handler.ts
new file mode 100644
index 000000000..43af7fb8a
--- /dev/null
+++ b/src/addon/mod/data/providers/delete-link-handler.ts
@@ -0,0 +1,121 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { AddonModDataProvider } from './data';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { CoreEventsProvider } from '@providers/events';
+
+/**
+ * Content links handler for database delete entry.
+ * Match mod/data/view.php?d=6&delete=5 with a valid data id and entryid.
+ */
+@Injectable()
+export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
+ name = 'AddonModDataDeleteLinkHandler';
+ featureName = 'CoreCourseModuleDelegate_AddonModData';
+ pattern = /\/mod\/data\/view\.php.*([\?\&](d|delete)=\d+)/;
+
+ constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
+ private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider) {
+ super();
+ }
+
+ /**
+ * Convenience function to help get courseId.
+ *
+ * @param {number} dataId Database Id.
+ * @param {string} siteId Site Id, if not set, current site will be used.
+ * @param {number} courseId Course Id if already set.
+ * @return {Promise} Resolved with course Id when done.
+ */
+ protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise {
+ if (courseId) {
+ return Promise.resolve(courseId);
+ }
+
+ return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
+ return module.course;
+ });
+ }
+
+ /**
+ * Get the list of actions for a link (url).
+ *
+ * @param {string[]} siteIds List of sites the URL belongs to.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {CoreContentLinksAction[]|Promise} List of (or promise resolved with list of) actions.
+ */
+ getActions(siteIds: string[], url: string, params: any, courseId?: number):
+ CoreContentLinksAction[] | Promise {
+ return [{
+ action: (siteId, navCtrl?): void => {
+ const modal = this.domUtils.showModalLoading(),
+ dataId = parseInt(params.d, 10),
+ entryId = parseInt(params.delete, 10);
+
+ this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
+ courseId = cId;
+
+ // Delete entry.
+ return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId).catch((message) => {
+ modal.dismiss();
+ this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
+
+ return Promise.reject(null);
+ });
+ }).then(() => {
+ const promises = [];
+ promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
+ promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
+
+ return Promise.all(promises);
+ }).then(() => {
+ this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId,
+ deleted: true}, siteId);
+
+ modal.dismiss();
+ this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
+ }).finally(() => {
+ // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+ modal.dismiss();
+ });
+ }
+ }];
+ }
+
+ /**
+ * Check if the handler is enabled for a certain site (site + user) and a URL.
+ * If not defined, defaults to true.
+ *
+ * @param {string} siteId The site ID.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {boolean|Promise} Whether the handler is enabled for the URL and site.
+ */
+ isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise {
+ if (typeof params.d == 'undefined' || typeof params.delete == 'undefined') {
+ // Required fields not defined. Cannot treat the URL.
+ return false;
+ }
+
+ return this.dataProvider.isPluginEnabled(siteId);
+ }
+}
diff --git a/src/addon/mod/data/providers/edit-link-handler.ts b/src/addon/mod/data/providers/edit-link-handler.ts
new file mode 100644
index 000000000..448035aeb
--- /dev/null
+++ b/src/addon/mod/data/providers/edit-link-handler.ts
@@ -0,0 +1,93 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
+import { AddonModDataProvider } from './data';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+
+/**
+ * Content links handler for database add or edit entry.
+ * Match mod/data/edit.php?d=6&rid=6 with a valid data and optional record id.
+ */
+@Injectable()
+export class AddonModDataEditLinkHandler extends CoreContentLinksHandlerBase {
+ name = 'AddonModDataEditLinkHandler';
+ featureName = 'CoreCourseModuleDelegate_AddonModData';
+ pattern = /\/mod\/data\/edit\.php.*([\?\&](d|rid)=\d+)/;
+
+ constructor(private linkHelper: CoreContentLinksHelperProvider, private dataProvider: AddonModDataProvider,
+ private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) {
+ super();
+ }
+
+ /**
+ * Get the list of actions for a link (url).
+ *
+ * @param {string[]} siteIds List of sites the URL belongs to.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {CoreContentLinksAction[]|Promise} List of (or promise resolved with list of) actions.
+ */
+ getActions(siteIds: string[], url: string, params: any, courseId?: number):
+ CoreContentLinksAction[] | Promise {
+ return [{
+ action: (siteId, navCtrl?): void => {
+ const modal = this.domUtils.showModalLoading(),
+ dataId = parseInt(params.d, 10),
+ rId = parseInt(params.rid, 10) || false;
+
+ this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
+ const stateParams = {
+ moduleId: module.id,
+ module: module,
+ courseId: module.course
+ };
+
+ if (rId) {
+ stateParams['entryId'] = rId;
+ }
+
+ return this.linkHelper.goInSite(navCtrl, 'AddonModDataEditPage', stateParams, siteId);
+ }).finally(() => {
+ // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+ modal.dismiss();
+ });
+ }
+ }];
+ }
+
+ /**
+ * Check if the handler is enabled for a certain site (site + user) and a URL.
+ * If not defined, defaults to true.
+ *
+ * @param {string} siteId The site ID.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {boolean|Promise} Whether the handler is enabled for the URL and site.
+ */
+ isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise {
+ if (typeof params.d == 'undefined') {
+ // Id not defined. Cannot treat the URL.
+ return false;
+ }
+
+ return this.dataProvider.isPluginEnabled(siteId);
+ }
+}
diff --git a/src/addon/mod/data/providers/offline.ts b/src/addon/mod/data/providers/offline.ts
index 03cff2081..c30316b1a 100644
--- a/src/addon/mod/data/providers/offline.ts
+++ b/src/addon/mod/data/providers/offline.ts
@@ -27,10 +27,10 @@ export class AddonModDataOfflineProvider {
protected logger;
// Variables for database.
- protected SURVEY_TABLE = 'addon_mod_data_entry';
+ protected DATA_ENTRY_TABLE = 'addon_mod_data_entry';
protected tablesSchema = [
{
- name: this.SURVEY_TABLE,
+ name: this.DATA_ENTRY_TABLE,
columns: [
{
name: 'dataid',
@@ -102,7 +102,7 @@ export class AddonModDataOfflineProvider {
*/
deleteEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise {
return this.sitesProvider.getSite(siteId).then((site) => {
- return site.getDb().deleteRecords(this.SURVEY_TABLE, {dataid: dataId, entryid: entryId, action: action});
+ return site.getDb().deleteRecords(this.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, action: action});
});
}
@@ -114,7 +114,7 @@ export class AddonModDataOfflineProvider {
*/
getAllEntries(siteId?: string): Promise {
return this.sitesProvider.getSite(siteId).then((site) => {
- return site.getDb().getAllRecords(this.SURVEY_TABLE);
+ return site.getDb().getAllRecords(this.DATA_ENTRY_TABLE);
});
}
@@ -127,7 +127,7 @@ export class AddonModDataOfflineProvider {
*/
getDatabaseEntries(dataId: number, siteId?: string): Promise {
return this.sitesProvider.getSite(siteId).then((site) => {
- return site.getDb().getRecords(this.SURVEY_TABLE, {dataid: dataId});
+ return site.getDb().getRecords(this.DATA_ENTRY_TABLE, {dataid: dataId});
});
}
@@ -142,7 +142,7 @@ export class AddonModDataOfflineProvider {
*/
getEntry(dataId: number, entryId: number, action: string, siteId?: string): Promise {
return this.sitesProvider.getSite(siteId).then((site) => {
- return site.getDb().getRecord(this.SURVEY_TABLE, {dataid: dataId, entryid: entryId, action: action});
+ return site.getDb().getRecord(this.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId, action: action});
});
}
@@ -156,7 +156,7 @@ export class AddonModDataOfflineProvider {
*/
getEntryActions(dataId: number, entryId: number, siteId?: string): Promise {
return this.sitesProvider.getSite(siteId).then((site) => {
- return site.getDb().getRecords(this.SURVEY_TABLE, {dataid: dataId, entryid: entryId});
+ return site.getDb().getRecords(this.DATA_ENTRY_TABLE, {dataid: dataId, entryid: entryId});
});
}
@@ -207,4 +207,38 @@ export class AddonModDataOfflineProvider {
return this.textUtils.concatenatePaths(folderPath, entryId + '_' + fieldId);
});
}
+
+ /**
+ * Save an entry data to be sent later.
+ *
+ * @param {number} dataId Database ID.
+ * @param {number} entryId Database entry Id. If action is add entryId should be 0 and -timemodified will be used.
+ * @param {string} action Action to be done to the entry: [add, edit, delete, approve, disapprove]
+ * @param {number} courseId Course ID of the database.
+ * @param {number} [groupId] Group ID. Only provided when adding.
+ * @param {any[]} [fields] Array of field data of the entry if needed.
+ * @param {number} [timemodified] The time the entry was modified. If not defined, current time.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resolved if stored, rejected if failure.
+ */
+ saveEntry(dataId: number, entryId: number, action: string, courseId: number, groupId?: number, fields?: any[],
+ timemodified?: number, siteId?: string): Promise {
+
+ return this.sitesProvider.getSite(siteId).then((site) => {
+ timemodified = timemodified || new Date().getTime();
+ entryId = typeof entryId == 'undefined' || entryId === null ? -timemodified : entryId;
+ const entry = {
+ dataid: dataId,
+ courseid: courseId,
+ groupid: groupId,
+ action: action,
+ entryid: entryId,
+ fields: fields,
+ timemodified: timemodified
+ };
+
+ return site.getDb().insertRecord(this.DATA_ENTRY_TABLE, entry);
+ });
+ }
+
}
diff --git a/src/addon/mod/data/providers/show-link-handler.ts b/src/addon/mod/data/providers/show-link-handler.ts
new file mode 100644
index 000000000..702a03345
--- /dev/null
+++ b/src/addon/mod/data/providers/show-link-handler.ts
@@ -0,0 +1,105 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
+import { AddonModDataProvider } from './data';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+
+/**
+ * Content links handler for database show entry.
+ * Match mod/data/view.php?d=6&rid=5 with a valid data id and entryid.
+ */
+@Injectable()
+export class AddonModDataShowLinkHandler extends CoreContentLinksHandlerBase {
+ name = 'AddonModDataShowLinkHandler';
+ featureName = 'CoreCourseModuleDelegate_AddonModData';
+ pattern = /\/mod\/data\/view\.php.*([\?\&](d|rid|page|group|mode)=\d+)/;
+
+ constructor(private linkHelper: CoreContentLinksHelperProvider, private dataProvider: AddonModDataProvider,
+ private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) {
+ super();
+ }
+
+ /**
+ * Get the list of actions for a link (url).
+ *
+ * @param {string[]} siteIds List of sites the URL belongs to.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {CoreContentLinksAction[]|Promise} List of (or promise resolved with list of) actions.
+ */
+ getActions(siteIds: string[], url: string, params: any, courseId?: number):
+ CoreContentLinksAction[] | Promise {
+ return [{
+ action: (siteId, navCtrl?): void => {
+ const modal = this.domUtils.showModalLoading(),
+ dataId = parseInt(params.d, 10),
+ rId = parseInt(params.rid, 10) || false,
+ group = parseInt(params.group, 10) || false,
+ page = parseInt(params.page, 10) || false;
+
+ this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
+ const stateParams = {
+ moduleId: module.id,
+ module: module,
+ courseId: module.course
+ };
+
+ if (group) {
+ stateParams['group'] = group;
+ }
+
+ if (params.mode && params.mode == 'single') {
+ stateParams['page'] = page || 1;
+ } else if (rId) {
+ stateParams['entryId'] = rId;
+ }
+
+ return this.linkHelper.goInSite(navCtrl, 'AddonModDataEntryPage', stateParams, siteId);
+ }).finally(() => {
+ // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+ modal.dismiss();
+ });
+ }
+ }];
+ }
+
+ /**
+ * Check if the handler is enabled for a certain site (site + user) and a URL.
+ * If not defined, defaults to true.
+ *
+ * @param {string} siteId The site ID.
+ * @param {string} url The URL to treat.
+ * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+ * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+ * @return {boolean|Promise} Whether the handler is enabled for the URL and site.
+ */
+ isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise {
+ if (typeof params.d == 'undefined') {
+ // Id not defined. Cannot treat the URL.
+ return false;
+ }
+
+ if ((!params.mode || params.mode != 'single') && typeof params.rid == 'undefined') {
+ return false;
+ }
+
+ return this.dataProvider.isPluginEnabled(siteId);
+ }
+}