View Javadoc
1   /*
2    * Copyright (c) 2002-2020, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.participatoryideation.service;
35  
36  import java.sql.Timestamp;
37  import java.text.Normalizer;
38  import java.util.Collection;
39  import java.util.Date;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Locale;
43  import java.util.Map;
44  
45  import javax.servlet.http.HttpServletRequest;
46  
47  import org.apache.commons.lang.StringUtils;
48  
49  import fr.paris.lutece.plugins.extend.business.extender.history.ResourceExtenderHistory;
50  import fr.paris.lutece.plugins.extend.modules.comment.business.Comment;
51  import fr.paris.lutece.plugins.extend.modules.comment.service.CommentService;
52  import fr.paris.lutece.plugins.extend.modules.comment.service.ICommentService;
53  import fr.paris.lutece.plugins.extend.modules.comment.service.extender.CommentResourceExtender;
54  import fr.paris.lutece.plugins.extend.service.extender.history.IResourceExtenderHistoryService;
55  import fr.paris.lutece.plugins.extend.service.extender.history.ResourceExtenderHistoryService;
56  import fr.paris.lutece.plugins.participatoryideation.business.proposal.Proposal;
57  import fr.paris.lutece.plugins.participatoryideation.business.proposal.ProposalHome;
58  import fr.paris.lutece.plugins.participatoryideation.business.proposal.ProposalSearcher;
59  import fr.paris.lutece.plugins.participatoryideation.util.ParticipatoryIdeationConstants;
60  import fr.paris.lutece.plugins.workflowcore.business.action.Action;
61  import fr.paris.lutece.plugins.workflowcore.service.workflow.IWorkflowService;
62  import fr.paris.lutece.portal.service.datastore.DatastoreService;
63  import fr.paris.lutece.portal.service.i18n.I18nService;
64  import fr.paris.lutece.portal.service.spring.SpringContextService;
65  import fr.paris.lutece.portal.service.template.AppTemplateService;
66  import fr.paris.lutece.portal.service.util.AppLogService;
67  import fr.paris.lutece.portal.service.util.AppPropertiesService;
68  import fr.paris.lutece.portal.service.workflow.WorkflowService;
69  import fr.paris.lutece.util.html.HtmlTemplate;
70  
71  public class ProposalWSService implements IProposalWSService
72  {
73  
74      private static final String BEAN_PROPOSAL_WEB_SERVICE_SERVICE = "participatoryideation.proposalWSService";
75      private static IProposalWSService _singleton;
76      private static SolrProposalIndexer _solrProposalIndexer;
77      private static final String BEAN_SOLR_PROPOSAL_INDEXER = "participatoryideation.solrProposalIndexer";
78      private static ICommentService _commentService;
79      private static IResourceExtenderHistoryService _resourceHistoryService;
80  
81      // properties
82      private static final String PROPERTY_POLITENESS_COMMENTS_PROJECTS_NOT_SELECTED = "eudonetbp.site_property.mail.politeness_comments_projects_not_selected.htmlblock";
83      // Mark
84      private static final String MARK_COMMENT = "comment";
85  
86      /**
87       * Get the unique instance of the Proposal Web Service Service
88       * 
89       * @return The instance
90       */
91      public static IProposalWSService getInstance( )
92      {
93          if ( _singleton == null )
94          {
95              _solrProposalIndexer = SpringContextService.getBean( BEAN_SOLR_PROPOSAL_INDEXER );
96              _singleton = SpringContextService.getBean( BEAN_PROPOSAL_WEB_SERVICE_SERVICE );
97              _commentService = SpringContextService.getBean( CommentService.BEAN_SERVICE );
98              _resourceHistoryService = SpringContextService.getBean( ResourceExtenderHistoryService.BEAN_SERVICE );
99  
100         }
101         return _singleton;
102 
103     }
104 
105     @Override
106     public Collection<Proposal> getProposalsList( )
107     {
108 
109         return ProposalHome.getProposalsList( );
110 
111     }
112 
113     @Override
114     public Proposal getProposalByIdentifiant( int nKey )
115     {
116 
117         return ProposalHome.findByPrimaryKey( nKey );
118 
119     }
120 
121     @Override
122     public Proposal getProposalByIdentifiantAndCampaign( int nKey, String strCampaign )
123     {
124 
125         return ProposalHome.findByCodes( strCampaign, nKey );
126 
127     }
128 
129     @Override
130     public Collection<Proposal> getProposalsListSearch( ProposalSearcher proposalSearcher )
131     {
132 
133         return ProposalHome.getProposalsListSearch( proposalSearcher );
134     }
135 
136     @Override
137     public void updateProposal( Proposal/../../../fr/paris/lutece/plugins/participatoryideation/business/proposal/Proposal.html#Proposal">Proposal proposalLutece, Proposal proposalEudonet, HttpServletRequest request )
138     {
139         updateProposal( proposalLutece, proposalEudonet, true, request );
140     }
141 
142     @Override
143     public void updateProposal( Proposal/../../../fr/paris/lutece/plugins/participatoryideation/business/proposal/Proposal.html#Proposal">Proposal proposalLutece, Proposal proposalEudonet, boolean notify, HttpServletRequest request )
144     {
145 
146         String valStatusLutece = proposalLutece.getStatusPublic( ).getValeur( ); // statut PUBLIC sur LUTECE
147         String valStatusEudonet = proposalEudonet.getStatusEudonet( ).getValeur( ); // statut EUDONET sur LUTECE (si non valué, sera value au statut PUBLIC sur
148         // LUTECE)
149         String valStatusEudonetLutece = valStatusLutece; // statut PUBLIC sur EUDONET
150 
151         if ( proposalLutece.getStatusEudonet( ) != null )
152         {
153             valStatusEudonetLutece = proposalLutece.getStatusEudonet( ).getValeur( );
154         }
155 
156         if ( valStatusLutece.equals( valStatusEudonet )
157                 || ( !valStatusLutece.equals( valStatusEudonet ) && !valStatusLutece.equals( valStatusEudonetLutece )
158                         && !valStatusEudonet.equals( valStatusEudonetLutece ) )
159                 || ( valStatusLutece.equals( Proposal.Status.STATUS_SUPPRIME_PAR_USAGER.getValeur( ) ) )
160                 || ( valStatusEudonet.equals( Proposal.Status.STATUS_SUPPRIME_PAR_USAGER.getValeur( ) ) ) )
161         {
162             // On ne fait rien
163         }
164         else
165         {
166             ProposalHome.updateBO( proposalEudonet );
167             if ( proposalEudonet.getMotifRecev( ) != null && StringUtils.isNotEmpty( proposalEudonet.getMotifRecev( ) )
168                     && StringUtils.isNotBlank( proposalEudonet.getMotifRecev( ) ) && !( proposalLutece.getMotifRecev( ) != null
169                             && proposalEudonet.getMotifRecev( ) != null && proposalEudonet.getMotifRecev( ).equals( proposalLutece.getMotifRecev( ) ) ) )
170             {
171                 createComment( proposalEudonet );
172             }
173             if ( proposalEudonet.getStatusPublic( ).equals( Proposal.Status.STATUS_SUPPRIME_PAR_MDP )
174                     || proposalEudonet.getStatusPublic( ).equals( Proposal.Status.STATUS_SUPPRIME_PAR_USAGER ) )
175             {
176                 _solrProposalIndexer.removeProposal( proposalEudonet );
177             }
178             if ( WorkflowService.getInstance( ).isAvailable( ) )
179             {
180                 processAction( valStatusEudonet, proposalLutece, notify, request );
181             }
182         }
183     }
184 
185     @Override
186     public Proposal/../../../fr/paris/lutece/plugins/participatoryideation/business/proposal/Proposal.html#Proposal">Proposal updateProposal( Proposal proposal )
187     {
188 
189         proposal = ProposalHome.updateBO( proposal );
190         return proposal;
191     }
192 
193     @Override
194     public void createComment( Proposal proposal )
195     {
196         String strCommentPNS = DatastoreService.getDataValue( PROPERTY_POLITENESS_COMMENTS_PROJECTS_NOT_SELECTED, "" );
197         String strContentCommentPNS = proposal.getMotifRecev( );
198         Map<String, String> model = new HashMap<String, String>( );
199         model.put( MARK_COMMENT, proposal.getMotifRecev( ) );
200         try
201         {
202             HtmlTemplate t = AppTemplateService.getTemplateFromStringFtl( strCommentPNS, new Locale( "fr", "FR" ), model );
203             strContentCommentPNS = t.getHtml( );
204         }
205         catch( Exception e )
206         {
207             // _service.addToLog( "Erreur updateProposal: "+e.getMessage( ));
208             AppLogService.error( "Erreur avec le template freemarker dans les proprietes du site: ", e );
209         }
210 
211         Comment comment = new Comment( );
212         comment.setIdExtendableResource( "" + proposal.getId( ) );
213         comment.setExtendableResourceType( Proposal.PROPERTY_RESOURCE_TYPE );
214         comment.setIdParentComment( 0 );
215         comment.setComment( strContentCommentPNS );
216         Timestamp currentDate = new Timestamp( new Date( ).getTime( ) );
217         comment.setDateComment( currentDate );
218         comment.setDateLastModif( currentDate );
219         comment.setName( "Mairie de Paris" );
220         comment.setEmail( "lutece@lutece.com" );
221         comment.setPublished( true );
222         comment.setIpAddress( "" );
223         comment.setIsAdminComment( true );
224         comment.setIsImportant( true );
225         // comment.setCommentOrder(1);
226         comment.setPinned( true );
227         _commentService.create( comment );
228 
229         ResourceExtenderHistory history = new ResourceExtenderHistory( );
230         history.setExtenderType( CommentResourceExtender.EXTENDER_TYPE_COMMENT );
231         // history.setIdExtendableResource( proposal.getCodeCampaign() + "-" + String.format("%06d", proposal.getCodeProposal()) );
232         history.setIdExtendableResource( "" + proposal.getId( ) );
233         history.setExtendableResourceType( Proposal.PROPERTY_RESOURCE_TYPE );
234         history.setIpAddress( StringUtils.EMPTY );
235         history.setUserGuid( AppPropertiesService.getProperty( ParticipatoryIdeationConstants.PROPERTY_GENERATE_PROPOSAL_LUTECE_USER_NAME ) ); // Le commentaire
236                                                                                                                                                // est déposé par
237         // l'équipe
238         // du Budget Participatif.
239         _resourceHistoryService.create( history );
240     }
241 
242     private void processAction( String proposalStatut, Proposal proposal, boolean notify, HttpServletRequest request )
243     {
244 
245         boolean foundAction = false;
246         int nIdWorkflow = AppPropertiesService.getPropertyInt( ParticipatoryIdeationConstants.PROPERTY_WORKFLOW_ID, -1 );
247         String proposalStatutLibelle = removeAccent(
248                 I18nService.getLocalizedString( ( Proposal.Status.getByValue( proposalStatut ).getLibelle( ) ), new Locale( "fr", "FR" ) ) )
249                 + ( notify ? " (avec notification)" : " (sans notification)" );
250 
251         if ( nIdWorkflow != -1 )
252         {
253             List<Action> actionsList = WorkflowService.getInstance( ).getMassActions( nIdWorkflow );
254             for ( Action action : actionsList )
255             {
256                 String actionLibelle = removeAccent( action.getName( ) );
257                 if ( actionLibelle.equals( proposalStatutLibelle ) )
258                 {
259                     foundAction = true;
260                     IWorkflowService _service = SpringContextService
261                             .getBean( fr.paris.lutece.plugins.workflowcore.service.workflow.WorkflowService.BEAN_SERVICE );
262                     _service.doProcessAction( proposal.getId( ), Proposal.WORKFLOW_RESOURCE_TYPE, action.getId( ), -1, request, new Locale( "fr", "FR" ), false,
263                             null );
264                     // WorkflowService.getInstance( ).doProcessAction(proposal.getId( ), Proposal.WORKFLOW_RESOURCE_TYPE, action.getId( ), -1 , null, new
265                     // Locale( "fr" ,
266                     // "FR" ), false);
267                 }
268             }
269 
270             if ( !foundAction )
271             {
272                 AppLogService.error( "No such action on workflow #" + nIdWorkflow + " : '" + proposalStatutLibelle + "'" );
273             }
274 
275             /*
276              * State state= WorkflowService.getInstance( ).getState( proposal.getId( ), Proposal.WORKFLOW_RESOURCE_TYPE, nIdWorkflow, -1 ); if(state != null){
277              * WorkflowService.getInstance( ).doProcessAutomaticReflexiveActions(proposal.getId( ), Proposal.WORKFLOW_RESOURCE_TYPE, state.getId( ), -1, new
278              * Locale( "fr" , "FR" )); }
279              */
280         }
281     }
282 
283     @Override
284     public void processActionByName( String strWorkflowProposalActionName, int nIdProposal, HttpServletRequest request )
285     {
286 
287         int nIdWorkflow = AppPropertiesService.getPropertyInt( ParticipatoryIdeationConstants.PROPERTY_WORKFLOW_ID, -1 );
288 
289         if ( nIdWorkflow != -1 && WorkflowService.getInstance( ).isAvailable( ) && !StringUtils.isEmpty( strWorkflowProposalActionName ) )
290         {
291 
292             List<Action> actionsList = WorkflowService.getInstance( ).getMassActions( nIdWorkflow );
293 
294             for ( Action action : actionsList )
295             {
296 
297                 if ( action.getName( ).equals( strWorkflowProposalActionName ) )
298                 {
299 
300                     WorkflowService.getInstance( ).doProcessAction( nIdProposal, Proposal.WORKFLOW_RESOURCE_TYPE, action.getId( ), -1, request,
301                             new Locale( "fr", "FR" ), true );
302 
303                 }
304             }
305         }
306 
307     }
308 
309     @Override
310     public void processActionByName( String strWorkflowProposalActionName, int nIdProposal )
311     {
312 
313         int nIdWorkflow = AppPropertiesService.getPropertyInt( ParticipatoryIdeationConstants.PROPERTY_WORKFLOW_ID, -1 );
314 
315         if ( nIdWorkflow != -1 && WorkflowService.getInstance( ).isAvailable( ) && !StringUtils.isEmpty( strWorkflowProposalActionName ) )
316         {
317 
318             List<Action> actionsList = WorkflowService.getInstance( ).getMassActions( nIdWorkflow );
319 
320             for ( Action action : actionsList )
321             {
322 
323                 if ( action.getName( ).equals( strWorkflowProposalActionName ) )
324                 {
325 
326                     WorkflowService.getInstance( ).doProcessAction( nIdProposal, Proposal.WORKFLOW_RESOURCE_TYPE, action.getId( ), -1, null,
327                             new Locale( "fr", "FR" ), true );
328 
329                 }
330             }
331         }
332 
333     }
334 
335     public static String removeAccent( String source )
336     {
337         return Normalizer.normalize( source, Normalizer.Form.NFD ).replaceAll( "[^\\p{ASCII}]", "" );
338     }
339 
340 }