skinr_ui.module 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. <?php
  2. /**
  3. * @file
  4. * Handles Skinr UI functionality allowing users to apply skins to their site.
  5. */
  6. /**
  7. * Implements hook_permission().
  8. */
  9. function skinr_ui_permission() {
  10. return array(
  11. 'administer skinr' => array(
  12. 'title' => t('Administer Skinr'),
  13. ),
  14. 'edit skin settings' => array(
  15. 'title' => t('Edit skin settings.'),
  16. ),
  17. 'edit advanced skin settings' => array(
  18. 'title' => t('Edit advanced skin settings'),
  19. 'description' => t('Edit advanced skin settings, such as custom CSS classes.'),
  20. ),
  21. );
  22. }
  23. /**
  24. * Determine whether the user has a given privilege.
  25. *
  26. * @param $string
  27. * The permission, such as "administer nodes", being checked for.
  28. * @param $account
  29. * (optional) The account to check, if not given use currently logged in user.
  30. *
  31. * @return
  32. * Boolean TRUE if the current user has the requested permission.
  33. *
  34. * @see user_access()
  35. */
  36. function skinr_ui_access($string, $account = NULL) {
  37. return user_access($string, $account) || user_access('administer skinr', $account);
  38. }
  39. /**
  40. * Implements hook_menu().
  41. */
  42. function skinr_ui_menu() {
  43. $items['admin/structure/skinr'] = array(
  44. 'title' => 'Skinr',
  45. 'description' => 'Manage your skin configurations and rules, import and export skin configurations.',
  46. 'page callback' => 'drupal_get_form',
  47. 'page arguments' => array('skinr_ui_list'),
  48. 'access arguments' => array('administer skinr'),
  49. 'file' => 'skinr_ui.admin.inc',
  50. );
  51. $items['admin/structure/skinr/list'] = array(
  52. 'title' => 'List',
  53. 'description' => t('Manage skinr configurations.'),
  54. 'type' => MENU_DEFAULT_LOCAL_TASK,
  55. 'weight' => -10,
  56. );
  57. // Themes.
  58. $default_theme = variable_get('theme_default', 'bartik');
  59. $items['admin/structure/skinr/library'] = array(
  60. 'title' => 'Library',
  61. 'description' => 'Manage what skins are available when configuring the way your site looks.',
  62. 'page callback' => 'skinr_ui_admin_library',
  63. 'page arguments' => array($default_theme),
  64. 'access arguments' => array('administer skinr'),
  65. 'file' => 'skinr_ui.admin.inc',
  66. 'type' => MENU_LOCAL_TASK,
  67. );
  68. foreach (list_themes() as $key => $theme) {
  69. $items['admin/structure/skinr/library/list/' . $key] = array(
  70. 'title' => check_plain($theme->info['name']),
  71. 'page arguments' => array($key),
  72. 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
  73. 'weight' => $key == $default_theme ? -10 : 0,
  74. 'access callback' => '_skinr_ui_themes_access',
  75. 'access arguments' => array($theme),
  76. 'file' => 'skinr_ui.admin.inc',
  77. );
  78. }
  79. // Rules.
  80. $items['admin/structure/skinr/rules'] = array(
  81. 'title' => 'Rules',
  82. 'page callback' => 'skinr_rules',
  83. 'type' => MENU_LOCAL_TASK,
  84. 'access arguments' => array('administer skinr'),
  85. 'weight' => 1,
  86. 'description' => t('Configure region and page level Skinr rules.'),
  87. 'file' => 'skinr_ui.rules.inc',
  88. );
  89. $items['admin/structure/skinr/rules/add'] = array(
  90. 'title' => 'Create a new rule',
  91. 'page callback' => 'drupal_get_form',
  92. 'page arguments' => array('skinr_rule_add'),
  93. 'type' => MENU_LOCAL_ACTION,
  94. 'access arguments' => array('administer skinr'),
  95. 'file' => 'skinr_ui.rules.inc',
  96. );
  97. $items['admin/structure/skinr/rules/%skinr_rule/edit'] = array(
  98. 'title' => 'Edit rule',
  99. 'page callback' => 'drupal_get_form',
  100. 'page arguments' => array('skinr_rule_edit', 4),
  101. 'type' => MENU_CALLBACK,
  102. 'access arguments' => array('administer skinr'),
  103. 'file' => 'skinr_ui.rules.inc',
  104. );
  105. $items['admin/structure/skinr/rules/%skinr_rule/delete'] = array(
  106. 'title' => 'Delete rule',
  107. 'page callback' => 'drupal_get_form',
  108. 'page arguments' => array('skinr_rule_delete_confirm', 4),
  109. 'type' => MENU_CALLBACK,
  110. 'access arguments' => array('administer skinr'),
  111. 'file' => 'skinr_ui.rules.inc',
  112. );
  113. // Import & Export.
  114. $items['admin/structure/skinr/import'] = array(
  115. 'title' => 'Import',
  116. 'page callback' => 'drupal_get_form',
  117. 'page arguments' => array('skinr_ui_import_form'),
  118. 'type' => MENU_LOCAL_TASK,
  119. 'access arguments' => array('administer skinr'),
  120. 'weight' => 2,
  121. 'description' => t('Import skin configurations.'),
  122. 'file' => 'skinr_ui.admin.inc',
  123. );
  124. $items['admin/structure/skinr/export'] = array(
  125. 'title' => 'Export',
  126. 'page callback' => 'drupal_get_form',
  127. 'page arguments' => array('skinr_ui_export_form'),
  128. 'type' => MENU_LOCAL_TASK,
  129. 'access arguments' => array('administer skinr'),
  130. 'weight' => 3,
  131. 'description' => t('Export skin configurations.'),
  132. 'file' => 'skinr_ui.admin.inc',
  133. );
  134. // Configure skin settings for an element.
  135. $items['admin/structure/skinr/edit/%skinr_js/%/%'] = array(
  136. 'title' => 'Edit skin',
  137. 'title callback' => 'skinr_ui_edit_title',
  138. 'title arguments' => array(5, 6),
  139. 'page callback' => 'skinr_ui_edit',
  140. 'page arguments' => array(4, 5, 6), // js|nojs, module, element
  141. 'type' => MENU_CALLBACK,
  142. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  143. 'access arguments' => array('edit skin settings'),
  144. );
  145. $items['admin/structure/skinr/edit/%skinr_js/%/%/configure'] = array(
  146. 'title' => 'Edit skin',
  147. 'title callback' => 'skinr_ui_edit_contextual_title',
  148. 'title arguments' => array(5, 6),
  149. 'type' => MENU_DEFAULT_LOCAL_TASK,
  150. 'context' => MENU_CONTEXT_INLINE,
  151. );
  152. // Enable a skin configuration.
  153. $items['admin/structure/skinr/skin/%skinr_skin/enable'] = array(
  154. 'title' => 'Enable skin',
  155. 'page callback' => 'skinr_ui_skin_status_set',
  156. 'page arguments' => array(4, TRUE),
  157. 'type' => MENU_CALLBACK,
  158. 'access arguments' => array('administer skinr'),
  159. 'file' => 'skinr_ui.admin.inc',
  160. );
  161. // Disable a skin configuration.
  162. $items['admin/structure/skinr/skin/%skinr_skin/disable'] = array(
  163. 'title' => 'Disable skin',
  164. 'page callback' => 'skinr_ui_skin_status_set',
  165. 'page arguments' => array(4, FALSE),
  166. 'type' => MENU_CALLBACK,
  167. 'access arguments' => array('administer skinr'),
  168. 'file' => 'skinr_ui.admin.inc',
  169. );
  170. // Delete a skin configuration.
  171. $items['admin/structure/skinr/skin/%skinr_skin/delete'] = array(
  172. 'title' => 'Delete skin',
  173. 'page callback' => 'drupal_get_form',
  174. 'page arguments' => array('skinr_ui_delete_confirm', 4),
  175. 'type' => MENU_CALLBACK,
  176. 'access arguments' => array('administer skinr'),
  177. 'file' => 'skinr_ui.admin.inc',
  178. );
  179. return $items;
  180. }
  181. /**
  182. * Helper function to determine if ajax is used to call a function.
  183. */
  184. function skinr_js_load($js = 'nojs') {
  185. if ($js == 'ajax') {
  186. return TRUE;
  187. }
  188. return 0;
  189. }
  190. /**
  191. * Menu item access callback - only admin or enabled themes can be accessed.
  192. */
  193. function _skinr_ui_themes_access($theme) {
  194. return user_access('administer skinr') && drupal_theme_access($theme);
  195. }
  196. /**
  197. * Implements hook_theme().
  198. */
  199. function skinr_ui_theme() {
  200. return array(
  201. 'skinr_ui_admin_library_fieldset' => array(
  202. 'render element' => 'form',
  203. 'file' => 'skinr_ui.admin.inc',
  204. ),
  205. 'skinr_ui_admin_library_summary' => array(
  206. 'variables' => array('name' => NULL, 'description' => NULL),
  207. 'file' => 'skinr_ui.admin.inc',
  208. ),
  209. );
  210. }
  211. /**
  212. * Implements hook_help().
  213. */
  214. function skinr_ui_help($path, $arg) {
  215. if (module_exists('advanced_help')) {
  216. $advanced_help = '<p>' . t('Visit the <a href="@skinr-help">help page</a> for full documentation.', array('@skinr-help' => url('admin/advanced_help/skinr'))). '</p>';
  217. }
  218. else {
  219. $advanced_help = '<p>' . t('Please download and enable the <a href="http://drupal.org/project/advanced_help">Advanced Help</a> module for full Skinr documentation.') . '</p>';
  220. }
  221. switch ($path) {
  222. case 'admin/structure/skinr':
  223. return '<p>' . t('Below is a list of all skin configurations in use on this site.') . '</p>' . $advanced_help;
  224. case 'admin/structure/skinr/rule':
  225. return '<p>' . t('Below is a list of Skinr rules. Rules can be created for <em>region</em> and <em>page</em> elements. Start by creating a new rule.') . '</p>';
  226. case 'admin/structure/skinr/rule/add':
  227. return '<p>' . t('Choose the type of rule you wish to create. Page rules apply classes to the &lt;body&gt; tag. Region rules apply to the region wrapper &lt;div&gt; tag.') . '</p>';
  228. case 'admin/structure/skinr/import':
  229. return '<p>' . t('To import skin configurations, paste exported code and click the "Import" button.') . '</p>';
  230. case 'admin/structure/skinr/export':
  231. return '<p>' . t('To export skin configurations, ensure the correct theme is selected and click the "Export" button.') . '</p>';
  232. case 'admin/structure/skinr/edit/%/%/%':
  233. // @todo Make this help text more relevant.
  234. $theme_hooks = skinr_theme_hooks($arg[5], $arg[6]);
  235. return '<p>' . t('Manage which skins you want to apply to the hooks <strong>!hooks</strong>.', array('!hooks' => implode(', ', $theme_hooks))) . '</p>';
  236. case 'admin/structure/skinr/rules/%/edit':
  237. // @todo Make this help text more relevant.
  238. $theme_hooks = skinr_theme_hooks('rules', $arg[4]);
  239. return '<p>' . t('Manage which skins you want to apply to the hooks <strong>!hooks</strong>.', array('!hooks' => implode(', ', $theme_hooks))) . '</p>';
  240. }
  241. }
  242. /**
  243. * Menu title callback; sets the title for a skins configuration form page.
  244. *
  245. * @param $module
  246. * The module that we're editing settings of.
  247. * @param $element
  248. * The element we're editing settings of.
  249. */
  250. function skinr_ui_edit_title($module, $element) {
  251. return t('Skin settings for !module type !element', array('!module' => $module, '!element' => $element));
  252. }
  253. /**
  254. * Menu title callback; sets the title for a skins configuration form page.
  255. *
  256. * @param $module
  257. * The module that we're editing settings for.
  258. * @param $element
  259. * The element we're editing settings for.
  260. */
  261. function skinr_ui_edit_contextual_title($module, $element) {
  262. $contextual_links = skinr_ui_get_contextual_links();
  263. foreach ($contextual_links as $hook => $links) {
  264. $counter = 1;
  265. foreach ($links as $link) {
  266. if ($link[1][0] == $module && $link[1][1] == $element) {
  267. if (count($links) > 1) {
  268. return t('Edit skin !number', array('!number' => $counter++));
  269. }
  270. break 2;
  271. }
  272. }
  273. }
  274. return t('Edit skin');
  275. }
  276. /**
  277. * Menu callback; prepares some variables and displays a Skinr edit form.
  278. *
  279. * @param $js
  280. * TRUE if called from javascript, FALSE otherwise.
  281. * @param $module
  282. * The module that we're editing settings of.
  283. * @param $element
  284. * The element of the object we're editing settings of.
  285. * @param $elements
  286. * An array of $element when more than one is returned from the preprocess
  287. * index handler. Used by the javascript UI to update all elements involved.
  288. */
  289. function skinr_ui_edit($js = FALSE, $module, $element, $elements = NULL) {
  290. if ($js) {
  291. // Do additional ajax related stuff.
  292. }
  293. $arguments = array(
  294. 'skinr' => array(
  295. 'module' => $module,
  296. 'element' => $element,
  297. 'elements' => $elements,
  298. ),
  299. );
  300. return drupal_get_form('skinr_ui_form', $arguments);
  301. }
  302. /**
  303. * Form builder for the skins configuration form.
  304. *
  305. * @param $arguments
  306. * An array of arguments as passed in by skinr_ui_edit().
  307. *
  308. * @ingroup forms
  309. */
  310. function skinr_ui_form($form, &$form_state, $arguments) {
  311. $form = array(
  312. '#attributes' => array('class' => 'skinr-form'),
  313. );
  314. $form['skinr']['module'] = array(
  315. '#type' => 'hidden',
  316. '#value' => !empty($form_state['skinr']['module']) ? $form_state['skinr']['module'] : $arguments['skinr']['module'],
  317. );
  318. $form['skinr']['element'] = array(
  319. '#type' => 'hidden',
  320. '#value' => !empty($form_state['skinr']['element']) ? $form_state['skinr']['element'] : $arguments['skinr']['element'],
  321. );
  322. if (!empty($form_state['skinr']['elements']) || !empty($arguments['skinr']['elements'])) {
  323. $form['skinr']['elements'] = array(
  324. '#type' => 'hidden',
  325. '#value' => !empty($form_state['skinr']['elements']) ? $form_state['skinr']['elements'] : $arguments['skinr']['elements'],
  326. );
  327. }
  328. $form['actions'] = array('#type' => 'actions');
  329. $form['actions']['submit'] = array(
  330. '#type' => 'submit',
  331. '#value' => t('Save'),
  332. '#weight' => 50,
  333. );
  334. return $form;
  335. }
  336. /**
  337. * Implements hook_form_alter().
  338. */
  339. function skinr_ui_form_alter(&$form, $form_state, $form_id) {
  340. // Fix for update script.
  341. if ($form_id == 'update_script_selection_form') {
  342. return;
  343. }
  344. // Ensure module and element values are set.
  345. if (empty($form['skinr']['module']['#value']) || empty($form['skinr']['element']['#value'])) {
  346. return;
  347. }
  348. // Check for access.
  349. if (!skinr_ui_access('edit skin settings')) {
  350. // Deny access.
  351. return;
  352. }
  353. $module = $form['skinr']['module']['#value'];
  354. $element = $form['skinr']['element']['#value'];
  355. $groups = skinr_get_group_info();
  356. $skin_infos = skinr_get_skin_info();
  357. // Apply overridden status to skins.
  358. foreach ($skin_infos as $skin_name => $skin_info) {
  359. $skin_infos[$skin_name]['status'] = skinr_skin_info_status_get($skin_infos[$skin_name]);
  360. }
  361. // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
  362. $theme_hooks = skinr_theme_hooks($module, $element);
  363. $form['skinr_settings'] = array(
  364. '#tree' => TRUE,
  365. // Set weight to accommodate Rules UI.
  366. '#weight' => 0,
  367. );
  368. $themes = list_themes();
  369. ksort($themes);
  370. // Get current theme, but make sure it's not the admin theme when we're editing with AJAX.
  371. $current_theme = skinr_current_theme(TRUE);
  372. foreach ($themes as $theme) {
  373. if (!$theme->status) {
  374. continue;
  375. }
  376. // If this hook is a region, and the region does not exist for this
  377. // theme, don't bother outputting any of the settings.
  378. if (strpos($theme_hooks[0], 'region') === 0) {
  379. // Strip the region__ part off the region name.
  380. $region = substr($theme_hooks[0], 8);
  381. $regions = system_region_list($theme->name, REGIONS_VISIBLE);
  382. if (!isset($regions[$region])) {
  383. continue;
  384. }
  385. }
  386. if (!$form_state['submitted']) {
  387. $params = array(
  388. 'theme' => $theme->name,
  389. 'module' => $module,
  390. 'element' => $element,
  391. );
  392. if ($skins = skinr_skin_load_multiple(skinr_skin_get_sids($params))) {
  393. $defaults = array();
  394. foreach ($skins as $skin) {
  395. $defaults[$skin->skin] = $skin->options;
  396. }
  397. }
  398. else {
  399. $defaults = array();
  400. }
  401. }
  402. else {
  403. // Handle preview before submit.
  404. // @todo Is this still needed? If so, it needs to be fixed.
  405. $defaults = $form_state['values'];
  406. }
  407. if (!isset($form['skinr_settings'][$module . '_type'])) {
  408. $form['skinr_settings'][$module . '_type'] = array(
  409. '#type' => 'container',
  410. );
  411. if ($module == 'rules') {
  412. $form['skinr_settings']['skinr_settings_title'] = array(
  413. '#type' => 'item',
  414. '#title' => t('Skinr settings'),
  415. '#weight' => -1,
  416. );
  417. }
  418. }
  419. $form['skinr_settings'][$module . '_type'][$theme->name] = array(
  420. '#type' => 'fieldset',
  421. '#title' => $theme->info['name'] . ($theme->name == $current_theme ? ' (' . t('enabled + default') . ')' : ''),
  422. '#collapsible' => TRUE,
  423. '#collapsed' => $theme->name == $current_theme ? FALSE : TRUE,
  424. );
  425. if ($theme->name == $current_theme) {
  426. // Current theme goes at the top.
  427. $form['skinr_settings'][$module . '_type'][$theme->name]['#attributes'] = array('class' => array('skinr-ui-current-theme'));
  428. $form['skinr_settings'][$module . '_type'][$theme->name]['#weight'] = -10;
  429. // Use vertical tabs.
  430. $form['skinr_settings'][$module . '_type'][$theme->name]['groups'] = array(
  431. '#type' => 'vertical_tabs',
  432. );
  433. }
  434. // Create individual widgets for each skin.
  435. foreach ($skin_infos as $skin_name => $skin_info) {
  436. // Check if this skin is disabled.
  437. if (empty($skin_info['status'][$theme->name])) {
  438. continue;
  439. }
  440. // Check if this skin applies to this hook.
  441. if (!is_array($skin_info['theme hooks']) || (!in_array('*', $skin_info['theme hooks']) && !_skinr_is_featured($theme_hooks, $skin_info['theme hooks']))) {
  442. continue;
  443. }
  444. // Create widget.
  445. $field = array();
  446. if (!empty($skin_info['form callback'])) {
  447. // Process custom form callbacks.
  448. // Load include file.
  449. if (!empty($skin_info['source']['filename'])) {
  450. skinr_load_include($skin_info['source']['path'] . '/' . $skin_info['source']['filename']);
  451. }
  452. // Execute form callback.
  453. if (function_exists($skin_info['form callback'])) {
  454. $context = array(
  455. 'theme' => $theme->name,
  456. 'skin_name' => $skin_name,
  457. 'skin_info' => $skin_info,
  458. );
  459. $field = $skin_info['form callback']($form, $form_state, $context);
  460. }
  461. }
  462. else {
  463. switch ($skin_info['type']) {
  464. case 'checkboxes':
  465. $field = array(
  466. '#type' => 'checkboxes',
  467. '#multiple' => TRUE,
  468. '#title' => t($skin_info['title']),
  469. '#options' => skinr_ui_info_options_to_form_options($skin_info['options']),
  470. '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : array(),
  471. '#description' => t($skin_info['description']),
  472. '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
  473. );
  474. break;
  475. case 'radios':
  476. $field = array(
  477. '#type' => 'radios',
  478. '#title' => t($skin_info['title']),
  479. '#options' => array_merge(array('' => '&lt;none&gt;'), skinr_ui_info_options_to_form_options($skin_info['options'])),
  480. '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
  481. '#description' => t($skin_info['description']),
  482. '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
  483. );
  484. break;
  485. case 'select':
  486. $field = array(
  487. '#type' => 'select',
  488. '#title' => t($skin_info['title']),
  489. '#options' => array_merge(array('' => '<none>'), skinr_ui_info_options_to_form_options($skin_info['options'])),
  490. '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
  491. '#description' => t($skin_info['description']),
  492. '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
  493. );
  494. break;
  495. default:
  496. // Raise an error.
  497. drupal_set_message(t("Widget %name's type is invalid.", array('%name' => $skin_name)), 'error', FALSE);
  498. break;
  499. }
  500. }
  501. if (empty($skin_info['group']) || empty($groups[$skin_info['group']])) {
  502. $form['skinr_settings'][$module . '_type'][$theme->name][$skin_name] = $field;
  503. }
  504. else {
  505. if (!empty($field) && !isset($form['skinr_settings'][$module . '_type'][$theme->name]['groups'][$skin_info['group']])) {
  506. $group = $groups[$skin_info['group']];
  507. $form['skinr_settings'][$module . '_type'][$theme->name]['groups'][$skin_info['group']] = array(
  508. '#type' => 'fieldset',
  509. '#title' => t($group['title']),
  510. '#description' => t($group['description']),
  511. '#weight' => isset($group['weight']) ? $group['weight'] : NULL,
  512. );
  513. }
  514. $form['skinr_settings'][$module . '_type'][$theme->name]['groups'][$skin_info['group']][$skin_name] = $field;
  515. }
  516. }
  517. // Check for access.
  518. if (skinr_ui_access('edit advanced skin settings')) {
  519. $skin_name = '_additional';
  520. $form['skinr_settings'][$module . '_type'][$theme->name]['groups']['_additional'] = array(
  521. '#type' => 'fieldset',
  522. '#title' => t('Advanced'),
  523. '#weight' => 50,
  524. );
  525. $form['skinr_settings'][$module . '_type'][$theme->name]['groups']['_additional']['_additional'] = array(
  526. '#type' => 'textfield',
  527. '#title' => t('CSS classes'),
  528. '#size' => 40,
  529. '#description' => t('To add CSS classes manually, enter classes separated by a single space i.e. <code>first-class second-class</code>'),
  530. '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
  531. );
  532. }
  533. }
  534. // Only add validation handler once.
  535. if (!isset($form['#validate']) || !in_array('skinr_ui_form_validate', $form['#validate'])) {
  536. $form['#validate'][] = 'skinr_ui_form_validate';
  537. }
  538. // Only add submit handler once.
  539. if (!isset($form['#submit']) || !in_array('skinr_ui_form_submit', $form['#submit'])) {
  540. $form['#submit'][] = 'skinr_ui_form_submit';
  541. }
  542. }
  543. /**
  544. * Form validation handler for skinr_ui_form_alter().
  545. */
  546. function skinr_ui_form_validate($form, &$form_state) {
  547. $module = $form_state['values']['module'];
  548. $element = $form_state['values']['element'];
  549. $error = FALSE;
  550. if (isset($form_state['values']['skinr_settings'][$module . '_type'])) {
  551. foreach ($form_state['values']['skinr_settings'][$module . '_type'] as $theme_name => $theme) {
  552. if (isset($theme['groups']['_additional']['_additional'])) {
  553. // Validate additional classes field.
  554. if (preg_match('/[^a-zA-Z0-9\-\_\s]/', $theme['groups']['_additional']['_additional'])) {
  555. form_set_error('skinr_settings][' . $module . '_type][' . $theme_name . '][groups][_additional][_additional', t('Additional classes for Skinr may only contain alphanumeric characters, spaces, - and _.'));
  556. $error = TRUE;
  557. }
  558. }
  559. }
  560. }
  561. if (!$error) {
  562. $groups = skinr_get_group_info();
  563. if (isset($form_state['values']['skinr_settings'][$module . '_type'])) {
  564. foreach ($form_state['values']['skinr_settings'][$module . '_type'] as $theme_name => $theme) {
  565. // Unset active tab variables.
  566. foreach ($theme['groups'] as $skin_name => $options) {
  567. if (strpos($skin_name, '__groups__active_tab') !== FALSE) {
  568. unset($form_state['values']['skinr_settings'][$module . '_type'][$theme_name]['groups'][$skin_name]);
  569. continue;
  570. }
  571. }
  572. // Undo any grouping to ease processing on submit.
  573. foreach ($groups as $group_name => $group) {
  574. if (!empty($theme['groups'][$group_name]) && is_array($theme['groups'][$group_name])) {
  575. $group_values = $theme['groups'][$group_name];
  576. unset($form_state['values']['skinr_settings'][$module . '_type'][$theme_name]['groups'][$group_name]);
  577. $form_state['values']['skinr_settings'][$module . '_type'][$theme_name]['groups'] = array_merge($form_state['values']['skinr_settings'][$module . '_type'][$theme_name]['groups'], $group_values);
  578. }
  579. }
  580. }
  581. }
  582. }
  583. }
  584. /**
  585. * Form submission handler for skinr_ui_form_alter().
  586. */
  587. function skinr_ui_form_submit($form, &$form_state) {
  588. $current_theme = skinr_current_theme(TRUE);
  589. $module = $form_state['values']['module'];
  590. $element = $form_state['values']['element'];
  591. if (isset($form_state['values']['skinr_settings'][$module . '_type'])) {
  592. foreach ($form_state['values']['skinr_settings'][$module . '_type'] as $theme_name => $theme) {
  593. // Process widgets.
  594. if (!empty($theme['groups']) && is_array($theme['groups'])) {
  595. foreach ($theme['groups'] as $skin_name => $options) {
  596. if ($skin_name == '_additional' && !user_access('edit advanced skin settings')) {
  597. // This user doesn't have access to alter these options.
  598. continue;
  599. }
  600. // Ensure options is an array.
  601. if (!is_array($options)) {
  602. $options = $skin_name == '_additional' ? explode(' ', $options) : array($options);
  603. }
  604. // Sanitize options.
  605. $options = _skinr_array_strip_empty($options);
  606. // Find existing skin.
  607. $params = array(
  608. 'theme' => $theme_name,
  609. 'module' => $module,
  610. 'element' => $element,
  611. 'skin' => $skin_name,
  612. );
  613. $sids = skinr_skin_get_sids($params);
  614. unset($skin);
  615. if (!empty($sids)) {
  616. $sid = reset($sids);
  617. $skin = skinr_skin_load($sid);
  618. }
  619. if (empty($options)) {
  620. if (!empty($skin)) {
  621. // Delete this skin configuration.
  622. skinr_skin_delete($skin->sid);
  623. }
  624. continue;
  625. }
  626. if (empty($skin)) {
  627. // It doesn't exist, so create a new skin.
  628. $skin = new stdClass();
  629. $skin->theme = $theme_name;
  630. $skin->module = $module;
  631. $skin->element = $element;
  632. $skin->skin = $skin_name;
  633. }
  634. $skin->options = $options;
  635. $skin->status = 1;
  636. // Save skin.
  637. if (!skinr_skin_save($skin)) {
  638. drupal_set_message(t("Skinr settings for %skin weren't saved due to an error.", array('%skin' => $skin_name)), 'error');
  639. }
  640. }
  641. }
  642. }
  643. }
  644. }
  645. /**
  646. * Implements hook_preprocess().
  647. */
  648. function skinr_ui_preprocess(&$variables, $hook) {
  649. $original_hook = $hook;
  650. $theme_registry = theme_get_registry();
  651. if (isset($theme_registry[$hook]['original hook'])) {
  652. $original_hook = $theme_registry[$hook]['original hook'];
  653. }
  654. $contextual_links = array();
  655. $counter = 0;
  656. $array_elements = skinr_invoke_all('skinr_elements', $variables, $original_hook, 'contextual_links');
  657. foreach ($array_elements as $module => $elements) {
  658. foreach ($elements as $element) {
  659. $contextual_links['skinr-' . $module . '-' . $counter++] = array(
  660. 'admin/structure/skinr/edit/nojs', array($module, $element),
  661. );
  662. }
  663. }
  664. if (!empty($contextual_links)) {
  665. skinr_ui_contextual_links($variables, $original_hook, $contextual_links);
  666. }
  667. return;
  668. }
  669. /**
  670. * Set contextual menu items for skinr.
  671. *
  672. * @param $variables
  673. * The $variables parameter from a preprocess function.
  674. * @param $hook
  675. * The $hook parameter from a preprocess function.
  676. * @param $contextual_links
  677. * An array of contextual links data as returned from Skinr's contextual
  678. * links handler.
  679. */
  680. function skinr_ui_contextual_links(&$variables, $hook, $contextual_links) {
  681. _skinr_ui_set_contextual_links($hook, $contextual_links);
  682. $hooks = theme_get_registry();
  683. // Determine the primary theme function argument.
  684. if (!empty($hooks[$hook]['variables'])) {
  685. $keys = array_keys($hooks[$hook]['variables']);
  686. $key = $keys[0];
  687. }
  688. elseif (!empty($hooks[$hook]['render element'])) {
  689. $key = $hooks[$hook]['render element'];
  690. }
  691. if (!empty($key) && isset($variables[$key])) {
  692. $element = &$variables[$key];
  693. }
  694. if (isset($element) && is_array($element)) {
  695. foreach ($contextual_links as $key => $contextual_link) {
  696. $element['#contextual_links'][$key] = $contextual_link;
  697. }
  698. }
  699. }
  700. /**
  701. * Get all contextual links as returned from Skinr's contextual links handler.
  702. *
  703. * @return
  704. * An array of contextual links data.
  705. */
  706. function skinr_ui_get_contextual_links() {
  707. return _skinr_ui_set_contextual_links();
  708. }
  709. /**
  710. * Store contextual links internally for future use.
  711. *
  712. * @return
  713. * An array of contextual links data.
  714. */
  715. function _skinr_ui_set_contextual_links($hook = NULL, $links = NULL) {
  716. static $contextual_links = array();
  717. if ($hook && $links) {
  718. if (!isset($contextual_links[$hook])) {
  719. $contextual_links[$hook] = $links;
  720. }
  721. }
  722. return $contextual_links;
  723. }
  724. /**
  725. * Helper function to determine whether one of a set of hooks exists in a list
  726. * of required theme hooks.
  727. *
  728. * @param $theme_hooks
  729. * An array of theme hooks available to this element.
  730. * @param $allowed_hooks
  731. * An array of allowed theme hooks.
  732. *
  733. * @return
  734. * TRUE if an overlap is found, FALSE otherwise.
  735. *
  736. * @todo Rename function to be more descriptive.
  737. */
  738. function _skinr_is_featured($theme_hooks, $allowed_hooks) {
  739. foreach ($theme_hooks as $theme_hook) {
  740. if (in_array($theme_hook, $allowed_hooks)) {
  741. return TRUE;
  742. }
  743. }
  744. return FALSE;
  745. }
  746. /**
  747. * Helper function to retrieve a unique id for each skinr class. Used by AJAX.
  748. *
  749. * @return
  750. * A unique ID number.
  751. *
  752. * @todo Evaluate the usefulness of this function. Should it go into
  753. * a UI front-end specific file?
  754. */
  755. function _skinr_ui_ajax_id() {
  756. static $skinr_id = 0;
  757. return ++$skinr_id;
  758. }
  759. /**
  760. * Helper function to convert an array of options, as specified in the .info
  761. * file, into an array usable by Form API.
  762. *
  763. * @param $options
  764. * An array containing at least the 'class' and 'label' keys.
  765. *
  766. * @return
  767. * A Form API compatible array of options.
  768. *
  769. * @todo Rename function to be more descriptive.
  770. */
  771. function skinr_ui_info_options_to_form_options($options) {
  772. $form_options = array();
  773. foreach ($options as $option_name => $option) {
  774. $form_options[$option_name] = t($option['title']);
  775. }
  776. return $form_options;
  777. }