diff --git a/core/includes/form.inc b/core/includes/form.inc
index 513615c..3684578 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -3337,6 +3337,52 @@ function form_process_actions($element, &$form_state) {
}
/**
+ * #pre_render callback for #type 'actions'.
+ *
+ * This callback iterates over all child elements of the #type 'actions'
+ * container to look for elements with a #dropbutton property, so as to group
+ * those elements into dropbuttons. As such, it works similar to #group, but is
+ * specialized for dropbuttons.
+ *
+ * The value of #dropbutton denotes the dropbutton to group the child element
+ * into. For example, two different values of 'foo' and 'bar' on child elements
+ * would generate two separate dropbuttons, which each contain the corresponding
+ * buttons.
+ *
+ * @param array $element
+ * The #type 'actions' element to process.
+ *
+ * @return array
+ * The processed #type 'actions' element, including individual buttons grouped
+ * into new #type 'dropbutton' elements.
+ */
+function form_pre_render_actions_dropbutton(array $element) {
+ $dropbuttons = array();
+ foreach (element_children($element, TRUE) as $key) {
+ if (isset($element[$key]['#dropbutton'])) {
+ $dropbutton = $element[$key]['#dropbutton'];
+ // If there is no dropbutton for this button group yet, create one.
+ if (!isset($dropbuttons[$dropbutton])) {
+ $dropbuttons[$dropbutton] = array(
+ '#type' => 'dropbutton',
+ );
+ }
+ // Add this button to the corresponding dropbutton.
+ // @todo Change #type 'dropbutton' to be based on theme_item_list()
+ // instead of theme_links() to avoid this preemptive rendering.
+ $button = drupal_render($element[$key]);
+ $dropbuttons[$dropbutton]['#links'][$key] = array(
+ 'title' => $button,
+ 'html' => TRUE,
+ );
+ }
+ }
+ // @todo For now, all dropbuttons appear first. Consider to invent a more
+ // fancy sorting/injection algorithm here.
+ return $dropbuttons + $element;
+}
+
+/**
* #process callback for #pattern form element property.
*
* @param $element
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index a38dbe0..6f27936 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1811,11 +1811,11 @@ function theme_links($variables) {
// Merge in default array properties into $link.
$link += array(
'html' => FALSE,
- 'attributes' => array(),
);
- $item = '';
- $item .= ($link['html'] ? $link['title'] : check_plain($link['title']));
- $item .= '';
+ $item = ($link['html'] ? $link['title'] : check_plain($link['title']));
+ if (isset($link['attributes'])) {
+ $item = '' . $item . '';
+ }
}
$output .= '
$class)) . '>';
@@ -1844,24 +1844,6 @@ function theme_dropbutton_wrapper($variables) {
}
/**
- * Returns HTML for wrapping a dropbutton list.
- *
- * Use this function if the dropbutton contains submit buttons. These elements
- * need to have a #prefix and #suffix element that wraps those into an
- * element.
- *
- * @param array $variables
- * An associative array containing:
- * - element: An associative array containing the properties and children of
- * the dropbutton list. Properties used: #children.
- */
-function theme_dropbutton_list_wrapper($variables) {
- if (!empty($variables['element']['#children'])) {
- return '';
- }
-}
-
-/**
* Returns HTML for an image.
*
* @param $variables
@@ -3172,9 +3154,6 @@ function drupal_common_theme() {
'dropbutton_wrapper' => array(
'render element' => 'element',
),
- 'dropbutton_list_wrapper' => array(
- 'render element' => 'element',
- ),
'image' => array(
// HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
// allows the alt attribute to be omitted in some cases. Therefore,
diff --git a/core/misc/dropbutton/dropbutton.base.css b/core/misc/dropbutton/dropbutton.base.css
index 9c1a3ef..35e9692 100644
--- a/core/misc/dropbutton/dropbutton.base.css
+++ b/core/misc/dropbutton/dropbutton.base.css
@@ -22,13 +22,21 @@
.js .dropbutton-widget {
max-width: 100%;
}
-
@media screen and (max-width:600px) {
.js .dropbutton-wrapper {
width: 100%;
}
}
+/* Splitbuttons */
+.form-actions .dropbutton-wrapper {
+ float: left;
+}
+.js .form-actions .dropbutton-widget {
+ position: static;
+}
+
+
.js .dropbutton-widget {
position: absolute;
}
@@ -79,7 +87,7 @@
text-indent: 110%;
top: 0;
white-space: nowrap;
- width: 2.08em;
+ width: 2em;
}
.dropbutton-toggle button {
background: none;
diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php
index a7354ee..af7d354 100644
--- a/core/modules/node/lib/Drupal/node/NodeFormController.php
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -274,89 +274,6 @@ public function form(array $form, array &$form_state, EntityInterface $node) {
return parent::form($form, $form_state, $node);
}
- /**
- * Overrides Drupal\entity\EntityFormController::actionsElement().
- */
- protected function actionsElement(array $form, array &$form_state) {
- $element = parent::actionsElement($form, $form_state);
- $node = $this->getEntity($form_state);
-
- // Because some of the 'links' are actually submit buttons, we have to
- // manually wrap each item in and the whole list in . The
- // is added with a #theme_wrappers function.
- $element['operations'] = array(
- '#type' => 'operations',
- '#subtype' => 'node',
- '#attached' => array (
- 'css' => array(
- drupal_get_path('module', 'node') . '/node.admin.css',
- ),
- ),
- );
-
- $element['operations']['actions'] = array(
- '#theme_wrappers' => array('dropbutton_list_wrapper')
- );
-
- // Depending on the state of the node (published or unpublished) and
- // whether the current user has the permission to change the status, the
- // labels and order of the buttons will vary.
- if (user_access('administer nodes')) {
- $element['operations']['actions']['publish'] = array(
- '#type' => 'submit',
- '#value' => t('Save and publish'),
- '#submit' => array(array($this, 'publish'), array($this, 'submit'), array($this, 'save')),
- '#validate' => array(array($this, 'validate')),
- '#button_type' => $node->status ? 'primary' : '',
- '#weight' => 0,
- '#prefix' => '- ',
- '#suffix' => '
',
- );
- $element['operations']['actions']['unpublish'] = array(
- '#type' => 'submit',
- '#value' => t('Save as unpublished'),
- '#submit' => array(array($this, 'unpublish'), array($this, 'submit'), array($this, 'save')),
- '#validate' => array(array($this, 'validate')),
- '#button_type' => empty($node->status) ? 'primary' : '',
- '#weight' => $node->status ? 1 : -1,
- '#prefix' => '- ',
- "#suffix" => '
',
- );
-
- if (!empty($node->nid)) {
- if ($node->status) {
- $publish_label = t('Save and keep published');
- $unpublish_label = t('Save and unpublish');
- }
- else {
- $publish_label = t('Save and publish');
- $unpublish_label = t('Save and keep unpublished');
- }
- $element['operations']['actions']['publish']['#value'] = $publish_label;
- $element['operations']['actions']['unpublish']['#value'] = $unpublish_label;
- }
- }
- // The user has no permission to change the status of the node. Just
- // show a save button without the 'publish' or 'unpublish' callback in
- // the #submit definition.
- else {
- $element['operations']['actions']['save'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- '#submit' => array(array($this, 'submit'), array($this, 'save')),
- '#validate' => array(array($this, 'validate')),
- '#button_type' => 'primary',
- '#weight' => 1,
- '#prefix' => '- ',
- "#suffix" => '
',
- );
- }
-
- unset($element['submit']);
-
- return $element;
- }
-
/*
* Overrides Drupal\Core\Entity\EntityFormController::actions().
*/
@@ -365,9 +282,63 @@ protected function actions(array $form, array &$form_state) {
$node = $this->getEntity($form_state);
$preview_mode = variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL);
+ $element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview']));
+
+ // If saving is an option, privileged users get dedicated form submit
+ // buttons to adjust the publishing status while saving in one go.
+ // @todo This adjustment makes it close to impossible for contributed
+ // modules to integrate with "the Save operation" of this form. Modules
+ // need a way to plug themselves into 1) the ::submit() step, and
+ // 2) the ::save() step, both decoupled from the pressed form button.
+ if ($element['submit']['#access'] && user_access('administer nodes')) {
+ // isNew | prev status » default & publish label & unpublish label
+ // 1 | 1 » publish & Save and publish & Save as unpublished
+ // 1 | 0 » unpublish & Save and publish & Save as unpublished
+ // 0 | 1 » publish & Save and keep published & Save and unpublish
+ // 0 | 0 » unpublish & Save and keep unpublished & Save and publish
+
+ // Add a "Publish" button.
+ $element['publish'] = $element['submit'];
+ $element['publish']['#dropbutton'] = 'save';
+ if ($node->isNew()) {
+ $element['publish']['#value'] = t('Save and publish');
+ }
+ else {
+ $element['publish']['#value'] = $node->status ? t('Save and keep published') : t('Save and publish');
+ }
+ $element['publish']['#weight'] = 0;
+ array_unshift($element['publish']['#submit'], array($this, 'publish'));
+
+ // Add a "Unpublish" button.
+ $element['unpublish'] = $element['submit'];
+ $element['unpublish']['#dropbutton'] = 'save';
+ if ($node->isNew()) {
+ $element['unpublish']['#value'] = t('Save as unpublished');
+ }
+ else {
+ $element['unpublish']['#value'] = !$node->status ? t('Save and keep unpublished') : t('Save and unpublish');
+ }
+ $element['unpublish']['#weight'] = 10;
+ array_unshift($element['unpublish']['#submit'], array($this, 'unpublish'));
+
+ // If already published, the 'publish' button is primary.
+ if ($node->status) {
+ unset($element['unpublish']['#button_type']);
+ }
+ // Otherwise, the 'unpublish' button is primary and should come first.
+ else {
+ unset($element['publish']['#button_type']);
+ $element['unpublish']['#weight'] = -10;
+ }
+
+ // Remove the "Save" button.
+ $element['submit']['#access'] = FALSE;
+ }
+
$element['preview'] = array(
'#access' => $preview_mode != DRUPAL_DISABLED,
'#value' => t('Preview'),
+ '#weight' => 20,
'#validate' => array(
array($this, 'validate'),
),
@@ -377,8 +348,8 @@ protected function actions(array $form, array &$form_state) {
),
);
- $element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview']));
$element['delete']['#access'] = node_access('delete', $node);
+ $element['delete']['#weight'] = 100;
return $element;
}
@@ -473,7 +444,7 @@ public function preview(array $form, array &$form_state) {
*/
public function publish(array $form, array &$form_state) {
$node = $this->getEntity($form_state);
- $node->status = TRUE;
+ $node->status = 1;
return $node;
}
@@ -487,7 +458,7 @@ public function publish(array $form, array &$form_state) {
*/
public function unpublish(array $form, array &$form_state) {
$node = $this->getEntity($form_state);
- $node->status = FALSE;
+ $node->status = 0;
return $node;
}
diff --git a/core/modules/node/node.admin.css b/core/modules/node/node.admin.css
index 9ef08e1..101a38d 100644
--- a/core/modules/node/node.admin.css
+++ b/core/modules/node/node.admin.css
@@ -9,130 +9,3 @@
.revision-current {
background: #ffc;
}
-
-/**
- * Node form dropbuttons.
- */
-.form-actions .dropbutton-wrapper {
- float: left;
- margin-right: 1em;
-}
-
-.form-actions .dropbutton-wrapper .dropbutton-widget {
- position: static;
-}
-
-.form-actions .dropbutton-wrapper li a,
-.form-actions .dropbutton-wrapper input {
- padding: 5px 17px 6px 17px;
- margin-bottom: 0em;
- border: medium;
- border-radius: 0;
- background: none;
-}
-
-.form-actions .dropbutton-wrapper input:hover {
- background: none;
- border: none;
-}
-
-.form-actions .button {
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: -moz-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: -o-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: linear-gradient(to bottom, #fefefe, #e0e0e0);
- border: 1px solid #c8c8c8;
- border-radius: 3px;
- text-decoration: none;
- padding: 6px 17px 6px 17px;
- margin-left: 0;
-}
-
-.form-actions .button:focus,
-.form-actions .button:hover {
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #fefefe, #eaeaea);
- background-image: -moz-linear-gradient(top, #fefefe, #eaeaea);
- background-image: -o-linear-gradient(top, #fefefe, #eaeaea);
- background-image: linear-gradient(to bottom, #fefefe, #eaeaea);
- -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- color: #2e2e2e;
- text-decoration: none;
-}
-.form-actions .button:active {
- border: 1px solid #c8c8c8;
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #eaeaea, #fefefe);
- background-image: -moz-linear-gradient(top, #eaeaea, #fefefe);
- background-image: -o-linear-gradient(top, #eaeaea, #fefefe);
- background-image: linear-gradient(to bottom, #eaeaea, #fefefe);
- -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- color: #2e2e2e;
- text-decoration: none;
- text-shadow: none;
-}
-
-/* Delete button */
-.form-actions .button-danger {
- color: #c72100;
- background: none;
- border: none;
- float: right;
- margin-right: 0;
- margin-left: 0;
- padding-right: 0;
- padding-left: 0;
-}
-.form-actions .button-danger:hover,
-.form-actions .button-danger:focus {
- color: #ff2a00;
- background: none;
- border: none;
- text-decoration: underline;
-}
-.form-actions .button-danger:active {
- color: #ff2a00;
- background: none;
- border: none;
- text-decoration: underline;
-}
-
-
-/**
- * Form edit action theming
- */
-.js .form-actions .dropbutton-widget {
- background-color: #50a0e9;
- background-image: -moz-linear-gradient(-90deg, #50a0e9, #4481dc);
- background-image: -o-linear-gradient(-90deg, #50a0e9, #4481dc);
- background-image: -webkit-linear-gradient(-90deg, #50a0e9, #4481dc);
- background-image: linear-gradient(180deg, #50a0e9, #4481dc);
- border-radius: 3px;
- border: 1px solid #3974ae;
-}
-.js .form-actions .dropbutton-widget .dropbutton li {
- border-top: 1px solid rgba(255, 255, 255, 0.5);
- border-top-left-radius: 3px;
-}
-.js .form-actions .dropbutton-widget .dropbutton .dropbutton-toggle {
- border-top-left-radius: 0px;
- border-top-right-radius: 3px;
- top: 1px;
-}
-.js .form-actions .dropbutton-widget .dropbutton .secondary-action {
- border-top: 1px solid rgba(255, 255, 255, 0.3);
- border-top-left-radius: 0px;
-}
-.js .form-actions .dropbutton-widget .button {
- color: #ffffff;
- text-shadow: 1px 1px 1px rgba(31, 83, 131, 0.8);
-}
-.js .form-actions .dropbutton-multiple.open .dropbutton-action:hover {
- background-color: #50a0e9;
-}
-
-
-
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index 10a12c6..7c360a4 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -434,9 +434,6 @@ function node_admin_nodes() {
'#title' => t('Update options'),
'#attributes' => array('class' => array('container-inline')),
'#access' => $admin_access,
- '#attached' => array (
- 'css' => array(drupal_get_path('module', 'node') . '/css/node-admin.theme.css'),
- ),
);
$options = array();
foreach (module_invoke_all('node_operations') as $operation => $array) {
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 17015c6..e580a84 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -576,7 +576,7 @@ function system_element_info() {
'#theme_wrappers' => array('container'),
);
$types['actions'] = array(
- '#process' => array('form_process_actions', 'form_process_container'),
+ '#process' => array('form_pre_render_actions_dropbutton', 'form_process_actions', 'form_process_container'),
'#weight' => 100,
'#theme_wrappers' => array('container'),
);
diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css
index a72d686..376c130 100644
--- a/core/themes/seven/style.css
+++ b/core/themes/seven/style.css
@@ -1529,3 +1529,114 @@ details.fieldset-no-legend {
.entity-meta details .summary {
display: none; /* Hide JS summaries. @todo Rethink summaries. */
}
+
+
+/**
+ * Node form dropbuttons.
+ */
+.form-actions .dropbutton-wrapper li a,
+.form-actions .dropbutton-wrapper input {
+ padding: 5px 17px 6px 17px;
+ margin-bottom: 0em;
+ border: medium;
+ border-radius: 0;
+ background: none;
+}
+.form-actions .dropbutton-wrapper input:hover {
+ background: none;
+ border: none;
+}
+.form-actions .button {
+ background: #fefefe;
+ background-image: -webkit-linear-gradient(top, #fefefe, #e0e0e0);
+ background-image: -moz-linear-gradient(top, #fefefe, #e0e0e0);
+ background-image: -o-linear-gradient(top, #fefefe, #e0e0e0);
+ background-image: linear-gradient(to bottom, #fefefe, #e0e0e0);
+ border: 1px solid #c8c8c8;
+ border-radius: 3px;
+ text-decoration: none;
+ padding: 6px 17px 6px 17px;
+ margin-left: 0;
+}
+.form-actions .button:focus,
+.form-actions .button:hover {
+ background: #fefefe;
+ background-image: -webkit-linear-gradient(top, #fefefe, #eaeaea);
+ background-image: -moz-linear-gradient(top, #fefefe, #eaeaea);
+ background-image: -o-linear-gradient(top, #fefefe, #eaeaea);
+ background-image: linear-gradient(to bottom, #fefefe, #eaeaea);
+ -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
+ box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
+ color: #2e2e2e;
+ text-decoration: none;
+}
+.form-actions .button:active {
+ border: 1px solid #c8c8c8;
+ background: #fefefe;
+ background-image: -webkit-linear-gradient(top, #eaeaea, #fefefe);
+ background-image: -moz-linear-gradient(top, #eaeaea, #fefefe);
+ background-image: -o-linear-gradient(top, #eaeaea, #fefefe);
+ background-image: linear-gradient(to bottom, #eaeaea, #fefefe);
+ -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
+ box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
+ color: #2e2e2e;
+ text-decoration: none;
+ text-shadow: none;
+}
+/* Delete button */
+.form-actions .button-danger {
+ color: #c72100;
+ background: none;
+ border: none;
+ float: right;
+ margin-right: 0;
+ margin-left: 0;
+ padding-right: 0;
+ padding-left: 0;
+}
+.form-actions .button-danger:hover,
+.form-actions .button-danger:focus {
+ color: #ff2a00;
+ background: none;
+ border: none;
+ text-decoration: underline;
+}
+.form-actions .button-danger:active {
+ color: #ff2a00;
+ background: none;
+ border: none;
+ text-decoration: underline;
+}
+
+/**
+ * Form edit action theming
+ */
+.js .form-actions .dropbutton-widget {
+ background-color: #50a0e9;
+ background-image: -moz-linear-gradient(-90deg, #50a0e9, #4481dc);
+ background-image: -o-linear-gradient(-90deg, #50a0e9, #4481dc);
+ background-image: -webkit-linear-gradient(-90deg, #50a0e9, #4481dc);
+ background-image: linear-gradient(180deg, #50a0e9, #4481dc);
+ border-radius: 3px;
+ border: 1px solid #3974ae;
+}
+.js .form-actions .dropbutton-widget .dropbutton li {
+ border-top: 1px solid rgba(255, 255, 255, 0.5);
+ border-top-left-radius: 3px;
+}
+.js .form-actions .dropbutton-widget .dropbutton .dropbutton-toggle {
+ border-top-left-radius: 0px;
+ border-top-right-radius: 3px;
+ top: 1px;
+}
+.js .form-actions .dropbutton-widget .dropbutton .secondary-action {
+ border-top: 1px solid rgba(255, 255, 255, 0.3);
+ border-top-left-radius: 0px;
+}
+.js .form-actions .dropbutton-widget .button {
+ color: #ffffff;
+ text-shadow: 1px 1px 1px rgba(31, 83, 131, 0.8);
+}
+.js .form-actions .dropbutton-multiple.open .dropbutton-action:hover {
+ background-color: #50a0e9;
+}