001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.lenya.cms.site.usecases;
019:
020: import java.text.SimpleDateFormat;
021: import java.util.Arrays;
022: import java.util.Collections;
023: import java.util.GregorianCalendar;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.avalon.framework.service.ServiceSelector;
028: import org.apache.cocoon.components.ContextHelper;
029: import org.apache.cocoon.environment.ObjectModelHelper;
030: import org.apache.cocoon.environment.Request;
031: import org.apache.cocoon.environment.Session;
032: import org.apache.lenya.ac.Identity;
033: import org.apache.lenya.ac.User;
034: import org.apache.lenya.cms.metadata.MetaData;
035: import org.apache.lenya.cms.metadata.MetaDataException;
036: import org.apache.lenya.cms.metadata.dublincore.DublinCore;
037: import org.apache.lenya.cms.publication.Document;
038: import org.apache.lenya.cms.publication.DocumentManager;
039: import org.apache.lenya.cms.publication.Publication;
040: import org.apache.lenya.cms.publication.PublicationException;
041: import org.apache.lenya.cms.publication.PublicationUtil;
042: import org.apache.lenya.cms.publication.ResourceType;
043: import org.apache.lenya.cms.publication.URLInformation;
044: import org.apache.lenya.cms.repository.Node;
045: import org.apache.lenya.cms.site.SiteStructure;
046: import org.apache.lenya.cms.usecase.AbstractUsecase;
047: import org.apache.lenya.cms.usecase.UsecaseException;
048:
049: /**
050: * <p>
051: * Abstract superclass for usecases to create a document.
052: * </p>
053: * <p>
054: * You can pass the following parameters into the usecase:
055: * </p>
056: * <ul>
057: * <li><strong>path</strong> - the path of the document to create (optional)</li>
058: * </ul>
059: *
060: * @version $Id: Create.java 571550 2007-08-31 19:18:54Z rfrovarp $
061: */
062: public abstract class Create extends AbstractUsecase {
063:
064: protected static final String RESOURCE_TYPES = "resourceTypes";
065: protected static final String LANGUAGE = "language";
066: protected static final String LANGUAGES = "languages";
067: protected static final String PATH = "path";
068: protected static final String NODE_NAME = "nodeName";
069: protected static final String VISIBLEINNAV = "visibleInNav";
070: protected static final String SAMPLE = "sample";
071: protected static final String SAMPLES = "samples";
072:
073: /**
074: * Ctor.
075: */
076: public Create() {
077: super ();
078: }
079:
080: /**
081: * @see org.apache.lenya.cms.usecase.AbstractUsecase#doCheckPreconditions()
082: */
083: protected void doCheckPreconditions() throws Exception {
084: super .doCheckPreconditions();
085:
086: if (!getArea().equals(Publication.AUTHORING_AREA)) {
087: addErrorMessage("This usecase can only be invoked in the authoring area!");
088: }
089: }
090:
091: /**
092: * @see org.apache.lenya.cms.usecase.AbstractUsecase#getNodesToLock()
093: */
094: protected Node[] getNodesToLock() throws UsecaseException {
095: try {
096: SiteStructure structure = getPublication().getArea(
097: getArea()).getSite();
098: Node[] nodes = { structure.getRepositoryNode() };
099: return nodes;
100: } catch (Exception e) {
101: throw new UsecaseException(e);
102: }
103: }
104:
105: /**
106: * @see org.apache.lenya.cms.usecase.AbstractUsecase#doCheckExecutionConditions()
107: */
108: protected void doCheckExecutionConditions() throws Exception {
109: String navigationTitle = getDublinCoreParameter(DublinCore.ELEMENT_TITLE);
110: if (navigationTitle.trim().equals("")) {
111: addErrorMessage("missing-navigation-title");
112: }
113:
114: if (getInitialDocument() == null) {
115: String[] samples = (String[]) getParameter(SAMPLES);
116: String sample = getParameterAsString(SAMPLE);
117: if (samples != null && samples.length > 1
118: && (sample == null || sample.equals(""))) {
119: addErrorMessage("missing-page-layout");
120: }
121: }
122:
123: if (isPathValid()) {
124: String path = getNewDocumentPath();
125: SiteStructure site = getPublication().getArea(getArea())
126: .getSite();
127: if (!createVersion() && site.contains(path)) {
128: String[] params = { path };
129: addErrorMessage("path-already-exists", params);
130: }
131: }
132:
133: String doctypeName = getDocumentTypeName();
134: if (getParameterAsString(SAMPLE) == null && doctypeName != null) {
135: initSampleParameters();
136: }
137: }
138:
139: /**
140: * This method is used by {@link #doCheckExecutionConditions()} to check if
141: * the path entered by the user is valid. If not, checking the existence of
142: * the new document in the site structure is omitted because this operation
143: * could cause errors.
144: * @return A boolean value.
145: */
146: protected boolean isPathValid() {
147: return true;
148: }
149:
150: /**
151: * @see org.apache.lenya.cms.usecase.AbstractUsecase#doExecute()
152: */
153: protected void doExecute() throws Exception {
154: super .doExecute();
155:
156: // create new document
157: DocumentManager documentManager = null;
158: ServiceSelector selector = null;
159: ResourceType resourceType = null;
160: try {
161:
162: documentManager = (DocumentManager) this .manager
163: .lookup(DocumentManager.ROLE);
164:
165: String language = getParameterAsString(LANGUAGE);
166: Document initialDocument = getInitialDocument();
167:
168: Document document;
169:
170: String title = getDublinCoreParameter(
171: DublinCore.ELEMENT_TITLE).trim();
172:
173: if (createVersion()) {
174: document = documentManager.addVersion(initialDocument,
175: getArea(), language, true);
176: document.getLink().setLabel(title);
177: } else {
178: if (initialDocument == null) {
179: selector = (ServiceSelector) this .manager
180: .lookup(ResourceType.ROLE + "Selector");
181: resourceType = (ResourceType) selector
182: .select(getDocumentTypeName());
183: String sampleName = getParameterAsString(SAMPLE,
184: resourceType.getSampleNames()[0]);
185: ResourceType.Sample sample = resourceType
186: .getSample(sampleName);
187: document = documentManager.add(
188: getDocumentFactory(), resourceType, sample
189: .getUri(), getPublication(),
190: getArea(), getNewDocumentPath(), language,
191: getSourceExtension(), title,
192: getVisibleInNav());
193: document.setMimeType(sample.getMimeType());
194: } else {
195: document = documentManager.add(initialDocument,
196: getArea(), getNewDocumentPath(), language,
197: getSourceExtension(), title,
198: getVisibleInNav());
199: }
200: }
201:
202: setMetaData(document);
203:
204: // the location to navigate to after completion of usecase
205: setDefaultTargetURL(document.getCanonicalWebappURL());
206:
207: } finally {
208: if (documentManager != null) {
209: this .manager.release(documentManager);
210: }
211: if (selector != null) {
212: if (resourceType != null) {
213: selector.release(resourceType);
214: }
215: this .manager.release(selector);
216: }
217: }
218: }
219:
220: protected String getDublinCoreParameter(String name) {
221: Object param = getParameter(DUBLIN_CORE_PREFIX + name);
222: if (param != null
223: && getParameter(DUBLIN_CORE_PREFIX + name).getClass()
224: .isArray()) {
225: String[] values = (String[]) getParameter(DUBLIN_CORE_PREFIX
226: + name);
227: StringBuffer paramValue = new StringBuffer();
228: for (int i = 0; i < values.length; i++) {
229: String value = values[i];
230: if (i > 0)
231: paramValue.append(',').append(value);
232: else
233: paramValue.append(value);
234: }
235: return paramValue.toString();
236: }
237: return getParameterAsString(DUBLIN_CORE_PREFIX + name, null);
238: }
239:
240: protected void setDublinCoreParameter(String name, String value) {
241: setParameter(DUBLIN_CORE_PREFIX + name, value);
242: }
243:
244: protected abstract boolean createVersion();
245:
246: /**
247: * @return the extension to use for the document source.
248: */
249: protected abstract String getSourceExtension();
250:
251: /**
252: * @return the name of the document being created in the usecase
253: */
254: protected abstract String getNewDocumentName();
255:
256: /**
257: * @return the id of the new document being created in the usecase
258: */
259: protected abstract String getNewDocumentPath();
260:
261: /**
262: * If the document created in the usecase shall have initial contents copied
263: * from an existing document, construct that document in this method.
264: *
265: * @return A document.
266: */
267: protected Document getInitialDocument() {
268: return null;
269: }
270:
271: /**
272: * @return The type of the created document.
273: */
274: protected abstract String getDocumentTypeName();
275:
276: protected static final String DUBLIN_CORE_PREFIX = "dublincore.";
277:
278: /**
279: * Sets the meta data of the created document.
280: *
281: * @param document The document.
282: * @throws MetaDataException if an error occurs.
283: */
284: protected void setMetaData(Document document)
285: throws MetaDataException {
286:
287: if (document == null)
288: throw new IllegalArgumentException(
289: "parameter document may not be null");
290:
291: MetaData dcMetaData = document
292: .getMetaData(DublinCore.DC_NAMESPACE);
293: String[] dcKeys = dcMetaData.getAvailableKeys();
294:
295: for (int i = 0; i < dcKeys.length; i++) {
296: String param = getDublinCoreParameter(dcKeys[i]);
297: if (param != null) {
298: dcMetaData.setValue(dcKeys[i], param);
299: }
300: }
301:
302: }
303:
304: /**
305: * @see org.apache.lenya.cms.usecase.AbstractUsecase#initParameters()
306: */
307: protected void initParameters() {
308: super .initParameters();
309:
310: Map objectModel = ContextHelper.getObjectModel(getContext());
311: Request request = ObjectModelHelper.getRequest(objectModel);
312: Session session = request.getSession(false);
313: Identity identity = (Identity) session
314: .getAttribute(Identity.class.getName());
315: User user = identity.getUser();
316: if (user != null) {
317: setDublinCoreParameter(DublinCore.ELEMENT_CREATOR, user
318: .getId());
319: } else {
320: setDublinCoreParameter(DublinCore.ELEMENT_CREATOR, "");
321: }
322:
323: SimpleDateFormat format = new SimpleDateFormat(
324: "yyyy-MM-dd HH:mm:ss");
325: setDublinCoreParameter(DublinCore.ELEMENT_DATE, format
326: .format(new GregorianCalendar().getTime()));
327:
328: String doctypeName = getDocumentTypeName();
329: if (doctypeName != null) {
330: initSampleParameters();
331: setParameter(RESOURCE_TYPES, Collections.EMPTY_LIST);
332: } else {
333: String[] resourceTypes = getPublication()
334: .getResourceTypeNames();
335: setParameter(RESOURCE_TYPES, Arrays.asList(resourceTypes));
336: }
337: }
338:
339: protected void initSampleParameters() {
340: ServiceSelector selector = null;
341: ResourceType resourceType = null;
342: try {
343: selector = (ServiceSelector) this .manager
344: .lookup(ResourceType.ROLE + "Selector");
345: resourceType = (ResourceType) selector
346: .select(getDocumentTypeName());
347: String[] samples = resourceType.getSampleNames();
348: if (samples.length == 0) {
349: addErrorMessage("The resource type ["
350: + resourceType.getName()
351: + "] doesn't provide any samples!");
352: } else {
353: setParameter(SAMPLES, samples);
354: String presetSample = getParameterAsString(SAMPLE);
355: if (presetSample == null) {
356: setParameter(SAMPLE, samples[0]);
357: } else {
358: List sampleList = Arrays.asList(samples);
359: if (!sampleList.contains(presetSample)) {
360: getLogger()
361: .warn(
362: "Sample ["
363: + presetSample
364: + "] not defined, using default sample.");
365: setParameter(SAMPLE, samples[0]);
366: }
367: }
368: }
369: } catch (Exception e) {
370: throw new RuntimeException(e);
371: } finally {
372: if (selector != null) {
373: if (resourceType != null) {
374: selector.release(resourceType);
375: }
376: this .manager.release(selector);
377: }
378: }
379: }
380:
381: /**
382: * @return The source document or <code>null</code> if the usecase was not
383: * invoked on a document.
384: */
385: protected Document getSourceDocument() {
386: Document document = null;
387: String url = getSourceURL();
388: try {
389: if (getDocumentFactory().isDocument(url)) {
390: document = getDocumentFactory().getFromURL(url);
391: }
392: } catch (Exception e) {
393: throw new RuntimeException(e);
394: }
395: return document;
396: }
397:
398: /**
399: * @return The new document.
400: */
401: protected Document getNewDocument() {
402: try {
403: String path = getNewDocumentPath();
404: String language = getParameterAsString(LANGUAGE);
405: return getPublication().getArea(getArea()).getSite()
406: .getNode(path).getLink(language).getDocument();
407: } catch (Exception e) {
408: throw new RuntimeException(e);
409: }
410: }
411:
412: /**
413: * @return The area without the "info-" prefix.
414: */
415: public String getArea() {
416: URLInformation info = new URLInformation(getSourceURL());
417: return info.getArea();
418: }
419:
420: /**
421: * Access to the current publication. Use this when the publication is not
422: * yet known in the usecase: e.g. when creating a global asset. When adding
423: * a resource or a child to a document, access the publication via that
424: * document's interface instead.
425: *
426: * @return the publication in which the use-case is being executed
427: */
428: protected Publication getPublication() {
429: try {
430: return PublicationUtil.getPublicationFromUrl(this .manager,
431: getDocumentFactory(), getSourceURL());
432: } catch (PublicationException e) {
433: throw new RuntimeException(e);
434: }
435: }
436:
437: /**
438: * @return the visibleInNav Attribute of the document being created in the
439: * usecase
440: */
441: protected boolean getVisibleInNav() {
442: if (getParameterAsString(VISIBLEINNAV).equals("false")) {
443: return false;
444: }
445: return true;
446: }
447:
448: }
|