(self, field, key, REQUEST)
return PlanningBoxEditor(field.id, update_dict)
main method to solve validation
    - rebuild the whole planning structure but do not display it.
      this is needed to be able to get the block positions
    - apply block movements recovered from the java script
    - for each block moved, check the modification (over he primary axis in
      case task association has changed, and also over the lane axis to
      check block movement / resizement).
      Beware : This checking part does not include constraint checking.
    - build a list with the validated blocks and also with the error blocks
      (if any).
    - return a dict of values to update objects in case no errors have been
      found. Otherwise save in the REQUEST the list of error blocks so that
      they can be displayed in a special way.
main method to solve validation
    - rebuild the whole planning structure but do not display it.
      this is needed to be able to get the block positions
    - apply block movements recovered from the java script
    - for each block moved, check the modification (over he primary axis in
      case task association has changed, and also over the lane axis to
      check block movement / resizement).
      Beware : This checking part does not include constraint checking.
    - build a list with the validated blocks and also with the error blocks
      (if any).
    - return a dict of values to update objects in case no errors have been
      found. Otherwise save in the REQUEST the list of error blocks so that
      they can be displayed in a special way.
main method to solve validation - rebuild the whole planning structure but do not display it . this is needed to be able to get the block positions - apply block movements recovered from the java script - for each block moved check the modification ( over he primary axis in case task association has changed and also over the lane axis to check block movement / resizement ) . Beware : This checking part does not include constraint checking . - build a list with the validated blocks and also with the error blocks ( if any ) . - return a dict of values to update objects in case no errors have been found . Otherwise save in the REQUEST the list of error blocks so that they can be displayed in a special way .
def validate(self, field, key, REQUEST):
    """
    main method to solve validation
    - rebuild the whole planning structure but do not display it.
      this is needed to be able to get the block positions
    - apply block movements recovered from the java script
    - for each block moved, check the modification (over he primary axis in
      case task association has changed, and also over the lane axis to
      check block movement / resizement).
      Beware : This checking part does not include constraint checking.
    - build a list with the validated blocks and also with the error blocks
      (if any).
    - return a dict of values to update objects in case no errors have been
      found. Otherwise save in the REQUEST the list of error blocks so that
      they can be displayed in a special way.
    """
    # init params
    value = None
    context = getContext(field, REQUEST)

    # recover usefull properties
    block_moved_string = REQUEST.get('block_moved','')
    block_previous_string = REQUEST.get('previous_block_moved','')

    ############## REBUILD STRUCTURE #################
    # build structure
    basic, planning = PlanningBoxWidgetInstance.render_structure(field=field,
                          REQUEST= REQUEST,
                          context=context)

    # getting coordinates script generator
    planning_coordinates_method = getattr(context, 'planning_coordinates')
    # calling script to generate coordinates
    planning_coordinates = planning_coordinates_method(basic=basic,
                                                       planning=planning)

    ########## RECOVERING BLOCK MOVED DICTS ##########
    #  converting string to a structure
    block_moved_list = self.getBlockPositionFromString(block_moved_string)
    # block_moved_list now holds a list of structure recovered from the REQUEST
    # and correspondig to the movements done before validating
    block_previous_list =\
                       self.getBlockPositionFromString(block_previous_string)
    # list of previous blocks moved if an error occured during previous
    # validation

    # updating block_moved_list using block_previous_list.
    # This is very important not to escape processing blocks that have been
    # moved during a previous validation attempt.
    if block_previous_list != [] and block_moved_list != []:
      for block_previous in block_previous_list:
        # checking if the block has been moved again in this validation attempt
        # if it is the case, the block must be also present in the current
        # block_moved_list
        block_found = {}
        for block_moved in block_moved_list:
          if block_moved['name'] == block_previous['name']:
            block_found = block_moved
            break
        if block_found != {}:
          # block has been moved again, updating its properties in the current
          # list to take into account its previous position. current block is
          # known as 'block_found', and the value to update is the original
          # absolute position used to get relative coordinates
          block_found['old_X'] = block_previous['old_X']
          block_found['old_Y'] = block_previous['old_Y']
        else:
          # block has not been moved again, adding old block information to
          # the current list of block_moved
          block_moved_list.append(block_previous)
    elif block_previous_list != []:
      # block_moved_list is empty but not block_previous_list. This means the
      # user is trying to validate again without any change
      block_moved_list = block_previous_list
    elif block_moved_list != []:
      # block_previous_list is empty, this means this is the first validation
      # attempt. Using the block_moved_list as it is
      pass
    else:
      # the two lists are empty : nothing to validate
      return None
    # block_moved_list is updated

    # dict aimed to hold all information about block
    final_block_dict = {}
    # dict holding all the activities that will need an update because at least
    # one of the blocks concerned is moved
    activity_dict = {}
    # list holding all the activities having one of their block not validated
    # in such a case the update process of the activity is canceled
    warning_activity_list = []
    error_block_list = []
    error_info_dict = {}

    ########## GETTING BLOCK INFORMATION ############
    # iterating each block_moved element and recovering all usefull properties
    # BEWARE : no update is done here as an activity can be composed of several
    # blocks and so we need first to check all the blocks moved
    for block_moved in block_moved_list:
      final_block = {}
      # recovering the block object from block_moved information
      final_block['block_object'] = self.getBlockObject(block_moved['name'], \
                                                        planning.content)
      # recovering original activity object
      final_block['activity_origin'] = \
           final_block['block_object'].parent_activity
      # recovering original axis_group object
      final_block['group_origin'] = \
           final_block['activity_origin'].parent_axis_element.parent_axis_group
      # recovering relative block information in planning_coordinates
      final_block['block_info'] = \
           planning_coordinates['content'][block_moved['name']]

      # calculating delta
      # block_moved holds coordinates recovered from drag&drop script, while
      # block_info has the relative coordinates.
      # In fact the Drag&Drop java script used to get destination coordinates
      # gives them in absolute. so using original block position to get the
      # relative position
      deltaX = block_moved['old_X'] - final_block['block_info']['margin-left']
      deltaY = block_moved['old_Y'] - final_block['block_info']['margin-top']

      # calculating new block position:
      # width and height are already in the good format
      block_moved['left'] = block_moved['new_X'] - deltaX
      block_moved['top']  = block_moved['new_Y'] - deltaY

      # abstracting axis representation (for generic processing)
      if planning.vertical_view == 0:
        block_moved['report_axis_position'] = block_moved['top']
        block_moved['report_axis_length'] = block_moved['height']
        block_moved['lane_axis_position'] = block_moved['left']
        block_moved['lane_axis_length'] = block_moved['width']
        # used afterwards to get destination group
        group_position = 'margin-top'
        group_length = 'height'
        # used afterwards to get lane axis displacements and modifications
        axis_length = 'width'
      else:
        block_moved['report_axis_position'] = block_moved['left']
        block_moved['report_axis_length'] = block_moved['width']
        block_moved['lane_axis_position'] = block_moved['top']
        block_moved['lane_axis_length'] = block_moved['height']
        group_position = 'margin-left'
        group_length = 'width'
        axis_length = 'height'

      # calculating center of block over main axis to check block position
      block_moved['center'] = (block_moved['report_axis_length'] / 2) + \
                               block_moved['report_axis_position']

      # now that block coordinates are recovered as well as planning
      # coordinates, recovering destination group over the main axis to know
      # if the block has been moved from a group to another
      group_destination = self.getDestinationGroup(basic, planning,
               block_moved,planning_coordinates['report_axis'],
               group_position, group_length)

      if group_destination is None:
        # !! Generate an Error !!
        # block has been moved outside the content area (not in line with any
        # group of the current area).
        # adding current block to error_list
        error_block_list.append(block_moved['name'])
        error_info_dict[block_moved['name']] = 'out of bounds on main axis'
        # adding if necessary current activity to warning_list
        if final_block['activity_origin'].name not in warning_activity_list:
          warning_activity_list.append(final_block['activity_origin'].name)
        lane_axis_positions = [ None , None ]
      else:
        # now that all information about the main axis changes are
        # known, checking modifications over the lane axis.
        lane_axis_positions = self.getDestinationBounds(basic, planning,
                block_moved, final_block['block_object'],
                planning_coordinates, axis_length,
                destination_group = group_destination)
        if lane_axis_positions[2] == 1 :
          # !! Generate an Error !!
          # block has been moved outside the content area (bounds do not match
          # current area limits)
          if block_moved['name'] not in error_block_list:
            error_block_list.append(block_moved['name'])
            error_info_dict[block_moved['name']] = 'out of bounds on sec axis'
            if final_block['activity_origin'].name not in warning_activity_list:
              warning_activity_list.append(final_block['activity_origin'].name)

      block_moved['lane_axis_start'] = lane_axis_positions[0]
      block_moved['lane_axis_stop'] = lane_axis_positions[1]

      final_block['block_moved'] = block_moved
      final_block['group_destination'] = group_destination

      #final_block_dict[block_moved['name']] = final_block
      try:
        activity_dict[final_block['activity_origin'].name].append(final_block)
      except KeyError:
        activity_dict[final_block['activity_origin'].name] = [final_block]


    # getting object_dict to update object properties once activities are up to
    # date. Activities values will be updated directly on the
    object_dict = self.getObjectDict(basic=basic, planning=planning)

    ############# UPDATING ACTIVITIES ################
    # if activity is composed of several blocks, then check if it is needed to
    # update activity itself depending on blocks moved. Beware this part only
    # deals with activities (i.e task object) : an object can be represented by
    # several activities (in case of calendar mode for example).
    # build a dict
    update_list = []
    errors_list = []
    # getting start & stop property names
    start_property = field.get_value('x_start_bloc')
    stop_property = field.get_value('x_stop_bloc')
    # getting round_script if exists
    round_script=getattr(context, field.get_value('round_script'), None)
    # now processing activity updates
    for activity_name in activity_dict.keys():
      # recovering list of moved blocks in the current activity
      activity_block_moved_list = activity_dict[activity_name]
      # recovering activity object from first moved block
      activity_object = activity_block_moved_list[0]['activity_origin']
      # now getting list of blocks related to the activity (moved or not)
      activity_block_list = activity_object.block_list
      if activity_object.name in warning_activity_list:
        # activity contains a block that has not been validated
        # The validation update process is canceled, and the error is reported
        err = ValidationError(StandardError,activity_object)
        errors_list.append(err)
        pass
      else:
        # no error : continue
        # recovering new activity bounds
        (start_value, stop_value) = \
             self.getActivityBounds(activity_object, activity_block_moved_list,
                                    activity_block_list)

        # call specific external method to round value
        if round_script is not None:
          start_value = round_script(start_value, axis='begin')
          stop_value = round_script(stop_value, axis='end')
        # adding object name to list of objects to update
        if activity_object.object.getUrl() not in update_list:
          update_list.append(activity_object.object.getUrl())
        # saving updated information in the final dict
        for activity_desc in object_dict[activity_object.object.getUrl()]:
          if activity_desc['activity_name'] == activity_object.name:
            activity_desc['axis_start'] = start_value
            activity_desc['axis_stop'] = stop_value

    ############### UPDATING OBJECTS #################
    # using result from updated activities to process update on objects.
    update_dict = {}
    # first building a dict with all information for each object.
    # now processing activity updates
    for object_name in update_list:
      object_info = object_dict[object_name]
      axis_start = None
      axis_stop  = None
      for activity in object_info:
        activity_start_date = activity['axis_start']
        activity_stop_date = activity['axis_stop']
        if activity['activity_name'] == 'update':
          # case current activity is in fact 'fake' activity, just data telling
          # if can update min & max bounds according to global decision toward
          # objects.
          can_update_start = activity_start_date
          can_update_stop  = activity_stop_date
        else:
          if axis_start > activity_start_date or axis_start is None:
            axis_start = activity_start_date
          if axis_stop  < activity_stop_date  or axis_stop  is None:
            axis_stop = activity_stop_date

      update_dict[object_name] = {}
      if can_update_start and axis_start is not None:
        update_dict[object_name][start_property] = axis_start
      if can_update_stop  and axis_stop  is not None:
        update_dict[object_name][stop_property]  = axis_stop

    # testing if need to raise errors
    if len(errors_list) > 0:
      # need to raise an error
      # rebuilt position string including new values
      block_moved_string = self.setBlockPositionToString(block_moved_list)
      # save the current block_list for repositionning the blocks
      # to their final position
      REQUEST.set('previous_block_moved',block_moved_string)
      # saving blocks not validated as such as the activity they belong to to
      # apply a special treatment.
      REQUEST.set('warning_activity_list',warning_activity_list)
      REQUEST.set('error_block_list',error_block_list)
      REQUEST.set('error_info_dict',error_info_dict)
      # now raise error => automatically called
      # parameters are :
      # - list of errors
      # - dict with error results
      raise FormValidationError(errors_list, {})

    # the whole process is now finished,
    # just need to return editor for updating data
    return PlanningBoxEditor(field.id, update_dict)
def validate ( self , field , key , REQUEST ) : # init params value = None context = getContext ( field , REQUEST ) # recover usefull properties block_moved_string = REQUEST . get ( 'block_moved' , '' ) block_previous_string = REQUEST . get ( 'previous_block_moved' , '' ) ############## REBUILD STRUCTURE ################# # build structure basic , planning = PlanningBoxWidgetInstance . render_structure ( field = field , REQUEST = REQUEST , context = context ) # getting coordinates script generator planning_coordinates_method = getattr ( context , 'planning_coordinates' ) # calling script to generate coordinates planning_coordinates = planning_coordinates_method ( basic = basic , planning = planning ) ########## RECOVERING BLOCK MOVED DICTS ########## #  converting string to a structure block_moved_list = self . getBlockPositionFromString ( block_moved_string ) # block_moved_list now holds a list of structure recovered from the REQUEST # and correspondig to the movements done before validating block_previous_list = self . getBlockPositionFromString ( block_previous_string ) # list of previous blocks moved if an error occured during previous # validation # updating block_moved_list using block_previous_list. # This is very important not to escape processing blocks that have been # moved during a previous validation attempt. if block_previous_list != [ ] and block_moved_list != [ ] : for block_previous in block_previous_list : # checking if the block has been moved again in this validation attempt # if it is the case, the block must be also present in the current # block_moved_list block_found = { } for block_moved in block_moved_list : if block_moved [ 'name' ] == block_previous [ 'name' ] : block_found = block_moved break if block_found != { } : # block has been moved again, updating its properties in the current # list to take into account its previous position. current block is # known as 'block_found', and the value to update is the original # absolute position used to get relative coordinates block_found [ 'old_X' ] = block_previous [ 'old_X' ] block_found [ 'old_Y' ] = block_previous [ 'old_Y' ] else : # block has not been moved again, adding old block information to # the current list of block_moved block_moved_list . append ( block_previous ) elif block_previous_list != [ ] : # block_moved_list is empty but not block_previous_list. This means the # user is trying to validate again without any change block_moved_list = block_previous_list elif block_moved_list != [ ] : # block_previous_list is empty, this means this is the first validation # attempt. Using the block_moved_list as it is pass else : # the two lists are empty : nothing to validate return None # block_moved_list is updated # dict aimed to hold all information about block final_block_dict = { } # dict holding all the activities that will need an update because at least # one of the blocks concerned is moved activity_dict = { } # list holding all the activities having one of their block not validated # in such a case the update process of the activity is canceled warning_activity_list = [ ] error_block_list = [ ] error_info_dict = { } ########## GETTING BLOCK INFORMATION ############ # iterating each block_moved element and recovering all usefull properties # BEWARE : no update is done here as an activity can be composed of several # blocks and so we need first to check all the blocks moved for block_moved in block_moved_list : final_block = { } # recovering the block object from block_moved information final_block [ 'block_object' ] = self . getBlockObject ( block_moved [ 'name' ] , planning . content ) # recovering original activity object final_block [ 'activity_origin' ] = final_block [ 'block_object' ] . parent_activity # recovering original axis_group object final_block [ 'group_origin' ] = final_block [ 'activity_origin' ] . parent_axis_element . parent_axis_group # recovering relative block information in planning_coordinates final_block [ 'block_info' ] = planning_coordinates [ 'content' ] [ block_moved [ 'name' ] ] # calculating delta # block_moved holds coordinates recovered from drag&drop script, while # block_info has the relative coordinates. # In fact the Drag&Drop java script used to get destination coordinates # gives them in absolute. so using original block position to get the # relative position deltaX = block_moved [ 'old_X' ] - final_block [ 'block_info' ] [ 'margin-left' ] deltaY = block_moved [ 'old_Y' ] - final_block [ 'block_info' ] [ 'margin-top' ] # calculating new block position: # width and height are already in the good format block_moved [ 'left' ] = block_moved [ 'new_X' ] - deltaX block_moved [ 'top' ] = block_moved [ 'new_Y' ] - deltaY # abstracting axis representation (for generic processing) if planning . vertical_view == 0 : block_moved [ 'report_axis_position' ] = block_moved [ 'top' ] block_moved [ 'report_axis_length' ] = block_moved [ 'height' ] block_moved [ 'lane_axis_position' ] = block_moved [ 'left' ] block_moved [ 'lane_axis_length' ] = block_moved [ 'width' ] # used afterwards to get destination group group_position = 'margin-top' group_length = 'height' # used afterwards to get lane axis displacements and modifications axis_length = 'width' else : block_moved [ 'report_axis_position' ] = block_moved [ 'left' ] block_moved [ 'report_axis_length' ] = block_moved [ 'width' ] block_moved [ 'lane_axis_position' ] = block_moved [ 'top' ] block_moved [ 'lane_axis_length' ] = block_moved [ 'height' ] group_position = 'margin-left' group_length = 'width' axis_length = 'height' # calculating center of block over main axis to check block position block_moved [ 'center' ] = ( block_moved [ 'report_axis_length' ] / 2 ) + block_moved [ 'report_axis_position' ] # now that block coordinates are recovered as well as planning # coordinates, recovering destination group over the main axis to know # if the block has been moved from a group to another group_destination = self . getDestinationGroup ( basic , planning , block_moved , planning_coordinates [ 'report_axis' ] , group_position , group_length ) if group_destination is None : # !! Generate an Error !! # block has been moved outside the content area (not in line with any # group of the current area). # adding current block to error_list error_block_list . append ( block_moved [ 'name' ] ) error_info_dict [ block_moved [ 'name' ] ] = 'out of bounds on main axis' # adding if necessary current activity to warning_list if final_block [ 'activity_origin' ] . name not in warning_activity_list : warning_activity_list . append ( final_block [ 'activity_origin' ] . name ) lane_axis_positions = [ None , None ] else : # now that all information about the main axis changes are # known, checking modifications over the lane axis. lane_axis_positions = self . getDestinationBounds ( basic , planning , block_moved , final_block [ 'block_object' ] , planning_coordinates , axis_length , destination_group = group_destination ) if lane_axis_positions [ 2 ] == 1 : # !! Generate an Error !! # block has been moved outside the content area (bounds do not match # current area limits) if block_moved [ 'name' ] not in error_block_list : error_block_list . append ( block_moved [ 'name' ] ) error_info_dict [ block_moved [ 'name' ] ] = 'out of bounds on sec axis' if final_block [ 'activity_origin' ] . name not in warning_activity_list : warning_activity_list . append ( final_block [ 'activity_origin' ] . name ) block_moved [ 'lane_axis_start' ] = lane_axis_positions [ 0 ] block_moved [ 'lane_axis_stop' ] = lane_axis_positions [ 1 ] final_block [ 'block_moved' ] = block_moved final_block [ 'group_destination' ] = group_destination #final_block_dict[block_moved['name']] = final_block try : activity_dict [ final_block [ 'activity_origin' ] . name ] . append ( final_block ) except KeyError : activity_dict [ final_block [ 'activity_origin' ] . name ] = [ final_block ] # getting object_dict to update object properties once activities are up to # date. Activities values will be updated directly on the object_dict = self . getObjectDict ( basic = basic , planning = planning ) ############# UPDATING ACTIVITIES ################ # if activity is composed of several blocks, then check if it is needed to # update activity itself depending on blocks moved. Beware this part only # deals with activities (i.e task object) : an object can be represented by # several activities (in case of calendar mode for example). # build a dict update_list = [ ] errors_list = [ ] # getting start & stop property names start_property = field . get_value ( 'x_start_bloc' ) stop_property = field . get_value ( 'x_stop_bloc' ) # getting round_script if exists round_script = getattr ( context , field . get_value ( 'round_script' ) , None ) # now processing activity updates for activity_name in activity_dict . keys ( ) : # recovering list of moved blocks in the current activity activity_block_moved_list = activity_dict [ activity_name ] # recovering activity object from first moved block activity_object = activity_block_moved_list [ 0 ] [ 'activity_origin' ] # now getting list of blocks related to the activity (moved or not) activity_block_list = activity_object . block_list if activity_object . name in warning_activity_list : # activity contains a block that has not been validated # The validation update process is canceled, and the error is reported err = ValidationError ( StandardError , activity_object ) errors_list . append ( err ) pass else : # no error : continue # recovering new activity bounds ( start_value , stop_value ) = self . getActivityBounds ( activity_object , activity_block_moved_list , activity_block_list ) # call specific external method to round value if round_script is not None : start_value = round_script ( start_value , axis = 'begin' ) stop_value = round_script ( stop_value , axis = 'end' ) # adding object name to list of objects to update if activity_object . object . getUrl ( ) not in update_list : update_list . append ( activity_object . object . getUrl ( ) ) # saving updated information in the final dict for activity_desc in object_dict [ activity_object . object . getUrl ( ) ] : if activity_desc [ 'activity_name' ] == activity_object . name : activity_desc [ 'axis_start' ] = start_value activity_desc [ 'axis_stop' ] = stop_value ############### UPDATING OBJECTS ################# # using result from updated activities to process update on objects. update_dict = { } # first building a dict with all information for each object. # now processing activity updates for object_name in update_list : object_info = object_dict [ object_name ] axis_start = None axis_stop = None for activity in object_info : activity_start_date = activity [ 'axis_start' ] activity_stop_date = activity [ 'axis_stop' ] if activity [ 'activity_name' ] == 'update' : # case current activity is in fact 'fake' activity, just data telling # if can update min & max bounds according to global decision toward # objects. can_update_start = activity_start_date can_update_stop = activity_stop_date else : if axis_start > activity_start_date or axis_start is None : axis_start = activity_start_date if axis_stop < activity_stop_date or axis_stop is None : axis_stop = activity_stop_date update_dict [ object_name ] = { } if can_update_start and axis_start is not None : update_dict [ object_name ] [ start_property ] = axis_start if can_update_stop and axis_stop is not None : update_dict [ object_name ] [ stop_property ] = axis_stop # testing if need to raise errors if len ( errors_list ) > 0 : # need to raise an error # rebuilt position string including new values block_moved_string = self . setBlockPositionToString ( block_moved_list ) # save the current block_list for repositionning the blocks # to their final position REQUEST . set ( 'previous_block_moved' , block_moved_string ) # saving blocks not validated as such as the activity they belong to to # apply a special treatment. REQUEST . set ( 'warning_activity_list' , warning_activity_list ) REQUEST . set ( 'error_block_list' , error_block_list ) REQUEST . set ( 'error_info_dict' , error_info_dict ) # now raise error => automatically called # parameters are : # - list of errors # - dict with error results raise FormValidationError ( errors_list , { } ) # the whole process is now finished, # just need to return editor for updating data return PlanningBoxEditor ( field . id , update_dict )