View Javadoc
1   package fr.ifremer.quadrige3.synchro.server.pages.admin;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: Quadrige3 Synchro server
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   *
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.quadrige3.core.vo.administration.program.ProgramVO;
27  import fr.ifremer.quadrige3.synchro.server.application.WebSession;
28  import fr.ifremer.quadrige3.synchro.server.pages.BasePage;
29  import fr.ifremer.quadrige3.synchro.server.service.ServiceLocator;
30  import fr.ifremer.quadrige3.synchro.server.service.synchro.ReferentialSynchroService;
31  import org.apache.commons.collections4.SetUtils;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.wicket.ajax.AjaxRequestTarget;
35  import org.apache.wicket.ajax.attributes.AjaxCallListener;
36  import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
37  import org.apache.wicket.ajax.markup.html.form.AjaxButton;
38  import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
39  import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
40  import org.apache.wicket.extensions.markup.html.form.palette.Palette;
41  import org.apache.wicket.extensions.markup.html.form.palette.theme.DefaultTheme;
42  import org.apache.wicket.markup.html.basic.Label;
43  import org.apache.wicket.markup.html.form.ChoiceRenderer;
44  import org.apache.wicket.markup.html.form.Form;
45  import org.apache.wicket.model.CompoundPropertyModel;
46  import org.apache.wicket.model.IModel;
47  import org.apache.wicket.model.LoadableDetachableModel;
48  import org.apache.wicket.model.StringResourceModel;
49  import org.apache.wicket.request.mapper.parameter.PageParameters;
50  
51  import java.util.*;
52  import java.util.stream.Collectors;
53  
54  @AuthorizeInstantiation("ROLE_ADMIN")
55  public class ConfigPage extends BasePage {
56      private static final long serialVersionUID = 1L;
57      private static final Log log = LogFactory.getLog(ConfigPage.class);
58  
59      private boolean confirmPanelVisible = false;
60  
61      private Set<String> synchroProgramCodeIncludes;
62  
63      private List<ProgramVO> availablePrograms;
64      private List<ProgramVO> selectedPrograms;
65  
66      public ConfigPage(final PageParameters parameters) {
67          super(parameters);
68  
69          // Read configuration
70          loadConfig();
71  
72          IModel<ConfigPage> model = new CompoundPropertyModel<>(this);
73          Form<ConfigPage> form = new Form<>("form", model);
74          form.setOutputMarkupId(true);
75          add(form);
76  
77          Palette<ProgramVO> programsPalette = new Palette<>("programsPalette",
78                  new LoadableDetachableModel<List<ProgramVO>>() {
79                      @Override
80                      protected List<ProgramVO> load() {
81                          return selectedPrograms;
82                      }
83                  },
84                  new LoadableDetachableModel<List<ProgramVO>>() {
85                      @Override
86                      protected List<ProgramVO> load() {
87                          return availablePrograms;
88                      }
89                  },
90                  new ChoiceRenderer<>("progCd", "progCd"),
91                  10,
92                  false,
93                  false
94          );
95          programsPalette.add(new DefaultTheme());
96          form.add(programsPalette);
97  
98          // confirmation panel components
99          Label confirmMessage = new Label("confirmMessage", new LoadableDetachableModel<String>() {
100             @Override
101             protected String load() {
102                 return getConfirmationMessage();
103             }
104         }) {
105             @Override
106             public boolean isVisible() {
107                 return confirmPanelVisible;
108             }
109         };
110         confirmMessage.setOutputMarkupPlaceholderTag(true);
111         confirmMessage.setEscapeModelStrings(false);
112         form.add(confirmMessage);
113 
114         AjaxButton cancelButton = new AjaxButton("cancelButton", form) {
115             @Override
116             public boolean isVisible() {
117                 return confirmPanelVisible;
118             }
119 
120             @Override
121             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
122                 confirmPanelVisible = false;
123                 loadConfig();
124                 target.add(ConfigPage.this);
125             }
126         };
127         cancelButton.setOutputMarkupPlaceholderTag(true);
128         form.add(cancelButton);
129 
130         AjaxButton confirmButton = new IndicatingAjaxButton("confirmButton", form) {
131             @Override
132             public boolean isVisible() {
133                 return confirmPanelVisible;
134             }
135 
136             @Override
137             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
138                 confirmPanelVisible = false;
139                 saveConfig();
140                 target.add(ConfigPage.this);
141             }
142 
143             @Override
144             protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
145                 super.updateAjaxAttributes(attributes);
146                 attributes.getAjaxCallListeners().add(new AjaxCallListener()
147                         .onBefore(String.format("$('#%s').hide();$('#%s').hide()", getMarkupId(), cancelButton.getMarkupId())));
148             }
149         };
150         confirmButton.setOutputMarkupPlaceholderTag(true);
151         form.add(confirmButton);
152 
153         // apply button
154         AjaxButton applyButton = new IndicatingAjaxButton("applyButton", form) {
155             private static final long serialVersionUID = 1L;
156 
157             @Override
158             public boolean isVisible() {
159                 return !confirmPanelVisible;
160             }
161 
162             @Override
163             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
164                 ConfigPage.this.getWebSession().getFeedbackMessages().clear();
165                 if (getConfirmationMessage().isEmpty()) {
166                     saveConfig();
167                 } else {
168                     confirmPanelVisible = true;
169                 }
170                 target.add(ConfigPage.this);
171             }
172 
173             @Override
174             protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
175                 super.updateAjaxAttributes(attributes);
176                 attributes.getAjaxCallListeners().add(new AjaxCallListener()
177                     .onBefore(String.format("$('#%s').hide()", getMarkupId())));
178             }
179         };
180         applyButton.setOutputMarkupPlaceholderTag(true);
181         form.add(applyButton);
182     }
183 
184     private String getConfirmationMessage() {
185 
186         StringBuilder message = new StringBuilder();
187 
188         Set<String> programCodesToRemove = getProgramCodesToRemove();
189         if (!programCodesToRemove.isEmpty()) {
190             String messageRemove = new StringResourceModel("config.confirmRemove").getString();
191             message.append(messageRemove).append(listify(programCodesToRemove));
192         }
193 
194         return message.toString();
195     }
196 
197     private Set<String> getProgramCodesToRemove() {
198 
199         Set<String> programCodesToRemove = new HashSet<>(synchroProgramCodeIncludes);
200         programCodesToRemove.removeAll(selectedPrograms.stream().map(ProgramVO::getProgCd).collect(Collectors.toSet()));
201         return programCodesToRemove;
202     }
203 
204     private String listify(Collection<String> strings) {
205         StringJoiner joiner = new StringJoiner("</li><li>", "<ul><li>", "</li></ul>");
206         strings.forEach(joiner::add);
207         return joiner.toString();
208     }
209 
210     private void saveConfig() {
211 
212         // collect new program codes
213         Set<String> selectedProgramCodes = selectedPrograms.stream()
214                 .map(ProgramVO::getProgCd).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
215 
216         // don't save if no change
217         if (SetUtils.isEqualSet(synchroProgramCodeIncludes, selectedProgramCodes)) {
218             return;
219         }
220 
221         try {
222             // update UPDATE_DT on program
223             ServiceLocator.instance().getProgramService().saveUpdateDate(selectedProgramCodes);
224             // clear cache
225             ServiceLocator.instance().getCacheService().clearCache(ReferentialSynchroService.REFERENTIAL_UPDATE_DATE_CACHE);
226 
227             // save to config
228             getConfiguration().setSynchroProgramCodeIncludes(selectedProgramCodes);
229             // save file
230             getConfiguration().save();
231 
232             // Reload metadata in ReferentialSynchroService
233             ServiceLocator.instance().getReferentialSynchroService().updateMetadata();
234 
235             // log
236             String message = new StringResourceModel("config.saved", ConfigPage.this, null).getString();
237             info(message);
238             log.info(message);
239             log.info(String.format("%s : %s", new StringResourceModel("config.compatiblePrograms", ConfigPage.this, null).getString(), selectedProgramCodes));
240 
241         } catch (Exception e) {
242             log.error(e.getLocalizedMessage(), e);
243             info(e.getLocalizedMessage());
244         }
245 
246         // reload
247         loadConfig();
248 
249     }
250 
251     private void loadConfig() {
252         availablePrograms = ServiceLocator.instance().getProgramService().getAll();
253         availablePrograms.sort(Comparator.comparing(ProgramVO::getProgCd));
254         synchroProgramCodeIncludes = getConfiguration().getSynchroProgramCodeIncludes();
255         selectedPrograms = synchroProgramCodeIncludes.stream().sorted()
256                 .map(s -> new ProgramVO(s, null, null))
257                 .collect(Collectors.toList());
258     }
259 
260     /* -- internal methods -- */
261 
262     protected IModel<String> getTitleModel() {
263         return new StringResourceModel("config.title", this, null);
264     }
265 
266     protected WebSession getWebSession() {
267         return (WebSession) getSession();
268     }
269 
270 }