NotifyUsersRule.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.document.modules.rulenotifyusers.business;

import fr.paris.lutece.plugins.document.business.rules.AbstractRule;
import fr.paris.lutece.plugins.document.business.workflow.DocumentState;
import fr.paris.lutece.plugins.document.business.workflow.DocumentStateHome;
import fr.paris.lutece.plugins.document.service.DocumentEvent;
import fr.paris.lutece.plugins.document.service.DocumentException;
import fr.paris.lutece.plugins.document.service.spaces.DocumentSpacesService;
import fr.paris.lutece.plugins.document.service.spaces.SpaceRemovalListenerService;
import fr.paris.lutece.plugins.document.utils.IntegerUtils;
import fr.paris.lutece.portal.business.mailinglist.MailingList;
import fr.paris.lutece.portal.business.mailinglist.MailingListHome;
import fr.paris.lutece.portal.business.mailinglist.Recipient;
import fr.paris.lutece.portal.business.user.AdminUser;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.mail.MailService;
import fr.paris.lutece.portal.service.mailinglist.AdminMailingListService;
import fr.paris.lutece.portal.service.mailinglist.MailingListRemovalListenerService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.ReferenceItem;
import fr.paris.lutece.util.ReferenceList;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.url.UrlItem;

import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;


/**
 * This class provides a rule to notify users on document events
 */
public class NotifyUsersRule extends AbstractRule
{
    ////////////////////////////////////////////////////////////////////////////
    // Constants
    private static final String REGEX_ID = "^[\\d]+$";
    private static final String NO_MAILING_LIST = "none";
    private static final String TEMPLATE_CREATE_RULE = "/admin/plugins/document/modules/rulenotifyusers/create_rule_notify_users.html";
    private static final String MARK_SPACE_SOURCE_PATH = "path_space_source";
    private static final String MARK_SPACE_SOURCE_ID = "id_space_source";
    private static final String MARK_STATE_ID = "id_state";
    private static final String MARK_MAILINGLIST_ID = "id_mailinglist";

    //    private static final String MARK_MESSAGE_TEMPLATE_KEY = "message_template_key";
    private static final String MARK_STATES_LIST = "states_list";
    private static final String MARK_MAILINGLISTS_LIST = "mailinglists_list";
    private static final String MARK_MESSAGE_TEMPLATES_LIST = "message_templates_list";
    private static final String MARK_USER = "user";
    private static final String MARK_DOCUMENT = "document";
    private static final String MARK_URL_PREVIEW = "url_preview";
    private static final String MARK_BASE_URL = "base_url";
    private static final String PARAMETER_SPACE_SOURCE_ID = "id_space_source";
    private static final String PARAMETER_STATE_ID = "id_state";
    private static final String PARAMETER_MAILINGLIST_ID = "id_mailinglist";
    private static final String PARAMETER_MESSAGE_TEMPLATE_KEY = "message_template_key";

    // defined in rulenotifyusers_messages.properties
    private static final String PROPERTY_RULE_NAME = "module.document.rulenotifyusers.ruleName";
    private static final String PROPERTY_RULE_DESCRIPTION = "module.document.rulenotifyusers.ruleLiteral";
    private static final String PROPERTY_MAIL_SENDER_NAME = "module.document.rulenotifyusers.mailSenderName";
    private static final String PROPERTY_RULE_ERROR_MAILING_LIST_ID = "module.document.rulenotifyusers.message.create_rule_notify_users.errorMailingListId";
    private static final String PROPERTY_CHOOSE_MAILING_LIST = "module.document.rulenotifyusers.create_rule_notify_users.chooseMailingList";
    private static final String PROPERTY_RULE_ERROR_NOT_SELECT_SPACE_SOURCE = "module.document.rulenotifyusers.message.create_rule_notify_users.errorNotSelectSpaceSource";

    // defined in document-rulenotifyusers.properties
    private static final String PROPERTY_MESSAGE_TEMPLATES_ENTRIES = "document-rulenotifyusers.messages";
    private static final String PROPERTY_MESSAGE_PREFIX = "document-rulenotifyusers.message.";
    private static final String PROPERTY_MESSAGE_PREFIX_INTERNATIONALIZATION = "module.document.rulenotifyusers.notify_users_rule.message.";
    private static final String SUFFIX_TEMPLATE = ".template";
    private static final String SUFFIX_SUBJECT = ".subject";
    private static final String SUFFIX_DESCRIPTION = ".description";

    // suffix that allows to get the url used in the mail - defined in document-rulenotifyusers.properties
    private static final String SUFFIX_URL_PREVIEW = "document-rulenotifyusers.previewDocument";
    private static String[] _attributes = 
        {
            PARAMETER_SPACE_SOURCE_ID, PARAMETER_MAILINGLIST_ID, PARAMETER_STATE_ID, PARAMETER_MESSAGE_TEMPLATE_KEY,
        };
    private static NotifyUsersMailingListRemovalListener _listenerMailingList;
    private static NotifyUsersSpaceRemovalListener _listenerSpace;
    private static final String PROPERTY_PROD_BASE_URL = "lutece.prod.url";

    /**
     * Initialize the rule
     */
    public void init(  )
    {
        // Create removal listeners and register them
        if ( _listenerMailingList == null )
        {
            _listenerMailingList = new NotifyUsersMailingListRemovalListener(  );
            MailingListRemovalListenerService.getService(  ).registerListener( _listenerMailingList );
        }

        if ( _listenerSpace == null )
        {
            _listenerSpace = new NotifyUsersSpaceRemovalListener(  );
            SpaceRemovalListenerService.getService(  ).registerListener( _listenerSpace );
        }
    }

    /**
     * Gets the Rule name key
     * @return The Rule name key
     */
    public String getNameKey(  )
    {
        return PROPERTY_RULE_NAME;
    }

    /**
     * Execute the rule
     * @param event The document event
     * @throws DocumentException raise when error occurs in event or rule
     */
    public void apply( DocumentEvent event ) throws DocumentException
    {
        try
        {
            int nSourceSpace = Integer.parseInt( getAttribute( PARAMETER_SPACE_SOURCE_ID ) );
            int nState = Integer.parseInt( getAttribute( PARAMETER_STATE_ID ) );
            int nMailingListId = Integer.parseInt( getAttribute( PARAMETER_MAILINGLIST_ID ) );

            UrlItem url = AppPathService.buildRedirectUrlItem( "", SUFFIX_URL_PREVIEW );

            if ( event.getStateId(  ) == nState )
            {
                if ( event.getSpaceId(  ) == nSourceSpace )
                {
                    Collection<Recipient> listRecipients = AdminMailingListService.getRecipients( nMailingListId );

                    for ( Recipient recipient : listRecipients )
                    {
                        // Build the mail message
                        Map<String, Object> model = new HashMap<String, Object>(  );
                        model.put( MARK_USER, event.getUser(  ) );
                        model.put( MARK_DOCUMENT, event.getDocument(  ) );
                        model.put( MARK_URL_PREVIEW, url.getUrl(  ) );
                        model.put( MARK_BASE_URL, AppPropertiesService.getProperty( PROPERTY_PROD_BASE_URL ) );

                        String strMessageTemplate = getMessageTemplate( getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) );
                        String strSubject = getMessageSubject( getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ),
                                event.getUser(  ).getLocale(  ) );
                        HtmlTemplate t = AppTemplateService.getTemplate( strMessageTemplate,
                                event.getUser(  ).getLocale(  ), model );

                        // Send Mail
                        String strSenderName = I18nService.getLocalizedString( PROPERTY_MAIL_SENDER_NAME,
                                event.getUser(  ).getLocale(  ) );
                        String strSenderEmail = MailService.getNoReplyEmail(  );

                        MailService.sendMailHtml( recipient.getEmail(  ), strSenderName, strSenderEmail, strSubject,
                            t.getHtml(  ) );
                    }
                }
            }
        }
        catch ( Exception e )
        {
            AppLogService.error( "Error in NotifyUserRule event : " + e.getMessage(  ), e );
        }
    }

    /**
     * Gets the Rule create form
     * @param user The current user using the form
     * @param locale The current locale
     * @return The HTML form
     */
    public String getCreateForm( AdminUser user, Locale locale )
    {
        Map<String, Object> model = new HashMap<String, Object>(  );

        Collection<ReferenceItem> listStates = DocumentStateHome.getDocumentStatesList( locale );
        ReferenceList listMailingLists = new ReferenceList(  );

        if ( this.getAttribute( PARAMETER_STATE_ID ) != null )
        {
            model.put( MARK_STATE_ID, this.getAttribute( PARAMETER_STATE_ID ) );
        }

        if ( this.getAttribute( PARAMETER_MAILINGLIST_ID ) != null )
        {
            model.put( MARK_MAILINGLIST_ID, this.getAttribute( PARAMETER_MAILINGLIST_ID ) );
        }

        if ( this.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) != null )
        {
            model.put( PARAMETER_MESSAGE_TEMPLATE_KEY, this.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) );
        }

        if ( this.getAttribute( PARAMETER_SPACE_SOURCE_ID ) != null )
        {
            int nIdSpaceSource = -1;
            String strPathSpaceSource;

            try
            {
                nIdSpaceSource = Integer.parseInt( this.getAttribute( PARAMETER_SPACE_SOURCE_ID ) );
                model.put( MARK_SPACE_SOURCE_ID, nIdSpaceSource );
            }
            catch ( NumberFormatException ne )
            {
                AppLogService.error( ne );
            }

            strPathSpaceSource = DocumentSpacesService.getInstance(  ).getLabelSpacePath( nIdSpaceSource, user );
            model.put( MARK_SPACE_SOURCE_PATH, strPathSpaceSource );
        }

        listMailingLists.addItem( NO_MAILING_LIST,
            I18nService.getLocalizedString( PROPERTY_CHOOSE_MAILING_LIST, locale ) );
        listMailingLists.addAll( AdminMailingListService.getMailingLists( user ) );

        model.put( MARK_STATES_LIST, listStates );
        model.put( MARK_MAILINGLISTS_LIST, listMailingLists );
        model.put( MARK_MESSAGE_TEMPLATES_LIST, getMessageTemplatesList( locale ) );

        HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_CREATE_RULE, locale, model );

        return template.getHtml(  );
    }

    /**
     * Check the rule
     *
     * @return null if rule is valid, message if rule not valid
     */
    public String validateRule(  )
    {
        String strMailingListId = getAttribute( PARAMETER_MAILINGLIST_ID );
        String strSourceSpaceId = getAttribute( PARAMETER_SPACE_SOURCE_ID );

        if ( strSourceSpaceId == null )
        {
            return PROPERTY_RULE_ERROR_NOT_SELECT_SPACE_SOURCE;
        }

        if ( ( strMailingListId == null ) || !strMailingListId.matches( REGEX_ID ) )
        {
            return PROPERTY_RULE_ERROR_MAILING_LIST_ID;
        }

        return null;
    }

    /**
     * true if the user is authorized to view the rule
     * @param user the current user
     * @return true if the user is authorized to view the rule
     */
    public boolean isAuthorized( AdminUser user )
    {
        int nSourceSpaceId = IntegerUtils.convert( getAttribute( PARAMETER_SPACE_SOURCE_ID ) );

        if ( !DocumentSpacesService.getInstance(  ).isAuthorizedViewByWorkgroup( nSourceSpaceId, user ) )
        {
            return false;
        }

        return true;
    }

    /**
     * Gets all attributes of the rule
     * @return attributes of the rule
     */
    public String[] getAttributesList(  )
    {
        return _attributes;
    }

    /**
     * Gets the explicit text of the rule
     * @return The text of the rule
     */
    public String getRule(  )
    {
        int nSourceSpaceId = IntegerUtils.convert( getAttribute( PARAMETER_SPACE_SOURCE_ID ) );
        String strSourceSpace = DocumentSpacesService.getInstance(  ).getLabelSpacePath( nSourceSpaceId, getUser(  ) );
        String strMailingListId = getAttribute( PARAMETER_MAILINGLIST_ID );
        String strMailingList = null;

        if ( StringUtils.isNotBlank( strMailingListId ) && strMailingListId.matches( REGEX_ID ) )
        {
            int nMailingListId = IntegerUtils.convert( strMailingListId );
            MailingList mailinglist = MailingListHome.findByPrimaryKey( nMailingListId );

            if ( mailinglist != null )
            {
                strMailingList = mailinglist.getDescription(  );
            }
        }

        String strMessageTemplate = getMessageDescription( getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ), getLocale(  ) );
        int nStateId = IntegerUtils.convert( getAttribute( PARAMETER_STATE_ID ) );
        DocumentState state = DocumentStateHome.findByPrimaryKey( nStateId );
        String strState = StringUtils.EMPTY;

        if ( state != null )
        {
            state.setLocale( getLocale(  ) );
            strState = state.getName(  );
        }

        String[] ruleArgs = { strSourceSpace, strState, strMailingList, strMessageTemplate };

        return I18nService.getLocalizedString( PROPERTY_RULE_DESCRIPTION, ruleArgs, getLocale(  ) );
    }

    /**
     * Get the parameter key for the mailing list Id
     * @return The parameter key
     */
    public static String getParameterKeyMailingListId(  )
    {
        return PARAMETER_MAILINGLIST_ID;
    }

    /**
     * Get the parameter key for the source space Id
     * @return The parameter key
     */
    public static String getParameterKeySourceSpaceId(  )
    {
        return PARAMETER_SPACE_SOURCE_ID;
    }

    /**
     * Get the parameter key for the state Id
     * @return The parameter key
     */
    public static String getParameterKeyStateId(  )
    {
        return PARAMETER_STATE_ID;
    }

    /**
     * Gets the list of message templates
     *
     * @param locale The current locale
     * @return A ReferenceList containing all available messages templates
     */
    private ReferenceList getMessageTemplatesList( Locale locale )
    {
        ReferenceList listTemplates = new ReferenceList(  );

        String strEntries = AppPropertiesService.getProperty( PROPERTY_MESSAGE_TEMPLATES_ENTRIES );

        // extracts each item (separated by a comma) from the list
        StringTokenizer strTokens = new StringTokenizer( strEntries, "," );

        while ( strTokens.hasMoreTokens(  ) )
        {
            String strMessageKey = strTokens.nextToken(  );
            String strTemplateDescription = getMessageDescription( strMessageKey, locale );
            listTemplates.addItem( strMessageKey, strTemplateDescription );
        }

        return listTemplates;
    }

    /**
     * Gets the message description from the
     * @param strMessageKey The message key
     * @param locale The current locale
     * @return The message description
     */
    private String getMessageDescription( String strMessageKey, Locale locale )
    {
        return I18nService.getLocalizedString( PROPERTY_MESSAGE_PREFIX_INTERNATIONALIZATION + strMessageKey +
            SUFFIX_DESCRIPTION, locale );
    }

    /**
     * Gets the message subject from the
     * @param strMessageKey The message key
     * @param locale The current locale
     * @return The message subject
     */
    private String getMessageSubject( String strMessageKey, Locale locale )
    {
        return I18nService.getLocalizedString( PROPERTY_MESSAGE_PREFIX_INTERNATIONALIZATION + strMessageKey +
            SUFFIX_SUBJECT, locale );
    }

    /**
     * Gets the message template from the
     * @param strMessageKey The message key
     * @return The message template
     */
    private String getMessageTemplate( String strMessageKey )
    {
        return AppPropertiesService.getProperty( PROPERTY_MESSAGE_PREFIX + strMessageKey + SUFFIX_TEMPLATE );
    }

    @Override
    public boolean equals( Object obj )
    {
        if ( obj == null )
        {
            return false;
        }

        if ( obj instanceof NotifyUsersRule )
        {
            NotifyUsersRule rule = (NotifyUsersRule) obj;

            if ( ( this.getAttribute( PARAMETER_SPACE_SOURCE_ID ) == null ) ||
                    ( this.getAttribute( PARAMETER_MAILINGLIST_ID ) == null ) ||
                    ( this.getAttribute( PARAMETER_STATE_ID ) == null ) ||
                    ( this.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) == null ) ||
                    ( rule.getAttribute( PARAMETER_SPACE_SOURCE_ID ) == null ) ||
                    ( rule.getAttribute( PARAMETER_MAILINGLIST_ID ) == null ) ||
                    ( rule.getAttribute( PARAMETER_STATE_ID ) == null ) ||
                    ( rule.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) == null ) )
            {
                return false;
            }

            if ( this.getAttribute( PARAMETER_SPACE_SOURCE_ID ).equals( rule.getAttribute( PARAMETER_SPACE_SOURCE_ID ) ) &&
                    this.getAttribute( PARAMETER_MAILINGLIST_ID ).equals( rule.getAttribute( PARAMETER_MAILINGLIST_ID ) ) &&
                    this.getAttribute( PARAMETER_STATE_ID ).equals( rule.getAttribute( PARAMETER_STATE_ID ) ) &&
                    this.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY )
                            .equals( rule.getAttribute( PARAMETER_MESSAGE_TEMPLATE_KEY ) ) )
            {
                return true;
            }
        }

        return false;
    }
}