flag_lists.admin.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <?php
  2. /**
  3. * @file
  4. * Contains administrative pages for creating, editing, and deleting flag lists.
  5. */
  6. /**
  7. * Flag administration page. Display a list of existing flags.
  8. */
  9. function flag_lists_admin_page() {
  10. return theme('flag_lists_admin_page', array('flag' => NULL));
  11. }
  12. /**
  13. * Theme the output for the main flag administration page.
  14. */
  15. function theme_flag_lists_admin_page($variables) {
  16. $flag = $variables['flag'];
  17. drupal_set_title(t('Flag lists'));
  18. $output = '<p>' . t('This page shows all the <em>lists</em> that are currently defined on this system.') . '</p>';
  19. // Can we use our default view?
  20. if (module_exists('views')) {
  21. $view = views_get_view('flag_lists', FALSE);
  22. if (!empty($view)) {
  23. $view->set_display('default');
  24. $output .= $view->render();
  25. }
  26. }
  27. // Else build a simplified display.
  28. else {
  29. $header = array(
  30. array('data' => t('List'), 'field' => 'name'),
  31. array('data' => t('List title'), 'field' => 'title'),
  32. array('data' => t('List type')),
  33. array('data' => t('Owner'), 'field' => 'uid'),
  34. array('data' => t('Node types')),
  35. array('data' => t('Operations')),
  36. );
  37. $flags = flag_lists_get_flags(25, $header);
  38. foreach ($flags as $flag) {
  39. $ops = theme('flag_lists_ops', $flag);
  40. $name = db_select('users', 'u')
  41. ->fields('u', array('name'))
  42. ->condition('uid', $flag->uid)
  43. ->execute()
  44. ->fetchField();
  45. $owner = l($name, 'user/' . $flag->uid, array('attributes' => array('title' => t('View user profile.'))));
  46. $rows[] = array(
  47. $flag->name,
  48. $flag->title,
  49. $flag->content_type,
  50. $owner,
  51. $flag->types ? implode(', ', $flag->types) : '-',
  52. $ops,
  53. );
  54. }
  55. if (!$flags) {
  56. $rows[] = array(
  57. array('data' => t('No flags are currently defined.'), 'colspan' => 6),
  58. );
  59. }
  60. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  61. $output .= theme('pager', NULL, 25, 0);
  62. }
  63. return $output;
  64. }
  65. function flag_lists_add($type = NULL) {
  66. if (is_null($type)) {
  67. drupal_access_denied();
  68. }
  69. return drupal_get_form('flag_lists_form', NULL, $type);
  70. }
  71. /**
  72. * Callback for adding new lists through AJAX.
  73. */
  74. function flag_lists_add_js($type = NULL) {
  75. if (is_null($type)) {
  76. drupal_json_output(array('error' => t('Type not supplied.')));
  77. exit();
  78. }
  79. $name = check_plain($_REQUEST['name']);
  80. if (!isset($name) || empty($name)) {
  81. drupal_json_output(array('error' => t('List name not supplied.')));
  82. exit();
  83. }
  84. if (drupal_strlen($name) > 255) {
  85. drupal_json_output(array('error' => t('List name is too long. Maximum is 255 characters.')));
  86. exit();
  87. }
  88. if (flag_lists_title_exists($name, $type)) {
  89. drupal_json_output(array('error' => t('You already have a @name with this name for this type of content.', array('@name' => variable_get('flag_lists_name', t('list'))))));
  90. exit();
  91. }
  92. if (!isset($account)) {
  93. $account = $GLOBALS['user'];
  94. }
  95. // New flag. Load the template row.
  96. $query = db_select('flag', 'f');
  97. $query->leftJoin('flag_lists_types', 'fl', 'f.name = fl.name');
  98. $query->fields('f')
  99. ->fields('fl')
  100. ->condition('fl.type', $type);
  101. $row = $query->execute()
  102. ->fetchObject();
  103. $newflag = flag_flag::factory_by_entity_type('node');
  104. $flag = $newflag->factory_by_row($row);
  105. // The template fid becomes the flag_lists parent flag.
  106. $flag->pfid = $row->fid;
  107. // The fid is NULL because this is really a new flag.
  108. $flag->fid = NULL;
  109. // The name is created in the save function.
  110. $flag->name = NULL;
  111. $flag->link_type = 'toggle';
  112. $flag->title = $name;
  113. $flag->types = array($type);
  114. $flag->uid = $account->uid;
  115. flag_lists_set_messages($flag);
  116. // Save it.
  117. flag_lists_save($flag, $account);
  118. drupal_json_output(array('error' => '', 'flag' => $flag));
  119. }
  120. /**
  121. * Form to Add or edit a list.
  122. */
  123. function flag_lists_form($form, $form_state, $name = NULL, $type = NULL) {
  124. // First some sanity checks. $name and $type can't both be NULL.
  125. // There must be a template for this content type.
  126. if (is_null($name) && is_null($type)) {
  127. drupal_access_denied();
  128. }
  129. if (!flag_lists_template_exists($type) && is_null($name)) {
  130. return;
  131. }
  132. // If name is numeric, then we have the fid, so get the name.
  133. if (is_numeric($name)) {
  134. $name = db_select('flag_lists_flags', 'f')
  135. ->fields('f', array('name'))
  136. ->condition('fid', $name)
  137. ->execute()
  138. ->fetchField();
  139. }
  140. // Adding a new list.
  141. if (!isset($name)) {
  142. drupal_set_title(t('Add a new list'));
  143. $form['edit'] = array(
  144. '#type' => 'hidden',
  145. '#value' => FALSE,
  146. );
  147. }
  148. // Editing an existing list.
  149. else {
  150. $flag = flag_lists_get_flag($name);
  151. $title_string_name = variable_get('flag_lists_name', 'list');
  152. $title_string = t('Edit your "@title" @name title', array('@title' => $flag->get_title(), '@name' => $title_string_name));
  153. drupal_set_title();
  154. $form['edit'] = array(
  155. '#type' => 'hidden',
  156. '#value' => TRUE,
  157. );
  158. $form['name'] = array(
  159. '#type' => 'hidden',
  160. '#value' => $name,
  161. );
  162. }
  163. $form['title'] = array(
  164. '#type' => 'textfield',
  165. '#title' => t('Title'),
  166. '#default_value' => empty($flag->title) ? '' : $flag->title,
  167. '#description' => t('A short, descriptive title for this @name list. Limit to 255 characters.', array('@name' => variable_get('flag_lists_name', t('list')))),
  168. '#maxlength' => 255,
  169. '#required' => TRUE,
  170. '#access' => empty($flag->locked['title']),
  171. '#weight' => -2,
  172. );
  173. $form['type'] = array(
  174. '#type' => 'hidden',
  175. '#value' => $type,
  176. );
  177. $form['submit'] = array(
  178. '#type' => 'submit',
  179. '#value' => t('Save'),
  180. );
  181. return $form;
  182. }
  183. /**
  184. * Validate a list.
  185. */
  186. function flag_lists_form_validate($form, $form_state) {
  187. // Ensure 255 charactor or less name.
  188. if (drupal_strlen($form_state['values']['title']) > 255) {
  189. form_set_error('name', t('The @name title may only be 255 characters long.', array('@name' => variable_get('flag_lists_name', t('list')))));
  190. }
  191. // Ensure the machine name is unique.
  192. if (!$form_state['values']['edit']) {
  193. if (flag_lists_title_exists($form_state['values']['title'], $form_state['values']['type'])) {
  194. form_set_error('title', t('You already have a @name with this name for this type of content.', array('@name' => variable_get('flag_lists_name', t('list')))));
  195. }
  196. }
  197. }
  198. /**
  199. * Save a list.
  200. */
  201. function flag_lists_form_submit($form, $form_state, $account = NULL) {
  202. if (!isset($account)) {
  203. $account = $GLOBALS['user'];
  204. }
  205. if (!empty($form_state['values']['edit'])) {
  206. // Editing the title.
  207. $flag = flag_lists_get_flag($form_state['values']['name']);
  208. $flag->title = $form_state['values']['title'];
  209. flag_lists_set_messages($flag);
  210. flag_lists_save($flag);
  211. _flag_lists_clear_cache();
  212. }
  213. else {
  214. // New flag. Load the template row.
  215. $type = $form_state['values']['type'];
  216. $query = db_select('flag', 'f');
  217. $query->leftJoin('flag_lists_types', 'fl', 'f.name = fl.name');
  218. $query->fields('f')
  219. ->fields('fl')
  220. ->condition('fl.type', $type);
  221. $row = $query->execute()
  222. ->fetchObject();
  223. $newflag = flag_flag::factory_by_entity_type('node');
  224. $flag = $newflag->factory_by_row($row);
  225. // The template fid becomes the flag_lists parent flag.
  226. $flag->pfid = $row->fid;
  227. // The fid is NULL because this is really a new flag.
  228. $flag->fid = NULL;
  229. // The name is created in the save function.
  230. $flag->name = NULL;
  231. $flag->link_type = 'toggle';
  232. $flag->title = $form_state['values']['title'];
  233. $flag->types = array($type);
  234. $flag->uid = $account->uid;
  235. flag_lists_set_messages($flag);
  236. // Save it.
  237. flag_lists_save($flag, $account);
  238. }
  239. }
  240. /**
  241. * Flag lists settings page.
  242. */
  243. function flag_lists_settings_form($form, $form_state) {
  244. drupal_set_title(t('Flag lists settings'));
  245. $form['text'] = array(
  246. '#title' => t('Using flag lists'),
  247. '#markup' => t('Flag lists allow users to create their own personal flags.
  248. No user can add to another user\'s lists. Lists inherit their
  249. settings from template flags, which exist as flags in the flags\' module.'
  250. ),
  251. );
  252. $form['flag_lists_name'] = array(
  253. '#type' => 'textfield',
  254. '#title' => t('Substitute "list" with your own terminology'),
  255. '#default_value' => variable_get('flag_lists_name', t('list')),
  256. '#description' => t('You can choose to use another name for "list", such as "favorites" or "bookmarks". Lowercase and plural names usually work best.'),
  257. '#required' => TRUE,
  258. );
  259. $form['rebuild'] = array(
  260. '#type' => 'fieldset',
  261. '#title' => t('Global rebuild'),
  262. '#description' => t('Changes to list templates and settings normally apply
  263. to only newly created flags. However, you can globally apply changes here.
  264. First adjust the settings above and the list templates and save them. Then
  265. click the link below. This will change ALL existing flag lists. It cannot
  266. be undone.'),
  267. '#tree' => FALSE,
  268. );
  269. $form['rebuild']['global_rebuild'] = array(
  270. '#markup' => l(t('Rebuild all flag lists.'), FLAG_ADMIN_PATH . '/flag-lists/rebuild'),
  271. );
  272. return system_settings_form($form);
  273. }
  274. /**
  275. * Confirm global settings rebuild.
  276. */
  277. function flag_lists_rebuild_confirm($form, $form_state) {
  278. return confirm_form($form,
  279. t('Are you sure you want to rebuild all flag lists?'),
  280. FLAG_ADMIN_PATH . '/lists/settings',
  281. t('This action cannot be undone.'),
  282. t('Rebuild'), t('Cancel')
  283. );
  284. }
  285. /**
  286. * Confirm global settings rebuild.
  287. */
  288. function flag_lists_rebuild_confirm_submit($form, $form_state) {
  289. flag_lists_rebuild();
  290. drupal_set_message(t('All flag lists have been rebuilt.'));
  291. drupal_goto(FLAG_ADMIN_PATH . '/lists/settings');
  292. }
  293. /**
  294. * Delete flag lists page.
  295. */
  296. function flag_lists_delete_confirm($form, $form_state, $name) {
  297. $flag = flag_lists_get_flag($name);
  298. if (empty($flag)) {
  299. drupal_goto();
  300. }
  301. $form['fid'] = array('#type' => 'value', '#value' => $flag->fid);
  302. return confirm_form($form,
  303. t('Are you sure you want to delete %title?', array('%title' => $flag->get_title())),
  304. isset($_GET['destination']) ? $_GET['destination'] : '/',
  305. t('This action cannot be undone.'),
  306. t('Delete'), t('Cancel')
  307. );
  308. }
  309. function flag_lists_delete_confirm_submit($form, &$form_state) {
  310. $flag = flag_lists_get_flag($form_state['values']['fid']);
  311. if ($form_state['values']['confirm']) {
  312. flag_lists_fl_delete($flag);
  313. }
  314. }
  315. /**
  316. * Form to create a new template.
  317. */
  318. function flag_lists_create_template_form($form, $form_state) {
  319. drupal_set_title(t('Add a new list template'));
  320. $form['help'] = array(
  321. '#value' => t('This form creates a new, blank list template. After saving, you will be able to configure further options.'),
  322. );
  323. $form['name'] = array(
  324. '#type' => 'textfield',
  325. '#title' => t('Template name'),
  326. '#description' => t('The machine-name for this template. It may be up to 32 characters long and my only contain lowercase letters, underscores, and numbers.'),
  327. '#maxlength' => 255,
  328. '#required' => TRUE,
  329. );
  330. $form['submit'] = array(
  331. '#type' => 'submit',
  332. '#value' => t('Save'),
  333. );
  334. return $form;
  335. }
  336. /**
  337. * New template validation.
  338. */
  339. function flag_lists_create_template_form_validate($form, &$form_state) {
  340. // Ensure a safe machine name.
  341. if (!preg_match('/^[a-z_][a-z0-9_]*$/', $form_state['values']['name'])) {
  342. form_set_error('name', t('The flag name may only contain lowercase letters, underscores, and numbers.'));
  343. }
  344. // Ensure 32 charactor or less name.
  345. if (drupal_strlen($form_state['values']['name']) > 32) {
  346. form_set_error('name', t('The flag name may only be 32 characters long.'));
  347. }
  348. // Ensure the machine name is unique.
  349. $flag = flag_get_flag('fl_template_' . $form_state['values']['name']);
  350. if ($flag) {
  351. form_set_error('name', t('Flag names must be unique. This flag name is already in use.'));
  352. }
  353. }
  354. /**
  355. * New template submit.
  356. */
  357. function flag_lists_create_template_form_submit($form, &$form_state) {
  358. $template = flag_lists_flag_default_flags('fl_template_' . $form_state['values']['name']);
  359. $flag = flag_flag::factory_by_array($template[0]);
  360. $flag->title = t('List template') . ' ' . $form_state['values']['name'];
  361. $flag->save();
  362. // Enter the new template into flag_lists_types.
  363. db_insert('flag_lists_types')
  364. ->fields(array(
  365. 'name' => $flag->name,
  366. ))
  367. ->execute();
  368. $form_state['redirect'] = FLAG_ADMIN_PATH . '/edit/' . $flag->name;
  369. }
  370. /**
  371. * Developer tool to generate dummy lists and listings.
  372. */
  373. // @todo use batch api.
  374. function flag_lists_generate_lists_form() {
  375. $templates = flag_lists_get_templates();
  376. $options = array();
  377. foreach ($templates as $template) {
  378. $options[$template->fid] = $template->name . ' (' . implode(', ', $template->types) . ')';
  379. }
  380. $form['templates'] = array(
  381. '#type' => 'checkboxes',
  382. '#title' => t('Generate lists and/or listings for the following templates.'),
  383. '#options' => $options,
  384. );
  385. $form['lists'] = array(
  386. '#type' => 'textfield',
  387. '#title' => t('How many lists should be generated?'),
  388. '#default_value' => '0',
  389. '#size' => 7,
  390. '#maxlength' => 7,
  391. );
  392. $form['listings'] = array(
  393. '#type' => 'textfield',
  394. '#title' => t('How many listings should be generated?'),
  395. '#default_value' => '0',
  396. );
  397. $form['kill_lists'] = array(
  398. '#type' => 'checkbox',
  399. '#title' => t('Delete ALL existing lists and listings before generating new ones? Templates will not be deleted.'),
  400. '#default_value' => FALSE,
  401. );
  402. $form['kill_listings'] = array(
  403. '#type' => 'checkbox',
  404. '#title' => t('Delete ALL existing listings before generating new ones? (Lists are not deleted unless the above is checked.)'),
  405. '#default_value' => FALSE,
  406. );
  407. $form['submit'] = array(
  408. '#type' => 'submit',
  409. '#value' => t('Do it!'),
  410. );
  411. return $form;
  412. }
  413. /**
  414. * Validation handler for flag_lists_generate_lists_form.
  415. */
  416. function flag_lists_generate_lists_form_validate($form, &$form_state) {
  417. if ($form_state['values']['lists'] && !is_numeric($form_state['values']['lists']) || $form_state['values']['lists'] < 0) {
  418. form_set_error('lists', t('Number of lists to generate must be a number, 1 or more.'));
  419. }
  420. if ($form_state['values']['listings'] && !is_numeric($form_state['values']['listings']) || $form_state['values']['listings'] < 0) {
  421. form_set_error('listings', t('Number of listings to generate must be a number, 1 or more.'));
  422. }
  423. if ($form_state['values']['lists'] > 0 && !count(array_filter($form_state['values']['templates']))) {
  424. form_set_error('templates', t('You must select at least 1 template for the lists to be genereated'));
  425. }
  426. if ($form_state['values']['listings'] > 0 && !count(array_filter($form_state['values']['templates']))) {
  427. form_set_error('templates', t('You must select at least 1 template for the listings to be genereated'));
  428. }
  429. }
  430. /**
  431. * Submit handler for flag_lists_generate_lists_form.
  432. */
  433. function flag_lists_generate_lists_form_submit($form, &$form_state) {
  434. global $user;
  435. module_load_include('inc', 'devel_generate');
  436. // Delete listings.
  437. if ($form_state['values']['kill_listings']) {
  438. $flags = flag_lists_get_flags();
  439. foreach ($flags as $flag) {
  440. $result = db_select('flag_lists_content', 'f')
  441. ->fields('f', array('fcid', 'content_id'))
  442. ->condition('fid', $flag->fid)
  443. ->execute();
  444. foreach ($result as $row) {
  445. db_delete('flag_lists_content')->condition('fid', $flag->fid)->execute();
  446. db_delete('flag_lists_counts')->condition('fid', $flag->fid)->execute();
  447. module_invoke_all('flag', 'unflag', $flag, $row->content_id, $account, $row->fcid);
  448. }
  449. }
  450. drupal_set_message(t('All listings were deleted.'));
  451. }
  452. // Delete lists and listings.
  453. if ($form_state['values']['kill_lists']) {
  454. // If we loaded all flags above, don't reload them here.
  455. if (!count($flags)) {
  456. $flags = flag_lists_get_flags();
  457. }
  458. foreach ($flags as $flag) {
  459. flag_lists_fl_delete($flag, $user);
  460. }
  461. drupal_set_message(t('All lists and their listings were deleted.'));
  462. }
  463. $templates = array_filter($form_state['values']['templates']);
  464. $uids = array_filter(devel_get_users()); // Don't use the anon user.
  465. // Generate lists.
  466. if ($form_state['values']['lists'] && count($templates)) {
  467. for ($i = 1; $i <= $form_state['values']['lists']; $i++ ) {
  468. $template = flag_get_flag(NULL, array_rand($templates));
  469. $account->uid = $uids[array_rand($uids)];
  470. $edit['values']['title'] = devel_create_greeking(7, TRUE);
  471. $edit['values']['type'] = $template->types[array_rand($template->types)];
  472. $form = array();
  473. flag_lists_form_submit($form, $edit, $account);
  474. }
  475. drupal_set_message(t('@lists created.', array('@lists' => format_plural($form_state['values']['lists'], '1 list', '@count lists'))));
  476. }
  477. // Generate listings.
  478. if ($form_state['values']['listings']) {
  479. $count = 0;
  480. for ($i = 1; $i <= $form_state['values']['listings']; $i++ ) {
  481. $account->uid = $uids[array_rand($uids)];
  482. $lists = flag_lists_get_user_flags(NULL, $account);
  483. // Remove any lists that don't use the chosen templates.
  484. foreach ($lists as $key => $list) {
  485. if (!isset($templates[$list->pfid])) {
  486. unset($lists[$key]);
  487. }
  488. }
  489. // Ensure user has lists.
  490. if (!count($lists)) {
  491. continue;
  492. }
  493. $list = $lists[array_rand($lists)];
  494. $content_type = $list->types[array_rand($list->types)];
  495. // We get the random nid with 2 SELECTS to avoid slow ORDER BY Rand().
  496. // Get max nid for our content type.
  497. $query = db_select('node')
  498. ->condition('type', $content_type);
  499. $query->addExpression('MAX(nid)', 'max');
  500. $max_nid = $query->execute()->fetchField();
  501. // Ensure a node exists for the type.
  502. if (!$max_nid) {
  503. continue;
  504. }
  505. $r = rand(1, $max_nid);
  506. $content_id = db_select('node', 'n')
  507. ->fields('n', array('nid'))
  508. ->condition('type', $content_type)
  509. ->condition('nid', $r)
  510. ->range(0, 1)
  511. ->execute()
  512. ->fetchField();
  513. // We can't assume that every attempt will result in a listing because
  514. // we may have a node type with no nodes yet or a user with no lists.
  515. // Also, if a listing already exists, we count it, but it can only be in
  516. // the db once.
  517. if (flag_lists_do_flag($list, 'flag', $content_id, $account, TRUE)) {
  518. $count++;
  519. }
  520. }
  521. drupal_set_message(t('@listings listed.', array('@listings' => format_plural($count, '1 node', '@count nodes'))));
  522. if ($count < $form_state['values']['listings']) {
  523. drupal_set_message(t('Listings are generated randomly. Occassionally a listing will not be created if the random user has no lists or if the node type to be listed has no nodes.'));
  524. }
  525. }
  526. }