commit
fdf7ac7fab
|
@ -35,10 +35,11 @@ import {
|
||||||
AddonModForumDiscussion,
|
AddonModForumDiscussion,
|
||||||
AddonModForumPost,
|
AddonModForumPost,
|
||||||
AddonModForumPostFormData,
|
AddonModForumPostFormData,
|
||||||
|
AddonModForumPrepareDraftAreaForPostWSResponse,
|
||||||
} from '../../services/forum';
|
} from '../../services/forum';
|
||||||
import { CoreTag } from '@features/tag/services/tag';
|
import { CoreTag } from '@features/tag/services/tag';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
|
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
|
||||||
import { AddonModForumSync } from '../../services/forum-sync';
|
import { AddonModForumSync } from '../../services/forum-sync';
|
||||||
import { CoreSync } from '@services/sync';
|
import { CoreSync } from '@services/sync';
|
||||||
import { CoreText } from '@singletons/text';
|
import { CoreText } from '@singletons/text';
|
||||||
|
@ -47,7 +48,7 @@ import { AddonModForumOffline } from '../../services/forum-offline';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreRatingInfo } from '@features/rating/services/rating';
|
import { CoreRatingInfo } from '@features/rating/services/rating';
|
||||||
import { CoreForms } from '@singletons/form';
|
import { CoreForms } from '@singletons/form';
|
||||||
import { CoreFileEntry } from '@services/file-helper';
|
import { CoreFileEntry, CoreFileHelper } from '@services/file-helper';
|
||||||
import { AddonModForumSharedPostFormData } from '../../pages/discussion/discussion';
|
import { AddonModForumSharedPostFormData } from '../../pages/discussion/discussion';
|
||||||
import { CoreDom } from '@singletons/dom';
|
import { CoreDom } from '@singletons/dom';
|
||||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||||
|
@ -56,6 +57,7 @@ import { CoreToasts } from '@services/toasts';
|
||||||
import { toBoolean } from '@/core/transforms/boolean';
|
import { toBoolean } from '@/core/transforms/boolean';
|
||||||
import { CorePopovers } from '@services/popovers';
|
import { CorePopovers } from '@services/popovers';
|
||||||
import { CoreLoadings } from '@services/loadings';
|
import { CoreLoadings } from '@services/loadings';
|
||||||
|
import { CoreWSFile } from '@services/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
|
* Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
|
||||||
|
@ -95,6 +97,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
displaySubject = true;
|
displaySubject = true;
|
||||||
optionsMenuEnabled = false;
|
optionsMenuEnabled = false;
|
||||||
|
|
||||||
|
protected preparePostData?: AddonModForumPrepareDraftAreaForPostWSResponse;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected elementRef: ElementRef,
|
protected elementRef: ElementRef,
|
||||||
) {}
|
) {}
|
||||||
|
@ -211,7 +215,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
this.formData.isEditing = !!isEditing;
|
this.formData.isEditing = !!isEditing;
|
||||||
this.formData.subject = subject || this.defaultReplySubject || '';
|
this.formData.subject = subject || this.defaultReplySubject || '';
|
||||||
this.formData.message = message || null;
|
this.formData.message = message || null;
|
||||||
this.formData.files = files || [];
|
this.formData.files = (files ?? []).slice(); // Make a copy to avoid modifying the original array.
|
||||||
this.formData.isprivatereply = !!isPrivate;
|
this.formData.isprivatereply = !!isPrivate;
|
||||||
this.formData.id = postId;
|
this.formData.id = postId;
|
||||||
|
|
||||||
|
@ -226,6 +230,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
|
|
||||||
// Show advanced fields if any of them has not the default value.
|
// Show advanced fields if any of them has not the default value.
|
||||||
this.advanced = this.formData.files.length > 0;
|
this.advanced = this.formData.files.length > 0;
|
||||||
|
|
||||||
|
if (!isEditing || !postId || postId <= 0) {
|
||||||
|
this.preparePostData = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,6 +322,28 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
// Ask confirm if there is unsaved data.
|
// Ask confirm if there is unsaved data.
|
||||||
try {
|
try {
|
||||||
await this.confirmDiscard();
|
await this.confirmDiscard();
|
||||||
|
} catch {
|
||||||
|
// Cancelled.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = await CoreLoadings.show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
let message = this.post.message;
|
||||||
|
|
||||||
|
if (this.post.id > 0) {
|
||||||
|
// Call prepare post for edition to retrieve the message without any added content (like filters and plagiarism).
|
||||||
|
this.preparePostData = await AddonModForum.preparePostForEdition(this.post.id, 'post');
|
||||||
|
|
||||||
|
const { text } = CoreFileHelper.replaceDraftfileUrls(
|
||||||
|
CoreSites.getRequiredCurrentSite().getURL(),
|
||||||
|
this.preparePostData.messagetext,
|
||||||
|
this.post.messageinlinefiles?.length ? this.post.messageinlinefiles : (this.preparePostData.files ?? []),
|
||||||
|
);
|
||||||
|
|
||||||
|
message = text;
|
||||||
|
}
|
||||||
|
|
||||||
this.formData.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId);
|
this.formData.syncId = AddonModForumSync.getDiscussionSyncId(this.discussionId);
|
||||||
CoreSync.blockOperation(ADDON_MOD_FORUM_COMPONENT, this.formData.syncId);
|
CoreSync.blockOperation(ADDON_MOD_FORUM_COMPONENT, this.formData.syncId);
|
||||||
|
@ -322,7 +352,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
this.post.parentid,
|
this.post.parentid,
|
||||||
true,
|
true,
|
||||||
this.post.subject,
|
this.post.subject,
|
||||||
this.post.message,
|
message,
|
||||||
this.post.attachments,
|
this.post.attachments,
|
||||||
this.post.isprivatereply,
|
this.post.isprivatereply,
|
||||||
this.post.id > 0 ? this.post.id : undefined,
|
this.post.id > 0 ? this.post.id : undefined,
|
||||||
|
@ -331,8 +361,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
this.scrollToForm();
|
this.scrollToForm();
|
||||||
|
|
||||||
this.analyticsLogEvent('mod_forum_update_discussion_post', `/mod/forum/post.php?edit=${this.post.id}`);
|
this.analyticsLogEvent('mod_forum_update_discussion_post', `/mod/forum/post.php?edit=${this.post.id}`);
|
||||||
} catch {
|
} catch (error) {
|
||||||
// Cancelled.
|
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_forum.errorgetpost', true);
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,83 +393,76 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let saveOffline = false;
|
|
||||||
let message = this.formData.message;
|
let message = this.formData.message;
|
||||||
const subject = this.formData.subject;
|
const subject = this.formData.subject;
|
||||||
const replyingTo = this.formData.replyingTo!;
|
const replyingTo = this.formData.replyingTo ?? 0;
|
||||||
const files = this.formData.files || [];
|
const files = this.formData.files || [];
|
||||||
const isEditOnline = this.formData.id && this.formData.id > 0;
|
const isEditOnline = this.formData.id && this.formData.id > 0;
|
||||||
const modal = await CoreLoadings.show('core.sending', true);
|
const modal = await CoreLoadings.show('core.sending', true);
|
||||||
|
|
||||||
|
if (isEditOnline && this.preparePostData) {
|
||||||
|
// Restore the draft file URLs, otherwise the treated URLs would be saved in the content, which can cause problems.
|
||||||
|
message = CoreFileHelper.restoreDraftfileUrls(
|
||||||
|
CoreSites.getRequiredCurrentSite().getURL(),
|
||||||
|
message,
|
||||||
|
this.preparePostData.messagetext,
|
||||||
|
this.post.messageinlinefiles?.length ? this.post.messageinlinefiles : (this.preparePostData.files ?? []),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Add some HTML to the message if needed.
|
// Add some HTML to the message if needed.
|
||||||
message = CoreText.formatHtmlLines(message);
|
message = CoreText.formatHtmlLines(message);
|
||||||
|
|
||||||
// Upload attachments first if any.
|
|
||||||
let attachments;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (files.length) {
|
|
||||||
try {
|
|
||||||
attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(
|
|
||||||
this.forum.id,
|
|
||||||
isEditOnline ? this.formData.id! : replyingTo,
|
|
||||||
files,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
// Cannot upload them in online, save them in offline.
|
|
||||||
if (!this.forum.id || isEditOnline || CoreUtils.isWebServiceError(error)) {
|
|
||||||
// Cannot store them in offline. Reject.
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveOffline = true;
|
|
||||||
attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sent = false;
|
let sent = false;
|
||||||
|
|
||||||
if (isEditOnline) {
|
if (this.formData.id && this.formData.id > 0) {
|
||||||
sent = await AddonModForum.updatePost(this.formData.id!, subject, message, {
|
const attachments = await this.uploadAttachmentsForEditOnline(this.formData.id);
|
||||||
attachmentsid: attachments,
|
|
||||||
});
|
|
||||||
} else if (saveOffline) {
|
|
||||||
// Save post in offline.
|
|
||||||
await AddonModForumOffline.replyPost(
|
|
||||||
replyingTo,
|
|
||||||
this.discussionId,
|
|
||||||
this.forum.id,
|
|
||||||
this.forum.name,
|
|
||||||
this.courseId,
|
|
||||||
subject,
|
|
||||||
message,
|
|
||||||
{
|
|
||||||
attachmentsid: attachments,
|
|
||||||
private: !!this.formData.isprivatereply,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set sent to false since it wasn't sent to server.
|
sent = await AddonModForum.updatePost(this.formData.id, subject, message, {
|
||||||
sent = false;
|
attachmentsid: attachments,
|
||||||
|
inlineattachmentsid: this.preparePostData?.draftitemid,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Try to send it to server.
|
const { attachments, saveOffline } = await this.uploadAttachmentsForReply(replyingTo);
|
||||||
// Don't allow offline if there are attachments since they were uploaded fine.
|
|
||||||
sent = await AddonModForum.replyPost(
|
if (saveOffline) {
|
||||||
replyingTo,
|
// Save post in offline.
|
||||||
this.discussionId,
|
await AddonModForumOffline.replyPost(
|
||||||
this.forum.id,
|
replyingTo,
|
||||||
this.forum.name,
|
this.discussionId,
|
||||||
this.courseId,
|
this.forum.id,
|
||||||
subject,
|
this.forum.name,
|
||||||
message,
|
this.courseId,
|
||||||
{
|
subject,
|
||||||
attachmentsid: attachments,
|
message,
|
||||||
private: !!this.formData.isprivatereply,
|
{
|
||||||
},
|
attachmentsid: attachments,
|
||||||
undefined,
|
private: !!this.formData.isprivatereply,
|
||||||
!files.length,
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Set sent to false since it wasn't sent to server.
|
||||||
|
sent = false;
|
||||||
|
} else {
|
||||||
|
// Try to send it to server.
|
||||||
|
// Don't allow offline if there are attachments since they were uploaded fine.
|
||||||
|
sent = await AddonModForum.replyPost(
|
||||||
|
replyingTo,
|
||||||
|
this.discussionId,
|
||||||
|
this.forum.id,
|
||||||
|
this.forum.name,
|
||||||
|
this.courseId,
|
||||||
|
subject,
|
||||||
|
message,
|
||||||
|
{
|
||||||
|
attachmentsid: attachments,
|
||||||
|
private: !!this.formData.isprivatereply,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
!files.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sent && this.forum.id) {
|
if (sent && this.forum.id) {
|
||||||
|
@ -464,6 +489,81 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload attachments when editing an online post.
|
||||||
|
*
|
||||||
|
* @param postId Post ID being edited.
|
||||||
|
* @returns Draft area id (if any attachment has changed).
|
||||||
|
*/
|
||||||
|
protected async uploadAttachmentsForEditOnline(postId: number): Promise<number | undefined> {
|
||||||
|
const files = this.formData.files || [];
|
||||||
|
const previousAttachments = (this.post.attachments ?? []) as CoreWSFile[];
|
||||||
|
|
||||||
|
if (!CoreFileUploader.areFileListDifferent(files, previousAttachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use prepare post for edition to avoid re-uploading all files.
|
||||||
|
let filesToKeep = files.filter((file): file is CoreWSFile => !CoreUtils.isFileEntry(file));
|
||||||
|
let removedFiles: { filepath: string; filename: string }[] | undefined;
|
||||||
|
|
||||||
|
if (previousAttachments.length && !filesToKeep.length) {
|
||||||
|
// Post had attachments but they were all removed. We cannot use the filesToKeep option because it doesn't allow
|
||||||
|
// removing all files. In this case we'll just keep 1 file and remove it later.
|
||||||
|
filesToKeep = [previousAttachments[0]];
|
||||||
|
removedFiles = [{
|
||||||
|
filename: previousAttachments[0].filename ?? '',
|
||||||
|
filepath: previousAttachments[0].filepath ?? '',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const preparePostData = await AddonModForum.preparePostForEdition(postId, 'attachment', { filesToKeep });
|
||||||
|
|
||||||
|
if (removedFiles?.length) {
|
||||||
|
await CoreFileUploader.deleteDraftFiles(preparePostData.draftitemid, removedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
await CoreFileUploader.uploadFiles(preparePostData.draftitemid, files);
|
||||||
|
|
||||||
|
return preparePostData.draftitemid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload attachments for a reply that isn't an online post being edited.
|
||||||
|
*
|
||||||
|
* @param replyingTo Replying to post ID.
|
||||||
|
* @returns Draft area id (if any attachment was uploaded) and whether data should be saved offline.
|
||||||
|
*/
|
||||||
|
async uploadAttachmentsForReply(
|
||||||
|
replyingTo: number,
|
||||||
|
): Promise<{ attachments: CoreFileUploaderStoreFilesResult | number | undefined; saveOffline: boolean }> {
|
||||||
|
const files = this.formData.files || [];
|
||||||
|
if (!files.length) {
|
||||||
|
return { attachments: undefined, saveOffline: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(
|
||||||
|
this.forum.id,
|
||||||
|
replyingTo,
|
||||||
|
files,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
return { attachments, saveOffline: false };
|
||||||
|
} catch (error) {
|
||||||
|
// Cannot upload them in online, save them in offline.
|
||||||
|
if (!this.forum.id || CoreUtils.isWebServiceError(error)) {
|
||||||
|
// Cannot store them in offline. Reject.
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachments = await AddonModForumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, true);
|
||||||
|
|
||||||
|
return { attachments, saveOffline: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel reply.
|
* Cancel reply.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -26,7 +26,13 @@ import { CoreGroups } from '@services/groups';
|
||||||
import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites';
|
import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites';
|
||||||
import { CoreUrl } from '@singletons/url';
|
import { CoreUrl } from '@singletons/url';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning, CoreWSStoredFile } from '@services/ws';
|
import {
|
||||||
|
CoreStatusWithWarningsWSResponse,
|
||||||
|
CoreWSExternalFile,
|
||||||
|
CoreWSExternalWarning,
|
||||||
|
CoreWSFile,
|
||||||
|
CoreWSStoredFile,
|
||||||
|
} from '@services/ws';
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { AddonModForumOffline, AddonModForumOfflineDiscussion, AddonModForumReplyOptions } from './forum-offline';
|
import { AddonModForumOffline, AddonModForumOfflineDiscussion, AddonModForumReplyOptions } from './forum-offline';
|
||||||
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
|
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
|
||||||
|
@ -606,7 +612,11 @@ export class AddonModForumProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
const site = await CoreSites.getSite(options.siteId);
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
|
|
||||||
const isGetDiscussionPostsAvailable = this.isGetDiscussionPostsAvailable(site);
|
const isGetDiscussionPostsAvailable = this.isGetDiscussionPostsAvailable(site);
|
||||||
|
if (isGetDiscussionPostsAvailable && site.isVersionGreaterEqualThan('4.0')) {
|
||||||
|
(params as AddonModForumGetDiscussionPostsWSParams).includeinlineattachments = true;
|
||||||
|
}
|
||||||
|
|
||||||
const response = isGetDiscussionPostsAvailable
|
const response = isGetDiscussionPostsAvailable
|
||||||
? await site.read<AddonModForumGetDiscussionPostsWSResponse>('mod_forum_get_discussion_posts', params, preSets)
|
? await site.read<AddonModForumGetDiscussionPostsWSResponse>('mod_forum_get_discussion_posts', params, preSets)
|
||||||
|
@ -1264,6 +1274,35 @@ export class AddonModForumProvider {
|
||||||
CoreUser.storeUsers(CoreUtils.objectToArray(users));
|
CoreUser.storeUsers(CoreUtils.objectToArray(users));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare post for edition.
|
||||||
|
*
|
||||||
|
* @param postId Post ID.
|
||||||
|
* @param area Area to prepare.
|
||||||
|
* @param options Other options.
|
||||||
|
* @returns Data of prepared area.
|
||||||
|
*/
|
||||||
|
async preparePostForEdition(
|
||||||
|
postId: number,
|
||||||
|
area: 'attachment'|'post',
|
||||||
|
options: AddonModForumPreparePostOptions = {},
|
||||||
|
): Promise<AddonModForumPrepareDraftAreaForPostWSResponse> {
|
||||||
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
|
|
||||||
|
const params: AddonModForumPrepareDraftAreaForPostWSParams = {
|
||||||
|
postid: postId,
|
||||||
|
area: area,
|
||||||
|
};
|
||||||
|
if (options.filesToKeep?.length) {
|
||||||
|
params.filestokeep = options.filesToKeep.map(file => ({
|
||||||
|
filename: file.filename ?? '',
|
||||||
|
filepath: file.filepath ?? '',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await site.write('mod_forum_prepare_draft_area_for_post', params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a certain post.
|
* Update a certain post.
|
||||||
*
|
*
|
||||||
|
@ -1903,6 +1942,7 @@ export type AddonModForumGetDiscussionPostsWSParams = {
|
||||||
discussionid: number; // The ID of the discussion from which to fetch posts.
|
discussionid: number; // The ID of the discussion from which to fetch posts.
|
||||||
sortby?: string; // Sort by this element: id, created or modified.
|
sortby?: string; // Sort by this element: id, created or modified.
|
||||||
sortdirection?: string; // Sort direction: ASC or DESC.
|
sortdirection?: string; // Sort direction: ASC or DESC.
|
||||||
|
includeinlineattachments?: boolean; // @since 4.0. Whether inline attachments should be included or not.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2086,6 +2126,46 @@ export type AddonModForumUpdateDiscussionPostWSParams = {
|
||||||
*/
|
*/
|
||||||
export type AddonModForumUpdateDiscussionPostWSResponse = CoreStatusWithWarningsWSResponse;
|
export type AddonModForumUpdateDiscussionPostWSResponse = CoreStatusWithWarningsWSResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_forum_prepare_draft_area_for_post WS.
|
||||||
|
*/
|
||||||
|
type AddonModForumPrepareDraftAreaForPostWSParams = {
|
||||||
|
postid: number; // Post to prepare the draft area for.
|
||||||
|
area: string; // Area to prepare: attachment or post.
|
||||||
|
draftitemid?: number; // The draft item id to use. 0 to generate one.
|
||||||
|
filestokeep?: AddonModForumFileToKeep[]; // Only keep these files in the draft file area. Empty for keeping all.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data to pass to mod_forum_prepare_draft_area_for_post to keep a file in the area.
|
||||||
|
*/
|
||||||
|
type AddonModForumFileToKeep = {
|
||||||
|
filename: string; // File name.
|
||||||
|
filepath: string; // File path.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by mod_forum_prepare_draft_area_for_post WS.
|
||||||
|
*/
|
||||||
|
export type AddonModForumPrepareDraftAreaForPostWSResponse = {
|
||||||
|
draftitemid: number; // Draft item id for the file area.
|
||||||
|
files?: CoreWSExternalFile[];
|
||||||
|
areaoptions: { // Draft file area options.
|
||||||
|
name: string; // Name of option.
|
||||||
|
value: string; // Value of option.
|
||||||
|
}[];
|
||||||
|
messagetext: string; // Message text with URLs rewritten.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to pass to preparePostForEdition.
|
||||||
|
*/
|
||||||
|
export type AddonModForumPreparePostOptions = {
|
||||||
|
filesToKeep?: CoreWSFile[]; // Only keep these files in the draft file area. Undefined or empty array for keeping all.
|
||||||
|
siteId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data passed to NEW_DISCUSSION_EVENT event.
|
* Data passed to NEW_DISCUSSION_EVENT event.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -286,9 +286,9 @@ abstract class AddonModGlossaryFormHandler {
|
||||||
* Upload attachments online.
|
* Upload attachments online.
|
||||||
*
|
*
|
||||||
* @param glossary Glossary.
|
* @param glossary Glossary.
|
||||||
* @returns Uploaded attachments item id.
|
* @returns Uploaded attachments item id, undefined if nothing to upload or change.
|
||||||
*/
|
*/
|
||||||
protected async uploadAttachments(glossary: AddonModGlossaryGlossary): Promise<number> {
|
protected async uploadAttachments(glossary: AddonModGlossaryGlossary): Promise<number | undefined> {
|
||||||
const data = this.page.data;
|
const data = this.page.data;
|
||||||
const itemId = await CoreFileUploader.uploadOrReuploadFiles(
|
const itemId = await CoreFileUploader.uploadOrReuploadFiles(
|
||||||
data.attachments,
|
data.attachments,
|
||||||
|
@ -643,10 +643,7 @@ class AddonModGlossaryOnlineFormHandler extends AddonModGlossaryFormHandler {
|
||||||
data.fullmatch = this.entry.fullmatch;
|
data.fullmatch = this.entry.fullmatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat offline attachments if any.
|
data.attachments = (this.entry.attachments ?? []).slice();
|
||||||
if (this.entry.attachments) {
|
|
||||||
data.attachments = this.entry.attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.page.originalData = {
|
this.page.originalData = {
|
||||||
concept: data.concept,
|
concept: data.concept,
|
||||||
|
@ -677,11 +674,7 @@ class AddonModGlossaryOnlineFormHandler extends AddonModGlossaryFormHandler {
|
||||||
const definition = CoreText.formatHtmlLines(data.definition);
|
const definition = CoreText.formatHtmlLines(data.definition);
|
||||||
|
|
||||||
// Upload attachments, if any.
|
// Upload attachments, if any.
|
||||||
let attachmentsId: number | undefined = undefined;
|
const attachmentsId = await this.uploadAttachments();
|
||||||
|
|
||||||
if (data.attachments.length) {
|
|
||||||
attachmentsId = await this.uploadAttachments(glossary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save entry data.
|
// Save entry data.
|
||||||
await AddonModGlossary.updateEntry(glossary.id, this.entry.id, data.concept, definition, options, attachmentsId);
|
await AddonModGlossary.updateEntry(glossary.id, this.entry.id, data.concept, definition, options, attachmentsId);
|
||||||
|
@ -694,6 +687,31 @@ class AddonModGlossaryOnlineFormHandler extends AddonModGlossaryFormHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload attachments online.
|
||||||
|
*
|
||||||
|
* @returns Uploaded attachments item id, undefined if nothing to upload or change.
|
||||||
|
*/
|
||||||
|
protected async uploadAttachments(): Promise<number | undefined> {
|
||||||
|
const data = this.page.data;
|
||||||
|
|
||||||
|
if (!CoreFileUploader.areFileListDifferent(data.attachments, this.entry.attachments ?? [])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { attachmentsid: attachmentsId } = await AddonModGlossary.prepareEntryForEdition(this.entry.id);
|
||||||
|
|
||||||
|
const removedFiles = CoreFileUploader.getFilesToDelete(this.entry.attachments ?? [], data.attachments);
|
||||||
|
|
||||||
|
if (removedFiles.length) {
|
||||||
|
await CoreFileUploader.deleteDraftFiles(attachmentsId, removedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
await CoreFileUploader.uploadFiles(attachmentsId, data.attachments);
|
||||||
|
|
||||||
|
return attachmentsId;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -626,6 +626,7 @@ export class AddonModGlossaryProvider {
|
||||||
*
|
*
|
||||||
* @param siteId Site id.
|
* @param siteId Site id.
|
||||||
* @returns Whether the site can update entries.
|
* @returns Whether the site can update entries.
|
||||||
|
* @since 3.10
|
||||||
*/
|
*/
|
||||||
async canUpdateEntries(siteId?: string): Promise<boolean> {
|
async canUpdateEntries(siteId?: string): Promise<boolean> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
@ -1097,6 +1098,26 @@ export class AddonModGlossaryProvider {
|
||||||
await site.getDb().insertRecord(ENTRIES_TABLE_NAME, entry);
|
await site.getDb().insertRecord(ENTRIES_TABLE_NAME, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare entry for edition.
|
||||||
|
*
|
||||||
|
* @param entryId Entry ID.
|
||||||
|
* @param siteId Site ID.
|
||||||
|
* @returns Data of prepared area.
|
||||||
|
*/
|
||||||
|
async prepareEntryForEdition(
|
||||||
|
entryId: number,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<AddonModGlossaryPrepareEntryForEditionWSResponse> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: AddonModGlossaryPrepareEntryForEditionWSParams = {
|
||||||
|
entryid: entryId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await site.write('mod_glossary_prepare_entry_for_edition', params);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonModGlossary = makeSingleton(AddonModGlossaryProvider);
|
export const AddonModGlossary = makeSingleton(AddonModGlossaryProvider);
|
||||||
|
@ -1435,6 +1456,31 @@ export type AddonModGlossaryViewEntryWSParams = {
|
||||||
id: number; // Glossary entry ID.
|
id: number; // Glossary entry ID.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_glossary_prepare_entry_for_edition WS.
|
||||||
|
*/
|
||||||
|
type AddonModGlossaryPrepareEntryForEditionWSParams = {
|
||||||
|
entryid: number; // Glossary entry id to update.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by mod_glossary_prepare_entry_for_edition WS.
|
||||||
|
*/
|
||||||
|
export type AddonModGlossaryPrepareEntryForEditionWSResponse = {
|
||||||
|
inlineattachmentsid: number; // Draft item id for the text editor.
|
||||||
|
attachmentsid: number; // Draft item id for the file manager.
|
||||||
|
areas: { // File areas including options.
|
||||||
|
area: string; // File area name.
|
||||||
|
options: { // Draft file area options.
|
||||||
|
name: string; // Name of option.
|
||||||
|
value: string; // Value of option.
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
aliases: string[];
|
||||||
|
categories: number[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to pass to add entry.
|
* Options to pass to add entry.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue