{"action":"created","comment":{"url":"https://api.github.com/repos/ding2/ding2/pulls/comments/78732382","id":78732382,"diff_hunk":"@@ -0,0 +1,2477 @@\n+<?php\n+/**\n+ * @file\n+ * Helps creating customizable lists of content that can be placed on a panel.\n+ */\n+\n+include_once 'ding_nodelist.features.inc';\n+\n+define('NL_WIDGET_LIST', 'simple');\n+define('NL_WIDGET_SINGLE', 'single');\n+define('NL_WIDGET_CAROUSEL', 'carousel');\n+define('NL_WIDGET_HOR_ACCORDION', 'horizontal_accordion');\n+define('NL_WIDGET_VER_ACCORDION', 'vertical_accordion');\n+define('NL_WIDGET_SLIDER', 'slider');\n+define('NL_WIDGET_TAXONOMY_LIKE', 'taxonomy');\n+\n+define('NL_TPL_ACTIVE', 0);\n+define('NL_TPL_HIDDEN', 1);\n+\n+define('NL_CACHE_LIFETIME', 1800);\n+define('NL_TITLE_SIZE', 25);\n+define('NL_BODY_SIZE', 100);\n+\n+/**\n+ * Implements hook_menu().\n+ */\n+function ding_nodelist_menu() {\n+  $items = array();\n+\n+  $items['admin/config/ding/ding_nodelist'] = array(\n+    'title' => 'Ding nodelist',\n+    'description' => 'View and customize lists of content.',\n+    'page callback' => 'drupal_get_form',\n+    'page arguments' => array('ding_nodelist_settings_form'),\n+    'access arguments' => array('configure nodelist'),\n+    'type' => MENU_NORMAL_ITEM,\n+    'file' => 'ding_nodelist.admin.inc',\n+  );\n+\n+  $items['admin/config/ding/ding_nodelist/settings'] = array(\n+    'title' => 'Settings',\n+    'description' => 'Configure general settings.',\n+    'tab_parent' => 'admin/config/ding/ding_nodelist',\n+    'type' => MENU_DEFAULT_LOCAL_TASK,\n+    'file' => 'ding_nodelist.admin.inc',\n+  );\n+\n+  $items['admin/config/ding/ding_nodelist/templates'] = array(\n+    'title' => 'Templates',\n+    'description' => 'Manage content templates.',\n+    'page callback' => 'drupal_get_form',\n+    'page arguments' => array('ding_nodelist_templates_form'),\n+    'access arguments' => array('configure nodelist'),\n+    'type' => MENU_LOCAL_TASK,\n+    'file' => 'ding_nodelist.admin.inc',\n+  );\n+\n+  $items['admin/config/ding/ding_nodelist/trim_content'] = array(\n+    'title' => 'Trim content',\n+    'description' => 'Define title and teaser trim parameters for each widget',\n+    'page callback' => 'drupal_get_form',\n+    'page arguments' => array('ding_nodelist_trim_content'),\n+    'access arguments' => array('configure nodelist'),\n+    'type' => MENU_LOCAL_TASK,\n+    'file' => 'ding_nodelist.admin.inc',\n+  );\n+\n+  $items['ding_nodelist/autocomplete/%'] = array(\n+    'page callback' => 'ding_nodelist_autocomplete',\n+    'page arguments' => array(2),\n+    'access arguments' => array('access content'),\n+    'type' => MENU_CALLBACK,\n+  );\n+\n+  return $items;\n+}\n+\n+/**\n+ * Implements hook_permission().\n+ */\n+function ding_nodelist_permission() {\n+  return array(\n+    'configure nodelist' => array(\n+      'title' => t('Configure nodelist'),\n+      'description' => t('Configure the behavior of nodelist'),\n+    ),\n+    'nodelist cache settings' => array(\n+      'title' => t('Nodelist cache'),\n+      'description' => t('Manage nodelist cache settings'),\n+    ),\n+  );\n+}\n+\n+/**\n+ * Implements hook_ctools_plugin_directory().\n+ */\n+function ding_nodelist_ctools_plugin_directory($module, $plugin) {\n+  // we'll be nice and limit scandir() calls.\n+  if ($module == 'ctools' && $plugin == 'content_types') {\n+    return 'plugins/content_types';\n+  }\n+}\n+\n+/**\n+ * Implements hook_ajax_render_alter().\n+ *\n+ * This method is a workaround to impossiblity\n+ * of telling Drupal the order of CSS files in order to\n+ * cascade styles properly. See the link.\n+ *\n+ * @link http://drupal.org/node/561858#comment-3171846\n+ *\n+ * @global boolean $_ding_nodelist_ipe_mode\n+ */\n+function ding_nodelist_ajax_render_alter(&$commands = array()) {\n+  global $_ding_nodelist_ipe_mode, $_ding_nodelist_ipe_redirect;\n+\n+  if ($_ding_nodelist_ipe_mode) {\n+    $path = drupal_get_path('module', 'ding_nodelist') . \"/css/ipe.css\";\n+    $styles = '<style type=\"text/css\" media=\"all\">@import url(\"' . $path . '\");</style>';\n+    $commands[] = ajax_command_append('head', $styles);\n+  }\n+\n+  if ($_ding_nodelist_ipe_redirect) {\n+    ctools_include('ajax');\n+    ctools_add_js('ajax-responder');\n+    $commands[] = ctools_ajax_command_redirect($_ding_nodelist_ipe_redirect);\n+  }\n+}\n+\n+/**\n+ * Implements hook_BASE_FORM_ID_alter().\n+ *\n+ * Add custom handler to IPE Save/Cancel buttons to force page reload.\n+ */\n+function ding_nodelist_form_panels_ipe_edit_control_form_alter(&$form, &$form_state) {\n+  $form['buttons']['submit']['#submit'][] = '_ding_nodelist_ipe_buttons_submit';\n+  $form['buttons']['cancel']['#submit'][] = '_ding_nodelist_ipe_buttons_submit';\n+  // Add necessary scripts for later call in ding_nodelist_ajax_render_alter().\n+  ctools_include('ajax');\n+  ctools_add_js('ajax-responder');\n+}\n+\n+/**\n+ * Save/Cancel IPE buttons submit handler.\n+ */\n+function _ding_nodelist_ipe_buttons_submit($form, &$form_state) {\n+  global $_ding_nodelist_ipe_redirect;\n+  $_ding_nodelist_ipe_redirect = $_SERVER['HTTP_REFERER'];\n+}\n+\n+/**\n+ * Implements hook_BASE_FORM_ID_alter().\n+ *\n+ * Add custom handler to Node save button to clear panel cache.\n+ */\n+function ding_nodelist_form_node_form_alter(&$form, &$form_state) {\n+  $types = variable_get('ding_nodelist_node_types', array());\n+  $node_type = $form_state['node']->type;\n+  if ($types[$node_type] === $node_type) {\n+    $form['actions']['submit']['#submit'][] = '_ding_nodelist_clear_panel_cache_submit';\n+  }\n+}\n+\n+/**\n+ * Clear panels cache on node unpublishing.\n+ *\n+ * @param array $form\n+ *   Submitted form array.\n+ * @param array $form_state\n+ *   Array of states of the submitted form.\n+ */\n+function _ding_nodelist_clear_panel_cache_submit($form = array(), &$form_state = array()) {\n+  $status = $form_state['values']['status'];\n+  if ($status == 0) {\n+    cache_clear_all('*', 'cache_panels', TRUE);\n+  }\n+}\n+\n+/**\n+ * Ctools plugin callback for settings form.\n+ */\n+function ding_nodelist_content_type_edit_form($form, &$form_state) {\n+  drupal_add_css(drupal_get_path('module', 'ding_nodelist') . '/css/ding_nodelist-admin.css', 'file');\n+  global $_ding_nodelist_ipe_mode;\n+\n+  // Tell to load additional CSS to make edit form look normally\n+  // in case of IPE.\n+  if (strpos($form_state['form_info']['path'], '/ipe/')) {\n+    $_ding_nodelist_ipe_mode = TRUE;\n+  }\n+\n+  // Cache the form for correct workflow.\n+  $form_state['cache'] = TRUE;\n+\n+  // Save original form to restore it later.\n+  // @todo: remove this workaround code and find ctools/drupal-way approach.\n+  if (!isset($form_state['restore'])) {\n+    $form_state['original_form'] = $form;\n+  }\n+\n+  $trigger = !empty($form_state['triggering_element']) ? $form_state['triggering_element'] : NULL;\n+\n+  // CT/widget select change handlers.\n+  // Process them first since they affect the rest of the form.\n+  if ($trigger) {\n+    if ($trigger['#name'] == 'content_type') {\n+      // Refresh CT setting.\n+      $node_type = $form_state['triggering_element']['#value'];\n+\n+      // Reset other form values.\n+      $form_state['values']['content_type'] = $node_type;\n+      $form_state['values']['selected_nodes'] = array();\n+      $form_state['values']['widget_type'] = '';\n+      $form_state['values']['taxonomy_filters'] = array();\n+      unset($form_state['input']);\n+      _ding_nodelist_content_type_form_save($form_state, TRUE);\n+    }\n+    elseif ($trigger['#name'] == 'widget_type') {\n+      $node_type = $form_state['values']['content_type'];\n+      // Reset default scrolling delay.\n+      unset($form_state['input']['autoscroll_delay']);\n+      $form_state['values']['autoscroll_delay'] = '';\n+\n+      // Reset nodelist title.\n+      unset($form_state['input']['override_title_text']);\n+      $form_state['values']['override_title_text'] = '';\n+\n+      _ding_nodelist_content_type_form_save($form_state, TRUE);\n+    }\n+  }\n+\n+  $conf = $form_state['conf'];\n+\n+  // Show only allowed content types.\n+  $node_types = variable_get('ding_nodelist_node_types', array());\n+  $all_node_types = node_type_get_names();\n+  $node_types_opts = array();\n+  foreach ($node_types as $nt => $active) {\n+    if ($active) {\n+      $node_types_opts[$nt] = $all_node_types[$nt];\n+    }\n+  }\n+\n+  if (empty($node_types_opts)) {\n+    drupal_set_message(t('No eligible content types were found, please configure a few first.'), 'error', FALSE);\n+  }\n+\n+  $node_type = isset($conf['content_type']) ? $conf['content_type'] : current(array_keys($node_types));\n+\n+  $default_conf = array(\n+    'content_type' => $node_type,\n+    'widget_type' => NL_WIDGET_CAROUSEL,\n+    'sort_field' => 'created',\n+    'limit' => 5,\n+    'more_text' => t('More'),\n+    'more_link' => '',\n+    'teaser_length' => 100,\n+    'autoscroll_delay' => 3000,\n+  );\n+\n+  $form['#prefix'] = '<div id=\"ding_nodelist-form-wrapper\">';\n+  $form['#suffix'] = '</div>';\n+\n+  // Initial settings.\n+  $form['ding_nodelist_wrapper'] = array(\n+    '#type' => 'fieldset',\n+    '#collapsible' => FALSE,\n+    '#tree' => FALSE,\n+    '#title' => t('Configure nodelist'),\n+    '#collapsible' => FALSE,\n+  );\n+\n+  $form['ding_nodelist_wrapper']['content_type'] = array(\n+    '#type' => 'select',\n+    '#title' => t('Content type'),\n+    '#required' => TRUE,\n+    '#default_value' => $node_type,\n+    '#options' => $node_types_opts,\n+    '#description' => t(\"Select the content type of the list's items.\"),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-selected-nodes',\n+      'callback' => 'ding_nodelist_js',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+  );\n+\n+  // Set up all the available view modes for this type of nodes.\n+  $entity = entity_get_info('node');\n+  $node_formats = array();\n+  foreach ($entity['view modes'] as $view_mode => $settings) {\n+    $node_formats[$view_mode] = $settings['label'];\n+  }\n+\n+  $widget_types = _ding_nodelist_get_widget_types($node_type);\n+  $widget_type = isset($conf['widget_type']) ? $conf['widget_type'] : $default_conf['widget_type'];\n+  $form['ding_nodelist_wrapper']['widget_type'] = array(\n+    '#type' => 'select',\n+    '#title' => t('List type'),\n+    '#required' => TRUE,\n+    '#default_value' => $widget_type,\n+    '#options' => $widget_types,\n+    '#empty_option' => t('- Select -'),\n+    '#description' => t('If single item selected, only the first item will be rendered'),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-item-template',\n+      'callback' => 'ding_nodelist_js_template',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+    '#prefix' => '<div class=ding_nodelist-widget-config>',\n+  );\n+\n+  $form['ding_nodelist_wrapper']['widget_dependent'] = array(\n+    '#type' => 'container',\n+    '#prefix' => '<div id=\"ding_nodelist-item-template\">',\n+    '#suffix' => '</div></div>',\n+  );\n+\n+  $autoscrolling_widgets = array(NL_WIDGET_CAROUSEL, NL_WIDGET_HOR_ACCORDION);\n+  if (in_array($widget_type, $autoscrolling_widgets)) {\n+    $generic_autoscroll_delay = variable_get('ding_nodelist_' . $widget_type . '_delay', $default_conf['autoscroll_delay']);\n+    $form['ding_nodelist_wrapper']['widget_dependent']['autoscroll_delay'] = array(\n+      '#type' => 'textfield',\n+      '#title' => t('Delay (miliseconds)'),\n+      '#default_value' => !empty($conf['autoscroll_delay']) ? $conf['autoscroll_delay'] : $generic_autoscroll_delay,\n+      '#description' => t('Time in milliseconds an item will be shown before switching to the next one'),\n+      '#size' => 4,\n+    );\n+  }\n+\n+  // Make title appear only on some widgets.\n+  $widgets_with_title = _ding_nodelist_widgets_with_titles();\n+  if (in_array($widget_type, $widgets_with_title)) {\n+    $form['ding_nodelist_wrapper']['widget_dependent']['override_title_text'] = array(\n+      '#type' => 'textfield',\n+      '#title' => t('List title'),\n+      '#description' => t('Enter a title to be shown above the list items. Leave empty to omit the title.') . ' ' .\n+      t('You may use %keywords from contexts, as well as %title to contain the original title.'),\n+      '#required' => FALSE,\n+      '#default_value' => isset($conf['override_title_text']) ? $conf['override_title_text'] : '',\n+    );\n+  }\n+\n+  // Manually controlled list.\n+  $form['ding_nodelist_wrapper']['nodelist_content'] = array(\n+    '#type' => 'fieldset',\n+    '#title' => t('Content'),\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['list_type'] = array(\n+    '#type' => 'radios',\n+    '#options' => array(\n+      'custom-dynamic' => t('Custom / Dynamic'),\n+      'all' => t('All content'),\n+    ),\n+    '#default_value' => isset($conf['list_type']) ? $conf['list_type'] : 'all',\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper'] = array(\n+    '#type' => 'container',\n+    '#states' => array(\n+      'visible' => array(\n+        ':input[name=list_type]' => array('value' => 'custom-dynamic'),\n+      ),\n+    ),\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['nodes_switch'] = array(\n+    '#type' => 'checkbox',\n+    '#title' => t('Custom content?'),\n+    '#prefix' => '<hr />',\n+    '#default_value' => isset($conf['nodes_switch']) ? $conf['nodes_switch'] : 0,\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes'] = array(\n+    '#type' => 'container',\n+    '#collapsible' => FALSE,\n+    '#tree' => FALSE,\n+    '#title' => t('Custom item list'),\n+    '#collapsible' => FALSE,\n+    '#prefix' => '<div class=\"clear-block\" id=\"ding_nodelist-wrapper\">',\n+    '#suffix' => '</div>',\n+    '#states' => array(\n+      'invisible' => array(\n+        ':input[name=nodes_switch]' => array('checked' => FALSE),\n+      ),\n+    ),\n+  );\n+\n+  // Render selected nodes.\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['selected_nodes'] = array(\n+    '#prefix' => '<div id=\"ding_nodelist-selected-nodes\">',\n+    '#suffix' => '</div>',\n+    '#theme' => '_ding_nodelist_items_table',\n+    '#tree' => TRUE,\n+  );\n+\n+  $delta = 0;\n+  $weight = 0;\n+  $max_weight = $weight;\n+  $node_count = NULL;\n+  $filter_count = NULL;\n+\n+  if (isset($form_state['values']['selected_nodes_count'])) {\n+    $node_count = $form_state['values']['selected_nodes_count'];\n+  }\n+  else {\n+    $node_count = empty($form_state['values']['selected_nodes']) ? 1 : count($form_state['values']['selected_nodes']);\n+  }\n+\n+  if (!empty($conf['selected_nodes'])) {\n+    $delta = count($conf['selected_nodes']);\n+    foreach ($conf['selected_nodes'] as $key => $node) {\n+      $weight = $node['weight'];\n+      $max_weight = ($max_weight < $weight) ? $weight : $max_weight;\n+      $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['selected_nodes'][$key] = _ding_nodelist_selected_node($key, $node_type, $node, $weight);\n+    }\n+  }\n+\n+  // Add new node field.\n+  $weight = ++$max_weight;\n+  for (; $delta < $node_count; $delta++) {\n+    $key = $delta;\n+    $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['selected_nodes'][$key] = _ding_nodelist_selected_node($key, $node_type, NULL, $weight);\n+  }\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['add_node'] = array(\n+    // Used to identify the button in validate/submit handler(s).\n+    '#name' => 'add_node',\n+    '#parents' => array('ding_nodelist_nodes', 'add_node'),\n+    '#type' => 'submit',\n+    '#value' => t('Add content'),\n+    '#weight' => 1,\n+    '#submit' => array('ding_nodelist_add_node_submit'),\n+    '#limit_validation_errors' => array(array('selected_nodes')),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-selected-nodes',\n+      'callback' => 'ding_nodelist_js_add_node',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['dynamic_switch'] = array(\n+    '#type' => 'checkbox',\n+    '#title' => t('Dynamic content?'),\n+    '#prefix' => '<hr />',\n+    '#default_value' => isset($conf['dynamic_switch']) ? $conf['dynamic_switch'] : 0,\n+  );\n+\n+  // Dynamic list settings.\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic'] = array(\n+    '#type' => 'container',\n+    '#states' => array(\n+      'invisible' => array(\n+        ':input[name=dynamic_switch]' => array('checked' => FALSE),\n+      ),\n+    ),\n+  );\n+\n+  // Render selected filters.\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'] = array(\n+    '#type' => 'container',\n+    '#prefix' => '<div id=\"ding_nodelist-taxonomy-filters\">',\n+    '#suffix' => '</div>',\n+    '#theme' => '_ding_nodelist_filters_table',\n+    '#tree' => TRUE,\n+  );\n+\n+  if (isset($form_state['values']['taxonomy_filters_count'])) {\n+    $filter_count = $form_state['values']['taxonomy_filters_count'];\n+  }\n+  else {\n+    $filter_count = empty($form_state['values']['taxonomy_filters']) ? 1 : count($form_state['values']['taxonomy_filters']);\n+  }\n+\n+  // Retrieve vocabularies current CT references.\n+  $allowed_fields = array();\n+  $field_settings = array();\n+  $fields = field_info_instances('node', $node_type);\n+  foreach ($fields as $field) {\n+    $field_config = field_info_field($field['field_name']);\n+\n+    // Check if the field is a term reference or entity reference field.\n+    $allowed_types = array('taxonomy_term_reference', 'entityreference');\n+    if (in_array($field_config['type'], $allowed_types)) {\n+      $allowed_fields[$field['field_name']] = $field['label'];\n+      $field_settings[$field['field_name']] = array(\n+        'filter_field' => $field_config['field_name'],\n+        'term_reference' => $field_config['type'],\n+      );\n+      if ($field_config['type'] == 'entityreference') {\n+        $field_settings[$field['field_name']]['bundle'] = $node_type;\n+        $field_settings[$field['field_name']]['type'] = $field_config['settings']['target_type'];\n+      }\n+      else {\n+        $field_settings[$field['field_name']]['vocabulary'] = $field_config['settings']['allowed_values'][0]['vocabulary'];\n+      }\n+    }\n+  }\n+  // Check if filter vocabulary was changed.\n+  $changed_filter_id = NULL;\n+  if (!empty($form_state['triggering_element']) && isset($form_state['triggering_element']['#parents'][2])\n+    && $form_state['triggering_element']['#parents'][2] == 'filter_field') {\n+    $changed_filter_id = $form_state['triggering_element']['#parents'][1];\n+    $changed_filter = $form_state['values']['taxonomy_filters'][$changed_filter_id];\n+\n+    // Update auto-complete, preserve other fields.\n+    $filter = $field_settings[$changed_filter['filter_field']];\n+\n+    // Important: delete old input!\n+    unset($form_state['input']['taxonomy_filters'][$changed_filter_id]);\n+    $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'][$changed_filter_id] = _ding_nodelist_taxonomy_filter($allowed_fields, $filter, $changed_filter_id);\n+  }\n+\n+  $delta = 0;\n+  if (!empty($conf['taxonomy_filters'])) {\n+    $delta = count($conf['taxonomy_filters']);\n+    foreach ($conf['taxonomy_filters'] as $key => $filter) {\n+      // Skip just changed filter.\n+      if ($changed_filter_id === $key) {\n+        continue;\n+      }\n+\n+      $tags = array();\n+      if ($filter['term_reference'] == 'entityreference') {\n+        foreach ($filter['filter_terms'] as $nid) {\n+          $title = db_select('node', 'n', array('target' => 'slave'))\n+            ->fields('n', array('title'))\n+            ->condition('n.nid', $nid)\n+            ->execute()\n+            ->fetchField();\n+          $tags[$nid] = $title . ' (' . $nid . ')';\n+        }\n+        $values = implode(', ', $tags);\n+      }\n+      else {\n+        foreach ($filter['filter_terms'] as $tid) {\n+          $tags[$tid] = taxonomy_term_load($tid);\n+        }\n+        $values = taxonomy_implode_tags($tags);\n+      }\n+      $filter = $field_settings[$filter['filter_field']];\n+      $filter['filter_terms'] = $values;\n+\n+      $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'][$key] = _ding_nodelist_taxonomy_filter($allowed_fields, $filter, $key);\n+    }\n+\n+    ksort($form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters']);\n+  }\n+\n+  // Add new filter field.\n+  for (; $delta < $filter_count; $delta++) {\n+    $key = $delta;\n+    if ($changed_filter_id === $key) {\n+      continue;\n+    }\n+\n+    // Set default field value.\n+    $first_field = array_shift(array_keys($allowed_fields));\n+    $filter = $field_settings[$first_field];\n+    $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'][$key] = _ding_nodelist_taxonomy_filter($allowed_fields, $filter, $key);\n+  }\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['help_text'] = array(\n+    '#prefix' => '<div class=\"ding-nodelist-help-text\">',\n+    '#suffix' => '</div>',\n+    '#markup' => t('Explain the OR/AND syntax.'),\n+    '#weight' => 9,\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['add_filter'] = array(\n+    '#name' => 'add_filter',\n+    '#type' => 'submit',\n+    '#value' => t('Add filter'),\n+    '#weight' => 10,\n+    '#submit' => array('ding_nodelist_add_filter_submit'),\n+    '#limit_validation_errors' => array(array('taxonomy_filters')),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-taxonomy-filters',\n+      'callback' => 'ding_nodelist_js_add_filter',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+  );\n+\n+  // Content filter.\n+  $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['content_filter_promoted'] = array(\n+    '#type' => 'checkbox',\n+    '#weight' => 11,\n+    '#title' => t('Show \"Promoted to front page\" content only'),\n+    '#default_value' => isset($conf['content_filter_promoted']) ? $conf['content_filter_promoted'] : FALSE,\n+  );\n+\n+  // Date field filtering.\n+  $content_filter_date = variable_get('content_filter_date', 0);\n+  $form['ding_nodelist_wrapper']['nodelist_content']['content_filter_date'] = array(\n+    '#type' => $node_type == 'ding_event' ? 'checkbox' : 'hidden',\n+    '#title' => t('Show upcoming events only'),\n+    '#description' => t('If checked only future events will be shown'),\n+    '#default_value' => $node_type == 'ding_event' ? $content_filter_date : 1,\n+    '#prefix' => '<div id=\"ding_nodelist-content-filter-date\">',\n+    '#suffix' => '</div>',\n+  );\n+\n+  // Various options.\n+  $form['ding_nodelist_wrapper']['nodelist_various_settings'] = array(\n+    '#type' => 'fieldset',\n+    '#title' => t('Various'),\n+    '#states' => array(\n+      'invisible' => array(\n+        'select[name=widget_type]' => array('value' => NL_WIDGET_SINGLE),\n+      ),\n+    ),\n+  );\n+\n+  // Initialize drop-down array's values.\n+  $options = array(0 => 'none') +\n+    array_combine(range(1, 25), range(1, 25)) +\n+    array_combine(range(30, 50, 5), range(30, 50, 5)) +\n+    array_combine(range(50, 100, 10), range(50, 100, 10));\n+\n+  $form['ding_nodelist_wrapper']['nodelist_various_settings']['limit'] = array(\n+    '#type' => 'select',\n+    '#title' => t('Item limit'),\n+    '#default_value' => isset($conf['limit']) ? $conf['limit'] : $default_conf['limit'],\n+    '#options' => $options,\n+    '#description' => t('Limit amount of items displayed. This value is used only for Dynamic and All content displays.'),\n+    '#states' => array(\n+      'invisible' => array(\n+        ':input[name=nodes_switch]' => array('checked' => TRUE),\n+        ':input[name=dynamic_switch]' => array('checked' => FALSE),\n+      ),\n+      'visible' => array(\n+        ':input[name=list_type]' => array(\n+          array('value' => 'custom-dynamic'),\n+          array('value' => 'all'),\n+        ),\n+      ),\n+    ),\n+  );\n+\n+  $form['ding_nodelist_wrapper']['nodelist_various_settings']['ding_nodelist_sorting'] = array(\n+    '#type' => 'container',\n+  );\n+\n+  $sort_options = array(\n+    'created' => t('Creation date'),\n+    'title' => t('Title'),\n+    'sticky' => t('Sticky first'),\n+  );\n+\n+  if ($node_type == 'ding_event') {\n+    $sort_options['event_date'] = t('Event date');\n+    $conf['sort_field'] = variable_get('ding_nodelist_event_sort', 'event_date');\n+  }\n+  elseif ($node_type == 'ding_news') {\n+    $conf['sort_field'] = variable_get('ding_nodelist_news_sort', 'created');\n+  }\n+\n+  $form['ding_nodelist_wrapper']['nodelist_various_settings']['ding_nodelist_sorting']['sort_field'] = array(\n+    '#type' => 'select',\n+    '#title' => t('Sort by'),\n+    '#options' => $sort_options,\n+    '#empty_option' => t('No sorting'),\n+    '#default_value' => ($conf['sort_field']) ? $conf['sort_field'] : '',\n+    '#prefix' => '<div id=\"ding_nodelist-sort-field\">',\n+    '#suffix' => '</div>',\n+  );\n+\n+  $sort_order = isset($conf['sort_order']) ? $conf['sort_order'] : '';\n+  $form['ding_nodelist_wrapper']['nodelist_various_settings']['ding_nodelist_sorting']['sort_order'] = array(\n+    '#type' => 'select',\n+    '#title' => t('Sorting order'),\n+    '#options' => array(\n+      'asc' => t('Ascending'),\n+      'desc' => t('Descending'),\n+    ),\n+    '#default_value' => $sort_order,\n+    '#states' => array(\n+      // Hide the settings when the sorting order is not applicable.\n+      'invisible' => array(\n+        ':input[name=\"sort_field\"]' => array(array('value' => ''), array('value' => 'sticky')),\n+      ),\n+    ),\n+  );\n+\n+  // Forms for Bottom settings.\n+  $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links'] = array(\n+    '#type' => 'fieldset',\n+    '#title' => t('Additional links'),\n+    '#collapsible' => FALSE,\n+    '#description' => t('List of \"read more\" links.'),\n+    '#states' => array(\n+      'visible' => array(\n+        'select[name=\"widget_type\"]' => array('value' => 'slider'),\n+      ),\n+    ),\n+  );\n+\n+  // Container for bottom links.\n+  $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links']['more_links'] = array(\n+    '#prefix' => '<div id=\"ding_nodelist-more-link\">',\n+    '#suffix' => '</div>',\n+    '#theme' => '_ding_nodelist_links_table',\n+    '#tree' => TRUE,\n+  );\n+\n+  // Get all bottom links.\n+  $delta = 0;\n+\n+  if (isset($form_state['values']['bottom_links_count'])) {\n+    $links_count = $form_state['values']['bottom_links_count'];\n+  }\n+  else {\n+    $links_count = empty($form_state['values']['more_links']) ? 1 : count($form_state['values']['more_links']);\n+  }\n+\n+  if (isset($conf['more_links'])) {\n+    $delta = count($conf['more_links']);\n+    foreach ($conf['more_links'] as $key => $link) {\n+      $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links']['more_links'][$key] = _ding_nodelist_links_form($key, $link);\n+    }\n+  }\n+\n+  // Add new bottom link field.\n+  for ($delta; $delta < $links_count; $delta++) {\n+    $key = $delta;\n+    $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links']['more_links'][$key] = _ding_nodelist_links_form($key, NULL);\n+  }\n+\n+  $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links']['ding_bottom_links_more'] = array(\n+    '#type' => 'submit',\n+    '#value' => t('Add more link'),\n+    '#submit' => array('ding_nodelist_add_bottom_link_submit'),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-more-link',\n+      'callback' => 'ding_nodelist_js_add_links',\n+      'effect' => 'fade',\n+      'method' => 'replace',\n+    ),\n+  );\n+\n+  // Cache settings.\n+  $form['ding_nodelist_wrapper']['cache_settings'] = array(\n+    '#type' => 'fieldset',\n+    '#collapsible' => TRUE,\n+    '#collapsed' => TRUE,\n+    '#tree' => FALSE,\n+    '#title' => t('Cache settings'),\n+  );\n+\n+  // Load cache settigns and prepare form.\n+  if (user_access('nodelist cache settings')) {\n+    ctools_include('plugins');\n+    $simple_cache_plugin = ctools_plugin_get_info('panels', 'cache');\n+    ctools_plugin_load_includes($simple_cache_plugin);\n+    $pane = $form_state['pane'];\n+    $settings = !empty($pane->cache['settings'])\n+      ? $pane->cache['settings']\n+      : array(\n+        'lifetime' => variable_get('ding_nodelist_cache_default_time', NL_CACHE_LIFETIME),\n+        'granularity' => 'context',\n+      );\n+    $cache_form = panels_simple_cache_settings_form($settings, $form_state['display'], $pane->pid);\n+    // @todo: Granularity is forced to 'context' now.\n+    unset($cache_form['granularity']);\n+    $form['ding_nodelist_wrapper']['cache_settings'] = array_merge($form['ding_nodelist_wrapper']['cache_settings'], $cache_form);\n+  }\n+\n+  // Restore other form elements if this is not the first form display.\n+  if (isset($form_state['restore'])) {\n+    $form = array_merge($form_state['original_form'], $form);\n+  }\n+\n+  // Remove some standard controls.\n+  unset($form['override_title']);\n+  unset($form['override_title_text']);\n+  unset($form['override_title_markup']);\n+\n+  return $form;\n+}\n+\n+/**\n+ * Generate a form piece for dynamic node select form.\n+ *\n+ * @param string $key\n+ *   Unique key for this piece.\n+ * @param string $type\n+ *   Node type.\n+ * @param array $node\n+ *   Array with the node oject.\n+ * @param int $weight\n+ *   Piece weight.\n+ *\n+ * @return array\n+ *   Form structure.\n+ */\n+function _ding_nodelist_selected_node($key, $type, $node = array(), $weight = 0) {\n+  $form = array(\n+    '#tree' => TRUE,\n+    '#weight' => $weight,\n+  );\n+\n+  // Set default value for 'node'\n+  $node_default = (!is_null($node) && isset($node['node'])) ? $node['node'] : '';\n+  $form['node'] = array(\n+    '#type' => 'textfield',\n+    '#title' => t('Select new node to add to list'),\n+    '#title_display' => 'invisible',\n+    '#autocomplete_path' => 'ding_nodelist/autocomplete/' . $type,\n+    '#parents' => array('selected_nodes', $key, 'node'),\n+    '#default_value' => $node_default,\n+  );\n+\n+  $form['nid'] = array(\n+    '#type' => 'value',\n+    '#value' => NULL,\n+    '#parents' => array('selected_nodes', $key, 'nid'),\n+  );\n+\n+  // Set default value for 'nid' if not empty field.\n+  if (!is_null($node) && isset($node['nid']) && ctype_digit($node['nid'])) {\n+    $form['nid']['#value'] = $node['nid'];\n+  }\n+  elseif (!is_null($node) && isset($node['node'])) {\n+    $tmp_node = explode(':', $node['node']);\n+    $tmp_node = array_pop($tmp_node);\n+    $tmp_node = substr($tmp_node, 0, -1);\n+    if (ctype_digit($tmp_node)) {\n+      $node['nid'] = $tmp_node;\n+    }\n+  }\n+\n+  $form['type'] = array(\n+    '#type' => 'value',\n+    '#value' => $type,\n+    '#parents' => array('selected_nodes', $key, 'type'),\n+  );\n+\n+  $form['weight'] = array(\n+    '#type' => 'weight',\n+    '#title' => $node['nid'] !== '' ? t('Weight for node @label', array('@label' => $node['nid'])) : t('Weight for new choice'),\n+    '#title_display' => 'invisible',\n+    '#default_value' => $weight,\n+    '#delta' => 50,\n+    '#parents' => array('selected_nodes', $key, 'weight'),\n+  );\n+\n+  $form['node_delete_' . $key] = array(\n+    '#name' => 'node_delete_' . $key,\n+    '#type' => 'submit',\n+    '#title_display' => 'invisible',\n+    '#value' => t('Delete'),\n+    '#parents' => array('selected_nodes', $key, 'node_delete_' . $key),\n+    '#submit' => array('ding_nodelist_node_delete_submit'),\n+    '#limit_validation_errors' => array(),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-selected-nodes',\n+      'callback' => 'ding_nodelist_js_add_node',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+  );\n+  return $form;\n+}\n+\n+/**\n+ * Builds more link form.\n+ */\n+function _ding_nodelist_links_form($key, $link) {\n+  $form = array(\n+    '#tree' => TRUE,\n+    '#weight' => $key,\n+  );\n+\n+  $form['text'] = array(\n+    '#type' => 'textfield',\n+    '#title' => t('Link title'),\n+    '#title_display' => 'invisible',\n+    '#size' => 20,\n+    '#tree' => TRUE,\n+    '#weight' => -1,\n+    '#parents' => array('more_links', $key, 'text'),\n+    '#default_value' => (!empty($link['text'])) ? $link['text'] : '',\n+  );\n+\n+  $form['links'] = array(\n+    '#type' => 'textfield',\n+    '#title' => t('URL'),\n+    '#title_display' => 'invisible',\n+    '#required' => FALSE,\n+    '#description' => check_plain(t('Local path or external URL starting with http(s). Enter \"<front>\" to link to home page.')),\n+    '#parents' => array('more_links', $key, 'links'),\n+    '#default_value' => (!empty($link['links'])) ? $link['links'] : '',\n+  );\n+\n+  return $form;\n+}\n+\n+/**\n+ * Builds filter form.\n+ */\n+function _ding_nodelist_taxonomy_filter($fields, $filter, $key) {\n+  $form = array(\n+    '#tree' => TRUE,\n+    '#weight' => $key,\n+  );\n+  $form['filter_field'] = array(\n+    '#type' => 'select',\n+    '#title_display' => 'invisible',\n+    '#options' => $fields,\n+    '#default_value' => !is_null($filter) && !empty($filter['filter_field']) ? $filter['filter_field'] : '',\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-taxonomy-filters',\n+      'callback' => 'ding_nodelist_js_add_filter',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+    '#parents' => array('taxonomy_filters', $key, 'filter_field'),\n+  );\n+\n+  if ($filter['term_reference'] == 'taxonomy_term_reference') {\n+    $autocomplete = !is_null($filter) && !empty($filter['filter_field']) ? 'taxonomy/autocomplete/' . $filter['filter_field'] : NULL;\n+  }\n+  else {\n+    $autocomplete = !is_null($filter) && !empty($filter['filter_field']) ? \"entityreference/autocomplete/tags/\" . $filter['filter_field'] . '/' . $filter['type'] . '/' . $filter['bundle'] . '/NULL' : NULL;\n+  }\n+\n+  $form['filter_terms'] = array(\n+    '#vocabulary_name' => isset($filter['vocabulary']) ? $filter['vocabulary'] : NULL,\n+    '#reference_type' => $filter['term_reference'],\n+    '#type' => 'textfield',\n+    '#title_display' => 'invisible',\n+    '#default_value' => !is_null($filter) && !empty($filter['filter_terms']) ? $filter['filter_terms'] : '',\n+    '#parents' => array('taxonomy_filters', $key, 'filter_terms'),\n+    '#maxlength' => 1024,\n+    '#element_validate' => array('_ding_nodelist_taxonomy_autocomplete_validate'),\n+    '#autocomplete_path' => $autocomplete,\n+  );\n+\n+  $form['filter_delete_' . $key] = array(\n+    '#name' => 'filter_delete_' . $key,\n+    '#type' => 'submit',\n+    '#title_display' => 'invisible',\n+    '#value' => t('Delete'),\n+    '#parents' => array('taxonomy_filters', $key, 'filter_delete_' . $key),\n+    '#submit' => array('ding_nodelist_filter_delete_submit'),\n+    '#limit_validation_errors' => array(),\n+    '#ajax' => array(\n+      'wrapper' => 'ding_nodelist-taxonomy-filters',\n+      'callback' => 'ding_nodelist_js_add_filter',\n+      'method' => 'replace',\n+      'effect' => 'fade',\n+    ),\n+  );\n+\n+  $form['term_reference'] = array(\n+    '#type' => 'value',\n+    '#value' => $filter['term_reference'],\n+  );\n+  $form['filter_name'] = array(\n+    '#type' => 'value',\n+    '#value' => $filter['filter_field'],\n+  );\n+\n+  return $form;\n+}\n+\n+/**\n+ * Custom submit function to add more choices.\n+ */\n+function ding_nodelist_add_node_submit($form, &$form_state) {\n+  // Add new node to list.\n+  $form_state['values']['selected_nodes_count'] = count($form_state['values']['selected_nodes']) + 1;\n+  unset($form_state['input']['selected_nodes']);\n+  foreach ($form_state['values']['selected_nodes'] as $key => $node) {\n+    if (!empty($node['node'])) {\n+      $tmp_nid = explode(':', $node['node']);\n+      $tmp_nid = array_pop($tmp_nid);\n+      $tmp_nid = substr($tmp_nid, 0, -1);\n+      // TODO: Implement more complex check for given title + ID.\n+      if (ctype_digit($tmp_nid)) {\n+        $form_state['values']['selected_nodes'][$key]['nid'] = $tmp_nid;\n+      }\n+    }\n+  }\n+\n+  // Copy 'input' since limit_validation_errors empties 'values'.\n+  $form_state['values'] += $form_state['input'];\n+  _ding_nodelist_content_type_form_save($form_state, TRUE);\n+}\n+\n+/**\n+ * Custom submit function for adding more filters.\n+ */\n+function ding_nodelist_add_filter_submit($form, &$form_state) {\n+  // Add new filter.\n+  $form_state['values']['taxonomy_filters_count'] = count($form_state['values']['taxonomy_filters']) + 1;\n+\n+  // Copy 'input' since limit_validation_errors empties 'values'.\n+  $form_state['values'] += $form_state['input'];\n+  _ding_nodelist_content_type_form_save($form_state, TRUE);\n+}\n+\n+/**\n+ * Custom submit function for filter deletion.\n+ */\n+function ding_nodelist_filter_delete_submit($form, &$form_state) {\n+\n+  $deleted_filter = $form_state['triggering_element']['#parents'][1];\n+  unset($form_state['values']['taxonomy_filters'][$deleted_filter]);\n+  unset($form_state['input']['taxonomy_filters'][$deleted_filter]);\n+\n+  // Re-index after deletion and save.\n+  $form_state['input']['taxonomy_filters'] = array_values($form_state['input']['taxonomy_filters']);\n+  $form_state['values'] = $form_state['input'];\n+  _ding_nodelist_content_type_form_save($form_state, TRUE);\n+}\n+\n+/**\n+ * Custom submit function for filter deletion.\n+ */\n+function ding_nodelist_node_delete_submit($form, &$form_state) {\n+  $deleted_node = $form_state['triggering_element']['#parents'][1];\n+  unset($form_state['values']['selected_nodes'][$deleted_node]);\n+  unset($form_state['input']['selected_nodes'][$deleted_node]);\n+\n+  // Re-index after deletion and save.\n+  $form_state['input']['selected_nodes'] = array_values($form_state['input']['selected_nodes']);\n+  $form_state['values'] = $form_state['input'];\n+  _ding_nodelist_content_type_form_save($form_state, TRUE);\n+}\n+\n+/**\n+ * Custom submit function for adding more links.\n+ */\n+function ding_nodelist_add_bottom_link_submit(&$form, &$form_state) {\n+  // Add new link to list.\n+  $form_state['values']['bottom_links_count'] = count($form_state['values']['more_links']) + 1;\n+  unset($form_state['input']['more_links']);\n+\n+  // Saving all the values of the form.\n+  _ding_nodelist_content_type_form_save($form_state, TRUE);\n+}\n+\n+/**\n+ * Edit form validation function.\n+ */\n+function ding_nodelist_content_type_edit_form_validate($form, &$form_state) {\n+  $form_state['restore'] = TRUE;\n+  // Do not validate if filter was changed or deleted.\n+  $te = empty($form_state['triggering_element']) ? NULL : $form_state['triggering_element'];\n+  if (strpos($te['#name'], 'widget_type') === 0 || $te['#name'] == 'filter_field') {\n+    return;\n+  }\n+\n+  $list_type = $form_state['values']['list_type'];\n+  if ($list_type === 'custom-dynamic') {\n+    $node_switch = isset($form_state['values']['nodes_switch']) ? $form_state['values']['nodes_switch'] : 0;\n+    $dynamic_switch = isset($form_state['values']['dynamic_switch']) ? $form_state['values']['dynamic_switch'] : 0;\n+\n+    if (!$node_switch && !$dynamic_switch) {\n+      form_set_error('', t('Select at least one content pick rule (custom and/or dynamic).'));\n+    }\n+    else {\n+      // Check for empty nodes.\n+      // $no_custom_content = FALSE;\n+      if (!empty($form_state['values']['selected_nodes']) && $form_state['values']['nodes_switch']) {\n+        $node_cnt = count($form_state['values']['selected_nodes']);\n+        foreach ($form_state['values']['selected_nodes'] as $key => $node) {\n+          if ($te['#name'] == 'add_node' || $te['#value'] === 'Finish' || $node_cnt > 1) {\n+            if (empty($node['node'])) {\n+              form_set_error(\"selected_nodes][$key\", t('No custom list content specified.'));\n+            }\n+          }\n+        }\n+      }\n+\n+      // Check for empty filters.\n+      if (!empty($form_state['values']['taxonomy_filters']) && $form_state['values']['dynamic_switch']) {\n+        $filter_cnt = count($form_state['values']['taxonomy_filters']);\n+        foreach ($form_state['values']['taxonomy_filters'] as $key => $filter) {\n+          if ($te['#name'] == 'add_filter' || $te['#value'] === 'Finish' || $filter_cnt > 1) {\n+            if (empty($filter['filter_terms'])) {\n+              form_set_error(\"taxonomy_filters][$key][filter_terms\", t('No terms specified for dynamic list filter.'));\n+            }\n+          }\n+        }\n+      }\n+    }\n+  }\n+\n+  // Check if the link is valid.\n+  foreach ($form_state['values']['more_links'] as $key => &$bottom) {\n+    if (!empty($bottom['text']) || !empty($bottom['links'])) {\n+      if (!_ding_nodelist_is_valid_link($bottom['links'])) {\n+        $msg = t('Invalid local or external link. External links must begin with \"http\" or \"https\", local links should point to existing pages.');\n+        form_set_error(\"more_links][$key][links\", $msg);\n+      }\n+    }\n+  }\n+}\n+\n+/**\n+ * Edit form submit function.\n+ */\n+function ding_nodelist_content_type_edit_form_submit($form, &$form_state) {\n+\n+  // Removing empty nodes.\n+  if (!empty($form_state['values']['selected_nodes'])) {\n+    foreach ($form_state['values']['selected_nodes'] as $key => $node) {\n+      if (empty($node['node'])) {\n+        unset($form_state['values']['selected_nodes'][$key]);\n+      }\n+      else {\n+        $tmp_nid = explode(':', $node['node']);\n+        $tmp_nid = array_pop($tmp_nid);\n+        $tmp_nid = substr($tmp_nid, 0, -1);\n+        // TODO: Implement more complex check for correct title of the given ID.\n+        if (ctype_digit($tmp_nid)) {\n+          $form_state['values']['selected_nodes'][$key]['nid'] = $tmp_nid;\n+        }\n+      }\n+    }\n+  }\n+\n+  // Removing empty filters.\n+  if (!empty($form_state['values']['taxonomy_filters'])) {\n+    foreach ($form_state['values']['taxonomy_filters'] as $key => $filter) {\n+      if (empty($filter['filter_terms'])) {\n+        unset($form_state['values']['taxonomy_filters'][$key]);\n+      }\n+    }\n+  }\n+\n+  // Check if there are any empty links.\n+  if (!empty($form_state['values']['more_links'])) {\n+    foreach ($form_state['values']['more_links'] as $key => &$bottom) {\n+      if (empty($bottom['text']) || empty($bottom['links'])) {\n+        unset($form_state['values']['more_links'][$key]);\n+      }\n+      // Trim slashes.\n+      $bottom['links'] = ltrim($bottom['links'], '/');\n+    }\n+  }\n+\n+  // Anything in $form_state['conf'] will be saved automatically.\n+  _ding_nodelist_content_type_form_save($form_state);\n+}\n+\n+/**\n+ * Ajax handler of content type select box.\n+ */\n+function ding_nodelist_js($form, &$form_state) {\n+  return array(\n+    '#type' => 'ajax',\n+    '#commands' => array(\n+      ajax_command_replace('#ding_nodelist-selected-nodes', drupal_render($form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['selected_nodes'])),\n+      ajax_command_replace('#ding_nodelist-taxonomy-filters', drupal_render($form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'])),\n+      ajax_command_replace('.form-item-widget-type', drupal_render($form['ding_nodelist_wrapper']['widget_type'])),\n+      ajax_command_replace('#ding_nodelist-content-filter-date', drupal_render($form['ding_nodelist_wrapper']['nodelist_content']['content_filter_date'])),\n+      ajax_command_replace('#ding_nodelist-sort-field', drupal_render($form['ding_nodelist_wrapper']['nodelist_various_settings']['ding_nodelist_sorting']['sort_field'])),\n+    ),\n+  );\n+}\n+\n+/**\n+ * Ajax handler of bottom links actions.\n+ */\n+function ding_nodelist_js_add_links($form, &$form_state) {\n+  return $form['ding_nodelist_wrapper']['ding_nodelist_bottom_links']['more_links'];\n+}\n+\n+/**\n+ * Ajax handler for custom nodes actions.\n+ */\n+function ding_nodelist_js_add_node($form, &$form_state) {\n+  return $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_nodes']['selected_nodes'];\n+}\n+\n+/**\n+ * Ajax handler of taxonomy filter actions.\n+ */\n+function ding_nodelist_js_add_filter($form, &$form_state) {\n+  return $form['ding_nodelist_wrapper']['nodelist_content']['nodelist_content_wrapper']['ding_nodelist_dynamic']['taxonomy_filters'];\n+}\n+\n+/**\n+ * Ajax handler of widget type select box.\n+ *\n+ * @return array\n+ *   Eligible item templates based on configuration.\n+ */\n+function ding_nodelist_js_template($form, &$form_state) {\n+  return $form['ding_nodelist_wrapper']['widget_dependent'];\n+}\n+\n+/**\n+ * Autocomplete function that gives the available nodes.\n+ */\n+function ding_nodelist_autocomplete($type, $title = '') {\n+  $nodes = array();\n+\n+  // Fetch all published nodes.\n+  $nodes = db_select('node', 'n')\n+    ->fields('n', array('nid', 'title'))\n+    ->condition('n.type', $type)\n+    ->condition('n.title', \"%$title%\", 'LIKE')\n+    ->condition('n.status', NODE_PUBLISHED)\n+    ->execute()\n+    ->fetchAllKeyed();\n+\n+  // Make a beatifull select.\n+  $formatted_result = array();\n+  foreach ($nodes as $nid => $title) {\n+    $formatted_result[$title . \" [nid:$nid]\"] = $title;\n+  }\n+\n+  drupal_json_output($formatted_result);\n+}\n+\n+/**\n+ * Implements hook_theme().\n+ */\n+function ding_nodelist_theme($existing, $type, $theme, $path) {\n+  // Do not rely on path - it may be unknown to module_invoke_all caller.\n+  $path = drupal_get_path('module', 'ding_nodelist') . '/templates';\n+\n+  $themes = array(\n+    // Configuration form templates.\n+    '_ding_nodelist_items_table' => array(\n+      'render element' => 'form',\n+    ),\n+    '_ding_nodelist_filters_table' => array(\n+      'render element' => 'form',\n+    ),\n+    '_ding_nodelist_links_table' => array(\n+      'render element' => 'form',\n+    ),\n+    '_ding_nodelist_templates_table' => array(\n+      'render element' => 'form',\n+    ),\n+    '_ding_nodelist_trim_text_table' => array(\n+      'render element' => 'form',\n+    ),\n+    // Widget templates.\n+    '_ding_nodelist_widget_simple' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_simple',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_single' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_single',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_horizontal_accordion' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_horizontal_accordion',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_vertical_accordion' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_vertical_accordion',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_carousel' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_carousel',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_slider' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_slider',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+    '_ding_nodelist_widget_taxonomy' => array(\n+      'path' => $path,\n+      'template' => 'ding_nodelist_widget_taxonomy',\n+      'variables' => array(\n+        'items' => NULL,\n+        'template' => NULL,\n+        'links' => NULL,\n+        'conf' => NULL,\n+      ),\n+    ),\n+  );\n+\n+  // Automatically register core templates.\n+  $templates = _ding_nodelist_get_core_templates();\n+  foreach ($templates as $template_name => $template) {\n+    $themes[$template_name] = array(\n+      'path' => $path,\n+      'template' => $template_name,\n+      'variables' => array(\n+        'item' => NULL,\n+        'conf' => NULL,\n+      ),\n+    );\n+  }\n+\n+  return $themes;\n+}\n+\n+/**\n+ * Implements hook_preprocess__ding_nodelist_widget_carousel().\n+ */\n+function template_preprocess__ding_nodelist_widget_carousel(&$variables) {\n+  $items = $variables['items'];\n+  $conf = $variables['conf'];\n+  $nodes = array();\n+\n+  foreach ($items as $item) {\n+    if ($conf['sorting'] == 'event_date') {\n+      $value = array_values($item);\n+      $nodes[] = theme($variables['template'], array('item' => array_shift($value), 'conf' => $conf));\n+    }\n+    else {\n+      $nodes[] = theme($variables['template'], array('item' => $item, 'conf' => $conf));\n+    }\n+  }\n+  $variables['items'] = $nodes;\n+}\n+/**\n+ * Helper theming function.\n+ */\n+function theme__ding_nodelist_items_table($variables) {\n+  $form = $variables['form'];\n+  $rows = array();\n+  $headers = array('', t('Selected content'), t('Weight'), '');\n+\n+  foreach (element_children($form) as $key) {\n+    // Set special classes for drag and drop updating.\n+    $form[$key]['weight']['#attributes']['class'] = array('ding_nodelist-weight');\n+\n+    // Build the table row.\n+    $row = array(\n+      'data' => array(\n+        array('class' => array('choice-flag')),\n+        drupal_render($form[$key]['node']),\n+        drupal_render($form[$key]['weight']),\n+        drupal_render($form[$key]['node_delete_' . $key]),\n+      ),\n+      'class' => array('draggable'),\n+    );\n+\n+    // Add any additional classes set on the row.\n+    if (!empty($form[$key]['#attributes']['class'])) {\n+      $row['class'] = array_merge($row['class'], $form[$key]['#attributes']['class']);\n+    }\n+\n+    $rows[] = $row;\n+  }\n+\n+  drupal_add_tabledrag('ding_nodelist-selected-table', 'order', 'sibling', 'ding_nodelist-weight');\n+\n+  $output = theme(\n+    'table',\n+    array(\n+      'header' => $headers,\n+      'rows' => $rows,\n+      'attributes' => array(\n+        'id' => 'ding_nodelist-selected-table',\n+      ),\n+    )\n+  );\n+  $output .= drupal_render_children($form);\n+  return $output;\n+}\n+\n+/**\n+ * Helper theming function.\n+ */\n+function theme__ding_nodelist_filters_table($variables) {\n+  $form = $variables['form'];\n+\n+  $rows = array();\n+  $header = array(t('Field'), t('Values'), '');\n+\n+  foreach (element_children($form) as $key) {\n+    // Build the table row.\n+    $row = array(\n+      'data' => array(\n+        drupal_render($form[$key]['filter_field']),\n+        drupal_render($form[$key]['filter_terms']),\n+        drupal_render($form[$key]['filter_delete_' . $key]),\n+      ),\n+    );\n+\n+    $rows[] = $row;\n+  }\n+\n+  $output = theme(\n+    'table',\n+    array(\n+      'header' => $header,\n+      'rows' => $rows,\n+      'attributes' => array(\n+        'id' => 'ding_nodelist-filters-table',\n+      ),\n+    )\n+  );\n+  $output .= drupal_render_children($form);\n+  return $output;\n+}\n+\n+/**\n+ * Helper theming function.\n+ */\n+function theme__ding_nodelist_links_table($variables) {\n+  $form = $variables['form'];\n+  $rows = array();\n+  $header = array(t('Text'), t('URL'));\n+\n+  foreach (element_children($form) as $key) {\n+    // Build the table row.\n+    $row = array(\n+      'data' => array(\n+        drupal_render($form[$key]['text']),\n+        drupal_render($form[$key]['links']),\n+      ),\n+    );\n+\n+    $rows[] = $row;\n+  }\n+\n+  $output = theme(\n+    'table',\n+    array(\n+      'header' => $header,\n+      'rows' => $rows,\n+      'attributes' => array(\n+        'id' => 'ding_nodelist-links-table',\n+      ),\n+    )\n+  );\n+  $output .= drupal_render_children($form);\n+  return $output;\n+}\n+\n+/**\n+ * Theme function to render the backend list of templates.\n+ */\n+function theme__ding_nodelist_templates_table($variables) {\n+  $header = array(\n+    t('Machine name'),\n+    t('Title'),\n+    t('Content type'),\n+    t('List type'),\n+    t('Visible'),\n+  );\n+  $rows = array();\n+  $form = $variables['form'];\n+  foreach (element_children($form) as $key) {\n+    $rows[] = array(\n+      $form[$key]['template_name']['#value'],\n+      drupal_render($form[$key]['title']),\n+      drupal_render($form[$key]['content_type']),\n+      drupal_render($form[$key]['widget']),\n+      drupal_render($form[$key]['visible']),\n+    );\n+  }\n+\n+  return theme('table', array('header' => $header, 'rows' => $rows)) . drupal_render_children($form);\n+}\n+\n+/**\n+ * Theme function to render backend lisy of widgets that require text trim.","path":"modules/ding_nodelist/ding_nodelist.module","position":1501,"original_position":1501,"commit_id":"8807f85d20b6e596456918671537cb4eb8e283d1","original_commit_id":"8807f85d20b6e596456918671537cb4eb8e283d1","user":{"login":"kasperg","id":73966,"avatar_url":"https://avatars.githubusercontent.com/u/73966?v=3","gravatar_id":"","url":"https://api.github.com/users/kasperg","html_url":"https://github.com/kasperg","followers_url":"https://api.github.com/users/kasperg/followers","following_url":"https://api.github.com/users/kasperg/following{/other_user}","gists_url":"https://api.github.com/users/kasperg/gists{/gist_id}","starred_url":"https://api.github.com/users/kasperg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kasperg/subscriptions","organizations_url":"https://api.github.com/users/kasperg/orgs","repos_url":"https://api.github.com/users/kasperg/repos","events_url":"https://api.github.com/users/kasperg/events{/privacy}","received_events_url":"https://api.github.com/users/kasperg/received_events","type":"User","site_admin":false},"body":"Nitpicking: Typo `lisy`.","created_at":"2016-09-14T11:42:26Z","updated_at":"2016-09-14T11:42:26Z","html_url":"https://github.com/ding2/ding2/pull/279#discussion_r78732382","pull_request_url":"https://api.github.com/repos/ding2/ding2/pulls/279","_links":{"self":{"href":"https://api.github.com/repos/ding2/ding2/pulls/comments/78732382"},"html":{"href":"https://github.com/ding2/ding2/pull/279#discussion_r78732382"},"pull_request":{"href":"https://api.github.com/repos/ding2/ding2/pulls/279"}}},"pull_request":{"url":"https://api.github.com/repos/ding2/ding2/pulls/279","id":77122078,"html_url":"https://github.com/ding2/ding2/pull/279","diff_url":"https://github.com/ding2/ding2/pull/279.diff","patch_url":"https://github.com/ding2/ding2/pull/279.patch","issue_url":"https://api.github.com/repos/ding2/ding2/issues/279","number":279,"state":"open","locked":false,"title":"Issue #1597 by Inlead: Implement ding_nodelist to DDB CMS.","user":{"login":"guddo","id":287648,"avatar_url":"https://avatars.githubusercontent.com/u/287648?v=3","gravatar_id":"","url":"https://api.github.com/users/guddo","html_url":"https://github.com/guddo","followers_url":"https://api.github.com/users/guddo/followers","following_url":"https://api.github.com/users/guddo/following{/other_user}","gists_url":"https://api.github.com/users/guddo/gists{/gist_id}","starred_url":"https://api.github.com/users/guddo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/guddo/subscriptions","organizations_url":"https://api.github.com/users/guddo/orgs","repos_url":"https://api.github.com/users/guddo/repos","events_url":"https://api.github.com/users/guddo/events{/privacy}","received_events_url":"https://api.github.com/users/guddo/received_events","type":"User","site_admin":false},"body":"http://platform.dandigbib.org/issues/1597","created_at":"2016-07-12T13:43:11Z","updated_at":"2016-09-14T11:42:26Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/ding2/ding2/pulls/279/commits","review_comments_url":"https://api.github.com/repos/ding2/ding2/pulls/279/comments","review_comment_url":"https://api.github.com/repos/ding2/ding2/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ding2/ding2/issues/279/comments","statuses_url":"https://api.github.com/repos/ding2/ding2/statuses/8807f85d20b6e596456918671537cb4eb8e283d1","head":{"label":"easySuite:feature/issue-1597","ref":"feature/issue-1597","sha":"8807f85d20b6e596456918671537cb4eb8e283d1","user":{"login":"easySuite","id":15833286,"avatar_url":"https://avatars.githubusercontent.com/u/15833286?v=3","gravatar_id":"","url":"https://api.github.com/users/easySuite","html_url":"https://github.com/easySuite","followers_url":"https://api.github.com/users/easySuite/followers","following_url":"https://api.github.com/users/easySuite/following{/other_user}","gists_url":"https://api.github.com/users/easySuite/gists{/gist_id}","starred_url":"https://api.github.com/users/easySuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/easySuite/subscriptions","organizations_url":"https://api.github.com/users/easySuite/orgs","repos_url":"https://api.github.com/users/easySuite/repos","events_url":"https://api.github.com/users/easySuite/events{/privacy}","received_events_url":"https://api.github.com/users/easySuite/received_events","type":"Organization","site_admin":false},"repo":{"id":48030945,"name":"ding2","full_name":"easySuite/ding2","owner":{"login":"easySuite","id":15833286,"avatar_url":"https://avatars.githubusercontent.com/u/15833286?v=3","gravatar_id":"","url":"https://api.github.com/users/easySuite","html_url":"https://github.com/easySuite","followers_url":"https://api.github.com/users/easySuite/followers","following_url":"https://api.github.com/users/easySuite/following{/other_user}","gists_url":"https://api.github.com/users/easySuite/gists{/gist_id}","starred_url":"https://api.github.com/users/easySuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/easySuite/subscriptions","organizations_url":"https://api.github.com/users/easySuite/orgs","repos_url":"https://api.github.com/users/easySuite/repos","events_url":"https://api.github.com/users/easySuite/events{/privacy}","received_events_url":"https://api.github.com/users/easySuite/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/easySuite/ding2","description":"Ding install profile","fork":true,"url":"https://api.github.com/repos/easySuite/ding2","forks_url":"https://api.github.com/repos/easySuite/ding2/forks","keys_url":"https://api.github.com/repos/easySuite/ding2/keys{/key_id}","collaborators_url":"https://api.github.com/repos/easySuite/ding2/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/easySuite/ding2/teams","hooks_url":"https://api.github.com/repos/easySuite/ding2/hooks","issue_events_url":"https://api.github.com/repos/easySuite/ding2/issues/events{/number}","events_url":"https://api.github.com/repos/easySuite/ding2/events","assignees_url":"https://api.github.com/repos/easySuite/ding2/assignees{/user}","branches_url":"https://api.github.com/repos/easySuite/ding2/branches{/branch}","tags_url":"https://api.github.com/repos/easySuite/ding2/tags","blobs_url":"https://api.github.com/repos/easySuite/ding2/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/easySuite/ding2/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/easySuite/ding2/git/refs{/sha}","trees_url":"https://api.github.com/repos/easySuite/ding2/git/trees{/sha}","statuses_url":"https://api.github.com/repos/easySuite/ding2/statuses/{sha}","languages_url":"https://api.github.com/repos/easySuite/ding2/languages","stargazers_url":"https://api.github.com/repos/easySuite/ding2/stargazers","contributors_url":"https://api.github.com/repos/easySuite/ding2/contributors","subscribers_url":"https://api.github.com/repos/easySuite/ding2/subscribers","subscription_url":"https://api.github.com/repos/easySuite/ding2/subscription","commits_url":"https://api.github.com/repos/easySuite/ding2/commits{/sha}","git_commits_url":"https://api.github.com/repos/easySuite/ding2/git/commits{/sha}","comments_url":"https://api.github.com/repos/easySuite/ding2/comments{/number}","issue_comment_url":"https://api.github.com/repos/easySuite/ding2/issues/comments{/number}","contents_url":"https://api.github.com/repos/easySuite/ding2/contents/{+path}","compare_url":"https://api.github.com/repos/easySuite/ding2/compare/{base}...{head}","merges_url":"https://api.github.com/repos/easySuite/ding2/merges","archive_url":"https://api.github.com/repos/easySuite/ding2/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/easySuite/ding2/downloads","issues_url":"https://api.github.com/repos/easySuite/ding2/issues{/number}","pulls_url":"https://api.github.com/repos/easySuite/ding2/pulls{/number}","milestones_url":"https://api.github.com/repos/easySuite/ding2/milestones{/number}","notifications_url":"https://api.github.com/repos/easySuite/ding2/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/easySuite/ding2/labels{/name}","releases_url":"https://api.github.com/repos/easySuite/ding2/releases{/id}","deployments_url":"https://api.github.com/repos/easySuite/ding2/deployments","created_at":"2015-12-15T08:53:09Z","updated_at":"2016-05-11T07:09:02Z","pushed_at":"2016-09-14T07:22:19Z","git_url":"git://github.com/easySuite/ding2.git","ssh_url":"git@github.com:easySuite/ding2.git","clone_url":"https://github.com/easySuite/ding2.git","svn_url":"https://github.com/easySuite/ding2","homepage":"","size":95306,"stargazers_count":0,"watchers_count":0,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":2,"forks":2,"open_issues":2,"watchers":0,"default_branch":"easyopac"}},"base":{"label":"ding2:master","ref":"master","sha":"f2fb31dbc673bab90878cce7792b66acd2c9de0b","user":{"login":"ding2","id":564698,"avatar_url":"https://avatars.githubusercontent.com/u/564698?v=3","gravatar_id":"","url":"https://api.github.com/users/ding2","html_url":"https://github.com/ding2","followers_url":"https://api.github.com/users/ding2/followers","following_url":"https://api.github.com/users/ding2/following{/other_user}","gists_url":"https://api.github.com/users/ding2/gists{/gist_id}","starred_url":"https://api.github.com/users/ding2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ding2/subscriptions","organizations_url":"https://api.github.com/users/ding2/orgs","repos_url":"https://api.github.com/users/ding2/repos","events_url":"https://api.github.com/users/ding2/events{/privacy}","received_events_url":"https://api.github.com/users/ding2/received_events","type":"Organization","site_admin":false},"repo":{"id":1266749,"name":"ding2","full_name":"ding2/ding2","owner":{"login":"ding2","id":564698,"avatar_url":"https://avatars.githubusercontent.com/u/564698?v=3","gravatar_id":"","url":"https://api.github.com/users/ding2","html_url":"https://github.com/ding2","followers_url":"https://api.github.com/users/ding2/followers","following_url":"https://api.github.com/users/ding2/following{/other_user}","gists_url":"https://api.github.com/users/ding2/gists{/gist_id}","starred_url":"https://api.github.com/users/ding2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ding2/subscriptions","organizations_url":"https://api.github.com/users/ding2/orgs","repos_url":"https://api.github.com/users/ding2/repos","events_url":"https://api.github.com/users/ding2/events{/privacy}","received_events_url":"https://api.github.com/users/ding2/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ding2/ding2","description":"Ding install profile","fork":false,"url":"https://api.github.com/repos/ding2/ding2","forks_url":"https://api.github.com/repos/ding2/ding2/forks","keys_url":"https://api.github.com/repos/ding2/ding2/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ding2/ding2/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ding2/ding2/teams","hooks_url":"https://api.github.com/repos/ding2/ding2/hooks","issue_events_url":"https://api.github.com/repos/ding2/ding2/issues/events{/number}","events_url":"https://api.github.com/repos/ding2/ding2/events","assignees_url":"https://api.github.com/repos/ding2/ding2/assignees{/user}","branches_url":"https://api.github.com/repos/ding2/ding2/branches{/branch}","tags_url":"https://api.github.com/repos/ding2/ding2/tags","blobs_url":"https://api.github.com/repos/ding2/ding2/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ding2/ding2/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ding2/ding2/git/refs{/sha}","trees_url":"https://api.github.com/repos/ding2/ding2/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ding2/ding2/statuses/{sha}","languages_url":"https://api.github.com/repos/ding2/ding2/languages","stargazers_url":"https://api.github.com/repos/ding2/ding2/stargazers","contributors_url":"https://api.github.com/repos/ding2/ding2/contributors","subscribers_url":"https://api.github.com/repos/ding2/ding2/subscribers","subscription_url":"https://api.github.com/repos/ding2/ding2/subscription","commits_url":"https://api.github.com/repos/ding2/ding2/commits{/sha}","git_commits_url":"https://api.github.com/repos/ding2/ding2/git/commits{/sha}","comments_url":"https://api.github.com/repos/ding2/ding2/comments{/number}","issue_comment_url":"https://api.github.com/repos/ding2/ding2/issues/comments{/number}","contents_url":"https://api.github.com/repos/ding2/ding2/contents/{+path}","compare_url":"https://api.github.com/repos/ding2/ding2/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ding2/ding2/merges","archive_url":"https://api.github.com/repos/ding2/ding2/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ding2/ding2/downloads","issues_url":"https://api.github.com/repos/ding2/ding2/issues{/number}","pulls_url":"https://api.github.com/repos/ding2/ding2/pulls{/number}","milestones_url":"https://api.github.com/repos/ding2/ding2/milestones{/number}","notifications_url":"https://api.github.com/repos/ding2/ding2/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ding2/ding2/labels{/name}","releases_url":"https://api.github.com/repos/ding2/ding2/releases{/id}","deployments_url":"https://api.github.com/repos/ding2/ding2/deployments","created_at":"2011-01-18T10:54:00Z","updated_at":"2016-05-24T20:06:04Z","pushed_at":"2016-09-14T10:52:32Z","git_url":"git://github.com/ding2/ding2.git","ssh_url":"git@github.com:ding2/ding2.git","clone_url":"https://github.com/ding2/ding2.git","svn_url":"https://github.com/ding2/ding2","homepage":"","size":95041,"stargazers_count":12,"watchers_count":12,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":33,"mirror_url":null,"open_issues_count":74,"forks":33,"open_issues":74,"watchers":12,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ding2/ding2/pulls/279"},"html":{"href":"https://github.com/ding2/ding2/pull/279"},"issue":{"href":"https://api.github.com/repos/ding2/ding2/issues/279"},"comments":{"href":"https://api.github.com/repos/ding2/ding2/issues/279/comments"},"review_comments":{"href":"https://api.github.com/repos/ding2/ding2/pulls/279/comments"},"review_comment":{"href":"https://api.github.com/repos/ding2/ding2/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ding2/ding2/pulls/279/commits"},"statuses":{"href":"https://api.github.com/repos/ding2/ding2/statuses/8807f85d20b6e596456918671537cb4eb8e283d1"}}}}
{"actor":{"display_login":"kasperg"}}