public function alterItems(array &$items) { // Prevent session information from being saved while indexing. drupal_save_session(FALSE); // Force the current user to anonymous to prevent access bypass in search // indexes. $original_user = $GLOBALS['user']; $GLOBALS['user'] = drupal_anonymous_user(); $entity_type = $this->index->getEntityType(); $entity_handler = panelizer_entity_plugin_get_handler($entity_type); foreach ($items as &$item) { $entity_id = entity_id($entity_type, $item); $item->search_api_panelizer_content = NULL; $item->search_api_panelizer_title = NULL; try { if ($render_info = $entity_handler->render_entity($item, 'page_manager')) { $item->search_api_panelizer_content = $render_info['content']; $item->search_api_panelizer_title = !empty($render_info['title']) ? $render_info['title'] : NULL; } } catch (Exception $e) { watchdog_exception('panelizer', $e, 'Error indexing Panelizer content for %entity_type with ID %entity_id', array('%entity_type' => $entity_type, '%entity_id' => $entity_id)); } } // Restore the user. $GLOBALS['user'] = $original_user; drupal_save_session(TRUE); }
public function alterItems(array &$items) { // Prevent session information from being saved while indexing. drupal_save_session(FALSE); // Force the current user to anonymous to prevent access bypass in search // indexes. $original_user = $GLOBALS['user']; $GLOBALS['user'] = drupal_anonymous_user(); $entity_type = $this->index->getEntityType(); $entity_handler = panelizer_entity_plugin_get_handler($entity_type); foreach ($items as &$item) { $entity_id = entity_id($entity_type, $item); $item->search_api_panelizer_content = NULL; $item->search_api_panelizer_title = NULL; // If Search API specifies a language to view the item in, force the // global language_content to be Search API item language. Fieldable // panel panes will render in the correct language. if (isset($item->search_api_language)) { global $language_content; $original_language_content = $language_content; $languages = language_list(); if (isset($languages[$item->search_api_language])) { $language_content = $languages[$item->search_api_language]; } else { $language_content = language_default(); } } try { if ($render_info = $entity_handler->render_entity($item, 'page_manager')) { $item->search_api_panelizer_content = $render_info['content']; $item->search_api_panelizer_title = !empty($render_info['title']) ? $render_info['title'] : NULL; } } catch (Exception $e) { watchdog_exception('panelizer', $e, 'Error indexing Panelizer content for %entity_type with ID %entity_id', array('%entity_type' => $entity_type, '%entity_id' => $entity_id)); } // Restore the language_content global if it was overridden. if (isset($original_language_content)) { $language_content = $original_language_content; } } // Restore the user. $GLOBALS['user'] = $original_user; drupal_save_session(TRUE); }
protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { $entity_uri = entity_uri($this->entityType, $entity); $row[] = array('data' => array('#theme' => 'entity_ui_overview_item', '#label' => entity_label($this->entityType, $entity), '#name' => !empty($this->entityInfo['exportable']) ? entity_id($this->entityType, $entity) : FALSE, '#url' => $entity_uri ? $entity_uri : FALSE, '#entity_type' => $this->entityType)); // Add in any passed additional cols. foreach ($additional_cols as $col) { $row[] = $col; } // Add a row for the exportable status. if (!empty($this->entityInfo['exportable'])) { $row[] = array('data' => array('#theme' => 'entity_status', '#status' => $entity->{$this->statusKey})); } // In case this is a bundle, we add links to the field ui tabs. $field_ui = !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui'); // For exportable entities we add an export link. $exportable = !empty($this->entityInfo['exportable']); // If i18n integration is enabled, add a link to the translate tab. $i18n = !empty($this->entityInfo['i18n controller class']); // Add operations depending on the status. if (entity_has_status($this->entityType, $entity, ENTITY_FIXED)) { $row[] = array('data' => l(t('clone'), $this->path . '/manage/' . $id . '/clone'), 'colspan' => $this->operationCount()); } else { $row[] = l(t('edit'), $this->path . '/manage/' . $id); if (empty($this->entityInfo['exportable']) || !entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) { $row[] = l(t('delete'), $this->path . '/manage/' . $id . '/delete', array('query' => drupal_get_destination())); } else { $row[] = ''; } } if ($exportable) { $row[] = l(t('export'), $this->path . '/manage/' . $id . '/export'); } $row[] = $entity->product_quantity; $row[] = l(t('quick+1'), '/product/' . $id . '/add/1'); $row[] = l(t('quick-1'), '/product/' . $id . '/sub/1'); return $row; }
/** * Responds to field collection item deletion. * * This hook is invoked after the field collection item has been removed from * the database. * * @param FieldCollectionItemEntity $field_collection_item * The field collection item that is being deleted. * * @see hook_entity_delete() */ function hook_field_collection_item_delete(FieldCollectionItemEntity $field_collection_item) { db_delete('mytable')->condition('pid', entity_id('field_collection_item', $field_collection_item))->execute(); }
/** * Overrides the 'revert' action, to not delete the workflows. * * @see https://www.drupal.org/node/2051079 * @see https://www.drupal.org/node/1043634 */ public function applyOperation($op, $entity) { $label = entity_label($this->entityType, $entity); $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); $id = entity_id($this->entityType, $entity); $edit_link = l(t('edit'), $this->path . '/manage/' . $id . '/edit'); switch ($op) { case 'revert': // Do not delete the workflow, but recreate features_get_default($entity_type, $module); // entity_delete($this->entityType, $id); $workflow = $entity; $entity_type = $this->entityType; $funcname = $workflow->module . '_default_' . $this->entityType; $defaults = $funcname(); // No defaults, no processing. if (empty($defaults)) { return; } foreach ($defaults as $name => $entity) { $existing[$name] = workflow_load($name); // If we got an existing entity with the same name, we reuse its entity id. if (isset($existing[$name])) { // Set the original as later reference. $entity->original = $existing[$name]; // As we got an ID, the entity is not new. $entity->wid = $entity->original->wid; unset($entity->is_new); // Update the status to be in code. // $entity->status |= ENTITY_IN_CODE; $entity->status = ENTITY_IN_CODE; // We mark it for being in revert mode. $entity->is_reverted = TRUE; entity_save($entity_type, $entity); unset($entity->is_reverted); } // The rest of the defaults is handled by default implementation. // @see entity_defaults_rebuild() } watchdog($this->entityType, 'Reverted %entity %label to the defaults.', $vars, WATCHDOG_NOTICE, $edit_link); return t('Reverted %entity %label to the defaults.', $vars); case 'delete': case 'import': default: return parent::applyOperation($op, $entity); } }
/** * Implements hook_field_update() -> FieldItemInterface::update(). * * @todo: in course of time, this is not used anymore... * It is called also from hook_field_insert(), since we need $nid to store {workflow_node_history}. * We cannot use hook_field_presave(), since $nid is not yet known at that moment. * * "Contrary to the old D7 hooks, the methods do not receive the parent entity * "or the langcode of the field values as parameters. If needed, those can be accessed * "by the getEntity() and getLangcode() methods on the Field and FieldItem classes. */ public function update(&$items) { // ($entity_type, $entity, $field, $instance, $langcode, &$items) { // @todo: apparentlly, in course of time, this is not used anymore. Restore or remove. $field_name = $this->field['field_name']; $wid = $this->field['settings']['wid']; $new_state = WorkflowState::load($sid = _workflow_get_sid_by_items($items), $wid); // @todo D8: remove below lines. $entity = $this->entity; $entity_type = $this->entity_type; $entity_id = entity_id($entity_type, $entity); if (!$entity) { // No entity available, we are on the field Settings page - 'default value' field. // This is hidden from the admin, because the default value can be different for every user. } elseif (!$entity_id && $entity_type == 'comment') { // Not possible: a comment on a non-existent node. } elseif ($entity_id && $this->entity_type == 'comment') { // This happens when we are on an entity's comment. $referenced_entity_type = 'node'; // Comments only exist on nodes. $referenced_entity = entity_load_single($referenced_entity_type, $entity_id); // Comments only exist on nodes. // Submit the data. $items is reset by reference to normal value, and is magically saved by the field itself. $form = array(); $form_state = array(); $widget = new WorkflowDefaultWidget($this->field, $this->instance, $referenced_entity_type, $referenced_entity); $widget->submit($form, $form_state, $items); // $items is a proprietary D7 parameter. // Remember: we are on a comment form, so the comment is saved automatically, but the referenced entity is not. // @todo: probably we'd like to do this form within the Widget, but that does not know // wether we are on a comment or a node form. // // Widget::submit() returns the new value in a 'sane' state. // Save the referenced entity, but only is transition succeeded, and is not scheduled. $old_sid = _workflow_get_sid_by_items($referenced_entity->{$field_name}['und']); $new_sid = _workflow_get_sid_by_items($items); if ($old_sid != $new_sid) { $referenced_entity->{$field_name}['und'] = $items; entity_save($referenced_entity_type, $referenced_entity); } } elseif ($this->entity_type != 'comment') { if (isset($items[0]['value'])) { // A 'normal' options.module-widget is used, and $items[0]['value'] is already properly set. } elseif (isset($items[0]['workflow'])) { // The WorkflowDefaultWidget is used. // Submit the data. $items is reset by reference to normal value, and is magically saved by the field itself. $form = array(); $form_state = array(); $widget = new WorkflowDefaultWidget($this->field, $this->instance, $entity_type, $entity); $widget->submit($form, $form_state, $items); // $items is a proprietary D7 parameter. } else { drupal_set_message('error in WorkfowItem->update()', 'error'); } } // A 'normal' node add page. // We should not be here, since we only do inserts after $entity_id is known. // $current_sid = Workflow::load($wid)->getCreationSid(); }
/** * Responds after order product deletion. * * This hook is invoked after the order product has been removed from the * database. * * @param object $order_product * The order product that is being deleted. * * @see hook_entity_delete() * @see hook_uc_order_edit_form_product_remove() */ function hook_uc_order_product_delete(object $order_product) { db_delete('mytable')->condition('opid', entity_id('uc_order_product', $order_product))->execute(); }
/** * Implements workflow_transition() -> WorkflowDefaultWidget::submit(). * * Overrides submit(array $form, array &$form_state). * Contains 2 extra parameters for D7 * * @param array $form * @param array $form_state * @param array $items * The value of the field. * @param bool $force * TRUE if all access must be overridden, e.g., for Rules. * * @return int * If update succeeded, the new State Id. Else, the old Id is returned. * * This is called from function _workflowfield_form_submit($form, &$form_state) * It is a replacement of function workflow_transition($node, $new_sid, $force, $field) * It performs the following actions; * - save a scheduled action * - update history * - restore the normal $items for the field. * @todo: remove update of {node_form} table. (separate task, because it has features, too) */ public function submit(array $form, array &$form_state, array &$items, $force = FALSE) { global $user; // @todo #2287057: verify if submit() really is only used for UI. If not, $user must be passed. $entity_type = $this->entity_type; $entity = $this->entity; $field_name = isset($this->field['field_name']) ? $this->field['field_name'] : ''; // Extract the data from $items, depending on the type of widget. // @todo D8: use MassageFormValues($values, $form, $form_state). $old_sid = workflow_node_previous_state($entity, $entity_type, $field_name); if (!$old_sid) { // At this moment, $old_sid should have a value. If the content does not // have a state yet, old_sid contains '(creation)' state. But if the // content is not associated to a workflow, old_sid is now 0. This may // happen in workflow_vbo, if you assign a state to non-relevant nodes. $entity_id = entity_id($entity_type, $entity); drupal_set_message(t('Error: content !id has no workflow attached. The data is not saved.', array('!id' => $entity_id)), 'error'); // The new state is still the previous state. $new_sid = $old_sid; return $new_sid; } $transition = $this->getTransition($old_sid, $items, $field_name, $user); $force = $force || $transition->isForced(); // Try to execute the transition. Return $old_sid when error. if (!$transition) { // This should only happen when testing/developing. drupal_set_message(t('Error: the transition from %old_sid to %new_sid could not be generated.'), 'error'); // The current value is still the previous state. $new_sid = $old_sid; } elseif (!$transition->isScheduled()) { // Now the data is captured in the Transition, and before calling the // Execution, restore the default values for Workflow Field. // For instance, workflow_rules evaluates this. if ($field_name) { // $items = array(); // $items[0]['value'] = $old_sid; // $entity->{$field_name}[$transition->language] = $items; } // It's an immediate change. Do the transition. // - validate option; add hook to let other modules change comment. // - add to history; add to watchdog // Return the new State ID. (Execution may fail and return the old Sid.) $new_sid = $transition->execute($force); } else { // A scheduled transition must only be saved to the database. // The entity is not changed. $transition->save(); // The current value is still the previous state. $new_sid = $old_sid; } // The entity is still to be saved, so set to a 'normal' value. if ($field_name) { $items = array(); $items[0]['value'] = $new_sid; $entity->{$field_name}[$transition->language] = $items; } return $new_sid; }
/** * Responds to rules link deletion. * * This hook is invoked after the rules link has been removed from the database. * * @param RulesLink $rules_link * The rules link that is being deleted. * * @see hook_entity_delete() */ function hook_rules_link_delete(RulesLink $rules_link) { db_delete('mytable')->condition('pid', entity_id('rules_link', $rules_link))->execute(); }
/** * Responds to application_type deletion. * * This hook is invoked after the application_type has been removed from the database. * * @param ApplicationType $application_type * The application_type that is being deleted. * * @see hook_entity_delete() */ function hook_application_type_delete(ApplicationType $application_type) { db_delete('mytable')->condition('pid', entity_id('application_type', $application_type))->execute(); }
/** * Responds to example_task_type deletion. * * This hook is invoked after the example_task_type has been removed from the database. * * @param ExampleTaskType $example_task_type * The example_task_type that is being deleted. * * @see hook_entity_delete() */ function hook_example_task_type_delete(ExampleTaskType $example_task_type) { db_delete('mytable')->condition('pid', entity_id('example_task_type', $example_task_type))->execute(); }
/** * Responds to search deletion. * * This hook is invoked after the search has been removed from the database. * * @param SearchApiAutocompleteSearch $search * The search that is being deleted. * * @see hook_entity_delete() */ function hook_search_api_autocomplete_search_delete(SearchApiAutocompleteSearch $search) { db_delete('mytable')->condition('pid', entity_id('search_api_autocomplete_search', $search))->execute(); }
/** * Responds to datasource_type deletion. * * This hook is invoked after the datasource_type has been removed from the database. * * @param DatasourceType $datasource_type * The datasource_type that is being deleted. * * @see hook_entity_delete() */ function hook_datasource_type_delete(DatasourceType $datasource_type) { db_delete('mytable')->condition('pid', entity_id('datasource_type', $datasource_type))->execute(); }
/** * Responds to publication_type deletion. * * This hook is invoked after the publication_type has been removed from the database. * * @param PublicationType $publication_type * The publication_type that is being deleted. * * @see hook_entity_delete() */ function hook_publication_type_delete(PublicationType $publication_type) { db_delete('mytable')->condition('pid', entity_id('publication_type', $publication_type))->execute(); }
/** * Responds to openbadging_type deletion. * * This hook is invoked after the openbadging_type has been removed from the database. * * @param OpenbadgingEntityType $openbadging_type * The openbadging_type that is being deleted. * * @see hook_entity_delete() */ function hook_openbadging_type_delete(OpenbadgingEntityType $openbadging_type) { db_delete('mytable')->condition('pid', entity_id('openbadging_type', $openbadging_type))->execute(); }
/** * Responds to spotify_playlist deletion. * * This hook is invoked after the spotify_playlist has been removed from the database. * * @param SpotifyPlaylist $spotify_playlist * The spotify_playlist that is being deleted. * * @see hook_entity_delete() */ function hook_spotify_playlist_type_delete(SpotifyPlaylist $spotify_playlist) { db_delete('mytable')->condition('pid', entity_id('spotify_playlist', $spotify_playlist))->execute(); }
/** * Responds to settings entity deletion. * * This hook is invoked after the settings entity has been removed from the * database. * * @param SearchApiSavedSearchSettings $settings * The settings entity that is being deleted. * * @see hook_entity_delete() */ function hook_search_api_saved_search_settings_delete(SearchApiSavedSearchSettings $settings) { db_delete('mytable')->condition('pid', entity_id('search_api_saved_search_settings', $settings))->execute(); }
/** * Implements hook_field_delete() -> FieldItemInterface::delete(). */ public function delete($items) { global $user; $entity_type = $this->entity_type; $entity = $this->entity; $entity_id = entity_id($entity_type, $entity); $field_name = $this->field['field_name']; // Delete the record in {workflow_node} - not for Workflow Field. // Use a one-liner for better code analysis when grepping on old code. !$field_name ? workflow_delete_workflow_node_by_nid($entity_id) : NULL; // Add a history record in {workflow_node_history}. // @see drupal.org/node/2165349, comment by Bastlynn: // The reason for this history log upon delete is because Workflow module // has historically been used to track node states and accountability in // business environments where accountability for changes over time is // *absolutely* required. Think banking and/or particularly strict // retention policies for legal reasons. // // However, a deleted nid may be re-used under certain circumstances: // e.g., working with InnoDB or after restart the DB server. // This may cause that old history is associated with a new node. $old_sid = _workflow_get_sid_by_items($items); $new_sid = (int) WORKFLOW_DELETION; $comment = t('Entity deleted.'); $transition = new WorkflowTransition(); $transition->setValues($entity_type, $entity, $field_name, $old_sid, $new_sid, $user->uid, REQUEST_TIME, $comment); $transition->save(); // Delete all records for this node in {workflow_scheduled_transition}. foreach (WorkflowScheduledTransition::load($entity_type, $entity_id, $field_name) as $scheduled_transition) { $scheduled_transition->delete(); } }
/** * Returns the allowed values for the current state. * * @deprecated workflow_field_choices() --> WorkflowState->getOptions() */ public function getOptions($entity_type, $entity, $force = FALSE) { global $user; static $cache = array(); // Entity-specific cache per page load. $options = array(); if (!$entity) { // If no entity is given, no result (e.g., on a Field settings page). $options = array(); return $options; } $entity_id = entity_id($entity_type, $entity); $current_sid = $this->sid; // Get options from page cache. if (isset($cache[$entity_type][$entity_id][$force][$current_sid])) { $options = $cache[$entity_type][$entity_id][$force][$current_sid]; return $options; } $workflow = Workflow::load($this->wid); if ($workflow) { $roles = array_keys($user->roles); // If this is a new page, give the authorship role. if (!$entity_id) { $roles = array_merge(array('author'), $roles); } elseif (isset($entity->uid) && $entity->uid == $user->uid && $user->uid > 0) { $roles = array_merge(array('author'), $roles); } // Superuser is special. And $force allows Rules to cause transition. if ($user->uid == 1 || $force) { $roles = 'ALL'; } // Workflow_allowable_transitions() does not return the entire transition row. Would like it to, but doesn't. // Instead it returns just the allowable data as: // [tid] => 1 [state_id] => 1 [state_name] => (creation) [state_weight] => -50 $transitions = workflow_allowable_transitions($current_sid, 'to', $roles); // Include current state if it is not the (creation) state. foreach ($transitions as $transition) { if ($transition->sysid != WORKFLOW_CREATION && !$force) { // Invoke a callback indicating that we are collecting state choices. // Modules may veto a choice by returning FALSE. // In this case, the choice is never presented to the user. // @todo: for better performance, call a hook only once: can we find a way to pass all transitions at once $result = module_invoke_all('workflow', 'transition permitted', $current_sid, $transition->state_id, $entity, $field_name = ''); // Did anybody veto this choice? if (!in_array(FALSE, $result)) { // If not vetoed, add to list. $options[$transition->state_id] = check_plain(t($transition->state_name)); } } } // Save to entity-specific cache. $cache[$entity_type][$entity_id][$force][$current_sid] = $options; } return $options; }
/** * Returns the allowed values for the current state. * * @param string $entity_type * The type of the entity at hand. * @param object $entity * The entity at hand. May be NULL (E.g., on a Field settings page). * @param $field_name * @param $user * @param bool $force * * @return array * An array of sid=>label pairs. * If $this->sid is set, returns the allowed transitions from this state. * If $this->sid is 0 or FALSE, then labels of ALL states of the State's * Workflow are returned. * * D7.x-2.x: deprecated workflow_field_choices() --> WorkflowState->getOptions() */ public function getOptions($entity_type, $entity, $field_name, $user, $force = FALSE) { // Define an Entity-specific cache per page load. static $cache = array(); $options = array(); $entity_id = $entity ? entity_id($entity_type, $entity) : ''; $current_sid = $this->sid; // Get options from page cache, using a non-empty index (just to be sure). $entity_index = !$entity ? 'x' : $entity_id; if (isset($cache[$entity_type][$entity_index][$force][$current_sid])) { $options = $cache[$entity_type][$entity_index][$force][$current_sid]; return $options; } $workflow = $this->getWorkflow(); if (!$workflow) { // No workflow, no options ;-) } elseif (!$current_sid) { // If no State ID is given, we return all states. // We cannot use getTransitions, since there are no ConfigTransitions // from State with ID 0, and we do not want to repeat States. foreach ($workflow->getStates() as $state) { $options[$state->value()] = $state->label(); // Translation is done later. } } else { /* @var $transition WorkflowTransition */ $transitions = $this->getTransitions($entity_type, $entity, $field_name, $user, $force); foreach ($transitions as $transition) { // Get the label of the transition, and if empty of the target state. // Beware: the target state may not exist, since it can be invented // by custom code in the above drupal_alter() hook. if (!($label = $transition->label())) { $target_state = $transition->getNewState(); $label = $target_state ? $target_state->label() : ''; } $new_sid = $transition->target_sid; $options[$new_sid] = $label; // Translation is done later. } // Include current state for same-state transitions, except when $sid = 0. // Caveat: this unnecessary since 7.x-2.3 (where stay-on-state transitions are saved, too.) // but only if the transitions have been saved at least one time. if ($current_sid && $current_sid != $workflow->getCreationSid()) { if (!isset($options[$current_sid])) { $options[$current_sid] = $this->label(); // Translation is done later. } } // Properly fix the labels. // Translate, convert '&', make secure. foreach ($options as $key => $label) { $options[$key] = html_entity_decode(check_plain(t($label))); } // Save to entity-specific cache. $cache[$entity_type][$entity_index][$force][$current_sid] = $options; } return $options; }
/** * Responds to recruit_application_type deletion. * * This hook is invoked after the recruit_application_type has been removed from the database. * * @param RecruitApplicationType $recruit_application_type * The recruit_application_type that is being deleted. * * @see hook_entity_delete() */ function hook_recruit_application_type_delete(RecruitApplicationType $recruit_application_type) { db_delete('mytable')->condition('pid', entity_id('recruit_application_type', $recruit_application_type))->execute(); }
/** * Responds to OG membership deletion. * * This hook is invoked after the OG membership has been removed from the database. * * @param OgMembership $og_membership * The OG membership that is being deleted. * * @see hook_entity_delete() */ function hook_og_membership_delete(OgMembership $og_membership) { db_delete('mytable')->condition('pid', entity_id('og_membership', $og_membership))->execute(); }
/** * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state, array &$items) { // $items is a D7 parameter. // @todo: clean this code up. It is the result of gluing code together. global $user; // @todo #2287057: verify if submit() really is only used for UI. If not, $user must be passed. $entity = $this->entity; $entity_type = $this->entity_type; $field = $this->field; $field_name = $field['field_name']; // Retrieve the data from the form. if (isset($form_state['values']['workflow_field'])) { // If $entity filled: We are on a Entity View page or Workflow History Tab page. // If $entity empty: We are on an Advanced Action page. // $field = $form_state['values']['workflow_field']; // $instance = $form_state['values']['workflow_instance']; // $entity_type = $form_state['values']['workflow_entity_type']; // $entity = $form_state['values']['workflow_entity']; // $field_name = $field['field_name']; } elseif (isset($form_state['triggering_element'])) { // We are on an Entity/Node/Comment Form page (add/edit). $field_name = $form_state['triggering_element']['#workflow_field_name']; } else { // We are on an Entity/Comment Form page (add/edit). } // Determine if the transition is forced. // This can be set by a 'workflow_vbo action' in an additional form element. $force = isset($form_state['input']['workflow_force']) ? $form_state['input']['workflow_force'] : FALSE; // Set language. Multi-language is not supported for Workflow Node. $langcode = _workflow_metadata_workflow_get_properties($entity, array(), 'langcode', $entity_type, $field_name); if (!$entity) { // E.g., on VBO form. } elseif ($field_name) { // Save the entity, but only if we were not in edit mode. // Perhaps there is a better way, but for now we use 'changed' property. // Also test for 'is_new'. When Migrating content, the 'changed' property may be set externally. // Caveat: Some entities do not have 'changed' property set. if (!empty($entity->is_new) || isset($entity->changed) && $entity->changed == REQUEST_TIME) { // We are in add/edit mode. No need to save the entity explicitly. // // Add the $form_state to the $items, so we can do a getTransition() later on. // $items[0]['workflow'] = $form_state['input']; // // Create a Transition. The Widget knows if it is scheduled. // $widget = new WorkflowDefaultWidget($field, $instance, $entity_type, $entity); // $new_sid = $widget->submit($form, $form_state, $items, $force); } elseif (isset($form_state['input'])) { // Save $entity, but only if sid has changed. // Use field_attach_update for this? Save always? $entity->{$field_name}[$langcode][0]['workflow'] = $form_state['input']; // @todo & totest: Save ony the field, not the complete entity. // workflow_entity_field_save($entity_type, $entity, $field_name, $langcode, FALSE); entity_save($entity_type, $entity); return; // <---- exit! } else { // We are saving a node from a comment. $entity->{$field_name}[$langcode] = $items; // @todo & totest: Save ony the field, not the complete entity. // workflow_entity_field_save($entity_type, $entity, $field_name, $langcode, FALSE); entity_save($entity_type, $entity); return; // <---- exit! } } else { // For a Node API form, only contrib fields need to be filled. // No updating of the node itself. // (Unless we need to record the timestamp.) // Add the $form_state to the $items, so we can do a getTransition() later on. $items[0]['workflow'] = $form_state['input']; // // Create a Transition. The Widget knows if it is scheduled. // $widget = new WorkflowDefaultWidget($field, $instance, $entity_type, $entity); // $new_sid = $widget->submit($form, $form_state, $items, $force); } // Extract the data from $items, depending on the type of widget. // @todo D8: use MassageFormValues($values, $form, $form_state). $old_sid = workflow_node_previous_state($entity, $entity_type, $field_name); if (!$old_sid) { // At this moment, $old_sid should have a value. If the content does not // have a state yet, old_sid contains '(creation)' state. But if the // content is not associated to a workflow, old_sid is now 0. This may // happen in workflow_vbo, if you assign a state to non-relevant nodes. $entity_id = entity_id($entity_type, $entity); drupal_set_message(t('Error: content !id has no workflow attached. The data is not saved.', array('!id' => $entity_id)), 'error'); // The new state is still the previous state. $new_sid = $old_sid; return $new_sid; } // Now, save/execute the transition. $transition = $this->getTransition($old_sid, $items, $field_name, $user, $form, $form_state); // Try to execute the transition. Return $old_sid when error. if (!$transition) { // This should only happen when testing/developing. drupal_set_message(t('Error: the transition from %old_sid to %new_sid could not be generated.'), 'error'); // The current value is still the previous state. $new_sid = $old_sid; } elseif ($transition->isScheduled() || $transition->isExecuted()) { // A scheduled or executed transition must only be saved to the database. // The entity is not changed. $force = $force || $transition->isForced(); $transition->save(); // The current value is still the previous state. $new_sid = $old_sid; } elseif (!$transition->isScheduled()) { // Now the data is captured in the Transition, and before calling the // Execution, restore the default values for Workflow Field. // For instance, workflow_rules evaluates this. if ($field_name) { // $items = array(); // $items[0]['value'] = $old_sid; // $entity->{$field_name}[$transition->language] = $items; } // It's an immediate change. Do the transition. // - validate option; add hook to let other modules change comment. // - add to history; add to watchdog // Return the new State ID. (Execution may fail and return the old Sid.) $force = $force || $transition->isForced(); $new_sid = $transition->execute($force); } // The entity is still to be saved, so set to a 'normal' value. if ($field_name) { $items = array(); $items[0]['value'] = $new_sid; $entity->{$field_name}[$transition->language] = $items; } return $new_sid; }
/** * Implements hook_field_widget_form --> WidgetInterface::formElement(). * * {@inheritdoc} * * Be careful: this widget may be shown in very different places. Test carefully!! * - On a entity add/edit page * - On a entity preview page * - On a entity view page * - On a entity 'workflow history' tab * - On a comment display, in the comment history * - On a comment form, below the comment history * @todo D8: change "array $items" to "FieldInterface $items" */ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { $field_name = $this->field['field_name']; $field = $this->instance; $instance = $this->instance; $entity = $this->entity; $entity_type = $this->entity_type; $entity_id = entity_id($entity_type, $entity); if (!$entity) { // If no entity given, do not show a form. E.g., on the field settings page. return $element; } // Capture settings to format the form/widget. $settings_title_as_name = !empty($this->field['settings']['widget']['name_as_title']); // The schedule cannot be shown on a Content add page. $settings_schedule = !empty($this->field['settings']['widget']['schedule']) && $entity_id; $settings_schedule_timezone = !empty($this->field['settings']['widget']['schedule_timezone']); // Show comment, when both Field and Instance allow this. $settings_comment = $this->field['settings']['widget']['comment'] ? 'textarea' : 'hidden'; $workflow = Workflow::load($this->field['settings']['wid']); $current_sid = workflow_node_current_state($entity, $entity_type, $field_name); $current_state = WorkflowState::load($current_sid); $options = array(); $options = $current_state->getOptions($entity_type, $entity); // Determine the default value. If we are in CreationState, use a fast alternative for $workflow->getFirstSid(). $default_value = $current_state->isCreationState() ? key($options) : $current_sid; // Get the scheduling info. This may change the $current_sid on the Form. $scheduled = '0'; $timestamp = REQUEST_TIME; $comment = NULL; if ($settings_schedule) { // Read scheduled information. // Technically you could have more than one scheduled, but this will only add the soonest one. foreach (WorkflowScheduledTransition::load($entity_type, $entity_id, $field_name) as $scheduled_transition) { $scheduled = '1'; $current_sid = $scheduled_transition->sid; $timestamp = $scheduled_transition->scheduled; $comment = $scheduled_transition->comment; break; } } // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.). $form_id = $form_state['build_info']['form_id']; $element_scheduled_name = 'workflow_scheduled_' . $form_id; $element_options_name = 'workflow_options_' . $form_id; $element_scheduled_name = 'workflow_scheduled'; $element_options_name = 'workflow_options'; $elt_state_name = 'workflow_scheduled_' . $form_id; $label = $workflow->label(); // Prepare a wrapper. This might be a fieldset. $element['workflow']['#type'] = 'container'; $element['workflow']['#attributes'] = array('class' => array('workflow-form-container')); // Save the current value of the node in the form, for later Workflow-module specific references. // We add prefix, since #tree == FALSE. $element['workflow']['workflow_entity'] = array('#type' => 'value', '#value' => $this->entity); $element['workflow']['workflow_entity_type'] = array('#type' => 'value', '#value' => $this->entity_type); $element['workflow']['workflow_field'] = array('#type' => 'value', '#value' => $this->field); $element['workflow']['workflow_instance'] = array('#type' => 'value', '#value' => $this->instance); // Save the form_id, so the form values can be retrieved in submit function. $element['workflow']['form_id'] = array('#type' => 'value', '#value' => $form_id); // First of all, we add the default value in the place were normal fields // have it. This is to cater for 'preview' of the entity. $element['#default_value'] = $default_value; // Decide if we show a widget or a formatter. // There is no need to a widget when the only choice is the current sid. if (!$current_state->showWidget($options)) { $element['workflow'][$element_options_name] = workflow_state_formatter($entity_type, $entity, $field, $instance); return $element; } else { $element['workflow'][$element_options_name] = array('#type' => $this->field['settings']['widget']['options'], '#title' => $settings_title_as_name ? t('Change !name state', array('!name' => $label)) : '', '#options' => $options, '#default_value' => $default_value); } // Display scheduling form, but only if entity is being edited and user has // permission. State change cannot be scheduled at entity creation because // that leaves the entity in the (creation) state. if ($settings_schedule == TRUE && user_access('schedule workflow transitions')) { global $user; if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) { $timezone = $user->timezone; } else { $timezone = variable_get('date_default_timezone', 0); } $timezones = drupal_map_assoc(timezone_identifiers_list()); $hours = format_date($timestamp, 'custom', 'H:i', $timezone); // $element['workflow']['workflow_scheduled'] = array( $element['workflow'][$element_scheduled_name] = array('#type' => 'radios', '#title' => t('Schedule'), '#options' => array('0' => t('Immediately'), '1' => t('Schedule for state change')), '#default_value' => $scheduled, '#attributes' => array('id' => 'scheduled_' . $form_id)); $element['workflow']['workflow_scheduled_date_time'] = array('#type' => 'fieldset', '#title' => t('At'), '#attributes' => array('class' => array('container-inline')), '#prefix' => '<div style="margin-left: 1em;">', '#suffix' => '</div>', '#states' => array('visible' => array(':input[id="' . 'scheduled_' . $form_id . '"]' => array('value' => '1')))); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_date'] = array('#type' => 'date', '#default_value' => array('day' => date('j', $timestamp), 'month' => date('n', $timestamp), 'year' => date('Y', $timestamp))); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_hour'] = array('#type' => 'textfield', '#title' => t('Time'), '#maxlength' => 5, '#size' => 6, '#default_value' => $scheduled ? $hours : '00:00'); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_timezone'] = array('#type' => $settings_schedule_timezone ? 'select' : 'hidden', '#title' => t('Time zone'), '#options' => $timezones, '#default_value' => array($timezone => $timezone)); $element['workflow']['workflow_scheduled_date_time']['workflow_scheduled_help'] = array('#type' => 'item', '#prefix' => '<br />', '#description' => t('Please enter a time in 24 hour (eg. HH:MM) format. If no time is included, the default will be midnight on the specified date. The current time is: @time.', array('@time' => format_date(REQUEST_TIME, 'custom', 'H:i', $timezone)))); } $element['workflow']['workflow_comment'] = array('#type' => $settings_comment, '#title' => t('Workflow comment'), '#description' => t('A comment to put in the workflow log.'), '#default_value' => $comment, '#rows' => 2); // The 'add submit' can explicitely set by workflowfield_field_formatter_view(), // to add the submit button on the Content view page and the Workflow history tab. if (!empty($this->instance['widget']['settings']['submit_function'])) { // Add a submit button, but only on Entity View and History page. $element['workflow']['submit'] = array('#type' => 'submit', '#value' => t('Update workflow'), '#executes_submit_callback' => TRUE, '#submit' => array($this->instance['widget']['settings']['submit_function'])); } return $element; }
/** * Set the Transitions $entity. * * @param $entity_type * the entity type of the entity. * @param $entity * the Entity ID or the Entity object, to add to the Transition. * * @return object $entity * the entity, that is added to the Transition. */ public function setEntity($entity_type, $entity) { if (!is_object($entity)) { $entity_id = $entity; // Use node API or Entity API to load the object first. $entity = entity_load_single($entity_type, $entity_id); } $this->entity = $entity; $this->entity_type = $entity_type; $this->entity_id = entity_id($entity_type, $entity); $this->nid = $this->entity_id; return $this->entity; }