I am in the process of extending the wizard functions provided by CTools to cater to the needs of my current project. My plan is generalized enough that it could be used by other modules. My code is raw thus far but I figured it would be worth the time to post what I am doing so I can get feedback as I would be surprised if you hadn't already considered something like this.

In a nutshell, here is what I am trying to do:

* Allow other modules to alter wizards and apply their own forms.
* Extend wizard capabilities to nativley support multiple levels.

My code currently works much like FAPI as you can get a wizard much like a form:

ctools_get_wizard('my_wizard', $param);

Modules can implement hooks to alter the wizard:

function mymodule_wizard_my_wizard_alter(&$wizard) { ..
function mymodule_wizard_alter(&$wizard, $wizard_id) { ..

And wizards can have multiple levels (nested steps):

$wizard = array( // aka $form_info
...
  'forms' => array(
    // normal form definition
    'step_x' => array(
      'form id' => 'form_callback_x',
      'title' => t('Step X'),
      'weight' => 0,
      'type' => 'form', // (default)
    ),
    // group of forms definition
    'group_y' => array(
      'title' => t('Group Title'),
      'type' => 'group',
      'weight' => 1,
      'forms' => array(
        'group_y_step_1' => array(
          'title' => ...
          'form id' => '..'
        ),
      ),
    ),
  );
);

I'm implementing this so that it could be integrated with CTools or live entirely in it's own module. The module provides "wizard" plugins which would allow defining wizards for existing modules.

Anyway, I figured I'd better share to see if there is anything already out there or to see if I may be headed in the wrong direction.

What are your thoughts?

(I can break this into 2 different posts as there are two distinct features, but I wanted to get your general thoughts on both the options)

Comments

merlinofchaos’s picture

One complication here is that the $form_info structure tends to get tinkered with a lot before it's used. Maybe a builder function is the right way here, but I'm not sure about it.

Certainly drupal_alter function at the beginning of the wizard would allow wizards to be more easily tinkered with. I hadn't given that a great deal of thought, though, but an alter right there would let you rearrange the wizard nicely.

I'm not sure how well the 'group' steps could easily be implemented. That's going to be quite tricky and I'm not sure there's really that much payoff for doing it.

lee20’s picture

I'm not sure how well the 'group' steps could easily be implemented. That's going to be quite tricky and I'm not sure there's really that much payoff for doing it.

Yeah I suspect it can be tough but my motivation for doing it was that I wanted to be able to provide a UI similar to that of page manager. I started off trying to replicate page_manager but got lost in the architecture and didn't see how to implement a new "task type".

I'm in the process of implementing the multiple levels right now, and I am basically implementing it similar to the trails in the page manager. It will use the path from menu_get_item to determine the additional arguments as the "active steps".

The builder also accepts a theme callback to allow customizing the output of a wizard so that horizontal navigation can be provided. This navigation makes it tricky as step forms can be required or not required and perhaps this navigation is only visual as if it were clickable it would have to simulate button clicks.

So I guess maybe I should ask is there away to define my own task type for page manager? And does my plan sound completely insane or impossible?

merlinofchaos’s picture

I've often wanted to abstract page manager to provide a similar UI.

Page Manager isn't just one wizard; each tab is a separate wizard. The tabs are their own system, and then each tab has a wizard attached to it so it can have multiple forms if needed. Putting all of the tabs into the same wizard would probably overload the poor wizard system, which isn't going to be able to handle quite that level of stuff.

Though as I think on it, all the forms get melded together anyway. Hmm. I'll ponder this some more.

lee20’s picture

Thanks for your time and your thought on this.

When I have something a little more workable I will create a sandbox for it, or I guess I need to figure out if I can create a clone of CTools as a sandbox. Still new to this Git stuff but I really like it so far.

I guess the one thing I am not accounting for within the wizard that is included in the page manager is the "actions" and "subactions" at the top of the UI. I'm not sure if this is something the wizard would need to support natively but it's another thing to consider.

merlinofchaos’s picture

Yeah, all of the actions and subactions in the UI are part of Page Manager's not-at-all abstracted "operations" concept which I sort of threw together when I was trying to figure out how to make the UI work.

You can clone a project into a git repo like this:

git clone [sandbox URL]
git remote add ctools [ctools URL]
git checkout -b 7.x-1.x ctools/7.x-1.x
git branch my_clone_branch_name
git checkout my_clone_branch_name
git push origin my_clone_branch_name

There might be a shorter path to all of this, like you might be able to just do one checkout and skip making a 7.x-1.x branch that is tied to the ctools remote branch but this way you can be sure you won't accidentally tie your new branch to the ctools remote.

Once you've got a branch setup, I can do reviews via the sandbox drupalcode.org URL and I can add the remote and do merges, but you do need to make sure your commit messages are good. (If not, I can just squash them, too).

lee20’s picture

Thanks for the direction. I'm kind of pressed for time so some of my additions may not have much for commit messages as I will probably mass add them. Any modifications I make to existing Ctools files will have good commit message though.

Also, I am working on a 6.x version. I put this request under 7.x as I figured that was best for feature requests.

So I will be cloning ctools/6.x-1.x and we can port it to 7.x when if/when we merge it.

Another thing to mention, I don't have much for administrative UI planned for this. Right now I am trying to integrate wizards with node forms so the wizard module will be providing a node plugin that allows you to override the node forms with a wizard, but this won't do much itself as it will only override the node add/edit form paths and provide a wizard with only a single step. From here, other modules can add their own forms using the alter functions, but the additional forms I will be implementing will be specialized to my needs. Perhaps it can additionally allow you to break out the node structure into separate forms using field groups or another mechanism.

lee20’s picture

Version: 7.x-1.x-dev » 6.x-1.x-dev
Assigned: Unassigned » lee20

Updating the issue meta..

lee20’s picture

@merlinofchaos

I am having trouble getting the sandbox initialized as a clone of ctools. I added you as a maintainer. Think you could help me get this initialized as a clone of ctools 6.x-1.x?

http://drupal.org/project/1098774
#1098774: CTools Wizard

lee20’s picture

Your instructions didn't include instructions for initializing the project to get the [sandbox URL]

So I tried the instructions described here and kept getting an error:

$ git remote add origin lee20@git.drupal.org:sandbox/lee20/1098774.git
fatal: remote origin already exists.

http://drupal.org/node/1053378

lee20’s picture

@merlinofchaos

Please disregard. I'll get this setup and running eventually!

lee20’s picture

Got it. Here is a more complete list of commands I used.

git clone [sandbox URL] [myfolder]
cd [myfolder]
git remote add ctools [ctools URL]
git fetch ctools
git checkout -b 7.x-1.x ctools/7.x-1.x
git branch my_clone_branch_name
git checkout my_clone_branch_name
git push origin my_clone_branch_name
Brandonian’s picture

Subscribing.

lee20’s picture

Just wanted to post and say that I have made some progress on this. So far I have put everything as a submodule of CTools but there would be a number of additions to the ctools wizard which I have contained in the submodule for now. I need to go through and clean it up and add some comments and then I'll commit what I have. It has a start for the multi-level support but not much yet.

lee20’s picture

Sweet. I just got something that's usable and has functionality packed in. The Wizard module includes a node plugin which overrides node forms with wizards. Initially I only had this as one step but a one step wizard is useless! It hit me to make the node preview the second step so now there is actually some value in the basic functionality.

The code is still a bit messy but I'll start committing to the sandbox.

steveoliver’s picture

+1 @lee20: I really like your idea! Previewing the node in a second step offers the opportunity to separate the content fields from the meta.

I as an author would enjoy focusing on only "content" first, and appreciate the context of preview/diff alongside my commit/revision message, tags, scheduling, etc.

I'll be tracking your repo, for sure. Thanks for sharing.

kenorb’s picture

Regarding node preview (#14, #15) in ctools, sorry if it's a little off-topic, my code looks like this:

function my_module_preview_node($form, $form_state) {
  $node = ctools_object_cache_get('my_module', 'node'); // get the existing node...
  // ...which was created as: $node = new stdClass(); $node->type = 'page'; node_object_prepare($node); ctools_object_cache_set('my_module', 'node', $node);
  node_build_content($node, 'full'); // Populate $node->content with a render() array.
  $build = $node->content;
  unset($node->content); // // We don't need duplicate rendering info in node->content.
  $type = 'node';
  $build += array(
    '#theme' => $type,
    '#node' => $node,
    '#view_mode' => 'full',
    '#language' => NULL,
  );
  drupal_alter(array('node_view', 'entity_view'), $build, $type);

  return array_merge($form, $build);
}

Then my_module_preview_node callback is defined my_module_form_info()
So hopefully some code could be useful.

kenorb’s picture

Assigned: lee20 » Unassigned
Issue summary: View changes
Status: Active » Closed (outdated)

Drupal 6 is no longer officially supported. If you think this issue is still relevant for 8.x, feel free to re-open.