/** * Copyright © 2014 Instituto Superior Técnico * * This file is part of FenixEdu CMS. * * FenixEdu CMS 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. * * FenixEdu CMS 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 FenixEdu CMS. If not, see <http://www.gnu.org/licenses/>. */ package org.fenixedu.cms.ui; import static java.util.Optional.ofNullable; import static org.fenixedu.cms.domain.PermissionEvaluation.canDoThis; import static org.fenixedu.cms.domain.PermissionEvaluation.ensureCanDoThis; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.core.groups.Group; import org.fenixedu.bennu.core.rest.UserResource; import org.fenixedu.bennu.io.domain.GroupBasedFile; import org.fenixedu.bennu.io.servlets.FileDownloadServlet; import org.fenixedu.bennu.signals.DomainObjectEvent; import org.fenixedu.bennu.signals.Signal; import org.fenixedu.cms.domain.Category; import org.fenixedu.cms.domain.PermissionEvaluation; import org.fenixedu.cms.domain.PermissionsArray.Permission; import org.fenixedu.cms.domain.Post; import org.fenixedu.cms.domain.PostFile; import org.fenixedu.cms.domain.PostMetadata; import org.fenixedu.cms.domain.Site; import org.fenixedu.commons.i18n.LocalizedString; import org.joda.time.DateTime; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import pt.ist.fenixframework.Atomic; import pt.ist.fenixframework.FenixFramework; /** * Created by borgez on 30-07-2015. */ @Service public class AdminPostsService { @Atomic(mode = Atomic.TxMode.WRITE) public Post createPost(Site site, LocalizedString name) { Post post = new Post(site); post.setName(Post.sanitize(name)); post.setBodyAndExcerpt(new LocalizedString(), new LocalizedString() ); post.setCanViewGroup(site.getCanViewGroup()); post.setPublicationBegin(new DateTime()); post.setActive(false); return post; } @Atomic(mode = Atomic.TxMode.WRITE) public PostFile createFile(Post post, String name, Boolean embedded, Group canViewGroup, MultipartFile file) { try { GroupBasedFile groupBasedFile = new GroupBasedFile(name, name, file.getBytes(), canViewGroup); return new PostFile(post, groupBasedFile, embedded, post.getFilesSet().size()); } catch(IOException e) { throw new RuntimeException("Error creating Post File for post " + post, e); } } @Atomic(mode = Atomic.TxMode.WRITE) public void processPostChanges(Site site, Post post, JsonObject postJson) { LocalizedString name = Post.sanitize(LocalizedString.fromJson(postJson.get("name"))); LocalizedString body = Post.sanitize(LocalizedString.fromJson(postJson.get("body"))); LocalizedString excerpt = Post.sanitize(LocalizedString.fromJson(postJson.get("excerpt"))); String slug = ofNullable(postJson.get("slug")) .map(JsonElement::getAsString).orElse(post.getSlug()); if(!post.getName().equals(name)) { post.setName(name); } if(!post.getBody().equals(body) || ( post.getExcerpt()==null && excerpt!=null ) || !post.getExcerpt().equals(excerpt)) { post.setBodyAndExcerpt(body, excerpt); } if(!post.getSlug().equals(slug)) { post.setSlug(slug); } processCategoryChanges(site, post, postJson); processFileChanges(site, post, postJson); processPublicationChanges(site, post, postJson); post.fixOrder(post.getFilesSorted()); Signal.emit(Post.SIGNAL_EDITED,new DomainObjectEvent<>(post)); } private boolean equalDates(DateTime time1,DateTime time2) { return ofNullable(time1).map(DateTime::toString).orElse("").equals(ofNullable(time2).map(DateTime::toString).orElse("")); } public JsonObject serializePost(Post post) { JsonObject postJson = new JsonObject(); JsonArray categoriesJson = new JsonArray(); JsonArray filesJson = new JsonArray(); if(canDoThis(post.getSite(), Permission.LIST_CATEGORIES, Permission.EDIT_CATEGORY)) { boolean canUsePrivileged = canDoThis(post.getSite(), Permission.USE_PRIVILEGED_CATEGORY); post.getSite().getCategoriesSet().stream() .filter(cat -> canUsePrivileged || !cat.getPrivileged()) .sorted(Category.CATEGORY_NAME_COMPARATOR) .map(category -> serializeCategory(category, post)).forEach(categoriesJson::add); } post.getFilesSorted().stream().map(this::serializePostFile).forEach(filesJson::add); postJson.addProperty("slug", post.getSlug()); postJson.add("name", ofNullable(post.getName()).map(LocalizedString::json) .orElseGet(JsonObject::new)); postJson.add("body", ofNullable(post.getBody()).map(LocalizedString::json).orElseGet(JsonObject::new)); postJson.add("excerpt", ofNullable(post.getExcerpt()).map(LocalizedString::json).orElseGet(JsonObject::new)); if(PermissionEvaluation.canDoThis(post.getSite(), Permission.SEE_METADATA)) { postJson.add("metadata", ofNullable(post.getMetadata()).map(PostMetadata::json) .orElseGet(JsonObject::new)); } else { postJson.add("metadata", JsonNull.INSTANCE); } postJson.add("categories", categoriesJson); postJson.add("files", filesJson); postJson.addProperty("address", post.getAddress()); if(canPublish(post)) { postJson.addProperty("active", post.getActive()); postJson.addProperty("canViewGroup", post.getCanViewGroup().getExpression()); postJson.add("createdBy", UserResource.getBuilder().view(post.getCreatedBy())); postJson.addProperty("publicationBegin", ofNullable(post.getPublicationBegin()) .map(DateTime::toString).orElse(null)); postJson.addProperty("publicationEnd", ofNullable(post.getPublicationEnd()) .map(DateTime::toString).orElse(null)); } return postJson; } private JsonObject serializeCategory(Category category, Post post) { JsonObject categoryJson = new JsonObject(); categoryJson.add("name", category.getName().json()); categoryJson.addProperty("slug", category.getSlug()); categoryJson.addProperty("use", post.getCategoriesSet().contains(category)); return categoryJson; } public JsonObject serializePostFile(PostFile postFile) { JsonObject fileJson = new JsonObject(); fileJson.addProperty("id", postFile.getExternalId()); fileJson.addProperty("displayName", postFile.getFiles().getDisplayName()); fileJson.addProperty("fileName", postFile.getFiles().getFilename()); fileJson.addProperty("contentType", postFile.getFiles().getContentType()); fileJson.addProperty("editUrl", postFile.getEditUrl()); fileJson.addProperty("isEmbedded", postFile.getIsEmbedded()); fileJson.addProperty("index", postFile.getIndex()); fileJson.addProperty("canViewGroup", postFile.getFiles().getAccessGroup().getExpression()); fileJson.addProperty("url", FileDownloadServlet.getDownloadUrl(postFile.getFiles())); return fileJson; } private void processPublicationChanges(Site site, Post post, JsonObject postJson) { if(canPublish(post)) { boolean active = ofNullable(postJson.get("active")) .map(JsonElement::getAsBoolean) .orElse(false); DateTime publicationBegin = ofNullable(postJson.get("publicationBegin")) .filter(JsonElement::isJsonPrimitive) .map(JsonElement::getAsString).filter(x -> !x.isEmpty()) .map(DateTime::parse) .orElse(null); DateTime publicationEnds = ofNullable(postJson.get("publicationEnd")) .filter(JsonElement::isJsonPrimitive) .map(JsonElement::getAsString).filter(x -> !x.isEmpty()) .map(DateTime::parse) .orElse(null); Group canViewGroup = ofNullable(postJson.get("canViewGroup")) .map(JsonElement::getAsString) .map(Group::parse) .orElse(post.getCanViewGroup()); if(PermissionEvaluation.canDoThis(site, Permission.CHANGE_OWNERSHIP_POST)) { User createdBy = ofNullable(postJson.get("createdBy")) .map(JsonElement::getAsJsonObject) .map(json -> json.get("username").getAsString()) .map(User::findByUsername) .orElse(post.getCreatedBy()); if(!post.getCreatedBy().equals(createdBy)) { post.setCreatedBy(createdBy); } } if(!equalDates(post.getPublicationBegin(), publicationBegin)) { post.setPublicationBegin(publicationBegin); } if(!equalDates(post.getPublicationEnd(), publicationEnds)) { post.setPublicationEnd(publicationEnds); } if(!post.getCanViewGroup().equals(canViewGroup)) { post.setCanViewGroup(canViewGroup); } if(post.getActive() != active) { post.setActive(active); } } } private void processCategoryChanges(Site site, Post post, JsonObject postJson) { if(canDoThis(post.getSite(), Permission.LIST_CATEGORIES, Permission.EDIT_CATEGORY)) { if (postJson.get("categories") != null && postJson.get("categories").isJsonArray()) { Set<Category> newCategories = new HashSet<>(); for (JsonElement categoryJsonEl : postJson.get("categories").getAsJsonArray()) { JsonObject categoryJson = categoryJsonEl.getAsJsonObject(); if (ofNullable(categoryJson.get("use")).map(JsonElement::getAsBoolean).orElse(false)) { String categorySlug = categoryJson.get("slug").getAsString(); LocalizedString categoryName = Post.sanitize(LocalizedString.fromJson(categoryJson.get("name"))); Category category = site.categoryForSlug(categorySlug); if (category == null) { PermissionEvaluation .ensureCanDoThis(site, Permission.CREATE_CATEGORY); category = new Category(site, categoryName); } else if(category.getPrivileged()) { ensureCanDoThis(site, Permission.USE_PRIVILEGED_CATEGORY); } newCategories.add(category); } } if(!canDoThis(site, Permission.USE_PRIVILEGED_CATEGORY)) { //If the user has no access to remove previleged categories, then we add them back HashSet<Category> removed = new HashSet<>(post.getCategoriesSet()); removed.removeAll(newCategories); removed.stream().filter(Category::getPrivileged).forEach(newCategories::add); } if (!newCategories.containsAll(post.getCategoriesSet()) || !post.getCategoriesSet() .containsAll(newCategories)) { post.getCategoriesSet().clear(); newCategories.stream().forEach(post::addCategories); } } } } private void processFileChanges(Site site, Post post, JsonObject postJson) { if(postJson.get("files")!=null && postJson.get("files").isJsonArray()) { for (JsonElement fileJsonEl : postJson.get("files").getAsJsonArray()) { JsonObject fileJson = fileJsonEl.getAsJsonObject(); PostFile postFile = FenixFramework.getDomainObject(fileJson.get("id").getAsString()); if(postFile.getPost() == post) { int index = fileJson.get("index").getAsInt(); boolean isEmbedded = fileJson.get("isEmbedded").getAsBoolean(); if(postFile.getIndex()!= index) { postFile.setIndex(index); } if(postFile.getIsEmbedded()!=isEmbedded) { postFile.setIsEmbedded(isEmbedded); } Signal.emit(PostFile.SIGNAL_EDITED, new DomainObjectEvent<>(postFile)); } } } } private boolean canPublish(Post post) { return (post.isStaticPost() && canDoThis(post.getSite(), Permission.PUBLISH_PAGES)) || (!post.isStaticPost() && canDoThis(post.getSite(), Permission.PUBLISH_POSTS)); } }