EditorFacade.java

package sk.iway.iwcm.editor.facade;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;

import sk.iway.iwcm.Constants;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.doc.DocDB;
import sk.iway.iwcm.doc.DocDetails;
import sk.iway.iwcm.doc.DocEditorFields;
import sk.iway.iwcm.doc.GroupDetails;
import sk.iway.iwcm.doc.GroupsDB;
import sk.iway.iwcm.doc.TemplatesDB;
import sk.iway.iwcm.editor.service.EditorService;
import sk.iway.iwcm.editor.service.GroupsService;
import sk.iway.iwcm.editor.service.MediaService;
import sk.iway.iwcm.editor.service.MultigroupService;
import sk.iway.iwcm.system.context.ContextFilter;
import sk.iway.iwcm.system.datatable.NotifyBean;
import sk.iway.iwcm.system.datatable.NotifyBean.NotifyType;
import sk.iway.iwcm.users.UserDetails;
import sk.iway.spirit.model.Media;

/**
 * Facade pre editaciu web stranok. Dolezite su metody getDocForEditor pre ziskanie DocDetails pre editor a save pre jeho ulozenie.
 */
@Service
@RequestScope
public class EditorFacade {

    private EditorService editorService;
    private MultigroupService multigroupService;
    private MediaService mediaService;
	private GroupsService groupsService;
    private HttpServletRequest request;

    //nastavene na true ak je potrebne vyvolat obnovenie stromovej struktury/datatabulky po ulozeni
	private boolean forceReload = false;

	//set to true if you want to directly edit multigroup page (eg. change sort order or groupId), used from jstree calls
	private boolean ignoreMultigroupMapping = false;

    @Autowired
    public EditorFacade(EditorService editorService, MultigroupService multigroupService, MediaService mediaService, GroupsService groupsService, HttpServletRequest request) {
        this.editorService = editorService;
        this.multigroupService = multigroupService;
        this.mediaService = mediaService;
		this.groupsService = groupsService;
        this.request = request;
    }

    /**
	 * Ulozenie web stranky, vykonava nasledovne operacie:
	 *
	 * - multigroup mapping - web stranka moze byt vo viacerych adresaroch, ak sa edituje child, tak sa prepne na editaciu master verzie a nasledne sa rozkopiruje do dalsich priecinkov
	 * - vykona samotne ulozenie stranky, vid metoda saveEditedDoc
	 * - po ulozeni aktualizuje TemplatesDB ak sa jedna o stranku zo System adresara
	 * - ak nastala zmena vyzadujuca reload nastavi atribut forceReload=true
	 * - ulozi Media novej web stranky (tie sa ukladaju do DB pre novu stranku s -user_id namiesto web stranky)
	 *
	 * @param entity
	 * @return
	 */
	public DocDetails save(DocDetails entity) {

		if (entity.getSortPriority() == -1 && entity.getEditorFields() != null && entity.getEditorFields().getGroupDetails() != null) {
			//if sort priority is 0/empty calculate new one
			editorService.setSortPriority(entity, entity.getEditorFields().getGroupDetails());
		}

		//najskor over multigroup mapping
		int docIdOriginal = entity.getDocId();
		int originalSortPriority = entity.getSortPriority();
		String originalVirtualPath = entity.getVirtualPath();
		if (isIgnoreMultigroupMapping()==false) {
			if (docIdOriginal > 0) {
				int masterId = multigroupService.getMasterDocId(docIdOriginal);
				if(masterId > 0) {
					entity.setDocId(masterId);

					DocDetails master = DocDB.getInstance().getDoc(masterId, -1, true);

					boolean multiGroupkeepSortPriority = Constants.getBoolean("multiGroupKeepSortPriority");
					//ak chcem zachovat sort priority a ukladam slave clanok, potrebujem zachovat sort priority mastra
					if(multiGroupkeepSortPriority && entity.getDocId()>0)
					{
						entity.setSortPriority(master.getSortPriority());
					}
					if (masterId != docIdOriginal) {
						//set original master virtualPath because we are saving MASTER doc (see entity.setDocId(masterId)) and want to keep URL on master
						entity.setVirtualPath(master.getVirtualPath());
					}
				}
			}
		}

		//prenesme nazad hodnoty z editorFields do DocDetails
		entity.getEditorFields().toDocDetails(entity);

		int historyId = editorService.saveEditedDoc(entity);
		Logger.debug(EditorFacade.class, "Page saved, historyId=" + historyId);

        multigroupService.setDefaultDocId(entity.getGroupId(), entity.getDocId());

		//ak je to stranka zo /System adresara tak refreshni TemplatesDB, v novom totiz mame lokalne System adresare
		GroupDetails group = entity.getGroup();
		if (group != null && group.getFullPath().startsWith("/System/"))
			TemplatesDB.getInstance(true);

		//ak sa jedna o novu stranku je potrebne zmenit mediam hodnotu -user_id na novo ulozene docId
		if(docIdOriginal < 1) {
			mediaService.assignDocIdToMedia(entity.getDocId());
		}

		//save attrs - MUST BE BEFORE multigroupHandleSlaves
		if (entity.getEditorFields().getAttrs() != null) {
			editorService.saveAttrs(entity, entity.getEditorFields().getAttrs(), Constants.getBoolean("attrAlwaysCleanOnSave"));
		}

		if (isIgnoreMultigroupMapping()==false) {
			boolean multigroupRedirectSlavesToMaster = Constants.getBoolean("multigroupRedirectSlavesToMaster");
			//Write multigroup mapping
			if (entity.getEditorFields().getGroupCopyDetails() != null) {
				entity.setSortPriority(originalSortPriority);
				entity.setVirtualPath(originalVirtualPath);
				multigroupService.multigroupHandleSlaves(entity, docIdOriginal, entity.getEditorFields().getGroupCopyDetails(), multigroupRedirectSlavesToMaster);
			}
		}

		//vrat aktualny zaznam z DB
		DocDetails savedCopy = editorService.getDoc(entity.getDocId(), historyId);
		DocEditorFields def = new DocEditorFields();
		def.fromDocDetails(savedCopy, true, true);
		savedCopy.setEditorFields(def);
		savedCopy.setHistoryId(historyId);

		//ak sme editovali multigroup stranku nastav nazad povodne docId
		if (docIdOriginal > 0) savedCopy.setDocId(docIdOriginal);

		return savedCopy;
	}

	/**
	 * Ulozi entitu ako B variantu web stranky pre app AB testovanie
	 * @param entity
	 * @return
	 */
	public DocDetails saveAsBVariant(DocDetails entity) {
		String virtualPath = entity.getVirtualPath();
		virtualPath = Tools.replace(virtualPath, "*", "");
		if (virtualPath.contains(Constants.getString("ABTestingName"))) {
			editorService.addNotify(new NotifyBean("components.abtesting.dialog_title", editorService.getProp().getText("editor.save_as_abtest.notify.isbvariant", entity.getTitle(), String.valueOf(entity.getId())), NotifyType.INFO, 5000));
			return null;
		}

		//nastav virtual path
		int i = virtualPath.indexOf(".");
		String variantName = Constants.getString("ABTestingName")+"b";
		if (i != -1) {
			virtualPath = virtualPath.substring(0, i) + "-" + variantName + virtualPath.substring(i);
		} else {
			virtualPath = virtualPath + variantName + ".html";
		}

		//over, ci uz nahodou neexistuje stranka so zadanou URL
		String domain = GroupsDB.getInstance().getDomain(entity.getGroupId());
		int actualDocId = DocDB.getDocIdFromURL(virtualPath, domain);
		if (actualDocId>0) {
			editorService.addNotify(new NotifyBean("components.abtesting.dialog_title", editorService.getProp().getText("editor.save_as_abtest.notify.exists", entity.getTitle(), String.valueOf(entity.getId()), String.valueOf(actualDocId)), NotifyType.INFO, 5000));
			return null;
		}

		//sprav kopiu entity
		entity.setDocId(-1);
		entity.setVirtualPath(virtualPath);
		entity.setGenerateUrlFromTitle(false);
		entity.setUrlInheritGroup(false);
		entity.setShowInMenu(false);
		entity.setLoggedShowInMenu(false);
		entity.setSearchable(false);

		return save(entity);
	}

    /**
	 * Pripravi entitu na editaciu, na rozdiel od standardneho ziskania getOne riesi:
	 *
	 * - multigroup mapovanie - ak editujem slave vrati udaje master verzie
	 * - ak sa jedna o novu stranku (doc_id==-1) pripravi stranke udaje podla adresara (sablona, poradie usporiadania...)
	 * - dokaze nacitat stranku aj z historie, pokial je zadane historyId
	 *
	 * @param docId - id web stranky, alebo -1 pre novu stranku
	 * @param historyId - id z historie, alebo -1 pre vratenie aktualnej verzie stranky
	 * @param groupId - id adresara
	 * @return
	 */
    public DocDetails getDocForEditor(int docId, int historyId, int groupId) {

        GroupsDB groupsDB = GroupsDB.getInstance();
		if (groupId < 1 && docId > 0) {
			//try to autodetect current groupId
			DocDetails current = DocDB.getInstance().getBasicDocDetails(docId, false);
			if (current != null && current.getGroupId()>0) groupId = current.getGroupId();
		}

		GroupDetails group = groupsDB.getGroup(groupId);
		if (group == null) {
			//in multithread enviroment sometimes group is not yet loaded, try to reload
			groupsDB = GroupsDB.getInstance(true);
			group = groupsDB.getGroup(groupId);
		}
		//lebo pre zadane docId group ignorujeme (moze to byt 999997)
		if (docId < 0 && group == null) return null;

		DocDetails editedDoc;

		// vymazem docasne ulozene media (uzivatel neulozil novu stranku po
		mediaService.deleteTempMedia();

        if (docId == -1 && group.getNewPageDocIdTemplate() > 0) {
			//je potrebne pouzit sablonu namiesto -1
			docId = -group.getNewPageDocIdTemplate();
		}

        //sablona nemoze byt -1 (to je blank page)
		if (docId < 0 && historyId < 0) {
			editedDoc = editorService.prepareNewDocForEditor(docId, group);
		} else {
			editedDoc = editorService.prepareDocForEditor(docId, historyId, ignoreMultigroupMapping);

			if(editedDoc == null) return null;
		}

		if (Constants.getBoolean("editorAutoFillPublishStart") && editedDoc.getPublishStartDate()==null) {
			editedDoc.setPublishStartDate(new Date(Tools.getNow()));
		}

		if (request != null) {
			editedDoc.setHistoryId(historyId);

			String domainName = DocDB.getDomain(request);
			GroupDetails editorGroup = groupsDB.getGroup(editedDoc.getGroupId());

			if (editorGroup != null && Tools.isNotEmpty(editorGroup.getDomainName())) domainName = editorGroup.getDomainName();
			request.getSession().setAttribute("preview.editorDomainName", domainName);
		}

		if (request != null && ContextFilter.isRunning(request)) {
			//do editoru nahrame texty s pridanymi linkami
			editedDoc.setData(ContextFilter.addContextPath(request.getContextPath(), editedDoc.getData()));
		}

        return editedDoc;
    }

	/**
	 * Vytvori web stranku
	 * @param group - adresar v ktorom ma byt vytvorena
	 * @param title - volitelny titulok stranky, ak je NULL vytvori sa podla mena adresara
	 * @param available - urci, ci stranka na byt ihned zobrazitelna (true), alebo nie (false)
	 * @return
	 */
	public DocDetails createEmptyWebPage(GroupDetails group, String title, boolean available) {
		DocDetails doc;
        if (group.getDefaultDocId()>0 && title==null) doc = getDocForEditor(group.getDefaultDocId(), -1, group.getGroupId());
        else doc = getDocForEditor(-1, -1, group.getGroupId());

        if (Tools.isEmpty(title)) title = group.getGroupName();

        doc.setTitle(title);
        doc.setNavbar(title);

		save(doc);

        return doc;
	}

	public boolean delete(DocDetails doc) {
		return editorService.deleteWebpage(doc, true);
	}

	public void recoverWebpageFromTrash(DocDetails doc) {
		if (doc != null && doc.getDocId()>0) editorService.recoverWebpageFromTrash(doc.getDocId());
	}

    public boolean isForceReload() {
        return forceReload || editorService.isForceReload();
    }

    /**
	 * Ak nastalo schvalovanie vrati zoznam schvalovatelov
	 * @return
	 */
	public List<UserDetails> getApprovers() {
		return editorService.getApprovers();
	}

	/**
	 * Ak ma web stranka publikovanie v buducnosti nastavi sa sem timestamp zaciatku publikovania
	 * @return
	 */
	public Long getPublihStart() {
		return editorService.getPublihStart();
	}

	/**
	 * Ak bola stranka uspesne vypublikovana na verejne zobrazenie vrati true
	 * @return
	 */
	public boolean isPageSavedToPublic() {
		return editorService.isPageSavedToPublic();
	}

	/**
	 * Ak bola stranka korektne ulozena ako rozpracovana verzia vrati true
	 * @return
	 */
	public boolean isPageSavedAsWorkVersion() {
		return editorService.isPageSavedAsWorkVersion();
	}

    /**
     * Vrati zoznam moznych notifikacii pre pouzivatela
     * @return
     */
    public List<NotifyBean> getNotify() {
		List<NotifyBean> notify = new ArrayList<>();
		if (editorService.getNotify()!=null) notify.addAll(editorService.getNotify());
		if (groupsService.getNotify()!=null) notify.addAll(groupsService.getNotify());
        return notify;
    }

	/**
	 * Clears notify list
	 */
	public void clearNotify() {
		if (editorService.getNotify()!=null) editorService.getNotify().clear();
		if (groupsService.getNotify()!=null) groupsService.getNotify().clear();
    }

	/**
	 * Vrati zoznam vsetkych medii priradenych k zadanej web stranke
	 * @param docId - ID stranky
	 * @return
	 */
	public List<Media> getAllMedia(Integer docId) {
		return mediaService.getAllMedia(docId, "documents");
	}

	/**
	 * Duplikuje media pri duplikovani web stranky
	 * @param oldDocId
	 * @param newDocId
	 * @return
	 */
	public void duplicateMedia(Integer oldDocId, Integer newDocId) {
		// zduplikuj aj media
        List<Media> mediaList = getAllMedia(oldDocId);
        if (mediaList != null && mediaList.isEmpty()==false) {
            for (Media m : mediaList) {
                Logger.debug(getClass(), "Duplikujem medium: "+m.getMediaId()+" "+m.getMediaTitleSk());
				m.setId(null);
				m.setMediaFkId(newDocId);
				m.save();
            }
        }
	}

	/**
	 * Overi, ci pouzivatel ma pravo na editaciu zadanej web stranky
	 * @param user
	 * @param doc
	 * @param isDelete - ak je true kontroluje sa pravo deletePage, inak sa kontroluje addPage/pageSave
	 * @return
	 */
	public boolean isPageEditable(Identity user, DocDetails doc, boolean isDelete) {
		return editorService.isPageEditable(user, doc, isDelete);
	}

	public boolean isIgnoreMultigroupMapping() {
		return ignoreMultigroupMapping;
	}

	/**
	 * set to true if you want to directly edit multigroup page (eg. change sort order or groupId)
	 * @param ignoreMultigroupMapping
	 */
	public void setIgnoreMultigroupMapping(boolean ignoreMultigroupMapping) {
		this.ignoreMultigroupMapping = ignoreMultigroupMapping;
	}

	/**
	 * Recover group from trash:
	 * - check permissions
	 * - set parentGroupId by history (if exists) or set to 0
	 * @param entity
	 * @param currentUser
	 * @return
	 */
	public boolean recoverGroupFromTrash(GroupDetails entity, Identity currentUser) {
		return groupsService.recoverGroupFromTrash(entity, currentUser);
	}
}