| Commit | Line | Data |
|---|---|---|
| 31dd0540 | 1 | <?php |
| 31dd0540 TGGM |
2 | /** |
| 3 | * @file views_ui.module | |
| 4 | * Provide structure for the administrative interface to Views. | |
| 5 | */ | |
| 6 | ||
| cd12e28f | 7 | /** |
| 2dd76fe0 | 8 | * Implements hook_menu(). |
| 31dd0540 TGGM |
9 | */ |
| 10 | function views_ui_menu() { | |
| 11 | $items = array(); | |
| 12 | ||
| 22367374 | 13 | //module_load_include('inc', 'views_ui', 'includes/admin'); |
| a185aad8 | 14 | |
| bf6e7a4a EM |
15 | // Drupal core loads the module file when uninstall a module. So views.module is not loaded. |
| 16 | // There is a core bug filled, remove it once the bug is fixed. | |
| 17 | // @see http://drupal.org/node/1029606 | |
| 18 | if (!module_exists('views')) { | |
| 19 | return; | |
| 20 | } | |
| 21 | ||
| 62abf345 | 22 | // Minor code reduction technique. |
| 31dd0540 TGGM |
23 | $base = array( |
| 24 | 'access callback' => 'user_access', | |
| 25 | 'access arguments' => array('administer views'), | |
| 26 | 'file' => 'includes/admin.inc', | |
| 27 | ); | |
| 28 | ||
| 62abf345 | 29 | // Top-level Views module pages (not tied to a particular View). |
| 62abf345 | 30 | $items['admin/structure/views/add'] = array( |
| 88b394ee | 31 | 'title' => 'Add new view', |
| 31dd0540 | 32 | 'page callback' => 'views_ui_add_page', |
| 88b394ee | 33 | 'type' => MENU_LOCAL_ACTION, |
| 62abf345 | 34 | ) + $base; |
| ced80ad3 | 35 | |
| ce174eb6 EM |
36 | // Top-level Views module pages (not tied to a particular View). |
| 37 | $items['admin/structure/views/add-template'] = array( | |
| 38 | 'title' => 'Add view from template', | |
| 39 | 'page callback' => 'views_ui_add_template_page', | |
| 40a9bf06 | 40 | // Don't show a local action link if there aren't any templates. |
| 41 | 'type' => views_get_all_templates() ? MENU_LOCAL_ACTION : MENU_VISIBLE_IN_BREADCRUMB, | |
| ce174eb6 EM |
42 | ) + $base; |
| 43 | ||
| 2b31a2d7 | 44 | $items['admin/structure/views/import'] = array( |
| dce0d189 | 45 | 'title' => 'Import', |
| 31dd0540 TGGM |
46 | 'page callback' => 'drupal_get_form', |
| 47 | 'page arguments' => array('views_ui_import_page'), | |
| 2b31a2d7 | 48 | 'access callback' => 'views_import_access', |
| 88b394ee | 49 | 'type' => MENU_LOCAL_ACTION, |
| 2b31a2d7 | 50 | ) + $base; |
| ced80ad3 | 51 | |
| aceb5217 KS |
52 | $items['admin/structure/views/settings'] = array( |
| 53 | 'title' => 'Settings', | |
| 31dd0540 | 54 | 'page callback' => 'drupal_get_form', |
| f83ae55f | 55 | 'page arguments' => array('views_ui_admin_settings_basic'), |
| 62abf345 | 56 | 'type' => MENU_LOCAL_TASK, |
| 57 | ) + $base; | |
| f83ae55f EM |
58 | $items['admin/structure/views/settings/basic'] = array( |
| 59 | 'title' => 'Basic', | |
| 60 | 'page arguments' => array('views_ui_admin_settings_basic'), | |
| 24192e23 | 61 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| aceb5217 | 62 | ) + $base; |
| 9b96c90e KS |
63 | $items['admin/structure/views/settings/advanced'] = array( |
| 64 | 'title' => 'Advanced', | |
| 65 | 'page arguments' => array('views_ui_admin_settings_advanced'), | |
| 31dd0540 TGGM |
66 | 'type' => MENU_LOCAL_TASK, |
| 67 | 'weight' => 1, | |
| aceb5217 | 68 | ) + $base; |
| 62abf345 | 69 | |
| a185aad8 | 70 | // The primary Edit View page. Secondary tabs for each Display are added in |
| e362b13e | 71 | // views_ui_menu_local_tasks_alter(). |
| 62abf345 | 72 | $items['admin/structure/views/view/%views_ui_cache'] = array( |
| 73 | 'title callback' => 'views_ui_edit_page_title', | |
| 74 | 'title arguments' => array(4), | |
| 31e25092 DW |
75 | 'page callback' => 'views_ui_edit_page', |
| 76 | 'page arguments' => array(4), | |
| 62abf345 | 77 | ) + $base; |
| 78 | $items['admin/structure/views/view/%views_ui_cache/edit'] = array( | |
| 613984f1 | 79 | 'title' => 'Edit view', |
| 62abf345 | 80 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 613984f1 | 81 | 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, |
| 62abf345 | 82 | 'weight' => -10, |
| 48336fba | 83 | 'theme callback' => 'ajax_base_page_theme', |
| 62abf345 | 84 | ) + $base; |
| e362b13e | 85 | $items['admin/structure/views/view/%views_ui_cache/edit/%/ajax'] = array( |
| efae7a9e | 86 | 'page callback' => 'views_ui_ajax_get_form', |
| e362b13e | 87 | 'page arguments' => array('views_ui_edit_form', 4, 6), |
| efae7a9e | 88 | 'delivery callback' => 'ajax_deliver', |
| 89 | 'theme callback' => 'ajax_base_page_theme', | |
| 31dd0540 | 90 | 'type' => MENU_CALLBACK, |
| efae7a9e | 91 | ) + $base; |
| 43fb73e0 DW |
92 | $items['admin/structure/views/view/%views_ui_cache/preview/%'] = array( |
| 93 | 'page callback' => 'views_ui_build_preview', | |
| 94 | 'page arguments' => array(4, 6), | |
| 95 | 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, | |
| 96 | 'type' => MENU_VISIBLE_IN_BREADCRUMB, | |
| 97 | ) + $base; | |
| 44ffdd18 EM |
98 | $items['admin/structure/views/view/%views_ui_cache/preview/%/ajax'] = array( |
| 99 | 'page callback' => 'views_ui_build_preview', | |
| 100 | 'page arguments' => array(4, 6), | |
| 101 | 'delivery callback' => 'ajax_deliver', | |
| 102 | 'theme callback' => 'ajax_base_page_theme', | |
| 103 | 'type' => MENU_CALLBACK, | |
| 104 | ) + $base; | |
| a185aad8 | 105 | |
| a185aad8 | 106 | // Additional pages for acting on a View. |
| 31dd0540 | 107 | |
| 62abf345 | 108 | $items['admin/structure/views/view/%views_ui_cache/break-lock'] = array( |
| 109 | 'title' => 'Break lock', | |
| 31dd0540 TGGM |
110 | 'page callback' => 'drupal_get_form', |
| 111 | 'page arguments' => array('views_ui_break_lock_confirm', 4), | |
| 87add604 | 112 | 'type' => MENU_VISIBLE_IN_BREADCRUMB, |
| 62abf345 | 113 | ) + $base; |
| 31dd0540 | 114 | |
| 36779205 | 115 | // NoJS/AJAX callbacks that can use the default Views AJAX form system. |
| 883f4d03 KS |
116 | $items['admin/structure/views/nojs/%/%views_ui_cache'] = array( |
| 117 | 'page callback' => 'views_ui_ajax_form', | |
| 118 | 'page arguments' => array(FALSE, 4, 5), | |
| 119 | 'type' => MENU_CALLBACK, | |
| 120 | ) + $base; | |
| 121 | $items['admin/structure/views/ajax/%/%views_ui_cache'] = array( | |
| 122 | 'page callback' => 'views_ui_ajax_form', | |
| 123 | 'page arguments' => array(TRUE, 4, 5), | |
| 124 | 'delivery callback' => 'ajax_deliver', | |
| 125 | 'type' => MENU_CALLBACK, | |
| 126 | ) + $base; | |
| 5ed42115 | 127 | |
| 36779205 | 128 | // NoJS/AJAX callbacks that require custom page callbacks. |
| 8a3d76c3 | 129 | $ajax_callbacks = array( |
| 8a3d76c3 EM |
130 | 'preview' => 'views_ui_preview', |
| 131 | ); | |
| 8a3d76c3 | 132 | foreach ($ajax_callbacks as $menu => $menu_callback) { |
| bca4c48c | 133 | $items['admin/structure/views/nojs/' . $menu . '/%views_ui_cache/%'] = array( |
| 2dd76fe0 | 134 | 'page callback' => $menu_callback, |
| bca4c48c BZ |
135 | 'page arguments' => array(5, 6), |
| 136 | ) + $base; | |
| 137 | $items['admin/structure/views/ajax/' . $menu . '/%views_ui_cache/%'] = array( | |
| 2dd76fe0 | 138 | 'page callback' => $menu_callback, |
| bca4c48c BZ |
139 | 'page arguments' => array(5, 6), |
| 140 | 'delivery callback' => 'ajax_deliver', | |
| 141 | ) + $base; | |
| 2dd76fe0 | 142 | } |
| 31dd0540 | 143 | |
| 62abf345 | 144 | // Autocomplete callback for tagging a View. |
| 36779205 | 145 | // Views module uses admin/views/... instead of admin/structure/views/... for |
| 146 | // autocomplete paths, so be consistent with that. | |
| 147 | // @todo Change to admin/structure/views/... when the change can be made to | |
| 148 | // Views module as well. | |
| 62abf345 | 149 | $items['admin/views/ajax/autocomplete/tag'] = array( |
| 31dd0540 | 150 | 'page callback' => 'views_ui_autocomplete_tag', |
| 2dd76fe0 | 151 | 'type' => MENU_CALLBACK, |
| 62abf345 | 152 | ) + $base; |
| 31dd0540 | 153 | |
| bdcab761 DW |
154 | // A page in the Reports section to show usage of fields in all views |
| 155 | $items['admin/reports/views-fields'] = array( | |
| 156 | 'title' => 'Fields used in views', | |
| 157 | 'description' => 'Overview of fields used in all views.', | |
| 158 | 'page callback' => 'views_ui_field_list', | |
| 159 | ) + $base; | |
| 160 | ||
| 31dd0540 TGGM |
161 | return $items; |
| 162 | } | |
| 163 | ||
| cd12e28f | 164 | /** |
| 2dd76fe0 | 165 | * Implements hook_theme(). |
| 31dd0540 TGGM |
166 | */ |
| 167 | function views_ui_theme() { | |
| 168 | $path = drupal_get_path('module', 'views'); | |
| 2dd76fe0 | 169 | require_once DRUPAL_ROOT . "/$path/includes/admin.inc"; |
| 31dd0540 TGGM |
170 | |
| 171 | return array( | |
| 172 | // edit a view | |
| 6631a572 | 173 | 'views_ui_display_tab_setting' => array( |
| 6c56994b | 174 | 'variables' => array('description' => '', 'link' => '', 'settings_links' => array(), 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => array()), |
| 6631a572 | 175 | 'template' => 'views-ui-display-tab-setting', |
| 31dd0540 TGGM |
176 | 'path' => "$path/theme", |
| 177 | ), | |
| e0695ad0 | 178 | 'views_ui_display_tab_bucket' => array( |
| 6631a572 | 179 | 'render element' => 'element', |
| e0695ad0 | 180 | 'template' => 'views-ui-display-tab-bucket', |
| 31dd0540 TGGM |
181 | 'path' => "$path/theme", |
| 182 | ), | |
| 183 | 'views_ui_edit_item' => array( | |
| 3dd4aaf0 | 184 | 'variables' => array('type' => NULL, 'view' => NULL, 'display' => NULL, 'no_fields' => FALSE), |
| 31dd0540 TGGM |
185 | 'template' => 'views-ui-edit-item', |
| 186 | 'path' => "$path/theme", | |
| 187 | ), | |
| 188 | 'views_ui_rearrange_form' => array( | |
| 3dd4aaf0 | 189 | 'render element' => 'form', |
| 31dd0540 | 190 | ), |
| dc4e7922 DW |
191 | 'views_ui_rearrange_filter_form' => array( |
| 192 | 'render element' => 'form', | |
| 193 | 'file' => 'includes/admin.inc', | |
| 194 | ), | |
| c9fc5940 BZ |
195 | 'views_ui_expose_filter_form' => array( |
| 196 | 'render element' => 'form', | |
| 197 | 'file' => 'includes/admin.inc', | |
| 198 | ), | |
| 31dd0540 TGGM |
199 | |
| 200 | // list views | |
| e28cdb45 | 201 | 'views_ui_view_info' => array( |
| ced80ad3 | 202 | 'variables' => array('view' => NULL, 'base' => NULL), |
| e28cdb45 | 203 | 'file' => "includes/admin.inc", |
| 31dd0540 | 204 | ), |
| 31dd0540 TGGM |
205 | |
| 206 | // tab themes | |
| 207 | 'views_tabset' => array( | |
| 3dd4aaf0 | 208 | 'variables' => array('tabs' => NULL), |
| 31dd0540 TGGM |
209 | ), |
| 210 | 'views_tab' => array( | |
| 3dd4aaf0 | 211 | 'variables' => array('body' => NULL), |
| 31dd0540 | 212 | ), |
| 758f9361 DW |
213 | 'views_ui_reorder_displays_form' => array( |
| 214 | 'render element' => 'form', | |
| 215 | 'file' => 'includes/admin.inc', | |
| 216 | ), | |
| 217 | ||
| 31dd0540 TGGM |
218 | |
| 219 | // On behalf of a plugin | |
| 220 | 'views_ui_style_plugin_table' => array( | |
| 3dd4aaf0 | 221 | 'render element' => 'form', |
| 31dd0540 | 222 | ), |
| 09cf411d | 223 | |
| 224 | // When previewing a view. | |
| 225 | 'views_ui_view_preview_section' => array( | |
| 040fee74 | 226 | 'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''), |
| 09cf411d | 227 | ), |
| 99048386 KS |
228 | |
| 229 | // Generic container wrapper, to use instead of theme_container when an id | |
| 230 | // is not desired. | |
| 231 | 'views_container' => array( | |
| 232 | 'render element' => 'element', | |
| 94bfcb70 | 233 | 'file' => 'theme/theme.inc', |
| 99048386 | 234 | ), |
| 31dd0540 TGGM |
235 | ); |
| 236 | } | |
| 237 | ||
| 238 | /** | |
| bb9c7e69 DW |
239 | * Impements hook_custom_theme() |
| 240 | */ | |
| 241 | function views_ui_custom_theme() { | |
| 242 | $theme = variable_get('views_ui_custom_theme', '_default'); | |
| 243 | ||
| 244 | if ($theme != '_default') { | |
| 245 | $available = list_themes(); | |
| 246 | ||
| 247 | if (isset($available[$theme]) && $available[$theme]->status && preg_match('/^admin\/structure\/views/', current_path())) { | |
| 248 | return $theme; | |
| 249 | } | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | /** | |
| 87add604 EM |
254 | * Page title callback for the Edit View page. |
| 255 | */ | |
| 2f011518 | 256 | function views_ui_edit_page_title($view) { |
| 87add604 EM |
257 | module_load_include('inc', 'views_ui', 'includes/admin'); |
| 258 | $bases = views_fetch_base_tables(); | |
| 259 | $name = $view->get_human_name(); | |
| 260 | if (isset($bases[$view->base_table])) { | |
| 261 | $name .= ' (' . $bases[$view->base_table]['title'] . ')'; | |
| 262 | } | |
| 263 | ||
| 264 | return $name; | |
| 265 | } | |
| 266 | ||
| 267 | /** | |
| e28cdb45 KS |
268 | * Specialized menu callback to load a view and check its locked status. |
| 269 | * | |
| 270 | * @param $name | |
| 271 | * The machine name of the view. | |
| 272 | * | |
| 273 | * @return | |
| 274 | * The view object, with a "locked" property indicating whether or not | |
| 275 | * someone else is already editing the view. | |
| 31dd0540 TGGM |
276 | */ |
| 277 | function views_ui_cache_load($name) { | |
| f0bb22cd | 278 | ctools_include('object-cache'); |
| 31dd0540 | 279 | views_include('view'); |
| f0bb22cd | 280 | $view = ctools_object_cache_get('view', $name); |
| 3c841b5c | 281 | $original_view = views_get_view($name); |
| 31dd0540 TGGM |
282 | |
| 283 | if (empty($view)) { | |
| 3c841b5c | 284 | $view = $original_view; |
| 31dd0540 TGGM |
285 | if (!empty($view)) { |
| 286 | // Check to see if someone else is already editing this view. | |
| f0bb22cd | 287 | $view->locked = ctools_object_cache_test('view', $view->name); |
| 4b5c3a6a DW |
288 | // Set a flag to indicate that this view is being edited. |
| 289 | // This flag will be used e.g. to determine whether strings | |
| 290 | // should be localized. | |
| 291 | $view->editing = TRUE; | |
| 31dd0540 TGGM |
292 | } |
| 293 | } | |
| 3c841b5c EM |
294 | else { |
| 295 | // Keep disabled/enabled status real. | |
| 296 | if ($original_view) { | |
| 9adb6f0d | 297 | $view->disabled = !empty($original_view->disabled); |
| 3c841b5c EM |
298 | } |
| 299 | } | |
| 31dd0540 TGGM |
300 | |
| 301 | if (empty($view)) { | |
| 302 | return FALSE; | |
| 303 | } | |
| 304 | ||
| 305 | else { | |
| 306 | return $view; | |
| 307 | } | |
| 308 | } | |
| 309 | ||
| 31dd0540 TGGM |
310 | /** |
| 311 | * Specialized cache function to add a flag to our view, include an appropriate | |
| 312 | * include, and cache more easily. | |
| 313 | */ | |
| 2f011518 | 314 | function views_ui_cache_set(&$view) { |
| 31dd0540 TGGM |
315 | if (!empty($view->locked)) { |
| 316 | drupal_set_message(t('Changes cannot be made to a locked view.'), 'error'); | |
| 317 | return; | |
| 318 | } | |
| f0bb22cd | 319 | ctools_include('object-cache'); |
| 31dd0540 TGGM |
320 | $view->changed = TRUE; // let any future object know that this view has changed. |
| 321 | ||
| ce174eb6 EM |
322 | if (isset($view->current_display)) { |
| 323 | // Add the knowledge of the changed display, too. | |
| 324 | $view->changed_display[$view->current_display] = TRUE; | |
| 325 | unset($view->current_display); | |
| 326 | } | |
| 5ed42115 | 327 | |
| 31dd0540 TGGM |
328 | // Unset handlers; we don't want to write these into the cache |
| 329 | unset($view->display_handler); | |
| 31dd0540 | 330 | unset($view->default_display); |
| a8f0683e | 331 | $view->query = NULL; |
| 31dd0540 TGGM |
332 | foreach (array_keys($view->display) as $id) { |
| 333 | unset($view->display[$id]->handler); | |
| 334 | unset($view->display[$id]->default_display); | |
| 335 | } | |
| f0bb22cd | 336 | ctools_object_cache_set('view', $view->name, $view); |
| 31dd0540 TGGM |
337 | } |
| 338 | ||
| 339 | ||
| 340 | /** | |
| 341 | * Specialized menu callback to load a view that is only a default | |
| 342 | * view. | |
| 343 | */ | |
| 344 | function views_ui_default_load($name) { | |
| 345 | $view = views_get_view($name); | |
| 346 | if ($view->type == t('Default')) { | |
| 347 | return $view; | |
| 348 | } | |
| 349 | ||
| 350 | return FALSE; | |
| 351 | } | |
| 352 | ||
| 353 | /** | |
| 09cf411d | 354 | * Theme preprocess for views-view.tpl.php. |
| 355 | */ | |
| 356 | function views_ui_preprocess_views_view(&$vars) { | |
| 357 | $view = $vars['view']; | |
| 24cbc9fb | 358 | if (!empty($view->views_ui_context) && module_exists('contextual')) { |
| 09cf411d | 359 | $view->hide_admin_links = TRUE; |
| 5a873017 | 360 | foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) { |
| 09cf411d | 361 | if (!empty($vars[$section])) { |
| f37fd0dd DW |
362 | $vars[$section] = array( |
| 363 | '#theme' => 'views_ui_view_preview_section', | |
| 364 | '#view' => $view, | |
| 365 | '#section' => $section, | |
| 61cc8b63 | 366 | '#content' => is_array($vars[$section]) ? drupal_render($vars[$section]) : $vars[$section], |
| f37fd0dd DW |
367 | '#theme_wrappers' => array('views_container'), |
| 368 | '#attributes' => array('class' => 'contextual-links-region'), | |
| 369 | ); | |
| 370 | $vars[$section] = drupal_render($vars[$section]); | |
| 09cf411d | 371 | } |
| 372 | } | |
| 373 | } | |
| 374 | } | |
| 375 | ||
| 376 | /** | |
| 377 | * Theme preprocess for theme_views_ui_view_preview_section(). | |
| 2dd76fe0 | 378 | * |
| 040fee74 DW |
379 | * @TODO |
| 380 | * Perhaps move this to includes/admin.inc or theme/theme.inc | |
| 31dd0540 | 381 | */ |
| 09cf411d | 382 | function template_preprocess_views_ui_view_preview_section(&$vars) { |
| 383 | switch ($vars['section']) { | |
| 5a873017 DW |
384 | case 'title': |
| 385 | $vars['title'] = t('Title'); | |
| 386 | $links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']); | |
| 387 | break; | |
| 09cf411d | 388 | case 'header': |
| 389 | $vars['title'] = t('Header'); | |
| 040fee74 DW |
390 | $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']); |
| 391 | break; | |
| 392 | case 'empty': | |
| 393 | $vars['title'] = t('No results behavior'); | |
| 394 | $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']); | |
| 09cf411d | 395 | break; |
| 396 | case 'exposed': | |
| 397 | // @todo Sorts can be exposed too, so we may need a better title. | |
| 398 | $vars['title'] = t('Exposed Filters'); | |
| 97f6eba4 | 399 | $links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']); |
| 09cf411d | 400 | break; |
| 401 | case 'rows': | |
| 98d300a2 BZ |
402 | // @todo The title needs to depend on what is being viewed. |
| 403 | $vars['title'] = t('Content'); | |
| 1eb00c3b | 404 | $links = views_ui_view_preview_section_rows_links($vars['view']); |
| 09cf411d | 405 | break; |
| 406 | case 'pager': | |
| 407 | $vars['title'] = t('Pager'); | |
| 97f6eba4 | 408 | $links = views_ui_view_preview_section_display_category_links($vars['view'], 'pager_options', $vars['title']); |
| 09cf411d | 409 | break; |
| 410 | case 'more': | |
| 411 | $vars['title'] = t('More'); | |
| 7e201f9a | 412 | $links = views_ui_view_preview_section_display_category_links($vars['view'], 'use_more', $vars['title']); |
| 09cf411d | 413 | break; |
| 414 | case 'footer': | |
| 415 | $vars['title'] = t('Footer'); | |
| 040fee74 | 416 | $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']); |
| 09cf411d | 417 | break; |
| 8ee02877 DW |
418 | case 'attachment_before': |
| 419 | // @todo: Add links to the attachment configuration page. | |
| 420 | $vars['title'] = t('Attachment before'); | |
| 421 | break; | |
| 422 | case 'attachment_after': | |
| 423 | // @todo: Add links to the attachment configuration page. | |
| 424 | $vars['title'] = t('Attachment after'); | |
| 09cf411d | 425 | break; |
| 426 | } | |
| 3afefdca | 427 | |
| e860b5f2 | 428 | if (isset($links)) { |
| 040fee74 DW |
429 | $build = array( |
| 430 | '#prefix' => '<div class="contextual-links-wrapper">', | |
| 431 | '#suffix' => '</div>', | |
| 432 | '#theme' => 'links__contextual', | |
| 433 | '#links' => $links, | |
| 434 | '#attributes' => array('class' => array('contextual-links')), | |
| 435 | '#attached' => array( | |
| 6352223c | 436 | 'library' => array(array('contextual', 'contextual-links')), |
| 040fee74 DW |
437 | ), |
| 438 | ); | |
| 439 | $vars['links'] = drupal_render($build); | |
| 2dd76fe0 | 440 | } |
| 09cf411d | 441 | $vars['theme_hook_suggestions'][] = 'views_ui_view_preview_section__' . $vars['section']; |
| 442 | } | |
| 443 | ||
| 444 | /** | |
| 445 | * Returns the HTML for a section of a View being previewed within the Views UI. | |
| 446 | */ | |
| 447 | function theme_views_ui_view_preview_section($vars) { | |
| 18d9d056 | 448 | return '<h1 class="section-title">' . $vars['title'] . '</h1>' |
| 97e89d71 | 449 | . $vars['links'] |
| bbc6915f | 450 | . '<div class="preview-section">'. $vars['content'] . '</div>'; |
| 040fee74 DW |
451 | } |
| 452 | ||
| 453 | /** | |
| 454 | * Returns contextual links for each handler of a certain section. | |
| 455 | * | |
| 456 | * @TODO | |
| 457 | * Bring in relationships | |
| 458 | * Refactor this function to use much stuff of views_ui_edit_form_get_bucket. | |
| 1eb00c3b DW |
459 | * |
| 460 | * @param $title | |
| 461 | * Add a bolded title of this section. | |
| 040fee74 | 462 | */ |
| 2f011518 | 463 | function views_ui_view_preview_section_handler_links($view, $type, $title = FALSE) { |
| 040fee74 DW |
464 | $display = $view->display_handler->display; |
| 465 | $handlers = $view->display_handler->get_handlers($type); | |
| bf6426ed | 466 | $links = array(); |
| 040fee74 | 467 | |
| 1eb00c3b DW |
468 | $types = views_object_types(); |
| 469 | if ($title) { | |
| 470 | $links[$type . '-title'] = array( | |
| 471 | 'title' => $types[$type]['title'], | |
| 472 | ); | |
| 2dd76fe0 | 473 | } |
| 1eb00c3b | 474 | |
| 040fee74 DW |
475 | foreach ($handlers as $id => $handler) { |
| 476 | $field_name = $handler->ui_name(TRUE); | |
| 477 | $links[$type . '-edit-' . $id] = array( | |
| 1eb00c3b | 478 | 'title' => t('Edit @section', array('@section' => $field_name)), |
| 040fee74 | 479 | 'href' => "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id", |
| 033d4e25 | 480 | 'attributes' => array('class' => array('views-ajax-link')), |
| 040fee74 DW |
481 | ); |
| 482 | } | |
| 1eb00c3b DW |
483 | $links[$type . '-add'] = array( |
| 484 | 'title' => t('Add new'), | |
| 485 | 'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type", | |
| 033d4e25 | 486 | 'attributes' => array('class' => array('views-ajax-link')), |
| 1eb00c3b DW |
487 | ); |
| 488 | ||
| 489 | return $links; | |
| 09cf411d | 490 | } |
| 913b92b2 | 491 | |
| 492 | /** | |
| 7e201f9a DW |
493 | * Returns a link to editing a certain display setting. |
| 494 | */ | |
| 2f011518 | 495 | function views_ui_view_preview_section_display_category_links($view, $type, $title) { |
| 7e201f9a DW |
496 | $display = $view->display_handler->display; |
| 497 | $links = array( | |
| 498 | $type . '-edit' => array( | |
| 499 | 'title' => t('Edit @section', array('@section' => $title)), | |
| 500 | 'href' => "admin/structure/views/nojs/display/$view->name/$display->id/$type", | |
| 501 | 'attributes' => array('class' => array('views-ajax-link')), | |
| 502 | ), | |
| 503 | ); | |
| c9fc5940 | 504 | |
| 7e201f9a DW |
505 | return $links; |
| 506 | } | |
| 507 | ||
| 508 | /** | |
| 1eb00c3b DW |
509 | * Returns all contextual links for the main content part of the view. |
| 510 | */ | |
| 2f011518 | 511 | function views_ui_view_preview_section_rows_links($view) { |
| 1eb00c3b DW |
512 | $display = $view->display_handler->display; |
| 513 | $links = array(); | |
| 71c4265a DW |
514 | $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE)); |
| 515 | $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE)); | |
| 516 | $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE)); | |
| 517 | $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE)); | |
| 518 | $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE)); | |
| 040fee74 DW |
519 | |
| 520 | return $links; | |
| 09cf411d | 521 | } |
| 913b92b2 | 522 | |
| 040fee74 | 523 | |
| 913b92b2 | 524 | /** |
| 525 | * Implments hook_ctools_plugin_directory(). | |
| 526 | * | |
| 527 | * Views UI provides wizard plugins on behalf of core base tables. | |
| 528 | */ | |
| 529 | function views_ui_ctools_plugin_directory($module, $plugin) { | |
| ced80ad3 | 530 | if ($module == 'views_ui' || ($module == 'ctools' && $plugin == 'export_ui')) { |
| 913b92b2 | 531 | return 'plugins/' . $plugin; |
| 532 | } | |
| 533 | } | |
| 534 | ||
| 535 | /** | |
| 536 | * Fetch metadata on a specific views ui wizard plugin. | |
| 537 | * | |
| d6bea670 | 538 | * @param $wizard_type |
| 539 | * Name of a wizard, or name of a base table. | |
| 913b92b2 | 540 | * |
| 541 | * @return | |
| 542 | * An array with information about the requested wizard type. | |
| 543 | */ | |
| d6bea670 | 544 | function views_ui_get_wizard($wizard_type) { |
| 913b92b2 | 545 | ctools_include('plugins'); |
| a397d7b4 | 546 | $wizard = ctools_get_plugins('views_ui', 'views_wizard', $wizard_type); |
| d6bea670 | 547 | // @todo - handle this via an alter hook instead. |
| a397d7b4 | 548 | if (!$wizard) { |
| d6bea670 | 549 | // Must be a base table using the default wizard plugin. |
| 550 | $base_tables = views_fetch_base_tables(); | |
| 551 | if (!empty($base_tables[$wizard_type])) { | |
| a397d7b4 DW |
552 | $wizard = views_ui_views_wizard_defaults(); |
| 553 | $wizard['base_table'] = $wizard_type; | |
| 554 | $wizard['title'] = $base_tables[$wizard_type]['title']; | |
| 3afefdca | 555 | } |
| f0c08154 DW |
556 | // The plugin is neither a base table nor an existing wizard. |
| 557 | else { | |
| 558 | vpr('Views Wizard: @wizard does not exist. Be sure to implement hook_ctools_plugin_directory.', array('@wizard' => $wizard_type)); | |
| 559 | } | |
| d6bea670 | 560 | } |
| a397d7b4 | 561 | return $wizard; |
| 913b92b2 | 562 | } |
| 563 | ||
| 564 | /** | |
| 565 | * Fetch metadata for all content_type plugins. | |
| 566 | * | |
| 567 | * @return | |
| 568 | * An array of arrays with information about all available views wizards. | |
| 569 | */ | |
| 570 | function views_ui_get_wizards() { | |
| 571 | ctools_include('plugins'); | |
| d6bea670 | 572 | $wizard_plugins = ctools_get_plugins('views_ui', 'views_wizard'); |
| a50d6ba6 | 573 | $wizard_tables = array(); |
| 574 | foreach ($wizard_plugins as $name => $info) { | |
| 575 | $wizard_tables[$info['base_table']] = TRUE; | |
| 576 | } | |
| d6bea670 | 577 | $base_tables = views_fetch_base_tables(); |
| 578 | $default_wizard = views_ui_views_wizard_defaults(); | |
| 579 | // Find base tables with no wizard. | |
| 580 | // @todo - handle this via an alter hook for plugins? | |
| 581 | foreach ($base_tables as $table => $info) { | |
| a50d6ba6 | 582 | if (!isset($wizard_tables[$table])) { |
| 3116f109 DW |
583 | $wizard = $default_wizard; |
| 584 | $wizard['title'] = $info['title']; | |
| 585 | $wizard['base_table'] = $table; | |
| 586 | $wizard_plugins[$table] = $wizard; | |
| 2dd76fe0 EM |
587 | } |
| 588 | } | |
| d6bea670 | 589 | return $wizard_plugins; |
| 590 | } | |
| 591 | ||
| 592 | /** | |
| 593 | * Helper function to define the default values for a Views wizard plugin. | |
| 594 | * | |
| 595 | * @return | |
| 596 | * An array of defaults for a views wizard. | |
| 597 | */ | |
| 598 | function views_ui_views_wizard_defaults() { | |
| 599 | return array( | |
| 600 | // The children may, for example, be a different variant for each node type. | |
| 601 | 'get children' => NULL, | |
| 602 | 'get child' => NULL, | |
| 603 | // title and base table must be populated. They are empty here just | |
| 604 | // so they are documented. | |
| 605 | 'title' => '', | |
| 606 | 'base_table' => NULL, | |
| 607 | // This is a callback that takes the wizard as argument and returns | |
| 608 | // an instantiazed Views UI form wizard object. | |
| 609 | 'get_instance' => 'views_ui_get_form_wizard_instance', | |
| 610 | 'form_wizard_class' => array( | |
| 611 | 'file' => 'views_ui_base_views_wizard', | |
| 612 | 'class' => 'ViewsUiBaseViewsWizard', | |
| 613 | ), | |
| 614 | ); | |
| 913b92b2 | 615 | } |
| 616 | ||
| 617 | /** | |
| 618 | * Inform CTools that the Views wizard plugin can have child plugins. | |
| 619 | */ | |
| 620 | function views_ui_ctools_plugin_type() { | |
| 621 | return array( | |
| 622 | 'views_wizard' => array( | |
| 623 | 'child plugins' => TRUE, | |
| 46160a0d | 624 | 'classes' => array( |
| d6bea670 | 625 | 'form_wizard_class', |
| 913b92b2 | 626 | ), |
| d6bea670 | 627 | 'defaults' => views_ui_views_wizard_defaults(), |
| 913b92b2 | 628 | ), |
| 629 | ); | |
| 630 | } | |
| 631 | ||
| d6bea670 | 632 | function views_ui_get_form_wizard_instance($wizard) { |
| 633 | if (isset($wizard['form_wizard_class']['class'])) { | |
| 634 | $class = $wizard['form_wizard_class']['class']; | |
| 635 | return new $class($wizard); | |
| 636 | } | |
| 2dd76fe0 | 637 | else { |
| d6bea670 | 638 | return new ViewsUiBaseViewsWizard($wizard); |
| 639 | } | |
| 46160a0d | 640 | } |
| bfb5bc38 JB |
641 | |
| 642 | /** | |
| 613984f1 DR |
643 | * Implements hook_views_plugins_alter(). |
| 644 | */ | |
| 645 | function views_ui_views_plugins_alter(&$plugins) { | |
| 646 | // Attach contextual links to each display plugin. The links will point to | |
| 647 | // paths underneath "admin/structure/views/view/{$view->name}" (i.e., paths | |
| 648 | // for editing and performing other contextual actions on the view). | |
| 649 | foreach ($plugins['display'] as &$display) { | |
| 650 | $display['contextual links']['views_ui'] = array( | |
| 651 | 'parent path' => 'admin/structure/views/view', | |
| 652 | 'argument properties' => array('name'), | |
| 653 | ); | |
| 31dd0540 | 654 | } |
| 613984f1 | 655 | } |
| 0895a002 | 656 | |
| 613984f1 | 657 | /** |
| d4993110 | 658 | * Implements hook_contextual_links_view_alter(). |
| bfb5bc38 | 659 | */ |
| d4993110 | 660 | function views_ui_contextual_links_view_alter(&$element, $items) { |
| 613984f1 DR |
661 | // Remove contextual links from being rendered, when so desired, such as |
| 662 | // within a View preview. | |
| d4993110 | 663 | if (views_ui_contextual_links_suppress()) { |
| 664 | $element['#links'] = array(); | |
| 0895a002 | 665 | } |
| 613984f1 DR |
666 | // Append the display ID to the Views UI edit links, so that clicking on the |
| 667 | // contextual link takes you directly to the correct display tab on the edit | |
| 668 | // screen. | |
| 669 | elseif (!empty($element['#links']['views-ui-edit']) && !empty($element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'])) { | |
| 670 | $display_id = $element['#element']['#views_contextual_links_info']['views_ui']['view_display_id']; | |
| 671 | $element['#links']['views-ui-edit']['href'] .= '/' . $display_id; | |
| 672 | } | |
| b0dcf5d1 | 673 | } |
| efae7a9e | 674 | |
| 675 | /** | |
| d4993110 | 676 | * Sets a static variable for controlling whether contextual links are rendered. |
| 677 | * | |
| 678 | * @see views_ui_contextual_links_view_alter() | |
| 679 | */ | |
| 680 | function views_ui_contextual_links_suppress($set = NULL) { | |
| 681 | $suppress = &drupal_static(__FUNCTION__); | |
| 682 | if (isset($set)) { | |
| 683 | $suppress = $set; | |
| 0895a002 | 684 | } |
| d4993110 | 685 | return $suppress; |
| 31dd0540 | 686 | } |
| d4993110 | 687 | |
| 688 | /** | |
| 689 | * Increments the views_ui_contextual_links_suppress() static variable. | |
| 690 | * | |
| 691 | * When this function is added to the #pre_render of an element, and | |
| 692 | * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the | |
| 693 | * same element, then all contextual links within the element and its | |
| 694 | * descendants are suppressed from being rendered. This is used, for example, | |
| 695 | * during a View preview, when it is not desired for nodes in the Views result | |
| 696 | * to have contextual links. | |
| 697 | * | |
| 698 | * @see views_ui_contextual_links_suppress_pop() | |
| 699 | */ | |
| 700 | function views_ui_contextual_links_suppress_push() { | |
| 701 | views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())+1); | |
| 702 | } | |
| 703 | ||
| 704 | /** | |
| 705 | * Decrements the views_ui_contextual_links_suppress() static variable. | |
| 706 | * | |
| 707 | * @see views_ui_contextual_links_suppress_push() | |
| 708 | */ | |
| 709 | function views_ui_contextual_links_suppress_pop() { | |
| 710 | views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())-1); | |
| 711 | } | |
| 712 | ||
| 713 | /** | |
| 7126c33a | 714 | * Menu callback; handles AJAX form submissions similar to ajax_form_callback(), but can be used for uncached forms. |
| efae7a9e | 715 | * |
| 716 | * ajax_form_callback(), the menu callback for the system/ajax path, requires | |
| 717 | * the form to be retrievable from the form cache, because it lacks a trusted | |
| 7126c33a | 718 | * $form_id argument with which to call drupal_retrieve_form(). When AJAX is |
| efae7a9e | 719 | * wanted on a non-cacheable form, #ajax['path'] can be set to a path whose |
| 720 | * menu router item's 'page callback' is this function, and whose | |
| 721 | * 'page arguments' is the form id, optionally followed by additional build | |
| 722 | * arguments, as expected by drupal_get_form(). | |
| 723 | * | |
| 724 | * The same caution must be used when defining a hook_menu() entry with this | |
| 725 | * page callback as is used when defining a hook_menu() entry with the | |
| 726 | * 'drupal_get_form' page callback: a 'page arguments' must be specified with a | |
| 727 | * literal value as the first argument, because $form_id determines which form | |
| 728 | * builder function gets called, so must be safe from user tampering. | |
| 729 | * | |
| 730 | * @see drupal_get_form() | |
| 731 | * @see ajax_form_callback() | |
| 732 | * @see http://drupal.org/node/774876 | |
| 733 | */ | |
| 734 | function views_ui_ajax_get_form($form_id) { | |
| 735 | // @see ajax_get_form() | |
| 736 | $form_state = array( | |
| 737 | 'no_redirect' => TRUE, | |
| 738 | ); | |
| 739 | $form_state['rebuild_info']['copy']['#build_id'] = TRUE; | |
| 740 | $form_state['rebuild_info']['copy']['#action'] = TRUE; | |
| 741 | ||
| 742 | // @see drupal_get_form() | |
| 743 | $args = func_get_args(); | |
| 744 | array_shift($args); | |
| 745 | $form_state['build_info']['args'] = $args; | |
| 746 | $form = drupal_build_form($form_id, $form_state); | |
| 747 | ||
| 748 | // @see ajax_form_callback() | |
| 749 | if (!empty($form_state['triggering_element'])) { | |
| 750 | $callback = $form_state['triggering_element']['#ajax']['callback']; | |
| 751 | } | |
| 752 | if (!empty($callback) && function_exists($callback)) { | |
| 753 | return $callback($form, $form_state); | |
| 754 | } | |
| 755 | } | |
| ced80ad3 EM |
756 | // @todo move these when we can |
| 757 | ||
| 758 | ||
| 759 | /** | |
| 760 | * Helper function to get a list of paths assigned to a view. | |
| 761 | * | |
| 762 | * @param $view | |
| 763 | * The view. | |
| 764 | * | |
| 765 | * @return | |
| 766 | * An array of links to this view's display paths. | |
| 767 | */ | |
| 2f011518 | 768 | function _views_ui_get_paths($view) { |
| ced80ad3 EM |
769 | $all_paths = array(); |
| 770 | if (empty($view->display)) { | |
| 771 | $all_paths[] = t('Edit this view to add a display.'); | |
| 772 | } | |
| 773 | else { | |
| 774 | $view->init_display(); // Make sure all the handlers are set up | |
| 775 | foreach ($view->display as $display) { | |
| 776 | if (!empty($display->handler) && $display->handler->has_path()) { | |
| 777 | $one_path = $display->handler->get_option('path'); | |
| 778 | if (empty($path_sort)) { | |
| 779 | $path_sort = strtolower($one_path); | |
| 780 | } | |
| 781 | if (empty($view->disabled) && strpos($one_path, '%') === FALSE) { | |
| fbd4d0f6 | 782 | $all_paths[] = l('/' . $one_path, $one_path); |
| ced80ad3 EM |
783 | } |
| 784 | else { | |
| fbd4d0f6 | 785 | $all_paths[] = check_plain('/' . $one_path); |
| ced80ad3 EM |
786 | } |
| 787 | } | |
| 788 | } | |
| 789 | } | |
| 790 | ||
| 791 | return array_unique($all_paths); | |
| 792 | } | |
| 793 | ||
| 794 | /** | |
| 795 | * Helper function to get a list of displays included in a view. | |
| 796 | * | |
| 797 | * @param $view | |
| 798 | * The view. | |
| 799 | * | |
| 800 | * @return | |
| 801 | * An array of display types that this view includes. | |
| 802 | */ | |
| 2f011518 | 803 | function _views_ui_get_displays_list($view) { |
| ced80ad3 EM |
804 | $displays = array(); |
| 805 | foreach ($view->display as $display) { | |
| 806 | if (!empty($display->handler->definition['admin'])) { | |
| 807 | $displays[$display->handler->definition['admin']] = TRUE; | |
| 808 | } | |
| 809 | } | |
| 810 | ||
| 811 | if ($displays) { | |
| 812 | ksort($displays); | |
| 813 | $displays = array_keys($displays); | |
| 814 | } | |
| 815 | return $displays; | |
| 816 | } | |
| 817 | ||
| edb447fb EM |
818 | /** |
| 819 | * This is part of a patch to address a jQueryUI bug. The bug is responsible | |
| 820 | * for the inability to scroll a page when a modal dialog is active. If the content | |
| 821 | * of the dialog extends beyond the bottom of the viewport, the user is only able | |
| 822 | * to scroll with a mousewheel or up/down keyboard keys. | |
| 823 | * | |
| 824 | * @see http://bugs.jqueryui.com/ticket/4671 | |
| 825 | * @see https://bugs.webkit.org/show_bug.cgi?id=19033 | |
| 826 | * @see /js/jquery.ui.dialog.patch.js | |
| 827 | * @see /js/jquery.ui.dialog.min.js | |
| 828 | * | |
| 829 | * The javascript patch overwrites the $.ui.dialog.overlay.events object to remove | |
| 830 | * the mousedown, mouseup and click events from the list of events that are bound | |
| 831 | * in $.ui.dialog.overlay.create. | |
| 832 | */ | |
| 833 | ||
| 834 | function views_ui_library_alter(&$libraries, $module) { | |
| 835 | if ($module == 'system' && isset($libraries['ui.dialog'])) { | |
| 836 | if (version_compare($libraries['ui.dialog']['version'], '1.7.2', '>=')) { | |
| 86f2a668 | 837 | $libraries['ui.dialog']['js'][drupal_get_path('module', 'views') . '/js/jquery.ui.dialog.patch.js'] = array(); |
| edb447fb EM |
838 | } |
| 839 | } | |
| 840 | } | |
| c64a9ebb EM |
841 | |
| 842 | /** | |
| 843 | * Handle bad updates from alpha versions to beta versions. | |
| 844 | * | |
| 845 | * This function will ONLY be called if there is an old menu entry for | |
| 846 | * this hanging around. This will force a menu rebuild and a cache clear | |
| 847 | * which should resolve the problem. | |
| 848 | */ | |
| 849 | function views_ui_list_views() { | |
| 850 | drupal_flush_all_caches(); | |
| 851 | menu_rebuild(); | |
| 852 | drupal_goto($_GET['q']); | |
| 853 | } | |
| 0be2cb32 EM |
854 | |
| 855 | /** | |
| 856 | * Truncate strings to a set length and provide a ... if they truncated. | |
| 857 | * | |
| 858 | * This is often used in the UI to ensure long strings fit. | |
| 859 | */ | |
| 860 | function views_ui_truncate($string, $length) { | |
| 861 | if (drupal_strlen($string) > $length) { | |
| 862 | $string = drupal_substr($string, 0, $length); | |
| 863 | $string .= '...'; | |
| 864 | } | |
| 865 | ||
| 866 | return $string; | |
| 867 | } |