cancel
Showing results for 
Search instead for 
Did you mean: 

Extending a Webform into a Wizard?

benvercammen
Champ in-the-making
Champ in-the-making
We currently have a system set up where content managers need to create 3 content types (via webforms) in a specific order and link them together in the end. Of course, this isn't very user-friendly and we would like to change the webform for the central content type into a sort of wizard, which could then create the other content types in each step and link them together without the user needing to do so. Baiscally this means we would like to merge 3 webforms into one…

So my question is, is it possible to modify the webform for a single content type, or is it better if we just create a wizard and leave the webforms as they are? Anybody already has any experience on this? We're looking forward to other people's opinions and advice!
7 REPLIES 7

benvercammen
Champ in-the-making
Champ in-the-making
I think I'm on the right track now. What I'm planning on doing is to override the CreateWebContentWizard and add some steps to it (http://wiki.alfresco.com/wiki/Customising_The_Create_Content_Wizard). However, I would like to add those steps only for one specific content type. I tried to use the "condition" statements outside the "step" elements in the configuration, but that doesn't seem to work. So is there any way to add some logic to which Wizard should be picked?

This works…

    <step …>
        <condition if="#{'true' == 'true'}">
            <page path="…" … />
        </condition>
    </step>

But this doesn't…

    <condition if="#{'true' == 'true'}">
        <step …>
            <page path="…" … />
        </step>
    </condition>

So if anyone can point out to me where the "Which wizard should be used" decision making is taking place, and optionally how to extend/modify it, we would be very happy indeed!

gavinc
Champ in-the-making
Champ in-the-making
Hi,

org.alfresco.web.app.AlfrescoNavigationHandler decides what dialog/wizard is used so this is a good starting point, it then uses the ConfigService to lookup the relevant configuration.

I presume you have looked at web-client-config-wziards.xml for examples of using the <condition> stuff, the 'createSpace' wizard uses them. However, there is a limitation in that only a linear path can be taken through the wizard i.e. you can't have 2 different paths with a different number of steps so that might not fit your use case anyway.

Hope this helps.

benvercammen
Champ in-the-making
Champ in-the-making
Thanks for the reply! I've also come to the conclusion that this isn't the way to go for me… I would've liked to be able to override the WizardConfig class to for example modify the getSteps() method, but I can't override the "AlfrescoNavigationHandler" (which ultimately leads to the WizardConfigElement and WizardConfig classes) in the faces-config-app.xml file. So I'm currently planning on giving up on that, unless there would be another way to be able to shove my custom WizardConfig class in there?

Just to be clear, I'm working with Enterprise 2.2.2 at the moment.

Now I'm looking into creating my own Action, which would start a Wizard to collect all necessary data and in the end creates/updates the necessary items and renditions. But my first issue with this approach is that I don't yet see how, where and when I could/should execute that Action. Maybe you've got an idea or suggestion on how or in which direction I can go best?

Once again thanks already!

benvercammen
Champ in-the-making
Champ in-the-making
After browsing through the code and quite some debugging and comparison, we've finally managed to do what we wanted; mashing 3 webforms into 1 wizard. And it actually isn't all that hard!  Smiley Happy  Below are listed the steps taken to come to our solution…

1. Create a new wizard

<config>
   <wizards>
      <wizard name="customWizard" managed-bean="CustomWizard"
         title-id="custom_create_content_wizard_title" description-id="create_content_desc"
         icon="/images/icons/new_content_large.gif">
         <step name="step1" title="Step 1" description="…">
            <page path="/jsp/wcm/create-web-content-wizard/create-xml.jsp"
               title="Step 1" description="…" instruction="…" />
         </step>
         <step name="step2" title="Step 2" description="…">
            <page path="/jsp/wcm/create-web-content-wizard/create-xml.jsp"
               title="Step 2" description="…" instruction="…" />
         </step>
         …
      </wizard>
   </wizards>
</config>
The important thing to note here is thate we make use of the /jsp/wcm/create-web-content-wizard/create-xml.jsp page. This is the jsp that's responsible to generate the webform from an XSD file.


public class CustomWizard extends CreateWebContentWizard {
   …
   private int currentStep = 0;

   @Override
   public void init(Map<String, String> parameters) {
      super.init(parameters);
      setWebForm();         // Make sure the first webForm name has been set…
   }

   @Override
   protected String finishImpl(FacesContext context, String outcome) throws Exception {
      super.finishImpl(context, outcome);
      …
      return outcome;
   }
   
   @Override
   public String next() {
      this.currentStep++;
      setWebForm();
      return super.next();
   }
   
   @Override
   public String back() {
      this.currentStep–;
      setWebForm();
      return super.back();
   }

   private void setWebForm() {
      switch (currentStep) {
         case 0 : this.setFormName("custom-step-1"); break;
         case 1 : this.setFormName("custom-step-2"); break;
      }
   }
   …
}
Things to note here are:
    Extended CreateWebContentWizard
    Manually set which WebForm should be used (this.setFormName()). Note that this should be a webform that's been added to your WebProject!
    Added our own code to track the current step so we can tell which webform to use

2. Create a custom command to trigger the WebForm
Check http://forums.alfresco.com/en/viewtopic.php?f=30&t=17097 for more on this…
After doing so, you get a URL which will open the Wizard (eg: localhost:8080/alfresco/c/ui/createwebpage). All you need to do now is add a link to that URL wherever you need it…


In the end you can't (easily) customize a webform to become a wizard, but you can create a wizard and make use of the webforms anyway without writing too much code yourself! The next thing we're looking into is how we can trigger a wizard from inside the current wizard and in the end have it return it's value to the first wizard… Any clues and tips are welcome!

benvercammen
Champ in-the-making
Champ in-the-making
Okay, cheered too early… Switching the formName like we do, we also lose our formdata and we get errors when submitting. Back to the drawing board…  Smiley Indifferent

benvercammen
Champ in-the-making
Champ in-the-making
We finally managed to load an XForm based only on an individual XSD (so no need to create a webform residing within Alfresco) by creating our own implementation of the org.alfresco.web.forms.Form interface (most methods throw an UnsupportedOperationException, but hey…). However, I'm hoping someone can tell me how we can access the values submitted to that form in our Wizard?

We know that in case we just render a JSP, we can use the following code and access the values with the "someProperty" getter and setter…
<h:inputText id="name" value="#{WizardManager.bean.someProperty}" size="50" maxlength="1024" required="true"/>

However, now that we have our form rendered via javascript and xform stuff, and we don't know how/where to access the submitted values. Can somebody please point us in the right direction?

benvercammen
Champ in-the-making
Champ in-the-making
For those who would like to know, we finally pulled it off!

To store the values of the submitted form, we needed to use a transaction:

private void storeCurrentForm() {
   FacesContext context = FacesContext.getCurrentInstance();
   RetryingTransactionHelper txnHelper = Repository.getRetryingTransactionHelper(context);
   RetryingTransactionCallback<String> callback =
      new RetryingTransactionCallback<String>() {
         public String execute() throws Throwable {
            // call the actual implementation
            saveContent();
            return null;
         }
      };
   try {
      // Execute the callback in a transaction
      txnHelper.doInTransaction(callback);
   } catch (Exception e) {
      // ….
   }
}
With the saveContent() method being:

@Override
protected void saveContent() throws Exception {
   System.out.println("Saving content for Wizard Step :: " + this.currentStep);
   final Form form = this.getForm();
   if (form != null) {
      Document instanceDataDocument = this.getInstanceDataDocument();
      // Make sure the super's variable is already set correctly, as it will be used further on…
      this.content = XMLUtil.toString(instanceDataDocument, false);
      contents[currentStep] = this.content;
      instanceDataDocuments[currentStep] = instanceDataDocument;
   }
}

The biggest trouble we had was that we forgot to keep some protected members of the super class (CreateWebContentWizard) in synch with what our getters would return (they retrieved the values stored in the arrays for the current step). Therefore we added the preloadWebForm() method (okay, the naming may not be all that…)


private void preloadWebForm() {
   this.content = contents[currentStep];
   this.formInstanceData = dataInstances[currentStep];
   this.formProcessorSession = formProcessorSessions[currentStep];
}

So when adding it all together, our next() and back() methods look like this:

@Override
public String next() {
   storeCurrentForm();
   this.currentStep++;
   preloadWebForm();
   return super.next();
}
   
@Override
public String back() {
   storeCurrentForm();
   this.currentStep–;
   preloadWebForm();
   return super.back();
}

In the end, in the finishImpl() method, we can access the resulting XML Strings via contents[0] or Documents via instanceDataDocuments[0]. We still need to manually trigger the renderings etc, but that shouldn't be too big a problem…

We might extend this functionality (if it's not yet available already) to be more generic, but for now it suits our needs. If someone still sees something that could be done better in this code, please let us know!
Getting started

Tags


Find what you came for

We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.