001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2007
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.war.beans.admin.content;
034:
035: import com.flexive.faces.FxJsfUtils;
036: import com.flexive.faces.beans.ActionBean;
037: import com.flexive.faces.messages.FxFacesMessage;
038: import com.flexive.shared.EJBLookup;
039: import com.flexive.shared.content.FxPK;
040: import com.flexive.shared.exceptions.FxApplicationException;
041: import static com.flexive.shared.tree.FxTreeMode.Edit;
042: import com.flexive.shared.tree.FxTreeNode;
043: import com.flexive.shared.tree.FxTreeNodeEdit;
044:
045: import javax.faces.application.FacesMessage;
046: import java.io.Serializable;
047: import java.net.URLEncoder;
048:
049: /**
050: * This Bean is used for the frontpage inline editing editor.
051: *
052: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
053: */
054: public class InlineContentEditorBean extends ContentEditorBean
055: implements ActionBean, Serializable {
056:
057: private static final String REQ_ATR_INITIALIZED = InlineContentEditorBean.class
058: .getName()
059: + ".pageIsInitialized";
060: private static final String PAGE_CLOSE_EDITOR = "iceClose";
061: private Long pageId;
062: private boolean doReload = true;
063: private FxTreeNode workingNode;
064: private FxTreeNode clipboardNode;
065: private Long clipboardPage;
066: private FxPK ancorPk;
067: private boolean ancoreBefore;
068: private CLIPBOARD_FUCTION clipboardFunction;
069: private String reloadUrl;
070:
071: private static enum CLIPBOARD_FUCTION {
072: CUT, COPY
073: }
074:
075: /**
076: * Returns the PK in the clipboard (used for cut/copy/paste function).
077: *
078: * @return the PK in the clipboard
079: */
080: public long getClipboardContentId() {
081: return clipboardNode == null ? -1 : clipboardNode
082: .getReference().getId();
083: }
084:
085: /**
086: * Returns the PK in the clipboard as string (used for cut/copy/paste function).
087: *
088: * @return the PK in the clipboard as string
089: */
090: public String getClipboardContentIdAsString() {
091: return clipboardNode == null ? "" : String
092: .valueOf(getClipboardContentId());
093: }
094:
095: /**
096: * The page that the content was cut/copied from.
097: *
098: * @return The page that the content was cut/copied from.
099: */
100: public Long getClipboardPage() {
101: return clipboardPage;
102: }
103:
104: /**
105: * Returns the tree node stored in the clipboard (used for cut/copy/paste function).
106: *
107: * @return the tree node stored in the clipboard
108: */
109: public FxTreeNode getClipboardNode() {
110: return clipboardNode;
111: }
112:
113: /**
114: * Returns true if the paste option is available.
115: *
116: * @return true if the paste option is available
117: */
118: public boolean getPasteEnabled() {
119: return clipboardNode != null;
120: }
121:
122: public InlineContentEditorBean() {
123: super ();
124: }
125:
126: @SuppressWarnings("UnusedDeclaration")
127: InlineContentEditorBean(boolean dummy) {
128: super (dummy);
129: }
130:
131: public static InlineContentEditorBean getSingleton() {
132: return new InlineContentEditorBean(true);
133: }
134:
135: public String getEditorType() {
136: return "INLINE";
137: }
138:
139: public void setEditorType(String editorType) {
140: super .setEditorType(editorType);
141: }
142:
143: @Override
144: protected String getEditorPage() {
145: return "inlineContentEditor";
146: }
147:
148: @Override
149: protected String getSessionCacheId() {
150: return super .getSessionCacheId();
151: }
152:
153: /**
154: * This getter returns true exactly one time every request, and is used to write some
155: * javascipts to the page on the first access.
156: *
157: * @return true exactly one time every request
158: */
159: public boolean getPageIsInitialized() {
160: boolean isIni = FxJsfUtils.getRequest().getAttribute(
161: REQ_ATR_INITIALIZED) != null;
162: if (!isIni) {
163: FxJsfUtils.getRequest().setAttribute(REQ_ATR_INITIALIZED,
164: Boolean.TRUE);
165: }
166: return isIni;
167: }
168:
169: @Override
170: public String saveInSession() {
171: return super .saveInSession();
172: }
173:
174: @Override
175: public String save() {
176: setDoReload(true);
177: super .save();
178: // Update the position of the node
179: try {
180: if (ancorPk != null) {
181: FxTreeNode treeNodeId = resolveNodeId(workingNode
182: .getId(), getPk().getId());
183: int ancorPos = resolveAncorPosition(workingNode, null,
184: ancorPk.getId());
185: if (ancoreBefore) {
186: ancorPos = ancorPos - 1;
187: }
188: tree
189: .move(treeNodeId.getMode(),
190: treeNodeId.getId()/*node to move*/,
191: workingNode.getId()/*parent*/,
192: ancorPos/*new pos*/);
193: }
194: } catch (Throwable t) {
195: System.out
196: .println("ContentInlineEditor: failed to position then node after save: "
197: + t.getMessage());
198: }
199: //
200: return hasError() ? null : PAGE_CLOSE_EDITOR;
201: }
202:
203: public void setDoReload(boolean doReload) {
204: this .doReload = doReload;
205: setReloadUrl();
206: }
207:
208: @Override
209: public String saveInNewVersion() {
210: setDoReload(true);
211: super .saveInNewVersion();
212: return null;
213: }
214:
215: @Override
216: public String delete() {
217: setDoReload(true);
218: super .delete();
219: return hasError() ? null : PAGE_CLOSE_EDITOR;
220: }
221:
222: private boolean hasError() {
223: for (FxFacesMessage msg : FxJsfUtils.getFxMessages()) {
224: if (msg.getSeverity() != FacesMessage.SEVERITY_INFO)
225: return true;
226: }
227: return false;
228: }
229:
230: @Override
231: public String reload() {
232: setDoReload(false);
233: return super .reload();
234: }
235:
236: @Override
237: public String cancel() {
238: setDoReload(false);
239: super .cancel();
240: return PAGE_CLOSE_EDITOR;
241: }
242:
243: protected String release() {
244: String result = super .release();
245: pageId = null;
246: ancorPk = null;
247: workingNode = null;
248: return result;
249: }
250:
251: /**
252: * {@inheritDoc}
253: */
254: @Override
255: public String getParseRequestParameters()
256: throws FxApplicationException {
257: String action = FxJsfUtils.getParameter("action");
258: String result = super .getParseRequestParameters();
259: if (action == null) {
260: // Do not handle unknown actions (ajax4jsf, ...)
261: return result;
262: }
263:
264: if (FxJsfUtils.hasParameter("page")) {
265: pageId = FxJsfUtils.getLongParameter("page");
266: }
267:
268: String sNode = (FxJsfUtils.hasParameter("node")) ? FxJsfUtils
269: .getParameter("node") : ".";
270: if (sNode.length() > 0 && !sNode.equals(".")) {
271: String path = FxJsfUtils.getParameter("node");
272: if (path.equals("..")) {
273: FxTreeNode _node = tree.getNode(Edit, pageId); // TODO: edit/live!
274: workingNode = tree.getNode(Edit, _node
275: .getParentNodeId());
276: } else {
277: FxTreeNode node = tree.getTree(Edit, pageId, 1); // TODO: edit/live
278: for (FxTreeNode child : node.getChildren()) {
279: if (child.getName().equalsIgnoreCase(path)) {
280: workingNode = child;
281: break;
282: }
283: }
284: }
285: } else {
286: workingNode = tree.getNode(Edit, pageId);
287: }
288:
289: if ("editInstance".equals(action)) {
290: // nothing
291: } else if (workingNode == null) {
292: // TODO: Error handling
293: System.err.println("Error: Empty working node!!");
294: } else {
295: if ("newInstance".equals(action)) {
296: addTreeNode(workingNode.getId());
297: ancoreBefore = isBeforeAncore();
298: ancorPk = getPkFromRParam("ancorPk");
299: } else if ("copy".equals(action)) {
300: FxPK clipboardPK = getPkFromRParam("pk");
301: clipboardFunction = CLIPBOARD_FUCTION.COPY;
302: clipboardNode = resolveNodeId(workingNode.getId(),
303: clipboardPK.getId());
304: clipboardPage = pageId;
305: setDoReload(true);
306: } else if ("cut".equals(action)) {
307: FxPK clipboardPK = getPkFromRParam("pk");
308: clipboardFunction = CLIPBOARD_FUCTION.CUT;
309: clipboardNode = resolveNodeId(workingNode.getId(),
310: clipboardPK.getId());
311: clipboardPage = pageId;
312: setDoReload(true);
313: } else if ("paste".equals(action)) {
314: doPaste();
315: }
316:
317: }
318: return result;
319: }
320:
321: private boolean isBeforeAncore() {
322: return !FxJsfUtils.hasParameter("beforeAncor")
323: || Boolean.valueOf(FxJsfUtils
324: .getParameter("beforeAncor"));
325: }
326:
327: /**
328: * Helper functions, reads a pk from a request parameter.
329: *
330: * @param param the parameter name that contains the pk
331: * @return the pk, or null if not set
332: */
333: private FxPK getPkFromRParam(String param) {
334: try {
335: String split[] = FxJsfUtils.getParameter(param)
336: .split("\\.");
337: long id = Long.valueOf(split[0]);
338: int ver = Integer.valueOf(split[1]);
339: return new FxPK(id, ver);
340: } catch (Throwable t) {
341: return null;
342: }
343: }
344:
345: /**
346: * Pastes the pk in the clipboard to the desired position (defined by the ancor pl).
347: */
348: private void doPaste() throws FxApplicationException {
349: setDoReload(true);
350: FxTreeNode newParent = tree.getTree(Edit, workingNode.getId(),
351: 1);
352: FxPK ancorPk = getPkFromRParam("ancorPk");
353: int ancorPos = 0;
354: if (ancorPk != null) {
355: long ancorContentId = getPkFromRParam("ancorPk").getId();
356: ancorPos = resolveAncorPosition(newParent, clipboardNode,
357: ancorContentId);
358: }
359: if (!isBeforeAncore()) {
360: ancorPos = ancorPos + 1;
361: }
362: if (clipboardFunction == CLIPBOARD_FUCTION.CUT) {
363: // Move the node to its new position
364: tree.move(clipboardNode.getMode(),
365: clipboardNode.getId()/*node to move*/, workingNode
366: .getId()/*parent*/, ancorPos/*new pos*/);
367: } else {
368: // Create a new node that references the instance in the clipboard
369: tree.save(FxTreeNodeEdit.createNewChildNode(workingNode)
370: .setName(String.valueOf(clipboardNode.getId()))
371: .setReference(clipboardNode.getReference()));
372: }
373: // Clear clipboard
374: clipboardFunction = null;
375: clipboardNode = null;
376: clipboardPage = null;
377: }
378:
379: /**
380: * Returns the id of the treenode that references the contentInstanceId in
381: * the current page.
382: *
383: * @param contentInstanceId the instance if to look for
384: * @param rootNode the root node to look in
385: * @return the tree node
386: * @throws FxApplicationException on errors
387: */
388: private FxTreeNode resolveNodeId(long rootNode,
389: long contentInstanceId) throws FxApplicationException {
390: FxTreeNode node = tree.getTree(Edit, rootNode, 1);
391: if (node.getReference().getId() == contentInstanceId) {
392: return node;
393: } else {
394: for (FxTreeNode child : node.getChildren()) {
395: if (child.getReference().getId() == contentInstanceId) {
396: return child;
397: }
398: }
399: }
400: return null;
401: }
402:
403: private int resolveAncorPosition(FxTreeNode newParent,
404: FxTreeNode nodeToMove, long ancorContentId)
405: throws FxApplicationException {
406: int pos = 0;
407: int offset = 0;
408: // Check if the childs of the new Parent are loaded
409: if (newParent.getDirectChildCount() > 0
410: && newParent.getChildren().size() == 0) {
411: newParent = tree.getTree(Edit, newParent.getId(), 1); // TODO: EDIT/LIVE
412: }
413: for (FxTreeNode child : newParent.getChildren()) {
414: if (nodeToMove != null
415: && child.getId() == nodeToMove.getId()) {
416: offset += 1;
417: }
418: if (child.getReference().getId() == ancorContentId) {
419: break;
420: }
421: pos++;
422: }
423: return pos - offset;
424: }
425:
426: private void setReloadUrl() {
427: try {
428: // TODO: toggle edit / live!!
429: String path = EJBLookup.getTreeEngine().getPaths(Edit,
430: new long[] { pageId }).get(0);
431: // TODO: this is a fake!!
432: if (path.startsWith("/a1.net")) {
433: path = path.substring("/a1.net".length());
434: } else if (path.startsWith("/bob.at")) {
435: path = path.substring("/bob.at".length());
436: }
437: this .reloadUrl = URLEncoder.encode(path, "UTF-8");
438: } catch (Throwable t) {
439: // eg content does no longer exist
440: this .reloadUrl = "/";
441: }
442: }
443:
444: public String getReloadUrl() {
445: return this .reloadUrl;
446: }
447:
448: public boolean getReloadPageOnClose() {
449: return doReload;
450: }
451:
452: /**
453: * Returns the active content editor beans of the calling jsf user session,
454: * or null id none is available at the call time
455: *
456: * @return the content editor beans, or null
457: */
458: public ContentEditorBean getInstance() {
459: return super.getInstance();
460: }
461:
462: }
|