View Javadoc
1   /**
2    * This file Copyright (c) 2020 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.blossom.dialog;
35  
36  import info.magnolia.config.registry.DefinitionProvider;
37  import info.magnolia.config.registry.DefinitionProviderWrapper;
38  import info.magnolia.config.registry.Registry;
39  import info.magnolia.config.registry.decoration.DefinitionDecorator;
40  import info.magnolia.config.registry.decoration.DefinitionDecoratorMetadata;
41  import info.magnolia.objectfactory.Components;
42  import info.magnolia.objectfactory.MgnlInstantiationException;
43  import info.magnolia.objectfactory.NoSuchComponentException;
44  import info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition;
45  import info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition;
46  import info.magnolia.ui.api.app.AppController;
47  import info.magnolia.ui.api.location.Location;
48  import info.magnolia.ui.dialog.DialogDefinition;
49  import info.magnolia.ui.dialog.definition.ConfiguredFormDialogDefinition;
50  
51  import java.util.Optional;
52  import java.util.function.Predicate;
53  
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  /**
58   * Populates a blossom dialogs by invoking methods on the factory object.
59   */
60  public class DialogCreatorDefinitionDecorator implements DefinitionDecorator<DialogDefinition> {
61      private static final Logger log = LoggerFactory.getLogger(DialogCreatorDefinitionDecorator.class);
62      private final BlossomDialogDescription dialogDescription;
63  
64      public DialogCreatorDefinitionDecorator(BlossomDialogDescription dialogDescription) {
65          this.dialogDescription = dialogDescription;
66      }
67  
68      @Override
69      public DefinitionDecoratorMetadata metadata() {
70          return () -> "blossom";
71      }
72  
73      @Override
74      public boolean appliesTo(DefinitionProvider<DialogDefinition> definitionProvider) {
75          if (definitionProvider instanceof BlossomDialogDefinitionProvider
76                  && dialogDescription.getId().equals(definitionProvider.get().getId())) {
77              return new CompatibilityPagesAppPredicate().test(((BlossomDialogDefinitionProvider) definitionProvider));
78          }
79          return false;
80      }
81  
82      @Override
83      public DefinitionProvider<DialogDefinition> decorate(DefinitionProvider<DialogDefinition> definitionProvider) {
84  
85          DialogCreationContext context = Optional.ofNullable(DialogCreationContextHolder.get())
86                  .orElse(new DialogCreationContext());
87  
88          context.setId(dialogDescription.getId());
89  
90          DialogCreator dialogCreator = dialogDescription.getDialogCreator();
91          DialogFactoryMetaData factoryMetaData = dialogDescription.getFactoryMetaData();
92          try {
93              dialogCreator.createDialog(factoryMetaData, context);
94          } catch (Exception e) {
95              String factoryMetaDataInfo = factoryMetaData.getFactoryMethod() != null ?
96                      factoryMetaData.getFactoryMethod().toString() :
97                      factoryMetaData.getFactoryObject().getClass().toString();
98              log.error("Failed to create dialog for {}", factoryMetaDataInfo, e);
99              throw new Registry.InvalidDefinitionException(dialogDescription.getId());
100         }
101 
102         ConfiguredFormDialogDefinition dialog = context.getDialog();
103 
104         if (dialog != null && dialog.getActions().isEmpty()) {
105             SaveDialogActionDefinition callbackAction = new SaveDialogActionDefinition();
106             callbackAction.setName("commit");
107             callbackAction.setLabel("save");
108             dialog.getActions().put(callbackAction.getName(), callbackAction);
109 
110             CancelDialogActionDefinition cancelAction = new CancelDialogActionDefinition();
111             cancelAction.setName("cancel");
112             cancelAction.setLabel("cancel");
113             dialog.getActions().put(cancelAction.getName(), cancelAction);
114         }
115 
116         return new DefinitionProviderWrapper<DialogDefinition>(definitionProvider) {
117             @Override
118             public DialogDefinition get() throws Registry.InvalidDefinitionException {
119                 return dialog;
120             }
121         };
122     }
123 
124     /**
125      * The {@link CompatibilityPagesAppPredicate} denotes <i>when</i> to trigger invocations of the dialog factories.
126      *
127      * <p>In a Magnolia 5 UI Framework context, there are usually two subsequent invocations;
128      * the latter one comes from the {@link BlossomFormDialogPresenter}, once the {@link DialogCreationContext} has been populated with Vaadin 7 Item, JCR Node, etc.
129      * So we need to defer actual invocation of controllers to that point.</p>
130      *
131      * <p>In a Magnolia 6 UI Framework context, there is only a single invocation, and injecting JCR Node and other content attributes is currently not supported.</p>
132      */
133     class CompatibilityPagesAppPredicate implements Predicate<BlossomDialogDefinitionProvider> {
134         private static final String OLD_PAGES_APP_NAME = "pages";
135 
136         @Override
137         public boolean test(BlossomDialogDefinitionProvider definitionProvider) {
138             AppController appController;
139             try {
140                 appController = Components.getComponent(AppController.class);
141             } catch (MgnlInstantiationException | NoSuchComponentException e) {
142                 log.warn("No AppController available while invoking Blossom dialog with id {}. Not invoking dialog controller.", definitionProvider.getMetadata().getReferenceId());
143                 return false;
144             }
145 
146             Location currentAppLocation = appController.getCurrentAppLocation();
147             if (currentAppLocation == null) {
148                 log.warn("Unable to determine current app location while invoking Blossom dialog with id {}. Not invoking dialog controller.", definitionProvider.getMetadata().getReferenceId());
149                 return false;
150             }
151 
152             // DialogCreationContextHolder is only #set by BlossomFormDialogPresenter; skip as long as it's not there, see class-level javadoc.
153             if (OLD_PAGES_APP_NAME.equals(currentAppLocation.getAppName()) && DialogCreationContextHolder.get() == null) {
154                 log.debug("Dialog creator initially invoked for old pages app without a DialogCreationContext set. Skipping dialog controller.");
155                 return false;
156             }
157 
158             return true;
159         }
160     }
161 }