1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297 |
- <?php
- /**
- * @file
- * Contains core Rules UI functions.
- */
- /**
- * Plugin UI Interface.
- */
- interface RulesPluginUIInterface {
- /**
- * Adds the whole configuration form of this rules configuration.
- *
- * For rule elements that are part of a configuration this method just adds
- * the elements configuration form.
- *
- * @param $form
- * The form array where to add the form.
- * @param $form_state
- * The current form state.
- * @param $options
- * An optional array of options with the known keys:
- * - 'show settings': Whether to include the 'settings' fieldset for
- * editing configuration settings like the label or categories. Defaults
- * to FALSE.
- * - 'button': Whether a submit button should be added. Defaults to FALSE.
- * - 'init': Whether the element is about to be configured the first time
- * and the configuration is about to be initialized. Defaults to FALSE.
- * - 'restrict plugins: May be used to restrict the list of rules plugins
- * that may be added to this configuration. For that set an array of
- * valid plugins. Note that conditions and actions are always valid, so
- * just set an empty array for just allowing those.
- * - 'restrict conditions': Optionally set an array of condition names to
- * restrict the conditions that are available for adding.
- * - 'restrict actions': Optionally set an array of action names to
- * restrict the actions that are available to for adding.
- * - 'restrict events': Optionally set an array of event names to restrict
- * the events that are available for adding.
- *
- * @todo Implement the 'restrict *' options.
- */
- public function form(&$form, &$form_state, $options = array());
- /**
- * Validate the configuration form of this rule element.
- *
- * @param $form
- * @param $form_state
- */
- public function form_validate($form, &$form_state);
- /**
- * Form submit handler for the element configuration form.
- *
- * Submit the configuration form of this rule element. This makes sure to
- * put the updated configuration in the form state. For saving changes
- * permanently, just call $config->save() afterwards.
- *
- * @param $form
- * @param $form_state
- */
- public function form_submit($form, &$form_state);
- /**
- * Returns a structured array for rendering this element in overviews.
- */
- public function buildContent();
- /**
- * Returns the help text for editing this plugin.
- */
- public function help();
- /**
- * Returns ui operations for this element.
- */
- public function operations();
- }
- /**
- * Helper object for mapping elements to ids.
- */
- class RulesElementMap {
- /**
- * @var RulesPlugin
- */
- protected $configuration;
- protected $index = array();
- protected $counter = 0;
- public function __construct(RulesPlugin $config) {
- $this->configuration = $config->root();
- }
- /**
- * Makes sure each element has an assigned id.
- */
- public function index() {
- foreach ($this->getUnIndexedElements($this->configuration) as $element) {
- $id = &$element->property('elementId');
- $id = ++$this->counter;
- $this->index[$id] = $element;
- }
- }
- protected function getUnIndexedElements($element, &$unindexed = array()) {
- // Remember unindexed elements.
- $id = $element->property('elementId');
- if (!isset($id)) {
- $unindexed[] = $element;
- }
- else {
- // Make sure $this->counter refers to the highest id.
- if ($id > $this->counter) {
- $this->counter = $id;
- }
- $this->index[$id] = $element;
- }
- // Recurse down the tree.
- if ($element instanceof RulesContainerPlugin) {
- foreach ($element as $child) {
- $this->getUnIndexedElements($child, $unindexed);
- }
- }
- return $unindexed;
- }
- /**
- * Looks up the element with the given id.
- */
- public function lookup($id) {
- if (!$this->index) {
- $this->index();
- }
- return isset($this->index[$id]) ? $this->index[$id] : FALSE;
- }
- }
- /**
- * Faces UI extender for all kind of Rules plugins.
- *
- * Provides various useful methods for any rules UI.
- */
- class RulesPluginUI extends FacesExtender implements RulesPluginUIInterface {
- /**
- * @var RulesPlugin
- */
- protected $element;
- /**
- * The base path determines where a Rules overview UI lives.
- *
- * All forms that want to display Rules (overview) forms need to set this
- * variable. This is necessary in order to get correct operation links,
- * paths, redirects, breadcrumbs etc. for the form() and overviewTable() methods.
- *
- * @see RulesUIController
- * @see rules_admin_reaction_overview()
- * @see rules_admin_components_overview()
- */
- public static $basePath = NULL;
- /**
- * Provide $this->element to make the code more meaningful.
- */
- public function __construct(FacesExtendable $object) {
- parent::__construct($object);
- $this->element = $object;
- }
- /**
- * Returns the form values for the given form, possible being only a part of the whole form.
- *
- * In case the form is embedded somewhere, this function figures out the
- * location of its form values and returns them for further use.
- *
- * @param $form
- * A form array, or an array of form elements to get the value for.
- * @param $form_state
- * The form state as usual.
- */
- public static function &getFormStateValues($form, &$form_state) {
- $values = NULL;
- if (isset($form_state['values'])) {
- // Assume the top level if parents are not yet set.
- $form += array('#parents' => array());
- $values = &$form_state['values'];
- foreach ($form['#parents'] as $parent) {
- $values = &$values[$parent];
- }
- }
- return $values;
- }
- /**
- * Implements RulesPluginUIInterface. Generates the element edit form.
- *
- * Note: Make sure that you set RulesPluginUI::$basePath before using this
- * method, otherwise paths, links, redirects etc. won't be correct.
- */
- public function form(&$form, &$form_state, $options = array()) {
- self::formDefaults($form, $form_state);
- $form_state += array('rules_element' => $this->element);
- // Add the help to the top of the form.
- $help = $this->element->help();
- $form['help'] = is_array($help) ? $help : array('#markup' => $help);
- // We use $form_state['element_settings'] to store the settings of both
- // parameter modes. That way one can switch between the parameter modes
- // without losing the settings of those.
- $form_state += array('element_settings' => $this->element->settings);
- $settings = $this->element->settings + $form_state['element_settings'];
- $form['parameter'] = array(
- '#tree' => TRUE,
- );
- foreach ($this->element->pluginParameterInfo() as $name => $parameter) {
- if ($parameter['type'] == 'hidden') {
- continue;
- }
- $form['parameter'][$name] = array(
- '#type' => 'fieldset',
- '#title' => check_plain($parameter['label']),
- '#description' => filter_xss(isset($parameter['description']) ? $parameter['description'] : ''),
- );
- // Init the parameter input mode.
- $form_state['parameter_mode'][$name] = !isset($form_state['parameter_mode'][$name]) ? NULL : $form_state['parameter_mode'][$name];
- $form['parameter'][$name] += $this->getParameterForm($name, $parameter, $settings, $form_state['parameter_mode'][$name]);
- }
- // Provide a form for editing the label and name of provided variables.
- $settings = $this->element->settings;
- foreach ($this->element->pluginProvidesVariables() as $var_name => $var_info) {
- $form['provides'][$var_name] = array(
- '#type' => 'fieldset',
- '#title' => check_plain($var_info['label']),
- '#description' => filter_xss(isset($var_info['description']) ? $var_info['description'] : ''),
- );
- $form['provides'][$var_name]['label'] = array(
- '#type' => 'textfield',
- '#title' => t('Variable label'),
- '#default_value' => isset($settings[$var_name . ':label']) ? $settings[$var_name . ':label'] : $var_info['label'],
- '#required' => TRUE,
- );
- $form['provides'][$var_name]['var'] = array(
- '#type' => 'textfield',
- '#title' => t('Variable name'),
- '#default_value' => isset($settings[$var_name . ':var']) ? $settings[$var_name . ':var'] : $var_name,
- '#description' => t('The variable name must contain only lowercase letters, numbers, and underscores and must be unique in the current scope.'),
- '#element_validate' => array('rules_ui_element_machine_name_validate'),
- '#required' => TRUE,
- );
- }
- if (!empty($form['provides'])) {
- $help = '<div class="description">' . t('Adjust the names and labels of provided variables, but note that renaming of already utilized variables invalidates the existing uses.') . '</div>';
- $form['provides'] += array(
- '#tree' => TRUE,
- '#prefix' => '<h4 class="rules-form-heading">' . t('Provided variables') . '</h4>' . $help,
- );
- }
- // Add settings form, if specified.
- if (!empty($options['show settings'])) {
- $this->settingsForm($form, $form_state);
- }
- // Add submit button, if specified.
- if (!empty($options['button'])) {
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- '#weight' => 10,
- );
- }
- }
- /**
- * Actually generates the parameter form for the given data type.
- */
- protected function getParameterForm($name, $info, $settings, &$mode) {
- $class = $this->getDataTypeClass($info['type'], $info);
- $supports_input_mode = in_array('RulesDataDirectInputFormInterface', class_implements($class));
- // Init the mode.
- if (!isset($mode)) {
- if (isset($settings[$name . ':select'])) {
- $mode = 'selector';
- }
- elseif (isset($settings[$name]) && $supports_input_mode) {
- $mode = 'input';
- }
- elseif (isset($info['restriction'])) {
- $mode = $info['restriction'];
- }
- else {
- // Allow the parameter to define the 'default mode' and fallback to the
- // data type default.
- $mode = !empty($info['default mode']) ? $info['default mode'] : call_user_func(array($class, 'getDefaultMode'));
- }
- }
- // For translatable parameters, pre-populate an internal translation source
- // key so data type forms or input evaluators (i18n) may show a suitable
- // help message.
- if (drupal_multilingual() && !empty($info['translatable'])) {
- $parameter = $this->element->pluginParameterInfo();
- $info['custom translation language'] = !empty($parameter['language']);
- }
- // Add the parameter form.
- if ($mode == 'input' && $supports_input_mode) {
- $form['settings'] = call_user_func(array($class, 'inputForm'), $name, $info, $settings, $this->element);
- }
- else {
- $form['settings'] = call_user_func(array($class, 'selectionForm'), $name, $info, $settings, $this->element);
- }
- // Add a link for switching the input mode when JS is enabled and a button
- // to switch it without JavaScript, in case switching is possible.
- if ($supports_input_mode && empty($info['restriction'])) {
- $value = $mode == 'selector' ? t('Switch to the direct input mode') : t('Switch to data selection');
- $form['switch_button'] = array(
- '#type' => 'submit',
- '#name' => 'param_' . $name,
- '#attributes' => array('class' => array('rules-switch-button')),
- '#parameter' => $name,
- '#value' => $value,
- '#submit' => array('rules_ui_parameter_replace_submit'),
- '#ajax' => rules_ui_form_default_ajax('none'),
- // Do not validate!
- '#limit_validation_errors' => array(),
- );
- }
- return $form;
- }
- /**
- * Implements RulesPluginUIInterface.
- */
- public function form_validate($form, &$form_state) {
- $this->form_extract_values($form, $form_state);
- $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
- if (isset($form_values['provides'])) {
- $vars = $this->element->availableVariables();
- foreach ($form_values['provides'] as $name => $values) {
- if (isset($vars[$values['var']])) {
- form_error($form['provides'][$name]['var'], t('The variable name %name is already taken.', array('%name' => $values['var'])));
- }
- }
- }
- // Settings have been updated, so process them now.
- $this->element->processSettings(TRUE);
- // Make sure the current user really has access to configure this element
- // as well as the used input evaluators and data processors.
- if (!user_access('bypass rules access') && !$this->element->root()->access()) {
- form_set_error('', t('Access violation! You have insufficient access permissions to edit this configuration.'));
- }
- if (!empty($form['settings'])) {
- $this->settingsFormValidate($form, $form_state);
- }
- }
- /**
- * Applies the values of the form to the element.
- */
- public function form_extract_values($form, &$form_state) {
- $this->element->settings = array();
- $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
- if (isset($form_values['parameter'])) {
- foreach ($form_values['parameter'] as $name => $values) {
- $this->element->settings += $values['settings'];
- }
- }
- if (isset($form_values['provides'])) {
- foreach ($form_values['provides'] as $name => $values) {
- $this->element->settings[$name . ':label'] = $values['label'];
- $this->element->settings[$name . ':var'] = $values['var'];
- }
- }
- if (!empty($form['settings'])) {
- $this->settingsFormExtractValues($form, $form_state);
- }
- }
- /**
- * Implements RulesPluginUIInterface.
- */
- public function form_submit($form, &$form_state) {
- if (!empty($form['settings'])) {
- $this->settingsFormSubmit($form, $form_state);
- }
- $this->element->save();
- }
- /**
- * Adds the configuration settings form (label, tags, description, ..).
- */
- public function settingsForm(&$form, &$form_state) {
- $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
- // Add the settings in a separate fieldset below.
- $form['settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Settings'),
- '#collapsible' => TRUE,
- '#collapsed' => empty($form_values['settings']['vars']['more']),
- '#weight' => 5,
- '#tree' => TRUE,
- );
- $form['settings']['label'] = array(
- '#type' => 'textfield',
- '#title' => t('Name'),
- '#default_value' => $this->element->label(),
- '#required' => TRUE,
- '#weight' => -5,
- );
- // @todo For Drupal 8 use "owner" for generating machine names and
- // module only for the modules providing default configurations.
- if (!empty($this->element->module) && !empty($this->element->name) && $this->element->module == 'rules' && strpos($this->element->name, 'rules_') === 0) {
- // Remove the Rules module prefix from the machine name.
- $machine_name = substr($this->element->name, strlen($this->element->module) + 1);
- }
- else {
- $machine_name = $this->element->name;
- }
- $form['settings']['name'] = array(
- '#type' => 'machine_name',
- '#default_value' => isset($machine_name) ? $machine_name : '',
- // The string 'rules_' is pre-pended to machine names, so the
- // maxlength must be less than the field length of 64 characters.
- '#maxlength' => 58,
- '#disabled' => entity_has_status('rules_config', $this->element, ENTITY_IN_CODE) && !(isset($form_state['op']) && $form_state['op'] == 'clone'),
- '#machine_name' => array(
- 'exists' => 'rules_config_load',
- 'source' => array('settings', 'label'),
- ),
- '#required' => TRUE,
- '#description' => t('The machine-readable name of this configuration is used by rules internally to identify the configuration. This name must contain only lowercase letters, numbers, and underscores and must be unique.'),
- );
- $form['settings']['tags'] = array(
- '#type' => 'textfield',
- '#title' => t('Tags'),
- '#default_value' => isset($this->element->tags) ? drupal_implode_tags($this->element->tags) : '',
- '#autocomplete_path' => 'admin/config/workflow/rules/autocomplete_tags',
- '#description' => t('Tags associated with this configuration, used for filtering in the admin interface. Separate multiple tags with commas.'),
- );
- // Show a form for editing variables for components.
- if (($plugin_info = $this->element->pluginInfo()) && !empty($plugin_info['component'])) {
- if ($this->element->hasStatus(ENTITY_IN_CODE)) {
- $description = t('The variables used by the component. They can not be edited for configurations that are provided in code.');
- }
- else {
- $description = t('Variables are normally input <em>parameters</em> for the component – data that should be available for the component to act on. Additionally, action components may <em>provide</em> variables back to the caller. Each variable must have a specified data type, a label and a unique machine readable name containing only lowercase alphanumeric characters and underscores. See <a href="@url">the online documentation</a> for more information about variables.',
- array('@url' => rules_external_help('variables'))
- );
- }
- $form['settings']['vars'] = array(
- '#prefix' => '<div id="rules-component-variables">',
- '#suffix' => '</div>',
- '#tree' => TRUE,
- '#element_validate' => array('rules_ui_element_variable_form_validate'),
- '#theme' => 'rules_ui_variable_form',
- '#title' => t('Variables'),
- '#description' => $description,
- // Variables can not be edited on configurations in code.
- '#disabled' => $this->element->hasStatus(ENTITY_IN_CODE),
- );
- $weight = 0;
- $provides = $this->element->providesVariables();
- foreach ($this->element->componentVariables() as $name => $var_info) {
- $form['settings']['vars']['items'][$name] = RulesPluginUI::getVariableForm($name, $var_info, isset($provides[$name]));
- $form['settings']['vars']['items'][$name]['weight']['#default_value'] = $weight++;
- }
- // Always add three empty forms.
- for ($i = 0; $i < 3; $i++) {
- $form['settings']['vars']['items'][$i] = RulesPluginUI::getVariableForm();
- $form['settings']['vars']['items'][$i]['weight']['#default_value'] = $weight++;
- }
- $form['settings']['vars']['more'] = array(
- '#type' => 'submit',
- '#value' => t('Add more'),
- // Enable AJAX once #756762 is fixed.
- // '#ajax' => rules_ui_form_default_ajax('none'),
- '#limit_validation_errors' => array(array('vars')),
- '#submit' => array('rules_form_submit_rebuild'),
- );
- if (!empty($this->element->id)) {
- // Display a setting to manage access.
- $form['settings']['access'] = array(
- '#weight' => 50,
- );
- $plugin_type = $this->element instanceof RulesActionInterface ? t('action') : t('condition');
- $form['settings']['access']['access_exposed'] = array(
- '#type' => 'checkbox',
- '#title' => t('Configure access for using this component with a permission.'),
- '#default_value' => !empty($this->element->access_exposed),
- '#description' => t('By default, the @plugin-type for using this component may be only used by users that have access to configure the component. If checked, access is determined by a permission instead.', array('@plugin-type' => $plugin_type)),
- );
- $form['settings']['access']['permissions'] = array(
- '#type' => 'container',
- '#states' => array(
- 'visible' => array(
- ':input[name="settings[access][access_exposed]"]' => array('checked' => TRUE),
- ),
- ),
- );
- $form['settings']['access']['permissions']['matrix'] = $this->settingsFormPermissionMatrix();
- }
- }
- // @todo Attach field form thus description.
- }
- /**
- * Provides a matrix permission for the component based in the existing roles.
- *
- * @return
- * Form elements with the matrix of permissions for a component.
- */
- protected function settingsFormPermissionMatrix() {
- $form['#theme'] = 'user_admin_permissions';
- $status = array();
- $options = array();
- $role_names = user_roles();
- $role_permissions = user_role_permissions($role_names);
- $component_permission = rules_permissions_by_component(array($this->element));
- $component_permission_name = key($component_permission);
- $form['permission'][$component_permission_name] = array(
- '#type' => 'item',
- '#markup' => $component_permission[$component_permission_name]['title'],
- );
- $options[$component_permission_name] = '';
- foreach ($role_names as $rid => $name) {
- if (isset($role_permissions[$rid][$component_permission_name])) {
- $status[$rid][] = $component_permission_name;
- }
- }
- // Build the checkboxes for each role.
- foreach ($role_names as $rid => $name) {
- $form['checkboxes'][$rid] = array(
- '#type' => 'checkboxes',
- '#options' => $options,
- '#default_value' => isset($status[$rid]) ? $status[$rid] : array(),
- '#attributes' => array('class' => array('rid-' . $rid)),
- );
- $form['role_names'][$rid] = array('#markup' => check_plain($name), '#tree' => TRUE);
- }
- // Attach the default permissions page JavaScript.
- $form['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.permissions.js';
- return $form;
- }
- public function settingsFormExtractValues($form, &$form_state) {
- $form_values = RulesPluginUI::getFormStateValues($form['settings'], $form_state);
- $this->element->label = $form_values['label'];
- // If the name was changed we have to redirect to the URL that contains
- // the new name, instead of rebuilding on the old URL with the old name.
- if ($form['settings']['name']['#default_value'] != $form_values['name']) {
- $module = isset($this->element->module) ? $this->element->module : 'rules';
- $this->element->name = $module . '_' . $form_values['name'];
- $form_state['redirect'] = RulesPluginUI::path($this->element->name, 'edit', $this->element);
- }
- $this->element->tags = empty($form_values['tags']) ? array() : drupal_explode_tags($form_values['tags']);
- if (isset($form_values['vars']['items'])) {
- $vars = &$this->element->componentVariables();
- $vars = array();
- if ($this->element instanceof RulesActionContainer) {
- $provides = &$this->element->componentProvidesVariables();
- $provides = array();
- }
- usort($form_values['vars']['items'], 'rules_element_sort_helper');
- foreach ($form_values['vars']['items'] as $item) {
- if ($item['type'] && $item['name'] && $item['label']) {
- $vars[$item['name']] = array('label' => $item['label'], 'type' => $item['type']);
- if (!$item['usage'][0]) {
- $vars[$item['name']]['parameter'] = FALSE;
- }
- if ($item['usage'][1] && isset($provides)) {
- $provides[] = $item['name'];
- }
- }
- }
- // Disable FAPI persistence for the variable form so renumbering works.
- $input = &$form_state['input'];
- foreach ($form['settings']['#parents'] as $parent) {
- $input = &$input[$parent];
- }
- unset($input['vars']);
- }
- $this->element->access_exposed = isset($form_values['access']['access_exposed']) ? $form_values['access']['access_exposed'] : FALSE;
- }
- public function settingsFormValidate($form, &$form_state) {
- $form_values = RulesPluginUI::getFormStateValues($form['settings'], $form_state);
- if ($form['settings']['name']['#default_value'] != $form_values['name'] && rules_config_load($this->element->name)) {
- form_error($form['settings']['name'], t('The machine-readable name %name is already taken.', array('%name' => $form_values['name'])));
- }
- }
- public function settingsFormSubmit($form, &$form_state) {
- if (isset($form_state['values']['settings']['access']) && !empty($this->element->access_exposed)) {
- // Save the permission matrix.
- foreach ($form_state['values']['settings']['access']['permissions']['matrix']['checkboxes'] as $rid => $value) {
- user_role_change_permissions($rid, $value);
- }
- }
- }
- /**
- * Returns the form for configuring the info of a single variable.
- */
- public function getVariableForm($name = '', $info = array(), $provided = FALSE) {
- $form['type'] = array(
- '#type' => 'select',
- '#options' => array(0 => '--') + RulesPluginUI::getOptions('data'),
- '#default_value' => isset($info['type']) ? $info['type'] : 0,
- );
- $form['label'] = array(
- '#type' => 'textfield',
- '#size' => 40,
- '#default_value' => isset($info['label']) ? $info['label'] : '',
- );
- $form['name'] = array(
- '#type' => 'textfield',
- '#size' => 40,
- '#default_value' => $name,
- '#element_validate' => array('rules_ui_element_machine_name_validate'),
- );
- $usage[0] = !isset($info['parameter']) || $info['parameter'] ? 1 : 0;
- $usage[1] = $provided ? 1 : 0;
- $form['usage'] = array(
- '#type' => 'select',
- '#default_value' => implode('', $usage),
- '#options' => array(
- '10' => t('Parameter'),
- '11' => t('Parameter + Provided'),
- '01' => t('Provided'),
- ),
- );
- if ($this->element instanceof RulesConditionContainer) {
- $form['usage']['#disabled'] = TRUE;
- }
- // Just set the weight #default_value for the returned form.
- $form['weight'] = array(
- '#type' => 'weight',
- );
- return $form;
- }
- /**
- * Returns the name of class for the given data type.
- *
- * @param $data_type
- * The name of the data type
- * @param $parameter_info
- * (optional) An array of info about the to be configured parameter. If
- * given, this array is complemented with data type defaults also.
- */
- public function getDataTypeClass($data_type, &$parameter_info = array()) {
- $cache = rules_get_cache();
- $data_info = $cache['data_info'];
- // Add in data-type defaults.
- if (empty($parameter_info['ui class'])) {
- $parameter_info['ui class'] = (is_string($data_type) && isset($data_info[$data_type]['ui class'])) ? $data_info[$data_type]['ui class'] : 'RulesDataUI';
- }
- if (is_subclass_of($parameter_info['ui class'], 'RulesDataInputOptionsListInterface')) {
- $parameter_info['options list'] = array($parameter_info['ui class'], 'optionsList');
- }
- return $parameter_info['ui class'];
- }
- /**
- * Implements RulesPluginUIInterface.
- *
- * Shows a preview of the configuration settings.
- */
- public function buildContent() {
- $config_name = $this->element->root()->name;
- $content['label'] = array(
- '#type' => 'link',
- '#title' => $this->element->label(),
- '#href' => $this->element->isRoot() ? RulesPluginUI::path($config_name) : RulesPluginUI::path($config_name, 'edit', $this->element),
- '#prefix' => '<div class="rules-element-label">',
- '#suffix' => '</div>',
- );
- // Put the elements below in a "description" div.
- $content['description'] = array(
- '#prefix' => '<div class="description">',
- );
- $content['description']['parameter'] = array(
- '#caption' => t('Parameter'),
- '#theme' => 'rules_content_group',
- );
- foreach ($this->element->pluginParameterInfo() as $name => $parameter) {
- $element = array();
- if (!empty($this->element->settings[$name . ':select'])) {
- $element['content'] = array(
- '#markup' => '[' . $this->element->settings[$name . ':select'] . ']',
- );
- }
- elseif (isset($this->element->settings[$name]) && (!isset($parameter['default value']) || $parameter['default value'] != $this->element->settings[$name])) {
- $class = $this->getDataTypeClass($parameter['type'], $parameter);
- $method = empty($parameter['options list']) ? 'render' : 'renderOptionsLabel';
- // We cannot use method_exists() here as it would trigger a PHP bug.
- // @see https://www.drupal.org/node/1258284
- $element = call_user_func(array($class, $method), $this->element->settings[$name], $name, $parameter, $this->element);
- }
- // Only add parameters that are really configured / not default.
- if ($element) {
- $content['description']['parameter'][$name] = array(
- '#theme' => 'rules_parameter_configuration',
- '#info' => $parameter,
- ) + $element;
- }
- }
- foreach ($this->element->providesVariables() as $name => $var_info) {
- $content['description']['provides'][$name] = array(
- '#theme' => 'rules_variable_view',
- '#info' => $var_info,
- '#name' => $name,
- );
- }
- if (!empty($content['description']['provides'])) {
- $content['description']['provides'] += array(
- '#caption' => t('Provides variables'),
- '#theme' => 'rules_content_group',
- );
- }
- // Add integrity exception messages if there are any for this element.
- try {
- $this->element->integrityCheck();
- // A configuration is still marked as dirty, but already works again.
- if (!empty($this->element->dirty)) {
- rules_config_update_dirty_flag($this->element);
- $variables = array('%label' => $this->element->label(), '%name' => $this->element->name, '@plugin' => $this->element->plugin());
- drupal_set_message(t('The @plugin %label (%name) was marked dirty, but passes the integrity check now and is active again.', $variables));
- rules_clear_cache();
- }
- }
- catch (RulesIntegrityException $e) {
- $content['description']['integrity'] = array(
- '#theme' => 'rules_content_group',
- '#caption' => t('Error'),
- '#attributes' => array('class' => array('rules-content-group-integrity-error')),
- 'error' => array(
- '#markup' => filter_xss($e->getMessage()),
- ),
- );
- // Also make sure the rule is marked as dirty.
- if (empty($this->element->dirty)) {
- rules_config_update_dirty_flag($this->element);
- rules_clear_cache();
- }
- }
- $content['#suffix'] = '</div>';
- $content['#type'] = 'container';
- $content['#attributes']['class'][] = 'rules-element-content';
- return $content;
- }
- /**
- * Implements RulesPluginUIInterface.
- */
- public function operations() {
- $name = $this->element->root()->name;
- $render = array(
- '#theme' => 'links__rules',
- );
- $render['#attributes']['class'][] = 'rules-operations';
- $render['#attributes']['class'][] = 'action-links';
- $render['#links']['edit'] = array(
- 'title' => t('edit'),
- 'href' => RulesPluginUI::path($name, 'edit', $this->element),
- );
- $render['#links']['delete'] = array(
- 'title' => t('delete'),
- 'href' => RulesPluginUI::path($name, 'delete', $this->element),
- );
- return $render;
- }
- /**
- * Implements RulesPluginUIInterface.
- */
- public function help() {}
- /**
- * Deprecated by the controllers overviewTable() method.
- */
- public static function overviewTable($conditions = array(), $options = array()) {
- return rules_ui()->overviewTable($conditions, $options);
- }
- /**
- * Generates an operation path.
- *
- * Generates a path using the given operation for the element with the given
- * id of the configuration with the given name.
- */
- public static function path($name, $op = NULL, RulesPlugin $element = NULL, $parameter = FALSE) {
- $element_id = isset($element) ? $element->elementId() : FALSE;
- if (isset(self::$basePath)) {
- $base_path = self::$basePath;
- }
- // Default to the paths used by 'rules_admin', so modules can easily re-use
- // its UI.
- else {
- $base_path = isset($element) && $element instanceof RulesTriggerableInterface ? 'admin/config/workflow/rules/reaction' : 'admin/config/workflow/rules/components';
- }
- // Only append the '/manage' path if it is not already present.
- if (substr($base_path, -strlen('/manage')) != '/manage') {
- $base_path .= '/manage';
- }
- return implode('/', array_filter(array($base_path, $name, $op, $element_id, $parameter)));
- }
- /**
- * Determines the default redirect target for an edited/deleted element.
- *
- * This is a parent element which is either a rule or the configuration root.
- */
- public static function defaultRedirect(RulesPlugin $element) {
- while (!$element->isRoot()) {
- if ($element instanceof Rule) {
- return self::path($element->root()->name, 'edit', $element);
- }
- $element = $element->parentElement();
- }
- return self::path($element->name);
- }
- /**
- * @see RulesUICategory::getOptions()
- */
- public static function getOptions($item_type, $items = NULL) {
- return RulesUICategory::getOptions($item_type, $items = NULL);
- }
- public static function formDefaults(&$form, &$form_state) {
- form_load_include($form_state, 'inc', 'rules', 'ui/ui.forms');
- // Add our own css.
- $form['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.css';
- // Workaround for problems with jquery css in seven theme and the core
- // autocomplete.
- if ($GLOBALS['theme'] == 'seven') {
- $form['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.seven.css';
- }
- // Specify the wrapper div used by #ajax.
- $form['#prefix'] = '<div id="rules-form-wrapper">';
- $form['#suffix'] = '</div>';
- // Preserve the base path in the form state. The after build handler will
- // set self::$basePath again for cached forms.
- if (isset(self::$basePath)) {
- $form_state['_rules_base_path'] = RulesPluginUI::$basePath;
- $form['#after_build'][] = 'rules_form_after_build_restore_base_path';
- }
- }
- public static function getTags() {
- $result = db_select('rules_tags')
- ->distinct()
- ->fields('rules_tags', array('tag'))
- ->groupBy('tag')
- ->execute()
- ->fetchCol('tag');
- return drupal_map_assoc($result);
- }
- }
- /**
- * UI for abstract plugins (conditions & actions).
- */
- class RulesAbstractPluginUI extends RulesPluginUI {
- /**
- * Overrides RulesPluginUI::form().
- *
- * Overridden to invoke the abstract plugins form alter callback and to add
- * the negation checkbox for conditions.
- */
- public function form(&$form, &$form_state, $options = array()) {
- parent::form($form, $form_state, $options);
- if ($this->element instanceof RulesCondition) {
- $form['negate'] = array(
- '#title' => t('Negate'),
- '#type' => 'checkbox',
- '#description' => t('If checked, the condition result is negated such that it returns TRUE if it evaluates to FALSE.'),
- '#default_value' => $this->element->isNegated(),
- '#weight' => 5,
- );
- }
- $this->element->call('form_alter', array(&$form, &$form_state, $options));
- }
- public function form_extract_values($form, &$form_state) {
- parent::form_extract_values($form, $form_state);
- $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
- if ($this->element instanceof RulesCondition && isset($form_values['negate'])) {
- $this->element->negate($form_values['negate']);
- }
- }
- public function form_validate($form, &$form_state) {
- parent::form_validate($form, $form_state);
- // Validate the edited element and throw validation errors if it fails.
- try {
- $this->element->integrityCheck();
- }
- catch (RulesIntegrityException $e) {
- form_set_error(implode('][', $e->keys), $e->getMessage());
- }
- }
- }
- /**
- * UI for Rules Container.
- */
- class RulesContainerPluginUI extends RulesPluginUI {
- /**
- * Generates a table for editing the contained elements.
- */
- public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
- parent::form($form, $form_state, $options);
- $form['elements'] = array(
- // Hide during creation or for embedded elements.
- '#access' => empty($options['init']) && $this->element->isRoot(),
- '#tree' => TRUE,
- '#theme' => 'rules_elements',
- '#empty' => t('None'),
- '#caption' => t('Elements'),
- );
- $form['elements']['#attributes']['class'][] = 'rules-container-plugin';
- // Recurse over all element children or use the provided iterator.
- $iterator = isset($iterator) ? $iterator : $this->element->elements();
- $root_depth = $this->element->depth();
- foreach ($iterator as $key => $child) {
- $id = $child->elementId();
- // Do not render rules as container element when displayed in a rule set.
- $is_container = $child instanceof RulesContainerPlugin && !($child instanceof Rule);
- $form['elements'][$id] = array(
- '#depth' => $child->depth() - $root_depth - 1,
- '#container' => $is_container,
- );
- $form['elements'][$id]['label'] = $child->buildContent();
- $form['elements'][$id]['weight'] = array(
- '#type' => 'weight',
- '#default_value' => $child->weight,
- '#delta' => 50,
- );
- $form['elements'][$id]['parent_id'] = array(
- '#type' => 'hidden',
- // If another iterator is passed in, the child parent may not equal
- // the current element. Thus ask the child for its parent.
- '#default_value' => $child->parentElement()->elementId(),
- );
- $form['elements'][$id]['element_id'] = array(
- '#type' => 'hidden',
- '#default_value' => $id,
- );
- $form['elements'][$id]['operations'] = $child->operations();
- }
- // Alter the submit button label.
- if (!empty($options['button']) && !empty($options['init'])) {
- $form['submit']['#value'] = t('Continue');
- }
- elseif (!empty($options['button']) && $this->element->isRoot()) {
- $form['submit']['#value'] = t('Save changes');
- }
- }
- /**
- * Applies the values of the form to the given rule configuration.
- */
- public function form_extract_values($form, &$form_state) {
- parent::form_extract_values($form, $form_state);
- $values = RulesPluginUI::getFormStateValues($form, $form_state);
- // Now apply the new hierarchy.
- if (isset($values['elements'])) {
- foreach ($values['elements'] as $id => $data) {
- $child = $this->element->elementMap()->lookup($id);
- $child->weight = $data['weight'];
- $parent = $this->element->elementMap()->lookup($data['parent_id']);
- $child->setParent($parent ? $parent : $this->element);
- }
- $this->element->sortChildren(TRUE);
- }
- }
- public function operations() {
- $ops = parent::operations();
- $add_ops = self::addOperations();
- $ops['#links'] += $add_ops['#links'];
- return $ops;
- }
- /**
- * Gets the Add-* operations for the given element.
- */
- public function addOperations() {
- $name = $this->element->root()->name;
- $render = array(
- '#theme' => 'links__rules',
- );
- $render['#attributes']['class'][] = 'rules-operations-add';
- $render['#attributes']['class'][] = 'action-links';
- foreach (rules_fetch_data('plugin_info') as $plugin => $info) {
- if (!empty($info['embeddable']) && $this->element instanceof $info['embeddable']) {
- $render['#links']['add_' . $plugin] = array(
- 'title' => t('Add !name', array('!name' => $plugin)),
- 'href' => RulesPluginUI::path($name, 'add', $this->element, $plugin),
- );
- }
- }
- return $render;
- }
- public function buildContent() {
- $content = parent::buildContent();
- // Don't link the title for embedded container plugins, except for rules.
- if (!$this->element->isRoot() && !($this->element instanceof Rule)) {
- $content['label']['#markup'] = check_plain($content['label']['#title']);
- unset($content['label']['#title']);
- }
- elseif ($this->element->isRoot()) {
- $content['description']['settings'] = array(
- '#theme' => 'rules_content_group',
- '#weight' => -4,
- 'machine_name' => array(
- '#markup' => t('Machine name') . ': ' . $this->element->name,
- ),
- 'weight' => array(
- '#access' => $this->element instanceof RulesTriggerableInterface,
- '#markup' => t('Weight') . ': ' . $this->element->weight,
- ),
- );
- if (!empty($this->element->tags)) {
- $content['description']['tags'] = array(
- '#theme' => 'rules_content_group',
- '#caption' => t('Tags'),
- 'tags' => array(
- '#markup' => check_plain(drupal_implode_tags($this->element->tags)),
- ),
- );
- }
- if ($vars = $this->element->componentVariables()) {
- $content['description']['variables'] = array(
- '#caption' => t('Parameter'),
- '#theme' => 'rules_content_group',
- );
- foreach ($vars as $name => $info) {
- if (!isset($info['parameter']) || $info['parameter']) {
- $content['description']['variables'][$name] = array(
- '#theme' => 'rules_variable_view',
- '#info' => $info,
- '#name' => $name,
- );
- }
- }
- }
- }
- return $content;
- }
- }
- /**
- * UI for Rules condition container.
- */
- class RulesConditionContainerUI extends RulesContainerPluginUI {
- public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
- parent::form($form, $form_state, $options, $iterator);
- // Add the add-* operation links.
- $form['elements']['#add'] = self::addOperations();
- $form['elements']['#attributes']['class'][] = 'rules-condition-container';
- $form['elements']['#caption'] = t('Conditions');
- // By default skip.
- if (!empty($options['init']) && !$this->element->isRoot()) {
- $config = $this->element->root();
- $form['init_help'] = array(
- '#type' => 'container',
- '#id' => 'rules-plugin-add-help',
- 'content' => array(
- '#markup' => t('You are about to add a new @plugin to the @config-plugin %label. Use indentation to make conditions a part of this logic group. See <a href="@url">the online documentation</a> for more information on condition sets.',
- array('@plugin' => $this->element->plugin(),
- '@config-plugin' => $config->plugin(),
- '%label' => $config->label(),
- '@url' => rules_external_help('condition-components'))),
- ),
- );
- }
- $form['negate'] = array(
- '#title' => t('Negate'),
- '#type' => 'checkbox',
- '#description' => t('If checked, the condition result is negated such that it returns TRUE if it evaluates to FALSE.'),
- '#default_value' => $this->element->isNegated(),
- '#weight' => 5,
- );
- }
- public function form_extract_values($form, &$form_state) {
- parent::form_extract_values($form, $form_state);
- $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
- if (isset($form_values['negate'])) {
- $this->element->negate($form_values['negate']);
- }
- }
- }
- /**
- * UI for Rules action container.
- */
- class RulesActionContainerUI extends RulesContainerPluginUI {
- public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
- parent::form($form, $form_state, $options, $iterator);
- // Add the add-* operation links.
- $form['elements']['#add'] = self::addOperations();
- $form['elements']['#attributes']['class'][] = 'rules-action-container';
- $form['elements']['#caption'] = t('Actions');
- }
- }
- /**
- * Class holding category related methods.
- */
- class RulesUICategory {
- /**
- * Gets info about all available categories, or about a specific category.
- *
- * @return array
- */
- public static function getInfo($category = NULL) {
- $data = rules_fetch_data('category_info');
- if (isset($category)) {
- return $data[$category];
- }
- return $data;
- }
- /**
- * Returns a group label, e.g. as usable for opt-groups in a select list.
- *
- * @param array $item_info
- * The info-array of an item, e.g. an entry of hook_rules_action_info().
- * @param bool $in_category
- * (optional) Whether group labels for grouping inside a category should be
- * return. Defaults to FALSE.
- *
- * @return string|bool
- * The group label to use, or FALSE if none can be found.
- */
- public static function getItemGroup($item_info, $in_category = FALSE) {
- if (isset($item_info['category']) && !$in_category) {
- return self::getCategory($item_info, 'label');
- }
- elseif (!empty($item_info['group'])) {
- return $item_info['group'];
- }
- return FALSE;
- }
- /**
- * Gets the category for the given item info array.
- *
- * @param array $item_info
- * The info-array of an item, e.g. an entry of hook_rules_action_info().
- * @param string|null $key
- * (optional) The key of the category info to return, e.g. 'label'. If none
- * is given the whole info array is returned.
- *
- * @return array|mixed|false
- * Either the whole category info array or the value of the given key. If
- * no category can be found, FALSE is returned.
- */
- public static function getCategory($item_info, $key = NULL) {
- if (isset($item_info['category'])) {
- $info = self::getInfo($item_info['category']);
- return isset($key) ? $info[$key] : $info;
- }
- return FALSE;
- }
- /**
- * Returns an array of options to use with a select.
- *
- * Returns an array of options to use with a selectfor the items specified
- * in the given hook.
- *
- * @param $item_type
- * The item type to get options for. One of 'data', 'event', 'condition' and
- * 'action'.
- * @param $items
- * (optional) An array of items to restrict the options to.
- *
- * @return array
- * An array of options.
- */
- public static function getOptions($item_type, $items = NULL) {
- $sorted_data = array();
- $ungrouped = array();
- $data = $items ? $items : rules_fetch_data($item_type . '_info');
- foreach ($data as $name => $info) {
- // Verify the current user has access to use it.
- if (!user_access('bypass rules access') && !empty($info['access callback']) && !call_user_func($info['access callback'], $item_type, $name)) {
- continue;
- }
- if ($group = RulesUICategory::getItemGroup($info)) {
- $sorted_data[drupal_ucfirst($group)][$name] = drupal_ucfirst($info['label']);
- }
- else {
- $ungrouped[$name] = drupal_ucfirst($info['label']);
- }
- }
- asort($ungrouped);
- foreach ($sorted_data as $key => $choices) {
- asort($choices);
- $sorted_data[$key] = $choices;
- }
- // Sort the grouped data by category weights, defaulting to weight 0 for
- // groups without a respective category.
- $sorted_groups = array();
- foreach (array_keys($sorted_data) as $label) {
- $sorted_groups[$label] = array('weight' => 0, 'label' => $label);
- }
- // Add in category weights.
- foreach (RulesUICategory::getInfo() as $info) {
- if (isset($sorted_groups[$info['label']])) {
- $sorted_groups[$info['label']] = $info;
- }
- }
- uasort($sorted_groups, '_rules_ui_sort_categories');
- // Now replace weights with group content.
- foreach ($sorted_groups as $group => $weight) {
- $sorted_groups[$group] = $sorted_data[$group];
- }
- return $ungrouped + $sorted_groups;
- }
- }
- /**
- * Helper for sorting categories.
- */
- function _rules_ui_sort_categories($a, $b) {
- // @see element_sort()
- $a_weight = isset($a['weight']) ? $a['weight'] : 0;
- $b_weight = isset($b['weight']) ? $b['weight'] : 0;
- if ($a_weight == $b_weight) {
- // @see element_sort_by_title()
- $a_title = isset($a['label']) ? $a['label'] : '';
- $b_title = isset($b['label']) ? $b['label'] : '';
- return strnatcasecmp($a_title, $b_title);
- }
- return ($a_weight < $b_weight) ? -1 : 1;
- }
|