- add moved class to core-cocoon
git-svn-id: https://svn.apache.org/repos/asf/lenya/trunk@1034327 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.lenya.core.cocoon/pom.xml b/org.apache.lenya.core.cocoon/pom.xml
index 844ce91..0b3fb15 100644
--- a/org.apache.lenya.core.cocoon/pom.xml
+++ b/org.apache.lenya.core.cocoon/pom.xml
@@ -16,7 +16,14 @@
<description>Contains core cocoon components defined for Lenya</description>
<dependencies>
-
+ <dependency>
+ <groupId>org.apache.lenya</groupId>
+ <artifactId>lenya-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lenya</groupId>
+ <artifactId>lenya-core-impl</artifactId>
+ </dependency>
<!-- TODO : see if all theses dependencies are required -->
<dependency>
<groupId>org.apache.cocoon</groupId>
diff --git a/org.apache.lenya.core.cocoon/rcl.properties b/org.apache.lenya.core.cocoon/rcl.properties
index 711c321..8fe3e22 100644
--- a/org.apache.lenya.core.cocoon/rcl.properties
+++ b/org.apache.lenya.core.cocoon/rcl.properties
@@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-org.apache.lenya.lenya-core-cocoon-components.service%classes-dir=./target/classes
\ No newline at end of file
+org.apache.lenya.core.cocoon.block%classes-dir=./target/classes
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java
new file mode 100644
index 0000000..5996053
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.cms.publication.Document;
+
+
+/**
+ * An action that tests if a document is already checked out by a given user.
+ * If it isn't, a check out will be tried.
+ */
+public class DiscoverCheckoutAction extends RevisionControllerAction {
+
+ /**
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src,
+ Parameters parameters) throws Exception {
+ super.act(redirector, resolver, objectModel, src, parameters);
+
+ HashMap actionMap = new HashMap();
+
+ Document document = getDocument();
+ if (document.isCheckedOut()) {
+ actionMap.put("filename", document.getSourceURI());
+ actionMap.put("user", document.getCheckoutUserId());
+ return actionMap;
+ }
+ return null;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java
new file mode 100644
index 0000000..b927992
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.acting.AbstractAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * Action that checks if the current URL represents an existing document.
+ */
+public class LanguageExistsAction extends AbstractAction {
+
+ private Repository repository;
+
+ /**
+ * Check if the current URL represents an existing document.
+ * @return an empty <code>Map</code> if there is a version of this document for the current
+ * language, <code>null</code> otherwise.
+ * @throws Exception if an error occurs
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source,
+ Parameters parameters) throws Exception {
+
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ Session session = this.repository.getSession(request);
+
+ String url = ServletHelper.getWebappURI(request);
+ if (session.getUriHandler().isDocument(url)) {
+ return Collections.unmodifiableMap(Collections.EMPTY_MAP);
+ }
+ else {
+ return null;
+ }
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java
new file mode 100644
index 0000000..d776919
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.ac.Identity;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * Checkin document
+ */
+public class ReservedCheckinAction extends RevisionControllerAction {
+
+ protected Repository repository;
+
+ /**
+ * Checkin document
+ * @return HashMap with checkin parameters
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector,
+ * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String,
+ * org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src,
+ Parameters parameters) throws Exception {
+ super.act(redirector, resolver, objectModel, src, parameters);
+
+ try {
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ Identity identity = (Identity) request.getSession().getAttribute(Identity.class.getName());
+ Session session = this.repository.startSession(identity, true);
+
+ String url = ServletHelper.getWebappURI(request);
+ if (session.getUriHandler().isDocument(url)) {
+ Document document = session.getUriHandler().getDocument(url);
+ if (document.isCheckedOutBySession(session.getId(), session.getIdentity().getUser().getId())) {
+ document.checkin();
+ }
+ }
+ else {
+ throw new RuntimeException("The URL [" + url + "] doesn't represent a document.");
+ }
+
+ } catch (final Exception e) {
+ getLogger().error("Could not check in node: ", e);
+ Map actionMap = new HashMap();
+ actionMap.put("exception", "genericException");
+ actionMap.put("filename", getDocument().getSourceURI());
+ actionMap.put("message", e.getMessage());
+ return actionMap;
+ }
+
+ return null;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java
new file mode 100644
index 0000000..b252a48
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.RepositoryException;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * Action doing reserved checkout
+ */
+public class ReservedCheckoutAction extends RevisionControllerAction {
+
+ /**
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src,
+ Parameters parameters) throws Exception {
+ super.act(redirector, resolver, objectModel, src, parameters);
+
+ HashMap actionMap = new HashMap();
+ Document doc = getDocument();
+
+ //check out
+ try {
+
+ String username = getUsername();
+
+ assert doc != null;
+ assert username != null;
+
+ Session session = doc.getSession();
+ if (!doc.isCheckedOutBySession(session.getId(), session.getIdentity().getUser().getId())) {
+ doc.checkout();
+ }
+ } catch (RepositoryException e) {
+ actionMap.put("exception", "genericException");
+ actionMap.put("filename", doc.getSourceURI());
+ actionMap.put("message", "" + e.getMessage());
+ getLogger().error("The document " + doc.getSourceURI() + " couldn't be checked out: ", e);
+
+ return actionMap;
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java
new file mode 100644
index 0000000..11e0f55
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.RepositoryException;
+
+/**
+ * An action that tests if a document is already checked out by a given user. If it isn't, a check
+ * out will be tried.
+ */
+
+public class ReservedCheckoutTestAction extends RevisionControllerAction {
+
+ /**
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector,
+ * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String,
+ * org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src,
+ Parameters parameters) throws Exception {
+ super.act(redirector, resolver, objectModel, src, parameters);
+
+ HashMap actionMap = new HashMap();
+
+ Document doc = getDocument();
+ try {
+
+ if (!doc.isCheckedOut() || !doc.getCheckoutUserId().equals(getUsername())) {
+ // check out
+ doc.checkout();
+ }
+ } catch (RepositoryException e) {
+ actionMap.put("exception", "RepositoryException");
+ actionMap.put("filename", doc.getSourceURI());
+
+ return actionMap;
+ } catch (Exception e) {
+ actionMap.put("exception", "genericException");
+ actionMap.put("filename", doc.getSourceURI());
+ actionMap.put("message", e.getMessage());
+ getLogger().error(
+ ".act(): The document " + doc.getSourceURI() + " couldn't be checked out: ",
+ e);
+
+ return actionMap;
+ }
+ return null;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java
new file mode 100644
index 0000000..24fe72d
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.acting.AbstractAction;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.TraversableSource;
+
+
+/**
+ * This action simply checks to see if a given resource exists. It checks
+ * whether the specified in the src attribute source exists or not.
+ * The action returns empty <code>Map</code> if it exists, null otherwise.
+ * <p>Instead of src attribute, source can be specified using
+ * parameter named 'url' (this is old syntax).
+ * <p>In order to differentiate between files and directories, the type can be specified
+ * using the parameter 'type' (<map:parameter name="type" value="file"/> or
+ * <map:parameter name="type" value="directory"/>). The parameter 'type' is optional.
+ * <p>
+ * <strong>Note:</strong> {@link org.apache.cocoon.selection.ResourceExistsSelector}
+ * should be preferred to this component, as the semantics of a Selector better
+ * match the supplied functionality.
+ */
+public class ResourceExistsAction extends AbstractAction implements ThreadSafe {
+ /**
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source,
+ Parameters parameters) throws Exception {
+ String url = parameters.getParameter("url", source);
+ String type = parameters.getParameter("type", "resource");
+ Source src = null;
+
+ try {
+ src = resolver.resolveURI(url);
+
+ if (src.exists()) {
+
+ boolean isCollection = false;
+ if (src instanceof TraversableSource) {
+ TraversableSource traversableSource = (TraversableSource) src;
+ isCollection = traversableSource.isCollection();
+ }
+
+ boolean exists = type.equals("resource")
+ || type.equals("file") && !isCollection
+ || type.equals("directory") && isCollection;
+
+ if (exists) {
+ getLogger().debug(type + " exists: " + src.getURI());
+ return Collections.EMPTY_MAP;
+ }
+ }
+ getLogger().debug(".act(): Resource " + source + " as type \"" + type +
+ "\" does not exist");
+ } finally {
+ if (src != null) {
+ resolver.release(src);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java
new file mode 100644
index 0000000..4528325
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.cocoon.acting;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.lenya.ac.Identity;
+import org.apache.lenya.ac.User;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.PageEnvelope;
+import org.apache.lenya.cms.publication.PageEnvelopeFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * Revision controller action.
+ *
+ * @version $Id$
+ */
+public class RevisionControllerAction extends ServiceableAction {
+
+ private String username = null;
+ private Repository repository;
+ private Document document;
+
+ /**
+ * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector,
+ * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String,
+ * org.apache.avalon.framework.parameters.Parameters)
+ */
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src,
+ Parameters parameters) throws Exception {
+ // Get request object
+ Request request = ObjectModelHelper.getRequest(objectModel);
+
+ if (request == null) {
+ getLogger().error(".act(): No request object");
+
+ return null;
+ }
+
+ Session repoSession = this.repository.getSession(request);
+
+ PageEnvelope envelope = null;
+ String id = new URLInformation(ServletHelper.getWebappURI(request)).getPublicationId();
+ Publication publication = repoSession.getPublication(id);
+
+ Document doc = null;
+
+ try {
+ envelope = PageEnvelopeFactory.getInstance().getPageEnvelope(objectModel, publication);
+ doc = envelope.getDocument();
+ } catch (Exception e) {
+ getLogger().error("Resolving page envelope failed: ", e);
+ throw e;
+ }
+
+ // Get session
+ HttpSession session = request.getSession(false);
+
+ if (session == null) {
+ getLogger().error(".act(): No session object");
+
+ return null;
+ }
+
+ Identity identity = (Identity) session.getAttribute(Identity.class.getName());
+ getLogger().debug(".act(): Identity: " + identity);
+
+ // FIXME: hack because of the uri for the editor bitflux. The filename
+ // cannot be get from
+ // the page-envelope
+
+ String path = doc.getPath();
+ int bx = path.lastIndexOf("-bxe");
+
+ if (bx > 0) {
+ String language = doc.getLanguage();
+
+ int l = path.length();
+ int bxLength = "-bxe".length();
+ int lang = path.lastIndexOf("_", bx);
+ int langLength = bx - lang;
+
+ if (bx > 0 && bx + bxLength <= l) {
+ path = path.substring(0, bx) + path.substring(bx + bxLength, l);
+
+ if (lang > 0 && langLength + lang < l) {
+ language = path.substring(lang + 1, lang + langLength);
+ path = path.substring(0, lang)
+ + path.substring(lang + langLength, l - bxLength);
+ }
+ }
+
+ this.document = doc.area().getSite().getNode(path).getLink(language).getDocument();
+
+ } else {
+ this.document = doc;
+ }
+
+ this.username = null;
+
+ if (identity != null) {
+ User user = identity.getUser();
+ if (user != null) {
+ this.username = user.getId();
+ }
+ } else {
+ getLogger().error(".act(): No identity yet");
+ }
+
+ getLogger().debug(".act(): Username: " + this.username);
+
+ return null;
+ }
+
+ /**
+ * Get the node.
+ * @return the node
+ */
+ protected Document getDocument() {
+ return this.document;
+ }
+
+ /**
+ * Get the user name.
+ * @return the user name
+ */
+ protected String getUsername() {
+ return this.username;
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java
new file mode 100644
index 0000000..8dcb0cb
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lenya.cms.cocoon.components.context;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.cocoon.components.ContextHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.Response;
+import org.apache.cocoon.util.AbstractLogEnabled;
+
+/**
+ * Utility class for getting the context, request, response and
+ * object model of the current request.
+ * @deprecated
+ */
+public class ContextUtility extends AbstractLogEnabled implements
+ Component, Contextualizable {
+ /**
+ * The component's role.
+ */
+ public static final String ROLE = ContextUtility.class.getName();
+
+ protected Context context;
+
+
+ /**
+ * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
+ */
+ public void contextualize(Context context) throws ContextException {
+ this.context = context;
+ }
+
+ /**
+ * Get the context object of the current request.
+ * @return The context object of the current request.
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * Get the request object of the current request.
+ * @return The request object of the current request.
+ */
+ public Request getRequest() {
+ return ContextHelper.getRequest(context);
+ }
+
+ /**
+ * Get the response object of the current request.
+ * @return The response object of the current request.
+ */
+ public Response getResponse() {
+ return ContextHelper.getResponse(context);
+ }
+
+ /**
+ * Get the object model of the current request.
+ * @return The object model of the current request.
+ */
+ public Map getObjectModel() {
+ return ContextHelper.getObjectModel(context);
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java
new file mode 100644
index 0000000..9a6c6a0
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.lenya.cms.publication.PageEnvelope;
+import org.apache.lenya.cms.publication.PageEnvelopeFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.util.ServletHelper;
+import org.apache.lenya.utils.URLInformation;
+/**
+ * Abstract superclass for classes which need access to the page envelope.
+ *
+ * The web application URL can be provided in the attribute name, separated by a colon (":").
+ */
+public abstract class AbstractPageEnvelopeModule extends OperationModule {
+
+ /**
+ * Get the the page envelope for the given objectModel.
+ * @param objectModel the objectModel for which the page enevelope is requested.
+ * @param name The attribute name.
+ * @return a <code>PageEnvelope</code>
+ * @throws ConfigurationException if the page envelope could not be instantiated.
+ */
+ protected PageEnvelope getEnvelope(Map objectModel, String name) throws ConfigurationException {
+
+ String webappUrl = null;
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ URLInformation urlInfo = new URLInformation();
+
+ PageEnvelope envelope = (PageEnvelope) request.getAttribute(PageEnvelope.class.getName());
+ if (envelope == null) {
+
+ String[] snippets = name.split(":");
+ if (snippets.length > 1) {
+ webappUrl = snippets[1];
+ } else {
+ webappUrl = urlInfo.getWebappUrl();
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Resolving page envelope for URL [" + webappUrl + "]");
+ }
+
+ String contextPath = request.getContextPath();
+
+ try {
+ Session session = getSession();
+ Publication pub = null;
+ String pubId = urlInfo.getPublicationId();
+ if (pubId != null && session.existsPublication(pubId)) {
+ pub = session.getPublication(pubId);
+ }
+ envelope = PageEnvelopeFactory.getInstance().getPageEnvelope(
+ contextPath,
+ webappUrl,
+ pub);
+ } catch (Exception e) {
+ throw new ConfigurationException("Resolving page envelope failed: ", e);
+ }
+ request.setAttribute(PageEnvelope.class.getName(), envelope);
+ }
+ return envelope;
+ }
+
+ /**
+ * @param name The original attribute name.
+ * @return The attribute name without URL attachment.
+ */
+ protected String getAttributeName(String name) {
+ final String[] snippets = name.split(":");
+ return snippets[0];
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java
new file mode 100644
index 0000000..583cf0c
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+
+/**
+ * The DateConverterModule converts a date string from one format into
+ * another format.
+ * The conversion is defined by the nested elements <src-pattern/> and
+ * <pattern/> of the module declaration.
+ *
+ */
+public class DateConverterModule extends AbstractInputModule implements ThreadSafe {
+
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel) throws ConfigurationException {
+
+ String srcPattern = (String) this.settings.get("src-pattern");
+ String pattern = (String) this.settings.get("pattern");
+
+ if (modeConf != null) {
+ srcPattern = modeConf.getChild("src-pattern").getValue(srcPattern);
+ pattern = modeConf.getChild("pattern").getValue(pattern);
+ }
+
+ if (srcPattern==null) {
+ throw new ConfigurationException("Source date pattern not specified.");
+ }
+ if (pattern==null) {
+ throw new ConfigurationException("Date pattern not specified.");
+ }
+
+ try {
+ SimpleDateFormat srcFormat = new SimpleDateFormat(srcPattern);
+ SimpleDateFormat format = new SimpleDateFormat(pattern);
+ Date date = srcFormat.parse(name);
+ return format.format(date);
+ } catch (Exception e) {
+ throw new ConfigurationException("Could not convert date: "+name, e);
+ }
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java
new file mode 100644
index 0000000..04c5444
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id:$ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentException;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * Input module to get document information.
+ * {doc-info:{publication-id}:{area}:{uuid}:{document-language}:{property}} where {property} may be:
+ * <ul>
+ * <li><strong><code>contentLength</code></strong> - the content length (number of bytes).</li>
+ * <li><strong><code>expires</code></strong> - the expiration date in ISO 8601 format.</li>
+ * <li><strong><code>lastModified</code></strong> - the last modification date in ISO 8601
+ * format.</li>
+ * <li><strong><code>mimeType</code></strong> - the MIME type</li>
+ * <li><strong><code>nodeName</code></strong> - the name of the node in the site structure</li>
+ * <li><strong><code>path</code></strong> - the path in the site structure (starting with a
+ * slash) or an empty string if the document is not referenced in the site structure.</li>
+ * <li><strong><code>resourceType</code></strong> - the name of the resource type</li>
+ * <li><strong><code>sourceExtension</code></strong> - the source extension</li>
+ * <li><strong><code>visibleInNav</code></strong> - <code>true</code> if the document's node
+ * is visible in the navigation, <code>false</code> otherwise.</li>
+ * <li><strong><code>webappUrl</code></strong> - the web application URL of the document or
+ * an empty string if the document is not referenced in the site structure.</li>
+ * </ul>
+ */
+public class DocumentInfoModule extends AbstractInputModule {
+
+ // Input module parameters:
+ protected final static String PARAM_PUBLICATION_ID = "publication-id";
+ protected final static String PARAM_AREA = "area";
+ protected final static String PARAM_UUID = "uuid";
+ protected final static String PARAM_DOCUMENT_LANGUAGE = "document-language";
+ protected final static String PARAM_PROPERTY = "property";
+ protected final static String PARAM_REVISION = "revision";
+ protected final static int MIN_MANDATORY_PARAMS = 5;
+
+ protected final static String UUID = "uuid";
+ protected final static String LANGUAGE = "language";
+ protected final static String PATH = "path";
+ protected final static String NODE_NAME = "nodeName";
+ protected final static String WEBAPP_URL = "webappUrl";
+ protected final static String DOCUMENT_URL = "documentUrl";
+ protected final static String RESOURCE_TYPE = "resourceType";
+ protected final static String LAST_MODIFIED = "lastModified";
+ protected final static String MIME_TYPE = "mimeType";
+ protected final static String CONTENT_LENGTH = "contentLength";
+ protected final static String SOURCE_EXTENSION = "sourceExtension";
+ protected final static String EXPIRES = "expires";
+ protected final static String VISIBLE_IN_NAVIGATION = "visibleInNav";
+
+ protected final static String[] PARAMS = { PARAM_PUBLICATION_ID, PARAM_AREA, PARAM_UUID,
+ PARAM_DOCUMENT_LANGUAGE, PARAM_PROPERTY, PARAM_REVISION };
+
+ protected final static String META_RESOURCE_TYPE = "resourceType";
+ protected final static String META_EXPIRES = "expires";
+
+ protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+
+ private Repository repository;
+
+ /**
+ * Parse the parameters and return a document.
+ * @param publicationId The publication ID.
+ * @param area The area.
+ * @param uuid The document UUID.
+ * @param language The document language.
+ * @param revision The revision.
+ * @param objectModel The object model.
+ * @return The document object created.
+ * @throws ConfigurationException
+ */
+ protected Document getDocument(String publicationId, String area, String uuid, String language,
+ int revision, Map objectModel) throws ConfigurationException {
+ Document document = null;
+
+ Request request = ObjectModelHelper.getRequest(objectModel);
+
+ try {
+ Session session = this.repository.getSession(request);
+ Publication pub = session.getPublication(publicationId);
+ document = pub.getArea(area).getDocument(uuid, language, revision);
+ } catch (Exception e) {
+ throw new ConfigurationException("Error getting document [" + publicationId + ":"
+ + area + ":" + uuid + ":" + language + "]: " + e.getMessage(), e);
+ }
+ return document;
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration, java.util.Map)
+ */
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object value = null;
+
+ InputModuleParameters params = new InputModuleParameters(name, PARAMS, MIN_MANDATORY_PARAMS);
+
+ try {
+ int rev = -1;
+ if (params.isParameter(PARAM_REVISION)) {
+ String revision = params.getParameter(PARAM_REVISION);
+ if (!revision.equals("")) {
+ rev = Integer.valueOf(revision).intValue();
+ }
+ }
+
+ Document document = getDocument(params.getParameter(PARAM_PUBLICATION_ID), params
+ .getParameter(PARAM_AREA), params.getParameter(PARAM_UUID), params
+ .getParameter(PARAM_DOCUMENT_LANGUAGE), rev, objectModel);
+
+ String attribute = params.getParameter(PARAM_PROPERTY);
+
+ if (attribute.equals(RESOURCE_TYPE)) {
+ value = document.getResourceType().getName();
+ } else if (attribute.equals(LAST_MODIFIED)) {
+ value = this.dateFormat.format(new Date(document.getLastModified()));
+ } else if (attribute.equals(MIME_TYPE)) {
+ value = document.getMimeType();
+ } else if (attribute.equals(CONTENT_LENGTH)) {
+ value = Long.toString(document.getContentLength());
+ } else if (attribute.equals(SOURCE_EXTENSION)) {
+ value = document.getSourceExtension();
+ } else if (attribute.equals(LANGUAGE)) {
+ value = document.getLanguage();
+ } else if (attribute.equals(PATH)) {
+ value = document.getPath();
+ } else if (attribute.equals(NODE_NAME)) {
+ value = document.getName();
+ } else if (attribute.equals(UUID)) {
+ value = document.getUUID();
+ } else if (attribute.equals(WEBAPP_URL)) {
+ value = document.getCanonicalWebappURL();
+ } else if (attribute.equals(DOCUMENT_URL)) {
+ value = document.getCanonicalDocumentURL();
+ } else if (attribute.equals(EXPIRES)) {
+ try {
+ Date expires = document.getExpires();
+ value = this.dateFormat.format(expires);
+ } catch (DocumentException e) {
+ throw new ConfigurationException("Error getting expires date from document.", e);
+ }
+ } else if (attribute.equals(VISIBLE_IN_NAVIGATION)) {
+ value = Boolean.toString(isVisibleInNavigation(document));
+ } else {
+ throw new ConfigurationException("Attribute '" + attribute + "' not supported ["
+ + name + "]");
+ }
+ } catch (ConfigurationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ConfigurationException("Error getting input module parameters.", e);
+ }
+
+ return value;
+ }
+
+ protected boolean isVisibleInNavigation(Document document) throws ConfigurationException {
+ try {
+ return document.getLink().getNode().isVisible();
+ } catch (DocumentException e) {
+ throw new ConfigurationException("Obtaining navigation visibility failed [" + document
+ + "]: " + e.getMessage(), e);
+ }
+
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java
new file mode 100644
index 0000000..610e170
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.lenya.cms.publication.util.DocumentHelper;
+
+/**
+ * <p>
+ * This module constructs the document url taking into account difference in the language .version
+ * being created and used.
+ * </p>
+ * <p>
+ * Example:
+ * <code>{document-url:{page-envelope:area}:{page-envelope:document-uuid}:{page-envelope:document-language}}</code>
+ * </p>
+ * @version $Id$
+ */
+public class DocumentURLModule extends AbstractPageEnvelopeModule {
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration, java.util.Map)
+ */
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+
+ String url;
+
+ final String[] attributes = name.split(":");
+
+ if (attributes.length < 3) {
+ throw new ConfigurationException("Invalid number of parameters: " + attributes.length
+ + ". Expected 3 (area, document-uuid, language)");
+ }
+
+ final String area = attributes[0];
+ final String uuid = attributes[1];
+ final String language = attributes[2];
+
+ try {
+ DocumentHelper helper = new DocumentHelper(objectModel);
+ url = helper.getDocumentUrl(uuid, area, language);
+ } catch (Exception e) {
+ throw new ConfigurationException("Resolving attribute [" + name + "] failed: ", e);
+ }
+
+ return url;
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration,
+ * java.util.Map)
+ */
+ public Iterator getAttributeNames(Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ return Collections.EMPTY_SET.iterator();
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration, java.util.Map)
+ */
+ public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object[] objects = { getAttribute(name, modeConf, objectModel) };
+ return objects;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java
new file mode 100644
index 0000000..d09133f
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.commons.lang.Validate;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.store.impl.MRUMemoryStore;
+import org.apache.lenya.cms.cocoon.source.FallbackSourceFactory;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * <p>
+ * This module returns the actual source URI of a fallback:// source. The protocol (fallback,
+ * template-fallback, ...) is configurable via the <em>protocol</em> parameter.
+ * </p>
+ */
+public class FallbackModule extends AbstractInputModule {
+
+ private String protocol;
+ protected MRUMemoryStore store;
+ private Repository repository;
+ private SourceResolver resolver;
+ private static Boolean useCache = null;
+
+ public void setRepository(Repository repo) {
+ this.repository = repo;
+ }
+
+ protected boolean useCache() {
+ return this.store != null;
+ }
+
+ public void setStore(MRUMemoryStore store) {
+ Validate.notNull(store);
+ this.store = store;
+ }
+
+ protected MRUMemoryStore getStore() {
+ return this.store;
+ }
+
+ protected String getPublicationId(Map objectModel) {
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ String webappUri = ServletHelper.getWebappURI(request);
+ URLInformation info = new URLInformation(webappUri);
+ String pubId = null;
+ try {
+ Session session = this.repository.getSession(request);
+ String pubIdCandidate = info.getPublicationId();
+ if (pubIdCandidate != null && session.existsPublication(pubIdCandidate)) {
+ pubId = pubIdCandidate;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return pubId;
+ }
+
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ String uri;
+ String fallbackUri = getFallbackUri(name);
+ if (useCache()) {
+ final String pubId = getPublicationId(objectModel);
+ String cacheKey = FallbackSourceFactory.getCacheKey(pubId, fallbackUri);
+ MRUMemoryStore store = getStore();
+ if (store.containsKey(cacheKey)) {
+ uri = (String) store.get(cacheKey);
+ } else {
+ uri = resolveSourceUri(name);
+ }
+ } else {
+ uri = resolveSourceUri(name);
+ }
+ return uri;
+ }
+
+ protected String resolveSourceUri(String name) throws ConfigurationException {
+ Source source = null;
+ try {
+ source = this.resolver.resolveURI(getFallbackUri(name));
+ return source.getURI();
+ } catch (Exception e) {
+ throw new ConfigurationException("Resolving fallback source [" + name + "] failed: ", e);
+ } finally {
+ if (source != null) {
+ this.resolver.release(source);
+ }
+ }
+ }
+
+ protected String getFallbackUri(String name) {
+ return this.protocol + "://" + name;
+ }
+
+ public Iterator getAttributeNames(Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ return Collections.EMPTY_SET.iterator();
+ }
+
+ public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object[] objects = { getAttribute(name, modeConf, objectModel) };
+ return objects;
+ }
+
+ public void setProtocol(String protocol) {
+ Validate.notNull(protocol);
+ this.protocol = protocol;
+ }
+
+ public void setSourceResolver(SourceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java
new file mode 100644
index 0000000..2ab073d
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id:$ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameters;
+
+/**
+ * Input module parameter class.
+ * Parses input module parameters and offers accessors for the parameters.
+ * The parameters are expected to be separated by ":".
+ */
+public class InputModuleParameters {
+
+ protected final static char PARAM_SEPARATOR = ':';
+
+ protected Parameters params;
+ protected final String[] paramList;
+
+ /**
+ * Parse input module parameters.
+ * @param parameters Parameter to be parsed.
+ * @param paramList List of expected parameters.
+ * @param minParams Minimum number of parameters expected.
+ * @throws ConfigurationException if there is an error parsing the parameters.
+ */
+ public InputModuleParameters(String parameters, final String[] paramList, int minParams)
+ throws ConfigurationException
+ {
+ params = new Parameters();
+ this.paramList = (String[])paramList.clone();
+ parseParameters(parameters, minParams);
+ }
+
+ /**
+ * Parse parameters according to the parameter list passed.
+ * @param parameters
+ * @param minParams Minimum number of parameters.
+ * @return Parameters object initialized with parsed parameters.
+ * @throws ConfigurationException
+ */
+ protected Parameters parseParameters(String parameters, int minParams)
+ throws ConfigurationException
+ {
+ // Parse parameters
+ int start = 0;
+ int end = parameters.indexOf(PARAM_SEPARATOR);
+ for (int i=0; i<paramList.length; i++) {
+ if (end != -1) {
+ String paramToken = parameters.substring(start, end);
+ params.setParameter(paramList[i], paramToken);
+ start = end+1;
+ end = parameters.indexOf(PARAM_SEPARATOR, start+1);
+ } else {
+ if ((i+1) < minParams) {
+ // A mandatory parameter is missing.
+ throw new ConfigurationException("Error parsing parameters: mandatory parameter '"
+ + paramList[i] + "' not found [" + parameters + "]");
+ } else if (i == 0) {
+ // Zero or one parameter passed.
+ if (parameters.length() != 0) {
+ params.setParameter(paramList[i], parameters);
+ }
+ break;
+ } else {
+ // All parameters parsed except the last one.
+ String paramToken = parameters.substring(start);
+ if (paramToken.length() != 0) {
+ params.setParameter(paramList[i], paramToken);
+ }
+ break;
+ }
+ }
+ }
+ return params;
+ }
+
+ /**
+ * Get a parameter.
+ * @param param Name of requested parameter.
+ * @return Requested parameter.
+ * @throws ParameterException if the specified parameter cannot be found
+ */
+ public String getParameter(String param) throws ParameterException
+ {
+ return params.getParameter(param);
+ }
+
+ /**
+ * Does a parameter with given name exists?
+ * @param param Parameter name.
+ * @return True if parameters exists, otherwise false.
+ */
+ public boolean isParameter(String param) {
+ return params.isParameter(param);
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java
new file mode 100644
index 0000000..b90d45a
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * Super class for operation-based input modules.
+ *
+ * @version $Id$
+ */
+public class OperationModule extends AbstractInputModule {
+
+ private Repository repository;
+ private org.apache.lenya.cms.publication.Session session;
+
+ protected Session getSession() {
+ if (this.session == null) {
+ ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = processInfo.getRequest();
+ this.session = this.getRepository().getSession(request);
+ }
+ return this.session;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java
new file mode 100644
index 0000000..407a1b6
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.net.MalformedURLException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.commons.lang.StringUtils;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentLocator;
+import org.apache.lenya.cms.publication.PageEnvelope;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.ResourceType;
+import org.apache.lenya.cms.site.SiteException;
+import org.apache.lenya.utils.URLInformation;
+
+/**
+ * Input module wrapping the page envelope. This module provides publication
+ * related information such as document-uuid, area, publication-id.
+ *
+ * @see org.apache.lenya.cms.publication.PageEnvelope
+ * @deprecated use DocumentInfoModule instead.
+ */
+public class PageEnvelopeModule extends AbstractPageEnvelopeModule {
+
+ protected static final String URI_PARAMETER_DOCTYPE = "doctype";
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration,
+ * java.util.Map)
+ */
+ public Object getAttribute(final String attributeName, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+
+ final String name = getAttributeName(attributeName);
+
+ if (!Arrays.asList(PageEnvelope.PARAMETER_NAMES).contains(name)) {
+ throw new ConfigurationException("The attribute [" + name + "] is not supported!");
+ }
+
+ PageEnvelope envelope = getEnvelope(objectModel, attributeName);
+ Object value = null;
+
+ try {
+ if (name.equals(PageEnvelope.AREA)) {
+ value = envelope.getArea();
+ } else if (name.equals(PageEnvelope.CONTEXT)) {
+ value = envelope.getContext();
+ } else if (name.equals(PageEnvelope.IS_PUBLICATION)) {
+ value = Boolean.toString(envelope.getPublication() != null);
+ } else if (name.equals(PageEnvelope.PUBLICATION_ID)) {
+ Publication pub = envelope.getPublication();
+ value = pub != null ? pub.getId() : "";
+ } else if (name.equals(PageEnvelope.PUBLICATION)) {
+ value = envelope.getPublication();
+ } else if (name.equals(PageEnvelope.PUBLICATION_LANGUAGES_CSV)) {
+ value = StringUtils.join(envelope.getPublication().getLanguages(), ',');
+ } else if (name.equals(PageEnvelope.DEFAULT_LANGUAGE)) {
+ value = envelope.getPublication().getDefaultLanguage();
+ } else if (name.equals(PageEnvelope.LANGUAGE)) {
+ value = envelope.getLanguage();
+ } else if (name.equals(PageEnvelope.BREADCRUMB_PREFIX)) {
+ value = envelope.getPublication().getBreadcrumbPrefix();
+ } else if (name.equals(PageEnvelope.DOCUMENT_PATH)) {
+ value = getPath(envelope, objectModel);
+ } else {
+ Document document = envelope.getDocument();
+ if (document != null) {
+ if (name.equals(PageEnvelope.DOCUMENT)) {
+ value = document;
+ } else if (name.equals(PageEnvelope.DOCUMENT_ID)) {
+ getLogger().warn(
+ "This attribute [ " + name + " ] is deprecated."
+ + " Use document-path or document-uuid instead!");
+ value = document.getUUID();
+ } else if (name.equals(PageEnvelope.DOCUMENT_PARENT)) {
+ value = document.getLocator().getParent().getPath();
+ } else if (name.equals(PageEnvelope.DOCUMENT_NAME)) {
+ value = document.getName();
+ } else if (name.equals(PageEnvelope.DOCUMENT_LABEL)) {
+ value = document.getLink().getLabel();
+ } else if (name.equals(PageEnvelope.DOCUMENT_URL)) {
+ value = document.getCanonicalDocumentURL();
+ } else if (name.equals(PageEnvelope.DOCUMENT_URL_WITHOUT_LANGUAGE)) {
+ value = document.getCanonicalWebappURL();
+ } else if (name.equals(PageEnvelope.DOCUMENT_EXTENSION)) {
+ value = document.getExtension();
+ } else if (name.equals(PageEnvelope.DOCUMENT_SOURCE_EXTENSION)) {
+ value = document.getSourceExtension();
+ } else if (name.equals(PageEnvelope.DOCUMENT_UUID)) {
+ value = document.getUUID();
+ } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGE)) {
+ value = document.getLanguage();
+ } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGES)) {
+ value = document.getLanguages();
+ } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGES_CSV)) {
+ value = StringUtils.join(document.getLanguages(), ',');
+ } else if (name.equals(PageEnvelope.DOCUMENT_LASTMODIFIED)) {
+ Date date = new Date(document.getLastModified());
+ value = new SimpleDateFormat(DATE_FORMAT).format(date);
+ } else if (name.equals(PageEnvelope.DOCUMENT_MIME_TYPE)) {
+ value = document.getMimeType();
+ } else if (name.equals(PageEnvelope.DOCUMENT_TYPE)) {
+ ResourceType resourceType = document.getResourceType();
+ if (resourceType == null) {
+ value = null;
+ } else {
+ value = resourceType.getName();
+ }
+ }
+ }
+ }
+ } catch (final Exception e) {
+ throw new ConfigurationException("Getting attribute for name [" + name + "] failed: ",
+ e);
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Returning [" + name + "] = [" + value + "]");
+ }
+
+ return value;
+ }
+
+ protected String getPath(PageEnvelope envelope, Map objectModel) throws SiteException {
+ String path;
+ Document doc = envelope.getDocument();
+ if (doc == null) {
+ Publication pub = envelope.getPublication();
+ URLInformation info = new URLInformation();
+ String url = info.getWebappUrl();
+ DocumentLocator loc;
+ try {
+ loc = pub.getDocumentBuilder().getLocator(pub.getSession(), url);
+ } catch (MalformedURLException e) {
+ throw new SiteException(e);
+ }
+ path = loc.getPath();
+ } else {
+ path = doc.getLocator().getPath();
+ }
+ return path;
+ }
+
+ /**
+ * <code>DATE_FORMAT</code> The date format
+ */
+ public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration,
+ * java.util.Map)
+ */
+ public Iterator getAttributeNames(Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ return Arrays.asList(PageEnvelope.PARAMETER_NAMES).iterator();
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration,
+ * java.util.Map)
+ */
+ public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object[] objects = { getAttribute(name, modeConf, objectModel) };
+
+ return objects;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java
new file mode 100644
index 0000000..c40ab46
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceResolver;
+
+/**
+ * Checks if a certain resource exists and returns either the string "true" or "false".
+ * @version $Id$
+ */
+public class ResourceExistsModule extends AbstractInputModule implements Serviceable, Disposable {
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration, java.util.Map)
+ */
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+
+ String resourceURI = name;
+
+ Source source = null;
+ boolean exists = false;
+ try {
+ source = this.resolver.resolveURI(resourceURI);
+ exists = source.exists();
+ } catch (SourceNotFoundException e) {
+ exists = false;
+ } catch (Exception e) {
+ getLogger().warn("Exception resolving resource [" + resourceURI + "]", e);
+ exists = false;
+ } finally {
+ if (source != null) {
+ this.resolver.release(source);
+ }
+ }
+
+ return Boolean.toString(exists);
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration,
+ * java.util.Map)
+ */
+ public Iterator getAttributeNames(Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ return Collections.EMPTY_SET.iterator();
+ }
+
+ private ServiceManager manager;
+ private SourceResolver resolver;
+
+ /**
+ * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+ */
+ public void service(ServiceManager _manager) throws ServiceException {
+ this.manager = _manager;
+ this.resolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE);
+ }
+
+ /**
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+ super.dispose();
+ this.manager.release(this.resolver);
+ this.resolver = null;
+ this.manager = null;
+ }
+
+ /**
+ * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String,
+ * org.apache.avalon.framework.configuration.Configuration, java.util.Map)
+ */
+ public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object result = this.getAttribute(name, modeConf, objectModel);
+ return (result == null ? null : new Object[] { result });
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java
new file mode 100644
index 0000000..c80abdf
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.components.modules.input;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.components.modules.input.AbstractInputModule;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.commons.lang.StringUtils;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.ResourceType;
+import org.apache.lenya.cms.publication.ResourceTypeResolver;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * <p>
+ * Resource type module.
+ * </p>
+ * <p>
+ * The syntax is either <code>{resource-type:<attribute>}</code> (which uses the resource type
+ * of the currenlty requested document) or
+ * <code>{resource-type:<name>:<attribute>}</code> (which allows to access an arbitrary
+ * resource type).
+ * </p>
+ * <p>
+ * Attributes:
+ * </p>
+ * <ul>
+ * <li><strong><code>expires</code></strong> - the expiration date in RFC 822/1123 format, see
+ * {@link org.apache.lenya.cms.publication.ResourceType#getExpires()}</li>
+ * <li><strong><code>schemaUri</code></strong> - see {@link org.apache.lenya.xml.Schema#getURI()}</li>
+ * <li><strong><code>httpSchemaUri</code></strong> - the URI to request the schema over HTTP,
+ * without Proxy and context (use {proxy:} around it).</li>
+ * <li><strong><code>supportsFormat:{format}</code></strong> - true if the resource type supports
+ * this format, false otherwise</li>
+ * </ul>
+ */
+public class ResourceTypeModule extends AbstractInputModule {
+
+ protected static final String SCHEMA_URI = "schemaUri";
+ protected static final String HTTP_SCHEMA_URI = "httpSchemaUri";
+ protected static final String EXPIRES = "expires";
+ protected static final String SUPPORTS_FORMAT = "supportsFormat";
+
+ private Repository repository;
+ private ResourceTypeResolver resourceTypeResolver;
+
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object value = null;
+
+ try {
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ Session session = this.repository.getSession(request);
+
+ ResourceType resourceType;
+ Publication pub = null;
+ String attribute;
+
+ String[] steps = name.split(":");
+ if (steps.length == 1) {
+ String webappUrl = ServletHelper.getWebappURI(request);
+ Document document = session.getUriHandler().getDocument(webappUrl);
+ pub = document.getPublication();
+
+ attribute = name;
+ resourceType = document.getResourceType();
+ } else {
+ attribute = steps[1];
+ String resourceTypeName = steps[0];
+ resourceType = getResourceTypeResolver().getResourceType(resourceTypeName);
+ }
+
+ if (attribute.startsWith("format-")) {
+ String[] formatSteps = name.split("-");
+ String format = formatSteps[1];
+ value = resourceType.getFormatURI(format);
+ } else if (attribute.equals(SCHEMA_URI)) {
+ value = resourceType.getSchema().getURI();
+ } else if (attribute.equals(HTTP_SCHEMA_URI)) {
+ String uri = resourceType.getSchema().getURI();
+ value = transformFallbackUriToHttp(pub.getId(), uri);
+ } else if (attribute.equals(EXPIRES)) {
+ Date expires = resourceType.getExpires();
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss zzz");
+ value = sdf.format(expires);
+ } else if (attribute.equals(SUPPORTS_FORMAT)) {
+ String format = steps[steps.length - 1];
+ String[] formats = resourceType.getFormats();
+ return Boolean.toString(Arrays.asList(formats).contains(format));
+ } else {
+ throw new ConfigurationException("Attribute [" + name + "] not supported!");
+ }
+
+ } catch (Exception e) {
+ throw new ConfigurationException("Resolving attribute [" + name + "] failed: ", e);
+ }
+
+ return value;
+ }
+
+ /**
+ * Transforms a fallback URI for resources into a HTTP URL.
+ *
+ * Currently only supports module urls:
+ *
+ * fallback://lenya/modules/foo/resources/schemas/bar.rng ->
+ * prefix/pubid/modules/foo/schemas/bar.rng
+ *
+ * FIXME: allow other kind of fallback URIs
+ *
+ * @param pubid publication id of the current document
+ * @param prefix prefix which will be prepended to the resulting URL
+ * @param uri fallback uri, must start with fallback://
+ * @return A string.
+ * @throws ConfigurationException
+ */
+ protected String transformFallbackUriToHttp(String pubid, String uri)
+ throws ConfigurationException {
+ if (uri.startsWith("fallback://lenya/modules/")) {
+ String path = StringUtils.substringAfter(uri, "fallback://lenya/modules/");
+ String module = StringUtils.substringBefore(path, "/");
+ path = StringUtils.substringAfter(path, module + "/resources");
+ return "/" + pubid + "/modules/" + module + path;
+ } else {
+ throw new ConfigurationException("Don't know how to create HTTP URL from : " + uri);
+ }
+ }
+
+ public void setResourceTypeResolver(ResourceTypeResolver resourceTypeResolver) {
+ this.resourceTypeResolver = resourceTypeResolver;
+ }
+
+ public ResourceTypeResolver getResourceTypeResolver() {
+ return resourceTypeResolver;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java
new file mode 100644
index 0000000..ffb55fc
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.flow;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon;
+import org.apache.lenya.ac.AccessControlException;
+import org.apache.lenya.cms.publication.PageEnvelope;
+import org.apache.lenya.cms.publication.PageEnvelopeException;
+import org.apache.lenya.cms.publication.util.DocumentHelper;
+import org.apache.lenya.workflow.WorkflowException;
+
+/**
+ * Flowscript utility class.
+ * @version $Id$
+ */
+public interface FlowHelper {
+
+ /**
+ * The Avalon Role.
+ */
+ String ROLE = FlowHelper.class.getName();
+
+ /**
+ * Returns the current page envelope.
+ * @param cocoon The FOM_Cocoon object.
+ * @return A page envelope.
+ * @throws PageEnvelopeException when something went wrong.
+ */
+ PageEnvelope getPageEnvelope(FOM_Cocoon cocoon) throws PageEnvelopeException;
+
+ /**
+ * Returns the request URI of the current request.
+ * @param cocoon The FOM_Cocoon object.
+ * @return A string.
+ */
+ String getRequestURI(FOM_Cocoon cocoon);
+
+ /**
+ * Returns the request object of the current request.
+ * @param cocoon The FOM_Cocoon object.
+ * @return A request object.
+ */
+ HttpServletRequest getRequest(FOM_Cocoon cocoon);
+
+ /**
+ * Returns the Cocoon Object Model
+ * @param cocoon The Flow Object Model of Cocoon
+ * @return The object model
+ */
+ Map getObjectModel(FOM_Cocoon cocoon);
+
+ /**
+ * Returns a DocumentHelper instance.
+ * @param cocoon The Flow Object Model of Cocoon
+ * @return The document helper
+ * @see DocumentHelper
+ */
+ DocumentHelper getDocumentHelper(FOM_Cocoon cocoon);
+
+ /**
+ * Resolves the request parameter value for a specific name. The parameter names are encoded as
+ * <code>{name}:{value}.{axis}</code>. This is a workaround for the <input type="image"/>
+ * bug in Internet Explorer.
+ * @param cocoon The FOM_Cocoon object.
+ * @param parameterName The request parameter name.
+ * @return A string.
+ */
+ String getImageParameterValue(FOM_Cocoon cocoon, String parameterName);
+
+ /**
+ * Trigger a workflow event for the document associated with the current PageEnvelope.
+ * @param cocoon The Cocoon Flow Object Model
+ * @param event The name of the workflow event to trigger.
+ * @throws WorkflowException If an workflow error occurs
+ * @throws PageEnvelopeException Page envelope can not operate properly.
+ * @throws AccessControlException If an access control violation occurs.
+ */
+ void triggerWorkflow(FOM_Cocoon cocoon, String event) throws WorkflowException,
+ PageEnvelopeException, AccessControlException;
+
+ /**
+ * Checkis in the current document from the PageEnvelope context.
+ * @param cocoon The Cocoon Flow Object Model
+ * @param backup Wether a new revision should be created.
+ * @throws Exception
+ */
+ void reservedCheckIn(FOM_Cocoon cocoon, boolean backup) throws Exception;
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java
new file mode 100644
index 0000000..3cea2a9
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.lenya.cms.cocoon.flow;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.lenya.ac.AccessControlException;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.PageEnvelope;
+import org.apache.lenya.cms.publication.PageEnvelopeException;
+import org.apache.lenya.cms.publication.PageEnvelopeFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.publication.util.DocumentHelper;
+import org.apache.lenya.cms.workflow.WorkflowUtil;
+import org.apache.lenya.util.ServletHelper;
+import org.apache.lenya.workflow.WorkflowException;
+
+/**
+ * Flowscript utility class. The FOM_Cocoon object is not passed in the constructor to avoid errors.
+ * This way, not the initial, but the current FOM_Cocoon object is used by the methods.
+ */
+public class FlowHelperImpl extends AbstractLogEnabled implements FlowHelper {
+
+ private Repository repository;
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getPageEnvelope(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon)
+ */
+ public PageEnvelope getPageEnvelope(FOM_Cocoon cocoon) throws PageEnvelopeException {
+ HttpServletRequest request = getRequest(cocoon);
+ try {
+ Session session = this.repository.getSession(request);
+ PageEnvelopeFactory factory = PageEnvelopeFactory.getInstance();
+ URLInformation info = new URLInformation(ServletHelper.getWebappURI(request));
+ Publication publication = session.getPublication(info.getPublicationId());
+ return factory.getPageEnvelope(cocoon.getObjectModel(), publication);
+ } catch (Exception e) {
+ throw new PageEnvelopeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getRequestURI(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon)
+ */
+ public String getRequestURI(FOM_Cocoon cocoon) {
+ return cocoon.getRequest().getRequestURI();
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getRequest(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon)
+ */
+ public HttpServletRequest getRequest(FOM_Cocoon cocoon) {
+ return cocoon.getRequest();
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getObjectModel(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon)
+ */
+ public Map getObjectModel(FOM_Cocoon cocoon) {
+ return cocoon.getObjectModel();
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getDocumentHelper(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon)
+ */
+ public DocumentHelper getDocumentHelper(FOM_Cocoon cocoon) {
+ return new DocumentHelper(cocoon.getObjectModel());
+ }
+
+ /**
+ * <code>SEPARATOR</code> The separator
+ */
+ public static final String SEPARATOR = ":";
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getImageParameterValue(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon,
+ * java.lang.String)
+ */
+ public String getImageParameterValue(FOM_Cocoon cocoon, String parameterName) {
+
+ getLogger().debug("Resolving parameter value for name [" + parameterName + "]");
+
+ Request request = cocoon.getRequest();
+ String value = request.getParameter(parameterName);
+
+ if (value == null) {
+ String prefix = parameterName + SEPARATOR;
+ Enumeration e = request.getParameterNames();
+ while (e.hasMoreElements() && value == null) {
+ String name = (String) e.nextElement();
+ if (name.startsWith(prefix)) {
+ getLogger().debug("Complete parameter name: [" + name + "]");
+ value = name.substring(prefix.length(), name.length() - 2);
+ getLogger().debug("Resolved value: [" + value + "]");
+ }
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#triggerWorkflow(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon,
+ * java.lang.String)
+ */
+ public void triggerWorkflow(FOM_Cocoon cocoon, String event) throws WorkflowException,
+ PageEnvelopeException, AccessControlException {
+ Document document = getPageEnvelope(cocoon).getDocument();
+ WorkflowUtil.invoke(document, event);
+ }
+
+ /**
+ * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#reservedCheckIn(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon,
+ * boolean)
+ */
+ public void reservedCheckIn(FOM_Cocoon cocoon, boolean backup) throws Exception {
+ final PageEnvelope pageEnvelope = getPageEnvelope(cocoon);
+ pageEnvelope.getDocument().checkin();
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java
new file mode 100644
index 0000000..fcafe85
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lenya.cms.cocoon.selection;
+
+import org.apache.cocoon.selection.Selector;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+import java.util.Map;
+
+/**
+ * <p>
+ * Last Modified Source Selector.
+ * </p>
+ *
+ * <pre>
+ * <map:selector name="last-mod" src="org.apache.lenya.cms.cocoon.selection.LastModSourceSelector"/>
+ *
+ * <map:select type="last-mod">
+ * <map:parameter name="compare-to" value="{sourceToCompareTo}"/>
+ *
+ * <map:when test="cachedsource">
+ * <!-- executes iff cachedsource last-modified > courceToCompareTo last-modified -->
+ * <map:read src="{cachedsource}" mime-type="text/xml; charset=utf-8"/>
+ * </map:when>
+ * <map:otherwise>
+ * <map:read src="{sourceToCompareTo}" mime-type="text/xml; charset=utf-8"/>
+ * </map:otherwise>
+ * </map:select>
+ * </pre>
+ */
+
+public class LastModSourceSelector extends AbstractLogEnabled
+ implements ThreadSafe, Serviceable, Disposable, Selector {
+
+ private ServiceManager manager;
+ private SourceResolver resolver;
+ private Source source = null;
+ private Source compare = null;
+
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ this.resolver = (SourceResolver)manager.lookup(SourceResolver.ROLE);
+ }
+
+ public void dispose() {
+ if (null != this.source) {
+ resolver.release(this.source);
+ this.source = null;
+ }
+ if (null != this.compare) {
+ resolver.release(this.compare);
+ this.compare = null;
+ }
+ this.manager.release(this.resolver);
+ this.resolver = null;
+ this.manager = null;
+ }
+
+ public boolean select(String expression, Map objectModel, Parameters parameters) {
+ String sourceToCompare = parameters.getParameter("compare-to",null);
+ String compareToSource = expression;
+ long sourceModDate = 0;
+ long compareModDate = 0;
+ try {
+ source = resolver.resolveURI(sourceToCompare);
+ sourceModDate = source.getLastModified();
+ compare = resolver.resolveURI(compareToSource);
+ compareModDate = compare.getLastModified();
+ } catch (SourceNotFoundException e) {
+ return false;
+ } catch (Exception e) {
+ getLogger().warn("Exception resolving resource ", e);
+ return false;
+ }
+ boolean isNewer = (compareModDate > sourceModDate);
+
+ return (sourceToCompare != null && isNewer);
+
+ }
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java
new file mode 100644
index 0000000..9a41a81
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lenya.cms.cocoon.serialization;
+
+import org.apache.cocoon.serialization.Serializer;
+import org.apache.cocoon.Constants;
+import org.apache.cocoon.xml.xlink.ExtendedXLinkPipe;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * Link serializer.
+ * TODO: add meaningful javadocs
+ */
+public class LinkSerializer extends ExtendedXLinkPipe implements Serializer {
+
+ private PrintStream out;
+
+ /**
+ * Set the {@link OutputStream} where the requested resource should be serialized.
+ */
+ public void setOutputStream(OutputStream out) throws IOException {
+ this.out = new PrintStream(out);
+ }
+
+ /**
+ * Get the mime-type of the output of this <code>Component</code>.
+ */
+ public String getMimeType() {
+ return Constants.LINK_CONTENT_TYPE;
+ }
+
+ public void simpleLink(String href, String role, String arcrole, String title, String show,
+ String actuate, String uri, String name, String raw, Attributes attr)
+ throws SAXException {
+ print(href);
+ super.simpleLink(href, role, arcrole, title, show, actuate, uri, name, raw, attr);
+ }
+
+ public void startLocator(String href, String role, String title, String label, String uri,
+ String name, String raw, Attributes attr) throws SAXException {
+ if (traversable(href)) {
+ print(href);
+ }
+ super.startLocator(href, role, title, label, uri, name, raw, attr);
+ }
+
+ private boolean traversable(String href) {
+ if (href.length() == 0)
+ return false;
+ if (href.charAt(0) == '#')
+ return false;
+ if (href.indexOf("://") != -1)
+ return false;
+ if (href.startsWith("mailto:"))
+ return false;
+ if (href.startsWith("news:"))
+ return false;
+ if (href.startsWith("javascript:"))
+ return false;
+ return true;
+ }
+
+ private void print(String href) {
+ int ankerPos = href.indexOf('#');
+ if (ankerPos == -1) {
+ // TODO: Xalan encodes international characters into URL encoding
+ out.println(href);
+ } else {
+ out.println(href.substring(0, ankerPos));
+ }
+ }
+
+ /**
+ * Test if the component wants to set the content length
+ */
+ public boolean shouldSetContentLength() {
+ return false;
+ }
+
+ /**
+ * Recyclable
+ */
+ public void recycle() {
+ super.recycle();
+ this.out = null;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java
new file mode 100644
index 0000000..f08be25
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.store.impl.MRUMemoryStore;
+import org.apache.lenya.cms.module.Module;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.publication.templating.AllExistingSourceResolver;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * <p>
+ * Aggregate all existing fallback sources by adding their XML content under the document element of
+ * the first encountered source. The document element of all subsequent sources is stripped.
+ * </p>
+ * <p>
+ * The fallback sources are resolved in bottom-up order, i.e.
+ * </p>
+ * <ul>
+ * <li>current publication</li>
+ * <li>template of the current publication</li>
+ * <li>template of the template publication</li>
+ * <li>...</li>
+ * <li>core</li>
+ * </ul>
+ * <p>
+ * If one of the fallback sources is not a well-formed XML document, a RuntimeException is thrown.
+ * </p>
+ */
+public class AggregatingFallbackSourceFactory extends FallbackSourceFactory {
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(final String location, Map parameters) throws IOException,
+ MalformedURLException {
+
+ String[] uris;
+
+ if (useCache()) {
+ MRUMemoryStore store = getStore();
+ final String cacheKey = getCacheKey(getPublicationId(), location);
+ final String[] cachedUris = (String[]) store.get(cacheKey);
+ if (cachedUris == null) {
+ uris = findUris(location, parameters);
+ store.hold(cacheKey, uris);
+ if (getLogger().isDebugEnabled()) {
+ getLogger()
+ .debug(
+ "No cached source URI for key " + cacheKey
+ + ", caching resolved URIs.");
+ }
+ } else {
+ uris = cachedUris;
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Using cached source URIs for key " + cacheKey);
+ }
+ }
+ } else {
+ uris = findUris(location, parameters);
+ }
+ return new AggregatingSource(location, uris, getSourceResolver());
+ }
+
+ protected String[] findUris(final String location, Map parameters) throws IOException,
+ MalformedURLException {
+
+ FallbackUri uri = new FallbackUri(location);
+ String pubId = uri.getPubId();
+ String path = uri.getPath();
+
+ try {
+
+ final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = processInfo.getRequest();
+
+ if (pubId == null) {
+ String webappUrl = ServletHelper.getWebappURI(request);
+ URLInformation info = new URLInformation(webappUrl);
+ pubId = info.getPublicationId();
+ }
+
+ Session session = getRepository().getSession(request);
+
+ String[] uris;
+
+ if (session.existsPublication(pubId)) {
+ Publication pub = session.getPublication(pubId);
+ AllExistingSourceResolver resolver = new AllExistingSourceResolver();
+ getTemplateManager().visit(pub, path, resolver);
+ uris = resolver.getUris();
+ } else {
+ uris = new String[0];
+ }
+
+ List allUris = new ArrayList();
+ allUris.addAll(Arrays.asList(uris));
+
+ String contextSourceUri = null;
+ if (path.startsWith("lenya/modules/")) {
+ final String moduleShortcut = path.split("/")[2];
+ Module module = (Module) getModules().get(moduleShortcut);
+ final String modulePath = path.substring(("lenya/modules/" + moduleShortcut)
+ .length());
+ contextSourceUri = module.getBaseUri() + modulePath;
+ } else {
+ contextSourceUri = "context://" + path;
+ }
+ if (org.apache.lenya.cms.cocoon.source.SourceUtil.exists(contextSourceUri,
+ getSourceResolver())) {
+ allUris.add(contextSourceUri);
+ }
+
+ return (String[]) allUris.toArray(new String[allUris.size()]);
+
+ } catch (Exception e) {
+ throw new RuntimeException("Resolving path [" + location + "] failed: ", e);
+ }
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java
new file mode 100644
index 0000000..5d81eb7
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java
@@ -0,0 +1,179 @@
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.cocoon.components.source.impl.MultiSourceValidity;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.lenya.xml.DocumentHelper;
+import org.apache.lenya.xml.NamespaceHelper;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * @see AggregatingFallbackSourceFactory
+ */
+public class AggregatingSource implements Source {
+
+ private String uri;
+ private String[] sourceUris;
+ private SourceResolver resolver;
+
+ /**
+ * @param uri
+ * @param uris
+ */
+ public AggregatingSource(String uri, String[] uris, SourceResolver resolver) {
+ this.sourceUris = (String[]) uris.clone();
+ this.uri = uri;
+ this.resolver = resolver;
+ }
+
+ public String toString() {
+ return getURI();
+ }
+
+ protected void loadDom() {
+ try {
+ for (int i = 0; i < sourceUris.length; i++) {
+
+ Document sourceDom = SourceUtil.readDOM(sourceUris[i], this.resolver);
+
+ if (sourceDom == null) {
+ throw new RuntimeException("The source [" + sourceUris[i]
+ + "] doesn't contain XML.");
+ }
+
+ Element docElement = sourceDom.getDocumentElement();
+ if (this.dom == null) {
+ String namespaceUri = docElement.getNamespaceURI();
+ String prefix = docElement.getPrefix();
+ String localName = docElement.getLocalName();
+
+ if (namespaceUri == null) {
+ this.dom = DocumentHelper.createDocument(null, localName, null);
+ } else {
+ NamespaceHelper helper = new NamespaceHelper(namespaceUri, prefix,
+ localName);
+ this.dom = helper.getDocument();
+ }
+ }
+
+ Element[] elements = DocumentHelper.getChildren(docElement);
+ for (int e = 0; e < elements.length; e++) {
+ Element clone = (Element) this.dom.importNode(elements[e], true);
+ this.dom.getDocumentElement().appendChild(clone);
+ }
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Document dom;
+ private byte[] data;
+
+ protected Document getDom() {
+ if (this.dom == null) {
+ loadDom();
+ }
+ return this.dom;
+ }
+
+ protected byte[] getData() {
+ if (this.data == null) {
+ Document dom = getDom();
+ if (dom != null) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ DocumentHelper.writeDocument(dom, out);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ this.data = out.toByteArray();
+ }
+ }
+ return this.data;
+ }
+
+ public boolean exists() {
+ return this.sourceUris.length > 0;
+ }
+
+ public long getContentLength() {
+ return getData().length;
+ }
+
+ public InputStream getInputStream() throws IOException, SourceNotFoundException {
+ if (!exists()) {
+ throw new SourceNotFoundException(this + " does not exist!");
+ }
+ return new ByteArrayInputStream(getData());
+ }
+
+ public long getLastModified() {
+ long lastModified = 0;
+ for (int i = 0; i < this.sourceUris.length; i++) {
+ try {
+ lastModified = Math.max(lastModified, SourceUtil.getLastModified(sourceUris[i],
+ this.resolver));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return lastModified;
+ }
+
+ public String getMimeType() {
+ return "application/xml";
+ }
+
+ public String getScheme() {
+ return "aggregate-template";
+ }
+
+ public String getURI() {
+ return this.uri;
+ }
+
+ private SourceValidity validity;
+
+ public SourceValidity getValidity() {
+ if (this.validity == null) {
+ try {
+ MultiSourceValidity aggregatedValidity = new MultiSourceValidity(resolver,
+ MultiSourceValidity.CHECK_ALWAYS);
+ for (int i = 0; i < this.sourceUris.length; i++) {
+ Source source = null;
+ try {
+ source = resolver.resolveURI(this.sourceUris[i]);
+ aggregatedValidity.addSource(source);
+ } finally {
+ if (source != null) {
+ resolver.release(source);
+ }
+ }
+ }
+ aggregatedValidity.close();
+ this.validity = aggregatedValidity;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return this.validity;
+ }
+
+ public void refresh() {
+ this.dom = null;
+ this.data = null;
+ this.validity = null;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java
new file mode 100644
index 0000000..095aabf
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import org.apache.excalibur.source.ModifiableSource;
+import org.apache.lenya.cms.publication.Document;
+
+public interface DocumentSource extends ModifiableSource {
+
+ Document getDocument();
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java
new file mode 100644
index 0000000..d44da8b
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.commons.lang.Validate;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceUtil;
+import org.apache.excalibur.source.URIAbsolutizer;
+import org.apache.excalibur.store.impl.MRUMemoryStore;
+import org.apache.lenya.cms.module.Module;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.publication.templating.ExistingSourceResolver;
+import org.apache.lenya.cms.publication.templating.PublicationTemplateManager;
+import org.apache.lenya.cms.publication.templating.VisitingSourceResolver;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * <p>
+ * Source factory following the fallback principle.
+ * </p>
+ * <p>
+ * The ID of the current publication can be passed in the URL (
+ * <code>fallback:pub://path</code),
+ * this is necessary as a workaround for bug 40564.
+ * </p>
+ *
+ * @version $Id$
+ */
+public class FallbackSourceFactory extends AbstractLogEnabled implements SourceFactory,
+ URIAbsolutizer {
+
+ protected MRUMemoryStore store;
+ private SourceResolver resolver;
+ private Repository repository;
+ private PublicationTemplateManager templateManager;
+ private Map modules;
+
+ /**
+ * Configure the spring bean accordingly if you want to use a store.
+ * @param store The store.
+ */
+ public void setStore(MRUMemoryStore store) {
+ Validate.notNull(store);
+ this.store = store;
+ }
+
+ protected boolean useCache() {
+ return this.store != null;
+ }
+
+ protected MRUMemoryStore getStore() {
+ return this.store;
+ }
+
+ public void setSourceResolver(SourceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ protected SourceResolver getSourceResolver() {
+ return this.resolver;
+ }
+
+ public void setTemplateManager(PublicationTemplateManager mgr) {
+ this.templateManager = mgr;
+ }
+
+ protected PublicationTemplateManager getTemplateManager() {
+ return this.templateManager;
+ }
+
+ public void setModules(Map modules) {
+ this.modules = modules;
+ }
+
+ protected Map getModules() {
+ return this.modules;
+ }
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(final String location, Map parameters) throws IOException,
+ MalformedURLException {
+
+ Source source;
+
+ if (useCache()) {
+ MRUMemoryStore store = getStore();
+ final String pubId = getPublicationId();
+ final String cacheKey = getCacheKey(pubId, location);
+ final String cachedSourceUri = (String) store.get(cacheKey);
+
+ if (cachedSourceUri == null) {
+ source = findSource(location, parameters);
+ final String resolvedSourceUri = source.getURI();
+ store.hold(cacheKey, resolvedSourceUri);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug(
+ "No cached source URI for key " + cacheKey + ", caching URI "
+ + resolvedSourceUri);
+ }
+ } else {
+ source = this.resolver.resolveURI(cachedSourceUri);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug(
+ "Using cached source URI " + cachedSourceUri + " for key " + cacheKey);
+ }
+ }
+
+ } else {
+ source = findSource(location, parameters);
+ }
+
+ return source;
+ }
+
+ /**
+ * @param pubId The publication ID.
+ * @param fallbackUri The fallback:// (or template-fallback:// etc.) URI.
+ * @return A string.
+ */
+ public static String getCacheKey(final String pubId, final String fallbackUri) {
+ String cacheKey = pubId == null ? fallbackUri : pubId + ":" + fallbackUri;
+ return cacheKey;
+ }
+
+ protected String getPublicationId() {
+ final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = processInfo.getRequest();
+ String webappUri = ServletHelper.getWebappURI(request);
+ URLInformation info = new URLInformation(webappUri);
+ String pubId = null;
+ try {
+ Session session = this.repository.getSession(request);
+ String pubIdCandidate = info.getPublicationId();
+ if (pubIdCandidate != null && session.existsPublication(pubIdCandidate)) {
+ pubId = pubIdCandidate;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return pubId;
+ }
+
+ protected Source findSource(final String location, Map parameters) throws IOException,
+ MalformedURLException {
+
+ FallbackUri uri = new FallbackUri(location);
+
+ String pubId = uri.getPubId();
+ String path = uri.getPath();
+
+ Source source = null;
+ try {
+ final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = processInfo.getRequest();
+
+ if (pubId == null) {
+ String webappUrl = request.getRequestURI().substring(
+ request.getContextPath().length());
+
+ URLInformation info = new URLInformation(webappUrl);
+ pubId = info.getPublicationId();
+ }
+
+ Session session = this.repository.getSession(request);
+ if (session.existsPublication(pubId)) {
+ Publication pub = session.getPublication(pubId);
+ VisitingSourceResolver resolver = getSourceVisitor();
+ this.templateManager.visit(pub, path, resolver);
+ source = resolver.getSource();
+ }
+
+ if (source == null) {
+ if (path.startsWith("lenya/modules/")) {
+ final String moduleShortcut = path.split("/")[2];
+ if (!this.modules.containsKey(moduleShortcut)) {
+ throw new RuntimeException("The module '" + moduleShortcut + "' is not registered.");
+ }
+ Module module = (Module) this.modules.get(moduleShortcut);
+ String baseUri = module.getBaseUri();
+ final String modulePath = path.substring(("lenya/modules/" + moduleShortcut)
+ .length());
+ source = this.resolver.resolveURI(baseUri + modulePath);
+ } else {
+ String contextUri = "context://" + path;
+ source = this.resolver.resolveURI(contextUri);
+ }
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Resolved source: [" + source.getURI() + "]");
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException("Resolving path [" + location + "] failed: ", e);
+ }
+
+ return source;
+ }
+
+ protected VisitingSourceResolver getSourceVisitor() {
+ return new ExistingSourceResolver();
+ }
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
+ */
+ public void release(Source source) {
+ // In fact, this method should never be called as this factory
+ // returns a source object from a different factory. So that
+ // factory should release the source
+ if (null != source) {
+ if (this.getLogger().isDebugEnabled()) {
+ this.getLogger().debug("Releasing source " + source.getURI());
+ }
+ this.resolver.release(source);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.URIAbsolutizer#absolutize(java.lang.String,
+ * java.lang.String)
+ */
+ public String absolutize(String baseURI, String location) {
+ return SourceUtil.absolutize(baseURI, location, true);
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java
new file mode 100644
index 0000000..e1725e4
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.net.MalformedURLException;
+import java.util.StringTokenizer;
+
+public class FallbackUri {
+
+ private String uri;
+ private String pubId;
+ private String path;
+ private String queryString;
+
+ public FallbackUri(String uri) throws MalformedURLException {
+
+ this.uri = uri;
+
+ // Remove the protocol and the first '//'
+ int pos = uri.indexOf("://");
+
+ if (pos == -1) {
+ throw new MalformedURLException("The URI [" + uri
+ + "] does not contain the string '://'");
+ }
+
+ String path = uri.substring(pos + 3);
+
+ // extract publication ID
+ String prefix = uri.substring(0, pos);
+ StringTokenizer tokens = new StringTokenizer(prefix, ":");
+ if (tokens.countTokens() > 1) {
+ tokens.nextToken();
+ this.pubId = tokens.nextToken();
+ }
+
+ // remove query string
+ int questionMarkIndex = path.indexOf("?");
+ if (questionMarkIndex > -1) {
+ this.queryString = path.substring(questionMarkIndex);
+ path = path.substring(0, questionMarkIndex);
+ }
+
+ if (path.length() == 0) {
+ throw new MalformedURLException("The path after the protocol must not be empty!");
+ }
+
+ this.path = path;
+ }
+
+ public String getPubId() {
+ return this.pubId;
+ }
+
+ public String getPath() {
+ return this.path;
+ }
+
+ public String getQeryString() {
+ return this.queryString;
+ }
+
+ public String getUri() {
+ return this.uri;
+ }
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java
new file mode 100644
index 0000000..9c6936c
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.ResourceNotFoundException;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.repository.NodeFactory;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * A factory for the "lenyadoc" scheme (virtual protocol), which is used to resolve any
+ * src="lenyadoc:<...>" attributes in sitemaps.
+ *
+ * <code>lenyadoc://<publication>/<area>/<language>/<uuid></code>
+ * <code>lenyadoc:/<language>/<uuid></code>
+ *
+ * If we want to request the meta data for a document
+ * instead of the document itself, we need to use
+ *
+ * <code>lenyadoc:meta:/<language>/<uuid></code>
+ * <code>lenyadoc:meta://<publication>/<area>/<language>/<uuid></code>
+ *
+ * @version $Id:$
+ * @deprecated Use <code>lenya-document</code> instead (see {@link org.apache.lenya.cms.cocoon.source.DocumentSourceFactory}.
+ */
+public class LenyaDocSourceFactory extends AbstractLogEnabled implements SourceFactory {
+
+ protected static final String SCHEME = "lenyadoc";
+
+ private Repository repository;
+ private NodeFactory nodeFactory;
+
+ /**
+ * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+ */
+ public void configure(Configuration configuration) throws ConfigurationException {
+ }
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(String location, Map parameters) throws MalformedURLException,
+ IOException, SourceException {
+ String scheme = null;
+ String area = null;
+ String language = null;
+ String uuid = null;
+ Publication pub;
+
+ // Parse the url
+ int start = 0;
+ int end;
+
+ // Scheme
+ end = location.indexOf(':', start);
+ if (end == -1) {
+ throw new MalformedURLException("Malformed lenyadoc: URI: can not find scheme part ["
+ + location + "]");
+ }
+ scheme = location.substring(start, end);
+ if (!SCHEME.equals(scheme)) {
+ throw new MalformedURLException("Malformed lenyadoc: URI: unknown scheme [" + location
+ + "]");
+ }
+
+ ProcessInfoProvider processInfoProvider = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = processInfoProvider.getRequest();
+ Session session = this.repository.getSession(request);
+ start = end + 1;
+
+ // Absolute vs. relative
+ if (location.startsWith("//", start)) {
+ // Absolute: get publication id
+ start += 2;
+ end = location.indexOf('/', start);
+ if (end == -1) {
+ throw new MalformedURLException("Malformed lenyadoc: URI: publication part not found ["
+ + location + "]");
+ }
+ String publicationId = location.substring(start, end);
+ try {
+ pub = session.getPublication(publicationId);
+ } catch (ResourceNotFoundException e) {
+ throw new MalformedURLException("Malformed lenyadoc: Publication [" + publicationId
+ + "] does not exist or could not be initialized");
+ }
+ if (pub == null || !pub.exists()) {
+ throw new SourceException("The publication [" + publicationId + "] does not exist!");
+ }
+
+ // Area
+ start = end + 1;
+ end = location.indexOf('/', start);
+ if (end == -1) {
+ throw new MalformedURLException("Malformed lenyadoc: URI: cannot find area ["
+ + location + "]");
+ }
+ area = location.substring(start, end);
+
+ } else if (location.startsWith("/", start)) {
+ end += 1;
+ // Relative: get publication id and area from page envelope
+ try {
+ String id = new URLInformation(ServletHelper.getWebappURI(request)).getPublicationId();
+ pub = session.getPublication(id);
+ } catch (ResourceNotFoundException e) {
+ throw new SourceException("Error getting publication id / area from page envelope ["
+ + location + "]");
+ }
+ if (pub != null && pub.exists()) {
+ String url = ServletHelper.getWebappURI(request);
+ area = new URLInformation(url).getArea();
+ } else {
+ throw new SourceException("Error getting publication id / area from page envelope ["
+ + location + "]");
+ }
+ } else {
+ throw new MalformedURLException("Malformed lenyadoc: URI [" + location + "]");
+ }
+
+ // Language
+ start = end + 1;
+ end = location.indexOf('/', start);
+ if (end == -1) {
+ throw new MalformedURLException("Malformed lenyadoc: URI: cannot find language ["
+ + location + "]");
+ }
+ language = location.substring(start, end);
+
+ // UUID
+ start = end + 1;
+ uuid = location.substring(start);
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Creating repository source for URI [" + location + "]");
+ }
+ Document document;
+ try {
+ document = pub.getArea(area).getDocument(uuid, language);
+ } catch (ResourceNotFoundException e) {
+ throw new MalformedURLException("Malformed lenyadoc: Document [" + uuid + ":"
+ + language + "] could not be created.");
+ }
+
+ String lenyaURL = document.getSourceURI();
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Mapping 'lenyadoc:' URL [" + location + "] to 'lenya:' URL ["
+ + lenyaURL + "]");
+ getLogger().debug("Creating repository source for URI [" + lenyaURL + "]");
+ }
+
+ return new RepositorySource(getNodeFactory(), lenyaURL, session, getLogger());
+ }
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
+ */
+ public void release(Source source) {
+ // Source will be released by delegated source factory.
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public void setNodeFactory(NodeFactory nodeFactory) {
+ this.nodeFactory = nodeFactory;
+ }
+
+ public NodeFactory getNodeFactory() {
+ return nodeFactory;
+ }
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java
new file mode 100644
index 0000000..e665683
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.cocoon.components.flow.FlowHelper;
+import org.apache.cocoon.components.modules.input.JXPathHelper;
+import org.apache.cocoon.components.modules.input.JXPathHelperConfiguration;
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.repository.NodeFactory;
+import org.apache.lenya.cms.repository.RepositoryException;
+import org.apache.lenya.util.Query;
+
+/**
+ * A factory for the "lenya" scheme (virtual protocol), which is used to resolve any src="lenya:..."
+ * attributes in sitemaps. This implementation constructs the path to the source document from the
+ * page envelope and delegates any further resolving to the "context" source resolver of Cocoon.
+ *
+ * @version $Id$
+ */
+public class LenyaSourceFactory extends AbstractLogEnabled implements SourceFactory {
+
+ protected static final String SCHEME = "lenya:";
+
+ /** fallback if no configuration is available */
+ protected static final String DEFAULT_DELEGATION_SCHEME = "context:";
+ protected static final String DEFAULT_DELEGATION_PREFIX = "/"
+ + Publication.PUBLICATION_PREFIX_URI;
+
+ private NodeFactory nodeFactory;
+ private Repository repository;
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(final String location, final Map parameters)
+ throws MalformedURLException, IOException, SourceException {
+
+ String sessionName = null;
+
+ String[] uriAndQuery = location.split("\\?");
+ if (uriAndQuery.length > 1) {
+ Query query = new Query(uriAndQuery[1]);
+ sessionName = query.getValue("session");
+ }
+
+ Session session;
+ try {
+ session = getSession(sessionName);
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Creating repository source for URI [" + location + "]");
+ }
+
+ return new RepositorySource(this.nodeFactory, location, session, getLogger());
+
+ }
+
+ protected Session getSession(String sessionName) throws RepositoryException {
+ Session session;
+ ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ if (sessionName == null) {
+ HttpServletRequest request = process.getRequest();
+ session = this.repository.getSession(request);
+ } else if (sessionName.equals("usecase")) {
+ session = getUsecaseSession(process.getObjectModel());
+ } else {
+ throw new RepositoryException("Invalid session: [" + sessionName + "]");
+ }
+
+ return session;
+ }
+
+ protected Session getUsecaseSession(Map objectModel) throws RepositoryException {
+ try {
+ Configuration config = new DefaultConfiguration("foo");
+ JXPathHelperConfiguration helperConfig = JXPathHelper.setup(config);
+ Object contextObject = FlowHelper.getContextObject(objectModel);
+ return (Session) JXPathHelper.getAttribute("usecase/session", config, helperConfig,
+ contextObject);
+ } catch (Exception e) {
+ throw new RepositoryException(e);
+ }
+ }
+
+ /**
+ * Does nothing because the delegated factory does this.
+ * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
+ */
+ public void release(Source source) {
+ // do nothing
+ }
+
+ public void setNodeFactory(NodeFactory nodeFactory) {
+ this.nodeFactory = nodeFactory;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java
new file mode 100644
index 0000000..70c5273
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java
@@ -0,0 +1,70 @@
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.ResourceNotFoundException;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * Syntax: pub:{pubId}:/path/to/file
+ */
+public class PublicationSourceFactory implements SourceFactory {
+
+ protected static final String PROTOCOL = "pub";
+
+ private Repository repository;
+ private SourceResolver sourceResolver;
+
+ public Source getSource(final String location, final Map parameters) throws IOException,
+ MalformedURLException {
+
+ final String pathInfo = location.substring(PROTOCOL.length() + 1);
+
+ final int colonIndex = pathInfo.indexOf(":");
+ if (colonIndex < 0) {
+ throw new MalformedURLException("The URI " + location
+ + " must contain the publication ID.");
+ }
+
+ final String pubId = pathInfo.substring(0, colonIndex);
+ ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = process.getRequest();
+ Session session = this.repository.getSession(request);
+ if (session.existsPublication(pubId)) {
+ Publication pub = session.getPublication(pubId);
+ final String path = pathInfo.substring(colonIndex + 1);
+ final String uri = pub.getSourceUri() + path;
+ return this.sourceResolver.resolveURI(uri);
+ } else {
+ throw new ResourceNotFoundException("The publication " + pubId + " does not exist.");
+ }
+ }
+
+ /**
+ * Does nothing because the delegated factory does this.
+ * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
+ */
+ public void release(Source source) {
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public void setSourceResolver(SourceResolver sourceResolver) {
+ this.sourceResolver = sourceResolver;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java
new file mode 100644
index 0000000..311b513
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.MalformedURLException;
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.commons.logging.Log;
+import org.apache.excalibur.source.ModifiableTraversableSource;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.AbstractSource;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.repository.ContentHolder;
+import org.apache.lenya.cms.repository.Node;
+import org.apache.lenya.cms.repository.NodeFactory;
+import org.apache.lenya.cms.repository.RepositoryException;
+import org.apache.lenya.cms.repository.SessionHolder;
+import org.apache.lenya.util.Query;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * Repository source.
+ *
+ * @version $Id$
+ */
+public class RepositorySource extends AbstractSource implements ModifiableTraversableSource {
+
+ private ContentHolder content;
+ private Session session;
+ private Log logger;
+ protected static final String SCHEME = "lenya";
+
+ private NodeFactory nodeFactory;
+
+ /**
+ * @param nodeFactory The node factory.
+ * @param uri The source URI.
+ * @param session The repository session.
+ * @param logger The logger.
+ * @throws SourceException if an error occurs.
+ * @throws MalformedURLException if an error occurs.
+ */
+ public RepositorySource(NodeFactory nodeFactory, String uri, Session session, Log logger)
+ throws SourceException, MalformedURLException {
+ this.logger = logger;
+ this.nodeFactory = nodeFactory;
+
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Init RepositorySource: " + uri);
+
+ if (session == null) {
+ throw new IllegalArgumentException("The repository session must not be null!");
+ }
+ this.session = session;
+
+ if (uri == null) {
+ throw new MalformedURLException("The source URI must not be null!");
+ }
+
+ setSystemId(uri);
+
+ // Scheme
+ int start = 0;
+ int end = uri.indexOf(':');
+ if (end == -1)
+ throw new MalformedURLException(
+ "Malformed uri for xmodule source (cannot find scheme) : " + uri);
+
+ String scheme = uri.substring(start, end);
+ if (!SCHEME.equals(scheme))
+ throw new MalformedURLException("Malformed uri for a xmodule source : " + uri);
+
+ setScheme(scheme);
+
+ try {
+
+ String sourceUri;
+ int revisionNumber = -1;
+
+ int questionMarkIndex = uri.indexOf("?");
+ if (questionMarkIndex > -1) {
+ sourceUri = uri.substring(0, questionMarkIndex);
+ Query query = new Query(uri.substring(questionMarkIndex + 1));
+ String revisionString = query.getValue("rev", null);
+ if (revisionString != null) {
+ ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = process.getRequest();
+ String webappUrl = ServletHelper.getWebappURI(request);
+ String pubId = new URLInformation(webappUrl).getPublicationId();
+ Publication pub = this.session.getPublication(pubId);
+ Document currentDoc = pub.getSession().getUriHandler().getDocument(webappUrl);
+ if (currentDoc.getSourceURI().equals(sourceUri)) {
+ revisionNumber = Integer.valueOf(revisionString).intValue();
+ }
+ }
+ } else {
+ sourceUri = uri;
+ }
+
+ org.apache.lenya.cms.repository.Session repoSession = ((SessionHolder) session)
+ .getRepositorySession();
+ if (revisionNumber == -1) {
+ this.content = (ContentHolder) repoSession
+ .getRepositoryItem(nodeFactory, sourceUri);
+ } else {
+ Node node = (Node) repoSession.getRepositoryItem(nodeFactory, sourceUri);
+ this.content = node.getHistory().getRevision(revisionNumber);
+ }
+
+ } catch (Exception e) {
+ throw new SourceException("Creating repository node failed: ", e);
+ }
+ }
+
+ /**
+ * @return The repository node which is accessed by this source.
+ */
+ public Node getNode() {
+
+ if (!(this.content instanceof Node)) {
+ throw new RuntimeException(
+ "This operation can only be invoked on nodes, not on revisions.");
+ }
+
+ return (Node) this.content;
+ }
+
+ protected Log getLogger() {
+ return this.logger;
+ }
+
+ /**
+ * @see org.apache.excalibur.source.ModifiableSource#getOutputStream()
+ */
+ public OutputStream getOutputStream() throws IOException {
+ try {
+ return getNode().getOutputStream();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.ModifiableSource#delete()
+ */
+ public void delete() {
+ try {
+ getNode().delete();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.ModifiableSource#canCancel(java.io.OutputStream)
+ */
+ public boolean canCancel(OutputStream arg0) {
+ return false;
+ }
+
+ /**
+ * @see org.apache.excalibur.source.ModifiableSource#cancel(java.io.OutputStream)
+ */
+ public void cancel(OutputStream arg0) throws IOException {
+ }
+
+ /**
+ * @see org.apache.excalibur.source.Source#exists()
+ */
+ public boolean exists() {
+ try {
+ if (getContent().exists()) {
+ return true;
+ } else {
+ return isCollection();
+ }
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.Source#getInputStream()
+ */
+ public InputStream getInputStream() throws IOException, SourceNotFoundException {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("Get InputStream for " + getURI());
+ if (!exists()) {
+ throw new SourceNotFoundException("The source [" + getURI() + "] does not exist!");
+ }
+ try {
+ return getContent().getInputStream();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected InputStream convert(org.w3c.dom.Document edoc) throws IOException {
+
+ final org.w3c.dom.Document doc = edoc;
+ final PipedOutputStream pos = new PipedOutputStream();
+ PipedInputStream pis = new PipedInputStream();
+ pis.connect(pos);
+
+ (new Thread(new Runnable() {
+
+ public void run() {
+ try {
+ transform(doc, pos);
+ } catch (TransformerException e) {
+ throw new RuntimeException(
+ "Failed to tranform org.w3c.dom.Document to PipedOutputStream", e);
+ } finally {
+ try {
+ pos.close();
+ } catch (Exception ignore) {
+ ignore.printStackTrace();
+ }
+ }
+ }
+ }, getClass().getName() + ".convert(org.w3c.dom.Document edoc)")).start();
+
+ return pis;
+ }
+
+ void transform(org.w3c.dom.Document edoc, PipedOutputStream pos) throws TransformerException {
+
+ TransformerFactory tFactory = TransformerFactory.newInstance();
+ Transformer transformer = tFactory.newTransformer();
+
+ transformer.setOutputProperty("encoding", "UTF-8");
+ transformer.setOutputProperty("indent", "yes");
+
+ transformer.transform(new DOMSource(edoc), new StreamResult(pos));
+
+ }
+
+ /**
+ * @return The content of this source.
+ */
+ public ContentHolder getContent() {
+ return this.content;
+ }
+
+ /**
+ * @see org.apache.excalibur.source.Source#getContentLength()
+ */
+ public long getContentLength() {
+ try {
+ return getContent().getContentLength();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.Source#getLastModified()
+ */
+ public long getLastModified() {
+ try {
+ return getContent().getLastModified();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @return The source URI.
+ */
+ public String getSourceURI() {
+ return getContent().getSourceURI();
+ }
+
+ /**
+ * @see org.apache.excalibur.source.Source#getMimeType()
+ */
+ public String getMimeType() {
+ try {
+ return getContent().getMimeType();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ *
+ */
+ public Source getParent() {
+ getLogger().warn("getParent() not implemented yet!");
+ return null;
+ }
+
+ /**
+ *
+ */
+ public void makeCollection() {
+ getLogger().warn("RepositorySource().makeCollection() not implemented yet!");
+ }
+
+ /**
+ *
+ */
+ public String getName() {
+ // Quick and dirty
+ String name = new java.io.File(getURI()).getName();
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("getName(): URI: " + name);
+ return name;
+ }
+
+ /**
+ *
+ */
+ public Source getChild(String name) {
+ getLogger().warn("getChild() not implemented yet!");
+ return null;
+ }
+
+ /**
+ *
+ */
+ public Collection getChildren() {
+ try {
+ Collection children = getNode().getChildren();
+ java.util.Iterator iterator = children.iterator();
+ java.util.Vector newChildren = new java.util.Vector();
+ while (iterator.hasNext()) {
+ Node child = (Node) iterator.next();
+ newChildren.add(new RepositorySource(getNodeFactory(), child.getSourceURI(),
+ this.session, getLogger()));
+ }
+ return newChildren;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ *
+ */
+ public boolean isCollection() {
+ try {
+ return getNode().isCollection();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private SourceValidity validity;
+
+ public SourceValidity getValidity() {
+ if (this.validity == null) {
+ this.validity = new RepositorySourceValidity(this);
+ }
+ return this.validity;
+ }
+
+ public NodeFactory getNodeFactory() {
+ return nodeFactory;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java
new file mode 100644
index 0000000..6249618
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import org.apache.excalibur.source.SourceValidity;
+
+/**
+ * Validity for repository sources.
+ */
+public class RepositorySourceValidity implements SourceValidity {
+
+ private static final long serialVersionUID = 1L;
+
+ private String sourceUri;
+ private long lastModified;
+
+ /**
+ * @param source The source this validity is for.
+ */
+ public RepositorySourceValidity(RepositorySource source) {
+ this.sourceUri = source.getSourceURI();
+ this.lastModified = source.exists() ? source.getLastModified() : 0;
+ }
+
+ public int isValid() {
+ return SourceValidity.UNKNOWN;
+ }
+
+ public int isValid(SourceValidity validity) {
+ if (validity instanceof RepositorySourceValidity) {
+ RepositorySourceValidity repoValidity = (RepositorySourceValidity) validity;
+ String repoValidityUri = repoValidity.getSourceURI();
+
+ if (!repoValidityUri.equals(this.sourceUri)) {
+ throw new RuntimeException("Wrong source URI: [" + repoValidityUri
+ + "] instead of [" + this.sourceUri + "]!");
+ }
+ if (this.lastModified >= repoValidity.getLastModified()) {
+ return SourceValidity.VALID;
+ } else {
+ return SourceValidity.INVALID;
+ }
+ } else {
+ return SourceValidity.INVALID;
+ }
+ }
+
+ protected long getLastModified() {
+ return this.lastModified;
+ }
+
+ protected String getSourceURI() {
+ return this.sourceUri;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java
new file mode 100644
index 0000000..068018a
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.cocoon.processing.ProcessInfoProvider;
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Repository;
+import org.apache.lenya.cms.publication.Session;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.cms.site.SiteStructure;
+import org.apache.lenya.util.ServletHelper;
+
+/**
+ * <p>
+ * This source factory allows to access documents based on their path in the site structure.
+ * </p>
+ * <p>
+ * Relative addressing refers to the current publication and area.
+ * </p>
+ * <p>
+ * Syntax:
+ * </p>
+ * <ul>
+ * <li>Absolute: <code>site://{pubId}/{area}/{language}{path}</code></li>
+ * <li>Relative: <code>site:/{language}{path}</code></li>
+ * </ul>
+ * <p>
+ * Usage examples:
+ * </p>
+ * <ul>
+ * <li><code>site://default/authoring/en/news/today</code></li>
+ * <li><code>site:/en/news/today</code></li>
+ * </ul>
+ */
+public class SiteSourceFactory extends AbstractLogEnabled implements SourceFactory {
+
+ private SourceResolver sourceResolver;
+ private Repository repository;
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(String location, Map parameters) throws MalformedURLException,
+ IOException, SourceException {
+ ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils
+ .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE);
+ HttpServletRequest request = process.getRequest();
+
+ String areaName = null;
+ String pubId;
+
+ StringTokenizer locationSteps = new StringTokenizer(location, "?");
+ String completePath = locationSteps.nextToken();
+
+ String relativePath;
+ try {
+
+ String scheme = completePath.substring(0, completePath.indexOf(":") + 1);
+ final String absolutePath = completePath.substring(scheme.length());
+ if (absolutePath.startsWith("//")) {
+ final String fullPath = absolutePath.substring(2);
+ StringTokenizer steps = new StringTokenizer(fullPath, "/");
+ pubId = steps.nextToken();
+ areaName = steps.nextToken();
+ String prefix = pubId + "/" + areaName;
+ relativePath = fullPath.substring(prefix.length());
+ } else if (absolutePath.startsWith("/")) {
+ String webappUrl = ServletHelper.getWebappURI(request);
+ URLInformation info = new URLInformation(webappUrl);
+ pubId = info.getPublicationId();
+ areaName = info.getArea();
+ relativePath = absolutePath;
+ } else {
+ throw new MalformedURLException("The path [" + absolutePath
+ + "] must start with at least one slash.");
+ }
+
+ Session session = this.repository.getSession(request);
+ SiteStructure site = session.getPublication(pubId).getArea(areaName).getSite();
+
+ String[] steps = relativePath.substring(1).split("/");
+
+ String language = steps[0];
+ String prefix = "/" + language;
+ String path = relativePath.substring(prefix.length());
+
+ if (site.contains(path, language)) {
+ Document doc = site.getNode(path).getLink(language).getDocument();
+ String docUri = "lenya-document:" + doc.getUUID() + ",lang=" + doc.getLanguage()
+ + ",area=" + doc.getArea() + ",pub=" + doc.getPublication().getId();
+
+ if (locationSteps.hasMoreTokens()) {
+ String queryString = locationSteps.nextToken();
+ docUri = docUri + "?" + queryString;
+ }
+ return this.sourceResolver.resolveURI(docUri);
+ } else {
+ throw new SourceNotFoundException("The source [" + location + "] doesn't exist.");
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
+ */
+ public void release(Source source) {
+ this.sourceResolver.release(source);
+ }
+
+ public SourceResolver getSourceResolver() {
+ return sourceResolver;
+ }
+
+ public void setSourceResolver(SourceResolver sourceResolver) {
+ this.sourceResolver = sourceResolver;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java
new file mode 100644
index 0000000..efd3fc9
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.cocoon.source;
+
+import org.apache.lenya.cms.publication.templating.ExistingAncestorSourceResolver;
+import org.apache.lenya.cms.publication.templating.VisitingSourceResolver;
+
+/**
+ * Source factory following the fallback principle, resolving the existing ancestor of the existing resource.
+ * For more information about the URL syntax, see {@link FallbackSourceFactory}.
+ *
+ * @version $Id: FallbackSourceFactory.java 264153 2005-08-29 15:11:14Z andreas $
+ */
+public class TemplateFallbackSourceFactory extends FallbackSourceFactory {
+
+ protected VisitingSourceResolver getSourceVisitor() {
+ return new ExistingAncestorSourceResolver();
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java
new file mode 100644
index 0000000..7d57a59
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lenya.cms.cocoon.source;
+//package org.apache.cocoon.components.source.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.cocoon.util.MIMEUtils;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceValidity;
+
+/**
+ * @author <a href="http://apache.org/~reinhard">Reinhard Poetz</a>
+ * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+ * @version CVS $Id: ZipSource.java 30932 2004-07-29 17:35:38Z vgritsenko $
+ * @since 2.1.4
+ */
+public class ZipSource extends AbstractLogEnabled implements Source {
+
+ Source archive;
+ String documentName;
+
+ /**
+ * Ctor.
+ * @param archive The archive source.
+ * @param fileName The filename.
+ */
+ public ZipSource(Source archive, String fileName) {
+ this.archive = archive;
+ this.documentName = fileName;
+ }
+
+ public boolean exists() {
+ if(!this.archive.exists()) {
+ return false;
+ }
+ ZipInputStream zipStream = null;
+ ZipEntry document = null;
+ boolean found = false;
+ try {
+ zipStream = new ZipInputStream(this.archive.getInputStream());
+ do {
+ document = zipStream.getNextEntry();
+ if (document != null) {
+ if (document.getName().equals(this.documentName)) {
+ found = true;
+ } else {
+ zipStream.closeEntry();
+ }
+ }
+ } while (document != null && found == false);
+ } catch(IOException ioe) {
+ return false;
+ } finally {
+ try {
+ zipStream.close();
+ } catch (IOException ioe) {
+ this.getLogger().error("Error while closing ZipInputStream: " + this.documentName);
+ }
+ }
+ return found;
+ }
+
+ public InputStream getInputStream()
+ throws IOException, SourceNotFoundException {
+
+ ZipInputStream zipStream =
+ new ZipInputStream(this.archive.getInputStream());
+ ZipEntry document = null;
+ boolean found = false;
+ do {
+ document = zipStream.getNextEntry();
+ if (document != null) {
+ if (document.getName().equals(this.documentName)) {
+ found = true;
+ } else {
+ // go to next entry
+ zipStream.closeEntry();
+ }
+ }
+ } while (document != null && found == false);
+
+ if (document == null) {
+ throw new SourceNotFoundException(
+ "The document "
+ + documentName
+ + " is not in the archive "
+ + this.archive.getURI());
+ }
+
+ // now we will extract the document and write it into a byte array
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[8192];
+ int length = -1;
+ while (zipStream.available() > 0) {
+ length = zipStream.read(buffer, 0, 8192);
+ if (length > 0) {
+ baos.write(buffer, 0, length);
+ }
+ }
+ zipStream.close();
+ baos.flush();
+
+ // return an input stream
+ return new ByteArrayInputStream(baos.toByteArray());
+ }
+
+ public String getURI() {
+ return this.archive.getURI() + "/" + this.documentName;
+ }
+
+ public String getScheme() {
+ return ZipSourceFactory.ZIP_SOURCE_SCHEME;
+ }
+
+ public SourceValidity getValidity() {
+ return this.archive.getValidity();
+ }
+
+ public void refresh() {
+ }
+
+ public String getMimeType() {
+ String ext = this.documentName.substring( this.documentName.lastIndexOf(".") );
+ return MIMEUtils.getMIMEType( ext );
+ }
+
+ public long getContentLength() {
+ return -1;
+ }
+
+ public long getLastModified() {
+ return this.archive.getLastModified();
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java
new file mode 100644
index 0000000..e26596b
--- /dev/null
+++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//package org.apache.cocoon.components.source.impl;
+package org.apache.lenya.cms.cocoon.source;
+
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceFactory;
+import org.apache.excalibur.source.SourceResolver;
+
+
+/** Implementation of a {@link Source} that gets its content from
+ * and ZIP archive.
+ *
+ * A ZIP source can be reached using the zip:// pseudo-protocol. The syntax is
+ * zip://myFile.xml@myZip.zip (zip://[file]@[archive])
+ *
+ * @author <a href="http://apache.org/~reinhard">Reinhard Poetz</a>
+ * @version CVS $Id: ZipSourceFactory.java 30932 2004-07-29 17:35:38Z vgritsenko $
+ * @since 2.1.4
+ */
+public class ZipSourceFactory extends AbstractLogEnabled
+ implements SourceFactory, ThreadSafe, Serviceable {
+
+ protected ServiceManager manager;
+ /**
+ * The URL scheme, including the colon.
+ */
+ public static final String ZIP_SOURCE_SCHEME = "zip:";
+
+ public Source getSource(String location, Map parameters)
+ throws IOException, MalformedURLException {
+
+ if ( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug("Processing " + location);
+ }
+
+ // syntax checks
+ int separatorPos = location.indexOf('@');
+ if (separatorPos == -1) {
+ throw new MalformedURLException("@ required in URI: " + location);
+ }
+ int protocolEnd = location.indexOf("://");
+ if (protocolEnd == -1) {
+ throw new MalformedURLException("URI does not contain '://' : " + location);
+ }
+
+ // get the source of the archive and return the ZipSource passing
+ // a source retrieved from the SourceResolver
+ String documentName = location.substring(protocolEnd + 3, separatorPos);
+ Source archive;
+ SourceResolver resolver = null;
+ try {
+ resolver = (SourceResolver)this.manager.lookup( SourceResolver.ROLE );
+ archive = resolver.resolveURI(location.substring(separatorPos + 1));
+ } catch (ServiceException se) {
+ throw new SourceException("SourceResolver is not available.", se);
+ } finally {
+ this.manager.release(resolver);
+ }
+ return new ZipSource(archive, documentName);
+ }
+
+
+ public void release(Source source) {
+ // not necessary here
+ }
+
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ }
+
+}
diff --git a/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml b/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml
index 8bff2f4..4a18f78 100644
--- a/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml
+++ b/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml
@@ -25,8 +25,8 @@
http://cocoon.apache.org/schema/servlet http://cocoon.apache.org/schema/servlet/cocoon-servlet-1.0.xsd">
<!-- exemple from the archetype -->
- <bean name="org.apache.lenya.lenya-core-cocoon-components.service" class="org.apache.cocoon.sitemap.SitemapServlet">
- <servlet:context mount-path="/lenya-core-cocoon-components" context-path="blockcontext:/lenya-core-cocoon-components/"/>
+ <bean name="org.apache.lenya.core.cocoon.block" class="org.apache.cocoon.sitemap.SitemapServlet">
+ <servlet:context mount-path="/lenya.core.cocoon" context-path="blockcontext:/lenya.core.cocoon/"/>
</bean>
</beans>