MOBILE-2856 data: Fix actions for offline entries

main
Albert Gasset 2019-04-10 18:19:52 +02:00
parent b810862fca
commit e6f9ecebcc
5 changed files with 187 additions and 146 deletions

View File

@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, OnInit, Injector } from '@angular/core';
import { NavController } from 'ionic-angular';
import { CoreEventsProvider } from '@providers/events';
import { AddonModDataProvider } from '../../providers/data';
import { AddonModDataHelperProvider } from '../../providers/helper';
import { AddonModDataOfflineProvider } from '../../providers/offline';
import { CoreSitesProvider } from '@providers/sites';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreUserProvider } from '@core/user/providers/user';
/**
@ -30,6 +33,8 @@ export class AddonModDataActionComponent implements OnInit {
@Input() action: string; // The field to render.
@Input() entry?: any; // The value of the field.
@Input() database: any; // Database object.
@Input() module: any; // Module object.
@Input() group: number; // Module object.
@Input() offset?: number; // Offset of the entry.
siteId: string;
@ -39,11 +44,72 @@ export class AddonModDataActionComponent implements OnInit {
constructor(protected injector: Injector, protected dataProvider: AddonModDataProvider,
protected dataOffline: AddonModDataOfflineProvider, protected eventsProvider: CoreEventsProvider,
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider) {
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider, private navCtrl: NavController,
protected linkHelper: CoreContentLinksHelperProvider, private dataHelper: AddonModDataHelperProvider) {
this.rootUrl = sitesProvider.getCurrentSite().getURL();
this.siteId = sitesProvider.getCurrentSiteId();
}
/**
* Component being initialized.
*/
ngOnInit(): void {
if (this.action == 'userpicture') {
this.userProvider.getProfile(this.entry.userid, this.database.courseid).then((profile) => {
this.userPicture = profile.profileimageurl;
});
}
}
/**
* Approve the entry.
*/
approveEntry(): void {
this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, true, this.database.courseid);
}
/**
* Show confirmation modal for deleting the entry.
*/
deleteEntry(): void {
this.dataHelper.showDeleteEntryModal(this.database.id, this.entry.id, this.database.courseid);
}
/**
* Disapprove the entry.
*/
disapproveEntry(): void {
this.dataHelper.approveOrDisapproveEntry(this.database.id, this.entry.id, false, this.database.courseid);
}
/**
* Go to the edit page of the entry.
*/
editEntry(): void {
const pageParams = {
courseId: this.database.course,
module: this.module,
entryId: this.entry.id
};
this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEditPage', pageParams);
}
/**
* Go to the view page of the entry.
*/
viewEntry(): void {
const pageParams: any = {
courseId: this.database.course,
module: this.module,
entryId: this.entry.id,
group: this.group,
offset: this.offset
};
this.linkHelper.goInSite(this.navCtrl, 'AddonModDataEntryPage', pageParams);
}
/**
* Undo delete action.
*
@ -60,37 +126,4 @@ export class AddonModDataActionComponent implements OnInit {
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, this.siteId);
});
}
/**
* Component being initialized.
*/
ngOnInit(): void {
switch (this.action) {
case 'more':
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&rid=' + this.entry.id;
if (typeof this.offset == 'number') {
this.url += '&mode=single&page=' + this.offset;
}
break;
case 'edit':
this.url = this.rootUrl + '/mod/data/edit.php?d= ' + this.entry.dataid + '&rid=' + this.entry.id;
break;
case 'delete':
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&delete=' + this.entry.id;
break;
case 'approve':
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&approve=' + this.entry.id;
break;
case 'disapprove':
this.url = this.rootUrl + '/mod/data/view.php?d= ' + this.entry.dataid + '&disapprove=' + this.entry.id;
break;
case 'userpicture':
this.userProvider.getProfile(this.entry.userid, this.database.courseid).then((profile) => {
this.userPicture = profile.profileimageurl;
});
break;
default:
break;
}
}
}

View File

@ -1,12 +1,12 @@
<a *ngIf="action == 'more'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.more' | translate">
<a *ngIf="action == 'more'" ion-button icon-only clear (click)="viewEntry()" [title]="'addon.mod_data.more' | translate">
<ion-icon name="search"></ion-icon>
</a>
<a *ngIf="action == 'edit'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'core.edit' | translate">
<a *ngIf="action == 'edit'" ion-button icon-only clear (click)="editEntry()" [title]="'core.edit' | translate">
<ion-icon name="cog"></ion-icon>
</a>
<a *ngIf="action == 'delete' && !entry.deleted" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'core.delete' | translate">
<a *ngIf="action == 'delete' && !entry.deleted" ion-button icon-only clear (click)="deleteEntry()" [title]="'core.delete' | translate">
<ion-icon name="trash"></ion-icon>
</a>
@ -14,11 +14,11 @@
<ion-icon name="undo"></ion-icon>
</a>
<a *ngIf="action == 'approve'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.approve' | translate">
<a *ngIf="action == 'approve'" ion-button icon-only clear (click)="approveEntry()" [title]="'addon.mod_data.approve' | translate">
<ion-icon name="thumbs-up"></ion-icon>
</a>
<a *ngIf="action == 'disapprove'" ion-button icon-only clear [href]="url" core-link capture="true" [title]="'addon.mod_data.disapprove' | translate">
<a *ngIf="action == 'disapprove'" ion-button icon-only clear (click)="disapproveEntry()" [title]="'addon.mod_data.disapprove' | translate">
<ion-icon name="thumbs-down"></ion-icon>
</a>

View File

@ -16,9 +16,7 @@ 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';
import { AddonModDataHelperProvider } from './helper';
/**
* Content links handler for database approve/disapprove entry.
@ -30,29 +28,10 @@ export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase
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) {
constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) {
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<number>} Resolved with course Id when done.
*/
protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise<number> {
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).
*
@ -66,34 +45,11 @@ export class AddonModDataApproveLinkHandler extends CoreContentLinksHandlerBase
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{
action: (siteId, navCtrl?): void => {
const modal = this.domUtils.showModalLoading(),
dataId = parseInt(params.d, 10),
const 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) => {
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);
this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true,
3000);
}).finally(() => {
modal.dismiss();
});
this.dataHelper.approveOrDisapproveEntry(dataId, entryId, approve, courseId, siteId);
}
}];
}

View File

@ -13,13 +13,10 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/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';
import { AddonModDataHelperProvider } from './helper';
/**
* Content links handler for database delete entry.
@ -31,30 +28,10 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
featureName = 'CoreCourseModuleDelegate_AddonModData';
pattern = /\/mod\/data\/view\.php.*([\?\&](d|delete)=\d+)/;
constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
private translate: TranslateService) {
constructor(private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider) {
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<number>} Resolved with course Id when done.
*/
protected getActivityCourseIdIfNotSet(dataId: number, siteId: string, courseId: number): Promise<number> {
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).
*
@ -68,38 +45,10 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{
action: (siteId, navCtrl?): void => {
const dataId = parseInt(params.d, 10);
const entryId = parseInt(params.delete, 10);
this.domUtils.showConfirm(this.translate.instant('addon.mod_data.confirmdeleterecord')).then(() => {
const modal = this.domUtils.showModalLoading(),
dataId = parseInt(params.d, 10),
entryId = parseInt(params.delete, 10);
return this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
courseId = cId;
// Delete entry.
return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId).catch((message) => {
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);
this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
}).finally(() => {
modal.dismiss();
});
}).catch(() => {
// Nothing to do.
});
this.dataHelper.showDeleteEntryModal(dataId, entryId, courseId);
}
}];
}

View File

@ -14,8 +14,12 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
import { AddonModDataFieldsDelegate } from './fields-delegate';
import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline';
@ -32,7 +36,9 @@ export class AddonModDataHelperProvider {
constructor(private sitesProvider: CoreSitesProvider, protected dataProvider: AddonModDataProvider,
private translate: TranslateService, private fieldsDelegate: AddonModDataFieldsDelegate,
private dataOffline: AddonModDataOfflineProvider, private fileUploaderProvider: CoreFileUploaderProvider,
private textUtils: CoreTextUtilsProvider, private ratingOffline: CoreRatingOfflineProvider) { }
private textUtils: CoreTextUtilsProvider, private eventsProvider: CoreEventsProvider, private utils: CoreUtilsProvider,
private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider,
private ratingOffline: CoreRatingOfflineProvider) {}
/**
* Returns the record with the offline actions applied.
@ -104,6 +110,46 @@ export class AddonModDataHelperProvider {
});
}
/**
* Approve or disapprove a database entry.
*
* @param {number} dataId Database ID.
* @param {number} entryId Entry ID.
* @param {boolaen} approve True to approve, false to disapprove.
* @param {number} [courseId] Course ID. It not defined, it will be fetched.
* @param {string} [siteId] Site ID. If not defined, current site.
*/
approveOrDisapproveEntry(dataId: number, entryId: number, approve: boolean, courseId?: number, siteId?: string): void {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const modal = this.domUtils.showModalLoading('core.sending', true);
this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => {
// Approve/disapprove entry.
return this.dataProvider.approveEntry(dataId, entryId, approve, courseId, siteId).catch((message) => {
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).catch(() => {
// Ignore errors.
});
}).then(() => {
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId}, siteId);
this.domUtils.showToast(approve ? 'addon.mod_data.recordapproved' : 'addon.mod_data.recorddisapproved', true, 3000);
}).catch(() => {
// Ignore error, it was already displayed.
}).finally(() => {
modal.dismiss();
});
}
/**
* Displays fields for being shown.
*
@ -146,8 +192,8 @@ export class AddonModDataHelperProvider {
} else if (action == 'approvalstatus') {
render = this.translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved'));
} else {
render = '<addon-mod-data-action action="' + action + '" [entry]="entries[' + entry.id +
']" mode="' + mode + '" [database]="data" [offset]="' + offset + '"></addon-mod-data-action>';
render = '<addon-mod-data-action action="' + action + '" [entry]="entries[' + entry.id + ']" mode="' + mode +
'" [database]="data" [module]="module" [offset]="' + offset + '" [group]="group" ></addon-mod-data-action>';
}
template = template.replace(replace, render);
} else {
@ -337,6 +383,24 @@ export class AddonModDataHelperProvider {
};
}
/**
* Convenience function to get the course id of the database.
*
* @param {number} dataId Database id.
* @param {number} [courseId] Course id, if known.
* @param {string} [siteId] Site id, if not set, current site will be used.
* @return {Promise<number>} Resolved with course Id when done.
*/
protected getActivityCourseIdIfNotSet(dataId: number, courseId?: number, siteId?: string): Promise<number> {
if (courseId) {
return Promise.resolve(courseId);
}
return this.courseProvider.getModuleBasicInfoByInstance(dataId, 'data', siteId).then((module) => {
return module.course;
});
}
/**
* Returns the default template of a certain type.
*
@ -543,6 +607,45 @@ export class AddonModDataHelperProvider {
});
}
/**
* Displays a confirmation modal for deleting an entry.
*
* @param {number} dataId Database ID.
* @param {number} entryId Entry ID.
* @param {number} [courseId] Course ID. It not defined, it will be fetched.
* @param {string} [siteId] Site ID. If not defined, current site.
*/
showDeleteEntryModal(dataId: number, entryId: number, courseId?: number, siteId?: string): void {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
this.domUtils.showConfirm(this.translate.instant('addon.mod_data.confirmdeleterecord')).then(() => {
const modal = this.domUtils.showModalLoading();
return this.getActivityCourseIdIfNotSet(dataId, courseId, siteId).then((courseId) => {
return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId);
}).catch((message) => {
this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
return Promise.reject(null);
}).then(() => {
return this.utils.allPromises([
this.dataProvider.invalidateEntryData(dataId, entryId, siteId),
this.dataProvider.invalidateEntriesData(dataId, siteId)
]).catch(() => {
// Ignore errors.
});
}).then(() => {
this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId, entryId, deleted: true}, siteId);
this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
}).finally(() => {
modal.dismiss();
});
}).catch(() => {
// Ignore error, it was already displayed.
});
}
/**
* Given a list of files (either online files or local files), store the local files in a local folder
* to be submitted later.