Skip to content

Instantly share code, notes, and snippets.

@fabsor
Last active August 29, 2015 14:04
Show Gist options
  • Save fabsor/0ad96f5b051036199300 to your computer and use it in GitHub Desktop.
Save fabsor/0ad96f5b051036199300 to your computer and use it in GitHub Desktop.
Dynamic links and choosable add links for references dialog
diff --git a/js/references-dialog.js b/js/references-dialog.js
index 0c23b31..ac88071 100644
--- a/js/references-dialog.js
+++ b/js/references-dialog.js
@@ -1,52 +1,152 @@
(function ($) {
-
var $window = $(window);
-
Drupal.behaviors.referencesDialog = {
- attach: function (context, settings) {
- // Add appropriate classes on all fields that should have it. This is
- // necessary since we don't actually know what markup we are dealing with.
- if (typeof settings.ReferencesDialog !== 'undefined') {
- $.each(settings.ReferencesDialog.fields, function (key, widget_settings) {
- $('.' + key + ' a.references-dialog-activate', context).click(function (e) {
- e.preventDefault();
- Drupal.ReferencesDialog.open($(this).attr('href'), $(this).html());
- Drupal.ReferencesDialog.entityIdReceived = function (entity_type, entity_id, label) {
- if (typeof widget_settings.format !== 'undefined') {
- var value = widget_settings.format
- .replace('$label', label)
- .replace('$entity_id', entity_id)
- .replace('$entity_type', entity_type);
- }
- // If we have a callback path, let's invoke that.
- if (typeof widget_settings.callback_path !== 'undefined') {
- var entity_info = {
- label: label,
- entity_id: entity_id,
- entity_type: entity_type
- };
- Drupal.ReferencesDialog.invokeCallback(widget_settings.callback_path, entity_info, widget_settings.callback_settings);
- }
- // If we have a target, use that.
- else if (typeof widget_settings.target !== 'undefined') {
- var target = $('#' + widget_settings.target);
- target.val(value);
- target.change();
+
+ /**
+ * Manage edit links by doing access checks and adding edit links
+ * where appropriate.
+ * @param field_name the name of the field
+ * @param target the target jQuery element.
+ * @param the settings for the widget handling the field.
+ */
+ editLinkController: function (field_name, settings) {
+ var _this = this;
+ var dialog_selector = '.dialog-links.' + field_name + ' > ul';
+ return function () {
+ var val = $(this).val();
+ var result = val.match(settings.match)
+ if (result && result[1]) {
+ var entity_id = result[1];
+ var url = '$callback/$entity_type/$entity_id'
+ .replace('$callback', _this.settings.access_callback)
+ .replace('$entity_type', settings.entity_type)
+ .replace('$entity_id', entity_id)
+ $.get(url, function (data) {
+ if (data.access) {
+ // Add the edit link if it's not already outputted.
+ if ($(dialog_selector + ' .edit-dialog').size() === 0) {
+ $(dialog_selector)
+ .append('<li><a href="' + data.path +
+ '" class="edit-dialog references-dialog-activate">' +
+ Drupal.t('Edit') + '</a></li>');
+ $(dialog_selector + ' .edit-dialog')
+ .click(_this.dialogController(field_name, settings));
}
- // If we have none of the above, we just insert the value in the item
- // that invoked this.
else {
- var key_el = $('#' + key);
- key_el.val(value);
- key_el.change();
+ $(dialog_selector + ' .edit-dialog').attr('href', data.path)
+ .show();
}
}
- return false;
+ else {
+ $(dialog_selector + ' .edit-dialog').hide();
+ }
});
- });
+ }
+ else {
+ $(dialog_selector + ' .edit-dialog').hide();
+ }
+ };
+ },
+
+ /**
+ * Get the target jQuery element. This will not work if the
+ * widget settings are fetched from a callback.
+ * @return
+ * The jquery element of the target, or null if the target
+ * is fetched with a callback.
+ */
+ getTarget: function (field_name, settings) {
+ if (settings.target) {
+ return $('#' + widget_settings.target);
+ }
+ else if (!settings.callback_path){
+ return $('#' + field_name);
}
+ },
+
+ /**
+ * Hides and shows the add links as needed.
+ */
+ addLinkController: function (field_name) {
+ var dialog_selector = '.dialog-links.' + field_name + ' > ul';
+ return function () {
+ // Hide the add dialog.
+ var val = $(this).val();
+ if (val && val.length > 0) {
+ $(dialog_selector + ' .add-dialog').hide();
+ }
+ else {
+ $(dialog_selector + ' .add-dialog').show();
+ }
+ };
+ },
+
+ /**
+ * Manage opening and return values from dialogs.
+ * @param field_name the name of the field
+ * param settings settings for the widget.
+ * @return a function that can be used in an event.
+ */
+ dialogController: function (field_name, settings) {
+ var _this = this;
+ return function (e) {
+ e.preventDefault();
+ Drupal.ReferencesDialog.open($(this).attr('href'), $(this).html());
+ Drupal.ReferencesDialog.entityIdReceived = function (entity_type, entity_id, label) {
+ if (settings.format) {
+ var value = settings.format
+ .replace('$label', label)
+ .replace('$entity_id', entity_id)
+ .replace('$entity_type', entity_type);
+ }
+ var target = _this.getTarget(field_name, settings);
+ if (target) {
+ target.val(value);
+ target.change();
+ }
+ // We can't get the target in the cases where
+ // we have a custom callback.
+ else {
+ var entity_info = {
+ label: label,
+ entity_id: entity_id,
+ entity_type: entity_type
+ };
+ Drupal.ReferencesDialog
+ .invokeCallback(settings.callback_path, entity_info,
+ settings.callback_settings);
+ }
+ }
+ return false;
+ };
+ },
+ attach: function (context, settings) {
+ if (typeof settings.ReferencesDialog === 'undefined') {
+ return;
+ }
+ var _this = this;
+ this.settings = settings.ReferencesDialog;
+ this.context = context;
+ // Add appropriate classes on all fields that should have it. This is
+ // necessary since we don't actually know what markup we are dealing with.
+ $.each(this.settings.fields, function (key, widget_settings) {
+ var target = _this.getTarget(key, widget_settings);
+ // We can insert an edit link automatically if the widget provider
+ // supplied us with a match regexp and an entity type.
+ if (target && widget_settings.editable && widget_settings.match
+ && widget_settings.entity_type) {
+ var editController = _this.editLinkController(key, widget_settings);
+ var addController = _this.addLinkController(key);
+ addController.bind(target)(key);
+ target.change(addController);
+ target.blur(addController);
+ target.change(editController);
+ target.blur(editController);
+ }
+ // Attach a dialog controller to our links.
+ $('.' + key + ' a.references-dialog-activate', context).click(_this.dialogController(key, widget_settings));
+ });
}
- };
+ }
/**
* Our dialog object. Can be used to open a dialog to anywhere.
diff --git a/references_dialog.api.php b/references_dialog.api.php
index 73835c6..60a451d 100644
--- a/references_dialog.api.php
+++ b/references_dialog.api.php
@@ -17,6 +17,9 @@
* An array keyed by the widget you want to attach links to. This array
* should contain the following keys:
* - 'element_type': The type of FAPI element the widget is.
+ * - 'entity_type': The type of entity that is being created by this widget.
+ * - 'type_callback': Specify a callback if the entity may vary.
+ * - 'match': A regular expreession that matches the id from a textarea.
* - 'format': The format in which the data should be inserted as a value
* into the form element. The following patterns are provided, $label, $entity_id and $entity_type.
* - operations: An array of available operations.
diff --git a/references_dialog.dialog_widgets.inc b/references_dialog.dialog_widgets.inc
index 02efc48..150aa34 100644
--- a/references_dialog.dialog_widgets.inc
+++ b/references_dialog.dialog_widgets.inc
@@ -15,6 +15,7 @@ function references_dialog_references_dialog_widgets() {
'dialog_form' => 'node_reference_dialog_form',
'entity_type' => 'node',
'format' => '$label [nid: $entity_id]',
+ 'match' => 'nid:\s?([1-9][0-9]*)',
'views_query' => 'references_dialog_node_reference_views_query',
'operations' => array(
'search' => array(
@@ -27,6 +28,7 @@ function references_dialog_references_dialog_widgets() {
),
'add' => array(
'function' => 'references_dialog_node_reference_add_link',
+ 'settings_form' => 'references_dialog_node_reference_settings',
'title' => t('Add dialog'),
),
),
@@ -35,6 +37,7 @@ function references_dialog_references_dialog_widgets() {
'element_type' => 'textfield',
'entity_type' => 'user',
'format' => '$label [uid: $entity_id]',
+ 'match' => 'uid:\s?([1-9][0-9]*)',
'operations' => array(
'search' => array(
'function' => 'references_dialog_get_field_search_links',
@@ -56,6 +59,7 @@ function references_dialog_references_dialog_widgets() {
'views_query' => 'references_dialog_entityreference_views_query',
'type_callback' => 'references_dialog_entityreference_get_type',
'format' => '$label ($entity_id)',
+ 'match' => '\(([1-9][0-9]*)\)',
'operations' => array(
'edit' => array(
'function' => 'references_dialog_entityreference_edit_link',
@@ -95,6 +99,29 @@ function references_dialog_references_dialog_widgets() {
);
}
+function references_dialog_node_reference_settings($form, $operation_settings, $widget, $field, $instance) {
+ $types = array();
+ foreach ($field['settings']['referenceable_types'] as $type => $enabled) {
+ if (!empty($enabled)) {
+ $types[$type] = $enabled;
+ }
+ }
+ $form['types'] = array(
+ '#title' => t('Show links for:'),
+ '#type' => 'checkboxes',
+ '#options' => $types,
+ '#default_value' => !empty($operation_settings['links']) ? $operation_settings['links'] : array(),
+ '#states' => array(
+ 'visible' => array(
+ 'input[name="instance[widget][settings][references_dialog_add][enabled]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ return $form;
+}
+
+
+
/**
* Edit link callback for node references.
*/
@@ -127,14 +154,15 @@ function references_dialog_node_reference_edit_link($element, $widget_settings,
/**
* Add link callback for node references.
*/
-function references_dialog_node_reference_add_link($element, $widget_settings, $field, $instance) {
- // Hide add link for non-empty default value.
- if (!empty($element['#default_value']) || !empty($element['#value'])) {
- return array();
- }
-
+function references_dialog_node_reference_add_link($element, $widget_settings, $field, $instance, $operation_settings) {
$add_links = array();
- foreach ($field['settings']['referenceable_types'] as $type => $active) {
+ if (isset($operation_settings['types'])) {
+ $types = $operation_settings['types'];
+ }
+ else {
+ $types = $field['settings']['referenceable_types'];
+ }
+ foreach ($types as $type => $active) {
if ($active !== 0) {
$node_type = node_type_load($type);
if (node_access('create', $node_type->type)) {
@@ -197,11 +225,6 @@ function references_dialog_user_reference_edit_link($element, $widget_settings,
* Add link callback for user references.
*/
function references_dialog_user_reference_add_link($element, $widget_settings, $field, $instance) {
- // Hide add link for non-empty default value.
- if (!empty($element['#default_value']) || !empty($element['#value'])) {
- return array();
- }
-
$user_links = array();
// Check permissions for adding users.
if (user_access('administer users')) {
@@ -243,11 +266,6 @@ function references_dialog_entityreference_edit_link($element, $widget_settings,
* Add link callback for entity references.
*/
function references_dialog_entityreference_add_link($element, $widget_settings, $field, $instance) {
- // Hide add link for non-empty default value.
- if (!empty($element['#default_value']) || !empty($element['#value'])) {
- return array();
- }
-
$add_links = array();
$entity_type = $field['settings']['target_type'];
$entity_info = entity_get_info($entity_type);
diff --git a/references_dialog.module b/references_dialog.module
index 26b203e..ffb9734 100644
--- a/references_dialog.module
+++ b/references_dialog.module
@@ -22,7 +22,17 @@ function references_dialog_element_info() {
*/
function references_dialog_attached() {
return array(
- 'js' => array(drupal_get_path('module', 'references_dialog') . '/js/references-dialog.js'),
+ 'js' => array(
+ drupal_get_path('module', 'references_dialog') . '/js/references-dialog.js',
+ array(
+ 'data' => array(
+ 'ReferencesDialog' => array(
+ 'access_callback' => url('references-dialog/access'),
+ ),
+ ),
+ 'type' => 'setting',
+ ),
+ ),
'css' => array(drupal_get_path('module', 'references_dialog') . '/css/references-dialog-admin.css'),
'library' => array(array('system', 'ui.dialog')),
);
@@ -56,9 +66,25 @@ function references_dialog_menu() {
'access callback' => 'references_dialog_redirect_access',
'access arguments' => array(2, 3),
);
+ // Determine if we have access to create or update entities
+ // of a particular type.
+ $items['references-dialog/access/%/%'] = array(
+ 'page callback' => 'references_dialog_entity_access',
+ 'page arguments' => array(2,3),
+ 'access callback' => TRUE,
+ );
return $items;
}
+function references_dialog_entity_access($entity_type, $entity) {
+ $entity = entity_load_single($entity_type, $entity);
+ return drupal_json_output(array(
+ 'access' => !empty($entity) && entity_access('update', $entity_type, $entity),
+ 'path' => url(references_dialog_get_admin_path($entity_type, 'edit', NULL, $entity)),
+ )
+ );
+}
+
/**
* Implements hook_admin_paths().
*/
@@ -136,8 +162,14 @@ function references_dialog_get_applicable_views() {
function references_dialog_field_widget_info_alter(array &$info) {
foreach (references_dialog_widgets() as $widget_name => $widget_info) {
if (isset($info[$widget_name]['settings'])) {
- foreach (array_keys($widget_info['operations']) as $operation) {
- $info[$widget_name]['settings']['references_dialog_' . $operation] = 0;
+ foreach ($widget_info['operations'] as $operation => $operation_settings) {
+ $info[$widget_name]['settings']['references_dialog_' . $operation] = array(
+ 'enabled' => FALSE,
+ );
+ // Add any default settings provided by the operation.
+ if (!empty($operation_settings['settings'])) {
+ $info[$widget_name]['settings']['references_dialog_' . $operation] + $operation_settings['settings'];
+ }
// Add search view setting if we have search.
if ($operation == 'search') {
$info[$widget_name]['settings']['references_dialog_search_view'] = '';
@@ -270,15 +302,25 @@ function references_dialog_settings_form($field, $instance) {
$dialog_widget = references_dialog_widget_load($widget['type']);
// Add our own additions.
foreach ($dialog_widget['operations'] as $operation => $dialog_settings) {
- $form['references_dialog_' . $operation] = array(
+ $form['references_dialog_' . $operation] = array('#tree' => TRUE);
+ $form['references_dialog_' . $operation]['enabled'] = array(
'#type' => 'checkbox',
'#title' => check_plain($dialog_settings['title']),
- '#default_value' => isset($settings['references_dialog_' . $operation]) ? $settings['references_dialog_' . $operation] : FALSE,
+ '#default_value' => _references_dialog_operation_enabled($operation, $settings),
);
}
+ if (!empty($dialog_settings['settings_form']) && function_exists($dialog_settings['settings_form'])) {
+ $form['references_dialog_' . $operation] = $dialog_settings['settings_form']($form['references_dialog_' . $operation], $settings['references_dialog_' . $operation], $dialog_widget, $field, $instance);
+ }
return $form;
}
+function _references_dialog_operation_enabled($operation, $settings) {
+ return isset($settings['references_dialog_' . $operation]) &&
+ (is_array($settings['references_dialog_' . $operation]) && $settings['references_dialog_' . $operation]['enabled'] ||
+ (!is_array($settings['references_dialog_' . $operation]) && $settings['references_dialog_' . $operation]));
+}
+
/**
* Menu access checker for references_dialog
*/
@@ -308,21 +350,35 @@ function references_dialog_process_widget(&$element) {
// Attach javascript and CSS needed.
$attached = references_dialog_attached();
$element['#attached']['js'][] = $attached['js'][0];
- $element['#attached']['js'][] = references_dialog_js_settings($element['#id'], array('format' => $dialog_widget['format']));
+ $element['#attached']['js'][] = $attached['js'][1];
+
+ $js_settings = array(
+ 'format' => $dialog_widget['format'],
+ 'match' => !empty($dialog_widget['match']) ? $dialog_widget['match'] : NULL,
+ );
+ if (isset($dialog_widget['entity_type'])) {
+ $js_settings['entity_type'] = $dialog_widget['entity_type'];
+ }
+ elseif (isset($dialog_widget['type_callback'])) {
+ $js_settings['entity_type'] = $dialog_widget['type_callback']($instance, $field);
+ }
$element['#attached']['css'][] = $attached['css'][0];
$element['#attached']['library'][] = $attached['library'][0];
-
$link_options = array('attributes' => array('class' => array('references-dialog-activate')));
$dialog_links = array();
foreach ($dialog_widget['operations'] as $operation => $settings) {
- if (isset($widget_settings['references_dialog_' . $operation]) && $widget_settings['references_dialog_' . $operation]) {
- $links = $settings['function']($element, $widget_settings, $field, $instance);
+ if (_references_dialog_operation_enabled($operation, $widget_settings)) {
+ if ($operation == 'edit') {
+ $js_settings['editable'] = TRUE;
+ }
+ $links = $settings['function']($element, $widget_settings, $field, $instance, $widget_settings['references_dialog_' . $operation]);
foreach ($links as $link) {
$link['attributes']['class'][] = $operation . '-dialog';
$dialog_links[] = references_dialog_link($link);
}
}
}
+ $element['#attached']['js'][] = references_dialog_js_settings($element['#id'], $js_settings);
if (count($dialog_links)) {
// We add a div directly into the markup here since we really need it in order
// to make sure the javascript works.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment