menu_clone.admin.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php
  2. /**
  3. * Create the clone form.
  4. *
  5. * @param form the form
  6. * @param form_state reference to the state of the form
  7. * @param menu the menu object received from hook_load()
  8. * @see menu_clone_load()
  9. *
  10. * TODO: See if we can improve the $form['edit_menu']['edit_tree'] field. Use
  11. * something else instead of 'markup'.
  12. */
  13. function menu_clone_clone_form($form, &$form_state, $menu) {
  14. $form['menu_name'] = array(
  15. '#type' => 'textfield',
  16. '#title' => t('Menu name'),
  17. '#default_value' => $menu['menu_name'],
  18. '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI - drupal_strlen('menu-'),
  19. '#description' => t('The machine-readable name of this menu. This text will be used for constructing the URL of the <em>menu overview</em> page for this menu. This name must contain only lowercase letters, numbers, and hyphens, and must be unique.'),
  20. '#required' => TRUE,
  21. );
  22. $form['title'] = array(
  23. '#type' => 'textfield',
  24. '#title' => t('Title'),
  25. '#default_value' => $menu['title'],
  26. '#required' => TRUE,
  27. );
  28. $form['description'] = array(
  29. '#type' => 'textarea',
  30. '#title' => t('Description'),
  31. '#default_value' => $menu['description'],
  32. );
  33. if (!empty($menu['tree'])) {
  34. // Add language support if available.
  35. $form['edit_menu'] = array(
  36. '#type' => 'fieldset',
  37. '#title' => t('Customise menu'),
  38. '#description' => t('Before actually cloning the menu, you can customise it first.'),
  39. '#collapsible' => FALSE,
  40. '#collapsed' => FALSE,
  41. '#tree' => TRUE,
  42. );
  43. if (module_exists('i18n_menu')) {
  44. $form['edit_menu']['menu_lang'] = array(
  45. '#type' => 'select',
  46. '#title' => t('Change language'),
  47. '#description' => t('You can globally change the language of <em>all</em> the available menu items.'),
  48. '#options' => array_merge(array('' => t('No change')), i18n_language_list()),
  49. );
  50. }
  51. // Append the tree form to the fieldset.
  52. $form['edit_menu']['edit_tree'] = array(
  53. '#markup' => '<div class="form-item"><label>' . t('Edit menu') . ':</label><div class="description">' . t('You can reorder the menu items and adjust basic settings here. Drag unwanted items below the <em>Ignore</em> row, or drag the <em>Ignore</em> row itself.') . '</div></div>',
  54. );
  55. module_load_include('inc', 'menu', 'menu.admin');
  56. // In D6, the _menu_overview_tree_form() function also added the 'expanded'
  57. // field. In D7 it has been removed from that function. If we want it back
  58. // we will have to create a _menu_clone_overview_tree_form() and add an
  59. // additional checkbox after $form[$mlid]['hidden'].
  60. $form['edit_menu']['tree'] = _menu_overview_tree_form($menu['tree']);
  61. _menu_clone_add_ignore_row($form['edit_menu']['tree']);
  62. }
  63. $form['submit'] = array(
  64. '#type' => 'submit',
  65. '#value' => t('Create new menu'),
  66. );
  67. return $form;
  68. }
  69. /**
  70. * Add the ignore row to the menu tree.
  71. *
  72. * @param form the form to which to add the ignore row.
  73. */
  74. function _menu_clone_add_ignore_row(&$form) {
  75. // Add ignore row with fake mlid and plid fields to make the tabledrag work
  76. // properly.
  77. $form['ignore']['title'] = array(
  78. '#markup' => '<strong>' . t("Ignore: the items below this row won't be cloned.") . '</strong>',
  79. );
  80. $form['ignore']['check'] = array(
  81. '#type' => 'hidden',
  82. '#value' => 'ignore',
  83. );
  84. $form['ignore']['weight'] = array(
  85. '#type' => 'weight',
  86. '#delta' => 50,
  87. '#default_value' => 50,
  88. );
  89. $form['ignore']['mlid'] = array(
  90. '#type' => 'hidden',
  91. '#value' => 0,
  92. );
  93. $form['ignore']['plid'] = array(
  94. '#type' => 'textfield',
  95. '#default_value' => 0,
  96. '#size' => 6,
  97. );
  98. }
  99. /**
  100. * Validation function for menu_clone_cone_form().
  101. *
  102. * @param form the form object
  103. * @param form_state reference to the form state
  104. * @see menu_clone_cone_form()
  105. */
  106. function menu_clone_clone_form_validate($form, &$form_state) {
  107. $menu = $form_state['values'];
  108. if (preg_match('/[^a-z0-9-]/', $menu['menu_name'])) {
  109. form_set_error('menu_name', t('The menu name may only consist of lowercase letters, numbers, and hyphens.'));
  110. }
  111. // SELECT menu_name FROM {menu_custom} WHERE menu_name = '%s'
  112. $result = db_select('menu_custom', 'm')
  113. ->fields('m', array('menu_name'))
  114. ->condition('menu_name', 'menu-' . $menu['menu_name'], '=')
  115. ->execute()
  116. ->fetchField();
  117. if (!empty($result)) {
  118. form_set_error('menu_name', t("The machine-readable name '@menu_name' must be unique. A menu named '@menu_name' already exists.", array('@menu_name' => $menu['menu_name'])));
  119. }
  120. }
  121. /**
  122. * Submit function for menu_clone_cone_form(). Parts were taken from the Drupal
  123. * core Menu module.
  124. *
  125. * @param form the form object
  126. * @param form_state reference to the form state
  127. * @see menu_clone_cone_form()
  128. * @see Drupal core menu.admin.inc menu_overview_form_submit()
  129. * @see Drupal core menu.admin.inc menu_edit_menu_submit()
  130. */
  131. function menu_clone_clone_form_submit($form, &$form_state) {
  132. // Create new menu.
  133. // Begin code taken from menu_edit_menu_submit().
  134. $menu = $form_state['values'];
  135. if (isset($menu['edit_menu']['menu_lang']) && !empty($menu['edit_menu']['menu_lang'])) {
  136. $menu['i18n_mode'] = I18N_MODE_MULTIPLE;
  137. }
  138. $path = 'admin/structure/menu/manage/';
  139. // Add 'menu-' to the menu name to help avoid name-space conflicts.
  140. $menu['menu_name'] = 'menu-' . $menu['menu_name'];
  141. $link['link_title'] = $menu['title'];
  142. $link['link_path'] = $path . $menu['menu_name'];
  143. $link['router_path'] = $path . '%';
  144. $link['module'] = 'menu';
  145. $link['plid'] = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :link AND module = :module", array(
  146. ':link' => 'admin/structure/menu',
  147. ':module' => 'system'
  148. ))
  149. ->fetchField();
  150. menu_link_save($link);
  151. menu_save($menu);
  152. // End code taken from menu_edit_menu_submit().
  153. // Add the menu items to the newly created menu.
  154. if (!empty($form_state['input']['edit_menu']['tree'])) {
  155. // When dealing with saving menu items, the order in which these items are
  156. // saved is critical. If a changed child item is saved before its parent,
  157. // the child item could be saved with an invalid path past its immediate
  158. // parent. To prevent this, save items in the form in the same order they
  159. // are sent by $_POST, ensuring parents are saved first, then their children.
  160. // See http://drupal.org/node/181126#comment-632270
  161. $order = array_flip(array_keys($form_state['input']['edit_menu']['tree'])); // Get the $_POST order.
  162. $form['edit_menu']['tree'] = array_merge($order, $form['edit_menu']['tree']); // Update our original form with the new order.
  163. //$order = array_flip(array_keys($form['#post']['edit_menu']['tree'])); // Get the $_POST order.
  164. //$form['edit_menu']['tree'] = array_merge($order, $form['edit_menu']['tree']); // Update our original form with the new order.
  165. $menu_items = array();
  166. $fields = array('weight', 'plid');
  167. foreach (element_children($form['edit_menu']['tree']) as $mlid) {
  168. if (isset($form['edit_menu']['tree'][$mlid]['#item'])) {
  169. $element = $form['edit_menu']['tree'][$mlid];
  170. // Update any fields that might have changed in this menu item.
  171. foreach ($fields as $field) {
  172. $element['#item'][$field] = $element[$field]['#value'];
  173. }
  174. if (isset($menu['edit_menu']['menu_lang']) && !empty($menu['edit_menu']['menu_lang'])) {
  175. $element['#item']['language'] = $menu['edit_menu']['menu_lang'];
  176. }
  177. // Hidden is a special case, the value needs to be reversed.
  178. // Convert to integer rather than boolean due to PDO cast to string.
  179. $element['#item']['hidden'] = $element['hidden']['#value'] ? 0 : 1;
  180. // Make sure we create a new item and not overwrite an existing one and
  181. // attach it to our new menu.
  182. $element['#item']['menu_name'] = $menu['menu_name'];
  183. $element['#item']['mlid'] = NULL;
  184. $element['#item']['module'] = 'menu';
  185. $menu_items[$mlid] = $element['#item'];
  186. }
  187. else if (isset($form['edit_menu']['tree'][$mlid]['check'])) {
  188. // Check if we have reached the ignore field so we can stop.
  189. if ($form['edit_menu']['tree'][$mlid]['check']['#value'] == 'ignore') {
  190. break;
  191. }
  192. }
  193. }
  194. // Save the items being cloned to the database.
  195. $depth_mlids = array(); // Keep track of the new mlids per depth and parent.
  196. foreach ($menu_items as $item) {
  197. if (intval($item['depth']) > 1 && !isset($depth_mlids[$item['depth']][$item['plid']])) {
  198. // Item with a parent, so keep track of the original parent and the
  199. // new mlid belonging to it.
  200. $item['plid'] = $depth_mlids[$item['depth']][$item['plid']] = $new_mlid;
  201. }
  202. else if (intval($item['depth']) > 1) {
  203. // Item with the same parent as we have aleady processed before.
  204. $item['plid'] = $depth_mlids[$item['depth']][$item['plid']];
  205. }
  206. $item['customized'] = 1;
  207. $new_mlid = menu_link_save($item);
  208. }
  209. }
  210. $form_state['redirect'] = $path . $menu['menu_name'];
  211. }
  212. /**
  213. * Theme the menu tree of the clone form into a table and display it inside its
  214. * proper fieldset.
  215. *
  216. * @param form The form array to be themed
  217. * @see menu_clone_clone_form()
  218. * @ingroup themeable
  219. */
  220. function theme_menu_clone_clone_form($variables) {
  221. $form = $variables['form'];
  222. // Theme only when necessary.
  223. if (!empty($form['edit_menu'])) {
  224. drupal_add_tabledrag('menu-clone', 'match', 'parent', 'menu-clone-plid', 'menu-clone-plid', 'menu-clone-mlid', TRUE, MENU_MAX_DEPTH - 1);
  225. drupal_add_tabledrag('menu-clone', 'order', 'sibling', 'menu-clone-weight');
  226. drupal_add_css(drupal_get_path('module', 'menu_clone') . '/css/menu_clone.css');
  227. $header = array(
  228. t('Menu item'),
  229. array('data' => t('Enabled'), 'class' => array('checkbox')),
  230. t('Weight'),
  231. );
  232. $rows = array();
  233. foreach (element_children($form['edit_menu']['tree']) as $mlid) {
  234. if (isset($form['edit_menu']['tree'][$mlid]['hidden'])) {
  235. $element = &$form['edit_menu']['tree'][$mlid];
  236. // Add special classes to be used for tabledrag.js.
  237. $element['plid']['#attributes']['class'] = array('menu-clone-plid');
  238. $element['mlid']['#attributes']['class'] = array('menu-clone-mlid');
  239. $element['weight']['#attributes']['class'] = array('menu-clone-weight');
  240. // Change the parent field to a hidden. This allows any value but hides
  241. // the field.
  242. $element['plid']['#type'] = 'hidden';
  243. $row = array();
  244. $row[] = theme('indentation', array('size' => $element['#item']['depth'] - 1)) . drupal_render($element['title']);
  245. $row[] = array('data' => drupal_render($element['hidden']), 'class' => array('checkbox'));
  246. $row[] = drupal_render($element['weight']) . drupal_render($element['plid']) . drupal_render($element['mlid']);
  247. $row = array_merge(array('data' => $row), $element['#attributes']);
  248. $row['class'][] = 'draggable';
  249. $rows[] = $row;
  250. }
  251. }
  252. // Add the Ignore row. The ignore row must be draggable as well to avoid
  253. // situations where the ignore row is at the top of the table and no items
  254. // can be dragged above it anymore.
  255. // A draggable ignore row does also add the ability of simply dragging the
  256. // ignore row to a spot in the menu, instead of having to dragg all the
  257. // unnecessary elements below it.
  258. $form['edit_menu']['tree']['ignore']['plid']['#attributes']['class'] = array('menu-clone-plid');
  259. $form['edit_menu']['tree']['ignore']['mlid']['#attributes']['class'] = array('menu-clone-mlid');
  260. $form['edit_menu']['tree']['ignore']['weight']['#attributes']['class'] = array('menu-clone-weight');
  261. $row = array();
  262. $row[] = array('data' => drupal_render($form['edit_menu']['tree']['ignore']['title']), 'colspan' => '2');
  263. $row[] = drupal_render($form['edit_menu']['tree']['ignore']['check']) . drupal_render($form['edit_menu']['tree']['ignore']['weight']) . drupal_render($form['edit_menu']['tree']['ignore']['mlid']) . drupal_render($form['edit_menu']['tree']['ignore']['plid']);
  264. $rows[] = array('data' => $row, 'class' => array('draggable', 'ignore'));
  265. // Make sure the draggable table is rendered inside the fieldset. Having
  266. // trouble with this? Check the possibilities here
  267. // http://drupal.org/node/82916 and here http://drupal.org/node/98065. The
  268. // latter worked for me, others did not.
  269. $form['edit_menu']['tree']['#children'] = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'menu-clone')));
  270. }
  271. $output = drupal_render_children($form);
  272. return $output;
  273. }