flag_lists.module 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785
  1. <?php
  2. module_load_include('inc', 'flag', 'includes/flag.admin');
  3. module_load_include('inc', 'flag', 'flag');
  4. /**
  5. * @file
  6. * The Flag Lists module.
  7. *
  8. * Extends flag to allow individual users to create personal flags.
  9. */
  10. /**
  11. * Implementation of hook_menu().
  12. */
  13. function flag_lists_menu() {
  14. $items = array();
  15. $items[FLAG_ADMIN_PATH . '/lists'] = array(
  16. 'title' => 'Lists',
  17. 'page callback' => 'drupal_get_form',
  18. 'page arguments' => array('flag_lists_settings_form'),
  19. 'access callback' => 'user_access',
  20. 'access arguments' => array('administer flags'),
  21. 'description' => 'Configure default settings allowing users to mark content with personal flags.',
  22. 'file' => 'flag_lists.admin.inc',
  23. 'type' => MENU_LOCAL_TASK,
  24. 'weight' => 100,
  25. );
  26. $items[FLAG_ADMIN_PATH . '/lists/settings'] = array(
  27. 'title' => 'Settings',
  28. 'page callback' => 'drupal_get_form',
  29. 'page arguments' => array('flag_lists_settings_form'),
  30. 'access callback' => 'user_access',
  31. 'access arguments' => array('administer flags'),
  32. 'description' => 'Configure default settings allowing users to mark content with personal flags.',
  33. 'file' => 'flag_lists.admin.inc',
  34. 'type' => MENU_DEFAULT_LOCAL_TASK,
  35. 'weight' => 1,
  36. );
  37. $items[FLAG_ADMIN_PATH . '/lists/list'] = array(
  38. 'title' => 'List',
  39. 'page callback' => 'flag_lists_admin_page',
  40. 'access callback' => 'user_access',
  41. 'access arguments' => array('administer flags'),
  42. 'file' => 'flag_lists.admin.inc',
  43. 'type' => MENU_LOCAL_TASK,
  44. 'weight' => 2,
  45. );
  46. $items[FLAG_ADMIN_PATH . '/lists/template'] = array(
  47. 'title' => 'New template',
  48. 'page callback' => 'drupal_get_form',
  49. 'page arguments' => array('flag_lists_create_template_form'),
  50. 'access callback' => 'user_access',
  51. 'access arguments' => array('administer flags'),
  52. 'file' => 'flag_lists.admin.inc',
  53. 'type' => MENU_LOCAL_TASK,
  54. 'weight' => 3,
  55. );
  56. $items[FLAG_ADMIN_PATH . '/lists/clean'] = array(
  57. 'title' => 'Clean lists from deleted entities',
  58. 'page callback' => 'flag_list_clean_deleted',
  59. 'access callback' => 'user_access',
  60. 'access arguments' => array('administer flags'),
  61. 'file' => 'flag_lists.admin.inc',
  62. 'type' => MENU_LOCAL_TASK,
  63. 'weight' => 4,
  64. );
  65. if (module_exists('devel_generate')) {
  66. $items['admin/config/development/generate/flag-lists'] = array(
  67. 'title' => 'Generate lists',
  68. 'description' => 'Generate a given number of lists and listings on site content. Optionally delete existing lists and listings.',
  69. 'page callback' => 'drupal_get_form',
  70. 'page arguments' => array('flag_lists_generate_lists_form'),
  71. 'access callback' => 'user_access',
  72. 'access arguments' => array('administer flags'),
  73. 'file' => 'flag_lists.admin.inc',
  74. );
  75. }
  76. $items['flag-lists/add/%'] = array(
  77. 'title' => 'Add a list',
  78. 'page callback' => 'flag_lists_add',
  79. 'page arguments' => array(2),
  80. 'access callback' => 'user_access',
  81. 'access arguments' => array('create flag lists'),
  82. 'file' => 'flag_lists.admin.inc',
  83. 'type' => MENU_NORMAL_ITEM,
  84. );
  85. // Callback for adding a new list through JS
  86. $items['flag-lists/add/%/js'] = array(
  87. 'title' => 'Add a list',
  88. 'page callback' => 'flag_lists_add_js',
  89. 'page arguments' => array(2),
  90. 'access callback' => 'user_access',
  91. 'access arguments' => array('create flag lists'),
  92. 'file' => 'flag_lists.admin.inc',
  93. 'type' => MENU_CALLBACK,
  94. );
  95. $items['flags/lists/edit/%'] = array(
  96. 'title' => 'Edit a list',
  97. 'page callback' => 'drupal_get_form',
  98. 'page arguments' => array('flag_lists_form', 3),
  99. 'access callback' => 'flag_lists_is_owner',
  100. 'access arguments' => array(2, 3),
  101. 'file' => 'flag_lists.admin.inc',
  102. 'type' => MENU_CALLBACK,
  103. );
  104. $items['flags/lists/delete/%'] = array(
  105. 'title' => 'Delete a list',
  106. 'page callback' => 'drupal_get_form',
  107. 'page arguments' => array('flag_lists_delete_confirm', 3),
  108. 'access callback' => 'flag_lists_is_owner',
  109. 'access arguments' => array(2, 3),
  110. 'file' => 'flag_lists.admin.inc',
  111. 'type' => MENU_CALLBACK,
  112. );
  113. $items[FLAG_ADMIN_PATH . '/flag-lists/rebuild'] = array(
  114. 'title' => 'Rebuild all flag lists',
  115. 'page callback' => 'drupal_get_form',
  116. 'page arguments' => array('flag_lists_rebuild_confirm'),
  117. 'access callback' => 'user_access',
  118. 'access arguments' => array('administer flag lists'),
  119. 'file' => 'flag_lists.admin.inc',
  120. 'type' => MENU_CALLBACK,
  121. );
  122. $items['flag-lists'] = array(
  123. 'title' => 'Flag',
  124. 'page callback' => 'flag_lists_page',
  125. 'access callback' => 'user_access',
  126. 'access arguments' => array('access content'),
  127. 'type' => MENU_CALLBACK,
  128. );
  129. $items['user/%user/flags/lists'] = array(
  130. 'title' => ucfirst(variable_get('flag_lists_name', 'list')),
  131. 'page callback' => 'flag_lists_user_page',
  132. 'page arguments' => array(1),
  133. 'access callback' => 'user_access',
  134. 'access arguments' => array('view flag lists'),
  135. 'type' => MENU_LOCAL_TASK,
  136. );
  137. $items['user/%user/flags/lists/%'] = array(
  138. 'title' => ucfirst(variable_get('flag_lists_name', 'list')) . ' content',
  139. 'page callback' => 'flag_lists_user_list',
  140. 'page arguments' => array(1, 4),
  141. 'access callback' => 'user_access',
  142. 'access arguments' => array('view flag lists'),
  143. 'type' => MENU_CALLBACK,
  144. );
  145. return $items;
  146. }
  147. /**
  148. * User flag page. Display a list of user-created flag lists.
  149. */
  150. function flag_lists_user_page($user) {
  151. // Can we use our default view?
  152. if (module_exists('views')) {
  153. $view = views_get_view('flag_lists_user_lists', FALSE);
  154. if (!empty($view)) {
  155. $view->set_display('default');
  156. $view->set_arguments(array($user->uid));
  157. $output = $view->render();
  158. drupal_set_title(str_replace(array_keys($view->build_info['substitutions']), $view->build_info['substitutions'], $view->build_info['title']));
  159. }
  160. return $output;
  161. }
  162. else {
  163. return theme('flag_lists_user_page', array('uid' => $user->uid));
  164. }
  165. }
  166. /**
  167. * Theme the output for a user flag administration page.
  168. */
  169. function theme_flag_lists_user_page($variables) {
  170. $uid = $variables['uid'];
  171. $account = user_load($uid);
  172. drupal_set_title(t('Lists'));
  173. if ($flags = flag_lists_get_user_flags(NULL, $account)) {
  174. // Build the list of flag lists for this node.
  175. foreach ($flags as $flag) {
  176. $ops = theme('flag_lists_ops', array('flag' => $flag));
  177. $items[] = l($flag->title, "user/$uid/flags/lists/" . $flag->fid) . $ops;
  178. }
  179. }
  180. drupal_add_css(drupal_get_path('module', 'flag_lists') . '/theme/flag_lists.css');
  181. return theme('item_list', array('items' => $items));
  182. }
  183. /**
  184. * List the contents of a user-defined list
  185. */
  186. function flag_lists_user_list($user, $fid) {
  187. $uid = $user->uid;
  188. // Can we use our default view?
  189. if (module_exists('views')) {
  190. $view = views_get_view('flag_lists_user_list', FALSE);
  191. if (!empty($view)) {
  192. $view->set_display('default');
  193. $view->set_arguments(array($fid));
  194. $output = $view->render();
  195. drupal_set_title(str_replace(array_keys($view->build_info['substitutions']), $view->build_info['substitutions'], $view->build_info['title']));
  196. }
  197. return $output;
  198. }
  199. else {
  200. return theme('flag_lists_user_list', array('uid' => $uid, 'fid' => $fid));
  201. }
  202. }
  203. /**
  204. * Theme the output of user-defined list page
  205. */
  206. function theme_flag_lists_user_list($variables) {
  207. $uid = $variables['uid'];
  208. $fid = $variables['fid'];
  209. $flag = flag_lists_get_flag($fid);
  210. drupal_set_title($flag->title);
  211. $content = flag_lists_get_flagged_content($fid, $uid);
  212. foreach ($content as $item) {
  213. if ($item->content_type == 'node') {
  214. $node = node_load($item->entity_id);
  215. $items[] = l($node->title, 'node/' . $node->nid);
  216. }
  217. }
  218. $breadcrumb = menu_get_active_breadcrumb();
  219. $breadcrumb[] = l(t('@name lists', array('@name' => drupal_ucfirst(variable_get('flag_lists_name', t('lists'))))), 'user/' . arg(1) . '/flags/lists');
  220. drupal_set_breadcrumb($breadcrumb);
  221. return theme('item_list', array('items' => $items));
  222. }
  223. /**
  224. * Implementation of hook_theme().
  225. */
  226. function flag_lists_theme() {
  227. $path = drupal_get_path('module', 'flag') . '/theme';
  228. return array(
  229. 'flag_lists_list' => array(
  230. 'variables' => array('node' => NULL, 'create' => NULL, 'ops' => NULL, 'use_flags' => NULL),
  231. ),
  232. 'flag_lists_admin_page' => array(
  233. 'variables' => array('flags' => NULL),
  234. ),
  235. 'flag_lists_user_page' => array(
  236. 'variables' => array('uid' => NULL),
  237. ),
  238. 'flag_lists_user_list' => array(
  239. 'variables' => array('flag_name' => NULL),
  240. ),
  241. 'flag_lists_ops' => array(
  242. 'variables' => array('flag' => NULL),
  243. )
  244. );
  245. }
  246. /**
  247. * Implementation of hook_permission().
  248. */
  249. function flag_lists_permission() {
  250. return array(
  251. 'create flag lists' => array(
  252. 'title' => t('Create flag lists'),
  253. 'description' => t(''),
  254. ),
  255. 'edit own flag lists' => array(
  256. 'title' => t('Edit own flag lists'),
  257. 'description' => t(''),
  258. ),
  259. 'delete own flag lists' => array(
  260. 'title' => t('Delete own flag lists'),
  261. 'description' => t(''),
  262. ),
  263. 'view flag lists' => array(
  264. 'title' => t('View flag lists'),
  265. 'description' => t(''),
  266. ));
  267. }
  268. /**
  269. * Implementation of hook_form_alter().
  270. */
  271. function flag_lists_form_alter(&$form, &$form_state, $form_id) {
  272. switch ($form_id) {
  273. case 'flag_form':
  274. // A template flag should always have a record in the flag_lists_types table.
  275. $result = db_select('flag_lists_types', 'f')->fields('f')->execute();
  276. foreach ($result as $type) {
  277. $types[$type->name] = $type->type;
  278. }
  279. if (isset($types[$form['name']['#default_value']])) {
  280. $form['name']['#type'] = 'value';
  281. $form['global']['#type'] = 'value';
  282. $form['title']['#description'] = t('A short, descriptive title for this template. It will be used in administrative interfaces to refer to this template.');
  283. // Warn about types that already have a template.
  284. foreach ($form['access']['types']['#options'] as $option => $value) {
  285. if (in_array($option, $types) && $form['access']['types']['#default_value'] != $option) {
  286. $form['access']['types']['#options'][$option] .= '<span class="description">' . t('(Already has a template.)') . '</span>';
  287. }
  288. }
  289. $form['access']['types']['#description'] .= t('A type may only be selected in one list template.');
  290. // Unset anon permissions for now. @todo allow anon listing.
  291. unset($form['access']['roles']['flag']['#options'][1]);
  292. unset($form['access']['roles']['unflag']['#options'][1]);
  293. foreach (element_children($form['display']) as $display) {
  294. $form['display'][$display]['#type'] = 'value';
  295. }
  296. $form['display']['link_type']['#default_value'] = 'fl_template';
  297. $form['display']['#description'] = t('Unlike normal flags, lists are only displayed in a block provided by this module or in views blocks. See <a href="/admin/build/block/list">the block admin page</a> to place the block.');
  298. $form['#validate'][] = 'flag_lists_template_validate';
  299. $form['#submit'][] = 'flag_lists_template_submit';
  300. }
  301. break;
  302. case 'views_exposed_form':
  303. // Force the exposed filters to perform actions on the page itself because
  304. // the views default viwe didn't come with a page display
  305. if ($form['#id'] == 'views-exposed-form-flag-lists-default') {
  306. $form['#action'] = '/' . implode('/', arg());
  307. }
  308. break;
  309. }
  310. // Flag lists operations related changes
  311. if (strpos($form_id, 'views_form_') === 0) {
  312. $flo = _flag_lists_ops_get_field($form_state['build_info']['args'][0]);
  313. // Not a FLO-enabled views form.
  314. if (empty($flo)) {
  315. return;
  316. }
  317. // Add FLO's custom callbacks.
  318. $form['#validate'][] = 'flag_lists_ops_form_validate';
  319. $form['#submit'][] = 'flag_lists_ops_form_submit';
  320. // Allow FLO to work when embedded using views_embed_view(), or in a block.
  321. if (empty($flo->view->override_path)) {
  322. if (!empty($flo->view->preview) || $flo->view->display_handler instanceof views_plugin_display_block) {
  323. $flo->view->override_path = $_GET['q'];
  324. }
  325. }
  326. // Quickfix for FLO & exposed filters using ajax. See http://drupal.org/node/1191928.
  327. $query = drupal_get_query_parameters($_GET, array('q'));
  328. $form['#action'] = url($flo->view->get_url(), array('query' => $query));
  329. // Add basic FLO functionality.
  330. if ($form_state['step'] == 'views_form_views_form') {
  331. $form = flag_lists_ops_form($form, $form_state, $flo);
  332. }
  333. }
  334. }
  335. /**
  336. * Add the Flag list select menu widget.
  337. */
  338. function flag_lists_ops_form($form, &$form_state, $flo) {
  339. $form['#attached']['js'][] = drupal_get_path('module', 'flag_lists') . '/js/flag_lists_ops.js';
  340. $form['#attached']['css'][] = drupal_get_path('module', 'flag_lists') . '/css/flag_lists_ops.css';
  341. $form['#prefix'] = '<div class="flo-views-form">';
  342. $form['#suffix'] = '</div>';
  343. $form_state['flo_operation'] = $flo->options['flo']['operation'];
  344. // Force browser to reload the page if Back is hit.
  345. if (preg_match('/msie/i', $_SERVER['HTTP_USER_AGENT'])) {
  346. drupal_add_http_header('Cache-Control', 'no-cache'); // works for IE6+
  347. }
  348. else {
  349. drupal_add_http_header('Cache-Control', 'no-store'); // works for Firefox and other browsers
  350. }
  351. global $user;
  352. global $base_url;
  353. $account = user_load($user->uid);
  354. $items = array();
  355. if ($flags = flag_lists_get_user_flags(NULL, $account)) {
  356. // Build the list of flag lists for this node.
  357. foreach ($flags as $flag) {
  358. $items[((string)$flag->fid)] = $flag->title;
  359. }
  360. }
  361. // Group items into a fieldset for easier theming.
  362. $form['flag_lists_' . $form_state['flo_operation']] = array(
  363. '#type' => 'fieldset',
  364. '#title' => t('List operations'),
  365. '#tree' => TRUE,
  366. '#attributes' => array('class' => array('flag-lists-ops-fieldset')),
  367. );
  368. switch ($flo->options['flo']['operation']) {
  369. case 'unflag':
  370. $null_text = 'Remove from a list';
  371. $operation = 'unflag';
  372. $form['flag_lists_' . $form_state['flo_operation']]['list'] = array(
  373. '#type' => 'button',
  374. '#value' => t('Remove from list'),
  375. '#ajax' => array(
  376. 'callback' => 'flag_lists_ops_form_ajax_callback',
  377. 'wrapper' => 'flag-list-ops-container-' . $flo->options['flo']['operation'],
  378. ),
  379. );
  380. break;
  381. default:
  382. $null_text = 'Add to a list';
  383. $operation = 'flag';
  384. drupal_add_library('system', 'ui.dialog');
  385. $form['flag_lists_' . $form_state['flo_operation']]['list'] = array(
  386. '#type' => 'select',
  387. '#options' => array('0' => t('- ' . $null_text . ' -')) + $items,
  388. '#default_value' => '0',
  389. '#ajax' => array(
  390. 'callback' => 'flag_lists_ops_form_ajax_callback',
  391. 'wrapper' => 'flag-list-ops-container-' . $flo->options['flo']['operation'],
  392. ),
  393. '#attributes' => array(
  394. 'class' => array(
  395. 'flag-lists-ops-dropdown',
  396. ),
  397. ),
  398. );
  399. // Add the necessary JS for creating a new list via AJAX
  400. $result = db_select('flag_lists_types')
  401. ->fields('flag_lists_types')
  402. ->execute();
  403. $list_types = array();
  404. foreach ($result as $row) {
  405. if (!empty($row->type)) {
  406. $list_types[] = $row->type;
  407. }
  408. }
  409. drupal_add_js(array('flag_lists' => array('types' => $list_types, 'json_path' => $base_url . '/flag-lists/add/%/js')), 'setting');
  410. break;
  411. }
  412. // Get the $ops from the originating form.
  413. if (!empty($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) {
  414. $list = $form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'];
  415. }
  416. if (!empty($_REQUEST['flag_lists_ops'])) {
  417. $ops = $_REQUEST['flag_lists_ops'];
  418. }
  419. if (!empty($ops) && !empty($list) && $form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation'] == 'unflag') {
  420. $hidden_deleted_values = '';
  421. foreach ($ops as $nid) {
  422. $hidden_deleted_values .= '<input type="hidden" class="flo-deleted-value" value="' . $nid . '" />';
  423. }
  424. }
  425. $form['flag_lists_' . $form_state['flo_operation']]['operation'] = array(
  426. '#type' => 'hidden',
  427. '#value' => $operation,
  428. );
  429. $form['flag_lists_' . $form_state['flo_operation']]['go'] = array(
  430. '#type' => 'submit',
  431. '#value' => t('Go'),
  432. '#attributes' => array(
  433. 'class' => array('flag-lists-ops-go'),
  434. ),
  435. );
  436. unset($form['actions']['submit']);
  437. // Generate a status message for AJAX submission.
  438. $form['flag_lists_' . $form_state['flo_operation']]['status_message'] = array('#markup' => '');
  439. $form['flag_lists_' . $form_state['flo_operation']]['#prefix'] = '<div id="flag-list-ops-container-' . $flo->options['flo']['operation'] . '">';
  440. $form['flag_lists_' . $form_state['flo_operation']]['#suffix'] = (!empty($hidden_deleted_values)) ? $hidden_deleted_values : '' . '</div>';
  441. return $form;
  442. }
  443. function flag_lists_ops_form_ajax_callback($form, $form_state) {
  444. // The form has already been submitted and updated. We can return the replaced
  445. // item as it is.
  446. if (!flag_lists_ops_form_validate($form, $form_state)) {
  447. $message = flag_lists_ops_form_submit($form, $form_state);
  448. }
  449. $form['flag_lists_' . $form_state['flo_operation']]['status_message']['#markup'] = '<div class="alert alert-success">' . $message . '</div>';
  450. if ($form_state['flo_operation'] == 'flag') $form['flag_lists_' . $form_state['flo_operation']]['list']['#value'] = '0';
  451. $form['flag_lists_' . $form_state['flo_operation']]['status_message']['#attributes']['class'][] = 'alert-success';
  452. return $form['flag_lists_' . $form_state['flo_operation']];
  453. }
  454. function flag_lists_ops_form_validate(&$form, &$form_state) {
  455. global $user;
  456. $error_count = 0;
  457. // Check to see if an items are selected, if not fail right away.
  458. if (!isset($_REQUEST['flag_lists_ops']) || empty($_REQUEST['flag_lists_ops'])) {
  459. form_set_error('', t('No content selected.'));
  460. $error_count++;
  461. return $error_count;
  462. }
  463. switch ($form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation']) {
  464. case 'unflag':
  465. $ops = $_REQUEST['flag_lists_ops'];
  466. foreach ($ops as $key => $op) {
  467. $ops[$key] = explode('-', $op);
  468. if (empty($ops[$key][1]) || !($flag = flag_lists_get_flag($ops[$key][1]))) {
  469. form_set_error('flag_lists][remove', t('Invalid options list selected to remove from.'));
  470. $error_count++;
  471. }
  472. else if ($flag->uid != $user->uid) {
  473. form_set_error('flag_lists][remove', t('You are only allowed to remove content from your own lists.'));
  474. $error_count++;
  475. }
  476. }
  477. break;
  478. default:
  479. if (empty($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) {
  480. form_set_error('flag_lists][list', t('No list selected. Please select a list to add to.'));
  481. $error_count++;
  482. }
  483. else if (!($flag = flag_lists_get_flag($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list']))) {
  484. form_set_error('flag_lists][list', t('Invalid list selected. Please select a list to add to.'));
  485. $error_count++;
  486. }
  487. else if ($flag->uid != $user->uid) {
  488. form_set_error('flag_lists][list', t('Invalid list selected. Please select a list to add to.'));
  489. $error_count++;
  490. }
  491. break;
  492. }
  493. return $error_count;
  494. }
  495. function flag_lists_ops_form_submit(&$form, &$form_state) {
  496. // Get the $ops from the originating form.
  497. $ops = $_REQUEST['flag_lists_ops'];
  498. $success_count = 0;
  499. // Get the operation, or set it
  500. switch ($form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation']) {
  501. case 'unflag':
  502. $operation = 'unflag';
  503. $message = 'removed from';
  504. foreach ($ops as $op) {
  505. // Process the ID into 2 parts
  506. list($nid, $fid) = explode('-', $op);
  507. if (empty($nid) || empty($fid)) return;
  508. if (($flag = flag_lists_get_flag($fid)) && ($node = node_load($nid))) {
  509. if (flag_lists_do_flag($flag, $operation, $nid)) {
  510. $success_count++;
  511. }
  512. }
  513. }
  514. break;
  515. default:
  516. $operation = 'flag';
  517. $message = 'added to';
  518. if ($flag = flag_lists_get_flag($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) {
  519. foreach ($ops as $nid) {
  520. if (flag_lists_do_flag($flag, $operation, $nid)) {
  521. $success_count++;
  522. }
  523. }
  524. }
  525. break;
  526. }
  527. $message = t('@count item(s) ' . $message . ' !title', array('@count' => $success_count, '!title' => $flag->title));
  528. if ($_GET['q'] != 'system/ajax') {
  529. drupal_set_message($message);
  530. }
  531. else {
  532. return $message;
  533. }
  534. }
  535. /**
  536. * Gets the FLO field if it exists on the passed-in view.
  537. *
  538. * @return
  539. * The field object if found. Otherwise, FALSE.
  540. */
  541. function _flag_lists_ops_get_field($view) {
  542. foreach ($view->field as $field_name => $field) {
  543. if ($field instanceof flag_lists_handler_field_ops) {
  544. // Add in the view object for convenience.
  545. $field->view = $view;
  546. return $field;
  547. }
  548. }
  549. return FALSE;
  550. }
  551. function flag_lists_template_validate($form, &$form_state) {
  552. $types = array_filter($form_state['values']['types']);
  553. $errors = array();
  554. foreach ($types as $type) {
  555. $result = db_select('flag_lists_types', 'f')
  556. ->fields('f')
  557. ->condition('type', $type)
  558. ->condition('name', $form_state['values']['name'], '<>')
  559. ->execute();
  560. foreach ($result as $errors) {
  561. $content_types[] = $errors->type;
  562. $templates[] = $errors->name;
  563. }
  564. }
  565. if (isset($content_types) && count($content_types)) {
  566. $content_types = implode(', ', $content_types);
  567. $templates = implode(', ', array_unique($templates));
  568. form_set_error('types', t('The flaggable content type(s) "@type" is(are) already assigned to the template(s) "@template." A content type may be assigned to only one template. To reassign a content type you must first remove its other assignment.', array('@type' => $content_types, '@template' => $templates)));
  569. }
  570. }
  571. function flag_lists_template_submit($form, &$form_state) {
  572. $types = array_filter($form_state['values']['types']);
  573. // Clean out the old types, then add the new.
  574. $num_deleted = db_delete('flag_lists_types')
  575. ->condition('name', $form_state['values']['name'])
  576. ->execute();
  577. foreach ($types as $type) {
  578. db_insert('flag_lists_types')
  579. ->fields(array(
  580. 'name' => $form_state['values']['name'],
  581. 'type' => $type,
  582. ))
  583. ->execute();
  584. }
  585. }
  586. /**
  587. * Helper function to build an array of all lists available to or owned by the
  588. * current user and that are available on the current content type.
  589. */
  590. function flag_lists_get_content_fids() {
  591. global $user;
  592. // This is a node view. We only care about nodes for now.
  593. if (arg(0) == 'node' && is_numeric(arg(1)) && is_null(arg(2))) {
  594. $type = db_select('node', 'n')
  595. ->fields('n', array('type'))
  596. ->condition('nid', arg(1))
  597. ->execute()
  598. ->fetchField();
  599. // Get current user's flags for this node.
  600. $query = db_select('flag_lists', 'fc')
  601. ->fields('f', 'fid')
  602. ->condition('fc.uid', $user->uid)
  603. ->condition('fn.type', $type);
  604. $query->leftJoin('flag_types', 'fn', 'fn.fid = fc.fid');
  605. $query->leftJoin('flags', 'f', 'fc.fid = f.fid');
  606. $fc_result = $query->execute();
  607. foreach ($fc_result as $row) {
  608. $fids[] = $row->fid;
  609. }
  610. }
  611. // This is the flag / unflag callback
  612. elseif (arg(0) == 'flag' && (arg(1) == 'flag' || arg(1) == 'unflag')) {
  613. // Get the flag for this request.
  614. $fids[] = db_select('flags', 'f')
  615. ->fields('f', array('fid'))
  616. ->condition('name', arg(2))
  617. ->execute()
  618. ->fetchField();
  619. }
  620. // Get the regular flags for this node. The flag module will narrow by role,
  621. // etc. when flag_get_flags() is called. These flag ids are always returned.
  622. $query = db_select('flags', 'f')
  623. ->fields('f', array('fid'))
  624. ->condition('fc.fid', NULL);
  625. $query->leftJoin('flag_lists', 'fc', 'fc.fid = f.fid');
  626. $f_result = $query->execute();
  627. foreach ($f_result as $obj) {
  628. $fids[] = $obj->fid;
  629. }
  630. if (is_array($fids)) {
  631. return array_unique($fids);
  632. }
  633. else {
  634. return array();
  635. }
  636. }
  637. /**
  638. * Implements hook_block_info();
  639. */
  640. function flag_lists_block_info() {
  641. return array(
  642. 'flag_lists' => array(
  643. 'info' => t('Lists'),
  644. 'cache' => DRUPAL_NO_CACHE,
  645. ),
  646. 'flag_lists_list' => array(
  647. 'info' => t('My lists'),
  648. 'cache' => DRUPAL_NO_CACHE,
  649. ),
  650. );
  651. }
  652. /**
  653. * Implements hook_block_configure().
  654. */
  655. function flag_lists_block_configure($delta = '') {
  656. $form = array();
  657. switch ($delta) {
  658. case 'flag_lists':
  659. $form = array(
  660. 'create_lists' => array(
  661. '#type' => 'checkbox',
  662. '#title' => t('Show link to add new list'),
  663. '#default_value' => variable_get('flag_lists_create_lists', 0),
  664. '#description' => t('Checking this adds a link to the create new list form.'),
  665. ),
  666. 'ops' => array(
  667. '#type' => 'checkbox',
  668. '#title' => t('Show edit and delete links'),
  669. '#default_value' => variable_get('flag_lists_ops', 0),
  670. '#description' => t('Checking this appends edit and delete links to each list name for users with access.'),
  671. ),
  672. 'include_flags' => array(
  673. '#type' => 'checkbox',
  674. '#title' => t('Include flag module flags'),
  675. '#default_value' => variable_get('flag_lists_include_flags', 0),
  676. '#description' => t('Checking this will append flag module flags to the list of lists.'),
  677. ),
  678. );
  679. break;
  680. }
  681. return $form;
  682. }
  683. /**
  684. * Implements hook_block_save().
  685. */
  686. function flag_lists_block_save($delta = '', $edit = array()) {
  687. switch ($delta) {
  688. case 'flag_lists':
  689. variable_set('flag_lists_create_lists', $edit['create_lists']);
  690. variable_set('flag_lists_ops', $edit['ops']);
  691. variable_set('flag_lists_include_flags', $edit['include_flags']);
  692. break;
  693. }
  694. }
  695. /**
  696. * Implements hook_block_view().
  697. */
  698. function flag_lists_block_view($delta = '') {
  699. $block = array();
  700. switch ($delta) {
  701. case 'flag_lists':
  702. if (user_access('create flag lists')) {
  703. $block = array(
  704. 'subject' => t('My lists'),
  705. 'content' => theme('flag_lists_list', array('node' => NULL, 'create' => variable_get('flag_lists_create_lists', 0), 'ops' =>variable_get('flag_lists_ops', 0), 'use_flags' => variable_get('flag_lists_include_flags', 0))),
  706. );
  707. }
  708. break;
  709. case 'flag_lists_list':
  710. if (user_access('create flag lists')) {
  711. global $user;
  712. $account = user_load($user->uid);
  713. $block = array(
  714. 'subject' => t('My lists'),
  715. 'content' => flag_lists_user_page($account),
  716. );
  717. }
  718. break;
  719. }
  720. return (!empty($block['content'])) ? $block : array();
  721. }
  722. /**
  723. * Implementation of hook_user_delete().
  724. */
  725. function flag_lists_user_delete($account) {
  726. // Remove personal flags by this user.
  727. $num_deleted = db_delete('flag_lists_flags')
  728. ->condition('uid', $account->uid)
  729. ->execute();
  730. }
  731. /**
  732. * Build a flag's messages.
  733. */
  734. function flag_lists_set_messages(&$flag) {
  735. // Get the parent flag. These are cached by the flag module.
  736. $pflag = flag_get_flag(NULL, $flag->pfid);
  737. $title = $flag->title;
  738. $lists_name = variable_get('flag_lists_name', t('list'));
  739. $flag->flag_short = $pflag->flag_short;
  740. $flag->flag_long = $pflag->flag_long;
  741. $flag->flag_message = $pflag->flag_message;
  742. $flag->unflag_short = $pflag->unflag_short;
  743. $flag->unflag_long = $pflag->unflag_long;
  744. $flag->unflag_message = $pflag->unflag_message;
  745. }
  746. /**
  747. * Implementation of hook_flag_access().
  748. *
  749. * Make sure a user can only see his/her own personal flags.
  750. */
  751. function flag_lists_flag_access($flag, $entity_id, $action, $account) {
  752. if (!empty($flag->module) && $flag->module == 'flag_lists') {
  753. switch ($action) {
  754. case 'flag':
  755. case 'unflag':
  756. $fid = db_select('flag_lists_flags', 'f')
  757. ->fields('f')
  758. ->condition('f.uid', $account->uid)
  759. ->execute()
  760. ->fetchField();
  761. if (!empty($fid)) {
  762. return array('flag_lists' => TRUE);
  763. }
  764. else {
  765. return array('flag_lists' => FALSE);
  766. }
  767. }
  768. }
  769. }
  770. /**
  771. * Implementation of hook_link().
  772. */
  773. // There may be a better way to keep flag lists out of the links, but this
  774. // works for now. @todo Find a better way to keep flags lists out of links.
  775. function flag_lists_link_alter(&$links, $node) {
  776. if (!variable_get('flag_lists_use_links', 1)) {
  777. foreach ($links as $name => $link) {
  778. if (stristr($name, 'flag-fl_')) {
  779. unset($links[$name]);
  780. }
  781. }
  782. }
  783. }
  784. /**
  785. * Implementation of hook_flag_alter().
  786. */
  787. function flag_lists_flag_alter(&$flag) {
  788. }
  789. /**
  790. * Implementation of hook_flag_delete().
  791. *
  792. * This is not in flag yet.
  793. */
  794. function flag_lists_flag_delete($flag) {
  795. // Template flag is being deleted. Clean up our tables.
  796. // Collect the sub-flag fids so we can delete counts and content records.
  797. $results = db_select('flag_lists_flags', 'f')
  798. ->fields('f', array('fid', 'name'))
  799. ->condition('pfid', $flag->fid)
  800. ->execute();
  801. foreach ($results as $fid) {
  802. db_delete('flag_lists_counts')
  803. ->condition('fid', $flag->fid)
  804. ->execute();
  805. db_delete('flag_lists_content')
  806. ->condition('fid', $flag->fid)
  807. ->execute();
  808. }
  809. // flag_lists_types uses the template flag name, not our own fid.
  810. db_delete('flag_lists_types')
  811. ->condition('name', $flag->name)
  812. ->execute();
  813. // Now delete the sub-flags.
  814. $num_deleted = db_delete('flag_lists_flags')
  815. ->condition('pfid', $flag->fid)
  816. ->execute();
  817. if (!empty($num_deleted)) {
  818. drupal_set_message(t('The template flag "@title" and all its sub-flags have been deleted.', array('@title' => $flag->title)));
  819. }
  820. }
  821. /**
  822. * Implementation of hook_views_api().
  823. */
  824. function flag_lists_views_api() {
  825. return array(
  826. 'api' => 2.0,
  827. 'path' => drupal_get_path('module', 'flag_lists') . '/includes',
  828. );
  829. }
  830. /**
  831. * Helper function to test if a flag is owned by the current user, or current
  832. * user can administer flags.
  833. */
  834. function flag_lists_is_owner($action, $name) {
  835. global $user;
  836. if (user_access('administer flags')) {
  837. return TRUE;
  838. }
  839. // If we don't have an fid, then we have the flag name.
  840. if (is_numeric($name)) {
  841. $query = db_select('flag_lists_flags', 'f')->condition('fid', $name);
  842. $query->addField('f', 'name');
  843. $name = $query->execute()->fetchField();
  844. }
  845. if (!user_access($action . ' own flag lists')) {
  846. return FALSE;
  847. }
  848. if (db_select('flag_lists_flags', 'f')->fields('f')->condition('f.name', $name)->condition('f.uid', $user->uid)->countQuery()->execute()->fetchField()) {
  849. return TRUE;
  850. }
  851. return FALSE;
  852. }
  853. /**
  854. * Get a single user's lists, and merge in flag module flags
  855. */
  856. function flag_lists_get_user_flags($content_type = NULL, $account = NULL, $use_flags = FALSE) {
  857. $flags = array();
  858. $lists = array();
  859. if (!isset($account)) {
  860. $account = $GLOBALS['user'];
  861. }
  862. // Get flag lists flags
  863. $query = db_select('flag_lists_flags', 'fl')
  864. ->fields('fl')
  865. ->condition('fl.uid', $account->uid);
  866. $query->leftJoin('flag', 'f', 'fl.pfid = f.fid');
  867. $query->leftJoin('flag_lists_types', 'ft', 'ft.name = f.name');
  868. $query->addField('ft', 'type');
  869. if ($content_type) {
  870. $query->condition('ft.type', $content_type);
  871. }
  872. $result = $query->execute();
  873. foreach ($result as $row) {
  874. if (!isset($lists[$row->name])) {
  875. $lists[$row->name] = flag_flag::factory_by_row($row);
  876. $lists[$row->name]->module = 'flag_lists';
  877. }
  878. else {
  879. $lists[$row->name]->types[] = $row->type;
  880. }
  881. }
  882. // Get regular flags.
  883. if ($use_flags) {
  884. $flags = flag_get_flags('node', $content_type, $account);
  885. // Strip out any list templates
  886. foreach ($flags as $key => $flag) {
  887. if (stristr($flag->name, 'fl_template') !== FALSE) {
  888. unset($flags[$key]);
  889. }
  890. }
  891. }
  892. $flags = array_merge($lists, $flags);
  893. return $flags;
  894. }
  895. /**
  896. * Theme function to return edit, delete links.
  897. *
  898. * @param $flag
  899. * The flag whose links are being built.
  900. */
  901. function theme_flag_lists_ops($variables) {
  902. $flag = $variables['flag'];
  903. $links = array(
  904. 'flags_edit' => array('title' => t('edit'), 'href' => 'flags/lists/edit/' . $flag->name, 'query' => drupal_get_destination()),
  905. 'flags_delete' => array('title' => t('delete'), 'href' => 'flags/lists/delete/' . $flag->name, 'query' => drupal_get_destination()),
  906. );
  907. return theme('links', array('links' => $links, 'attributes' => array('class' => 'flag_lists_ops')));
  908. }
  909. /**
  910. * Theme a list of lists
  911. *
  912. * @param $node
  913. * The listable node
  914. * @param boolean $create
  915. * Show the create list form.
  916. * @param boolean $ops
  917. * Show the edit / delete links for lists
  918. * @param boolean $use_flags
  919. * Show flags from the flag module
  920. * @return <type>
  921. */
  922. // @todo Separate out the code from the theming better.
  923. function theme_flag_lists_list($variables) {
  924. $node = $variables['node'];
  925. $create = $variables['create'];
  926. $ops = $variables['ops'];
  927. $use_flags = $variables['use_flags'];
  928. $items = array();
  929. // Make sure we have a node.
  930. if (is_object($node) && user_access('create flag lists')) {
  931. $content_type = $node->type;
  932. $entity_id = $node->nid;
  933. }
  934. // Or at least confirm we are on a node page and use has access.
  935. elseif (arg(0) == 'node' && is_numeric(arg(1)) && user_access('create flag lists')) {
  936. $entity_id = arg(1);
  937. $query = db_select('node')->condition('nid', $entity_id);
  938. $query->addField('node', 'type');
  939. $content_type = $query->execute()->fetchField();
  940. }
  941. else {
  942. return;
  943. }
  944. // Do we have a list template for this node type, or are we s
  945. if (!flag_lists_template_exists($content_type) && !$use_flags) {
  946. return;
  947. }
  948. global $user;
  949. if ($flags = flag_lists_get_user_flags($content_type, $user, $use_flags)) {
  950. // Build the list of lists for this node.
  951. foreach ($flags as $flag) {
  952. if ($flag->module == 'flag_lists') {
  953. $action = _flag_lists_is_flagged($flag, $entity_id, $user->uid, 0) ? 'unflag' : 'flag';
  954. }
  955. else {
  956. $action = $flag->is_flagged($entity_id) ? 'unflag' : 'flag';;
  957. }
  958. // Do we need the ops?
  959. if ($ops && $flag->module == 'flag_lists') {
  960. $ops_links = theme('flag_lists_ops', array('flag' => $flag));
  961. $link = $flag->theme($action, $entity_id) . $ops_links;
  962. }
  963. else {
  964. $link = $flag->theme($action, $entity_id);
  965. }
  966. // If it's a list, fix the link.
  967. if ($flag->module == 'flag_lists') {
  968. flag_lists_fix_link($link, $action);
  969. }
  970. $items[] = $link;
  971. }
  972. }
  973. if ($create && flag_lists_template_exists($content_type)) {
  974. $items[] = l(t('Make a new @name', array('@name' => variable_get('flag_lists_name', t('list')))), 'flag-lists/add/' . $content_type, array('query' => drupal_get_destination()));
  975. }
  976. // Return if nothing to display.
  977. if (empty($items) || !count($items)) {
  978. return;
  979. }
  980. drupal_add_css(drupal_get_path('module', 'flag_lists') . '/theme/flag_lists.css');
  981. return theme('item_list', array('items' => $items, 'type' => 'ul', 'attributes' => array('class' => 'flag-lists-links')));
  982. }
  983. // Do we still need this, and/or do we need our own cache?
  984. /**
  985. * Clear the flag cache.
  986. *
  987. * This is a less severe cache clear than provided by flag. All flag lists
  988. * users must be authorized, so we don't need to flush the page cache. For now,
  989. * flag lists titles won't be in the menu, so no need to clear that.
  990. */
  991. function _flag_lists_clear_cache() {
  992. // We're not using _flag_clear_cache because we probably don't need the menu
  993. // rebuild and don't need to clear the page cache.
  994. if (module_exists('views')) {
  995. views_invalidate_cache();
  996. }
  997. }
  998. /**
  999. * Update ALL flag lists with settings form values.
  1000. */
  1001. function flag_lists_rebuild() {
  1002. $flags = flag_lists_get_flags();
  1003. foreach ($flags as $flag) {
  1004. flag_lists_set_messages($flag);
  1005. $flag->link_type = 'toggle';
  1006. flag_lists_save($flag);
  1007. }
  1008. }
  1009. /**
  1010. * Build array of all flag lists.
  1011. *
  1012. * @return If limit and header arguments are provided, the paged flags, otherwise
  1013. * an array of all flags.
  1014. */
  1015. function flag_lists_get_flags($limit = NULL, $header = NULL) {
  1016. $flags = array();
  1017. if ($limit) {
  1018. $query = db_select('flag_lists_flags', 'fl')
  1019. ->fields('fl')
  1020. ->groupBy('fl.fid')
  1021. ->extend('PagerDefault')
  1022. ->limit($limit);
  1023. $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
  1024. $query->addExpression('GROUP_CONCAT(ft.type)', 'types');
  1025. $result = $query->execute();
  1026. }
  1027. else {
  1028. $query = db_select('flag_lists_flags', 'fl')
  1029. ->fields('fl')
  1030. ->groupBy('fl.fid');
  1031. $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
  1032. $query->addExpression('GROUP_CONCAT(ft.type)', 'types');
  1033. $result = $query->execute();
  1034. }
  1035. foreach ($result as $row) {
  1036. $flags[$row->name] = flag_flag::factory_by_row($row);
  1037. $flags[$row->name]->types = explode(',', $row->types);
  1038. $flags[$row->name]->uid = $row->uid;
  1039. }
  1040. return $flags;
  1041. }
  1042. /**
  1043. * Get a specific flag.
  1044. *
  1045. * Using this instead of flag_get_flag() for performance.
  1046. */
  1047. function flag_lists_get_flag($fid) {
  1048. // If we don't have an fid, then we have the flag name.
  1049. if (!is_numeric($fid)) {
  1050. $query = db_select('flag_lists_flags')
  1051. ->condition('name', $fid);
  1052. $query->addField('flag_lists_flags', 'fid');
  1053. $fid = $query->execute()->fetchField();
  1054. }
  1055. $query = db_select('flag_lists_flags', 'fl')
  1056. ->fields('fl')
  1057. ->condition('fl.fid', $fid);
  1058. $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
  1059. $query->addField('ft', 'type');
  1060. $result = $query->execute();
  1061. foreach ($result as $row) {
  1062. if (!isset($flag->name)) {
  1063. $flag = flag_flag::factory_by_row($row);
  1064. }
  1065. else {
  1066. $flag->types[] = $row->type;
  1067. }
  1068. }
  1069. return $flag;
  1070. }
  1071. /**
  1072. * Get all flagged content in a flag.
  1073. *
  1074. * Using this instead of flag_get_flagged_content() because we need to make sure that we use flag_lists_get_flags()
  1075. *
  1076. * @param
  1077. * The flag name for which to retrieve flagged content.
  1078. */
  1079. function flag_lists_get_flagged_content($fid, $uid) {
  1080. $return = array();
  1081. $flag = flag_lists_get_flag($fid);
  1082. $result = db_select('flag_lists_content', 'f')
  1083. ->fields('f')
  1084. ->condition('f.fid', $flag->fid)
  1085. ->condition('f.uid', $uid)
  1086. ->execute();
  1087. foreach ($result as $row) {
  1088. $return[] = $row;
  1089. }
  1090. return $return;
  1091. }
  1092. /**
  1093. * Implementation of hook_flag_link().
  1094. *
  1095. * When Flag uses a link type provided by this module, it will call this
  1096. * implementation of hook_flag_link(). It returns a single link's attributes,
  1097. * using the same structure as hook_link(). Note that "title" is provided by
  1098. * the Flag configuration if not specified here.
  1099. *
  1100. * @param $flag
  1101. * The full flag object of for the flag link being generated.
  1102. * @param $action
  1103. * The action this link will perform. Either 'flag' or 'unflag'.
  1104. * @param $entity_id
  1105. * The ID of the node, comment, user, or other object being flagged.
  1106. * @return
  1107. * An array defining properties of the link.
  1108. */
  1109. function flag_lists_flag_link($flag, $action, $entity_id) {
  1110. return array();
  1111. }
  1112. /**
  1113. * Implementation of hook_flag_link_types().
  1114. */
  1115. function flag_lists_flag_link_types() {
  1116. return array(
  1117. 'fl_template' => array(
  1118. 'title' => t('Flag Lists toggle'),
  1119. 'description' => t('If you are creating a Flag lists template flag, you must select this link type.'),
  1120. ),
  1121. );
  1122. }
  1123. function flag_lists_flag_default_flags($name = 'fl_template') {
  1124. // return array(
  1125. // array(
  1126. // 'api_version' => 2,
  1127. // 'name' => $name,
  1128. // 'module' => 'flag_lists',
  1129. // 'content_type' => 'node',
  1130. // 'global' => 0,
  1131. // 'show_on_page' => 0,
  1132. // 'show_on_teaser' => 0,
  1133. // 'show_on_form' => 0,
  1134. // // The following UI labels aren't wrapped in t() because they are written
  1135. // // to the DB in English. They are passed to t() later, thus allowing for
  1136. // // multilingual sites.
  1137. // 'title' => 'Flag lists template',
  1138. // 'flag_short' => 'Add to your [flag_lists:title] [flag_lists:term]',
  1139. // 'flag_long' => 'Add this post to your [flag_lists:title] [flag_lists:term]',
  1140. // 'flag_message' => 'This post has been added to your [flag_lists:title] [flag_lists:term]',
  1141. // 'unflag_short' => 'Remove this from your [flag_lists:title] [flag_lists:term]',
  1142. // 'unflag_long' => 'Remove this post from your [flag_lists:title] [flag_lists:term]',
  1143. // 'unflag_message' => 'This post has been removed from your [flag_lists:title] [flag_lists:term]',
  1144. // 'types' => array(),
  1145. // 'link_type' => 'toggle',
  1146. // ),
  1147. // );
  1148. $flags = array();
  1149. // Exported flag: "Flag lists template".
  1150. $flags['fl_template'] = array(
  1151. 'entity_type' => 'node',
  1152. 'title' => 'Flag lists template',
  1153. 'global' => 0,
  1154. 'types' => array(),
  1155. 'flag_short' => 'Add to your [flag_lists:title] [flag_lists:term]',
  1156. 'flag_long' => 'Add this post to your [flag_lists:title] [flag_lists:term]',
  1157. 'flag_message' => 'This post has been added to your [flag_lists:title] [flag_lists:term]',
  1158. 'unflag_short' => 'Remove this from your [flag_lists:title] [flag_lists:term]',
  1159. 'unflag_long' => 'Remove this post from your [flag_lists:title] [flag_lists:term]',
  1160. 'unflag_message' => 'This post has been removed from your [flag_lists:title] [flag_lists:term]',
  1161. 'unflag_denied_text' => '',
  1162. 'link_type' => 'toggle',
  1163. 'weight' => 0,
  1164. 'api_version' => 3,
  1165. 'module' => 'flag_lists',
  1166. 'show_on_page' => 0,
  1167. 'show_on_teaser' => 0,
  1168. 'show_on_form' => 0,
  1169. 'status' => FALSE,
  1170. 'import_roles' => array(
  1171. 'flag' => array(),
  1172. 'unflag' => array(),
  1173. ),
  1174. );
  1175. return $flags;
  1176. }
  1177. /**
  1178. * Saves a flag to the database. It is a wrapper around update($flag) and insert($flag).
  1179. */
  1180. function flag_lists_save(&$flag, $account = NULL) {
  1181. if (!isset($account)) {
  1182. $account = $GLOBALS['user'];
  1183. }
  1184. if (isset($flag->fid)) {
  1185. flag_lists_update($flag);
  1186. $flag->is_new = FALSE;
  1187. module_invoke_all('flag_lists', $flag, $account);
  1188. }
  1189. else {
  1190. flag_lists_insert($flag);
  1191. $flag->is_new = TRUE;
  1192. module_invoke_all('flag_lists', $flag, $account);
  1193. }
  1194. // Clear the page cache for anonymous users.
  1195. // cache_clear_all('*', 'cache_page', TRUE);
  1196. }
  1197. /**
  1198. * Saves an existing flag to the database. Better use save($flag).
  1199. */
  1200. function flag_lists_update($flag) {
  1201. $num_updated = db_update('flag_lists_flags')
  1202. ->fields(array(
  1203. 'title' => $flag->title,
  1204. 'name' => $flag->name,
  1205. 'options' => $flag->get_serialized_options($flag),
  1206. ))
  1207. ->condition('fid', $flag->fid)
  1208. ->execute();
  1209. }
  1210. /**
  1211. * Saves a new flag to the database. Better use save($flag).
  1212. */
  1213. function flag_lists_insert($flag) {
  1214. $flag->fid = db_insert('flag_lists_flags')
  1215. ->fields(array(
  1216. 'pfid' => $flag->pfid,
  1217. 'uid' => $flag->uid,
  1218. 'entity_type' => $flag->entity_type,
  1219. 'name' => $flag->name,
  1220. 'title' => $flag->title,
  1221. 'options' => $flag->get_serialized_options($flag),
  1222. ))
  1223. ->execute();
  1224. $flag->name = 'flag_lists_' . $flag->uid . '_' . $flag->fid;
  1225. flag_lists_update($flag);
  1226. }
  1227. /**
  1228. * Delete a flag_lists flag.
  1229. *
  1230. */
  1231. function flag_lists_fl_delete($flag, $account = NULL) {
  1232. if (!isset($account)) {
  1233. $account = $GLOBALS['user'];
  1234. }
  1235. db_delete('flag_lists_counts')->condition('fid', $flag->fid)->execute();
  1236. db_delete('flag_lists_content')->condition('fid', $flag->fid)->execute();
  1237. db_delete('flag_lists_flags')->condition('fid', $flag->fid)->execute();
  1238. $flag->is_deleted = TRUE;
  1239. module_invoke_all('flag_lists', $flag, $account);
  1240. _flag_lists_clear_cache();
  1241. drupal_set_message(t('The @name @title has been deleted.', array('@name' => variable_get('flag_lists_name', t('list')), '@title' => $flag->title)));
  1242. }
  1243. /**
  1244. * Menu callback for (un)flagging a node.
  1245. *
  1246. * Used both for the regular callback as well as the JS version. We use this
  1247. * instead of the flag module's because our flags are not in the flags table.
  1248. */
  1249. function flag_lists_page($action = NULL, $flag_name = NULL, $entity_id = NULL) {
  1250. global $user;
  1251. // Shorten up the variables that affect the behavior of this page.
  1252. $js = isset($_REQUEST['js']);
  1253. $token = $_REQUEST['token'];
  1254. // Specifically $_GET to avoid getting the $_COOKIE variable by the same key.
  1255. $has_js = isset($_GET['has_js']);
  1256. // Check the flag token, then perform the flagging.
  1257. if (!flag_check_token($token, $entity_id)) {
  1258. $error = t('Bad token. You seem to have followed an invalid link.');
  1259. }
  1260. elseif ($user->uid == 0 && !$has_js) {
  1261. $error = t('You must have JavaScript and cookies enabled in your browser to flag content.');
  1262. }
  1263. else {
  1264. if (empty($flag_name) || !($flag = flag_lists_get_flag($flag_name))) {
  1265. // Flag does not exist.
  1266. $error = t('You are not allowed to flag, or unflag, this content.');
  1267. }
  1268. // Identify it as ours.
  1269. $flag->module = 'flag_lists';
  1270. flag_lists_do_flag($flag, $action, $entity_id);
  1271. }
  1272. // If an error was received, set a message and exit.
  1273. if (isset($error)) {
  1274. if ($js) {
  1275. drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
  1276. print drupal_to_js(array(
  1277. 'status' => FALSE,
  1278. 'errorMessage' => $error,
  1279. ));
  1280. exit;
  1281. }
  1282. else {
  1283. drupal_set_message($error);
  1284. drupal_access_denied();
  1285. return;
  1286. }
  1287. }
  1288. // If successful, return data according to the request type.
  1289. if ($js) {
  1290. drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
  1291. // $flag = flag_lists_get_flag($flag_name);
  1292. // $flag->link_type = 'toggle';
  1293. $sid = flag_get_sid($user->uid);
  1294. $new_action = _flag_lists_is_flagged($flag, $entity_id, $user->uid, $sid) ? 'unflag' : 'flag';
  1295. $new_link = $flag->theme($new_action, $entity_id, array("after_flagging" => TRUE));
  1296. flag_lists_fix_link($new_link, $new_action);
  1297. drupal_json_output(array(
  1298. 'status' => TRUE,
  1299. 'newLink' => $new_link,
  1300. // Further information for the benefit of custom JavaScript event handlers:
  1301. 'contentId' => $entity_id,
  1302. 'contentType' => $flag->content_type,
  1303. 'flagName' => $flag->name,
  1304. 'flagStatus' => $action,
  1305. ));
  1306. exit;
  1307. }
  1308. else {
  1309. $flag = flag_lists_get_flag($flag->fid);
  1310. drupal_set_message($flag->get_label($action . '_message', $entity_id));
  1311. drupal_goto();
  1312. }
  1313. }
  1314. function flag_lists_fix_link(&$link, $action) {
  1315. // This is a hack to let us use our own flag/unflag callbacks without having
  1316. // to override $flag->theme and creating our own flag_link type.
  1317. $link = str_replace('/flag/' . $action . '/', '/flag-lists/' . $action . '/', $link);
  1318. }
  1319. /**
  1320. * Flags, or unflags, an item.
  1321. *
  1322. * @param $action
  1323. * Either 'flag' or 'unflag'.
  1324. * @param $entity_id
  1325. * The ID of the item to flag or unflag.
  1326. * @param $account
  1327. * The user on whose behalf to flag. Leave empty for the current user.
  1328. * @param $skip_permission_check
  1329. * Flag the item even if the $account user doesn't have permission to do so.
  1330. * @return
  1331. * FALSE if some error occured (e.g., user has no permission, flag isn't
  1332. * applicable to the item, etc.), TRUE otherwise.
  1333. */
  1334. function flag_lists_do_flag($flag, $action, $entity_id, $account = NULL, $skip_permission_check = FALSE) {
  1335. if (!isset($account)) {
  1336. $account = $GLOBALS['user'];
  1337. }
  1338. if (!$account) {
  1339. return FALSE;
  1340. }
  1341. if (!$skip_permission_check) {
  1342. if (!$flag->access($entity_id, $action, $account)) {
  1343. // User has no permission to flag/unflag this object.
  1344. return FALSE;
  1345. }
  1346. }
  1347. else {
  1348. // We are skipping permission checks. However, at a minimum we must make
  1349. // sure the flag applies to this content type:
  1350. if (!$flag->applies_to_content_id($entity_id)) {
  1351. return FALSE;
  1352. }
  1353. }
  1354. // Clear various caches; We don't want code running after us to report
  1355. // wrong counts or false flaggings.
  1356. // flag_get_counts(NULL, NULL, TRUE);
  1357. // flag_get_user_flags(NULL, NULL, NULL, NULL, TRUE);
  1358. // Find out which user id to use.
  1359. $uid = $flag->global ? 0 : $account->uid;
  1360. $sid = flag_get_sid($uid);
  1361. // Anonymous users must always have a session id.
  1362. if ($sid == 0 && $account->uid == 0) {
  1363. return FALSE;
  1364. }
  1365. // Perform the flagging or unflagging of this flag. We invoke hook_flag here
  1366. // because we do our own flagging.
  1367. $flagged = _flag_lists_is_flagged($flag, $entity_id, $uid, $sid);
  1368. if ($action == 'unflag') {
  1369. if ($flagged) {
  1370. $fcid = _flag_lists_unflag($flag, $entity_id, $uid, $sid);
  1371. module_invoke_all('flag', 'unflag', $flag, $entity_id, $account, $fcid);
  1372. }
  1373. }
  1374. elseif ($action == 'flag') {
  1375. if (!$flagged) {
  1376. $fcid = _flag_lists_flag($flag, $entity_id, $uid, $sid);
  1377. module_invoke_all('flag', 'flag', $flag, $entity_id, $account, $fcid);
  1378. }
  1379. }
  1380. return TRUE;
  1381. }
  1382. /**
  1383. * Returns TRUE if a certain user has flagged this content.
  1384. *
  1385. *
  1386. * This method is similar to is_flagged() except that it does direct SQL and
  1387. * doesn't do caching. Use it when you want to not affect the cache, or to
  1388. * bypass it.
  1389. *
  1390. */
  1391. function _flag_lists_is_flagged($flag, $entity_id, $uid, $sid) {
  1392. $query = db_select('flag_lists_content')
  1393. ->condition('fid', $flag->fid)
  1394. ->condition('uid', $uid)
  1395. ->condition('sid', $sid)
  1396. ->condition('entity_id', $entity_id);
  1397. $query->addField('flag_lists_content', 'fid');
  1398. return $query->execute()->fetchField();
  1399. }
  1400. /**
  1401. * A low-level method to flag content.
  1402. *
  1403. * You probably shouldn't call this raw private method: call the
  1404. * flag_lists_do_flag() function instead.
  1405. *
  1406. */
  1407. function _flag_lists_flag($flag, $entity_id, $uid, $sid) {
  1408. $fcid = db_insert('flag_lists_content')
  1409. ->fields(array(
  1410. 'fid' => $flag->fid,
  1411. 'entity_type' => $flag->entity_type,
  1412. 'entity_id' => $entity_id,
  1413. 'uid' => $uid,
  1414. 'sid' => $sid,
  1415. 'timestamp' => REQUEST_TIME,
  1416. ))
  1417. ->execute();
  1418. _flag_lists_update_count($flag, $entity_id);
  1419. return $fcid;
  1420. }
  1421. /**
  1422. * A low-level method to unflag content.
  1423. *
  1424. * You probably shouldn't call this raw private method: call the
  1425. * flag_lists_do_flag() function instead.
  1426. *
  1427. */
  1428. function _flag_lists_unflag($flag, $entity_id, $uid, $sid) {
  1429. $query = db_select('flag_lists_content')
  1430. ->condition('fid', $flag->fid)
  1431. ->condition('entity_id', $entity_id)
  1432. ->condition('uid', $uid)
  1433. ->condition('sid', $sid);
  1434. $query->addField('flag_lists_content', 'fcid');
  1435. $fcid = $query->execute()->fetchField();
  1436. if ($fcid) {
  1437. db_delete('flag_lists_content')
  1438. ->condition('fcid', $fcid)
  1439. ->execute();
  1440. _flag_lists_update_count($flag, $entity_id);
  1441. }
  1442. return $fcid;
  1443. }
  1444. /**
  1445. * Remove all entries of entity_id and type
  1446. *
  1447. * @param $entity_id
  1448. * Entity id which has been flagged.
  1449. * @param $type
  1450. * The entity type.
  1451. */
  1452. function _flag_lists_remove_entity($entity_id, $type) {
  1453. $query = db_select('flag_lists_content')
  1454. ->condition('entity_id', $entity_id)
  1455. ->condition('entity_type', $type);
  1456. $query->fields('flag_lists_content', array('fcid', 'fid', 'uid', 'sid'));
  1457. $items = $query->execute()->fetchAll();
  1458. if ($items) {
  1459. foreach ($items as $key => $value) {
  1460. db_delete('flag_lists_content')
  1461. ->condition('fcid', $value->fcid)
  1462. ->execute();
  1463. watchdog('flag_lists', t('Deleted entry @fcid from flat_lists_content', array('@fcid' => $value->fcid)));
  1464. }
  1465. }
  1466. }
  1467. /**
  1468. * Updates the flag count for this content
  1469. */
  1470. function _flag_lists_update_count($flag, $entity_id) {
  1471. $count = db_select('flag_lists_content', 'f')
  1472. ->fields('f')
  1473. ->condition('fid', $flag->fid)
  1474. ->condition('entity_id', $entity_id)
  1475. ->countQuery()
  1476. ->execute()
  1477. ->fetchField();
  1478. if (empty($count)) {
  1479. $num_deleted = db_delete('flag_lists_counts')
  1480. ->condition('fid', $flag->fid)
  1481. ->condition('entity_id', $entity_id)
  1482. ->execute();
  1483. }
  1484. else {
  1485. $num_updated = db_update('flag_lists_counts')
  1486. ->fields(array(
  1487. 'count' => $count,
  1488. ))
  1489. ->condition('fid', $flag->fid)
  1490. ->condition('entity_id', $entity_id)
  1491. ->execute();
  1492. if (empty($num_updated)) {
  1493. db_insert('flag_lists_counts')
  1494. ->fields(array(
  1495. 'fid' => $flag->fid,
  1496. 'entity_type' => $flag->entity_type,
  1497. 'entity_id' => $entity_id,
  1498. 'count' => $count,
  1499. ))
  1500. ->execute();
  1501. }
  1502. }
  1503. }
  1504. /**
  1505. * Checks for a list template for a content type.
  1506. */
  1507. function flag_lists_template_exists($type) {
  1508. $query = db_select('flag_lists_types')
  1509. ->condition('type', $type);
  1510. $query->addField('flag_lists_types', 'type');
  1511. $exists = $query->execute()->fetchField();
  1512. if (!empty($exists)) {
  1513. return TRUE;
  1514. }
  1515. return FALSE;
  1516. }
  1517. /**
  1518. * Checks for a list title by node type.
  1519. */
  1520. function flag_lists_title_exists($title, $type) {
  1521. return db_query("SELECT COUNT(flf.fid) FROM {flag_lists_flags} flf LEFT JOIN {flag_types} ft ON flf.pfid=ft.fid WHERE flf.title=:title AND ft.type=:type AND flf.uid=:uid", array(':title' => $title, ':type' => $type, ':uid' => $GLOBALS['user']->uid))->fetchField();
  1522. }
  1523. /**
  1524. * Get a list of template flag names.
  1525. */
  1526. function flag_lists_get_templates() {
  1527. $templates = array();
  1528. $result = db_select('flag_lists_types', 'f')
  1529. ->fields('f', array(
  1530. 'name'
  1531. ))
  1532. ->distinct()
  1533. ->execute();
  1534. foreach ($result as $obj) {
  1535. $templates[] = flag_get_flag($obj->name);
  1536. }
  1537. return $templates;
  1538. }
  1539. /**
  1540. * Implements hook_token_info().
  1541. */
  1542. function flag_lists_token_info() {
  1543. $type = array(
  1544. 'name' => t('Flag lists'),
  1545. 'description' => t('Tokens related to flag lists.'),
  1546. 'needs-data' => 'flag_lists',
  1547. );
  1548. $flag_lists['term'] = array(
  1549. 'name' => t("Term"),
  1550. 'description' => t("The terminology used to name the lists, such as list, wishlist, favorites, etc."),
  1551. );
  1552. $flag_lists['title'] = array(
  1553. 'name' => t("Title"),
  1554. 'description' => t("The title of the list."),
  1555. );
  1556. return array(
  1557. 'types' => array('flag_lists' => $type),
  1558. 'tokens' => array('flag_lists' => $flag_lists),
  1559. );
  1560. }
  1561. /**
  1562. * Implements hook_tokens().
  1563. */
  1564. function flag_lists_tokens($type, $tokens, array $data = array(), array $options = array()) {
  1565. $replacements = array();
  1566. if ($type == 'flag_lists' && !empty($data['flag_lists'])) {
  1567. $flag_list = $data['flag_lists'];
  1568. foreach ($tokens as $name => $original) {
  1569. switch ($name) {
  1570. case 'title':
  1571. $replacements[$original] = $flag_list->title;
  1572. break;
  1573. case 'term':
  1574. $replacements[$original] = variable_get('flag_lists_name', t('list'));
  1575. break;
  1576. }
  1577. }
  1578. }
  1579. return $replacements;
  1580. }
  1581. /**
  1582. * Preprocess link title and text for the flag.tpl.php
  1583. *
  1584. * This seems to be the only place to do this
  1585. */
  1586. function flag_lists_preprocess_flag(&$variables) {
  1587. if (module_exists('token') && !empty($variables['flag']->module) && $variables['flag']->module == 'flag_lists') {
  1588. if (!empty($variables['link_text'])) {
  1589. $variables['link_text'] = token_replace($variables['link_text'], array('flag_lists' => $variables['flag']));
  1590. }
  1591. if (!empty($variables['link_title'])) {
  1592. $variables['link_title'] = token_replace($variables['link_title'], array('flag_lists' => $variables['flag']));
  1593. }
  1594. if (!empty($variables['message_text'])) {
  1595. $variables['message_text'] = token_replace($variables['message_text'], array('flag_lists' => $variables['flag']));
  1596. }
  1597. }
  1598. }
  1599. /**
  1600. * Implements hook_views_form_substitutions().
  1601. */
  1602. function flag_lists_views_form_substitutions() {
  1603. // Views check_plains the column label, so Flag lists needs to do the same
  1604. // in order for the replace operation to succeed.
  1605. $select_all_placeholder = check_plain('<!--flag-lists-ops-select-all-->');
  1606. $select_all = array(
  1607. '#type' => 'checkbox',
  1608. '#default_value' => FALSE,
  1609. '#attributes' => array('class' => array('flo-table-select-all')),
  1610. );
  1611. return array(
  1612. $select_all_placeholder => drupal_render($select_all),
  1613. );
  1614. }
  1615. /**
  1616. * Implements hook_entity_delete
  1617. */
  1618. function flag_lists_entity_delete($entity, $type) {
  1619. foreach (flag_get_flags($type) as $flag) {
  1620. if (isset($entity->vid)) {
  1621. $items = _flag_lists_remove_entity($entity->vid, $type);
  1622. }
  1623. }
  1624. }