diff --git a/notes-service/src/main/java/io/meeds/notes/model/NoteFeaturedImage.java b/notes-service/src/main/java/io/meeds/notes/model/NoteFeaturedImage.java new file mode 100644 index 0000000000..c1cffc8578 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/model/NoteFeaturedImage.java @@ -0,0 +1,78 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +package io.meeds.notes.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.InputStream; +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NoteFeaturedImage implements Serializable { + + private Long id; + + private String fileName; + + private String mimeType; + + private long fileSize; + + private Long lastUpdated; + + private String uploadId; + + private String altText; + + private transient InputStream fileInputStream; + + private boolean toDelete; + + public NoteFeaturedImage(Long id, + String fileName, + String mimeType, + long fileSize, + Long lastUpdated, + InputStream fileInputStream) { + this.id = id; + this.fileName = fileName; + this.mimeType = mimeType; + this.fileSize = fileSize; + this.lastUpdated = lastUpdated; + this.fileInputStream = fileInputStream; + } + + public NoteFeaturedImage(Long id, + String mimeType, + String uploadId, + String altText, + Long lastUpdated, + boolean toDelete) { + this.id = id; + this.mimeType = mimeType; + this.uploadId = uploadId; + this.altText = altText; + this.lastUpdated = lastUpdated; + this.toDelete = toDelete; + } +} diff --git a/notes-service/src/main/java/io/meeds/notes/model/NoteMetadataObject.java b/notes-service/src/main/java/io/meeds/notes/model/NoteMetadataObject.java new file mode 100644 index 0000000000..e4f767eedd --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/model/NoteMetadataObject.java @@ -0,0 +1,29 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * + */ +package io.meeds.notes.model; + +import org.exoplatform.social.metadata.model.MetadataObject; + +public class NoteMetadataObject extends MetadataObject { + + public NoteMetadataObject(String type, String id, String parentId, long spaceId) { + super(type, id, parentId, spaceId); + } +} diff --git a/notes-service/src/main/java/io/meeds/notes/model/NotePageProperties.java b/notes-service/src/main/java/io/meeds/notes/model/NotePageProperties.java new file mode 100644 index 0000000000..4e42b68c46 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/model/NotePageProperties.java @@ -0,0 +1,41 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * + */ + +package io.meeds.notes.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NotePageProperties implements Serializable { + + private long noteId; + + private String summary; + + private NoteFeaturedImage featuredImage; + + private boolean isDraft; +} diff --git a/notes-service/src/main/java/io/meeds/notes/notifications/plugin/MentionInNoteNotificationPlugin.java b/notes-service/src/main/java/io/meeds/notes/notifications/plugin/MentionInNoteNotificationPlugin.java new file mode 100644 index 0000000000..ed4b2dc6cd --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/notifications/plugin/MentionInNoteNotificationPlugin.java @@ -0,0 +1,115 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.notifications.plugin; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.model.ArgumentLiteral; +import org.exoplatform.commons.api.notification.model.NotificationInfo; +import org.exoplatform.commons.api.notification.plugin.BaseNotificationPlugin; +import org.exoplatform.commons.utils.CommonsUtils; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.organization.OrganizationService; +import org.exoplatform.services.organization.User; +import org.exoplatform.services.organization.UserHandler; +import org.exoplatform.social.notification.Utils; + +public class MentionInNoteNotificationPlugin extends BaseNotificationPlugin { + + public final static String ID = "MentionInNoteNotificationPlugin"; + + private static final Log LOG = ExoLogger.getLogger(MentionInNoteNotificationPlugin.class); + + public static final ArgumentLiteral CURRENT_USER = new ArgumentLiteral<>(String.class, "CURRENT_USER"); + + public static final ArgumentLiteral NOTE_AUTHOR = new ArgumentLiteral<>(String.class, "NOTE_AUTHOR"); + + public static final ArgumentLiteral AUTHOR_AVATAR_URL = new ArgumentLiteral<>(String.class, "AUTHOR_AVATAR_URL"); + + public static final ArgumentLiteral AUTHOR_PROFILE_URL = new ArgumentLiteral<>(String.class, "AUTHOR_PROFILE_URL"); + + public static final ArgumentLiteral ACTIVITY_LINK = new ArgumentLiteral<>(String.class, "ACTIVITY_LINK"); + + public static final ArgumentLiteral MENTIONED_IDS = new ArgumentLiteral(Set.class, "MENTIONED_IDS"); + + public static final ArgumentLiteral NOTE_URL = new ArgumentLiteral<>(String.class, "NOTE_URL"); + + public static final ArgumentLiteral NOTE_TITLE = new ArgumentLiteral<>(String.class, "NOTE_TITLE"); + + public static final ArgumentLiteral SPACE_ID = new ArgumentLiteral<>(String.class, "SPACE_ID"); + + public MentionInNoteNotificationPlugin(InitParams initParams) { + super(initParams); + } + + @Override + public String getId() { + return ID; + } + + @Override + public boolean isValid(NotificationContext ctx) { + return true; + } + + @Override + public NotificationInfo makeNotification(NotificationContext ctx) { + String currentUserName = ctx.value(CURRENT_USER); + String currentUserFullName = currentUserName; + try { + currentUserFullName = getUserFullName(currentUserName); + } catch (Exception e) { + LOG.error("An error occured when trying to retreive a user with username " + currentUserName + " " + e.getMessage(), e); + } + List mentionedIds = new ArrayList<>(ctx.value(MENTIONED_IDS)); + String spaceId = ctx.value(SPACE_ID); + Set receivers = new HashSet<>(); + String[] mentionnedIdArray = new String[mentionedIds.size()]; + Utils.sendToMentioners(receivers, mentionedIds.toArray(mentionnedIdArray), currentUserName, spaceId); + return NotificationInfo.instance() + .setFrom(currentUserName) + .to(new ArrayList<>(receivers)) + .key(getKey()) + .with(NOTE_URL.getKey(), ctx.value(NOTE_URL)) + .with(NOTE_TITLE.getKey(), ctx.value(NOTE_TITLE)) + .with(NOTE_AUTHOR.getKey(), ctx.value(NOTE_AUTHOR)) + .with(AUTHOR_AVATAR_URL.getKey(), ctx.value(AUTHOR_AVATAR_URL)) + .with(AUTHOR_PROFILE_URL.getKey(), ctx.value(AUTHOR_PROFILE_URL)) + .with(ACTIVITY_LINK.getKey(), ctx.value(ACTIVITY_LINK)) + .with(MENTIONED_IDS.getKey(), String.valueOf(mentionedIds)) + .with(CURRENT_USER.getKey(), currentUserFullName) + .end(); + } + + private String getUserFullName(String userName) throws Exception { + OrganizationService organizationService = CommonsUtils.getService(OrganizationService.class); + UserHandler userHandler = organizationService.getUserHandler(); + User user = userHandler.findUserByName(userName); + if (user == null) { + throw new Exception("An error occured when trying to retreive a user with username " + userName); + } + return user.getFullName(); + } +} diff --git a/notes-service/src/main/java/io/meeds/notes/notifications/provider/MailTemplateProvider.java b/notes-service/src/main/java/io/meeds/notes/notifications/provider/MailTemplateProvider.java new file mode 100644 index 0000000000..53a8bd7335 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/notifications/provider/MailTemplateProvider.java @@ -0,0 +1,168 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.notifications.provider; + +import java.io.IOException; +import java.io.Writer; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin; +import org.exoplatform.commons.utils.CommonsUtils; +import org.gatein.common.text.EntityEncoder; + +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.annotation.TemplateConfig; +import org.exoplatform.commons.api.notification.annotation.TemplateConfigs; +import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder; +import org.exoplatform.commons.api.notification.channel.template.TemplateProvider; +import org.exoplatform.commons.api.notification.model.MessageInfo; +import org.exoplatform.commons.api.notification.model.NotificationInfo; +import org.exoplatform.commons.api.notification.model.PluginKey; +import org.exoplatform.commons.api.notification.service.template.TemplateContext; +import org.exoplatform.commons.notification.template.DigestTemplate; +import org.exoplatform.commons.notification.template.TemplateUtils; +import org.exoplatform.commons.utils.HTMLEntityEncoder; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.social.core.identity.model.Identity; +import org.exoplatform.social.core.identity.model.Profile; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; +import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.notification.LinkProviderUtils; +import org.exoplatform.webui.utils.TimeConvertUtils; + +@TemplateConfigs(templates = { + @TemplateConfig(pluginId = MentionInNoteNotificationPlugin.ID, template = "war:/notification/templates/mail/MentionInNoteNotificationPlugin.gtmpl") }) +public class MailTemplateProvider extends TemplateProvider { + protected static Log log = ExoLogger.getLogger(MailTemplateProvider.class); + + private final IdentityManager identityManager; + + public MailTemplateProvider(InitParams initParams, IdentityManager identityManager) { + super(initParams); + this.templateBuilders.put(PluginKey.key(MentionInNoteNotificationPlugin.ID), new TemplateBuilder()); + this.identityManager = identityManager; + } + + public class TemplateBuilder extends AbstractTemplateBuilder { + @Override + public MessageInfo makeMessage(NotificationContext ctx) { + NotificationInfo notification = ctx.getNotificationInfo(); + String pluginId = notification.getKey().getId(); + + String language = getLanguage(notification); + String activityLink = notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.ACTIVITY_LINK.getKey()); + String authorProfileUrl = notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.AUTHOR_PROFILE_URL.getKey()); + String currentDomain = CommonsUtils.getCurrentDomain(); + activityLink = currentDomain + activityLink; + authorProfileUrl = currentDomain + authorProfileUrl; + TemplateContext templateContext = TemplateContext.newChannelInstance(getChannelKey(), pluginId, language); + + HTMLEntityEncoder encoder = HTMLEntityEncoder.getInstance(); + templateContext.put(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey(), + encoder.encode(notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey()))); + templateContext.put(MentionInNoteNotificationPlugin.NOTE_AUTHOR.getKey(), + encoder.encode(notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.NOTE_AUTHOR.getKey()))); + templateContext.put(MentionInNoteNotificationPlugin.CURRENT_USER.getKey(), + notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.CURRENT_USER.getKey())); + templateContext.put(MentionInNoteNotificationPlugin.AUTHOR_AVATAR_URL.getKey(), + encoder.encode(notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.AUTHOR_AVATAR_URL.getKey()))); + templateContext.put(MentionInNoteNotificationPlugin.AUTHOR_PROFILE_URL.getKey(), + encoder.encode(authorProfileUrl)); + templateContext.put(MentionInNoteNotificationPlugin.ACTIVITY_LINK.getKey(), + encoder.encode(activityLink)); + + templateContext.put("READ", Boolean.valueOf(notification.isRead()) ? "read" : "unread"); + templateContext.put("NOTIFICATION_ID", notification.getId()); + Calendar lastModified = Calendar.getInstance(); + lastModified.setTimeInMillis(notification.getLastModifiedDate()); + templateContext.put("LAST_UPDATED_TIME", + TimeConvertUtils.convertXTimeAgoByTimeServer(lastModified.getTime(), + "EE, dd yyyy", + new Locale(language), + TimeConvertUtils.YEAR)); + // Receiver + Identity receiver = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, notification.getTo()); + if (receiver == null || receiver.getRemoteId().equals(notification.getFrom())) { + return null; + } + templateContext.put("FIRST_NAME", encoder.encode(receiver.getProfile().getProperty(Profile.FIRST_NAME).toString())); + // Footer + templateContext.put("FOOTER_LINK", LinkProviderUtils.getRedirectUrl("notification_settings", receiver.getRemoteId())); + templateContext.put("COMPANY_LINK", LinkProviderUtils.getBaseUrl()); + String subject = TemplateUtils.processSubject(templateContext); + String body = TemplateUtils.processGroovy(templateContext); + // binding the exception throws by processing template + ctx.setException(templateContext.getException()); + MessageInfo messageInfo = new MessageInfo(); + return messageInfo.subject(subject).body(body).end(); + } + + @Override + protected boolean makeDigest(NotificationContext ctx, Writer writer) { + EntityEncoder encoder = HTMLEntityEncoder.getInstance(); + List notifications = ctx.getNotificationInfos(); + NotificationInfo notificationInfo = notifications.get(0); + try { + String pluginId = notificationInfo.getKey().getId(); + if (pluginId.equals(MentionInNoteNotificationPlugin.ID)) { + String mentionedIds = notificationInfo.getValueOwnerParameter(MentionInNoteNotificationPlugin.MENTIONED_IDS.getKey()); + String ids = mentionedIds.substring(1, mentionedIds.length() - 1); + List mentionedList = Stream.of(ids.split(",")).map(String::trim).collect(Collectors.toList()); + if (!mentionedList.contains(notificationInfo.getTo())) { + return false; + } + } + String language = getLanguage(notificationInfo); + TemplateContext templateContext = new TemplateContext(pluginId, language); + // + Identity receiver = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, notificationInfo.getTo()); + templateContext.put("FIRST_NAME", encoder.encode(receiver.getProfile().getProperty(Profile.FIRST_NAME).toString())); + templateContext.put("FOOTER_LINK", LinkProviderUtils.getRedirectUrl("notification_settings", receiver.getRemoteId())); + + writer.append(buildDigestMsg(notifications, templateContext)); + } catch (IOException e) { + ctx.setException(e); + return false; + } + return true; + } + + protected String buildDigestMsg(List notifications, TemplateContext templateContext) { + StringBuilder sb = new StringBuilder(); + for (NotificationInfo notification : notifications) { + templateContext.put(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey(), + notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey())); + templateContext.put("USER", notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.NOTE_AUTHOR.getKey())); + templateContext.digestType(DigestTemplate.ElementType.DIGEST_ONE.getValue()); + + sb.append("
  • "); + String digester = TemplateUtils.processDigest(templateContext); + sb.append(digester); + sb.append("
  • "); + } + return sb.toString(); + } + } +} diff --git a/notes-service/src/main/java/io/meeds/notes/notifications/provider/PushTemplateProvider.java b/notes-service/src/main/java/io/meeds/notes/notifications/provider/PushTemplateProvider.java new file mode 100644 index 0000000000..aa2a24c141 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/notifications/provider/PushTemplateProvider.java @@ -0,0 +1,89 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.notifications.provider; + +import java.io.Writer; + +import io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin; + +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.annotation.TemplateConfig; +import org.exoplatform.commons.api.notification.annotation.TemplateConfigs; +import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder; +import org.exoplatform.commons.api.notification.channel.template.TemplateProvider; +import org.exoplatform.commons.api.notification.model.MessageInfo; +import org.exoplatform.commons.api.notification.model.NotificationInfo; +import org.exoplatform.commons.api.notification.model.PluginKey; +import org.exoplatform.commons.api.notification.service.template.TemplateContext; +import org.exoplatform.commons.notification.template.TemplateUtils; +import org.exoplatform.commons.utils.HTMLEntityEncoder; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.social.core.identity.model.Identity; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; +import org.exoplatform.social.core.manager.IdentityManager; + +@TemplateConfigs(templates = { + @TemplateConfig(pluginId = MentionInNoteNotificationPlugin.ID, template = "war:/notification/templates/push/MentionInNoteNotificationPlugin.gtmpl") }) +public class PushTemplateProvider extends TemplateProvider { + protected static Log log = ExoLogger.getLogger(MailTemplateProvider.class); + + private final IdentityManager identityManager; + + public PushTemplateProvider(InitParams initParams, IdentityManager identityManager) { + super(initParams); + this.templateBuilders.put(PluginKey.key(MentionInNoteNotificationPlugin.ID), new TemplateBuilder()); + this.identityManager = identityManager; + } + + protected class TemplateBuilder extends AbstractTemplateBuilder { + @Override + protected MessageInfo makeMessage(NotificationContext ctx) { + NotificationInfo notification = ctx.getNotificationInfo(); + String pluginId = notification.getKey().getId(); + + String language = getLanguage(notification); + TemplateContext templateContext = TemplateContext.newChannelInstance(getChannelKey(), pluginId, language); + + HTMLEntityEncoder encoder = HTMLEntityEncoder.getInstance(); + templateContext.put(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey(), + encoder.encode(notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.NOTE_TITLE.getKey()))); + templateContext.put(MentionInNoteNotificationPlugin.CURRENT_USER.getKey(), + encoder.encode(notification.getValueOwnerParameter(MentionInNoteNotificationPlugin.CURRENT_USER.getKey()))); + // Receiver + Identity receiver = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, notification.getTo()); + if (receiver == null || receiver.getRemoteId().equals(notification.getFrom())) { + return null; + } + String subject = TemplateUtils.processSubject(templateContext); + String body = TemplateUtils.processGroovy(templateContext); + // binding the exception throws by processing template + ctx.setException(templateContext.getException()); + MessageInfo messageInfo = new MessageInfo(); + return messageInfo.subject(subject).body(body).end(); + } + + @Override + protected boolean makeDigest(NotificationContext ctx, Writer writer) { + return false; + } + + } +} diff --git a/notes-service/src/main/java/io/meeds/notes/rest/model/DraftPageEntity.java b/notes-service/src/main/java/io/meeds/notes/rest/model/DraftPageEntity.java new file mode 100644 index 0000000000..4016560289 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/rest/model/DraftPageEntity.java @@ -0,0 +1,20 @@ +package io.meeds.notes.rest.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DraftPageEntity extends PageEntity implements Serializable { + + private String targetPageId; + + private boolean newPage; + +} diff --git a/notes-service/src/main/java/io/meeds/notes/rest/model/FeaturedImageEntity.java b/notes-service/src/main/java/io/meeds/notes/rest/model/FeaturedImageEntity.java new file mode 100644 index 0000000000..25c09c252d --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/rest/model/FeaturedImageEntity.java @@ -0,0 +1,43 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.rest.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FeaturedImageEntity implements Serializable { + + private Long id; + + private String mimeType; + + private String uploadId; + + private String altText; + + private Long lastUpdated; + + private boolean toDelete; +} diff --git a/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java b/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java new file mode 100644 index 0000000000..1085dc6ca7 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java @@ -0,0 +1,47 @@ +package io.meeds.notes.rest.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PageEntity implements Serializable { + + private String id; + + private String name; + + private String title; + + private String content; + + private Long pageParentId; + + private String parentPageName; + + private String parentPageId; + + private String wikiType; + + private String wikiOwner; + + private String owner; + + private String author; + + private boolean toBePublished; + + private String url; + + private String syntax; + + private String appName; + + private String lang; + + private PagePropertiesEntity properties; +} diff --git a/notes-service/src/main/java/io/meeds/notes/rest/model/PagePropertiesEntity.java b/notes-service/src/main/java/io/meeds/notes/rest/model/PagePropertiesEntity.java new file mode 100644 index 0000000000..36a491307b --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/rest/model/PagePropertiesEntity.java @@ -0,0 +1,39 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.rest.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PagePropertiesEntity implements Serializable { + + private long noteId; + + private String summary; + + private FeaturedImageEntity featuredImage; + + private boolean isDraft; +} diff --git a/notes-service/src/main/java/io/meeds/notes/rest/utils/EntityBuilder.java b/notes-service/src/main/java/io/meeds/notes/rest/utils/EntityBuilder.java new file mode 100644 index 0000000000..c382f8e012 --- /dev/null +++ b/notes-service/src/main/java/io/meeds/notes/rest/utils/EntityBuilder.java @@ -0,0 +1,107 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * + */ +package io.meeds.notes.rest.utils; + +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NotePageProperties; +import io.meeds.notes.rest.model.DraftPageEntity; +import io.meeds.notes.rest.model.FeaturedImageEntity; +import io.meeds.notes.rest.model.PageEntity; +import io.meeds.notes.rest.model.PagePropertiesEntity; +import org.exoplatform.wiki.model.DraftPage; +import org.exoplatform.wiki.model.Page; + +public class EntityBuilder { + + private EntityBuilder() { // NOSONAR + } + + public static NotePageProperties toNotePageProperties(PagePropertiesEntity pagePropertiesEntity) { + if (pagePropertiesEntity == null) { + return null; + } + return new NotePageProperties(pagePropertiesEntity.getNoteId(), + pagePropertiesEntity.getSummary(), + toNoteFeaturedImage(pagePropertiesEntity.getFeaturedImage()), + pagePropertiesEntity.isDraft()); + } + + public static NoteFeaturedImage toNoteFeaturedImage(FeaturedImageEntity featuredImageEntity) { + if (featuredImageEntity == null) { + return null; + } + return new NoteFeaturedImage(featuredImageEntity.getId(), + featuredImageEntity.getMimeType(), + featuredImageEntity.getUploadId(), + featuredImageEntity.getAltText(), + featuredImageEntity.getLastUpdated(), + featuredImageEntity.isToDelete()); + } + + public static Page toPage(PageEntity pageEntity) { + if (pageEntity == null) { + return null; + } + Page page = new Page(); + page.setId(pageEntity.getId()); + page.setTitle(pageEntity.getTitle()); + page.setName(pageEntity.getName()); + page.setUrl(pageEntity.getUrl()); + page.setAuthor(pageEntity.getAuthor()); + page.setOwner(pageEntity.getOwner()); + page.setSyntax(pageEntity.getSyntax()); + page.setParentPageId(pageEntity.getParentPageId()); + page.setParentPageName(pageEntity.getParentPageName()); + page.setContent(pageEntity.getContent()); + page.setWikiType(pageEntity.getWikiType()); + page.setWikiOwner(pageEntity.getWikiOwner()); + page.setAppName(pageEntity.getAppName()); + page.setLang(pageEntity.getLang()); + page.setToBePublished(pageEntity.isToBePublished()); + page.setProperties(toNotePageProperties(pageEntity.getProperties())); + return page; + } + + public static DraftPage toDraftPage(DraftPageEntity draftPageEntity) { + if (draftPageEntity == null) { + return null; + } + DraftPage draftPage = new DraftPage(); + draftPage.setId(draftPageEntity.getId()); + draftPage.setTitle(draftPageEntity.getTitle()); + draftPage.setName(draftPageEntity.getName()); + draftPage.setUrl(draftPageEntity.getUrl()); + draftPage.setAuthor(draftPageEntity.getAuthor()); + draftPage.setOwner(draftPageEntity.getOwner()); + draftPage.setSyntax(draftPageEntity.getSyntax()); + draftPage.setParentPageId(draftPageEntity.getParentPageId()); + draftPage.setParentPageName(draftPageEntity.getParentPageName()); + draftPage.setContent(draftPageEntity.getContent()); + draftPage.setWikiType(draftPageEntity.getWikiType()); + draftPage.setWikiOwner(draftPageEntity.getWikiOwner()); + draftPage.setAppName(draftPageEntity.getAppName()); + draftPage.setToBePublished(draftPageEntity.isToBePublished()); + draftPage.setLang(draftPageEntity.getLang()); + draftPage.setTargetPageId(draftPageEntity.getTargetPageId()); + draftPage.setNewPage(draftPageEntity.isNewPage()); + draftPage.setProperties(toNotePageProperties(draftPageEntity.getProperties())); + return draftPage; + } +} diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/EntityConverter.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/EntityConverter.java index 1bee657786..2cecfe3850 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/EntityConverter.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/EntityConverter.java @@ -19,6 +19,11 @@ package org.exoplatform.wiki.jpa; +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NoteMetadataObject; +import io.meeds.notes.model.NotePageProperties; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.file.model.FileInfo; @@ -30,12 +35,18 @@ import org.exoplatform.social.core.identity.model.Identity; import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; +import org.exoplatform.social.metadata.MetadataService; +import org.exoplatform.social.metadata.model.MetadataKey; +import org.exoplatform.social.metadata.model.MetadataType; import org.exoplatform.wiki.WikiException; import org.exoplatform.wiki.jpa.dao.PageDAO; import org.exoplatform.wiki.jpa.dao.WikiDAO; import org.exoplatform.wiki.jpa.entity.*; import org.exoplatform.wiki.model.*; import org.exoplatform.wiki.service.IDType; +import org.exoplatform.wiki.service.impl.NoteServiceImpl; import org.exoplatform.wiki.utils.Utils; import java.io.ByteArrayInputStream; @@ -46,7 +57,17 @@ */ public class EntityConverter { - private static final Log LOG = ExoLogger.getLogger(EntityConverter.class); + private static final Log LOG = ExoLogger.getLogger(EntityConverter.class); + + private static SpaceService spaceService; + + private static MetadataService metadataService; + + public static final MetadataType NOTES_METADATA_TYPE = new MetadataType(1001, "notes"); + + public static final MetadataKey NOTES_METADATA_KEY = new MetadataKey(NOTES_METADATA_TYPE.getName(), + Utils.NOTES_METADATA_OBJECT_TYPE, + 0); public static Wiki convertWikiEntityToWiki(WikiEntity wikiEntity) { Wiki wiki = null; @@ -122,10 +143,64 @@ public static Page convertPageEntityToPage(PageEntity pageEntity) { page.setActivityId(pageEntity.getActivityId()); page.setDeleted(pageEntity.isDeleted()); page.setUrl(Utils.getPageUrl(page)); + buildNotePageMetadata(page, page.isDraftPage()); } return page; } + public static void buildNotePageMetadata(Page note, boolean isDraft) { + if (note == null) { + return; + } + Space space = getSpaceService().getSpaceByGroupId(note.getWikiOwner()); + if (space != null) { + String noteId = note.getId(); + if (note.getLang() != null) { + noteId = noteId + "-" + note.getLang(); + } + NoteMetadataObject noteMetadataObject = new NoteMetadataObject(isDraft ? "noteDraftPage" : "notePage", + noteId, + note.getParentPageId(), + Long.parseLong(space.getId())); + getMetadataService().getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY, noteMetadataObject) + .stream() + .findFirst() + .ifPresent(metadataItem -> { + if (!MapUtils.isEmpty(metadataItem.getProperties())) { + buildPageProperties(metadataItem.getProperties(), note); + } + }); + + } + } + + private static void buildPageProperties(Map properties, Page note) { + NotePageProperties notePageProperties = new NotePageProperties(); + NoteFeaturedImage noteFeaturedImage = new NoteFeaturedImage(); + notePageProperties.setNoteId(Long.parseLong(note.getId())); + notePageProperties.setSummary(properties.get(NoteServiceImpl.SUMMARY_PROP)); + noteFeaturedImage.setId(Long.valueOf(properties.getOrDefault(NoteServiceImpl.FEATURED_IMAGE_ID, "0"))); + noteFeaturedImage.setLastUpdated(Long.valueOf(properties.getOrDefault(NoteServiceImpl.FEATURED_IMAGE_UPDATED_DATE, "0"))); + noteFeaturedImage.setAltText(properties.get(NoteServiceImpl.FEATURED_IMAGE_ALT_TEXT)); + notePageProperties.setDraft(note.isDraftPage()); + notePageProperties.setFeaturedImage(noteFeaturedImage); + note.setProperties(notePageProperties); + } + + private static SpaceService getSpaceService() { + if (spaceService == null) { + spaceService = CommonsUtils.getService(SpaceService.class); + } + return spaceService; + } + + private static MetadataService getMetadataService() { + if (metadataService == null) { + metadataService = CommonsUtils.getService(MetadataService.class); + } + return metadataService; + } + public static List convertPermissionEntitiesToPermissionEntries(List permissionEntities, List filteredPermissionTypes) { List permissionEntries = new ArrayList<>(); @@ -365,6 +440,13 @@ public static DraftPageAttachmentEntity convertAttachmentToDraftPageAttachmentEn return attachmentEntity; } + public static List convertDraftPageEntitiesToDraftPages(List draftPageEntities) { + if (CollectionUtils.isEmpty(draftPageEntities)) { + return new ArrayList<>(); + } + return draftPageEntities.stream().map(EntityConverter::convertDraftPageEntityToDraftPage).toList(); + } + public static DraftPage convertDraftPageEntityToDraftPage(DraftPageEntity draftPageEntity) { DraftPage draftPage = null; if (draftPageEntity != null) { @@ -406,6 +488,7 @@ public static DraftPage convertDraftPageEntityToDraftPage(DraftPageEntity draftP draftPage.setWikiType(wiki.getType()); } } + buildNotePageMetadata(draftPage, true); } return draftPage; } @@ -435,6 +518,7 @@ public static DraftPageEntity convertDraftPageToDraftPageEntity(DraftPage draftP draftPageEntity.setParentPage(pageDAO.find(Long.valueOf(parentPageId))); } draftPageEntity.setTargetRevision(draftPage.getTargetPageRevision()); + buildNotePageMetadata(draftPage, true); } return draftPageEntity; } @@ -454,6 +538,8 @@ public static PageVersion convertPageVersionEntityToPageVersion(PageVersionEntit pageVersion.setOwner(pageVersionEntity.getAuthor()); pageVersion.setParent(convertPageEntityToPage(pageVersionEntity.getPage())); pageVersion.setLang(pageVersionEntity.getLang()); + pageVersion.setWikiOwner(pageVersionEntity.getPage().getWiki().getOwner()); + buildNotePageMetadata(pageVersion, pageVersion.isDraftPage()); } return pageVersion; } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java index c40b45c32a..d525a25c78 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java @@ -20,20 +20,6 @@ package org.exoplatform.wiki.jpa; -import static org.exoplatform.wiki.jpa.EntityConverter.convertAttachmentEntityToAttachment; -import static org.exoplatform.wiki.jpa.EntityConverter.convertAttachmentToDraftPageAttachmentEntity; -import static org.exoplatform.wiki.jpa.EntityConverter.convertAttachmentToPageAttachmentEntity; -import static org.exoplatform.wiki.jpa.EntityConverter.convertDraftPageEntityToDraftPage; -import static org.exoplatform.wiki.jpa.EntityConverter.convertDraftPageToDraftPageEntity; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPageEntityToPage; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPageToPageEntity; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPageVersionEntityToPageHistory; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPageVersionEntityToPageVersion; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPermissionEntitiesToPermissionEntries; -import static org.exoplatform.wiki.jpa.EntityConverter.convertPermissionEntriesToPermissionEntities; -import static org.exoplatform.wiki.jpa.EntityConverter.convertWikiEntityToWiki; -import static org.exoplatform.wiki.jpa.EntityConverter.convertWikiToWikiEntity; - import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; @@ -105,7 +91,9 @@ import org.exoplatform.wiki.utils.Utils; import org.exoplatform.wiki.utils.VersionNameComparatorDesc; -/** +import static org.exoplatform.wiki.jpa.EntityConverter.*; + + /** * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com * 9/8/15 */ @@ -382,15 +370,14 @@ private void deletePageEntity(PageEntity pageEntity) { @Override public void deleteDraftOfPage(Page page) throws WikiException { - List draftPages = draftPageDAO.findDraftPagesByTargetPage(Long.parseLong(page.getId())); - for (DraftPageEntity draftPage: draftPages) { - if(draftPage != null){ - deleteAttachmentsOfDraftPage(draftPage); - } - } - draftPageDAO.deleteDraftPagesByTargetPage(Long.valueOf(page.getId())); + draftPageDAO.deleteDraftPagesByTargetPage(Long.parseLong(page.getId())); } + @Override + public void deleteAttachmentsOfDraftPage(DraftPage draftPage) { + deleteAttachmentsOfDraftPage(convertDraftPageToDraftPageEntity(draftPage, pageDAO)); + } + @Override public void deleteDraftOfPage(Page page, String lang) throws WikiException { List draftPages = draftPageDAO.findDraftPagesByTargetPage(Long.parseLong(page.getId())); @@ -670,15 +657,21 @@ public Page getExsitedOrNewDraftPageById(String wikiType, String wikiOwner, Stri } @Override - public List getDraftsOfPage(Long pageId) { - List draftPages = new ArrayList<>(); - List draftPagesOfUser = draftPageDAO.findDraftPagesByTargetPage(pageId); - for (DraftPageEntity draft : draftPagesOfUser) { - draftPages.add(convertDraftPageEntityToDraftPage(draft)); + public DraftPage getDraftOfPageByLang(Page page, String lang) throws WikiException { + List draftPages = draftPageDAO.findDraftPagesByTargetPage(Long.parseLong(page.getId())); + for (DraftPageEntity draftPage : draftPages) { + if (draftPage != null && StringUtils.equals(draftPage.getLang(), lang)) { + return convertDraftPageEntityToDraftPage(draftPage); + } } + return null; + } - return draftPages; + @Override + public List getDraftsOfPage(Long pageId) { + return convertDraftPageEntitiesToDraftPages(draftPageDAO.findDraftPagesByTargetPage(pageId)); } + @Override public DraftPage getDraft(WikiPageParams wikiPageParams) throws WikiException { DraftPage latestDraft = null; @@ -1144,7 +1137,7 @@ public List getHistoryOfPage(Page page) throws WikiException { @Override @ExoTransactional - public void addPageVersion(Page page , String userName) throws WikiException { + public PageVersion addPageVersion(Page page , String userName) throws WikiException { if(page != null) { PageEntity pageEntity = fetchPageEntity(page); @@ -1154,7 +1147,6 @@ public void addPageVersion(Page page , String userName) throws WikiException { } PageVersionEntity pageVersionEntity = new PageVersionEntity(); - Long versionNumber = pageVersionDAO.getLastversionNumberOfPage(pageEntity.getId()); if(versionNumber == null) { versionNumber = 1L; @@ -1191,13 +1183,15 @@ public void addPageVersion(Page page , String userName) throws WikiException { pageEntity.setVersions(pageVersionEntities); pageDAO.update(pageEntity); + + return EntityConverter.convertPageVersionEntityToPageVersion(pageVersionEntity); } else { throw new WikiException("Cannot create version of a page null"); } } @Override - public void restoreVersionOfPage(String versionName, Page page) throws WikiException { + public PageVersion restoreVersionOfPage(String versionName, Page page) throws WikiException { if(page != null) { PageEntity pageEntity = fetchPageEntity(page); @@ -1211,6 +1205,7 @@ public void restoreVersionOfPage(String versionName, Page page) throws WikiExcep pageEntity.setContent(versionToRestore.getContent()); pageEntity.setUpdatedDate(Calendar.getInstance().getTime()); pageDAO.update(pageEntity); + return EntityConverter.convertPageVersionEntityToPageVersion(versionToRestore); } else { throw new WikiException("Cannot restore version " + versionName + " of a page " + page.getWikiType() + ":" + page.getWikiOwner() + ":" + page.getName() + " because version does not exist."); @@ -1329,7 +1324,7 @@ public void deleteWatcherOfPage(String username, Page page) throws WikiException } @ExoTransactional - public void deleteAttachmentsOfDraftPage(DraftPageEntity page) throws WikiException { + public void deleteAttachmentsOfDraftPage(DraftPageEntity page) { List attachmentsEntities = page.getAttachments(); if (attachmentsEntities != null) { for (int i = 0; i < attachmentsEntities.size(); i++) { @@ -1523,7 +1518,16 @@ public PageVersion getPublishedVersionByPageIdAndLang(Long pageId, String lang) if (pageId == null) { throw new IllegalArgumentException("targetPageId argument is null"); } - return convertPageVersionEntityToPageVersion(pageVersionDAO.findLatestVersionByPageIdAndLang(pageId, lang)); + PageVersion pageVersion = + convertPageVersionEntityToPageVersion(pageVersionDAO.findLatestVersionByPageIdAndLang(pageId, lang)); + if (pageVersion != null) { + Page page = pageVersion.getParent(); + page.setLang(lang); + EntityConverter.buildNotePageMetadata(page, false); + pageVersion.setProperties(page.getProperties()); + return pageVersion; + } + return null; } /** @@ -1568,4 +1572,12 @@ public void deleteVersionsByNoteIdAndLang(Long noteId, String lang) throws WikiE public void deleteOrphanDraftPagesByParentPage(long parentPageId) { draftPageDAO.deleteOrphanDraftPagesByParentPage(parentPageId); } + + /** + * {@inheritDoc} + */ + @Override + public PageVersion getPageVersionById(long versionId) { + return EntityConverter.convertPageVersionEntityToPageVersion(pageVersionDAO.find(versionId)); + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/model/DraftPage.java b/notes-service/src/main/java/org/exoplatform/wiki/model/DraftPage.java index 9aa99ea929..f85d830cb4 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/model/DraftPage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/model/DraftPage.java @@ -25,13 +25,13 @@ @Data public class DraftPage extends Page { - private String targetPageId; + private String targetPageId; - private String targetPageRevision; + private String targetPageRevision; - private boolean newPage; + private boolean newPage; - private DiffResult changes; + private DiffResult changes; @Override public boolean isDraftPage() { diff --git a/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java b/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java index 16d60123bd..415bf763a0 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.meeds.notes.model.NotePageProperties; +import io.meeds.notes.rest.model.PagePropertiesEntity; import org.exoplatform.social.metadata.model.MetadataItem; import org.exoplatform.wiki.service.BreadcrumbData; @@ -101,6 +103,8 @@ public class Page { private String lang; + private NotePageProperties properties; + public Page(String name) { this.name = name; } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/resolver/TitleResolver.java b/notes-service/src/main/java/org/exoplatform/wiki/resolver/TitleResolver.java index a9eec3f831..6e100aced0 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/resolver/TitleResolver.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/resolver/TitleResolver.java @@ -18,6 +18,8 @@ import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; public class TitleResolver { @@ -32,7 +34,7 @@ public static String getId(String title, boolean isEncoded) { if (isEncoded) { title = URLDecoder.decode(title, StandardCharsets.UTF_8); } - return title.replaceAll("[^a-zA-Z0-9_\\-]", "_"); + return title.replaceAll("[^a-zA-Z0-9_\\-]", "_") + "_" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date(System.currentTimeMillis())); } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java b/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java index 6da4bde05a..05dff8ae55 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/DataStorage.java @@ -71,6 +71,8 @@ public interface DataStorage { public Page getPageById(String id) throws WikiException; public DraftPage getDraftPageById(String id) throws WikiException; + + public DraftPage getDraftOfPageByLang(Page page, String lang) throws WikiException; public Page getParentPageOf(Page page) throws WikiException; @@ -197,9 +199,25 @@ public default List getAttachmentsOfPage(Page page, boolean loadCont public List getHistoryOfPage(Page page) throws WikiException; - public void addPageVersion(Page page, String userName) throws WikiException; + /** + * Creates a new page version + * + * @param page target note page + * @param userName current username + * @return {@link PageVersion} + * @throws WikiException + */ + PageVersion addPageVersion(Page page, String userName) throws WikiException; - public void restoreVersionOfPage(String versionName, Page page) throws WikiException; + /** + * Restore a note from given version + * + * @param versionName version name + * @param page target note page + * @return {@link PageVersion} + * @throws WikiException + */ + PageVersion restoreVersionOfPage(String versionName, Page page) throws WikiException; public Page updatePage(Page page) throws WikiException; @@ -220,6 +238,13 @@ public default List getAttachmentsOfPage(Page page, boolean loadCont */ public List getPagesOfWiki(String wikiType, String wikiOwner); + /** + * Delete attachments of target draft page + * + * @param draftPage target draft page + */ + void deleteAttachmentsOfDraftPage(DraftPage draftPage); + /** * Retrieves list of page versions by page id and lang * @@ -270,4 +295,12 @@ public default List getAttachmentsOfPage(Page page, boolean loadCont * @param parentPageId Note parent page id */ void deleteOrphanDraftPagesByParentPage(long parentPageId); + + /** + * Gets page version by its given id + * + * @param versionId page version id + * @return {@link PageVersion} + */ + PageVersion getPageVersionById(long versionId); } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java index 3c59998946..334514f22c 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/NoteService.java @@ -22,7 +22,10 @@ import java.io.IOException; import java.util.List; +import java.util.Map; +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NotePageProperties; import org.gatein.api.EntityNotFoundException; import org.exoplatform.commons.utils.PageList; @@ -361,9 +364,9 @@ List getBreadCrumb(String noteType, * * @param param Note location params. * @param lang draft lang. - * @throws WikiException if an error occured + * @throws Exception if an error occured */ - void removeDraftOfNote(WikiPageParams param, String lang) throws WikiException; + void removeDraftOfNote(WikiPageParams param, String lang) throws Exception; /** * Remove the Drafts of a note @@ -397,8 +400,9 @@ List getBreadCrumb(String noteType, * * @param draftId Technical Id of the draft page. * @throws WikiException if an error occured + * @throws Exception */ - void removeDraftById(String draftId) throws WikiException; + void removeDraftById(String draftId) throws Exception; /** * Gets all the Histories of the given note @@ -466,7 +470,8 @@ List getBreadCrumb(String noteType, */ Page updateNote(Page note, PageUpdateType type, Identity userIdentity) throws WikiException, IllegalAccessException, - EntityNotFoundException; + EntityNotFoundException, + Exception; /** * Update the given note. This does not automatically create a new version. If @@ -532,10 +537,11 @@ DraftPage updateDraftForExistPage(DraftPage draftNoteToUpdate, * * @param draftNoteToUpdate the draft note to be updated * @param currentTimeMillis + * @param userIdentityId user identity id * @return Updated draft * @throws WikiException */ - DraftPage updateDraftForNewPage(DraftPage draftNoteToUpdate, long currentTimeMillis) throws WikiException; + DraftPage updateDraftForNewPage(DraftPage draftNoteToUpdate, long currentTimeMillis, long userIdentityId) throws WikiException; /** * Creates a draft for an existing page @@ -560,10 +566,11 @@ DraftPage createDraftForExistPage(DraftPage draftNoteToSave, * * @param draftNoteToSave The draft note to be created * @param currentTimeMillis + * @param userIdentityId user identity id * @return Created draft * @throws WikiException */ - DraftPage createDraftForNewPage(DraftPage draftNoteToSave, long currentTimeMillis) throws WikiException; + DraftPage createDraftForNewPage(DraftPage draftNoteToSave, long currentTimeMillis, long userIdentityId) throws WikiException; /** * Return the list of children of the note to export @@ -618,7 +625,7 @@ DraftPage createDraftForExistPage(DraftPage draftNoteToSave, */ void importNotes(String zipLocation, Page parent, String conflict, Identity userIdentity) throws WikiException, IllegalAccessException, - IOException; + IOException, Exception; /** * Import Notes from a list of files @@ -635,7 +642,7 @@ void importNotes(String zipLocation, Page parent, String conflict, Identity user */ void importNotes(List files, Page parent, String conflict, Identity userIdentity) throws WikiException, IllegalAccessException, - IOException; + IOException, Exception; /** * Searches in all wiki pages. @@ -752,7 +759,7 @@ Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source, Strin * @param lang language. * @throws WikiException if an error occured */ - void deleteVersionsByNoteIdAndLang(Long noteId, String lang) throws WikiException; + void deleteVersionsByNoteIdAndLang(Long noteId, String lang) throws Exception; /** * Deletes a list of versions of note by language. @@ -764,7 +771,7 @@ Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source, Strin */ @Deprecated // Use {@link deleteVersionsByNoteIdAndLang(Long noteId, String lang)} instead - void deleteVersionsByNoteIdAndLang(Long noteId, String username, String lang) throws WikiException; + void deleteVersionsByNoteIdAndLang(Long noteId, String username, String lang) throws Exception; /** @@ -773,4 +780,60 @@ Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source, Strin * @param parentPageId Note parent page id */ void removeOrphanDraftPagesByParentPage(long parentPageId); + + /** + * Saves Note featured Image + * + * @param note target note page + * @param featuredImage Note featured image + * @return saved featured image ID + */ + Long saveNoteFeaturedImage(Page note, NoteFeaturedImage featuredImage) throws Exception; + + /** + * Get Note featured Image by its given id + * + * @param noteId Note id + * @param lang note version language + * @param isDraft is target not a draft + * @param thumbnailSize featured image thumbnail size + * @param userIdentityId user identity id + * @return {@link NoteFeaturedImage} + */ + NoteFeaturedImage getNoteFeaturedImageInfo(Long noteId, String lang, boolean isDraft, String thumbnailSize, long userIdentityId) throws Exception; + + /** + * Save note metadata properties + * + * @param pageProperties note metadata properties to save + * @param lang target version language + * @param userIdentityId user identity id + * @return {@link NotePageProperties} + */ + NotePageProperties saveNoteMetadata(NotePageProperties pageProperties, String lang, Long userIdentityId) throws Exception; + + /** + * Removes note featured image and its related metadata property + * + * @param noteId target note id + * @param featuredImageId featured image id + * @param lang target version language + * @param isDraft is the target note a draft + * @param userIdentityId user identity id + * @throws Exception + */ + void removeNoteFeaturedImage(Long noteId, Long featuredImageId, String lang, boolean isDraft, Long userIdentityId) throws Exception; + + /** + * Gets page version by its given id + * + * @param versionId page version id + * @return {@link PageVersion} + */ + PageVersion getPageVersionById(Long versionId); + + /** + * {@inheritDoc} + */ + DraftPage getDraftOfPageByLang(WikiPageParams param, String lang) throws WikiException; } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/PageUpdateType.java b/notes-service/src/main/java/org/exoplatform/wiki/service/PageUpdateType.java index 7a6ae24056..abdbea0dac 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/PageUpdateType.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/PageUpdateType.java @@ -10,5 +10,6 @@ public enum PageUpdateType { EDIT_PAGE_CONTENT_AND_TITLE, EDIT_PAGE_PERMISSIONS, MOVE_PAGE, - PUBLISH + PUBLISH, + EDIT_PAGE_PROPERTIES } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java index cd0d6b78f7..061648f037 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java @@ -20,8 +20,7 @@ package org.exoplatform.wiki.service.impl; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; @@ -37,13 +36,20 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.exoplatform.wiki.jpa.entity.DraftPageEntity; import org.gatein.api.EntityNotFoundException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import org.exoplatform.commons.exception.ObjectNotFoundException; +import org.exoplatform.commons.file.model.FileInfo; +import org.exoplatform.commons.file.model.FileItem; +import org.exoplatform.commons.file.services.FileService; import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.commons.utils.ObjectPageList; import org.exoplatform.commons.utils.PageList; @@ -52,10 +58,8 @@ import org.exoplatform.services.listener.ListenerService; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; -import org.exoplatform.services.organization.OrganizationService; import org.exoplatform.services.security.Identity; import org.exoplatform.services.security.IdentityConstants; -import org.exoplatform.services.security.IdentityRegistry; import org.exoplatform.social.common.service.HTMLUploadImageProcessor; import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; import org.exoplatform.social.core.manager.IdentityManager; @@ -63,7 +67,12 @@ import org.exoplatform.social.core.space.spi.SpaceService; import org.exoplatform.social.metadata.MetadataService; import org.exoplatform.social.metadata.model.MetadataItem; +import org.exoplatform.social.metadata.model.MetadataKey; import org.exoplatform.social.metadata.model.MetadataObject; +import org.exoplatform.social.metadata.model.MetadataType; +import org.exoplatform.services.thumbnail.ImageThumbnailService; +import org.exoplatform.upload.UploadResource; +import org.exoplatform.upload.UploadService; import org.exoplatform.wiki.WikiException; import org.exoplatform.wiki.model.DraftPage; import org.exoplatform.wiki.model.ImportList; @@ -90,21 +99,49 @@ import org.exoplatform.wiki.utils.NoteConstants; import org.exoplatform.wiki.utils.Utils; +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NoteMetadataObject; +import io.meeds.notes.model.NotePageProperties; import io.meeds.notes.service.NotePageViewService; import io.meeds.social.cms.service.CMSService; import lombok.SneakyThrows; public class NoteServiceImpl implements NoteService { - public static final String CACHE_NAME = "wiki.PageRenderingCache"; + public static final String CACHE_NAME = "wiki.PageRenderingCache"; - public static final String ATT_CACHE_NAME = "wiki.PageAttachmentCache"; + public static final String ATT_CACHE_NAME = "wiki.PageAttachmentCache"; - private static final String UNTITLED_PREFIX = "Untitled_"; + private static final String UNTITLED_PREFIX = "Untitled_"; - private static final String TEMP_DIRECTORY_PATH = "java.io.tmpdir"; + private static final String TEMP_DIRECTORY_PATH = "java.io.tmpdir"; - private static final Log log = ExoLogger.getLogger(NoteServiceImpl.class); + private static final String FILE_NAME_SPACE = "wiki"; + + private static final Log log = + ExoLogger.getLogger(NoteServiceImpl.class); + + private static final MetadataType NOTES_METADATA_TYPE = + new MetadataType(1001, "notes"); + + private static final MetadataKey NOTES_METADATA_KEY = + new MetadataKey(NOTES_METADATA_TYPE.getName(), + Utils.NOTES_METADATA_OBJECT_TYPE, + 0); + + public static final String NOTE_METADATA_PAGE_OBJECT_TYPE = "notePage"; + + public static final String NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE = "noteDraftPage"; + + public static final String NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE = "noteVersionPage"; + + public static final String SUMMARY_PROP = "summary"; + + public static final String FEATURED_IMAGE_ID = "featuredImageId"; + + public static final String FEATURED_IMAGE_UPDATED_DATE = "featuredImageUpdatedDate"; + + public static final String FEATURED_IMAGE_ALT_TEXT = "featuredImageAltText"; private final WikiService wikiService; @@ -114,7 +151,7 @@ public class NoteServiceImpl implements NoteService { private final ExoCache attachmentCountCache; - private final Map> pageLinksMap = new ConcurrentHashMap<>(); + private final Map> pageLinksMap = new ConcurrentHashMap<>(); private final IdentityManager identityManager; @@ -122,23 +159,29 @@ public class NoteServiceImpl implements NoteService { private final CMSService cmsService; - private final IdentityRegistry identityRegistry; - - private final OrganizationService organizationService; - private final ListenerService listenerService; + + private final FileService fileService; + + private final UploadService uploadService; + + private final MetadataService metadataService; + + private final ImageThumbnailService imageThumbnailService; private final HTMLUploadImageProcessor htmlUploadImageProcessor; - public NoteServiceImpl( DataStorage dataStorage, - CacheService cacheService, - WikiService wikiService, - IdentityManager identityManager, - SpaceService spaceService, - CMSService cmsService, - IdentityRegistry identityRegistry, - OrganizationService organizationService, - ListenerService listenerService) { + public NoteServiceImpl(DataStorage dataStorage, + CacheService cacheService, + WikiService wikiService, + IdentityManager identityManager, + SpaceService spaceService, + CMSService cmsService, + ListenerService listenerService, + FileService fileService, + UploadService uploadService, + MetadataService metadataService, + ImageThumbnailService imageThumbnailService) { this.dataStorage = dataStorage; this.wikiService = wikiService; this.identityManager = identityManager; @@ -147,8 +190,10 @@ public NoteServiceImpl( DataStorage dataStorage, this.spaceService = spaceService; this.listenerService = listenerService; this.cmsService = cmsService; - this.identityRegistry = identityRegistry; - this.organizationService = organizationService; + this.fileService = fileService; + this.uploadService = uploadService; + this.metadataService = metadataService; + this.imageThumbnailService = imageThumbnailService; this.htmlUploadImageProcessor = null; } @@ -158,9 +203,11 @@ public NoteServiceImpl(DataStorage dataStorage, IdentityManager identityManager, SpaceService spaceService, CMSService cmsService, - IdentityRegistry identityRegistry, - OrganizationService organizationService, ListenerService listenerService, + FileService fileService, + UploadService uploadService, + MetadataService metadataService, + ImageThumbnailService imageThumbnailService, HTMLUploadImageProcessor htmlUploadImageProcessor) { this.dataStorage = dataStorage; this.wikiService = wikiService; @@ -170,8 +217,10 @@ public NoteServiceImpl(DataStorage dataStorage, this.spaceService = spaceService; this.listenerService = listenerService; this.cmsService = cmsService; - this.identityRegistry = identityRegistry; - this.organizationService = organizationService; + this.fileService = fileService; + this.uploadService = uploadService; + this.metadataService = metadataService; + this.imageThumbnailService = imageThumbnailService; this.htmlUploadImageProcessor = htmlUploadImageProcessor; } @@ -199,11 +248,25 @@ public Page createNote(Wiki noteBook, String parentNoteName, Page note, Identity note.setAuthor(userIdentity.getUserId()); note.setContent(note.getContent()); Page createdPage = createNote(noteBook, parentPage, note); - - Space space = spaceService.getSpaceByGroupId(note.getWikiOwner()); - createdPage.setCanManage(Utils.canManageNotes(note.getAuthor(), space, note)); - createdPage.setCanImport(canImportNotes(note.getAuthor(), space, note)); - createdPage.setCanView(canViewNotes(note.getAuthor(), space, note)); + NotePageProperties properties = note.getProperties(); + try { + if (properties != null) { + properties.setNoteId(Long.parseLong(createdPage.getId())); + properties.setDraft(false); + properties = saveNoteMetadata(properties, + note.getLang(), + Long.valueOf(identityManager.getOrCreateUserIdentity(userIdentity.getUserId()).getId())); + } + } catch (Exception e) { + log.error("Failed to save note metadata", e); + } + if (createdPage != null) { + createdPage.setProperties(properties); + Space space = spaceService.getSpaceByGroupId(note.getWikiOwner()); + createdPage.setCanManage(Utils.canManageNotes(note.getAuthor(), space, note)); + createdPage.setCanImport(canImportNotes(note.getAuthor(), space, note)); + createdPage.setCanView(canViewNotes(note.getAuthor(), space, note)); + } return createdPage; } else { throw new EntityNotFoundException("Parent note not found"); @@ -228,6 +291,10 @@ public Page createNote(Wiki noteBook, Page parentPage, Page note) throws WikiExc Utils.broadcast(listenerService, "note.posted", note.getAuthor(), createdPage); postAddPage(noteBook.getType(), noteBook.getOwner(), note.getName(), createdPage); + Matcher mentionMatcher = Utils.MENTION_PATTERN.matcher(createdPage.getContent()); + if (mentionMatcher.find()) { + Utils.sendMentionInNoteNotification(createdPage, null, createdPage.getAuthor()); + } return createdPage; } @@ -246,7 +313,8 @@ public Page updateNote(Page note) throws WikiException { @Override public Page updateNote(Page note, PageUpdateType type, Identity userIdentity) throws WikiException, IllegalAccessException, - EntityNotFoundException { + EntityNotFoundException, + Exception { Page existingNote = getNoteById(note.getId()); if (existingNote == null) { throw new EntityNotFoundException("Note to update not found"); @@ -255,11 +323,25 @@ public Page updateNote(Page note, PageUpdateType type, Identity userIdentity) th if (userIdentity != null && !Utils.canManageNotes(userIdentity.getUserId(), space, existingNote)) { throw new IllegalAccessException("User does not have edit the note."); } - if (PageUpdateType.EDIT_PAGE_CONTENT.equals(type) || PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE.equals(type)) { + if (PageUpdateType.EDIT_PAGE_CONTENT.equals(type) || PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE.equals(type) + || PageUpdateType.EDIT_PAGE_PROPERTIES.equals(type)) { note.setUpdatedDate(Calendar.getInstance().getTime()); } note.setContent(note.getContent()); Page updatedPage = dataStorage.updatePage(note); + NotePageProperties properties = note.getProperties(); + if (userIdentity != null && properties != null) { + try { + properties.setNoteId(Long.parseLong(updatedPage.getId())); + properties.setDraft(false); + properties = saveNoteMetadata(note.getProperties(), + note.getLang(), + Long.valueOf(identityManager.getOrCreateUserIdentity(userIdentity.getUserId()).getId())); + updatedPage.setProperties(properties); + } catch (Exception e) { + log.error("Error while updating note metadata properties", e); + } + } invalidateCache(note); updatedPage.setUrl(Utils.getPageUrl(updatedPage)); @@ -273,9 +355,14 @@ public Page updateNote(Page note, PageUpdateType type, Identity userIdentity) th updatedPage.setMetadatas(metadata); note.setAuthor(userIdentity.getUserId()); } + + Matcher mentionsMatcher = Utils.MENTION_PATTERN.matcher(note.getContent()); + if (mentionsMatcher.find()) { + Utils.sendMentionInNoteNotification(note, existingNote, userIdentity != null ? userIdentity.getUserId() : existingNote.getAuthor()); + } Utils.broadcast(listenerService, "note.updated", note.getAuthor(), updatedPage); postUpdatePage(updatedPage.getWikiType(), updatedPage.getWikiOwner(), updatedPage.getName(), updatedPage, type); - + updatedPage.setLang(note.getLang()); return updatedPage; } @@ -349,7 +436,6 @@ public boolean deleteNote(String noteType, String noteOwner, String noteName, Id } deleteNote(noteType, noteOwner, noteName); - postDeletePage(noteType, noteOwner, noteName, note); // Post delete activity for all children pages @@ -815,16 +901,38 @@ public void removeDraftOfNote(Page page, String username) throws WikiException { */ @Override public void removeDraftOfNote(Page page) throws WikiException { + List draftPages = dataStorage.getDraftsOfPage(Long.valueOf(page.getId())); + for (DraftPage draftPage : draftPages) { + if (draftPage != null) { + dataStorage.deleteAttachmentsOfDraftPage(draftPage); + try { + deleteNoteMetadataProperties(draftPage, draftPage.getLang(), NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE); + } catch (Exception e) { + log.error("Error while deleting draft properties"); + } + } + } dataStorage.deleteDraftOfPage(page); } - + /** * {@inheritDoc} */ @Override - public void removeDraftOfNote(WikiPageParams param, String lang) throws WikiException { + public DraftPage getDraftOfPageByLang(WikiPageParams param, String lang) throws WikiException { Page page = getNoteOfNoteBookByName(param.getType(), param.getOwner(), param.getPageName()); - dataStorage.deleteDraftOfPage(page, lang); + return dataStorage.getDraftOfPageByLang(page, lang); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeDraftOfNote(WikiPageParams param, String lang) throws Exception { + DraftPage draftPage = getDraftOfPageByLang(param, lang); + if (draftPage != null) { + removeDraftById(draftPage.getId()); + } } /** @@ -839,10 +947,12 @@ public void removeDraft(String draftName) throws WikiException { * {@inheritDoc} */ @Override - public void removeDraftById(String draftId) throws WikiException { + public void removeDraftById(String draftId) throws Exception { + DraftPage draftNote = dataStorage.getDraftPageById(draftId); + deleteNoteMetadataProperties(draftNote, draftNote.getLang(), NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE); dataStorage.deleteDraftById(draftId); } - + /** * {@inheritDoc} */ @@ -850,7 +960,15 @@ public void removeDraftById(String draftId) throws WikiException { public List getVersionsHistoryOfNote(Page note, String userName) throws WikiException { List versionsHistory = dataStorage.getHistoryOfPage(note); if (versionsHistory == null || versionsHistory.isEmpty()) { - dataStorage.addPageVersion(note, userName); + PageVersion pageVersion = dataStorage.addPageVersion(note, userName); + pageVersion.setId(note.getId() + "-" + pageVersion.getName()); + copyNotePageProperties(note, + pageVersion, + note.getLang(), + null, + NOTE_METADATA_PAGE_OBJECT_TYPE, + NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE, + userName); versionsHistory = dataStorage.getHistoryOfPage(note); } for (PageHistory version : versionsHistory) { @@ -869,10 +987,30 @@ public List getVersionsHistoryOfNote(Page note, String userName) th */ @Override public void createVersionOfNote(Page note, String userName) throws WikiException { - dataStorage.addPageVersion(note, userName); + PageVersion pageVersion = dataStorage.addPageVersion(note, userName); if (note.getLang() != null) { - String versionLandId = note.getId() + "-" + note.getLang(); - postUpdatePageVersionLanguage(versionLandId); + try { + NotePageProperties properties = note.getProperties(); + if (properties != null) { + properties.setNoteId(Long.parseLong(note.getId())); + properties.setDraft(false); + saveNoteMetadata(properties, note.getLang(), Long.valueOf(identityManager.getOrCreateUserIdentity(userName).getId())); + } + } catch (Exception e) { + log.error("Error while saving note version language metadata", e); + } + String versionLangId = note.getId() + "-" + note.getLang(); + postUpdatePageVersionLanguage(versionLangId); + } else { + pageVersion.setId(note.getId() + "-" + pageVersion.getName()); + copyNotePageProperties(note, + pageVersion, + note.getLang(), + null, + NOTE_METADATA_PAGE_OBJECT_TYPE, + NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE, + userName + ); } } @@ -881,7 +1019,15 @@ public void createVersionOfNote(Page note, String userName) throws WikiException */ @Override public void restoreVersionOfNote(String versionName, Page note, String userName) throws WikiException { - dataStorage.restoreVersionOfPage(versionName, note); + PageVersion pageVersion = dataStorage.restoreVersionOfPage(versionName, note); + pageVersion.setId(note.getId() + "-" + pageVersion.getName()); + copyNotePageProperties(pageVersion, + note, + null, + null, + NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE, + NOTE_METADATA_PAGE_OBJECT_TYPE, + userName); createVersionOfNote(note, userName); invalidateCache(note); } @@ -944,9 +1090,23 @@ public DraftPage updateDraftForExistPage(DraftPage draftNoteToUpdate, } else { newDraftPage.setTargetPageRevision(revision); } - newDraftPage = dataStorage.updateDraftPageForUser(newDraftPage, Utils.getCurrentUser()); - + NotePageProperties properties = draftNoteToUpdate.getProperties(); + try { + if (properties != null) { + NoteFeaturedImage featuredImage = properties.getFeaturedImage(); + if (featuredImage != null && featuredImage.getUploadId() != null + && isOriginalFeaturedImage(draftNoteToUpdate, targetPage)) { + featuredImage.setId(0L); + } + properties = saveNoteMetadata(properties, + newDraftPage.getLang(), + Long.valueOf(identityManager.getOrCreateUserIdentity(username).getId())); + } + } catch (Exception e) { + log.error("Failed to save draft note metadata", e); + } + newDraftPage.setProperties(properties); return newDraftPage; } @@ -954,7 +1114,7 @@ public DraftPage updateDraftForExistPage(DraftPage draftNoteToUpdate, * {@inheritDoc} */ @Override - public DraftPage updateDraftForNewPage(DraftPage draftNoteToUpdate, long clientTime) throws WikiException { + public DraftPage updateDraftForNewPage(DraftPage draftNoteToUpdate, long clientTime, long userIdentityId) throws WikiException { // Create suffix for draft name String draftSuffix = getDraftNameSuffix(clientTime); @@ -973,7 +1133,13 @@ public DraftPage updateDraftForNewPage(DraftPage draftNoteToUpdate, long clientT newDraftPage.setUpdatedDate(new Date(clientTime)); newDraftPage = dataStorage.updateDraftPageForUser(newDraftPage, Utils.getCurrentUser()); - + NotePageProperties properties = draftNoteToUpdate.getProperties(); + try { + properties = saveNoteMetadata(properties, newDraftPage.getLang(), userIdentityId); + } catch (Exception e) { + log.error("Failed to save draft note metadata", e); + } + newDraftPage.setProperties(properties); return newDraftPage; } @@ -1010,9 +1176,23 @@ public DraftPage createDraftForExistPage(DraftPage draftPage, } else { newDraftPage.setTargetPageRevision(revision); } - newDraftPage = dataStorage.createDraftPageForUser(newDraftPage, username); - + NotePageProperties properties = draftPage.getProperties(); + try { + if (properties != null) { + NoteFeaturedImage featuredImage = properties.getFeaturedImage(); + if (featuredImage != null && featuredImage.getUploadId() != null) { + featuredImage.setId(0L); + } + properties.setNoteId(Long.parseLong(newDraftPage.getId())); + properties = saveNoteMetadata(properties, + draftPage.getLang(), + Long.valueOf(identityManager.getOrCreateUserIdentity(username).getId())); + } + } catch (Exception e) { + log.error("Failed to save draft note metadata", e); + } + newDraftPage.setProperties(properties); return newDraftPage; } @@ -1020,7 +1200,7 @@ public DraftPage createDraftForExistPage(DraftPage draftPage, * {@inheritDoc} */ @Override - public DraftPage createDraftForNewPage(DraftPage draftPage, long clientTime) throws WikiException { + public DraftPage createDraftForNewPage(DraftPage draftPage, long clientTime, long userIdentityId) throws WikiException { // Create suffix for draft name String draftSuffix = getDraftNameSuffix(clientTime); @@ -1037,9 +1217,17 @@ public DraftPage createDraftForNewPage(DraftPage draftPage, long clientTime) thr newDraftPage.setSyntax(draftPage.getSyntax()); newDraftPage.setCreatedDate(new Date(clientTime)); newDraftPage.setUpdatedDate(new Date(clientTime)); - newDraftPage = dataStorage.createDraftPageForUser(newDraftPage, Utils.getCurrentUser()); - + NotePageProperties properties = draftPage.getProperties(); + try { + if (properties != null) { + properties.setNoteId(Long.parseLong(newDraftPage.getId())); + properties = saveNoteMetadata(properties, draftPage.getLang(), userIdentityId); + } + } catch (Exception e) { + log.error("Failed to save draft note metadata", e); + } + newDraftPage.setProperties(properties); return newDraftPage; } @@ -1091,7 +1279,7 @@ public String getNoteRenderedContent(Page note) { @Override public void importNotes(String zipLocation, Page parent, String conflict, Identity userIdentity) throws WikiException, IllegalAccessException, - IOException { + IOException, Exception { List files = Utils.unzip(zipLocation, System.getProperty(TEMP_DIRECTORY_PATH)); importNotes(files, parent, conflict, userIdentity); } @@ -1102,7 +1290,7 @@ public void importNotes(String zipLocation, Page parent, String conflict, Identi @Override public void importNotes(List files, Page parent, String conflict, Identity userIdentity) throws WikiException, IllegalAccessException, - IOException { + IOException, Exception { String notesFilePath = ""; for (String file : files) { @@ -1197,6 +1385,7 @@ public Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source page.setTitle(publishedVersion.getTitle()); page.setContent(publishedVersion.getContent()); page.setLang(publishedVersion.getLang()); + page.setProperties(publishedVersion.getProperties()); if (lang != null) { page.setMetadatas(retrieveMetadataItems(pageId + "-" + lang, userIdentity.getUserId())); } @@ -1256,7 +1445,15 @@ public List getPageAvailableTranslationLanguages(Long pageId, String use public List getVersionsHistoryOfNoteByLang(Page note, String userName, String lang) throws WikiException { List pageHistories = dataStorage.getPageHistoryVersionsByPageIdAndLang(Long.valueOf(note.getId()), lang); if (lang == null && pageHistories.isEmpty()) { - dataStorage.addPageVersion(note, userName); + PageVersion pageVersion = dataStorage.addPageVersion(note, userName); + pageVersion.setId(note.getId() + "-" + pageVersion.getName()); + copyNotePageProperties(note, + pageVersion, + note.getLang(), + null, + NOTE_METADATA_PAGE_OBJECT_TYPE, + NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE, + userName); pageHistories = dataStorage.getPageHistoryVersionsByPageIdAndLang(Long.valueOf(note.getId()), null); } return pageHistories; @@ -1282,22 +1479,46 @@ public DraftPage getLatestDraftPageByUserAndTargetPageAndLang(Long targetPageId, * {@inheritDoc} */ @Override - public void deleteVersionsByNoteIdAndLang(Long noteId, String lang) throws WikiException { + public void deleteVersionsByNoteIdAndLang(Long noteId, String lang) throws Exception { + Page note = getNoteById(String.valueOf(noteId)); + if (note != null) { + deleteNoteMetadataProperties(note, lang, NOTE_METADATA_PAGE_OBJECT_TYPE); + } dataStorage.deleteVersionsByNoteIdAndLang(noteId, lang); List drafts = dataStorage.getDraftsOfPage(noteId); for (DraftPage draftPage : drafts) { if (StringUtils.equals(draftPage.getLang(),lang)) { - removeDraft(draftPage.getName()); + removeDraftById(draftPage.getId()); } } postDeletePageVersionLanguage(noteId + "-" + lang); } + private void deleteNoteMetadataProperties(Page note, String lang, String objectType) throws Exception { + MetadataItem draftNoteMetadataItem = getNoteMetadataItem(note, lang, objectType); + if (draftNoteMetadataItem != null) { + Map properties = draftNoteMetadataItem.getProperties(); + if (properties != null && properties.getOrDefault(FEATURED_IMAGE_ID, null) != null) { + String featuredImageId = properties.get(FEATURED_IMAGE_ID); + if (note.isDraftPage() && ((DraftPage) note).getTargetPageId() != null) { + removeNoteFeaturedImage(Long.parseLong(note.getId()), + Long.parseLong(featuredImageId), + lang, + true, + Long.parseLong(identityManager.getOrCreateUserIdentity(note.getOwner()).getId())); + } else { + fileService.deleteFile(Long.parseLong(featuredImageId)); + } + } + metadataService.deleteMetadataItem(draftNoteMetadataItem.getId(), false); + } + } + /** * {@inheritDoc} */ @Override - public void deleteVersionsByNoteIdAndLang(Long noteId, String userName, String lang) throws WikiException { + public void deleteVersionsByNoteIdAndLang(Long noteId, String userName, String lang) throws Exception { deleteVersionsByNoteIdAndLang(noteId, lang); } @@ -1476,7 +1697,7 @@ protected void invalidateAttachmentCache(Page note) { /******* Private methods *******/ private void importNote(Page note, Page parent, Wiki wiki, String conflict, Identity userIdentity) throws WikiException, - IllegalAccessException { + IllegalAccessException, Exception { Page parent_ = getNoteOfNoteBookByName(wiki.getType(), wiki.getOwner(), parent.getName()); if (parent_ == null) { @@ -1757,7 +1978,6 @@ private Map> retrieveMetadataItems(String noteId, Str identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, username); long currentUserId = Long.parseLong(currentIdentity.getId()); - MetadataService metadataService = CommonsUtils.getService(MetadataService.class); MetadataObject metadataObject = new MetadataObject(Utils.NOTES_METADATA_OBJECT_TYPE, noteId); List metadataItems = metadataService.getMetadataItemsByObject(metadataObject); Map> metadata = new HashMap<>(); @@ -1779,4 +1999,275 @@ private Map> retrieveMetadataItems(String noteId, Str public void removeOrphanDraftPagesByParentPage(long parentPageId) { dataStorage.deleteOrphanDraftPagesByParentPage(parentPageId); } + + /** + * {@inheritDoc} + */ + @Override + public Long saveNoteFeaturedImage(Page note, NoteFeaturedImage featuredImage) throws Exception { + if (featuredImage == null) { + return null; + } + long featuredImageId = featuredImage.getId() != null ? featuredImage.getId(): 0L; + String uploadId = featuredImage.getUploadId(); + if (uploadId != null) { + + UploadResource uploadResource = uploadService.getUploadResource(uploadId); + if (uploadResource != null) { + String fileDiskLocation = uploadResource.getStoreLocation(); + try (InputStream inputStream = new FileInputStream(fileDiskLocation);) { + FileItem fileItem = new FileItem(featuredImageId, + note.getName(), + featuredImage.getMimeType(), + FILE_NAME_SPACE, + inputStream.available(), + new Date(), + null, + false, + inputStream); + if (featuredImageId == 0) { + fileItem = fileService.writeFile(fileItem); + } else { + fileItem = fileService.updateFile(fileItem); + } + if (fileItem != null && fileItem.getFileInfo() != null) { + return fileItem.getFileInfo().getId(); + } + } catch (Exception e) { + log.error("Error while saving note featured image", e); + } finally { + uploadService.removeUploadResource(uploadId); + } + } + } + return featuredImageId; + } + + /** + * {@inheritDoc} + */ + @Override + public NoteFeaturedImage getNoteFeaturedImageInfo(Long noteId, String lang, boolean isDraft, String thumbnailSize, long userIdentityId) throws Exception { + if (noteId == null) { + throw new IllegalArgumentException("note id is mandatory"); + } + Page note; + org.exoplatform.social.core.identity.model.Identity identity = identityManager.getIdentity(String.valueOf(userIdentityId)); + if (isDraft) { + note = getDraftNoteById(String.valueOf(noteId), identity.getRemoteId()); + } else { + note = getNoteByIdAndLang(noteId, lang); + } + if (note == null) { + throw new ObjectNotFoundException("Note with id: " + noteId + " and lang: " + lang + " not found"); + } + + MetadataItem metadataItem = getNoteMetadataItem(note, + lang, + isDraft ? NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE + : NOTE_METADATA_PAGE_OBJECT_TYPE); + if (metadataItem != null && !MapUtils.isEmpty(metadataItem.getProperties())) { + String featuredImageIdProp = metadataItem.getProperties().get(FEATURED_IMAGE_ID); + long noteFeaturedImageId = featuredImageIdProp != null + && !featuredImageIdProp.equals("null") ? Long.parseLong(featuredImageIdProp) : 0L; + FileItem fileItem = fileService.getFile(noteFeaturedImageId); + if (fileItem != null && fileItem.getFileInfo() != null) { + FileInfo fileInfo = fileItem.getFileInfo(); + if (thumbnailSize != null) { + int[] dimension = org.exoplatform.social.common.Utils.parseDimension(thumbnailSize); + fileItem = imageThumbnailService.getOrCreateThumbnail(fileItem, dimension[0], dimension[1]); + } + return new NoteFeaturedImage(fileInfo.getId(), + fileInfo.getName(), + fileInfo.getMimetype(), + fileInfo.getSize(), + fileInfo.getUpdatedDate().getTime(), + fileItem.getAsStream()); + } + } + return null; + } + + private NoteMetadataObject buildNoteMetadataObject(Page note, String lang, String objectType) { + Space space = spaceService.getSpaceByGroupId(note.getWikiOwner()); + long spaceId = space != null ? Long.parseLong(space.getId()) : 0L; + String noteId = String.valueOf(note.getId()); + noteId = lang != null ? noteId + "-" + lang : noteId; + return new NoteMetadataObject(objectType, noteId, note.getParentPageId(), spaceId); + } + + private MetadataItem getNoteMetadataItem(Page note, String lang, String objectType) { + NoteMetadataObject noteMetadataObject = buildNoteMetadataObject(note, lang, objectType); + return metadataService.getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY, noteMetadataObject) + .stream() + .findFirst() + .orElse(null); + } + + private void copyNotePageProperties(Page oldNote, + Page note, + String oldLang, + String lang, + String oldObjectType, + String newObjectType, + String username) { + if (note == null || oldNote == null) { + return; + } + if (username != null) { + org.exoplatform.social.core.identity.model.Identity identity = + identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, + username); + NoteMetadataObject newNoteMetadataObject = buildNoteMetadataObject(note, lang, newObjectType); + MetadataItem oldNoteMetadataItem = getNoteMetadataItem(oldNote, oldLang, oldObjectType); + MetadataItem newNoteMetadataItem = getNoteMetadataItem(note, lang, newObjectType); + if (oldNoteMetadataItem != null && oldNoteMetadataItem.getProperties() != null) { + if (newNoteMetadataItem != null) { + newNoteMetadataItem.setProperties(oldNoteMetadataItem.getProperties()); + metadataService.updateMetadataItem(newNoteMetadataItem, Long.parseLong(identity.getId()), false); + } else { + try { + metadataService.createMetadataItem(newNoteMetadataObject, + NOTES_METADATA_KEY, + oldNoteMetadataItem.getProperties(), + Long.parseLong(identity.getId()), + false); + } catch (Exception e) { + log.error("Error while creating note metadata item", e); + } + } + } + } + } + + private boolean isOriginalFeaturedImage(Page draftPage, Page targetPage) { + if (draftPage == null || targetPage == null) { + return false; + } + if (draftPage.getProperties() == null || targetPage.getProperties() == null) { + return false; + } + NoteFeaturedImage draftFeaturedImage = draftPage.getProperties().getFeaturedImage(); + NoteFeaturedImage targetFeaturedImage = targetPage.getProperties().getFeaturedImage(); + return draftFeaturedImage != null && targetFeaturedImage != null + && targetFeaturedImage.getId().equals(draftFeaturedImage.getId()); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNoteFeaturedImage(Long noteId, + Long featuredImageId, + String lang, + boolean isDraft, + Long userIdentityId) throws Exception { + boolean removeFeaturedImageFile = true; + Page note; + if (isDraft) { + DraftPage draftPage = getDraftNoteById(String.valueOf(noteId), + identityManager.getIdentity(String.valueOf(userIdentityId)).getRemoteId()); + if (draftPage != null && draftPage.getTargetPageId() != null + && isOriginalFeaturedImage(draftPage, getNoteByIdAndLang(Long.valueOf(draftPage.getTargetPageId()), lang))) { + removeFeaturedImageFile = false; + } + note = draftPage; + } else { + note = getNoteByIdAndLang(noteId, lang); + } + if (note == null) { + throw new ObjectNotFoundException("note not found"); + } + if (removeFeaturedImageFile && featuredImageId != null && featuredImageId > 0) { + fileService.deleteFile(featuredImageId); + } + MetadataItem metadataItem = getNoteMetadataItem(note, + lang, + isDraft ? NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE + : NOTE_METADATA_PAGE_OBJECT_TYPE); + if (metadataItem != null && metadataItem.getProperties() != null) { + Map properties = metadataItem.getProperties(); + properties.remove(FEATURED_IMAGE_ID); + properties.remove(FEATURED_IMAGE_UPDATED_DATE); + properties.remove(FEATURED_IMAGE_ALT_TEXT); + metadataItem.setProperties(properties); + metadataService.updateMetadataItem(metadataItem, userIdentityId, false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public NotePageProperties saveNoteMetadata(NotePageProperties notePageProperties, String lang, Long userIdentityId) throws Exception { + if (notePageProperties == null) { + return null; + } + Page note; + Long featuredImageId = null; + NoteFeaturedImage featuredImage = notePageProperties.getFeaturedImage(); + if (notePageProperties.isDraft()) { + note = getDraftNoteById(String.valueOf(notePageProperties.getNoteId()), + identityManager.getIdentity(String.valueOf(userIdentityId)).getRemoteId()); + } else { + note = getNoteByIdAndLang(notePageProperties.getNoteId(), lang); + } + if (note == null) { + throw new ObjectNotFoundException("note not found"); + } + + if (featuredImage != null && featuredImage.isToDelete()) { + removeNoteFeaturedImage(Long.valueOf(note.getId()), + featuredImage.getId(), + lang, + notePageProperties.isDraft(), + userIdentityId); + } else { + featuredImageId = saveNoteFeaturedImage(note, featuredImage); + } + NoteMetadataObject noteMetadataObject = + buildNoteMetadataObject(note, + lang, + notePageProperties.isDraft() ? NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE + : NOTE_METADATA_PAGE_OBJECT_TYPE); + MetadataItem metadataItem = getNoteMetadataItem(note, + lang, + notePageProperties.isDraft() ? NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE + : NOTE_METADATA_PAGE_OBJECT_TYPE); + + Map properties = new HashMap<>(); + if (metadataItem != null && metadataItem.getProperties() != null) { + properties = metadataItem.getProperties(); + } + properties.put(SUMMARY_PROP, notePageProperties.getSummary()); + if (featuredImageId != null) { + properties.put(FEATURED_IMAGE_ID, String.valueOf(featuredImageId)); + properties.put(FEATURED_IMAGE_UPDATED_DATE, String.valueOf(new Date().getTime())); + properties.put(FEATURED_IMAGE_ALT_TEXT, notePageProperties.getFeaturedImage().getAltText()); + } + if (metadataItem == null) { + metadataService.createMetadataItem(noteMetadataObject, NOTES_METADATA_KEY, properties, userIdentityId, false); + } else { + metadataItem.setProperties(properties); + metadataService.updateMetadataItem(metadataItem, userIdentityId, false); + } + if (featuredImage != null) { + featuredImage.setId(featuredImageId); + featuredImage.setLastUpdated(Long.valueOf(properties.getOrDefault(FEATURED_IMAGE_UPDATED_DATE, "0"))); + featuredImage.setUploadId(null); + notePageProperties.setFeaturedImage(featuredImage); + } + return notePageProperties; + } + + /** + * {@inheritDoc} + */ + @Override + public PageVersion getPageVersionById(Long versionId) { + if (versionId == null) { + throw new IllegalArgumentException("version id is mandatory"); + } + return dataStorage.getPageVersionById(versionId); + } } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java index 9d87b8a3b4..0cd52ab1e3 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java @@ -20,6 +20,7 @@ package org.exoplatform.wiki.service.rest; +import java.io.InputStream; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -29,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; @@ -45,7 +47,9 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; +import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @@ -53,14 +57,13 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; -import org.exoplatform.social.core.manager.IdentityManager; -import org.exoplatform.wiki.model.*; import org.gatein.api.EntityNotFoundException; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.exoplatform.common.http.HTTPStatus; +import org.exoplatform.commons.exception.ObjectNotFoundException; +import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.commons.utils.HTMLSanitizer; import org.exoplatform.portal.localization.LocaleContextInfoUtils; import org.exoplatform.services.log.ExoLogger; @@ -71,12 +74,22 @@ import org.exoplatform.services.rest.resource.ResourceContainer; import org.exoplatform.services.security.ConversationState; import org.exoplatform.services.security.Identity; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; +import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.core.utils.MentionUtils; import org.exoplatform.social.rest.api.EntityBuilder; import org.exoplatform.social.rest.api.RestUtils; import org.exoplatform.social.rest.entity.IdentityEntity; import org.exoplatform.upload.UploadResource; import org.exoplatform.upload.UploadService; import org.exoplatform.wiki.WikiException; +import org.exoplatform.wiki.model.Attachment; +import org.exoplatform.wiki.model.DraftPage; +import org.exoplatform.wiki.model.Page; +import org.exoplatform.wiki.model.PageHistory; +import org.exoplatform.wiki.model.PageVersion; +import org.exoplatform.wiki.model.Wiki; +import org.exoplatform.wiki.model.WikiType; import org.exoplatform.wiki.resolver.TitleResolver; import org.exoplatform.wiki.service.NoteService; import org.exoplatform.wiki.service.NotesExportService; @@ -96,6 +109,10 @@ import org.exoplatform.wiki.utils.NoteConstants; import org.exoplatform.wiki.utils.Utils; +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NotePageProperties; +import io.meeds.notes.rest.model.DraftPageEntity; +import io.meeds.notes.rest.model.PageEntity; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -128,6 +145,17 @@ public class NotesRestService implements ResourceContainer { private final CacheControl cc; + private static final int CACHE_DURATION_SECONDS = 31536000; + + private static final long CACHE_DURATION_MILLISECONDS = CACHE_DURATION_SECONDS * 1000L; + + private static final CacheControl ILLUSTRATION_CACHE_CONTROL = new CacheControl(); + + static { + ILLUSTRATION_CACHE_CONTROL.setMaxAge(CACHE_DURATION_SECONDS); + } + + public NotesRestService(NoteService noteService, WikiService noteBookService, UploadService uploadService, @@ -277,7 +305,7 @@ public Response getNoteById(@Parameter(description = "Note id", required = true) if (note.getContent().contains("wiki-children-pages ck-widget")) { note = updateChildrenContainer(note); } - note.setContent(HTMLSanitizer.sanitize(note.getContent())); + note.setContent(sanitizeAndSubstituteMentions(note.getContent(), lang)); note.setBreadcrumb(noteService.getBreadCrumb(note.getWikiType(), note.getWikiOwner(), note.getName(), @@ -373,7 +401,7 @@ public Response getDraftNoteById(@Parameter(description = "Note id", required = if (parentPage == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - draftNote.setContent(HTMLSanitizer.sanitize(draftNote.getContent())); + draftNote.setContent(sanitizeAndSubstituteMentions(draftNote.getContent(), lang)); draftNote.setBreadcrumb(noteService.getBreadCrumb(parentPage.getWikiType(), parentPage.getWikiOwner(), draftNote.getId(), @@ -412,6 +440,9 @@ public Response getLatestDraftOfPage(@Parameter(description = "Note id", require } DraftPage draftNote = noteService.getLatestDraftPageByTargetPageAndLang(Long.valueOf(targetPage.getId()), lang); + if (draftNote != null) { + draftNote.setContent(sanitizeAndSubstituteMentions(draftNote.getContent(), lang)); + } return Response.ok(draftNote != null ? draftNote : org.json.JSONObject.NULL).build(); } catch (Exception e) { log.error("Can't get draft note {}", noteId, e); @@ -438,7 +469,9 @@ public Response getNoteVersions(@Parameter(description = "Note id", required = t if (note == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - return Response.ok(noteService.getVersionsHistoryOfNoteByLang(note, identity.getUserId(), lang)) + List pageHistories = noteService.getVersionsHistoryOfNoteByLang(note, identity.getUserId(), lang); + pageHistories.forEach(pageHistory -> pageHistory.setContent(sanitizeAndSubstituteMentions(pageHistory.getContent(), lang))); + return Response.ok(pageHistories) .build(); } catch (IllegalAccessException e) { log.error("User does not have view permissions on the note {}", noteId, e); @@ -458,7 +491,7 @@ public Response getNoteVersions(@Parameter(description = "Note id", required = t @ApiResponse(responseCode = "403", description = "Unauthorized operation"), @ApiResponse(responseCode = "404", description = "Resource not found") }) public Response createNote(@Parameter(description = "note object to be created", required = true) - Page note) { + PageEntity note) { if (note == null) { return Response.status(Response.Status.BAD_REQUEST).build(); } @@ -496,9 +529,12 @@ public Response createNote(@Parameter(description = "note object to be created", note.setAuthor(currentUser); note.setOwner(currentUser); note.setSyntax(syntaxId); - note.setName(TitleResolver.getId(note.getTitle(), false)); + note.setName(note.getTitle()); note.setUrl(""); - Page createdNote = noteService.createNote(noteBook, note.getParentPageName(), note, identity); + Page createdNote = noteService.createNote(noteBook, + note.getParentPageName(), + io.meeds.notes.rest.utils.EntityBuilder.toPage(note), + identity); return Response.ok(createdNote, MediaType.APPLICATION_JSON).cacheControl(cc).build(); } catch (IllegalAccessException e) { log.error("User does not have view permissions on the note {}", note.getName(), e); @@ -517,7 +553,7 @@ public Response createNote(@Parameter(description = "note object to be created", @ApiResponse(responseCode = "400", description = "Invalid query input"), @ApiResponse(responseCode = "403", description = "Unauthorized operation"), @ApiResponse(responseCode = "404", description = "Resource not found") }) public Response saveDraft(@RequestBody(description = "Note draft page object to be created", required = true) - DraftPage draftNoteToSave) { + DraftPageEntity draftNoteToSave) { if (draftNoteToSave == null) { return Response.status(Response.Status.BAD_REQUEST).build(); } @@ -530,6 +566,7 @@ public Response saveDraft(@RequestBody(description = "Note draft page object to String noteBookOwner = draftNoteToSave.getWikiOwner(); Page parentNote = null; Page targetNote = null; + DraftPage savedDraftPage = null; try { Identity identity = ConversationState.getCurrent().getIdentity(); if (StringUtils.isNoneEmpty(draftNoteToSave.getParentPageId())) { @@ -564,28 +601,31 @@ public Response saveDraft(@RequestBody(description = "Note draft page object to draftNoteToSave.setId(draftPage.getId()); } } - if (StringUtils.isNoneEmpty(draftNoteToSave.getId())) { - draftNoteToSave = targetNote != null - ? noteService.updateDraftForExistPage(draftNoteToSave, + savedDraftPage = targetNote != null + ? noteService.updateDraftForExistPage(io.meeds.notes.rest.utils.EntityBuilder.toDraftPage(draftNoteToSave), targetNote, null, System.currentTimeMillis(), currentUser) - : noteService.updateDraftForNewPage(draftNoteToSave, System.currentTimeMillis()); + : noteService.updateDraftForNewPage(io.meeds.notes.rest.utils.EntityBuilder.toDraftPage(draftNoteToSave), + System.currentTimeMillis(), + RestUtils.getCurrentUserIdentityId()); } else { - draftNoteToSave = targetNote != null - ? noteService.createDraftForExistPage(draftNoteToSave, + savedDraftPage = targetNote != null + ? noteService.createDraftForExistPage(io.meeds.notes.rest.utils.EntityBuilder.toDraftPage(draftNoteToSave), targetNote, null, System.currentTimeMillis(), currentUser) - : noteService.createDraftForNewPage(draftNoteToSave, System.currentTimeMillis()); + : noteService.createDraftForNewPage(io.meeds.notes.rest.utils.EntityBuilder.toDraftPage(draftNoteToSave), + System.currentTimeMillis(), + RestUtils.getCurrentUserIdentityId()); } - return Response.ok(draftNoteToSave, MediaType.APPLICATION_JSON).cacheControl(cc).build(); + return Response.ok(savedDraftPage, MediaType.APPLICATION_JSON).cacheControl(cc).build(); } catch (Exception ex) { - log.warn("Failed to perform save noteBook draft note {}:{}:{}", noteBookType, noteBookOwner, draftNoteToSave.getId(), ex); + log.warn("Failed to perform save noteBook draft note {}:{}", noteBookType, noteBookOwner, ex); return Response.status(HTTPStatus.INTERNAL_ERROR).cacheControl(cc).build(); } } @@ -607,7 +647,7 @@ public Response updateNote(@Parameter(description = "NoteBook Type", required = @PathParam("noteId") String noteId, @RequestBody(description = "note object to be updated", required = true) - Page note) { + PageEntity note) { if (note == null) { return Response.status(Response.Status.BAD_REQUEST).build(); } @@ -675,10 +715,8 @@ public Response updateNote(@Parameter(description = "NoteBook Type", required = @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @ApiResponse(responseCode = "400", description = "Invalid query input"), @ApiResponse(responseCode = "403", description = "Unauthorized operation"), @ApiResponse(responseCode = "404", description = "Resource not found") }) - public Response updateNoteById(@Parameter(description = "Note id", required = true) - @PathParam("noteId") - String noteId, @RequestBody(description = "note object to be updated", required = true) - Page note) { + public Response updateNoteById(@Parameter(description = "Note id", required = true) @PathParam("noteId") String noteId, + @RequestBody(description = "note object to be updated", required = true) PageEntity note) { if (note == null) { return Response.status(Response.Status.BAD_REQUEST).build(); } @@ -701,22 +739,29 @@ public Response updateNoteById(@Parameter(description = "Note id", required = tr return Response.status(Response.Status.CONFLICT).entity(NOTE_NAME_EXISTS).build(); } note_.setToBePublished(note.isToBePublished()); + NotePageProperties notePageProperties = io.meeds.notes.rest.utils.EntityBuilder.toNotePageProperties(note.getProperties()); + NoteFeaturedImage featuredImage = null; + if (notePageProperties != null) { + featuredImage = notePageProperties.getFeaturedImage(); + } String newNoteName = note_.getName(); if (!note_.getTitle().equals(note.getTitle()) && !note_.getContent().equals(note.getContent())) { if (StringUtils.isBlank(note.getLang())) { newNoteName = TitleResolver.getId(note.getTitle(), false); note_.setTitle(note.getTitle()); note_.setContent(note.getContent()); + note_.setProperties(notePageProperties); if (!NoteConstants.NOTE_HOME_NAME.equals(note.getName()) && !note.getName().equals(newNoteName)) { noteService.renameNote(note_.getWikiType(), note_.getWikiOwner(), note_.getName(), newNoteName, note.getTitle()); note_.setName(newNoteName); } note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE, identity); } else { + note_.setLang(note.getLang()); note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE, identity); note_.setTitle(note.getTitle()); note_.setContent(note.getContent()); - note_.setLang(note.getLang()); + note_.setProperties(notePageProperties); } noteService.createVersionOfNote(note_, identity.getUserId()); if (!Utils.ANONYM_IDENTITY.equals(identity.getUserId())) { @@ -731,11 +776,13 @@ public Response updateNoteById(@Parameter(description = "Note id", required = tr note_.setName(newNoteName); } note_.setTitle(note.getTitle()); + note_.setProperties(notePageProperties); note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_TITLE, identity); } else { + note_.setLang(note.getLang()); note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_TITLE, identity); note_.setTitle(note.getTitle()); - note_.setLang(note.getLang()); + note_.setProperties(notePageProperties); } noteService.createVersionOfNote(note_, identity.getUserId()); if (!Utils.ANONYM_IDENTITY.equals(identity.getUserId())) { @@ -745,11 +792,28 @@ public Response updateNoteById(@Parameter(description = "Note id", required = tr } else if (!note_.getContent().equals(note.getContent())) { if (StringUtils.isBlank(note.getLang())) { note_.setContent(note.getContent()); + note_.setProperties(notePageProperties); note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_CONTENT, identity); } else { + note_.setLang(note.getLang()); note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_CONTENT, identity); note_.setContent(note.getContent()); + note_.setProperties(notePageProperties); + } + noteService.createVersionOfNote(note_, identity.getUserId()); + if (!Utils.ANONYM_IDENTITY.equals(identity.getUserId())) { + WikiPageParams noteParams = new WikiPageParams(note_.getWikiType(), note_.getWikiOwner(), newNoteName); + noteService.removeDraftOfNote(noteParams, note.getLang()); + } + } else if ((featuredImage != null && (featuredImage.isToDelete() || featuredImage.getUploadId() != null)) + || !Objects.equals(note_.getProperties(), notePageProperties)) { + if (StringUtils.isBlank(note.getLang())) { + note_.setProperties(notePageProperties); + note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_PROPERTIES, identity); + } else { note_.setLang(note.getLang()); + note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_PROPERTIES, identity); + note_.setProperties(notePageProperties); } noteService.createVersionOfNote(note_, identity.getUserId()); if (!Utils.ANONYM_IDENTITY.equals(identity.getUserId())) { @@ -761,7 +825,7 @@ public Response updateNoteById(@Parameter(description = "Note id", required = tr } else { // in this case, the note didnt change on title nor content. As we need the page // url in front side, we compute it here - note_.setUrl(Utils.getPageUrl(note)); + note_.setUrl(Utils.getPageUrl(io.meeds.notes.rest.utils.EntityBuilder.toPage(note))); } return Response.ok(note_, MediaType.APPLICATION_JSON).cacheControl(cc).build(); } catch (IllegalAccessException e) { @@ -902,8 +966,7 @@ public Response deleteDraftNote(@Parameter(description = "Note id", required = t if (draftNote == null) { return Response.status(Response.Status.BAD_REQUEST).build(); } - String draftNoteName = draftNote.getName(); - noteService.removeDraft(draftNoteName); + noteService.removeDraftById(draftNote.getId()); return Response.ok().build(); } catch (Exception ex) { log.warn("Failed to perform Delete of noteBook note {}", noteId, ex); @@ -1497,6 +1560,59 @@ public Response getAvailableLanguages(@Context } } + @GET + @Path( "/illustration/{noteId}") + @RolesAllowed("users") + @Operation( + summary = "Gets a note featured image illustration by note Id", + description = "Gets a note featured image illustration by note Id", + method = "GET") + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), + @ApiResponse(responseCode = "500", description = "Internal server error"), + @ApiResponse(responseCode = "400", description = "Invalid query input"), + @ApiResponse(responseCode = "404", description = "Resource not found") }) + public Response getFeaturedImageIllustration(@Context Request request, + @Parameter(description = "target note id", required = true) @PathParam("noteId") Long noteId, + @Parameter(description = "target version language", required = true) @QueryParam("isDraft") boolean isDraft, + @Parameter(description = "target version language", required = true) @QueryParam("lang") String lang, + @Parameter(description = "Optional size parameter", required = true) @QueryParam("size") String size, + @Parameter(description = "Optional last modified parameter") @QueryParam("v") long lastModified) { + + if (noteId == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("note id is mandatory").build(); + } + try { + NoteFeaturedImage noteFeaturedImage = noteService.getNoteFeaturedImageInfo(noteId, + lang, + isDraft, + size, + RestUtils.getCurrentUserIdentityId()); + if (noteFeaturedImage == null) { + return Response.status(HTTPStatus.NOT_FOUND).build(); + } + Long lastUpdated = noteFeaturedImage.getLastUpdated(); + EntityTag eTag = new EntityTag(lastUpdated + noteId + lang + size, true); + Response.ResponseBuilder builder = request.evaluatePreconditions(eTag); + if (builder == null) { + InputStream stream = noteFeaturedImage.getFileInputStream(); + builder = Response.ok(stream, noteFeaturedImage.getMimeType()); + builder.tag(eTag); + if (lastModified > 0) { + builder.lastModified(new Date(lastUpdated)); + builder.expires(new Date(System.currentTimeMillis() + CACHE_DURATION_MILLISECONDS)); + builder.cacheControl(ILLUSTRATION_CACHE_CONTROL); + } + } + return builder.build(); + } catch (ObjectNotFoundException e) { + log.warn("target note not found", e); + return Response.status(Response.Status.NOT_FOUND).build(); + } catch (Exception e) { + log.error("An error occurred while getting featured image illustration", e); + return Response.serverError().build(); + } + } + private List getJsonTree(WikiPageParams params, HashMap context) throws Exception { Wiki noteBook = noteBookService.getWikiByTypeAndOwner(params.getType(), params.getOwner()); WikiTreeNode noteBookNode = new WikiTreeNode(noteBook); @@ -1565,4 +1681,15 @@ private String formatWikiOwnerToGroupId(String wikiOwner) { return wikiOwner; } + private String sanitizeAndSubstituteMentions(String content, String local) { + try { + Locale locale = local == null ? null : Locale.forLanguageTag(local); + String sanitizedBody = HTMLSanitizer.sanitize(content); + sanitizedBody = sanitizedBody.replace("@", "@"); + return MentionUtils.substituteUsernames(CommonsUtils.getCurrentPortalOwner(),sanitizedBody, locale); + } catch (Exception e) { + return content; + } + } + } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/utils/Utils.java b/notes-service/src/main/java/org/exoplatform/wiki/utils/Utils.java index c9c7fe6219..9c8e87b64e 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/utils/Utils.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/utils/Utils.java @@ -27,19 +27,33 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.map.HashedMap; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.model.PluginKey; +import org.exoplatform.commons.notification.impl.NotificationContextImpl; +import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider; +import org.exoplatform.social.core.storage.api.IdentityStorage; +import org.exoplatform.social.core.utils.MentionUtils; +import org.exoplatform.social.notification.LinkProviderUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.suigeneris.jrcs.diff.DifferentiationFailedException; @@ -149,6 +163,9 @@ public class Utils { public static final String PAGE_OWNER_KEY = "page_owner"; + public static final Pattern MENTION_PATTERN = + Pattern.compile("@([^\\s]+)|@([^\\s]+)$"); + public static String normalizeUploadedFilename(String name) { name = name.replace("%22", "\""); // Fix the bug in Chrome which a double quotes is encoded to %22 name = name.replace("\\\"", "\""); // Fix the bug in Firefox which a double quotes is escaped to \\" @@ -756,4 +773,71 @@ public static org.exoplatform.services.security.Identity getIdentity(String user } return aclIdentity; } + + public static Set processMentions(String content, Space space) { + Set mentions = new HashSet<>(); + mentions.addAll(MentionUtils.getMentionedUsernames(content)); + + if (space != null) { + IdentityStorage identityStorage = CommonsUtils.getService(IdentityStorage.class); + String spaceIdentityId = identityStorage.findIdentityId(SpaceIdentityProvider.NAME, space.getPrettyName()); + Set mentionedRoles = MentionUtils.getMentionedRoles(content, spaceIdentityId); + mentionedRoles.forEach(role -> { + if (StringUtils.equals("member", role) && space.getMembers() != null) { + mentions.addAll(Arrays.asList(space.getMembers())); + } else if (StringUtils.equals("manager", role) && space.getManagers() != null) { + mentions.addAll(Arrays.asList(space.getManagers())); + } else if (StringUtils.equals("redactor", role) && space.getRedactors() != null) { + mentions.addAll(Arrays.asList(space.getRedactors())); + } else if (StringUtils.equals("publisher", role) && space.getPublishers() != null) { + mentions.addAll(Arrays.asList(space.getPublishers())); + } + }); + } + + return mentions.stream().map(remoteId -> { + IdentityStorage identityStorage = CommonsUtils.getService(IdentityStorage.class); + + Identity identity = identityStorage.findIdentity(OrganizationIdentityProvider.NAME, remoteId); + return identity == null ? null : identity.getId(); + }).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static void sendMentionInNoteNotification(Page note, Page originalNote, String currentUser) { + SpaceService spaceService = CommonsUtils.getService(SpaceService.class); + IdentityManager identityManager = CommonsUtils.getService(IdentityManager.class); + Space space = spaceService.getSpaceByGroupId(note.getWikiOwner()); + org.exoplatform.social.core.identity.model.Identity identity = identityManager.getOrCreateUserIdentity(note.getAuthor()); + String authorAvatarUrl = LinkProviderUtils.getUserAvatarUrl(identity.getProfile()); + String authorProfileUrl = identity.getProfile().getUrl(); + Set mentionedIds = Utils.processMentions(note.getContent(), space); + if (originalNote != null) { + Set previousMentionedIds = Utils.processMentions(originalNote.getContent(), space); + mentionedIds = mentionedIds.stream().filter(id -> !previousMentionedIds.contains(id)).collect(Collectors.toSet()); + } + NotificationContext mentionNotificationCtx = + NotificationContextImpl.cloneInstance() + .append(MentionInNoteNotificationPlugin.CURRENT_USER, + currentUser) + .append(MentionInNoteNotificationPlugin.NOTE_AUTHOR, + note.getAuthor()) + .append(MentionInNoteNotificationPlugin.SPACE_ID, + space.getId()) + .append(MentionInNoteNotificationPlugin.NOTE_URL, + note.getUrl()) + .append(MentionInNoteNotificationPlugin.NOTE_TITLE, + note.getTitle()) + .append(MentionInNoteNotificationPlugin.AUTHOR_AVATAR_URL, + authorAvatarUrl) + .append(MentionInNoteNotificationPlugin.AUTHOR_PROFILE_URL, + authorProfileUrl) + .append(MentionInNoteNotificationPlugin.ACTIVITY_LINK, + note.getUrl()) + .append(MentionInNoteNotificationPlugin.MENTIONED_IDS, + mentionedIds); + mentionNotificationCtx.getNotificationExecutor() + .with(mentionNotificationCtx.makeCommand(PluginKey.key(MentionInNoteNotificationPlugin.ID))) + .execute(mentionNotificationCtx); + } + } diff --git a/notes-service/src/test/java/io/meeds/notes/notification/plugin/MentionInNoteNotificationPluginTest.java b/notes-service/src/test/java/io/meeds/notes/notification/plugin/MentionInNoteNotificationPluginTest.java new file mode 100644 index 0000000000..44cbb02543 --- /dev/null +++ b/notes-service/src/test/java/io/meeds/notes/notification/plugin/MentionInNoteNotificationPluginTest.java @@ -0,0 +1,152 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.notes.notification.plugin; + +import io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin; +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.model.NotificationInfo; +import org.exoplatform.commons.api.notification.model.PluginKey; +import org.exoplatform.commons.api.notification.service.NotificationCompletionService; +import org.exoplatform.commons.api.notification.service.storage.NotificationService; +import org.exoplatform.commons.notification.impl.NotificationContextImpl; +import org.exoplatform.commons.utils.CommonsUtils; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.services.organization.OrganizationService; +import org.exoplatform.services.organization.User; +import org.exoplatform.services.organization.UserHandler; +import org.exoplatform.social.core.identity.model.Identity; +import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; +import org.exoplatform.wiki.utils.Utils; +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class MentionInNoteNotificationPluginTest { + + @Mock + private UserHandler userhandler; + + @Mock + private InitParams initParams; + + @Mock + private OrganizationService orgService; + + @Mock + private SpaceService spaceService; + + @Mock + private PortalContainer portalContainer; + + @Mock + private IdentityManager identityManager; + + private static final MockedStatic COMMONS_UTILS = mockStatic(CommonsUtils.class); + + private static final MockedStatic SOCIAL_UTILS = mockStatic(Utils.class); + + private static final MockedStatic PLUGIN_KEY = mockStatic(PluginKey.class); + + private static final MockedStatic EXO_CONTAINER_CONTEXT = mockStatic(ExoContainerContext.class); + + private static final MockedStatic PORTAL_CONTAINER = mockStatic(PortalContainer.class); + + @AfterClass + public static void afterRunBare() throws Exception { // NOSONAR + COMMONS_UTILS.close(); + SOCIAL_UTILS.close(); + PLUGIN_KEY.close(); + EXO_CONTAINER_CONTEXT.close(); + PORTAL_CONTAINER.close(); + } + + @Test + public void testShouldMakeNotificationForMentionInNoteContext() throws Exception { + // Given + when(orgService.getUserHandler()).thenReturn(userhandler); + MentionInNoteNotificationPlugin notePlugin = new MentionInNoteNotificationPlugin(initParams); + Set mentionedIds = new HashSet<>(Collections.singleton("1")); + + COMMONS_UTILS.when(() -> CommonsUtils.getService(NotificationService.class)).thenReturn(null); + COMMONS_UTILS.when(() -> CommonsUtils.getService(NotificationCompletionService.class)).thenReturn(null); + NotificationContext ctx = + NotificationContextImpl.cloneInstance() + .append(MentionInNoteNotificationPlugin.NOTE_TITLE, "title") + .append(MentionInNoteNotificationPlugin.NOTE_AUTHOR, "root") + .append(MentionInNoteNotificationPlugin.CURRENT_USER, "root") + .append(MentionInNoteNotificationPlugin.SPACE_ID, "1") + .append(MentionInNoteNotificationPlugin.MENTIONED_IDS, mentionedIds) + .append(MentionInNoteNotificationPlugin.AUTHOR_AVATAR_URL, + "http://localhost:8080/portal/rest/v1/social/users/default-image/avatar") + .append(MentionInNoteNotificationPlugin.ACTIVITY_LINK, + "http://localhost:8080/portal/g/:spaces:space_test/space_test/notes/1"); + + User currentUser = mock(User.class); + when(userhandler.findUserByName("root")).thenReturn(currentUser); + when(currentUser.getFullName()).thenReturn("root root"); + + Space space = new Space(); + space.setId("1"); + space.setGroupId("space_test"); + + COMMONS_UTILS.when(() -> CommonsUtils.getService(OrganizationService.class)).thenReturn(orgService); + PORTAL_CONTAINER.when(PortalContainer::getInstance).thenReturn(portalContainer); + when(portalContainer.getComponentInstanceOfType(SpaceService.class)).thenReturn(spaceService); + when(portalContainer.getComponentInstanceOfType(IdentityManager.class)).thenReturn(identityManager); + Identity identity = mock(Identity.class); + when(identity.getRemoteId()).thenReturn("receiver"); + when(identityManager.getIdentity(anyString(), anyBoolean())).thenReturn(identity); + when(spaceService.isMember(any(Space.class), anyString())).thenReturn(true); + when(spaceService.getSpaceById(anyString())).thenReturn(space); + + // When + NotificationInfo notificationInfo = notePlugin.makeNotification(ctx); + + // Then + assertEquals("root", notificationInfo.getFrom()); + assertEquals("", notificationInfo.getTitle()); + assertEquals("title", notificationInfo.getValueOwnerParameter("NOTE_TITLE")); + assertEquals("root", notificationInfo.getValueOwnerParameter("NOTE_AUTHOR")); + assertEquals("http://localhost:8080/portal/rest/v1/social/users/default-image/avatar", + notificationInfo.getValueOwnerParameter("AUTHOR_AVATAR_URL")); + assertEquals("http://localhost:8080/portal/g/:spaces:space_test/space_test/notes/1", + notificationInfo.getValueOwnerParameter("ACTIVITY_LINK")); + } + +} diff --git a/notes-service/src/test/java/io/meeds/notes/notification/provider/MailTemplateProviderTest.java b/notes-service/src/test/java/io/meeds/notes/notification/provider/MailTemplateProviderTest.java new file mode 100644 index 0000000000..fe722d6a0b --- /dev/null +++ b/notes-service/src/test/java/io/meeds/notes/notification/provider/MailTemplateProviderTest.java @@ -0,0 +1,95 @@ +package io.meeds.notes.notification.provider; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +import io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin; +import io.meeds.notes.notifications.provider.MailTemplateProvider; +import org.exoplatform.commons.api.notification.NotificationContext; +import org.exoplatform.commons.api.notification.model.MessageInfo; +import org.exoplatform.commons.api.notification.model.NotificationInfo; +import org.exoplatform.commons.api.notification.model.PluginKey; +import org.exoplatform.commons.api.notification.plugin.NotificationPluginUtils; +import org.exoplatform.commons.api.notification.service.template.TemplateContext; +import org.exoplatform.commons.notification.template.TemplateUtils; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.social.core.identity.model.Identity; +import org.exoplatform.social.core.identity.model.Profile; +import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; +import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.notification.LinkProviderUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class MailTemplateProviderTest { + + private MailTemplateProvider.TemplateBuilder templateBuilder; + + @Mock + private IdentityManager identityManager; + + @Mock + private NotificationContext ctx; + + @Mock + private NotificationInfo notificationInfo; + + @Mock + private Identity receiver; + + @Mock + private Profile profile; + + private static final MockedStatic TEMPLATE_UTILS = mockStatic(TemplateUtils.class); + + private static final MockedStatic NOTIFICATION_PLUGIN_UTILS = + mockStatic(NotificationPluginUtils.class); + + private static final MockedStatic LINK_PROVIDER_UTILS = mockStatic(LinkProviderUtils.class); + + @Before + public void setUp() { + MailTemplateProvider mailTemplateProvider = new MailTemplateProvider(new InitParams(), identityManager); + templateBuilder = mailTemplateProvider.new TemplateBuilder(); + } + + @After + public void tearDown() { + TEMPLATE_UTILS.close(); + NOTIFICATION_PLUGIN_UTILS.close(); + LINK_PROVIDER_UTILS.close(); + } + + @Test + public void testMakeMessage() { + + when(ctx.getNotificationInfo()).thenReturn(notificationInfo); + when(notificationInfo.getKey()).thenReturn(new PluginKey(MentionInNoteNotificationPlugin.ID)); + when(notificationInfo.getValueOwnerParameter(anyString())).thenReturn("testValue"); + when(notificationInfo.getTo()).thenReturn("receiverId"); + when(identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, "receiverId")).thenReturn(receiver); + when(receiver.getProfile()).thenReturn(profile); + when(receiver.getRemoteId()).thenReturn("receiverId"); + when(profile.getProperty(Profile.FIRST_NAME)).thenReturn("ReceiverFirstName"); + TEMPLATE_UTILS.when(() -> TemplateUtils.processGroovy(any(TemplateContext.class))) + .thenReturn("you have been mentioned in a note"); + TEMPLATE_UTILS.when(() -> TemplateUtils.processSubject(any(TemplateContext.class))) + .thenReturn("author mentioned you in the note test"); + NOTIFICATION_PLUGIN_UTILS.when(() -> NotificationPluginUtils.getLanguage(anyString())).thenReturn("en"); + LINK_PROVIDER_UTILS.when(() -> LinkProviderUtils.getBaseUrl()).thenReturn("baseUrl"); + LINK_PROVIDER_UTILS.when(() -> LinkProviderUtils.getRedirectUrl(anyString(), anyString())) + .thenReturn("notifications/settings/redirect/url"); + MessageInfo messageInfo = templateBuilder.makeMessage(ctx); + + assertNotNull(messageInfo); + assertTrue(messageInfo.getBody().equals("you have been mentioned in a note")); + assertTrue(messageInfo.getSubject().equals("author mentioned you in the note test")); + } +} diff --git a/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java b/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java index bfdfc2fff9..c4bd2b75b1 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/jpa/JPADataStorageTest.java @@ -1164,6 +1164,28 @@ public void testDeleteOrphanDraftPagesByParentPage() throws Exception { persist(); assertNull(draftPageDAO.find(draft.getId())); } + + public void testGetVersionById() throws Exception { + // Given + Wiki wiki = new Wiki(); + wiki.setType("portal"); + wiki.setOwner("wiki1"); + wiki = storage.createWiki(wiki); + + Page page = new Page(); + page.setWikiId(wiki.getId()); + page.setWikiType(wiki.getType()); + page.setWikiOwner(wiki.getOwner()); + page.setName("testGetVersionById"); + page.setTitle("testGetVersionById"); + page.setContent("content"); + Page createdPage = storage.createPage(wiki, wiki.getWikiHome(), page); + org.exoplatform.social.core.identity.model.Identity identity = + Mockito.mock(org.exoplatform.social.core.identity.model.Identity.class); + // When + PageVersion pageVersion = storage.addPageVersion(createdPage, identity.getId()); + assertNotNull(storage.getPageVersionById(Long.parseLong(pageVersion.getId()))); + } protected void startSessionAs(String user) { startSessionAs(user, new HashSet()); diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java index f32521f8b2..b8d091bfcb 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java @@ -23,16 +23,25 @@ import static org.exoplatform.social.core.jpa.test.AbstractCoreTest.persist; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.File; +import java.lang.reflect.Field; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; +import java.util.*; +import io.meeds.notes.model.NoteFeaturedImage; +import io.meeds.notes.model.NotePageProperties; import org.apache.commons.io.FileUtils; +import org.exoplatform.commons.file.services.FileService; +import org.exoplatform.social.core.manager.IdentityManager; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; +import org.exoplatform.upload.UploadResource; +import org.exoplatform.upload.UploadService; +import org.exoplatform.wiki.model.*; import org.junit.Assert; import org.exoplatform.commons.ObjectAlreadyExistsException; @@ -44,19 +53,14 @@ import org.exoplatform.wiki.WikiException; import org.exoplatform.wiki.jpa.BaseTest; import org.exoplatform.wiki.jpa.JPADataStorage; -import org.exoplatform.wiki.model.DraftPage; -import org.exoplatform.wiki.model.NoteToExport; -import org.exoplatform.wiki.model.Page; -import org.exoplatform.wiki.model.PageHistory; -import org.exoplatform.wiki.model.Permission; -import org.exoplatform.wiki.model.PermissionEntry; -import org.exoplatform.wiki.model.PermissionType; -import org.exoplatform.wiki.model.Wiki; - -public class TestNoteService extends BaseTest { + + public class TestNoteService extends BaseTest { private WikiService wService; private NoteService noteService; private NotesExportService notesExportService; + private IdentityManager identityManager; + private FileService fileService; + private SpaceService spaceService; public TestNoteService() { setForceContainerReload(true); @@ -66,7 +70,10 @@ public void setUp() throws Exception { super.setUp() ; wService = getContainer().getComponentInstanceOfType(WikiService.class) ; noteService = getContainer().getComponentInstanceOfType(NoteService.class) ; - notesExportService = getContainer().getComponentInstanceOfType(NotesExportService.class) ; + notesExportService = getContainer().getComponentInstanceOfType(NotesExportService.class); + identityManager = getContainer().getComponentInstanceOfType(IdentityManager.class) ; + fileService = getContainer().getComponentInstanceOfType(FileService.class) ; + spaceService = getContainer().getComponentInstanceOfType(SpaceService.class) ; getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); } @@ -77,9 +84,9 @@ public void testGetGroupPageById() throws WikiException, IllegalAccessException assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.GROUP_TYPE, "platform/users", "Home")) ; - noteService.createNote(wiki, "Home", new Page("testGetGroupPageById-101", "testGetGroupPageById-101"),root); + Page notePage = noteService.createNote(wiki, "Home", new Page("testGetGroupPageById-101", "testGetGroupPageById-101"),root); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.GROUP_TYPE, "platform/users", "testGetGroupPageById-101")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.GROUP_TYPE, "platform/users", notePage.getName())) ; assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.GROUP_TYPE, "unknown", "Home")); } @@ -88,40 +95,40 @@ public void testGetUserPageById() throws WikiException, IllegalAccessException { Identity john = new Identity("john"); assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "john", "Home")) ; - noteService.createNote(wiki, "Home", new Page("testGetUserPageById-101", "testGetUserPageById-101"), john); + Page notePage = noteService.createNote(wiki, "Home", new Page("testGetUserPageById-101", "testGetUserPageById-101"), john); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "john", "testGetUserPageById-101")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "john", notePage.getName())) ; assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "unknown", "Home")); } public void testCreatePageAndSubNote() throws WikiException, IllegalAccessException { Wiki wiki = new Wiki(PortalConfig.PORTAL_TYPE, "classic"); Identity root = new Identity("root"); - noteService.createNote(wiki, "Home", new Page("parentPage_", "parentPage_"), root) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "parentPage_", root)) ; - noteService.createNote(wiki, "parentPage_", new Page("childPage_", "childPage_"),root) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "childPage_", root)) ; + Page parentNotePage = noteService.createNote(wiki, "Home", new Page("parentPage_", "parentPage_"), root) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", parentNotePage.getName(), root)) ; + Page childNotePage = noteService.createNote(wiki, parentNotePage.getName(), new Page("childPage_", "childPage_"),root) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", childNotePage.getName(), root)) ; } public void testGetBreadcumb() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); - noteService.createNote(portalWiki, "Home", new Page("Breadcumb1_", "Breadcumb1_"),root) ; - noteService.createNote(portalWiki, "Breadcumb1_", new Page("Breadcumb2_", "Breadcumb2_"),root) ; - noteService.createNote(portalWiki, "Breadcumb2_", new Page("Breadcumb3_", "Breadcumb3_"),root) ; - List breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", "Breadcumb3_", false); + Page breadcrumb1NotPage = noteService.createNote(portalWiki, "Home", new Page("Breadcrumb1_", "Breadcrumb1_"),root) ; + Page breadcrumb2NotPage = noteService.createNote(portalWiki, breadcrumb1NotPage.getName(), new Page("Breadcrumb2_", "Breadcrumb2_"),root) ; + Page breadcrumb3NotPage = noteService.createNote(portalWiki, breadcrumb2NotPage.getName(), new Page("Breadcrumb3_", "Breadcrumb3_"),root) ; + List breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", breadcrumb3NotPage.getName(), false); assertEquals(4, breadCumbs.size()); assertEquals("Home", breadCumbs.get(0).getId()); - assertEquals("Breadcumb1_", breadCumbs.get(1).getId()); - assertEquals("Breadcumb2_", breadCumbs.get(2).getId()); - assertEquals("Breadcumb3_", breadCumbs.get(3).getId()); + assertEquals(breadcrumb1NotPage.getName(), breadCumbs.get(1).getId()); + assertEquals(breadcrumb2NotPage.getName(), breadCumbs.get(2).getId()); + assertEquals(breadcrumb3NotPage.getName(), breadCumbs.get(3).getId()); } public void testGetBreadcumbWithLanguage() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); Page note1 = noteService.createNote(portalWiki, "Home", new Page("Breadcumb1", "Breadcumb1"),root) ; - Page note2 = noteService.createNote(portalWiki, "Breadcumb1", new Page("Breadcumb2", "Breadcumb2"),root) ; - Page note3 = noteService.createNote(portalWiki, "Breadcumb2", new Page("Breadcumb3", "Breadcumb3"),root) ; + Page note2 = noteService.createNote(portalWiki, note1.getName(), new Page("Breadcumb2", "Breadcumb2"),root) ; + Page note3 = noteService.createNote(portalWiki, note2.getName(), new Page("Breadcumb3", "Breadcumb3"),root) ; note1.setLang("fr"); note1.setTitle("Breadcumb1_fr"); @@ -132,13 +139,13 @@ public void testGetBreadcumbWithLanguage() throws WikiException, IllegalAccessEx note3.setLang("fr"); note3.setTitle("Breadcumb3_fr"); noteService.createVersionOfNote(note3, "root"); - List breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", "Breadcumb3", false); + List breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", note3.getName(), false); assertEquals(4, breadCumbs.size()); assertEquals("Home", breadCumbs.get(0).getId()); assertEquals("Breadcumb1", breadCumbs.get(1).getTitle()); assertEquals("Breadcumb2", breadCumbs.get(2).getTitle()); assertEquals("Breadcumb3", breadCumbs.get(3).getTitle()); - breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", "Breadcumb3", "fr", root, false); + breadCumbs = noteService.getBreadCrumb(PortalConfig.PORTAL_TYPE, "classic", note3.getName(), "fr", root, false); assertEquals(4, breadCumbs.size()); assertEquals("Home", breadCumbs.get(0).getId()); assertEquals("Breadcumb1_fr", breadCumbs.get(1).getTitle()); @@ -150,45 +157,45 @@ public void testMoveNote() throws WikiException, IllegalAccessException { //moving page in same space Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); - noteService.createNote(portalWiki, "Home", new Page("oldParent_", "oldParent_"),root) ; - noteService.createNote(portalWiki, "oldParent_", new Page("child_", "child_"),root) ; - noteService.createNote(portalWiki, "Home", new Page("newParent_", "newParent_"),root) ; + Page oldParentNotePage = noteService.createNote(portalWiki, "Home", new Page("oldParent_", "oldParent_"), root) ; + Page childNotePage = noteService.createNote(portalWiki, oldParentNotePage.getName(), new Page("child_", "child_"), root) ; + Page newsParentNotPage = noteService.createNote(portalWiki, "Home", new Page("newParent_", "newParent_"), root) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "oldParent_")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "child_")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "newParent_")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", oldParentNotePage.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", childNotePage.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", newsParentNotPage.getName())) ; WikiPageParams currentLocationParams= new WikiPageParams(); WikiPageParams newLocationParams= new WikiPageParams(); - currentLocationParams.setPageName("child_"); + currentLocationParams.setPageName(childNotePage.getName()); currentLocationParams.setType(PortalConfig.PORTAL_TYPE); currentLocationParams.setOwner("classic"); - newLocationParams.setPageName("newParent_"); + newLocationParams.setPageName(newsParentNotPage.getName()); newLocationParams.setType(PortalConfig.PORTAL_TYPE); newLocationParams.setOwner("classic"); - assertTrue(noteService.moveNote(currentLocationParams,newLocationParams,root)) ; + assertTrue(noteService.moveNote(currentLocationParams, newLocationParams, root)) ; //moving page from different spaces Wiki userWiki = getOrCreateWiki(wService, PortalConfig.USER_TYPE, "root"); - noteService.createNote(userWiki, "Home", new Page("acmePage_", "acmePage_"),root) ; - noteService.createNote(portalWiki, "Home", new Page("classicPage_", "classicPage_"),root) ; + Page acmeNotePage = noteService.createNote(userWiki, "Home", new Page("acmePage_", "acmePage_"), root) ; + Page classicNotePage = noteService.createNote(portalWiki, "Home", new Page("classicPage_", "classicPage_"), root) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "root", "acmePage_",root)) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "classicPage_",root)) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "root", acmeNotePage.getName(), root)) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", classicNotePage.getName(), root)) ; - currentLocationParams.setPageName("acmePage_"); + currentLocationParams.setPageName(acmeNotePage.getName()); currentLocationParams.setType(PortalConfig.USER_TYPE); currentLocationParams.setOwner("root"); - newLocationParams.setPageName("classicPage_"); + newLocationParams.setPageName(classicNotePage.getName()); newLocationParams.setType(PortalConfig.PORTAL_TYPE); newLocationParams.setOwner("classic"); - assertTrue(noteService.moveNote(currentLocationParams,newLocationParams,root)) ; + assertTrue(noteService.moveNote(currentLocationParams, newLocationParams, root)) ; // moving a page to another read-only page Wiki demoWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "root"); - noteService.createNote(demoWiki, "Home", new Page("toMovedPage_", "toMovedPage_"),root); - Page page = noteService.createNote(userWiki, "Home", new Page("privatePage_", "privatePage_"),root); + Page toMovedNotePage = noteService.createNote(demoWiki, "Home", new Page("toMovedPage_", "toMovedPage_"), root); + Page privateNotePage = noteService.createNote(userWiki, "Home", new Page("privatePage_", "privatePage_"), root); HashMap permissionMap = new HashMap<>(); permissionMap.put("any", new String[] {PermissionType.VIEWPAGE.toString(), PermissionType.EDITPAGE.toString()}); List permissionEntries = new ArrayList<>(); @@ -197,15 +204,15 @@ public void testMoveNote() throws WikiException, IllegalAccessException { new Permission(PermissionType.EDITPAGE, true) }); permissionEntries.add(permissionEntry); - page.setPermissions(permissionEntries); + privateNotePage.setPermissions(permissionEntries); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "root", "toMovedPage_")); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "root", "privatePage_")); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "root", toMovedNotePage.getName())); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.USER_TYPE, "root", privateNotePage.getName())); - currentLocationParams.setPageName("toMovedPage_"); + currentLocationParams.setPageName(toMovedNotePage.getName()); currentLocationParams.setType(PortalConfig.PORTAL_TYPE); currentLocationParams.setOwner("root"); - newLocationParams.setPageName("privatePage_"); + newLocationParams.setPageName(privateNotePage.getName()); newLocationParams.setType(PortalConfig.USER_TYPE); newLocationParams.setOwner("root"); } @@ -213,37 +220,37 @@ public void testMoveNote() throws WikiException, IllegalAccessException { public void testDeleteNote() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); - noteService.createNote(portalWiki, "Home", new Page("deletePage_", "deletePage_"),root) ; - assertTrue(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", "deletePage_")) ; - //wait(10) ; - noteService.createNote(portalWiki, "Home", new Page("deletePage_", "deletePage_"),root) ; - assertTrue(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", "deletePage_")) ; - assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "deletePage_")) ; - assertFalse(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", "Home")) ; + Page notePage1 = noteService.createNote(portalWiki, "Home", new Page("deletePage_", "deletePage_"), root); + assertTrue(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", notePage1.getName())); + // wait(10) ; + Page notePage2 = noteService.createNote(portalWiki, "Home", new Page("deletePage_", "deletePage_"), root); + assertTrue(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", notePage2.getName())); + assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", notePage2.getName())); + assertFalse(noteService.deleteNote(PortalConfig.PORTAL_TYPE, "classic", "Home")); } public void testRenameNote() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); - noteService.createNote(portalWiki, "Home", new Page("currentPage_", "currentPage_"),root) ; - assertTrue(noteService.renameNote(PortalConfig.PORTAL_TYPE, "classic", "currentPage_", "renamedPage_", "renamedPage_")) ; + Page currentNotePage = noteService.createNote(portalWiki, "Home", new Page("currentPage_", "currentPage_"),root); + assertTrue(noteService.renameNote(PortalConfig.PORTAL_TYPE, "classic", currentNotePage.getName(), "renamedPage_", "renamedPage_")) ; assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "renamedPage_")) ; } public void testRenamePageToExistingNote() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic"); - noteService.createNote(portalWiki, "Home", new Page("currentPage_", "currentPage_"),root) ; - noteService.createNote(portalWiki, "Home", new Page("currentPage2_", "currentPage2_"),root) ; + Page currentNotePage = noteService.createNote(portalWiki, "Home", new Page("currentPage_", "currentPage_"), root) ; + Page currentNotePage2 = noteService.createNote(portalWiki, "Home", new Page("currentPage2_", "currentPage2_"), root) ; try { - noteService.renameNote(PortalConfig.PORTAL_TYPE, "classic", "currentPage_", "currentPage2_", "renamedPage2_"); - fail("Renaming page currentPage to the existing page currentPage2_ should throw an exception"); + noteService.renameNote(PortalConfig.PORTAL_TYPE, "classic", currentNotePage.getName(), currentNotePage2.getName(), "renamedPage2_"); + fail("Renaming page currentPage to the existing page " + currentNotePage2.getName() + " should throw an exception"); } catch (WikiException e) { - assertEquals("Note portal:classic:currentPage2_ already exists, cannot rename it.", e.getMessage()); + assertEquals("Note portal:classic:" + currentNotePage2.getName() + " already exists, cannot rename it.", e.getMessage()); } - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "currentPage_")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", "currentPage2_")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", currentNotePage.getName())); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "classic", currentNotePage2.getName())) ; } public void testHasPermisionOnSystemPage() throws WikiException, IllegalAccessException, ObjectAlreadyExistsException { @@ -294,7 +301,7 @@ public void testHasPermisionOnSystemPage() throws WikiException, IllegalAccessEx () -> noteService.getNoteById(storedSystemPermissionPage.getId(), userAclIdentity)); } - public void testUpdateNote() throws WikiException, IllegalAccessException { + public void testUpdateNote() throws WikiException, IllegalAccessException, Exception { Identity root = new Identity("root"); // Get Home getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "classic").getWikiHome(); @@ -309,6 +316,10 @@ public void testUpdateNote() throws WikiException, IllegalAccessException { // update content of page page.setContent("Page content updated_"); + DraftPage draftPage = new DraftPage(); + draftPage.setContent("test"); + draftPage.setTitle("test"); + noteService.createDraftForExistPage(draftPage, page, "", new Date().getTime(), "root"); noteService.updateNote(page, PageUpdateType.EDIT_PAGE_CONTENT,root); assertNotNull(page); assertEquals("Page content updated_", page.getContent()); @@ -328,7 +339,7 @@ public void testDraftPage() throws WikiException, IllegalAccessException { draftOfNewPage.setTitle("Draft page"); draftOfNewPage.setContent("Draft page content"); long now = new Date().getTime(); - draftOfNewPage = noteService.createDraftForNewPage(new DraftPage(), now); + draftOfNewPage = noteService.createDraftForNewPage(new DraftPage(), now, 1L); assertNotNull(draftOfNewPage); String draftNameForNewPage = draftOfNewPage.getName(); assertTrue(draftOfNewPage.isNewPage()); @@ -359,7 +370,7 @@ public void testDraftPage() throws WikiException, IllegalAccessException { //Test Update draft page draftOfNewPage.setTitle("Draft page updated"); draftOfNewPage.setContent("Draft page content updated"); - DraftPage updatedDraftOfNewPage = noteService.updateDraftForNewPage(draftOfNewPage, now); + DraftPage updatedDraftOfNewPage = noteService.updateDraftForNewPage(draftOfNewPage, now, 1L); assertNotNull(updatedDraftOfNewPage); assertEquals(updatedDraftOfNewPage.getTitle(), draftOfNewPage.getTitle()); assertEquals(updatedDraftOfNewPage.getContent(), draftOfNewPage.getContent()); @@ -387,7 +398,7 @@ public void testGEtNoteById() throws WikiException, IllegalAccessException { Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); Page note1 = noteService.createNote(portalWiki, "Home", new Page("exported1", "exported1"),root) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal", "exported1")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal", note1.getName())) ; Page note = noteService.getNoteById(note1.getId(),root,""); @@ -406,21 +417,21 @@ public void testGEtNoteById() throws WikiException, IllegalAccessException { public void testGetNoteByIdAndLang() throws WikiException, IllegalAccessException { Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); - Page note1 = noteService.createNote(portalWiki, "Home", new Page("testPage", "testPage"), root) ; + Page note1 = noteService.createNote(portalWiki, "Home", new Page("testPage", "testPage"), root); note1.setLang("en"); noteService.createVersionOfNote(note1, "root"); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal", "testPage")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal", note1.getName())); - Page note = noteService.getNoteByIdAndLang(Long.valueOf(note1.getId()),root,"", "en"); + Page note = noteService.getNoteByIdAndLang(Long.valueOf(note1.getId()), root, "", "en"); assertNotNull(note); - assertEquals(note.getName(),note1.getName()); + assertEquals(note.getName(), note1.getName()); assertFalse(note.isDeleted()); noteService.deleteNote(note.getWikiType(), note.getWikiOwner(), note.getName()); - Page deletedNote = noteService.getNoteById(note1.getId(),root,""); + Page deletedNote = noteService.getNoteById(note1.getId(), root, ""); assertNotNull(deletedNote); assertTrue(deletedNote.isDeleted()); @@ -488,20 +499,19 @@ public void testExportNotes() throws Exception { Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "exportPortal"); Page page1 = new Page("exported1", "exported1"); page1.setContent("
    "); - Page note1 = noteService.createNote(portalWiki, "Home",page1,root) ; + Page note1 = noteService.createNote(portalWiki, "Home", page1, root); Page page2 = new Page("exported2", "exported2"); page2.setContent("Règles de rédaction des tutoriels "); - Page note2 = noteService.createNote(portalWiki, "Home",page2,root) ; - + Page note2 = noteService.createNote(portalWiki, "Home", page2, root); Page page3 = new Page("exported3", "exported3"); - page3.setContent("Home"); - Page note3 = noteService.createNote(portalWiki, "Home",page3,root) ; + page3.setContent("Home"); + Page note3 = noteService.createNote(portalWiki, "Home", page3, root); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", "exported1")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", "exported2")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", "exported3")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", note1.getName())); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", note2.getName())); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "exportPortal", note3.getName())); String[] notes = new String[3]; notes[0] = note1.getId(); @@ -509,9 +519,9 @@ public void testExportNotes() throws Exception { notes[2] = note3.getId(); notesExportService.startExportNotes(200231, notes, true, root); - boolean exportDone= false; - while (!exportDone){ - if(notesExportService.getStatus(200231).getStatus().equals("ZIP_CREATED")){ + boolean exportDone = false; + while (!exportDone) { + if (notesExportService.getStatus(200231).getStatus().equals("ZIP_CREATED")) { exportDone = true; } } @@ -526,9 +536,9 @@ public void testImportNotes() throws Exception { Page note2 = noteService.createNote(portalWiki, "Home", new Page("to_be_imported2", "to_be_imported2"),user) ; Page note3 = noteService.createNote(portalWiki, "Home", new Page("to_be_imported3", "to_be_imported3"),user) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", "to_be_imported1")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", "to_be_imported2")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", "to_be_imported3")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", note1.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", note2.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "importPortal", note3.getName())) ; String[] notes = new String[3]; notes[0] = note1.getId(); @@ -557,41 +567,58 @@ public void testImportNotes() throws Exception { public void testGetNotesOfWiki() throws WikiException, IllegalAccessException { Identity user = new Identity("user"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal1"); - noteService.createNote(portalWiki, "Home", new Page("to_be_imported1", "to_be_imported1"),user) ; - noteService.createNote(portalWiki, "Home", new Page("to_be_imported2", "to_be_imported2"),user) ; - noteService.createNote(portalWiki, "Home", new Page("to_be_imported3", "to_be_imported3"),user) ; + Page toBeImported1NotPage = noteService.createNote(portalWiki, "Home", new Page("to_be_imported1", "to_be_imported1"),user) ; + Page toBeImported2NotPage = noteService.createNote(portalWiki, "Home", new Page("to_be_imported2", "to_be_imported2"),user) ; + Page toBeImported3NotPage = noteService.createNote(portalWiki, "Home", new Page("to_be_imported3", "to_be_imported3"),user) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", "to_be_imported1")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", "to_be_imported2")) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", "to_be_imported3")) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", toBeImported1NotPage.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", toBeImported2NotPage.getName())) ; + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal1", toBeImported3NotPage.getName())) ; List pages = noteService.getNotesOfWiki(portalWiki.getType(),portalWiki.getOwner()); assertEquals(pages.size(),4); } - public void testDeleteNote1() throws WikiException, IllegalAccessException { + public void testDeleteNote1() throws Exception { Identity user = new Identity("user"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal2"); - noteService.createNote(portalWiki, "Home", new Page("note1", "note1"),user) ; - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal2", "note1")) ; - noteService.deleteNote(PortalConfig.PORTAL_TYPE, "testPortal2", "note1",user); - - assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal2", "note1")) ; + Page notePage = noteService.createNote(portalWiki, "Home", new Page("note1", "note1"), user); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal2", notePage.getName())); + noteService.deleteNote(PortalConfig.PORTAL_TYPE, "testPortal2", notePage.getName(), user); + assertNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal2", notePage.getName())); } - public void testDeleteVersionsByNoteIdAndLang() throws WikiException, IllegalAccessException { + public void testDeleteVersionsByNoteIdAndLang() throws Exception { Identity root = new Identity("root"); - Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + org.exoplatform.social.core.identity.model.Identity identity = identityManager.getOrCreateUserIdentity("root"); + Space space = new Space(); + space.setDisplayName("test2"); + space.setPrettyName("test2"); + space.setRegistration(Space.OPEN); + space.setVisibility(Space.PUBLIC); + space = spaceService.createSpace(space, "root"); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, space.getGroupId()); Page note1 = noteService.createNote(portalWiki, "Home", new Page("testPage1", "testPage1"), root); - assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, "testPortal", "testPage1")); + assertNotNull(noteService.getNoteOfNoteBookByName(PortalConfig.PORTAL_TYPE, space.getGroupId(), note1.getName())); note1 = noteService.getNoteById(note1.getId()); note1.setLang("en"); note1.setTitle("englishTitle"); noteService.createVersionOfNote(note1, "root"); note1.setLang("fr"); note1.setTitle("frenchTitle"); + + // Check delete properties + this.bindMockedUploadService(); + NotePageProperties notePageProperties = createNotePageProperties(Long.parseLong(note1.getId()), "alt text", "summary test"); + note1.setProperties(notePageProperties); noteService.createVersionOfNote(note1, "root"); + NoteFeaturedImage featuredImage = noteService.getNoteFeaturedImageInfo(Long.valueOf(note1.getId()), + "fr", + false, + null, + Long.parseLong(identity.getId())); + assertNotNull(featuredImage); noteService.deleteVersionsByNoteIdAndLang(Long.valueOf(note1.getId()), "en"); Page note = noteService.getNoteByIdAndLang(Long.valueOf(note1.getId()), root, "", "en"); assertEquals(note.getTitle(), "testPage1"); @@ -599,6 +626,7 @@ public void testDeleteVersionsByNoteIdAndLang() throws WikiException, IllegalAcc assertNotNull(note); assertEquals(note.getTitle(), "frenchTitle"); noteService.deleteVersionsByNoteIdAndLang(Long.valueOf(note.getId()), "fr"); + assertTrue(fileService.getFile(featuredImage.getId()).getFileInfo().isDeleted()); note = noteService.getNoteByIdAndLang(Long.valueOf(note1.getId()), root, "", "fr"); assertEquals(note.getTitle(), "testPage1"); noteService.deleteNote(note1.getWikiType(), note1.getWikiOwner(), note1.getName()); @@ -626,7 +654,7 @@ public void testGetChildrenNoteOf() throws WikiException, IllegalAccessException assertEquals(eXportCildren,childern); } - public void testRemoveDraftOfNote() throws WikiException, IllegalAccessException { + public void testRemoveDraftOfNote() throws Exception { Identity root = new Identity("root"); startSessionAs("root"); long now = new Date().getTime(); @@ -689,10 +717,256 @@ public void testRemoveOrphanDraftPagesByParentPage() throws Exception { DraftPage draft = new DraftPage(); draft.setParentPageId(homePage.getId()); draft.setTargetPageId(null); - draft = noteService.createDraftForNewPage(draft, new Date().getTime()); + draft = noteService.createDraftForNewPage(draft, new Date().getTime(), 1L); assertNotNull(draft); noteService.removeOrphanDraftPagesByParentPage(Long.parseLong(homePage.getId())); persist(); assertNull(noteService.getDraftNoteById(draft.getId(), "root")); } -} + + private Page createTestNoteWithVersionLang(String name, String lang, Identity user) throws Exception { + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + Page note = noteService.createNote(portalWiki, "Home", new Page(name, name), user); + note.setLang(lang); + note.setTitle("language title"); + note.setContent("language content"); + noteService.createVersionOfNote(note, user.getUserId()); + return note; + } + + private void bindMockedUploadService() throws Exception { + UploadService uploadService = mock(UploadService.class); + UploadResource uploadResource = mock(UploadResource.class); + when(uploadResource.getUploadedSize()).thenReturn(12548d); + when(uploadService.getUploadResource(anyString())).thenReturn(uploadResource); + String location = getClass().getResource("/images/John.png").getPath(); + when(uploadResource.getStoreLocation()).thenReturn(location); + Field field = noteService.getClass().getDeclaredField("uploadService"); + field.setAccessible(true); + field.set(noteService, uploadService); + } + + private NotePageProperties createNotePageProperties(long noteId, String altText, String summary) { + NotePageProperties notePageProperties = new NotePageProperties(); + NoteFeaturedImage featuredImage = new NoteFeaturedImage(); + featuredImage.setMimeType("image/png"); + featuredImage.setUploadId("123"); + featuredImage.setAltText(altText); + notePageProperties.setFeaturedImage(featuredImage); + notePageProperties.setNoteId(noteId); + notePageProperties.setSummary(summary); + return notePageProperties; + } + + public void testSaveNoteFeaturedImage() throws Exception { + Identity user = new Identity("user"); + Page note = createTestNoteWithVersionLang("testMetadata", "en", user); + + this.bindMockedUploadService(); + + NotePageProperties notePageProperties = createNotePageProperties(Long.parseLong(note.getId()), "alt text", "summary test"); + NotePageProperties properties = noteService.saveNoteMetadata(notePageProperties, null, 1L); + assertEquals("summary test", properties.getSummary()); + + notePageProperties.setSummary("version language summary"); + properties = noteService.saveNoteMetadata(notePageProperties, "en", 1L); + assertEquals("version language summary", properties.getSummary()); + } + + public void testRemoveNoteFeaturedImage() throws Exception { + Identity user = new Identity("user"); + Page note = createTestNoteWithVersionLang("testMetadataRemove", "fr", user); + + this.bindMockedUploadService(); + + NotePageProperties notePageProperties = createNotePageProperties(Long.parseLong(note.getId()), "alt text", "summary test"); + NotePageProperties properties = noteService.saveNoteMetadata(notePageProperties, null, 1L); + noteService.saveNoteMetadata(notePageProperties, "fr", 1L); + + assertNotNull(properties.getFeaturedImage().getId()); + assertNotNull(noteService.getNoteFeaturedImageInfo(Long.parseLong(note.getId()), null, false, null, 1L)); + + noteService.removeNoteFeaturedImage(Long.parseLong(note.getId()), + properties.getFeaturedImage().getId(), + null, + false, + 1L); + + NoteFeaturedImage savedFeaturedImage = noteService.getNoteFeaturedImageInfo(Long.parseLong(note.getId()), + null, + false, + null, + 1L); + assertNull(savedFeaturedImage); + + assertNotNull(noteService.getNoteFeaturedImageInfo(Long.parseLong(note.getId()), "fr", false, null, 1L)); + } + + public void testGetNoteFeaturedImageInfo() throws Exception { + Identity user = new Identity("user"); + Page note = createTestNoteWithVersionLang("testGetMetadataInfo", "ar", user); + + this.bindMockedUploadService(); + + NotePageProperties notePageProperties = createNotePageProperties(Long.parseLong(note.getId()), "alt text", "summary Test"); + noteService.saveNoteMetadata(notePageProperties, null, 1L); + notePageProperties = createNotePageProperties(Long.parseLong(note.getId()), "alt text", "summary Test"); + noteService.saveNoteMetadata(notePageProperties, "ar", 1L); + NoteFeaturedImage featuredImage = noteService.getNoteFeaturedImageInfo(Long.parseLong(note.getId()), null, false, "150x150", 1L); + NoteFeaturedImage versionLanguageFeaturedImage = noteService.getNoteFeaturedImageInfo(Long.parseLong(note.getId()), "ar", false, "150x150", 1L); + + assertNotNull(featuredImage); + assertTrue(featuredImage.getLastUpdated() > 0L); + assertNotSame(featuredImage.getId(), versionLanguageFeaturedImage.getId()); + } + + public void testCreatePageWithProperties() throws Exception { + Identity user = new Identity("user"); + this.bindMockedUploadService(); + NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); + DraftPage draftPage = new DraftPage(); + draftPage.setTitle("test"); + draftPage.setContent("test"); + draftPage.setProperties(notePageProperties); + draftPage = noteService.createDraftForNewPage(draftPage, new Date().getTime(), 1L); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + + // case save properties of new page from new page draft + Page page = new Page(); + page.setTitle("testSaveProperties1"); + page.setName("testSaveProperties1"); + page.setContent("test"); + page.setProperties(draftPage.getProperties()); + Page note = noteService.createNote(portalWiki, "Home", page , user); + assertNotNull(note); + assertNotNull(note.getProperties()); + + notePageProperties.setFeaturedImage(null); + page.setTitle("testSaveProperties2"); + page.setName("testSaveProperties2"); + page.setProperties(notePageProperties); + note = noteService.createNote(portalWiki, "Home", page , user); + assertNotNull(note); + assertNotNull(note.getProperties()); + } + + public void testCreateDraftForNewPageWithProperties() throws Exception { + Identity user = new Identity("user"); + this.bindMockedUploadService(); + NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); + DraftPage draftPage = new DraftPage(); + draftPage.setTitle("test"); + draftPage.setContent("test"); + draftPage.setProperties(notePageProperties); + draftPage = noteService.createDraftForNewPage(draftPage, new Date().getTime(), 1L); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + assertNotNull(draftPage); + assertNotNull(draftPage.getProperties()); + } + + public void testCreateDraftForExistPageWithProperties() throws Exception { + Identity user = new Identity("user"); + this.bindMockedUploadService(); + NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); + DraftPage draftPage = new DraftPage(); + draftPage.setTitle("test"); + draftPage.setContent("test"); + draftPage.setProperties(notePageProperties); + Page page = new Page(); + page.setTitle("testSaveProperties3"); + page.setName("testSaveProperties3"); + page.setContent("test"); + page.setProperties(draftPage.getProperties()); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + Page note = noteService.createNote(portalWiki, "Home", page , user); + // Case copy from target page + draftPage = noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + assertNotNull(draftPage); + assertNotNull(draftPage.getProperties()); + + // Case save from the draft page + draftPage.setId("0"); + notePageProperties.getFeaturedImage().setId(0L); + draftPage.setProperties(notePageProperties); + draftPage = noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + assertNotNull(draftPage); + assertNotNull(draftPage.getProperties()); + } + + public void testGetVersionById() throws Exception { + Identity user = new Identity("user"); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); + Page note = noteService.createNote(portalWiki, "Home", new Page("testGetVersionById", "testGetVersionById"), user); + note.setLang("en"); + note.setTitle("english title"); + note.setContent("english content"); + noteService.createVersionOfNote(note, user.getUserId()); + PageVersion pageVersion = noteService.getPublishedVersionByPageIdAndLang(Long.valueOf(note.getId()), "en"); + assertNotNull(noteService.getPageVersionById(Long.valueOf(pageVersion.getId()))); + } + + public void testFeaturedImageWhenRemoveDraftById() throws Exception { + startSessionAs("root"); + identityManager.getOrCreateUserIdentity("root"); + Space space = new Space(); + space.setDisplayName("test"); + space.setPrettyName("test"); + space.setRegistration(Space.OPEN); + space.setVisibility(Space.PUBLIC); + space = spaceService.createSpace(space, "root"); + Identity user = new Identity("root"); + this.bindMockedUploadService(); + NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); + notePageProperties.setDraft(true); + DraftPage draftPage = new DraftPage(); + draftPage.setTitle("test"); + draftPage.setContent("test"); + draftPage.setWikiOwner(space.getGroupId()); + draftPage.setProperties(notePageProperties); + + // Draft not related to page + draftPage = noteService.createDraftForNewPage(draftPage, new Date().getTime(), 1L); + Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, space.getGroupId()); + NoteFeaturedImage featuredImage = + noteService.getNoteFeaturedImageInfo(Long.valueOf(draftPage.getId()), null, true, null, 1L); + assertNotNull(featuredImage); + noteService.removeDraftById(draftPage.getId()); + assertTrue(fileService.getFile(featuredImage.getId()).getFileInfo().isDeleted()); + + // Draft related to page + // Once deleted we should be aware of illustration assigned to parent page or not + Page page = new Page(); + page.setTitle("testRemoveImageWhenDraftRemoved"); + page.setName("testRemoveImageWhenDraftRemoved"); + page.setContent("testRemoveImageWhenDraftRemoved"); + page.setWikiOwner(space.getGroupId()); + + notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); + page.setProperties(notePageProperties); + Page note = noteService.createNote(portalWiki, "Home", page , user); + + + // Draft has same associated file with parent, it should not be deleted + draftPage.setTargetPageId(note.getId()); + draftPage.setId(null); + notePageProperties = note.getProperties(); + notePageProperties.setDraft(true); + draftPage.setProperties(notePageProperties); + draftPage = noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + featuredImage = noteService.getNoteFeaturedImageInfo(Long.valueOf(draftPage.getId()), null, true, null, 1L); + assertNotNull(featuredImage); + noteService.removeDraftById(draftPage.getId()); + assertFalse(fileService.getFile(featuredImage.getId()).getFileInfo().isDeleted()); + + // Draft has different associated file, it should be deleted + draftPage = noteService.createDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + notePageProperties = createNotePageProperties(Long.parseLong(draftPage.getId()), "alt text", "summary Test"); + notePageProperties.setDraft(true); + draftPage.setProperties(notePageProperties); + draftPage = noteService.updateDraftForExistPage(draftPage, note, null, new Date().getTime(), "root"); + featuredImage = noteService.getNoteFeaturedImageInfo(Long.valueOf(draftPage.getId()), null, true, null, 1L); + assertNotNull(featuredImage); + noteService.removeDraftById(draftPage.getId()); + assertTrue(fileService.getFile(featuredImage.getId()).getFileInfo().isDeleted()); + } + } diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java index 421baad47a..02a5566e1e 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java @@ -27,20 +27,21 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.Deque; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; +import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import jakarta.persistence.EntityManager; -import org.exoplatform.social.core.manager.IdentityManager; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,7 @@ import org.mockito.MockedStatic; import org.mockito.junit.MockitoJUnitRunner; +import org.exoplatform.commons.exception.ObjectNotFoundException; import org.exoplatform.commons.utils.PageList; import org.exoplatform.component.test.AbstractKernelTest; import org.exoplatform.container.ExoContainer; @@ -57,6 +59,7 @@ import org.exoplatform.services.rest.impl.EnvironmentContext; import org.exoplatform.services.security.ConversationState; import org.exoplatform.services.security.Identity; +import org.exoplatform.social.core.manager.IdentityManager; import org.exoplatform.social.core.space.model.Space; import org.exoplatform.social.core.space.spi.SpaceService; import org.exoplatform.social.rest.api.EntityBuilder; @@ -79,6 +82,7 @@ import org.exoplatform.wiki.utils.NoteConstants; import org.exoplatform.wiki.utils.Utils; +import io.meeds.notes.model.NoteFeaturedImage; import jakarta.servlet.http.HttpServletRequest; @RunWith(MockitoJUnitRunner.Silent.class) @@ -443,4 +447,29 @@ public void testSearchData() throws Exception { Response response = notesRestService.searchData(uriInfo, "test", 10, "wikiType", "wikiOwner", true, new ArrayList<>()); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); } + + @Test + public void getFeaturedImageIllustration() throws Exception { + Request request = mock(Request.class); + Response response = notesRestService.getFeaturedImageIllustration(request, null, false, null, null,0L); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + REST_UTILS.when(RestUtils::getCurrentUserIdentityId).thenReturn(1L); + + NoteFeaturedImage noteFeaturedImage = new NoteFeaturedImage(); + noteFeaturedImage.setId(123L); + noteFeaturedImage.setLastUpdated(new Date().getTime()); + noteFeaturedImage.setMimeType("image/png"); + when(noteService.getNoteFeaturedImageInfo(1L, null, false, "150x150", 1L)).thenReturn(noteFeaturedImage); + response = notesRestService.getFeaturedImageIllustration(request, 1L, false, null, "150x150", 12359547L); + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + + when(noteService.getNoteFeaturedImageInfo(1L, null, false, "150x150", 1L)).thenThrow(new ObjectNotFoundException("note not found")); + response = notesRestService.getFeaturedImageIllustration(request, 1L, false, null, "150x150", 12359547L); + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + + reset(noteService); + when(noteService.getNoteFeaturedImageInfo(1L, null, false, "150x150", 1L)).thenThrow(new RuntimeException()); + response = notesRestService.getFeaturedImageIllustration(request, 1L, false, null, "150x150", 12359547L); + assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); + } } diff --git a/notes-service/src/test/resources/conf/metadata-configuration.xml b/notes-service/src/test/resources/conf/metadata-configuration.xml new file mode 100644 index 0000000000..03b60acf50 --- /dev/null +++ b/notes-service/src/test/resources/conf/metadata-configuration.xml @@ -0,0 +1,47 @@ + + + + + org.exoplatform.social.metadata.MetadataService + + NotesMetadataTypePlugin + addMetadataTypePlugin + org.exoplatform.social.metadata.MetadataTypePlugin + + + metadataType + + + 1001 + + + notes + + + + + + + diff --git a/notes-service/src/test/resources/conf/portal/configuration.xml b/notes-service/src/test/resources/conf/portal/configuration.xml index ac52517066..15da04530f 100644 --- a/notes-service/src/test/resources/conf/portal/configuration.xml +++ b/notes-service/src/test/resources/conf/portal/configuration.xml @@ -24,5 +24,6 @@ xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"> jar:/conf/notes-service-configuration.xml + jar:/conf/metadata-configuration.xml - \ No newline at end of file + diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ar.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ar.properties index be9f4f7c51..9239d5b50c 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ar.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ar.properties @@ -47,7 +47,6 @@ notes.button.loadMore=تحميل المزيد notes.label.drop.draft=حذفها notes.composer.createNotes=Create notes.edit.editNotes=تعديل الملاحظة -notes.edit.editTextFor=تحرير مربع النص {0} popup.confirm=تأكيد popup.msg.confirmation=تأكيد diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_az.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_az.properties index 3993ad0600..6cab362b1d 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_az.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_az.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Təsdiqlə popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ca.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ca.properties index b9f0a12d96..c0ca449312 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ca.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ca.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirma popup.msg.confirmation=Confirmació diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ceb.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ceb.properties index 202ebd6913..b8c31dc9bd 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ceb.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ceb.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Ang pagkompirma diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_co.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_co.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_co.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_co.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_cs.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_cs.properties index ab4095e55e..f32cffaaed 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_cs.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_cs.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Potvrdit popup.msg.confirmation=Potvrzení diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_de.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_de.properties index 8df2716a51..e98702167b 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_de.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_de.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Mehr laden notes.label.drop.draft=Ablegen notes.composer.createNotes=Create notes.edit.editNotes=Notiz bearbeiten -notes.edit.editTextFor=Text für {0} bearbeiten popup.confirm=Bestätigen popup.msg.confirmation=Bestätigung diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_el.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_el.properties index d084a46a0d..bb2169dbb2 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_el.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_el.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Επιβεβαίωση popup.msg.confirmation=Επιβεβαίωση diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_en.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_en.properties index fff1d4211f..91cd4959ca 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_en.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_en.properties @@ -1,5 +1,6 @@ #Portlet notes notes.title.placeholderContentInput=Title +notes.title.max.length.warning.message=The title cannot be longer than {0} chars notes.body.placeholderContentInput=Body notes.label.noteHome=Home notes.label.addPage=Add Page @@ -46,8 +47,23 @@ notes.button.ok=Move notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create -notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} +notes.edit.editNotes=Edit +notes.save.success.message=Note saved successfully +notes.view.label=View + +notes.metadata.properties.label=Properties +notes.metadata.featuredImage.label=Featured images +notes.metadata.featuredImage.add.label=Add an image +notes.metadata.featuredImage.alt=Featured image +notes.metadata.summary.label=Summary +notes.metadata.add.summary.placeholder=Write a text to summarize the content +notes.metadata.featuredImage.edit.label=Edit The Featured Image +notes.metadata.saved.success.message=Note properties saved successfully +notes.metadata.saved.success.error=Error while saving note properties +notes.featuredImage.remove.error.message=Error while removing featured image +notes.featuredImage.remove.success.message=Featured image removed successfully +notes.featuredImage.size.error.message=Featured image size should be less or equals to 20MB +notes.metadata.close.drawer=Close the drawer popup.confirm=Confirm popup.msg.confirmation=Confirmation @@ -186,3 +202,18 @@ SpaceSettings.application.wiki.description=Notes Portlet SpaceSettings.application.notes.application.title=Notes SpaceSettings.application.notes.application.description=Notes Application + +############################################################################# +#Notification +############################################################################# + +UINotification.label.mentionInNote=Mentions in note +UINotification.label.note=Notes Notifications +note.notification.mention.in.note.description=You have been mentioned in a note: +Notification.label.types.notes=Notes +UINotification.title.MentionInNoteNotificationPlugin=Someone mentions me in a note +notes.notification.label.footer=If you do not want to receive such notifications, click here to change your notification settings. +notes.notification.button.goToNote.label=Go to note +notes.notification.title.mention.in.note=You have been mentioned in a note "{0}" +Notification.label.SayHello=Hi +Notification.subject.MentionInNoteNotificationPlugin=$CURRENT_USER mentioned you in the note $NOTE_TITLE diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_es_ES.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_es_ES.properties index 8c8a8ed19d..62ff207049 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_es_ES.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_es_ES.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Cargar más notes.label.drop.draft=¡Suéltalo! notes.composer.createNotes=Create notes.edit.editNotes=Editar nota -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirmar popup.msg.confirmation=Confirmación diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_eu.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_eu.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_eu.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_eu.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fa.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fa.properties index 483f2feec1..bc291940eb 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fa.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fa.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=تایید popup.msg.confirmation=تاییدیه diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fi.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fi.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fi.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fi.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fil.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fil.properties index bd3e7aaa6e..a028822dce 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fil.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fil.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Kumpirmahin popup.msg.confirmation=Ang Kumpirmasyon diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fr.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fr.properties index 5587042c2c..cf1220d993 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fr.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_fr.properties @@ -1,5 +1,6 @@ #Portlet notes notes.title.placeholderContentInput=Titre +notes.title.max.length.warning.message=Le titre est limité à {0} caractères notes.body.placeholderContentInput=Contenu notes.label.noteHome=Accueil notes.label.addPage=Ajouter @@ -45,9 +46,9 @@ notes.button.updateAndPost=Publier notes.button.ok=Move notes.button.loadMore=Charger plus notes.label.drop.draft=Abandonner + notes.composer.createNotes=Créer notes.edit.editNotes=Une de mes notes est modifiée -notes.edit.editTextFor=Éditer le champ texte {0} popup.confirm=Confirmer popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hi.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hi.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hi.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hi.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hu.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hu.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hu.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_hu.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_id.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_id.properties index e94f00e541..50be0cd79a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_id.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_id.properties @@ -47,7 +47,6 @@ notes.button.loadMore=muat lebih banyak notes.label.drop.draft=Lepaskan notes.composer.createNotes=Create notes.edit.editNotes=Edit Catatan -notes.edit.editTextFor=Edit text box {0} popup.confirm=Konfirmasi popup.msg.confirmation=Konfirmasi diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_it.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_it.properties index a102fea487..51cc39ab2f 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_it.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_it.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Carica più notes.label.drop.draft=Rilasciarlo notes.composer.createNotes=Create notes.edit.editNotes=Modificare la nota -notes.edit.editTextFor=Modifichi la casella di testo {0} popup.confirm=Confermare popup.msg.confirmation=Conferma diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ja.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ja.properties index e848c6e78d..d9b1aec144 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ja.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ja.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=確認 popup.msg.confirmation=確認 diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ko.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ko.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ko.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ko.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_lt.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_lt.properties index 4605df7115..e361a58c1a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_lt.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_lt.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Patvirtinti popup.msg.confirmation=Patvirtinimas diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ms.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ms.properties index bf4b01f3d1..08c0a41382 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ms.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ms.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Mengesahkan popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_nl.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_nl.properties index 65d58326d5..e7f7bc5232 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_nl.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_nl.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Bevestig popup.msg.confirmation=Bevestiging diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_no.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_no.properties index 50397148e5..df5a9c89a5 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_no.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_no.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Bekreft popup.msg.confirmation=Bekreftelse diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pcm.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pcm.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pcm.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pcm.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pl.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pl.properties index ba9bb4a4a1..f649073c6b 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pl.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pl.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Potwierdź popup.msg.confirmation=Potwierdź diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_BR.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_BR.properties index 7d0aafadd0..b95c0cfa6c 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_BR.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_BR.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Editar nota -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirmar popup.msg.confirmation=Confirmação diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_PT.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_PT.properties index 5910d2800e..09f826dde0 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_PT.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_pt_PT.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirmar popup.msg.confirmation=Confirmação diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ro.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ro.properties index f2e01ccaa1..c9bd3d0366 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ro.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ro.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirmă popup.msg.confirmation=Confirmare diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ru.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ru.properties index 5c4546d88c..b800e1f2f4 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ru.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ru.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Подтвердить popup.msg.confirmation=Подтверждение diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sk.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sk.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sk.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sk.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sl.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sl.properties index 7d1316d998..36ac4f904f 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sl.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sl.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Naloži več notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Potrdi popup.msg.confirmation=Potrditev diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sq.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sq.properties index e3a87522a3..9e38759b9d 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sq.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sq.properties @@ -47,7 +47,6 @@ notes.button.loadMore=crwdns44680:0crwdne44680:0 notes.label.drop.draft=crwdns44682:0crwdne44682:0 notes.composer.createNotes=crwdns44684:0crwdne44684:0 notes.edit.editNotes=crwdns44686:0crwdne44686:0 -notes.edit.editTextFor=crwdns44688:0{0}crwdne44688:0 popup.confirm=crwdns44690:0crwdne44690:0 popup.msg.confirmation=crwdns44692:0crwdne44692:0 diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sv_SE.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sv_SE.properties index 90cab2a1ce..6b1648167c 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sv_SE.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_sv_SE.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Bekräfta popup.msg.confirmation=Bekräfta diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_th.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_th.properties index fff1d4211f..d9db17394a 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_th.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_th.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Confirm popup.msg.confirmation=Confirmation diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tl.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tl.properties index b6182cf218..40f9d9daa6 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tl.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tl.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Kumpirmahin popup.msg.confirmation=Ang kumpermasyon diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tr.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tr.properties index cd33376e5e..b15e2e44a4 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tr.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_tr.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Daha fazla yükle notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Onayla popup.msg.confirmation=Onay diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_uk.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_uk.properties index 89e9eee222..3f3c5d47af 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_uk.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_uk.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Завантажити ще notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Підтвердити popup.msg.confirmation=Повторіть пароль diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ur_IN.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ur_IN.properties index 1f02f9554c..f78fd7f655 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ur_IN.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_ur_IN.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=تصدیق کریں popup.msg.confirmation=تصدیق diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_vi.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_vi.properties index 8be6e33803..f0a90e7b7d 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_vi.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_vi.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=Xác nhận popup.msg.confirmation=Xác nhận diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_CN.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_CN.properties index fc405b1dac..bb667f5e45 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_CN.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_CN.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=确认 popup.msg.confirmation=确认 diff --git a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_TW.properties b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_TW.properties index 99c2553236..ead31135a8 100644 --- a/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_TW.properties +++ b/notes-webapp/src/main/resources/locale/portlet/notes/notesPortlet_zh_TW.properties @@ -47,7 +47,6 @@ notes.button.loadMore=Load more notes.label.drop.draft=Drop it notes.composer.createNotes=Create notes.edit.editNotes=Edit note -notes.edit.editTextFor=Edit text box {0} popup.confirm=確認 popup.msg.confirmation=確認 diff --git a/notes-webapp/src/main/webapp/WEB-INF/conf/configuration.xml b/notes-webapp/src/main/webapp/WEB-INF/conf/configuration.xml index 182a5bb781..e5c8abd60f 100644 --- a/notes-webapp/src/main/webapp/WEB-INF/conf/configuration.xml +++ b/notes-webapp/src/main/webapp/WEB-INF/conf/configuration.xml @@ -32,5 +32,6 @@ war:/conf/wiki/search-configuration.xml war:/conf/wiki/ckeditor-configuration.xml war:/conf/wiki/metadata-configuration.xml + war:/conf/wiki/notification-configuration.xml diff --git a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js index 30afb36462..ba6289729c 100644 --- a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js +++ b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js @@ -55,7 +55,23 @@ CKEDITOR.editorConfig = function (config) { items: blocksToolbarGroup }, ]; - let extraPlugins = `a11ychecker,balloonpanel,indent,indentblock,indentlist,codesnippet,sharedspace,copyformatting,table,tabletools,embedsemantic,autolink,colordialog${!webPageNote && ',tagSuggester' || ''},emoji,link,font,justify,widget,${!webPageNote && ',insertOptions' || ''},contextmenu,tabletools,tableresize,toc,linkBalloon`; + const mobileToolbar = [ + { + name: 'basicstyles', + groups: ['basicstyles', 'cleanup'], + items: ['Bold', 'Italic'] + }, + { + name: 'paragraph', + groups: ['list'], + items: ['BulletedList','NumberedList'], + }, + { + name: 'blocks', + items: ['Blockquote'] + }, + ]; + let extraPlugins = `a11ychecker,balloonpanel,indent,indentblock,indentlist,codesnippet,sharedspace,copyformatting,table,tabletools,embedsemantic,autolink,colordialog${!webPageNote && ',tagSuggester' || ''},emoji,link,font,justify,widget,${!webPageNote && ',insertOptions' || ''},contextmenu,tabletools,tableresize,toc,linkBalloon,suggester`; let removePlugins = `image,confirmBeforeReload,maximize,resize,autoembed${webPageNote && ',tagSuggester' || ''}`; require(['SHARED/extensionRegistry'], function(extensionRegistry) { @@ -86,9 +102,13 @@ CKEDITOR.editorConfig = function (config) { } }); + if (window.innerWidth < 600) { + config.toolbar = mobileToolbar; + } else { + config.toolbar = toolbar; + } config.extraPlugins = extraPlugins; config.removePlugins = removePlugins; - config.toolbar = toolbar; config.toolbarGroups = [ { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, { name: 'paragraph', groups: ['align', 'list', 'indent', ] }, @@ -99,4 +119,4 @@ CKEDITOR.editorConfig = function (config) { config.autoGrow_minHeight = 500; config.height = 'auto' config.format_tags = 'p;h1;h2;h3'; -}; \ No newline at end of file +}; diff --git a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/metadata-configuration.xml b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/metadata-configuration.xml index 288accb8bf..8bf6899d14 100644 --- a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/metadata-configuration.xml +++ b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/metadata-configuration.xml @@ -46,4 +46,25 @@ - \ No newline at end of file + + org.exoplatform.social.metadata.MetadataService + + NotesMetadataTypePlugin + addMetadataTypePlugin + org.exoplatform.social.metadata.MetadataTypePlugin + + + metadataType + + + 1001 + + + notes + + + + + + + diff --git a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/notification-configuration.xml b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/notification-configuration.xml new file mode 100644 index 0000000000..9b211431a0 --- /dev/null +++ b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/notification-configuration.xml @@ -0,0 +1,106 @@ + + + + org.exoplatform.commons.api.notification.service.setting.PluginSettingService + + + notification.groups + registerGroupConfig + org.exoplatform.commons.api.notification.plugin.GroupProviderPlugin + Initial the default groups. + + + notes + The information of Notification group : componentUpdateState + + + notes + + + UINotification.label.note + + + 120 + + + + + + + + + org.exoplatform.commons.api.notification.service.setting.PluginContainer + + notification.plugins + addPlugin + io.meeds.notes.notifications.plugin.MentionInNoteNotificationPlugin + Initial information for plugin MentionInNoteNotificationPlugin + + + template.MentionInNoteNotificationPlugin + The template for the plugin of the state updates of notes + + + MentionInNoteNotificationPlugin + + + UINotification.label.mentionInNote + + + 10 + + + + + daily + + + Instantly + + + + + notes + + + locale.portlet.notes.notesPortlet + + + false + + + + + + + + + org.exoplatform.commons.api.notification.channel.ChannelManager + + mail.channel.notes.template + registerTemplateProvider + io.meeds.notes.notifications.provider.MailTemplateProvider + + + channel-id + MAIL_CHANNEL + + + + + web.channel.notes.template + registerTemplateProvider + io.meeds.notes.notifications.provider.PushTemplateProvider + + + channel-id + PUSH_CHANNEL + + + + + + \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/WEB-INF/gatein-resources.xml b/notes-webapp/src/main/webapp/WEB-INF/gatein-resources.xml index 263abdc559..521ad8296d 100644 --- a/notes-webapp/src/main/webapp/WEB-INF/gatein-resources.xml +++ b/notes-webapp/src/main/webapp/WEB-INF/gatein-resources.xml @@ -79,14 +79,50 @@ + + NotesRichEditor + NotesEditorGRP + + + commonVueComponents + + + extensionRegistry + + + vue + + + vuetify + + + commons-editor + editor + + + eXoVueI18n + + + imageCropper + + + jquery + $ + + + NotesEditor - NotesEditorGRP + + NotesRichEditor + commonVueComponents @@ -99,10 +135,6 @@ vuetify - - commons-editor - editor - eXoVueI18n @@ -274,5 +306,25 @@ commonVueComponents - + + + notesNotificationExtension + notificationGRP + + + vue + + + vuetify + + + eXoVueI18n + + + extensionRegistry + + + diff --git a/notes-webapp/src/main/webapp/WEB-INF/notification/templates/mail/MentionInNoteNotificationPlugin.gtmpl b/notes-webapp/src/main/webapp/WEB-INF/notification/templates/mail/MentionInNoteNotificationPlugin.gtmpl new file mode 100644 index 0000000000..305ac20aee --- /dev/null +++ b/notes-webapp/src/main/webapp/WEB-INF/notification/templates/mail/MentionInNoteNotificationPlugin.gtmpl @@ -0,0 +1,128 @@ +<% +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +%> + + + + + + + + <% _ctx.include("war:/notification/templates/mail/NotificationFooter.gtmpl", _templateContext);%> +
    + + + + +
    + +<% +def title = _ctx.appRes("notes.notification.title.mention.in.note",NOTE_TITLE); +%> +$title + +
    +
    + + + + +
    + + + + +
    +

    +<%=_ctx.appRes("Notification.label.SayHello")%> $FIRST_NAME, +

    + + + +
    + + + + +
    + + + + +

    +<% +def notificationDescription = _ctx.appRes("note.notification.mention.in.note.description"); +%> +

    + + + + + + + + +
    +

    + + +

    +

    + <%=notificationDescription%> +

    +
    +

    +<% +def buttonLabel=_ctx.appRes("notes.notification.button.goToNote.label"); +%> +$buttonLabel +

    +
    +
    +
    +
    +

    + <%=_ctx.appRes("", "" + FOOTER_LINK)%> +

    +
    +
    + +
    \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/WEB-INF/notification/templates/push/MentionInNoteNotificationPlugin.gtmpl b/notes-webapp/src/main/webapp/WEB-INF/notification/templates/push/MentionInNoteNotificationPlugin.gtmpl new file mode 100644 index 0000000000..7a283700a8 --- /dev/null +++ b/notes-webapp/src/main/webapp/WEB-INF/notification/templates/push/MentionInNoteNotificationPlugin.gtmpl @@ -0,0 +1,21 @@ +<% +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +%> +<%=_ctx.appRes("notes.notification.title.mention.in.note",NOTE_TITLE)%> \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/groovy/webui/workspace/UINotesHeadTemplate.gtmpl b/notes-webapp/src/main/webapp/groovy/webui/workspace/UINotesHeadTemplate.gtmpl deleted file mode 100644 index c4757187bc..0000000000 --- a/notes-webapp/src/main/webapp/groovy/webui/workspace/UINotesHeadTemplate.gtmpl +++ /dev/null @@ -1,11 +0,0 @@ -<% - import org.exoplatform.commons.api.settings.ExoFeatureService; - import org.exoplatform.portal.application.PortalRequestContext; - def rcontext = _ctx.getRequestContext(); - ExoFeatureService featureService = uicomponent.getApplicationComponent(ExoFeatureService.class); - def userName = rcontext.getRemoteUser(); -%> - - \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js index bb046c5f1c..1889489aed 100644 --- a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js +++ b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js @@ -401,3 +401,44 @@ export function deleteNoteTranslation(noteId, translation) { }); } +export function saveNoteMetadata(properties, lang) { + const formData = new FormData(); + if (lang) { + formData.append('lang', lang); + } + const params = new URLSearchParams(formData).toString(); + return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/metadata?${params}`, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + method: 'POST', + body: JSON.stringify(properties), + credentials: 'include', + }).then((resp) => { + if (!resp?.ok) { + throw new Error('Error while saving note metadata'); + } else { + return resp.json(); + } + }); +} + +export function removeNoteFeaturedImage(noteId, isDraft, lang) { + const formData = new FormData(); + if (lang) { + formData.append('lang', lang); + } + formData.append('isDraft', isDraft); + const params = new URLSearchParams(formData).toString(); + return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/metadata/featured-image/${noteId}?${params}`, { + credentials: 'include', + method: 'DELETE', + }).then((resp) => { + if (resp?.ok) { + return resp; + } else { + throw new Error('Error when deleting note featured image'); + } + }); +} diff --git a/notes-webapp/src/main/webapp/skin/less/notes/notes.less b/notes-webapp/src/main/webapp/skin/less/notes/notes.less index 45f5502045..9aae3f39f0 100644 --- a/notes-webapp/src/main/webapp/skin/less/notes/notes.less +++ b/notes-webapp/src/main/webapp/skin/less/notes/notes.less @@ -73,7 +73,7 @@ .notes-content-form { min-height: calc(~"100vh - 140px"); - #cke_notesContent { + .notes-content-wrapper > div[role="application"] { border-color: transparent; flex: 0 1 100%; margin-bottom: 0; @@ -104,6 +104,30 @@ z-index: 10; background-color: @baseBackground; + .translation-button-icon { + width: 25px; + height: 21px; + } + + .editor-icon { + margin-bottom: 2px; + } + + .close-editor-icon { + width: 14px; + height: 21px; + } + + .save-button-icon { + width: 18px; + height: 21px; + } + + .metadata-button-icon { + width: 20px; + height: 21px; + } + .notesActions { .notesFormButtons { .notesFormLeftActions { @@ -188,7 +212,80 @@ } } -#notesOverviewApplication, .notesApplication { +#editorMetadataDrawer { + + .add-image-area, .image-preview { + background-color: @primaryBackground !important; + } + + .image-pre-upload { + .v-image__image { + filter: blur(2px); + } + } + + .cke_bottom { + height: auto; + opacity: 0.4; + background: transparent; + } + + .feature-image-trash-icon { + width: 18px; + height: 21px; + } + + .feature-image-file-icon { + width: 15px; + height: 21px; + } + + .feature-image-button { + background: @greyColorLighten1Default; + background: rgba(0, 0, 0, 0.25); + border: 1px solid @whiteColorDefault; + color: @whiteColorDefault + } + + .summary-metadata-input { + .v-input__slot { + padding: 0; + + fieldset { + padding: 0; + } + + .v-text-field__slot { + margin: 0; + + textarea { + overflow: auto; + border: 0; + margin: 0 auto; + padding: 12px; + } + } + } + } +} + +#notesActionMenuBottomDrawer, #notesOverviewApplication, + .notesApplication .notes-application-header .note-actions-menu { + .action-menu-item { + min-height: 28px !important; + } + + .delete-option-color { + color: @errorColor; + } + + .icon-menu { + width: 28px; + height: 36px; + } +} + +#notesOverviewApplication { max-width: 100%; .notes-wrapper { @@ -217,11 +314,24 @@ min-height: calc(~"100vh - 170px"); .notes-application-header { + .note-actions-menu { + right: 35px ~'; /** orientation=lt */ '; + right: auto ~'; /** orientation=rt */ '; + top: 50px !important; + left: auto ~'!important; /** orientation=lt */ '; + left: 35px ~'!important; /** orientation=rt */ '; + } + + .note-summary { + font-size: 20px; + line-height: 1.2; + } + .notes-title { - .title{ + .title { font-weight: bold; font-family: 'Helvetica', 'Arial', sans-serif; - font-size: 18px!important; + font-size: 34px!important; } .notes-header-icons { min-width: 100px !important; @@ -230,17 +340,6 @@ button { padding: 1px 2px; } - - .note-actions-menu { - min-width: 150px !important; - right: 7px; - top: 25px !important; - left: auto !important; - - .action-menu-item { - min-height: 30px !important; - } - } } } @@ -265,6 +364,12 @@ color: @primaryColor !important; } } + + .not-clickable { + &:hover { + color: @primaryColor !important; + } + } } } @@ -1142,8 +1247,4 @@ ul.note-manual-child { .remove-focus:focus::after { opacity: 0 !important; -} - - - - +} \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/vue-app/note-page-view/services.js b/notes-webapp/src/main/webapp/vue-app/note-page-view/services.js index 22be8440b2..f7823d71fa 100644 --- a/notes-webapp/src/main/webapp/vue-app/note-page-view/services.js +++ b/notes-webapp/src/main/webapp/vue-app/note-page-view/services.js @@ -19,7 +19,7 @@ import * as notePageViewService from './js/NotePageViewService.js'; import * as notesService from '../../javascript/eXo/wiki/notesService.js'; -import * as noteUtils from '../notes-editor/js/Utils.js'; +import * as noteUtils from '../notes-rich-editor/js/Utils.js'; if (!Vue.prototype.$notePageViewService) { window.Object.defineProperty(Vue.prototype, '$notePageViewService', { diff --git a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NoteTablePluginsDrawer.vue b/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NoteTablePluginsDrawer.vue deleted file mode 100644 index f0ee0628a2..0000000000 --- a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NoteTablePluginsDrawer.vue +++ /dev/null @@ -1,386 +0,0 @@ - - - diff --git a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue b/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue index 9c8d61331b..f739b86d88 100644 --- a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue +++ b/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue @@ -20,115 +20,40 @@ -->