WikiIndexer.java
/*
* Copyright (c) 2002-2023, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.wiki.search;
import fr.paris.lutece.plugins.wiki.business.Topic;
import fr.paris.lutece.plugins.wiki.business.TopicHome;
import fr.paris.lutece.plugins.wiki.business.TopicVersion;
import fr.paris.lutece.plugins.wiki.business.TopicVersionHome;
import fr.paris.lutece.plugins.wiki.service.WikiLocaleService;
import fr.paris.lutece.plugins.wiki.service.parser.LuteceWikiParser;
import fr.paris.lutece.plugins.wiki.web.Constants;
import fr.paris.lutece.portal.service.content.XPageAppService;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.search.IndexationService;
import fr.paris.lutece.portal.service.search.SearchIndexer;
import fr.paris.lutece.portal.service.search.SearchItem;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.url.UrlItem;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
/**
* Wiki Indexer
*
*/
public class WikiIndexer implements SearchIndexer
{
private static final String PROPERTY_INDEXER_NAME = "wiki.indexer.name";
private static final String SHORT_NAME_TOPIC = "wis";
private static final String PARAMETER_PAGE_NAME = "page_name";
private static final String PLUGIN_NAME = "wiki";
private static final String PROPERTY_PAGE_BASE_URL = "search.pageIndexer.baseUrl";
private static final String PROPERTY_DOCUMENT_TYPE = "wiki.indexer.documentType";
private static final String PROPERTY_INDEXER_DESCRIPTION = "wiki.indexer.description";
private static final String PROPERTY_INDEXER_VERSION = "wiki.indexer.version";
private static final String PROPERTY_INDEXER_ENABLE = "wiki.indexer.enable";
private static final String ENABLE_VALUE_TRUE = "1";
private static final String JSP_SEARCH_WIKI = "jsp/site/Portal.jsp?page=wiki&action=search";
/**
* {@inheritDoc }
*/
@Override
public String getDescription( )
{
return AppPropertiesService.getProperty( PROPERTY_INDEXER_DESCRIPTION );
}
/**
* {@inheritDoc }
*/
@Override
public List<Document> getDocuments( String strDocument ) throws IOException, InterruptedException, SiteMessageException
{
List<Document> listDocs = new ArrayList<>( );
String strPortalUrl = AppPropertiesService.getProperty( PROPERTY_PAGE_BASE_URL );
Topic topic = TopicHome.findByPrimaryKey( Integer.parseInt( strDocument ) );
if ( topic != null )
{
UrlItem urlSubject = new UrlItem( strPortalUrl );
urlSubject.addParameter( XPageAppService.PARAM_XPAGE_APP, PLUGIN_NAME );
urlSubject.addParameter( Constants.PARAMETER_VIEW, Constants.VIEW_PAGE );
urlSubject.addParameter( Constants.PARAMETER_PAGE_NAME, topic.getPageName( ) );
for ( String strLanguage : WikiLocaleService.getLanguages( ) )
{
Document docSubject = getDocument( topic, urlSubject.getUrl( ), strLanguage );
listDocs.add( docSubject );
}
}
return listDocs;
}
/**
* {@inheritDoc }
*/
@Override
public String getName( )
{
return AppPropertiesService.getProperty( PROPERTY_INDEXER_NAME );
}
/**
* {@inheritDoc }
*/
@Override
public String getVersion( )
{
return AppPropertiesService.getProperty( PROPERTY_INDEXER_VERSION );
}
/**
* {@inheritDoc }
*/
@Override
public void indexDocuments( ) throws IOException, InterruptedException, SiteMessageException
{
for ( Topic topic : TopicHome.getTopicsList( ) )
{
for ( String strLanguage : WikiLocaleService.getLanguages( ) )
{
indexTopic( topic, strLanguage );
}
}
}
/**
* {@inheritDoc }
*/
@Override
public boolean isEnable( )
{
boolean bReturn = false;
String strEnable = AppPropertiesService.getProperty( PROPERTY_INDEXER_ENABLE );
if ( ( strEnable != null ) && ( strEnable.equalsIgnoreCase( Boolean.TRUE.toString( ) ) || strEnable.equals( ENABLE_VALUE_TRUE ) )
&& PluginService.isPluginEnable( PLUGIN_NAME ) )
{
bReturn = true;
}
return bReturn;
}
/**
* Indexe the topic
*
* @param topic
* The topic
* @param strLanguage
* The language
* @throws IOException
* if an IO error occurs
* @throws InterruptedException
* if a Thread error occurs
*/
public void indexTopic( Topic topic, String strLanguage ) throws IOException, InterruptedException
{
String strPortalUrl = AppPropertiesService.getProperty( PROPERTY_PAGE_BASE_URL );
UrlItem urlSubject = new UrlItem( strPortalUrl );
urlSubject.addParameter( XPageAppService.PARAM_XPAGE_APP, PLUGIN_NAME );
urlSubject.addParameter( Constants.PARAMETER_VIEW, Constants.VIEW_PAGE );
urlSubject.addParameter( PARAMETER_PAGE_NAME, topic.getPageName( ) );
Document docTopic = null;
try
{
docTopic = getDocument( topic, urlSubject.getUrl( ), strLanguage );
}
catch( Exception e )
{
String strMessage = "Topic ID : " + topic.getIdTopic( );
IndexationService.error( this, e, strMessage );
}
if ( docTopic != null )
{
IndexationService.write( docTopic );
}
}
/**
* Get a document for indexing
*
* @param topic
* The topic
* @param strUrl
* The URL
* @param strLanguage
* The language
* @return The document
* @throws IOException
* if an IO error occurs
* @throws InterruptedException
* if a Thread error occurs
*/
public static Document getDocument( Topic topic, String strUrl, String strLanguage ) throws IOException, InterruptedException
{
// make a new, empty document
Document doc = new Document( );
FieldType fieldType = new FieldType( StringField.TYPE_STORED );
fieldType.setOmitNorms( false );
FieldType ftNotStored = new FieldType( StringField.TYPE_NOT_STORED );
ftNotStored.setOmitNorms( false );
ftNotStored.setTokenized( false );
// Add the url as a field named "url". Use an UnIndexed field, so
// that the url is just stored with the question/answer, but is not
// searchable.
doc.add( new Field( SearchItem.FIELD_URL, strUrl, fieldType ) );
// Add the uid as a field, so that index can be incrementally
// maintained.
// This field is not stored with question/answer, it is indexed, but it
// is not
// tokenized prior to indexing.
String strIdSubject = String.valueOf( topic.getPageName( ) );
doc.add( new Field( SearchItem.FIELD_UID, strIdSubject + "_" + SHORT_NAME_TOPIC, ftNotStored ) );
TopicVersion latestTopicVersion = TopicVersionHome.findLastVersion( topic.getIdTopic( ) );
String strWikiContent = latestTopicVersion.getWikiContent( strLanguage ).getWikiContent( );
String strWikiResult = "";
if ( latestTopicVersion.getWikiContent( strLanguage ).getHtmlWikiContent() != null ) {
strWikiResult = latestTopicVersion.getWikiContent( strLanguage ).getHtmlWikiContent( );
} else
{
strWikiResult = new LuteceWikiParser( strWikiContent, topic.getPageName( ), null, strLanguage ).toString( );
}
doc.add( new Field( SearchItem.FIELD_CONTENTS, strWikiResult, TextField.TYPE_NOT_STORED ) );
String strDate = DateTools.dateToString( latestTopicVersion.getDateEdition( ), DateTools.Resolution.DAY );
doc.add( new Field( SearchItem.FIELD_DATE, strDate, fieldType ) );
// Add the subject name as a separate Text field, so that it can be
// searched separately.
doc.add( new Field( SearchItem.FIELD_TITLE, latestTopicVersion.getWikiContent( strLanguage ).getPageTitle( ), fieldType ) );
doc.add( new Field( SearchItem.FIELD_TYPE, getDocumentType( ), fieldType ) );
doc.add( new Field( SearchItem.FIELD_ROLE, topic.getViewRole( ), fieldType ) );
doc.add( new Field( SearchItem.FIELD_METADATA, strLanguage, fieldType ) );
return doc;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getListType( )
{
List<String> listType = new ArrayList<>( );
listType.add( getDocumentType( ) );
return listType;
}
/**
* {@inheritDoc}
*/
@Override
public String getSpecificSearchAppUrl( )
{
return JSP_SEARCH_WIKI;
}
/**
* Get Lucene index document type
*
* @return The document type
*/
public static String getDocumentType( )
{
return AppPropertiesService.getProperty( PROPERTY_DOCUMENT_TYPE );
}
}