text: import re
  from django import forms
  from django.template import loader
  from django.contrib.formtools.wizard.storage import get_storage
  from django.contrib.formtools.wizard.forms import ManagementForm
  from django.contrib.formtools.wizard.views import StepsHelper
  from django.utils.datastructures import SortedDict
  from django.forms import ValidationError
  from django.forms.models import modelform_factory
  from xadmin.sites import site
  from xadmin.views import BaseAdminPlugin, ModelFormAdminView
  
  
  def normalize_name(name):
      new = re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', '_\\1', name)
      return new.lower().strip('_')
  
  
  class WizardFormPlugin(BaseAdminPlugin):
  
      wizard_form_list = None
      wizard_for_update = False
  
      storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
      form_list = None
      initial_dict = None
      instance_dict = None
      condition_dict = None
  
      def _get_form_prefix(self, step=None):
          if step is None:
              step = self.steps.current
          return 'step_%d' % self.get_form_list().keys().index(step)
  
      def get_form_list(self):
          if not hasattr(self, '_form_list'):
              init_form_list = SortedDict()
  
              assert len(
                  self.wizard_form_list) > 0, 'at least one form is needed'
  
              for i, form in enumerate(self.wizard_form_list):
                  init_form_list[unicode(form[0])] = form[1]
  
              self._form_list = init_form_list
  
          return self._form_list
  
      # Plugin replace methods
      def init_request(self, *args, **kwargs):
          if self.request.is_ajax() or ("_ajax" in self.request.GET) or not hasattr(self.request, 'session') or (args and not self.wizard_for_update):
              #update view
              return False
          return bool(self.wizard_form_list)
  
      def prepare_form(self, __):
          # init storage and step helper
          self.prefix = normalize_name(self.__class__.__name__)
          self.storage = get_storage(
              self.storage_name, self.prefix, self.request,
              getattr(self, 'file_storage', None))
          self.steps = StepsHelper(self)
          self.wizard_goto_step = False
  
          if self.request.method == 'GET':
              self.storage.reset()
              self.storage.current_step = self.steps.first
  
              self.admin_view.model_form = self.get_step_form()
          else:
              # Look for a wizard_goto_step element in the posted data which
              # contains a valid step name. If one was found, render the requested
              # form. (This makes stepping back a lot easier).
              wizard_goto_step = self.request.POST.get('wizard_goto_step', None)
              if wizard_goto_step and int(wizard_goto_step) < len(self.get_form_list()):
                  self.storage.current_step = self.get_form_list(
                  ).keys()[int(wizard_goto_step)]
                  self.admin_view.model_form = self.get_step_form()
                  self.wizard_goto_step = True
                  return
  
              # Check if form was refreshed
              management_form = ManagementForm(
                  self.request.POST, prefix=self.prefix)
              if not management_form.is_valid():
                  raise ValidationError(
                      'ManagementForm data is missing or has been tampered.')
  
              form_current_step = management_form.cleaned_data['current_step']
              if (form_current_step != self.steps.current and
                      self.storage.current_step is not None):
                  # form refreshed, change current step
                  self.storage.current_step = form_current_step
  
              # get the form for the current step
              self.admin_view.model_form = self.get_step_form()
  
      def get_form_layout(self, __):
          attrs = self.get_form_list()[self.steps.current]
          if type(attrs) is dict and 'layout' in attrs:
              self.admin_view.form_layout = attrs['layout']
          else:
              self.admin_view.form_layout = None
          return __()
  
      def get_step_form(self, step=None):
          if step is None:
              step = self.steps.current
          attrs = self.get_form_list()[step]
          if type(attrs) in (list, tuple):
              return modelform_factory(self.model, form=forms.ModelForm,
                                       fields=attrs, formfield_callback=self.admin_view.formfield_for_dbfield)
          elif type(attrs) is dict:
              if attrs.get('fields', None):
                  return modelform_factory(self.model, form=forms.ModelForm,
                                           fields=attrs['fields'], formfield_callback=self.admin_view.formfield_for_dbfield)
              if attrs.get('callback', None):
                  callback = attrs['callback']
                  if callable(callback):
                      return callback(self)
                  elif hasattr(self.admin_view, str(callback)):
                      return getattr(self.admin_view, str(callback))(self)
          elif isinstance(attrs, forms.BaseForm):
              return attrs
          return None
  
      def get_step_form_obj(self, step=None):
          if step is None:
              step = self.steps.current
          form = self.get_step_form(step)
          return form(prefix=self._get_form_prefix(step),
                      data=self.storage.get_step_data(step),
                      files=self.storage.get_step_files(step))
  
      def get_form_datas(self, datas):
          datas['prefix'] = self._get_form_prefix()
          if self.request.method == 'POST' and self.wizard_goto_step:
              datas.update({
                  'data': self.storage.get_step_data(self.steps.current),
                  'files': self.storage.get_step_files(self.steps.current)
              })
          return datas
  
      def valid_forms(self, __):
          if self.wizard_goto_step:
              # goto get_response directly
              return False
          return __()
  
      def _done(self):
          cleaned_data = self.get_all_cleaned_data()
          form = modelform_factory(self.model, form=forms.ModelForm,
                                   formfield_callback=self.admin_view.formfield_for_dbfield)
          form_obj = form(data=cleaned_data, instance=self.admin_view.org_obj)
          self.admin_view.new_obj = form_obj.save(commit=True)
  
      def save_forms(self, __):
          # if the form is valid, store the cleaned data and files.
          form_obj = self.admin_view.form_obj
          self.storage.set_step_data(self.steps.current, form_obj.data)
          self.storage.set_step_files(self.steps.current, form_obj.files)
  
          # check if the current step is the last step
          if self.steps.current == self.steps.last:
              # no more steps, render done view
              return self._done()
  
      def save_models(self, __):
          pass
  
      def save_related(self, __):
          pass
  
      def get_context(self, context):
          context.update({
              "show_save": False,
              "show_save_as_new": False,
              "show_save_and_add_another": False,
              "show_save_and_continue": False,
          })
          return context
  
      def get_response(self, response):
          self.storage.update_response(response)
          return response
  
      def post_response(self, __):
          if self.steps.current == self.steps.last:
              self.storage.reset()
              return __()
  
          # change the stored current step
          self.storage.current_step = self.steps.next
  
          self.admin_view.form_obj = self.get_step_form_obj()
          self.admin_view.setup_forms()
  
          return self.admin_view.get_response()
  
      def get_all_cleaned_data(self):
          """
          Returns a merged dictionary of all step cleaned_data dictionaries.
          If a step contains a `FormSet`, the key will be prefixed with formset
          and contain a list of the formset cleaned_data dictionaries.
          """
          cleaned_data = {}
          for form_key, attrs in self.get_form_list().items():
              form_obj = self.get_step_form_obj(form_key)
              if form_obj.is_valid():
                  if type(attrs) is dict and 'convert' in attrs:
                      callback = attrs['convert']
                      if callable(callback):
                          callback(self, cleaned_data, form_obj)
                      elif hasattr(self.admin_view, str(callback)):
                          getattr(self.admin_view,
                                  str(callback))(self, cleaned_data, form_obj)
                  elif isinstance(form_obj.cleaned_data, (tuple, list)):
                      cleaned_data.update({
                          'formset-%s' % form_key: form_obj.cleaned_data
                      })
                  else:
                      cleaned_data.update(form_obj.cleaned_data)
          return cleaned_data
  
      def get_cleaned_data_for_step(self, step):
          """
          Returns the cleaned data for a given `step`. Before returning the
          cleaned data, the stored values are being revalidated through the
          form. If the data doesn't validate, None will be returned.
          """
          if step in self.get_form_list():
              form_obj = self.get_step_form_obj(step)
              if form_obj.is_valid():
                  return form_obj.cleaned_data
          return None
  
      def get_next_step(self, step=None):
          """
          Returns the next step after the given `step`. If no more steps are
          available, None will be returned. If the `step` argument is None, the
          current step will be determined automatically.
          """
          if step is None:
              step = self.steps.current
          form_list = self.get_form_list()
          key = form_list.keyOrder.index(step) + 1
          if len(form_list.keyOrder) > key:
              return form_list.keyOrder[key]
          return None
  
      def get_prev_step(self, step=None):
          """
          Returns the previous step before the given `step`. If there are no
          steps available, None will be returned. If the `step` argument is
          None, the current step will be determined automatically.
          """
          if step is None:
              step = self.steps.current
          form_list = self.get_form_list()
          key = form_list.keyOrder.index(step) - 1
          if key >= 0:
              return form_list.keyOrder[key]
          return None
  
      def get_step_index(self, step=None):
          """
          Returns the index for the given `step` name. If no step is given,
          the current step will be used to get the index.
          """
          if step is None:
              step = self.steps.current
          return self.get_form_list().keyOrder.index(step)
  
      def block_before_fieldsets(self, context, nodes):
          context.update(dict(self.storage.extra_data))
          context['wizard'] = {
              'steps': self.steps,
              'management_form': ManagementForm(prefix=self.prefix, initial={
                  'current_step': self.steps.current,
              }),
          }
          nodes.append(loader.render_to_string('xadmin/blocks/model_form.before_fieldsets.wizard.html', context_instance=context))
  
      def block_submit_line(self, context, nodes):
          context.update(dict(self.storage.extra_data))
          context['wizard'] = {
              'steps': self.steps
          }
          nodes.append(loader.render_to_string('xadmin/blocks/model_form.submit_line.wizard.html', context_instance=context))
  
  site.register_plugin(WizardFormPlugin, ModelFormAdminView)
  