View | Details | Raw Unified | Return to bug 44990
Collapse All | Expand All

(-)impl/java/org/apache/lenya/cms/publication/DocumentImpl.java (+117 lines)
Lines 23-28 Link Here
23
import java.io.OutputStream;
23
import java.io.OutputStream;
24
import java.util.ArrayList;
24
import java.util.ArrayList;
25
import java.util.Date;
25
import java.util.Date;
26
import java.util.Iterator;
26
import java.util.List;
27
import java.util.List;
27
28
28
import org.apache.avalon.framework.container.ContainerUtil;
29
import org.apache.avalon.framework.container.ContainerUtil;
Lines 39-50 Link Here
39
import org.apache.lenya.cms.repository.ContentHolder;
40
import org.apache.lenya.cms.repository.ContentHolder;
40
import org.apache.lenya.cms.repository.Node;
41
import org.apache.lenya.cms.repository.Node;
41
import org.apache.lenya.cms.repository.NodeFactory;
42
import org.apache.lenya.cms.repository.NodeFactory;
43
import org.apache.lenya.cms.repository.Persistable;
42
import org.apache.lenya.cms.repository.RepositoryException;
44
import org.apache.lenya.cms.repository.RepositoryException;
43
import org.apache.lenya.cms.repository.Session;
45
import org.apache.lenya.cms.repository.Session;
44
import org.apache.lenya.cms.site.Link;
46
import org.apache.lenya.cms.site.Link;
45
import org.apache.lenya.cms.site.SiteException;
47
import org.apache.lenya.cms.site.SiteException;
46
import org.apache.lenya.cms.site.SiteStructure;
48
import org.apache.lenya.cms.site.SiteStructure;
47
import org.apache.lenya.util.Assert;
49
import org.apache.lenya.util.Assert;
50
import org.apache.lenya.xml.DocumentHelper;
51
import org.apache.xpath.XPathAPI;
52
import org.w3c.dom.Attr;
53
import org.w3c.dom.traversal.NodeIterator;
48
54
49
/**
55
/**
50
 * A typical CMS document.
56
 * A typical CMS document.
Lines 557-562 Link Here
557
    public Node getRepositoryNode() {
563
    public Node getRepositoryNode() {
558
        if (this.repositoryNode == null) {
564
        if (this.repositoryNode == null) {
559
            this.repositoryNode = getRepositoryNode(this.manager, getFactory(), getSourceURI());
565
            this.repositoryNode = getRepositoryNode(this.manager, getFactory(), getSourceURI());
566
            try {
567
                Persistable persistable = this.repositoryNode.getPersistable();
568
                if (!(persistable instanceof ComposedPersistable)) {
569
                    ComposedPersistable compPersistable = new ComposedPersistable();
570
                    compPersistable.add(new LinkExtractor(this));
571
                    compPersistable.add(persistable);
572
                    this.repositoryNode.setPersistable(compPersistable);
573
                }
574
            } catch (RepositoryException e) {
575
                throw new RuntimeException(e);
576
            }
560
        }
577
        }
561
        return this.repositoryNode;
578
        return this.repositoryNode;
562
    }
579
    }
Lines 706-710 Link Here
706
        }
723
        }
707
        return this.revision;
724
        return this.revision;
708
    }
725
    }
726
    
727
    protected static class ComposedPersistable implements Persistable {
728
        
729
        private List persistables = new ArrayList();
730
        
731
        public void add(Persistable persistable) {
732
            Assert.notNull("persistable", persistable);
733
            this.persistables.add(persistable);
734
        }
709
735
736
        public boolean isModified() {
737
            for (Iterator i = this.persistables.iterator(); i.hasNext(); ) {
738
                Persistable persistable = (Persistable) i.next();
739
                if (persistable.isModified()) {
740
                    return true;
741
                }
742
            }
743
            return false;
744
        }
745
746
        public void save() throws RepositoryException {
747
            for (Iterator i = this.persistables.iterator(); i.hasNext(); ) {
748
                Persistable persistable = (Persistable) i.next();
749
                persistable.save();
750
            }
751
        }
752
        
753
    }
754
    
755
    protected static class LinkExtractor implements Persistable {
756
        
757
        protected static final String LINK_METADATA_NAMESPACE = "http://apache.org/lenya/metadata/link/1.0";
758
        protected static final String KEY_OUTGOING_LINKS = "outgoingLinks";
759
        
760
        private Document doc;
761
        
762
        public LinkExtractor(Document doc) {
763
            this.doc = doc;
764
        }
765
        
766
        public boolean isModified() {
767
            return true;
768
        }
769
        
770
        public void save() throws RepositoryException {
771
            try {
772
                MetaData linkMetaData = this.doc.getMetaData(LINK_METADATA_NAMESPACE);
773
                linkMetaData.removeAllValues(KEY_OUTGOING_LINKS);
774
                String[] xPaths = this.doc.getResourceType().getLinkAttributeXPaths();
775
                if (xPaths.length > 0) {
776
                    org.w3c.dom.Document xml = DocumentHelper.readDocument(this.doc.getInputStream());
777
778
                    if (xml == null) {
779
                        throw new RuntimeException("The document [" + this
780
                                + "] doesn't contain any XML content.");
781
                    }
782
                    for (int i = 0; i < xPaths.length; i++) {
783
                        NodeIterator iter = XPathAPI.selectNodeIterator(xml, xPaths[i]);
784
                        org.w3c.dom.Node node;
785
                        while ((node = iter.nextNode()) != null) {
786
                            Attr attr = (Attr) node;
787
                            String uri = attr.getValue();
788
                            if (isLinkUri(uri)) {
789
                            String linkUri = getDocumentLinkUri(uri);
790
                                linkMetaData.addValue(KEY_OUTGOING_LINKS, linkUri);
791
                            }
792
                        }
793
                    }
794
                }
795
            } catch (RuntimeException e) {
796
                throw e;
797
            } catch (Exception e) {
798
                throw new RuntimeException(e);
799
            }
800
        }
801
802
        /**
803
         * @param uri The URI as used in the content.
804
         * @return The actual link URI without anchor and query string.
805
         */
806
        protected String getDocumentLinkUri(String uri) {
807
            String docUri = uri;
808
            docUri = removeSuffix(docUri, '#');
809
            docUri = removeSuffix(docUri, '?');
810
            return docUri;
811
        }
812
813
        protected String removeSuffix(String docUri, char delimiter) {
814
            int anchorIndex = docUri.indexOf(delimiter);
815
            if (anchorIndex > -1) {
816
                docUri = docUri.substring(0, anchorIndex);
817
            }
818
            return docUri;
819
        }
820
821
        protected boolean isLinkUri(String uri) {
822
            return uri.startsWith("lenya-document:");
823
        }
824
    }
825
826
710
}
827
}
(-)java/org/apache/lenya/cms/search/SearchUtil.java (+38 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.cms.search;
19
20
/**
21
 * Search utility class.
22
 */
23
public class SearchUtil {
24
25
    /**
26
     * Returns the field name for a meta data element. Caution: The field name
27
     * has to be escaped using
28
     * {@link org.apache.lucene.queryParser.QueryParser#escape(String)} to use
29
     * it in query strings.
30
     * @param namespaceUri The namespace URI of the meta data element set.
31
     * @param elementName The name of the meta data element.
32
     * @return The field name.
33
     */
34
    public static final String getMetaDataFieldName(String namespaceUri, String elementName) {
35
        return "{" + namespaceUri + "}" + elementName;
36
    }
37
38
}
(-)java/org/apache/lenya/cms/search/SearchException.java (+23 lines)
Line 0 Link Here
1
package org.apache.lenya.cms.search;
2
3
public class SearchException extends RuntimeException {
4
5
    private static final long serialVersionUID = 1L;
6
7
    public SearchException() {
8
        super();
9
    }
10
11
    public SearchException(String message, Throwable cause) {
12
        super(message, cause);
13
    }
14
15
    public SearchException(String message) {
16
        super(message);
17
    }
18
19
    public SearchException(Throwable cause) {
20
        super(cause);
21
    }
22
23
}
(-)java/org/apache/lenya/cms/search/SearchResult.java (+70 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.cms.search;
19
20
/**
21
 * A search result.
22
 */
23
public interface SearchResult {
24
    
25
    /**
26
     * Shortcut for <code>getFieldValue(DocumentSearcher.FIELD_PUBLICATION).</code>
27
     * @return The publication ID or <code>null</code> if the result doesn't
28
     *         contain this field.
29
     */
30
    String getPublicationId();
31
32
    /**
33
     * Shortcut for <code>getFieldValue(DocumentSearcher.FIELD_UUID).</code>
34
     * @return The UUID or <code>null</code> if the result doesn't contain
35
     *         this field.
36
     */
37
    String getUuid();
38
39
    /**
40
     * Shortcut for <code>getFieldValue(DocumentSearcher.FIELD_LANGUAGE).</code>
41
     * @return The language or <code>null</code> if the result doesn't contain
42
     *         this field.
43
     */
44
    String getLanguage();
45
46
    /**
47
     * @return The names of the fields of this result.
48
     */
49
    String[] getFieldNames();
50
51
    /**
52
     * @param fieldName The name of the field.
53
     * @return the field value or <code>null</code> if the result doesn't
54
     *         contain this field.
55
     */
56
    String getFieldValue(String fieldName);
57
58
    /**
59
     * @param fieldName The name of the field.
60
     * @return an array of values or <code>null</code> if the result doesn't
61
     *         contain this field.
62
     */
63
    String[] getFieldValues(String fieldName);
64
65
    /**
66
     * @return The score of this result.
67
     */
68
    float getScore();
69
70
}
(-)java/org/apache/lenya/cms/search/SearchResults.java (+36 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.cms.search;
19
20
/**
21
 * A list of search results.
22
 */
23
public interface SearchResults {
24
    
25
    /**
26
     * @return The number of search results in this list.
27
     */
28
    int getLength();
29
    
30
    /**
31
     * @param number The number of the result to return.
32
     * @return A search result.
33
     */
34
    SearchResult getResult(int number);
35
36
}
(-)java/org/apache/lenya/cms/search/DocumentSearcher.java (+64 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.cms.search;
19
20
import org.apache.lucene.search.Query;
21
22
/**
23
 * Service to search for documents.
24
 */
25
public interface DocumentSearcher {
26
    
27
    /**
28
     * The name of the field which stores the publication ID.
29
     */
30
    String FIELD_PUBLICATION = "publication";
31
    
32
    /**
33
     * The name of the field which stores the UUID.
34
     */
35
    String FIELD_UUID = "uuid";
36
    
37
    /**
38
     * The name of the field which stores the language.
39
     */
40
    String FIELD_LANGUAGE = "language";
41
42
    /**
43
     * The service role.
44
     */
45
    String ROLE = DocumentSearcher.class.getName();
46
    
47
    /**
48
     * @param indexId The ID of the index to use.
49
     * @param queryString The query string, conforming to the Lucene query parser syntax.
50
     * @param defaultField The field to search if no field is specified in the query string.
51
     * @return A search results object.
52
     * @throws SearchException if the index does not exist or the query string is invalid.
53
     */
54
    SearchResults find(String indexId, String queryString, String defaultField) throws SearchException;
55
    
56
    /**
57
     * @param indexId The ID of the index to use.
58
     * @param query The query.
59
     * @return A search results object.
60
     * @throws SearchException if the index does not exist.
61
     */
62
    SearchResults find(String indexId, Query query) throws SearchException;
63
    
64
}
(-)modules/lucene/java/src/org/apache/cocoon/components/search/components/impl/IndexManagerImpl.java (-12 / +22 lines)
Lines 50-58 Link Here
50
import org.apache.lenya.cms.publication.DocumentFactory;
50
import org.apache.lenya.cms.publication.DocumentFactory;
51
import org.apache.lenya.cms.publication.DocumentUtil;
51
import org.apache.lenya.cms.publication.DocumentUtil;
52
import org.apache.lenya.cms.publication.Publication;
52
import org.apache.lenya.cms.publication.Publication;
53
import org.apache.lenya.cms.publication.PublicationException;
54
import org.apache.lenya.cms.publication.PublicationManager;
53
import org.apache.lenya.cms.publication.PublicationManager;
55
import org.apache.lenya.modules.lucene.MetaDataFieldRegistry;
54
import org.apache.lenya.cms.search.DocumentSearcher;
55
import org.apache.lenya.cms.search.SearchUtil;
56
56
57
/**
57
/**
58
 * Index Manager Component. Configure and Manage the differents indexes.
58
 * Index Manager Component. Configure and Manage the differents indexes.
Lines 290-296 Link Here
290
     */
290
     */
291
    private void addIndexes(Configuration configuration) throws ConfigurationException {
291
    private void addIndexes(Configuration configuration) throws ConfigurationException {
292
        AnalyzerManager analyzerManager = null;
292
        AnalyzerManager analyzerManager = null;
293
        MetaDataFieldRegistry registry = null;
293
        MetaDataRegistry registry = null;
294
294
295
        Configuration[] confs = configuration.getChildren(INDEX_ELEMENT);
295
        Configuration[] confs = configuration.getChildren(INDEX_ELEMENT);
296
296
Lines 299-305 Link Here
299
        }
299
        }
300
        try {
300
        try {
301
            analyzerManager = (AnalyzerManager) this.manager.lookup(AnalyzerManager.ROLE);
301
            analyzerManager = (AnalyzerManager) this.manager.lookup(AnalyzerManager.ROLE);
302
            registry = (MetaDataFieldRegistry) this.manager.lookup(MetaDataFieldRegistry.ROLE);
302
            registry = (MetaDataRegistry) this.manager.lookup(MetaDataRegistry.ROLE);
303
303
304
            // configure each index
304
            // configure each index
305
            for (int i = 0; i < confs.length; i++) {
305
            for (int i = 0; i < confs.length; i++) {
Lines 318-331 Link Here
318
318
319
                addMetaDataFieldDefinitions(registry, docdecl);
319
                addMetaDataFieldDefinitions(registry, docdecl);
320
                
320
                
321
                FieldDefinition uuidDef = FieldDefinition.create("uuid", FieldDefinition.KEYWORD);
321
                FieldDefinition uuidDef = FieldDefinition.create(DocumentSearcher.FIELD_UUID, FieldDefinition.KEYWORD);
322
                uuidDef.setStore(true);
322
                uuidDef.setStore(true);
323
                docdecl.addFieldDef(uuidDef);
323
                docdecl.addFieldDef(uuidDef);
324
324
325
                FieldDefinition langDef = FieldDefinition.create("language", FieldDefinition.KEYWORD);
325
                FieldDefinition langDef = FieldDefinition.create(DocumentSearcher.FIELD_LANGUAGE, FieldDefinition.KEYWORD);
326
                langDef.setStore(true);
326
                langDef.setStore(true);
327
                docdecl.addFieldDef(langDef);
327
                docdecl.addFieldDef(langDef);
328
328
329
                FieldDefinition pubDef = FieldDefinition.create(DocumentSearcher.FIELD_PUBLICATION, FieldDefinition.KEYWORD);
330
                pubDef.setStore(true);
331
                docdecl.addFieldDef(pubDef);
332
329
                for (int j = 0; j < fields.length; j++) {
333
                for (int j = 0; j < fields.length; j++) {
330
334
331
                    FieldDefinition fielddecl;
335
                    FieldDefinition fielddecl;
Lines 398-410 Link Here
398
        }
402
        }
399
    }
403
    }
400
404
401
    protected void addMetaDataFieldDefinitions(MetaDataFieldRegistry registry,
405
    protected void addMetaDataFieldDefinitions(MetaDataRegistry registry,
402
            IndexStructure indexStructure) throws MetaDataException {
406
            IndexStructure indexStructure) throws MetaDataException {
403
        String[] fieldNames = registry.getFieldNames();
407
        
404
        for (int i = 0; i < fieldNames.length; i++) {
408
        String[] namespaces = registry.getNamespaceUris();
405
            FieldDefinition fieldDef = FieldDefinition.create(fieldNames[i], FieldDefinition.TEXT);
409
        for (int n = 0; n < namespaces.length; n++) {
406
            fieldDef.setStore(false);
410
            ElementSet elementSet = registry.getElementSet(namespaces[n]);
407
            indexStructure.addFieldDef(fieldDef);
411
            Element[] elements = elementSet.getElements();
412
            for (int e = 0; e < elements.length; e++) {
413
                String fieldName = SearchUtil.getMetaDataFieldName(namespaces[n], elements[e].getName());
414
                FieldDefinition fieldDef = FieldDefinition.create(fieldName, FieldDefinition.TEXT);
415
                fieldDef.setStore(false);
416
                indexStructure.addFieldDef(fieldDef);
417
            }
408
        }
418
        }
409
    }
419
    }
410
420
(-)modules/lucene/java/src/org/apache/cocoon/transformation/LuceneIndexTransformer2.java (-1 / +1 lines)
Lines 596-602 Link Here
596
    }
596
    }
597
597
598
    /**
598
    /**
599
     * Set a custum analyzer (default: the analyzer of the index).
599
     * Set a custom analyzer (default: the analyzer of the index).
600
     * @param analyzerId The analyzer ID (may be null)
600
     * @param analyzerId The analyzer ID (may be null)
601
     * @throws SAXException if an error occurs.
601
     * @throws SAXException if an error occurs.
602
     */
602
     */
(-)modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResult.java (+71 lines)
Line 0 Link Here
1
package org.apache.lenya.modules.lucene.impl;
2
3
import java.util.Enumeration;
4
import java.util.HashSet;
5
import java.util.Set;
6
7
import org.apache.lenya.cms.search.SearchResult;
8
import org.apache.lenya.util.Assert;
9
import org.apache.lucene.document.Document;
10
import org.apache.lucene.document.Field;
11
12
public class LuceneSearchResult implements SearchResult {
13
14
    private Document document;
15
    private float score;
16
17
    protected LuceneSearchResult(Document luceneDoc, float score) {
18
        Assert.notNull("lucene document", luceneDoc);
19
        this.document = luceneDoc;
20
        this.score = score;
21
    }
22
23
    private Set fieldNames;
24
25
    protected Set fieldNames() {
26
        if (this.fieldNames == null) {
27
            this.fieldNames = new HashSet();
28
            for (Enumeration e = this.document.fields(); e.hasMoreElements();) {
29
                Field field = (Field) e.nextElement();
30
                this.fieldNames.add(field.name());
31
            }
32
        }
33
        return this.fieldNames;
34
    }
35
36
    public String[] getFieldNames() {
37
        Set names = fieldNames();
38
        return (String[]) names.toArray(new String[names.size()]);
39
    }
40
41
    public float getScore() {
42
        return this.score;
43
    }
44
45
    public String getFieldValue(String fieldName) {
46
        confirmFieldExists(fieldName);
47
        return this.document.get(fieldName);
48
    }
49
50
    public String[] getFieldValues(String fieldName) {
51
        confirmFieldExists(fieldName);
52
        return this.document.getValues(fieldName);
53
    }
54
55
    protected void confirmFieldExists(String fieldName) {
56
        Assert.isTrue("field '" + fieldName + "' exists", fieldNames().contains(fieldName));
57
    }
58
59
    public String getLanguage() {
60
        return getFieldValue(LuceneDocumentSearcher.FIELD_LANGUAGE);
61
    }
62
63
    public String getPublicationId() {
64
        return getFieldValue(LuceneDocumentSearcher.FIELD_PUBLICATION);
65
    }
66
67
    public String getUuid() {
68
        return getFieldValue(LuceneDocumentSearcher.FIELD_UUID);
69
    }
70
71
}
(-)modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResults.java (+42 lines)
Line 0 Link Here
1
package org.apache.lenya.modules.lucene.impl;
2
3
import java.io.IOException;
4
import java.util.HashMap;
5
import java.util.Map;
6
7
import org.apache.lenya.cms.publication.DocumentFactory;
8
import org.apache.lenya.cms.search.SearchResult;
9
import org.apache.lenya.cms.search.SearchResults;
10
import org.apache.lenya.util.Assert;
11
import org.apache.lucene.search.Hits;
12
13
public class LuceneSearchResults implements SearchResults {
14
    
15
    private Hits hits;
16
    private Map number2result = new HashMap();
17
    
18
    public LuceneSearchResults(Hits hits) {
19
        Assert.notNull("hits", hits);
20
        this.hits = hits;
21
    }
22
23
    public int getLength() {
24
        return this.hits.length();
25
    }
26
    
27
    public SearchResult getResult(int number) {
28
        Integer key = new Integer(number);
29
        SearchResult result = (SearchResult) this.number2result.get(key);
30
        if (result == null) {
31
            try {
32
                result = new LuceneSearchResult(this.hits.doc(number),
33
                        this.hits.score(number));
34
            } catch (IOException e) {
35
                throw new RuntimeException(e);
36
            }
37
            this.number2result.put(key, result);
38
        }
39
        return result;
40
    }
41
42
}
(-)modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneDocumentSearcher.java (+95 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.modules.lucene.impl;
19
20
import org.apache.avalon.framework.logger.AbstractLogEnabled;
21
import org.apache.avalon.framework.service.ServiceException;
22
import org.apache.avalon.framework.service.ServiceManager;
23
import org.apache.avalon.framework.service.Serviceable;
24
import org.apache.avalon.framework.thread.ThreadSafe;
25
import org.apache.cocoon.ProcessingException;
26
import org.apache.cocoon.components.search.Index;
27
import org.apache.cocoon.components.search.LuceneCocoonSearcher;
28
import org.apache.cocoon.components.search.components.AnalyzerManager;
29
import org.apache.cocoon.components.search.components.IndexManager;
30
import org.apache.lenya.cms.search.DocumentSearcher;
31
import org.apache.lenya.cms.search.SearchResults;
32
import org.apache.lucene.search.Hits;
33
import org.apache.lucene.search.Query;
34
35
public class LuceneDocumentSearcher extends AbstractLogEnabled implements DocumentSearcher,
36
        Serviceable, ThreadSafe {
37
38
    private ServiceManager manager;
39
40
    public SearchResults find(String indexId, final String queryString, final String defaultField) {
41
        return getResults(indexId, new ResultExtractor() {
42
            public SearchResults getResults(LuceneCocoonSearcher searcher)
43
                    throws ProcessingException {
44
                Hits hits = searcher.search(queryString, defaultField);
45
                return new LuceneSearchResults(hits);
46
            }
47
        });
48
    }
49
50
    public void service(ServiceManager manager) throws ServiceException {
51
        this.manager = manager;
52
    }
53
54
    public SearchResults find(String indexId, final Query query) {
55
        return getResults(indexId, new ResultExtractor() {
56
            public SearchResults getResults(LuceneCocoonSearcher searcher)
57
                    throws ProcessingException {
58
                Hits hits = searcher.search(query);
59
                return new LuceneSearchResults(hits);
60
            }
61
        });
62
    }
63
64
    protected interface ResultExtractor {
65
        SearchResults getResults(LuceneCocoonSearcher searcher) throws ProcessingException;
66
    }
67
68
    protected SearchResults getResults(String indexId, ResultExtractor extractor) {
69
        IndexManager indexManager = null;
70
        AnalyzerManager analyzerManager = null;
71
        LuceneCocoonSearcher searcher = null;
72
        try {
73
            searcher = (LuceneCocoonSearcher) this.manager.lookup(LuceneCocoonSearcher.ROLE);
74
            indexManager = (IndexManager) this.manager.lookup(IndexManager.ROLE);
75
            analyzerManager = (AnalyzerManager) this.manager.lookup(AnalyzerManager.ROLE);
76
            Index index = indexManager.getIndex(indexId);
77
            String analyzerId = "standard";
78
            searcher.setAnalyzer(analyzerManager.getAnalyzer(analyzerId));
79
            searcher.setDirectory(index.getDirectory());
80
            return extractor.getResults(searcher);
81
        } catch (RuntimeException e) {
82
            throw e;
83
        } catch (Exception e) {
84
            throw new RuntimeException(e);
85
        } finally {
86
            if (indexManager != null) {
87
                this.manager.release(indexManager);
88
            }
89
            if (analyzerManager != null) {
90
                this.manager.release(analyzerManager);
91
            }
92
        }
93
    }
94
95
}
(-)modules/lucene/java/src/org/apache/lenya/modules/lucene/QueryStringModule.java (-35 / +8 lines)
Lines 31-37 Link Here
31
import org.apache.lenya.cms.metadata.Element;
31
import org.apache.lenya.cms.metadata.Element;
32
import org.apache.lenya.cms.metadata.ElementSet;
32
import org.apache.lenya.cms.metadata.ElementSet;
33
import org.apache.lenya.cms.metadata.MetaDataRegistry;
33
import org.apache.lenya.cms.metadata.MetaDataRegistry;
34
import org.apache.lenya.cms.search.SearchUtil;
34
import org.apache.lucene.index.Term;
35
import org.apache.lucene.index.Term;
36
import org.apache.lucene.queryParser.QueryParser;
35
import org.apache.lucene.search.BooleanQuery;
37
import org.apache.lucene.search.BooleanQuery;
36
import org.apache.lucene.search.TermQuery;
38
import org.apache.lucene.search.TermQuery;
37
39
Lines 44-63 Link Here
44
    protected static final String PARAM_QUERY_STRING = "queryString";
46
    protected static final String PARAM_QUERY_STRING = "queryString";
45
    protected static final String[] DEFAULT_FIELDS = { "body" };
47
    protected static final String[] DEFAULT_FIELDS = { "body" };
46
    
48
    
47
    protected static final char[] ESCAPED_CHARACTERS = { '+', '-', '&', '|', '!', '(', ')', '{',
48
        '}', '[', ']', '^', '"', '~', '*', '?', ':', '\\' };
49
50
    protected ServiceManager manager;
49
    protected ServiceManager manager;
51
50
52
    public Object getAttribute(String name, Configuration modeConf, Map objectModel)
51
    public Object getAttribute(String name, Configuration modeConf, Map objectModel)
53
            throws ConfigurationException {
52
            throws ConfigurationException {
54
55
        if (name.equals("queryString")) {
53
        if (name.equals("queryString")) {
56
            return getQueryString(objectModel);
54
            return getQueryString(objectModel);
57
        } else {
55
        } else {
58
            throw new IllegalArgumentException("The attribute [" + name + "] is not supported.");
56
            throw new IllegalArgumentException("The attribute [" + name + "] is not supported.");
59
        }
57
        }
60
61
    }
58
    }
62
59
63
    protected String getQueryString(Map objectModel) {
60
    protected String getQueryString(Map objectModel) {
Lines 68-73 Link Here
68
            return "";
65
            return "";
69
        }
66
        }
70
        
67
        
68
        if (searchTerm.indexOf(':') > -1) {
69
            return searchTerm;
70
        }
71
        
71
        if (searchTerm.indexOf(' ') > -1) {
72
        if (searchTerm.indexOf(' ') > -1) {
72
            searchTerm = "(" + searchTerm + ")";
73
            searchTerm = "(" + searchTerm + ")";
73
        }
74
        }
Lines 89-105 Link Here
89
        }
90
        }
90
91
91
        MetaDataRegistry registry = null;
92
        MetaDataRegistry registry = null;
92
        MetaDataFieldRegistry fieldRegistry = null;
93
        try {
93
        try {
94
            registry = (MetaDataRegistry) this.manager.lookup(MetaDataRegistry.ROLE);
94
            registry = (MetaDataRegistry) this.manager.lookup(MetaDataRegistry.ROLE);
95
            fieldRegistry = (MetaDataFieldRegistry) this.manager.lookup(MetaDataFieldRegistry.ROLE);
96
            String[] namespaces = registry.getNamespaceUris();
95
            String[] namespaces = registry.getNamespaceUris();
97
            for (int n = 0; n < namespaces.length; n++) {
96
            for (int n = 0; n < namespaces.length; n++) {
98
                ElementSet elementSet = registry.getElementSet(namespaces[n]);
97
                ElementSet elementSet = registry.getElementSet(namespaces[n]);
99
                Element[] elements = elementSet.getElements();
98
                Element[] elements = elementSet.getElements();
100
                for (int e = 0; e < elements.length; e++) {
99
                for (int e = 0; e < elements.length; e++) {
101
                    if (elements[e].isSearchable()) {
100
                    if (elements[e].isSearchable()) {
102
                        String field = fieldRegistry.getFieldName(namespaces[n], elements[e].getName());
101
                        String field = SearchUtil.getMetaDataFieldName(namespaces[n], elements[e].getName());
103
                        TermQuery termQuery = new TermQuery(getTerm(field, searchTerm));
102
                        TermQuery termQuery = new TermQuery(getTerm(field, searchTerm));
104
                        query.add(termQuery, false, false);
103
                        query.add(termQuery, false, false);
105
                    }
104
                    }
Lines 111-148 Link Here
111
            if (registry != null) {
110
            if (registry != null) {
112
                this.manager.release(registry);
111
                this.manager.release(registry);
113
            }
112
            }
114
            if (fieldRegistry != null) {
115
                this.manager.release(fieldRegistry);
116
            }
117
        }
113
        }
118
        return query;
114
        return query;
119
    }
115
    }
120
116
121
    protected boolean shallEscape(char c) {
122
        for (int i = 0; i < ESCAPED_CHARACTERS.length; i++) {
123
            if (ESCAPED_CHARACTERS[i] == c) {
124
                return true;
125
            }
126
        }
127
        return false;
128
    }
129
130
    protected String escape(final String prefix) {
131
        StringBuffer buffer = new StringBuffer();
132
        StringCharacterIterator i = new StringCharacterIterator(prefix);
133
        char c = i.current();
134
        while (c != CharacterIterator.DONE) {
135
            if (shallEscape(c)) {
136
                buffer.append('\\');
137
            }
138
            buffer.append(c);
139
            c = i.next();
140
        }
141
        return buffer.toString();
142
    }
143
    
144
    protected Term getTerm(String field, String value) {
117
    protected Term getTerm(String field, String value) {
145
        return new Term(escape(field), value);
118
        return new Term(QueryParser.escape(field), value);
146
    }
119
    }
147
120
148
    public void service(ServiceManager manager) throws ServiceException {
121
    public void service(ServiceManager manager) throws ServiceException {
(-)modules/lucene/config/advancedSearch.xml (+25 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
  
10
  http://www.apache.org/licenses/LICENSE-2.0
11
  
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
19
<!--
20
  Override this file in your publication to create a customized "Advanced Search" page.
21
-->
22
<search:search xmlns:search="http://apache.org/cocoon/search/1.0">
23
  <search:field name="title@http://purl.org/dc/elements/1.1/"/>
24
  <search:field name="title@http://purl.org/dc/elements/1.1/"/>
25
</search:search>
(-)modules/lucene/config/cocoon-xconf/documentsearcher.xconf (+26 lines)
Line 0 Link Here
1
<?xml version="1.0"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
10
      http://www.apache.org/licenses/LICENSE-2.0
11
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
19
<!-- $Id:$ -->
20
21
<xconf xpath="/cocoon" unless="/cocoon/component[@role = 'org.apache.lenya.cms.search.DocumentSearcher']">
22
23
  <component logger="lenya.modules.lucene" role="org.apache.lenya.cms.search.DocumentSearcher"
24
    class="org.apache.lenya.modules.lucene.impl.LuceneDocumentSearcher"/>
25
  
26
</xconf>
(-)modules/lucene/sitemap.xmap (+1 lines)
Lines 145-150 Link Here
145
        </map:aggregate>
145
        </map:aggregate>
146
        <map:transform src="fallback://lenya/modules/lucene/xslt/index.xsl">
146
        <map:transform src="fallback://lenya/modules/lucene/xslt/index.xsl">
147
          <map:parameter name="index" value="{1}-{2}"/>
147
          <map:parameter name="index" value="{1}-{2}"/>
148
          <map:parameter name="pub" value="{1}"/>
148
          <map:parameter name="uuid" value="{3}"/>
149
          <map:parameter name="uuid" value="{3}"/>
149
          <map:parameter name="language" value="{4}"/>
150
          <map:parameter name="language" value="{4}"/>
150
        </map:transform>
151
        </map:transform>
(-)modules/lucene/xslt/metadata2index.xsl (-1 / +19 lines)
Lines 13-20 Link Here
13
  
13
  
14
  
14
  
15
  <xsl:template match="*">
15
  <xsl:template match="*">
16
    <lucene:field boost="0.5" namespace="{namespace-uri()}" name="{local-name()}"><xsl:value-of select="."/></lucene:field>
16
    <xsl:variable name="followingValues" select="following-sibling::*[
17
      namespace-uri() = namespace-uri(current()) and
18
      local-name() = local-name(current())
19
      ]"/>
20
    <lucene:field boost="0.5" namespace="{namespace-uri()}" name="{local-name()}">
21
      <xsl:choose>
22
        <xsl:when test="count($followingValues) &gt; 0">
23
          <xsl:value-of select="."/>
24
          <xsl:apply-templates select="$followingValues" mode="followingValues"/>
25
        </xsl:when>
26
        <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
27
      </xsl:choose>
28
    </lucene:field>
17
  </xsl:template>
29
  </xsl:template>
18
  
30
  
19
  
31
  
32
  <xsl:template match="*" mode="followingValues">
33
    <xsl:text> </xsl:text>
34
    <xsl:value-of select="."/>
35
  </xsl:template>
36
  
37
  
20
</xsl:stylesheet>
38
</xsl:stylesheet>
(-)modules/lucene/xslt/index.xsl (+2 lines)
Lines 8-19 Link Here
8
  >
8
  >
9
  
9
  
10
  <xsl:param name="index"/>
10
  <xsl:param name="index"/>
11
  <xsl:param name="pub"/>
11
  <xsl:param name="uuid"/>
12
  <xsl:param name="uuid"/>
12
  <xsl:param name="language"/>
13
  <xsl:param name="language"/>
13
  
14
  
14
  <xsl:template match="/lucene:index">
15
  <xsl:template match="/lucene:index">
15
    <lucene:index indexid="{$index}" lucene:clear="false" lucene:merge-factor="100" lucene:analyzer="stopword_{$language}">
16
    <lucene:index indexid="{$index}" lucene:clear="false" lucene:merge-factor="100" lucene:analyzer="stopword_{$language}">
16
      <lucene:document uid="{$uuid}:{$language}">
17
      <lucene:document uid="{$uuid}:{$language}">
18
        <lucene:field name="publication"><xsl:value-of select="$pub"/></lucene:field>
17
        <lucene:field name="uuid"><xsl:value-of select="$uuid"/></lucene:field>
19
        <lucene:field name="uuid"><xsl:value-of select="$uuid"/></lucene:field>
18
        <lucene:field name="language"><xsl:value-of select="$language"/></lucene:field>
20
        <lucene:field name="language"><xsl:value-of select="$language"/></lucene:field>
19
        <xsl:apply-templates select="lucene:document/*"/>
21
        <xsl:apply-templates select="lucene:document/*"/>
(-)modules-core/linking/java/src/org/apache/lenya/cms/linking/impl/MetaDataLinkManager.java (+167 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 *
17
 */
18
package org.apache.lenya.cms.linking.impl;
19
20
import java.util.HashSet;
21
import java.util.Set;
22
23
import org.apache.avalon.framework.activity.Disposable;
24
import org.apache.avalon.framework.logger.AbstractLogEnabled;
25
import org.apache.avalon.framework.service.ServiceException;
26
import org.apache.avalon.framework.service.ServiceManager;
27
import org.apache.avalon.framework.service.Serviceable;
28
import org.apache.lenya.cms.linking.Link;
29
import org.apache.lenya.cms.linking.LinkManager;
30
import org.apache.lenya.cms.linking.LinkResolver;
31
import org.apache.lenya.cms.linking.LinkTarget;
32
import org.apache.lenya.cms.metadata.MetaData;
33
import org.apache.lenya.cms.metadata.MetaDataException;
34
import org.apache.lenya.cms.publication.Area;
35
import org.apache.lenya.cms.publication.Document;
36
import org.apache.lenya.cms.publication.DocumentFactory;
37
import org.apache.lenya.cms.publication.Publication;
38
import org.apache.lenya.cms.publication.PublicationException;
39
import org.apache.lenya.cms.search.DocumentSearcher;
40
import org.apache.lenya.cms.search.SearchResult;
41
import org.apache.lenya.cms.search.SearchResults;
42
import org.apache.lenya.cms.search.SearchUtil;
43
import org.apache.lucene.index.Term;
44
import org.apache.lucene.queryParser.QueryParser;
45
import org.apache.lucene.search.Query;
46
import org.apache.lucene.search.WildcardQuery;
47
48
public class MetaDataLinkManager extends AbstractLogEnabled implements LinkManager, Serviceable,
49
        Disposable {
50
51
    protected static final String LINK_NAMESPACE = "http://apache.org/lenya/metadata/link/1.0";
52
    protected static final String ELEMENT_OUTGOING_LINKS = "outgoingLinks";
53
54
    private ServiceManager manager;
55
    private DocumentSearcher searcher;
56
    private LinkResolver linkResolver;
57
58
    public Link[] getLinksFrom(Document source) {
59
        try {
60
            String[] uris = getOutgoingLinkUris(source);
61
            Link[] links = new Link[uris.length];
62
            for (int i = 0; i < links.length; i++) {
63
                links[i] = new Link(uris[i]);
64
            }
65
            return links;
66
        } catch (Exception e) {
67
            throw new RuntimeException(e);
68
        }
69
    }
70
71
    protected String[] getOutgoingLinkUris(Document source) throws MetaDataException {
72
        MetaData meta = source.getMetaData(LINK_NAMESPACE);
73
        return meta.getValues(ELEMENT_OUTGOING_LINKS);
74
    }
75
76
    public Document[] getReferencingDocuments(Document target) {
77
        try {
78
            DocumentSearcher searcher = getSearcher();
79
            String indexId = target.getPublication().getId() + "-" + target.getArea();
80
            //String queryString = getReferencingDocumentsQueryString(searcher, target);
81
            //SearchResults results = searcher.find(indexId, queryString, "");
82
            
83
            String fieldName = SearchUtil.getMetaDataFieldName(LINK_NAMESPACE, ELEMENT_OUTGOING_LINKS);
84
            String value = QueryParser.escape("lenya-document:" + target.getUUID() + "*");
85
            SearchResults results = searcher.find(indexId, value, fieldName);
86
87
            Set matchingDocs = new HashSet();
88
            LinkResolver resolver = getLinkResolver();
89
            for (int d = 0; d < results.getLength(); d++) {
90
                SearchResult result = results.getResult(d);
91
                DocumentFactory factory = target.getFactory();
92
                String areaName = target.getArea();
93
                Document doc = getDocument(result, factory, areaName);
94
                if (doc != null) {
95
                    String[] links = getOutgoingLinkUris(doc);
96
                    for (int l = 0; l < links.length; l++) {
97
                        LinkTarget linkTarget = resolver.resolve(doc, links[l]);
98
                        if (linkTarget.exists() && linkTarget.getDocument() == target) {
99
                            matchingDocs.add(doc);
100
                        }
101
                    }
102
                }
103
            }
104
            return (Document[]) matchingDocs.toArray(new Document[matchingDocs.size()]);
105
        } catch (Exception e) {
106
            throw new RuntimeException(e);
107
        }
108
    }
109
110
    protected Document getDocument(SearchResult result, DocumentFactory factory, String areaName)
111
            throws PublicationException {
112
        Document doc = null;
113
        String uuid = result.getUuid();
114
        String language = result.getLanguage();
115
        String pubId = result.getPublicationId();
116
        if (uuid != null && language != null && pubId != null) {
117
            Publication pub = factory.getPublication(pubId);
118
            Area area = pub.getArea(areaName);
119
            if (area.contains(uuid, language)) {
120
                doc = area.getDocument(uuid, language);
121
            }
122
        }
123
        return doc;
124
    }
125
126
    protected String getReferencingDocumentsQueryString(DocumentSearcher searcher, Document target) {
127
        String fieldName = SearchUtil.getMetaDataFieldName(LINK_NAMESPACE, ELEMENT_OUTGOING_LINKS);
128
        String value = "lenya-document:" + target.getUUID() + "*";
129
        return QueryParser.escape(fieldName) + ":" + QueryParser.escape(value);
130
    }
131
132
    protected DocumentSearcher getSearcher() {
133
        if (this.searcher == null) {
134
            try {
135
                this.searcher = (DocumentSearcher) this.manager.lookup(DocumentSearcher.ROLE);
136
            } catch (ServiceException e) {
137
                throw new RuntimeException(e);
138
            }
139
        }
140
        return this.searcher;
141
    }
142
143
    protected LinkResolver getLinkResolver() {
144
        if (this.linkResolver == null) {
145
            try {
146
                this.linkResolver = (LinkResolver) this.manager.lookup(LinkResolver.ROLE);
147
            } catch (ServiceException e) {
148
                throw new RuntimeException(e);
149
            }
150
        }
151
        return this.linkResolver;
152
    }
153
154
    public void service(ServiceManager manager) throws ServiceException {
155
        this.manager = manager;
156
    }
157
158
    public void dispose() {
159
        if (this.searcher != null) {
160
            this.manager.release(searcher);
161
        }
162
        if (this.linkResolver == null) {
163
            this.manager.release(this.linkResolver);
164
        }
165
    }
166
167
}
(-)modules-core/linking/config/cocoon-xconf/linkmanager.xconf (-1 / +1 lines)
Lines 21-26 Link Here
21
<xconf xpath="/cocoon" unless="/cocoon/component[@role = 'org.apache.lenya.cms.linking.LinkManager']">
21
<xconf xpath="/cocoon" unless="/cocoon/component[@role = 'org.apache.lenya.cms.linking.LinkManager']">
22
22
23
  <component logger="" role="org.apache.lenya.cms.linking.LinkManager"
23
  <component logger="" role="org.apache.lenya.cms.linking.LinkManager"
24
    class="org.apache.lenya.cms.linking.ContentLinkManager"/>
24
    class="org.apache.lenya.cms.linking.impl.MetaDataLinkManager"/>
25
  
25
  
26
</xconf>
26
</xconf>
(-)modules-core/linking/config/cocoon-xconf/link-metadata.xconf (+31 lines)
Line 0 Link Here
1
<?xml version="1.0"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
10
  http://www.apache.org/licenses/LICENSE-2.0
11
  
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
19
<!-- $Id: usecases-workflow-deactivate.xconf 348547 2005-11-23 20:13:01Z chestnut $ -->
20
<!--
21
  This file defines the publication specific use-cases
22
-->
23
24
  <xconf xpath="/cocoon/meta-data"
25
    unless="/cocoon/meta-data/component-instance[@name = 'http://apache.org/lenya/metadata/link/1.0']">
26
    <component-instance name="http://apache.org/lenya/metadata/link/1.0"
27
      class="org.apache.lenya.cms.metadata.ConfigurableElementSet">
28
      <element name="outgoingLinks" multiple="true"/>
29
    </component-instance>
30
  </xconf>
31
  

Return to bug 44990