content.module

  1. 6.x modules/content/content.module
  2. 7.x modules/content/content.module
  3. 4.x modules/content/content.module
  4. 5.x modules/content/content.module

File

modules/content/content.module
View source
  1. <?php
  2. // Define constants
  3. define('CONTENT_PRIVATE_FILE', 0); // must be logged in to view
  4. define('CONTENT_PUBLIC_FILE', 1); // publicly (anonymous) can view with URL
  5. /**
  6. * This module lets administrators create content, when then appear can appear in a block.
  7. */
  8. function content_menu() {
  9. $items = array();
  10. $items["admin-tools/content"] = array(
  11. "title" => "Content",
  12. "description" => "Manage content in " . variable_get("system_name", "FlightPath") . ". Ex: Announcements.",
  13. "page_callback" => "content_display_content_admin_list",
  14. "access_arguments" => array("admin_content"),
  15. "page_settings" => array(
  16. "menu_icon" => fp_get_module_path('content') . "/icons/application_edit.png",
  17. "page_show_title" => TRUE,
  18. "menu_links" => array(
  19. 0 => array(
  20. "text" => t("Admin Tools"),
  21. "path" => "admin-tools",
  22. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  23. ),
  24. ),
  25. ),
  26. 'type' => MENU_TYPE_DEFAULT_TAB,
  27. 'tab_family' => 'content_man_tabs',
  28. "weight" => 0,
  29. );
  30. $items["admin-tools/content/files"] = array(
  31. "title" => "Public Files",
  32. "page_callback" => 'fp_render_form',
  33. 'page_arguments' => array('content_public_files_form'),
  34. "access_arguments" => array("admin_public_files"),
  35. "page_settings" => array(
  36. "page_show_title" => TRUE,
  37. "menu_links" => array(
  38. 0 => array(
  39. "text" => t("Admin Tools"),
  40. "path" => "admin-tools",
  41. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  42. ),
  43. ),
  44. ),
  45. 'type' => MENU_TYPE_TAB,
  46. 'tab_family' => 'content_man_tabs',
  47. "weight" => 10,
  48. );
  49. /*
  50. $items["content/delete"] = array(
  51. "page_callback" => "content_handle_delete_content",
  52. "access_arguments" => array("admin_content"),
  53. "type" => MENU_TYPE_CALLBACK,
  54. );
  55. */
  56. // content/add/TYPE
  57. $items["content/add/%"] = array(
  58. "page_callback" => "fp_render_form",
  59. "page_arguments" => array("content_edit_content_form", "", 2, "new"),
  60. "access_callback" => "content_user_access",
  61. "access_arguments" => array("add", 2),
  62. "type" => MENU_TYPE_NORMAL_ITEM,
  63. "page_settings" => array(
  64. "menu_links" => array(
  65. 0 => array(
  66. "text" => t("Admin Tools"),
  67. "path" => "admin-tools",
  68. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  69. ),
  70. 1 => array(
  71. "text" => "Content",
  72. "path" => "admin-tools/content",
  73. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  74. ),
  75. ),
  76. ),
  77. );
  78. // Add content/add/TYPE/ with multiple wildcards for future module developers. Ex:
  79. // in hook_form_alter, using the arg() function to find out what these values are.
  80. $items["content/add/%/%"] = $items["content/add/%"];
  81. $items["content/add/%/%/%"] = $items["content/add/%"];
  82. $items["content/add/%/%/%/%"] = $items["content/add/%"];
  83. $items["content/add/%/%/%/%/%"] = $items["content/add/%"];
  84. $items["content/add/%/%/%/%/%/%"] = $items["content/add/%"];
  85. $items["content/%"] = array(
  86. "title" => "View",
  87. "page_callback" => "content_view_content",
  88. "page_arguments" => array(1),
  89. "access_callback" => "content_user_access",
  90. "access_arguments" => array("view", 1),
  91. "page_settings" => array(
  92. "page_show_title" => TRUE,
  93. "menu_links" => array(
  94. 0 => array(
  95. "text" => t("Admin Tools"),
  96. "path" => "admin-tools",
  97. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  98. ),
  99. 1 => array(
  100. "text" => "Content",
  101. "path" => "admin-tools/content",
  102. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  103. ),
  104. ),
  105. ),
  106. "type" => MENU_TYPE_TAB,
  107. "tab_family" => "content",
  108. "weight" => 10,
  109. );
  110. $items["content/%/edit"] = array(
  111. "title" => "Edit",
  112. "page_callback" => "fp_render_form",
  113. "page_arguments" => array("content_edit_content_form", "", "", 1),
  114. "access_callback" => "content_user_access",
  115. "access_arguments" => array("edit", 1),
  116. "page_settings" => array(
  117. "page_hide_report_error" => TRUE,
  118. "menu_links" => array(
  119. 0 => array(
  120. "text" => t("Admin Tools"),
  121. "path" => "admin-tools",
  122. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  123. ),
  124. 1 => array(
  125. "text" => "Content",
  126. "path" => "admin-tools/content",
  127. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  128. ),
  129. ),
  130. ),
  131. "type" => MENU_TYPE_TAB,
  132. "tab_family" => "content",
  133. "weight" => 20,
  134. );
  135. $items["content/%/devel"] = array(
  136. "title" => "Devel",
  137. "page_callback" => "content_display_devel",
  138. "page_arguments" => array(1),
  139. "access_arguments" => array("view_fpm_debug"),
  140. "page_settings" => array(
  141. "page_hide_report_error" => TRUE,
  142. "menu_links" => array(
  143. 0 => array(
  144. "text" => t("Admin Tools"),
  145. "path" => "admin-tools",
  146. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  147. ),
  148. 1 => array(
  149. "text" => "Content",
  150. "path" => "admin-tools/content",
  151. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  152. ),
  153. ),
  154. ),
  155. "type" => MENU_TYPE_TAB,
  156. "tab_family" => "content",
  157. "weight" => 30,
  158. );
  159. $items['content/%/remove'] = array(
  160. "page_callback" => "fp_render_form",
  161. "page_arguments" => array("content_unpublish_content_form", "", 1),
  162. "access_callback" => "content_user_access",
  163. "access_arguments" => array("edit", 1), // since we are editing the published field, we simply check for "edit"
  164. );
  165. $items["content-files/handle-download/%"] = array(
  166. "page_callback" => "content_files_handle_download",
  167. "page_arguments" => array(2),
  168. "access_callback" => "content_files_user_may_download_file",
  169. "access_arguments" => array(2),
  170. "type" => MENU_TYPE_CALLBACK,
  171. );
  172. /**
  173. * Generic screen which, if we have saved content from within an iframe dialog,
  174. * we want to display a "success" message, then just refresh the screen.
  175. */
  176. $items["content-dialog-handle-after-save"] = array(
  177. "page_callback" => "content_dialog_handle_after_save",
  178. "access_callback" => TRUE,
  179. "page_settings" => array (
  180. "display_currently_advising" => FALSE,
  181. "screen_mode" => "not_advising",
  182. "page_is_popup" => TRUE,
  183. ),
  184. "type" => MENU_TYPE_CALLBACK,
  185. );
  186. return $items;
  187. }
  188. /**
  189. * Returns TRUE or FALSE if the user has access to download this particular student's file.
  190. */
  191. function content_files_user_may_download_file($fid) {
  192. global $user;
  193. $file = content_get_uploaded_file($fid);
  194. if ($user->id == 1) return TRUE; // this is the admin user.
  195. // Get cid of original piece of content this file belonged to.
  196. $cid = intval($file['cid']);
  197. if ($cid === 0) return TRUE; // no CID attached, so we can assume they can view it.
  198. // Otherwise, see if the user is allowed to "view" this piece of content. If so, that means they
  199. // can view any files attached as well.
  200. return content_user_access('view', $cid);
  201. }
  202. /**
  203. * This actually finds and downloads the file for the user, decrypting if necessary.
  204. */
  205. function content_files_handle_download($fid) {
  206. $file = content_get_uploaded_file($fid);
  207. if (!$file) {
  208. display_not_found();
  209. die;
  210. }
  211. // Otherwise, now we proceed.
  212. $file_contents = @file_get_contents($file["full_filename"]);
  213. if ($file_contents && intval($file["is_encrypted"]) === 1 && function_exists("encryption_decrypt")) {
  214. $file_contents = encryption_decrypt($file_contents);
  215. }
  216. // Okay, now let's spit it out to the browser for download.
  217. header('Content-type: ' . $file["filetype"]);
  218. header('Content-Disposition: attachment; filename="' . $file["original_filename"] . '"');
  219. print $file_contents;
  220. die;
  221. }
  222. /**
  223. * This screen lets the user upload/manage/delete "public files" stored at custom/files/content_uploads/public_uploads/
  224. */
  225. function content_public_files_form() {
  226. fp_add_css(fp_get_module_path('content') . '/css/content.css');
  227. $form = array();
  228. fp_set_title('');
  229. // Did we receive a request to delete a file?
  230. if (isset($_GET['del']) && is_numeric($_GET['del'])) {
  231. $del_fid = intval($_GET['del']);
  232. $del_file = content_get_uploaded_file($del_fid);
  233. // Make sure the file is a "public" file.
  234. if (intval($del_file['attributes']) === CONTENT_PUBLIC_FILE) {
  235. // Double-check the user has access to delete
  236. if (user_has_permission('admin_public_files')) {
  237. // Okay, unlink the file.
  238. $temp = unlink(fp_get_files_path() . '/content_uploads/public_uploads/' . $del_file['filename']);
  239. if ($temp == FALSE) {
  240. fp_add_message(t("Unable to delete file. Possible server permissions issue, or file not found."), 'error');
  241. }
  242. else {
  243. // We did delete from file system, so delete from content_files table as well.
  244. db_query("DELETE FROM content_files WHERE fid = ?", array($del_fid));
  245. fp_add_message(t("File deleted successfully."));
  246. }
  247. }
  248. } // if public file
  249. // Use fp_goto() to avoid issue with user hitting "refresh"
  250. fp_goto("admin-tools/content/files");
  251. } // trying to delete file
  252. $form["#attributes"] = array("enctype" => 'multipart/form-data'); // allow the form itself to submit files.
  253. $form['mark_top'] = array(
  254. 'value' => '<p>' . t("This form allows you to upload files which are publicly accessible (without needing to log in) through the web.
  255. For example, you may wish to upload image files, css, etc, for use in other settings.
  256. <br><br>
  257. <strong>Note:</strong> These files will not be encrypted, and will be publicly available. Do not upload sensitive
  258. information, like student records or any personally-identifying details.") . "</p><hr>",
  259. );
  260. $form['upload_file'] = array(
  261. 'type' => 'file',
  262. 'label' => t("Upload file:"),
  263. 'description' => t("Allowed file extensions: %ext", array('%ext' => strtolower(variable_get("public_files_allowed_extensions", "css, txt, pdf, doc, docx, csv, xls, xlsx, ppt, pptx, rtf, odt, jpg, jpeg, png, gif, zip, 7z")))),
  264. );
  265. $overwrite_val = FALSE;
  266. if (isset($_SESSION['public_file_form_overwrite_val'])) {
  267. $overwrite_val = (bool) $_SESSION['public_file_form_overwrite_val'];
  268. }
  269. $form['overwrite'] = array(
  270. 'type' => 'checkbox',
  271. 'label' => t('Keep original filename and overwrite existing file by the same name?'),
  272. 'value' => $overwrite_val,
  273. 'prefix' => t("<strong>Important:</strong> By default, files are given randomized names when uploaded, so as not to overwrite an existing file with the same name.
  274. <br>If you would prefer to overwrite files with the same name, check the box below:"),
  275. );
  276. $form['upload_btn'] = array(
  277. 'type' => 'submit',
  278. 'value' => t("Upload"),
  279. );
  280. $form['mark_below_upload'] = array(
  281. 'value' => "<hr>",
  282. );
  283. ///////////////////////////////////////////////////////////
  284. $html = "";
  285. $table_headers = array();
  286. $table_headers[] = array("label" => "Actions");
  287. $table_headers[] = array("label" => "FID", "field" => "fid");
  288. $table_headers[] = array("label" => "Filename", "field" => "original_filename");
  289. $table_headers[] = array("label" => "Link");
  290. $table_headers[] = array("label" => "Uploaded", "field" => "posted");
  291. // Set our initial sort, if none is already set.
  292. theme_table_header_sortable_set_initial_sort("posted", 'DESC');
  293. $html .= "<table class='public-files-table' cellspacing='0' cellpadding='4'>";
  294. // Draw our our table headers, with links....
  295. $html .= theme_table_header_sortable($table_headers);
  296. // Get our order by clause based on selected table header, if any.
  297. $order_by = theme_table_header_sortable_order_by($table_headers);
  298. $res = pager_query("SELECT * FROM content_files WHERE `attributes` = ?
  299. $order_by", array(CONTENT_PUBLIC_FILE), 10, 0);
  300. while ($cur = db_fetch_object($res)) {
  301. $pol = (@$pol == "even") ? "odd" : "even";
  302. $posted = format_date(convert_time($cur->posted), "short");
  303. $abs = base_url() . "/custom/files/content_uploads/public_uploads/$cur->filename";
  304. $rel = base_path() . "/custom/files/content_uploads/public_uploads/$cur->filename";
  305. $absolute_link = "<a href='$abs' title='Link to file' class='abs-link' target='_blank'><i class='fa fa-link'></i></a>";
  306. $del_link = fp_get_js_confirm_link("Are you sure you want to delete this file?\\n\\nThis action cannot be undone.", "window.location=\"" . fp_url("admin-tools/content/files", "del=$cur->fid") . "\";", "<i class='fa fa-remove' title='" . t("Delete?") . "'></i>", "delete-link");
  307. $html .= "<tr class='row-$pol'>
  308. <td valign='top'>$del_link</td>
  309. <td valign='top'>$cur->fid</td>
  310. <td valign='top'>$cur->original_filename</td>
  311. <td valign='top'>$absolute_link</td>
  312. <td valign='top'>$posted</td>
  313. </tr>
  314. ";
  315. } // while
  316. $html .= "</table>";
  317. $html .= theme_pager();
  318. $form['mark_files_table'] = array(
  319. 'value' => $html,
  320. );
  321. return $form;
  322. } // content_public_files_form
  323. function content_public_files_form_validate($form, $form_state) {
  324. if (isset($form_state['values']['upload_btn']) && $form_state['values']['upload_btn'] != "") {
  325. // The user is trying to upload a file.
  326. // Make sure there is something there.
  327. if (!isset($_FILES['upload_file']) || trim($_FILES['upload_file']['name'][0]) == '') {
  328. form_error('upload_file', t('Please select a file from your computer to upload.'));
  329. return;
  330. }
  331. // Make sure it's the correct extension.
  332. $original_filename = $_FILES['upload_file']['name'][0];
  333. // Figure out the extension of the original filename.
  334. $temp = explode(".", $original_filename);
  335. $ext = $temp[count($temp) - 1];
  336. // Make sure that this extension is allowed.
  337. $allowed_extensions = csv_to_array(strtolower(variable_get("public_files_allowed_extensions", "css, txt, pdf, doc, docx, csv, xls, xlsx, ppt, pptx, rtf, odt, jpg, jpeg, png, gif, zip, 7z")));
  338. if (!in_array(strtolower($ext), $allowed_extensions)) {
  339. // Meaning, this extension is not allowed!
  340. form_error('upload_file', t("Sorry, the file's type/extension (%ext) is not allowed. Please rename or select another file, then try again.", array("%ext" => $ext)));
  341. return;
  342. }
  343. // If we made it here, then there's nothing wrong and we can continue to the _submit function.
  344. } // upload file
  345. } // ... validate
  346. function content_public_files_form_submit($form, $form_state) {
  347. if (isset($form_state['values']['upload_btn']) && $form_state['values']['upload_btn'] != "") {
  348. // The user is trying to upload a file.
  349. $file = $_FILES["upload_file"];
  350. $temp = fp_re_array_files($file);
  351. $file = $temp[0];
  352. $_SESSION['public_file_form_overwrite_val'] = $form_state['values']['overwrite'];
  353. content_add_new_uploaded_file($file, 0, !$form_state['values']['overwrite'], CONTENT_PUBLIC_FILE, TRUE);
  354. fp_add_message("File uploaded successfully.");
  355. }
  356. } // ... submit
  357. function content_unpublish_content_form($cid) {
  358. $content = content_load($cid);
  359. fp_add_css(fp_get_module_path("content") . "/css/content.css");
  360. // Display a confirmation page
  361. $types = content_get_types();
  362. $type = $content->type;
  363. // We want to redirect correctly based on this type's options....
  364. // Are there any special settings for the title?
  365. if (isset($types[$type]['settings']) && isset($types[$type]['settings']['title'])) {
  366. if (is_array($types[$type]['settings']['#redirect'])) {
  367. $form['#redirect'] = $types[$type]['settings']['#redirect'];
  368. }
  369. }
  370. $form['cid'] = array(
  371. 'type' => 'hidden',
  372. 'value' => $cid,
  373. );
  374. $form['mark_top'] = array(
  375. 'type' => 'markup',
  376. 'value' => "<h2>" . t("Are you sure you wish to remove <em>$content->title</em>?") . "</h2>
  377. <p>" . t("This action will cause the content to not appear in reports, and not be easily accessible. It can only be restored by an administrator.") . "</p>
  378. <hr>",
  379. );
  380. $form['submit_confirm'] = array(
  381. 'type' => 'submit',
  382. 'value' => 'Confirm & Remove',
  383. 'spinner' => TRUE,
  384. 'attributes' => array('class' => 'content-submit-btn'),
  385. );
  386. $form['submit_cancel'] = array(
  387. 'type' => 'submit',
  388. 'value' => 'Cancel',
  389. 'attributes' => array('class' => 'content-delete-btn'),
  390. );
  391. return $form;
  392. }
  393. function content_unpublish_content_form_submit(&$form, &$form_state) {
  394. $values = $form_state['values'];
  395. if (trim($values['submit_cancel']) != '') {
  396. // We want to CANCEL this operation!
  397. // find out if we should redirect anywhere in particular for this cancelation
  398. if (isset($form['#redirect'])) {
  399. fp_add_message(t("The operation was cancelled."));
  400. $form['#redirect']['query'] .= "&cancel=yes";
  401. }
  402. else {
  403. // No redirect was specified, so let's just return.
  404. fp_add_message(t("The operation was cancelled."));
  405. }
  406. return;
  407. }
  408. $cid = $form_state['values']['cid'];
  409. $content = content_load($cid);
  410. $content->published = 0;
  411. content_save($content);
  412. watchdog('content', "Content with cid $cid was removed/unpublished.");
  413. fp_add_message("Content has been removed.");
  414. }
  415. /**
  416. * Simply returns the fpm() results for the supplied content.
  417. */
  418. function content_display_devel($cid) {
  419. $rtn = "";
  420. $content = content_load($cid);
  421. $rtn .= "&nbsp; &nbsp;";
  422. fpm($content);
  423. return $rtn;
  424. }
  425. /**
  426. * Returns the timestamp of last access, or ZERO if never accessed
  427. */
  428. function content_get_last_access($cid, $account = null) {
  429. global $user;
  430. if ($account == NULL) $account = $user;
  431. $res = db_result(db_query("SELECT last_access FROM content_last_access WHERE cid = ? AND user_id = ?", array($cid, $account->id)));
  432. return intval($res);
  433. }
  434. /**
  435. * Sets the content_last_access timestamp for this user and content. Should be called
  436. * whenever a "content" node is viewed by the user.
  437. */
  438. function content_set_last_access($cid, $account = NULL) {
  439. global $user;
  440. if ($account == NULL) $account = $user;
  441. db_query("DELETE FROM content_last_access WHERE cid = ? AND user_id = ?", array($cid, $account->id));
  442. db_query("INSERT INTO content_last_access(cid, user_id, last_access)
  443. VALUES (?, ?, ?)", array($cid, $account->id, time()));
  444. // We need to re-calculate our alerts count just in case now.
  445. fp_recalculate_alert_count_by_type($account);
  446. }
  447. /**
  448. * Custom user access function to determine if the user can add, edit, etc, the content
  449. *
  450. * $op can equal: "add" (adding a new piece of content) $var will be the content_type. Ex: "engagement"
  451. * "view" (attempting to view the content as a full page)
  452. * "edit" (trying to edit an existing piece of content
  453. * "delete" (trying to delete the content)
  454. *
  455. *
  456. * @param unknown_type $op
  457. * @param unknown_type $var
  458. */
  459. function content_user_access($op, $var) {
  460. global $user;
  461. if ($user->id == 1) return TRUE; // the admin user.
  462. $cid = intval($var);
  463. $content = NULL;
  464. if ($op != "add") {
  465. $content = content_load($cid);
  466. }
  467. // Simply trying to view the content. Do we have permission to view published content of this type?
  468. if ($op == 'view' && user_has_permission("view_" . $content->type . "_content")) {
  469. if (intval($content->published) === 1) {
  470. // If the user is a student, we must take special considerations into account, like if
  471. // the visibility is set to faculty/staff only, and if the content is not ABOUT the student.
  472. if ($user->is_student == TRUE) {
  473. // Is this a faculty only file, and the user is a student?
  474. if (isset($content->field__visibility) && $content->field__visibility['value'] == "faculty") {
  475. return FALSE; // nope, can't view it.
  476. }
  477. // Is the user a student, and is the content ABOUT this student? If not, then they cannot view it. In other words,
  478. // a student can't see another student's engagements, etc.
  479. if (isset($content->field__student_id) && $content->field__student_id['value'] != $user->cwid) {
  480. return FALSE; // nope, can't view it.
  481. }
  482. }
  483. return TRUE;
  484. }
  485. }
  486. // Trying to add new content of the type specified in $var. Do we have permission?
  487. if ($op == 'add' && user_has_permission("add_" . $var . "_content")) {
  488. return TRUE;
  489. }
  490. // Trying to edit or delete this content.
  491. if ($op == "edit" || $op == "delete") {
  492. // Does the user have permission to edit/delete ANY of this type?
  493. if (user_has_permission($op . "_any_" . $content->type . "_content")) {
  494. return TRUE;
  495. }
  496. // Otherwise, does the content belong the the user, and do they have permission to edit/delete their OWN content?
  497. if ($content->user_id == $user->id && user_has_permission($op . "_own_" . $content->type . "_content")) {
  498. return TRUE;
  499. }
  500. } // op == edit or delete
  501. // Failed all of the previous tests, so return false.
  502. return FALSE;
  503. }
  504. /**
  505. * This is an implementation of hook_menu_handle_replacement_pattern.
  506. * It will search for and replace replacement patterns which we are aware of it in $str.
  507. */
  508. function content_menu_handle_replacement_pattern($str) {
  509. $cid = @$_REQUEST["cid"];
  510. if ($cid) {
  511. $content = content_load($cid);
  512. if (is_array($content)) {
  513. if (strpos($str, "%CONTENT_CID%") !== 0) {
  514. // It contains this replacement pattern!
  515. $str = str_replace("%CONTENT_CID%", $cid, $str);
  516. }
  517. if (strpos($str, "%CONTENT_TYPE%") !== 0) {
  518. // It contains this replacement pattern!
  519. $str = str_replace("%CONTENT_TYPE%", $content["type"], $str);
  520. }
  521. }
  522. }
  523. return $str;
  524. }
  525. /**
  526. * This is the URL we redirect to after saving a piece of content in a dialog,
  527. * so all we really want to do is close the dialog and reload the parent page.
  528. *
  529. * Remember, we are inside an iframe.
  530. */
  531. function content_dialog_handle_after_save() {
  532. $rtn = "";
  533. $rtn = "";
  534. $timeout = 1000;
  535. if (isset($_REQUEST['del']) && $_REQUEST['del'] != '') {
  536. $rtn .= "<h2 class='dialog-handle-after-save'>" . t("The content has been deleted successfully.<br><br>Closing window...") . "</h2>";
  537. }
  538. else if (isset($_REQUEST['cancel']) && $_REQUEST['cancel'] != '') {
  539. $rtn .= "<h2 class='dialog-handle-after-save'>" . t("Cancellation successful.<br><br>Closing window...") . "</h2>";
  540. }
  541. else {
  542. $rtn .= "<h2 class='dialog-handle-after-save'>" . t("Your submission has been saved correctly!<br><br>Closing window...") . "</h2>";
  543. }
  544. $rtn .= "<br><br><p>" . t("If this windows doesn't close within 3 seconds,") . " <a href='javascript:window.parent.location.reload();'>" . t("click here") . "</a>.</p>";
  545. // Add this javascript code to when the page loads.
  546. ///* DEV: comment out while testing
  547. $rtn .= "
  548. <script type='text/javascript'>
  549. $(document).ready(function() {
  550. setTimeout(function() {
  551. window.parent.location.reload();
  552. }, $timeout);
  553. });
  554. </script>
  555. ";
  556. //*/
  557. return $rtn;
  558. }
  559. /**
  560. * Display the content specified in the GET's cid.
  561. *
  562. */
  563. function content_view_content($cid) {
  564. $render = array();
  565. $render["#id"] = "content_" . $cid; // We will be doing this as a render array, so other modules can alter.
  566. $render['#cid'] = intval($cid);
  567. fp_add_css(fp_get_module_path('content') . '/css/content.css');
  568. if ($_GET['content_tabs'] == 'false') { // the string "false", not FALSE
  569. fp_add_body_class('content-tabs-false');
  570. }
  571. $content = content_load($cid); // assumed to be path/content/CID So, arg(1) == the cid
  572. if (!$content) {
  573. // Couldn't find it! Show page not found.
  574. display_not_found();
  575. return;
  576. }
  577. $render['#content_object'] = $content;
  578. $type = $content->type;
  579. $types = content_get_types();
  580. $author = fp_load_user($content->user_id);
  581. $render['#content-type'] = $type;
  582. $render['#class'] = "content-type-" . $type;
  583. $weight = 0;
  584. if (!strstr($content->title, "[hide]")) {
  585. fp_set_title($content->title);
  586. }
  587. fp_add_body_class("content-type-" . $content->type);
  588. //$rtn .= "<div class='content-view content-view-{$content->type}'>";
  589. $render['content_title'] = array(
  590. 'type' => 'markup',
  591. 'value' => "<div class='content-title'>" . $content->title . "</div>",
  592. 'weight' => $weight,
  593. );
  594. $render['content_submitted'] = array(
  595. 'type' => 'markup',
  596. 'value' => "<div class='content-submitted'>" . t("Submitted by") . " " . $author->name . " " . t("on") . " " . format_date(convert_time($content->updated), 'short') . "</div>",
  597. 'weight' => $weight += 10,
  598. );
  599. if ($content->delete_flag == 1) {
  600. $render['content_deleted'] = array(
  601. 'type' => 'markup',
  602. 'value' => "<div class='content-deleted'>" . t("NOTE: This content has been marked as deleted, and is scheduled for permanent removal.") . "</div>",
  603. 'weight' => $weight += 10,
  604. );
  605. }
  606. // Display all the various fields
  607. if (is_array($types[$type]['fields'])) {
  608. foreach($types[$type]["fields"] as $field_name => $field_details) {
  609. $display_value = $content->{"field__$field_name"}['display_value'];
  610. if ($field_details['type'] != 'hidden' && $field_details['type'] != 'cfieldset') {
  611. $render['field__' . $field_name] = array(
  612. 'type' => 'markup',
  613. 'label' => $field_details['label'],
  614. 'value' => "<div class='field-value'>$display_value</div>",
  615. 'weight' => $weight += 10,
  616. );
  617. }
  618. if ($field_details['type'] == 'cfieldset') {
  619. // TODO: make this appear within a fieldset?
  620. foreach ($field_details['elements'] as $c => $v) {
  621. foreach ($field_details['elements'][$c] as $efield_name => $edetails) {
  622. $display_value = $content->{"field__$efield_name"}['display_value'];
  623. if ($edetails['type'] != 'hidden' && $edetails['type'] != 'cfieldset') {
  624. $render['field__' . $efield_name] = array(
  625. 'type' => 'markup',
  626. 'label' => $edetails['label'],
  627. 'value' => "<div class='field-value'>$display_value</div>",
  628. 'weight' => $weight += 10,
  629. );
  630. }
  631. }
  632. }
  633. } // its a fieldset
  634. }
  635. }
  636. content_set_last_access($cid);
  637. if (isset($_GET['content_crumbs'])) {
  638. if ($_GET['content_crumbs'] == 'alerts') {
  639. $crumbs = array();
  640. $crumbs[] = array(
  641. 'text' => 'Alerts',
  642. 'path' => 'alerts',
  643. );
  644. fp_set_breadcrumbs($crumbs);
  645. }
  646. }
  647. watchdog("content", "view cid:$cid, type:$type", array());
  648. return fp_render_content($render);
  649. }
  650. /**
  651. * Meant to be run by admin, this generates table creation sql for a given type.
  652. */
  653. function _content_generate_create_table_sql($type) {
  654. $rtn = "";
  655. $keys = "";
  656. $types = content_get_types();
  657. fpm($types);
  658. $rtn .= "CREATE TABLE `content__$type` (
  659. `cid` int unsigned NOT NULL,
  660. `vid` int NOT NULL, \n";
  661. foreach ($types[$type]['fields'] as $name => $element) {
  662. if ($element['type'] == '' || strstr($element['type'], 'markup')) continue;
  663. fpm($name);
  664. $rtn .= " `field__$name` varchar(255) DEFAULT NULL, \n";
  665. $keys .= " KEY `field__$name` (`field__$name`), \n";
  666. } //foreach
  667. $keys = "PRIMARY KEY (`vid`),
  668. KEY `cid` (`cid`),
  669. $keys";
  670. $rtn .= rtrim($keys, ", \n") . "\n);";
  671. fpm(ppm($rtn, TRUE));
  672. } // _content_generate_create_table_sql
  673. /**
  674. * This form lets the user edit some piece of content
  675. *
  676. */
  677. function content_edit_content_form($type = "", $cid = "") {
  678. global $user;
  679. $form = array();
  680. $m = 0;
  681. if (@$_GET['content_tabs'] === 'false') { // the string "false", not FALSE
  682. fp_add_body_class('content-tabs-false');
  683. }
  684. fp_add_css(fp_get_module_path("content") . "/css/content.css");
  685. fp_add_js(fp_get_module_path("content") . "/js/content.js");
  686. if ($type == "") {
  687. @$type = strip_tags($_REQUEST["type"]); // strip_tags to make it safer.
  688. }
  689. if ($cid == "") {
  690. @$cid = strip_tags($_REQUEST["cid"]);
  691. }
  692. $types = content_get_types();
  693. if ($cid != "new") {
  694. $content = content_load($cid);
  695. if (!$content) {
  696. display_not_found();
  697. return;
  698. }
  699. fp_set_title(t("Editing") . " " . $content->title . "");
  700. // Re-set $type, just in case the user put the wrong type in the
  701. // URL
  702. $type = $content->type;
  703. }
  704. else {
  705. // New piece of content
  706. $content = new stdClass();
  707. $content->published = 1;
  708. $content->title = "";
  709. $content->delete_flag = 0;
  710. fp_set_title(t("Add new") . " " . $types[$type]["title"]);
  711. }
  712. fp_add_body_class('content-edit-type--' . $type);
  713. // For simplicity, and because it causes no harm except a small amount of extra bandwidth, we will
  714. // state that all "content" forms are multipart (so they can handle file uploads) even if there
  715. // are no files being uploaded.
  716. $form["#attributes"] = array("enctype" => 'multipart/form-data');
  717. $form["type"] = array(
  718. "type" => "hidden",
  719. "value" => $type,
  720. );
  721. $form["cid"] = array(
  722. "type" => "hidden",
  723. "value" => $cid,
  724. );
  725. $form["title"] = array(
  726. "type" => "textfield",
  727. "label" => t("Title:"),
  728. "value" => $content->title,
  729. "required" => TRUE,
  730. );
  731. // Are there any special settings for the title?
  732. if (isset($types[$type]['settings']) && isset($types[$type]['settings']['title'])) {
  733. if (is_array($types[$type]['settings']['title'])) {
  734. // Use array_merge to just override with whatever we have in our settings for the title.
  735. $form['title'] = array_merge($form['title'], $types[$type]['settings']['title']);
  736. if (isset($form['title']['auto']) && $form['title']['auto'] == TRUE) {
  737. // We will set the form to an automatic title, and hide.
  738. $form['title']['value'] = $type . ' content saved on ' . format_date(convert_time(time()));
  739. $form['title']['type'] = 'hidden';
  740. }
  741. }
  742. }
  743. if (isset($types[$type]['settings']['#redirect'])) {
  744. $form['#redirect'] = $types[$type]['settings']['#redirect'];
  745. }
  746. if (isset($types[$type]['settings']['#validate_handlers'])) {
  747. $form['#validate_handlers'] = $types[$type]['settings']['#validate_handlers'];
  748. }
  749. if (isset($types[$type]['settings']['#submit_handlers'])) {
  750. $form['#submit_handlers'] = $types[$type]['settings']['#submit_handlers'];
  751. }
  752. // If a #redirect has not been set, then we'll go back to this content's "view"...
  753. if (!isset($form['#redirect'])) {
  754. $form['#redirect'] = array('path' => 'content/' . $cid);
  755. }
  756. // Any js or css to add for this form, defined in the settings?
  757. if (isset($types[$type]['settings']['js'])) {
  758. fp_add_js($types[$type]['settings']['js']);
  759. }
  760. if (isset($types[$type]['settings']['css'])) {
  761. fp_add_css($types[$type]['settings']['css']);
  762. }
  763. if ($content->delete_flag == 1) {
  764. $form['mark_content_deleted'] = array(
  765. 'type' => 'markup',
  766. 'value' => "<div class='content-deleted'>" . t("NOTE: This content has been marked as deleted, and is scheduled for permanent removal.") . "</div>",
  767. 'weight' => -999,
  768. );
  769. }
  770. // Are there extra fields? If so, add their values in as well.
  771. if (is_array($types[$type]['fields'])) {
  772. foreach($types[$type]["fields"] as $field_name => $field_details) {
  773. $form[$field_name] = $field_details;
  774. if ($field_details['type'] != 'cfieldset') {
  775. $value = NULL;
  776. if (isset($content->{"field__$field_name"}['value'])) {
  777. $value = $content->{"field__$field_name"}['value'];
  778. }
  779. if ($cid == 'new') {
  780. // value won't be anything. Use value attr from the form as the initial value
  781. $value = @$form[$field_name]['value'];
  782. }
  783. $form[$field_name]['value'] = $value;
  784. // If this is a datetime-local field, then the value needs to be adjusted for it to work correctly. (it was stored as UTC in database)
  785. if ($form[$field_name]['type'] == 'datetime-local') {
  786. if (trim($value) != '') {
  787. $form[$field_name]["value"] = date('Y-m-d\TH:i', convert_time(strtotime($value)));
  788. }
  789. }
  790. // Similar to datetime-local, if this is a "time" field, then it has been stored as UTC in the database, and now
  791. // needs to be converted to local timezone.
  792. if ($form[$field_name]['type'] == 'time') {
  793. if (trim($value) != '') {
  794. $form[$field_name]["value"] = date('H:i', convert_time(strtotime($value)));
  795. }
  796. }
  797. // If this is a file, we want to have a way to REMOVE the file, so we can upload a different one, or simply to
  798. // have no file there at all.
  799. if ($form[$field_name]['type'] == 'file') {
  800. $limit = intval(@$form[$field_name]['limit']);
  801. $allowed = trim(@$form[$field_name]['allowed']);
  802. if (!$allowed) {
  803. $allowed = "txt, pdf, doc, docx, xls, xlsx, ppt, pptx, rtf, odt, jpg, jpeg, png, gif, zip, 7z"; // default safe files
  804. }
  805. $form['file_allowed_ext_' . $field_name] = array(
  806. 'type' => 'hidden',
  807. 'value' => $allowed,
  808. );
  809. $file_count = 0;
  810. if ($limit == 0) $limit = 1;
  811. $form['file_limit_' . $field_name] = array(
  812. 'type' => 'hidden',
  813. 'value' => $limit - 1,
  814. );
  815. // Do we already have a list of files we've attached?
  816. $html = "";
  817. $ofids = $value;
  818. $fids = explode(",", $value);
  819. $bool_is_empty = TRUE;
  820. foreach ($fids as $fid) {
  821. if ($fid == '' || intval($fid) == 0) continue;
  822. $fid = intval($fid);
  823. $file = content_get_uploaded_file($fid);
  824. if ($file) {
  825. $bool_is_empty = FALSE;
  826. $file_count++;
  827. // create a markup field, including a delete option, with the filename listed.
  828. $url = $file['url'];
  829. $delete_link = "<a href='javascript:contentDeleteFile(\"$field_name\",\"$fid\");'>Delete</a>";
  830. $html .= "<div class='file-field-line-$fid'><a href='$url' target='_blank'>" . $file['original_filename'] . "</a> - $delete_link</div>";
  831. } // we already had a file.
  832. } // foreach
  833. if (!$bool_is_empty) {
  834. $html = "<label>" . t("(Existing)") . " " . $form[$field_name]['label'] . "</label>" . $html;
  835. }
  836. $form["markup_$field_name"] = array(
  837. 'type' => 'markup',
  838. 'value' => $html,
  839. 'weight' => @intval($form[$field_name]['weight']) - 1,
  840. );
  841. // Add a hidden field to note that we have already uploaded this file.
  842. $form["previous_upload_$field_name"] = array(
  843. 'type' => 'hidden',
  844. 'value' => $ofids,
  845. 'weight' => @$form[$field_name]['weight'],
  846. );
  847. $form["file_count_" . $field_name] = array(
  848. 'type' => 'hidden',
  849. 'value' => $file_count,
  850. );
  851. if ($file_count >= $limit) {
  852. // hide the original field using CSS.
  853. $form[$field_name]['attributes'] = array("class" => 'hidden');
  854. }
  855. if ($allowed) {
  856. if (!isset($form[$field_name]['description'])) $form[$field_name]['description'] = '';
  857. $form[$field_name]['description'] .= "<div class='content-allowed-ext'><strong>" . t("Allowed file extensions:") . "</strong> " . $allowed . "</div>";
  858. }
  859. if ($limit > 1 && $file_count <= $limit) {
  860. $add_link = "<a href='javascript:contentAddMoreFile(\"$field_name\");' class='add-more-link add-more-link-$field_name'><i class='fa fa-plus'></i> " . t("Add More") . "</a>";
  861. if (!isset($form[$field_name]['suffix'])) $form[$field_name]['suffix'] = '';
  862. $form[$field_name]['suffix'] .= $add_link;
  863. }
  864. } // type == file
  865. } // is not cfieldset
  866. else {
  867. // If this is a fieldset, then we need to assign values to its ELEMENTS. If not empty, then OPEN the cfieldset!
  868. foreach ($form[$field_name]['elements'] as $c => $efields) {
  869. foreach ($efields as $efield_name => $efield_details) {
  870. $value = $content->{"field__$efield_name"}['value'];
  871. if ($cid == 'new') {
  872. // value won't be anything. Use value attr from the form as the initial value
  873. $value = @$efield_details['value'];
  874. }
  875. $efield_details['value'] = $value;
  876. // If this is a datetime-local field, then the value needs to be adjusted for it to work correctly.
  877. if ($efield_details['type'] == 'datetime-local') {
  878. if (trim($value) != '') {
  879. $value = date('Y-m-d\TH:i', convert_time(strtotime($value)));
  880. }
  881. }
  882. // Similar to datetime-local, if this is a "time" field, then it has been stored as UTC in the database, and now
  883. // needs to be converted to local timezone.
  884. if ($efield_details['type'] == 'time') {
  885. if (trim($value) != '') {
  886. $value = date('H:i', convert_time(strtotime($value)));
  887. }
  888. }
  889. $form[$field_name]['elements'][$c][$efield_name]["value"] = $value;
  890. if (trim($value)) {
  891. $form[$field_name]['start_closed'] = FALSE;
  892. }
  893. }
  894. }
  895. } // else is fieldset
  896. }
  897. }
  898. // We never show the published option, since we will always give the user a way to "remove" and "restore" content.
  899. $form['published'] = array(
  900. 'type' => 'hidden',
  901. 'value' => intval($content->published),
  902. );
  903. // Draw the controls (buttons)
  904. $form["submit_submit"] = array(
  905. "type" => "submit",
  906. "value" => t("Submit"),
  907. "spinner" => TRUE,
  908. 'weight' => 9920,
  909. 'attributes' => array('class' => 'content-submit-btn'),
  910. );
  911. if ($cid != 'new') {
  912. // Only display the delete button if we have permission
  913. if (content_user_access('delete', $content->cid)) {
  914. $form["submit_delete"] = array(
  915. "type" => "submit",
  916. "value" => t("Delete"),
  917. "confirm" => t('Are you sure you wish to delete this?\nThis action cannot be undone.'),
  918. 'weight' => 9930,
  919. //'spinner' => TRUE,
  920. 'attributes' => array('class' => 'content-delete-btn'),
  921. );
  922. }
  923. }
  924. watchdog('content', "edit_content_form cid:$cid, type:$type", array(), WATCHDOG_DEBUG);
  925. return $form;
  926. }
  927. function content_get_uploaded_file($fid) {
  928. $res = db_query("SELECT * FROM content_files WHERE fid = ?", array($fid));
  929. $cur = db_fetch_array($res);
  930. if ($cur) {
  931. $temp = explode(".", $cur['filename']);
  932. $ext = $temp[count($temp) - 1]; // get the extension.
  933. $cur['full_filename'] = fp_get_files_path() . '/content_uploads/' . $cur['filename'];
  934. // also get the URL for downloading this file.
  935. // If this file was encrypted, then the url should change to the url to decrypt this particular file (probably by passing it the fid)
  936. //if (intval($cur['is_encrypted']) === 1) {
  937. // *** Always handle downloads through FlightPath, for security reasons. ***
  938. $cur['url'] = fp_url('content-files/handle-download/' . $cur['fid']);
  939. //}
  940. //else {
  941. // $cur['url'] = base_path() . "/custom/files/content_uploads/" . $cur['filename'];
  942. //}
  943. $cur['ext'] = $ext;
  944. }
  945. return $cur;
  946. }
  947. /**
  948. * Find by mime type OR file extension.
  949. */
  950. function content_get_fontawesome_icon_for_mimetype($mime_type, $ext = "") {
  951. // List of official MIME Types: http://www.iana.org/assignments/media-types/media-types.xhtml
  952. $icon_classes = array(
  953. // Media
  954. 'image' => 'fa-file-image-o',
  955. 'audio' => 'fa-file-audio-o',
  956. 'video' => 'fa-file-video-o',
  957. // Documents
  958. 'application/pdf' => 'fa-file-pdf-o',
  959. 'application/msword' => 'fa-file-word-o',
  960. 'application/vnd.ms-word' => 'fa-file-word-o',
  961. 'application/vnd.oasis.opendocument.text' => 'fa-file-word-o',
  962. 'application/vnd.openxmlformats-officedocument.wordprocessingml' => 'fa-file-word-o',
  963. 'application/vnd.ms-excel' => 'fa-file-excel-o',
  964. 'application/vnd.openxmlformats-officedocument.spreadsheetml' => 'fa-file-excel-o',
  965. 'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel-o',
  966. 'application/vnd.ms-powerpoint' => 'fa-file-powerpoint-o',
  967. 'application/vnd.openxmlformats-officedocument.presentationml' => 'fa-file-powerpoint-o',
  968. 'application/vnd.oasis.opendocument.presentation' => 'fa-file-powerpoint-o',
  969. 'text/plain' => 'fa-file-text-o',
  970. 'text/html' => 'fa-file-code-o',
  971. 'application/json' => 'fa-file-code-o',
  972. // Archives
  973. 'application/gzip' => 'fa-file-archive-o',
  974. 'application/zip' => 'fa-file-archive-o',
  975. );
  976. $rtn = "";
  977. foreach ($icon_classes as $text => $icon) {
  978. if (strpos($mime_type, $text) === 0) {
  979. $rtn = $icon;
  980. }
  981. }
  982. if ($rtn) return $rtn;
  983. // else, we search by file extension now....
  984. $ext_array = array(
  985. 'png' => 'fa-file-image-o',
  986. 'jpg' => 'fa-file-image-o',
  987. 'jpeg' => 'fa-file-image-o',
  988. 'gif' => 'fa-file-image-o',
  989. "mp3" => "fa-file-audio-o",
  990. "mpg" => "fa-file-video-o",
  991. "mpeg" => "fa-file-video-o",
  992. "mp4" => "fa-file-video-o",
  993. 'pdf' => 'fa-file-pdf-o',
  994. 'doc' => 'fa-file-word-o',
  995. 'pages' => 'fa-file-word-o',
  996. 'docx' => 'fa-file-word-o',
  997. 'rtf' => 'fa-file-word-o',
  998. 'odt' => 'fa-file-word-o',
  999. 'xls' => 'fa-file-excel-o',
  1000. 'xlsx' => 'fa-file-excel-o',
  1001. 'numbers' => 'fa-file-excel-o',
  1002. 'csv' => 'fa-file-excel-o',
  1003. 'ppt' => 'fa-file-powerpoint-o',
  1004. 'pptx' => 'fa-file-powerpoint-o',
  1005. 'odp' => 'fa-file-powerpoint-o',
  1006. 'txt' => 'fa-file-text-o',
  1007. 'html' => 'fa-file-code-o',
  1008. 'htm' => 'fa-file-code-o',
  1009. 'json' => 'fa-file-code-o',
  1010. // Archives
  1011. 'gzip' => 'fa-file-archive-o',
  1012. 'zip' => 'fa-file-archive-o',
  1013. );
  1014. foreach ($ext_array as $text => $icon) {
  1015. if (strpos($ext, $text) === 0) {
  1016. $rtn = $icon;
  1017. }
  1018. }
  1019. if ($rtn) return $rtn;
  1020. return 'fa-file-o';
  1021. }
  1022. function content_edit_content_form_validate(&$form, &$form_state) {
  1023. // If we are submitting any files, make sure they have allowed extensions.
  1024. $values = $form_state['values'];
  1025. $type = $values["type"];
  1026. $types = content_get_types();
  1027. foreach ($types[$type]['fields'] as $fieldname => $details) {
  1028. if ($details['type'] != 'cfieldset') {
  1029. // If this field type is "file" then we are going to do something special with it
  1030. // don't need to do this if we are just editing an existing content node and hitting "save"
  1031. if ($details['type'] == 'file') {
  1032. $uploaded_files = fp_re_array_files($_FILES[$fieldname]); // makes them easier to work with.
  1033. $allowed = '';
  1034. if (isset($details['allowed'])) {
  1035. $allowed = trim($details['allowed']);
  1036. }
  1037. if (!$allowed) {
  1038. $allowed = "txt, pdf, doc, docx, xls, xlsx, ppt, pptx, rtf, odt, jpg, jpeg, png, gif, zip, 7z";
  1039. }
  1040. $allowed = str_replace(".", "", strtolower($allowed)); // get rid of dots and make lowercase.
  1041. $allowed = str_replace(" ", "", $allowed);
  1042. $allowed_csv = csv_to_array($allowed);
  1043. $fid = "";
  1044. foreach ($uploaded_files as $file) {
  1045. if (trim($file['name']) == "") continue; // blank, so skip it.
  1046. // Get the extension for the file.
  1047. $temp = explode(".", $file['name']);
  1048. $ext = strtolower(trim($temp[count($temp) - 1]));
  1049. if (!in_array($ext, $allowed_csv)) {
  1050. form_error($fieldname, t("Sorry, but the file %file is not allowed to be uploaded (file extension \"%ext\" not allowed). Please see the list of allowed file types/extensions.", array("%file" => $file['name'], "%ext" => $ext)));
  1051. return;
  1052. }
  1053. } // foreach uploaded files
  1054. } // if a file
  1055. } // if not a cfieldset
  1056. } // foreach
  1057. } // ... validate
  1058. /**
  1059. * Submit handler for the edit content form.
  1060. *
  1061. * @param unknown_type $form
  1062. * @param unknown_type $form_state
  1063. */
  1064. function content_edit_content_form_submit(&$form, &$form_state) {
  1065. global $user;
  1066. $values = $form_state["values"];
  1067. $cid = $values['cid'];
  1068. $type = $values["type"];
  1069. // are we trying to delete?
  1070. if (isset($values['submit_delete']) && trim($values['submit_delete']) != '') {
  1071. // Yes, we are deleting!
  1072. $content = content_load($cid);
  1073. if (!content_user_access('delete', $content->cid)) {
  1074. fp_add_message(t("Sorry, you do not have permission to delete this content."));
  1075. fp_goto("<front>");
  1076. return;
  1077. }
  1078. $content->delete_flag = 1;
  1079. content_save($content);
  1080. watchdog("content", "delete cid:$cid, type:$type", array());
  1081. // find out if we should redirect anywhere in particular for this deletion.
  1082. if (isset($form['#redirect'])) {
  1083. fp_add_message(t("The content was deleted successfully."));
  1084. $form['#redirect']['query'] .= "&del=" . $content->cid;
  1085. }
  1086. else {
  1087. // No redirect was specified, so let's send us to the home page.
  1088. fp_add_message(t("The content was deleted successfully."));
  1089. $form['#redirect']['path'] = "<front>";
  1090. }
  1091. return;
  1092. }
  1093. $now = time();
  1094. $types = content_get_types();
  1095. // Does the user have permission to edit/add this content?
  1096. $access_action = "add";
  1097. $var = $type;
  1098. if ($cid !== "new") {
  1099. $access_action = "edit";
  1100. $var = $cid;
  1101. }
  1102. if (!content_user_access($access_action, $var)) {
  1103. fp_add_message(t("Sorry, you do not have permission to @action that content type.", array("@action" => $access_action)));
  1104. fp_goto("admin-tools/content");
  1105. return;
  1106. }
  1107. // Assemble into a content object, then call content_save();
  1108. if ($values['cid'] == 'new') {
  1109. // This is a NEW content obj!
  1110. $content = new stdClass();
  1111. $content->type = $type;
  1112. $content->cid = "new";
  1113. $content->published = 1;
  1114. $content->delete_flag = 0;
  1115. content_save($content); // Do an initial save, so we get its cid back.
  1116. }
  1117. else {
  1118. // This is an EXISTING content. Load it.
  1119. $content = content_load($values['cid']);
  1120. }
  1121. $cid = $content->cid;
  1122. // Clean the title of any XSS attempts
  1123. $title = filter_markup($values['title']);
  1124. $content->title = $title; // required
  1125. // set published status. 0 for not, 1 for published
  1126. $content->published = (bool) $values['published'];
  1127. // Add in our new field values.
  1128. foreach ($types[$type]['fields'] as $fieldname => $details) {
  1129. if ($details['type'] != 'cfieldset') {
  1130. if ($details['type'] == 'datetime-local') {
  1131. // Since this is datetime-local, the value is actually already in the user's timezone.
  1132. // We want to CHANGE THIS TO UTC, so it saves in the database as UTC.
  1133. $val_ts = @strtotime($values[$fieldname]);
  1134. $utc_ts = convert_time($val_ts, fp_get_user_timezone(), 'UTC');
  1135. $values[$fieldname] = date('Y-m-d H:i:s', $utc_ts);
  1136. }
  1137. // Do the same for "time" field type. We need to convert to UTC from the user's timezone.
  1138. if ($details['type'] == 'time') {
  1139. // Since this is time, the value is actually already in the user's timezone.
  1140. // We want to CHANGE THIS TO UTC, so it saves in the database as UTC.
  1141. $val_ts = @strtotime("2000-01-01 " . $values[$fieldname]); // give a bogus date just so our times make since.
  1142. $utc_ts = convert_time($val_ts, fp_get_user_timezone(), 'UTC');
  1143. $values[$fieldname] = date('H:i', $utc_ts);
  1144. }
  1145. // If this field type is "file" then we are going to do something special with it
  1146. // don't need to do this if we are just editing an existing content node and hitting "save"
  1147. if ($details['type'] == 'file') {
  1148. $uploaded_files = fp_re_array_files($_FILES[$fieldname]); // makes them easier to work with.
  1149. $fid = "";
  1150. foreach ($uploaded_files as $file) {
  1151. if ($file && $file['name'] != '') {
  1152. // Now, save to the content_files table.
  1153. $fid .= content_add_new_uploaded_file($file, $cid) . ",";
  1154. }
  1155. } // foreach
  1156. // Do we have any previously uploaded files we need to include?
  1157. if (@$form_state['values']['previous_upload_' . $fieldname] != "") {
  1158. $fid .= "," . $form_state['values']['previous_upload_' . $fieldname] . ",";
  1159. }
  1160. $fid = ltrim($fid, ",");
  1161. for ($t = 0; $t < 10; $t++) {
  1162. $fid = str_replace(",,", ",", $fid); // get rid of any duplicate commas
  1163. }
  1164. $fid = rtrim($fid, ","); // trim any extra commas
  1165. $values[$fieldname] = $fid;
  1166. } // if details = file
  1167. $content->{'field__' . $fieldname}['value'] = @$values[$fieldname];
  1168. }
  1169. else {
  1170. foreach ($details['elements'] as $c => $v) {
  1171. foreach ($details['elements'][$c] as $fieldname => $edetails) {
  1172. if ($details['type'] == 'datetime-local') {
  1173. // Since this is datetime-local, the value is actually already in the user's timezone.
  1174. // We want to CHANGE THIS TO UTC, so it saves in the database as UTC.
  1175. $val_ts = @strtotime($values[$fieldname]);
  1176. $utc_ts = convert_time($val_ts, fp_get_user_timezone(), 'UTC');
  1177. $values[$fieldname] = date('Y-m-d H:i:s', $utc_ts);
  1178. }
  1179. // Do the same for "time" field type. We need to convert to UTC from the user's timezone.
  1180. if ($details['type'] == 'time') {
  1181. // Since this is time, the value is actually already in the user's timezone.
  1182. // We want to CHANGE THIS TO UTC, so it saves in the database as UTC.
  1183. $val_ts = @strtotime("2000-01-01 " . $values[$fieldname]); // give a bogus date just so our times make since.
  1184. $utc_ts = convert_time($val_ts, fp_get_user_timezone(), 'UTC');
  1185. $values[$fieldname] = date('H:i', $utc_ts);
  1186. }
  1187. $content->{'field__' . $fieldname}['value'] = @$values[$fieldname];
  1188. }
  1189. }
  1190. }
  1191. }
  1192. content_save($content);
  1193. $form_state['content_object'] = $content;
  1194. watchdog("content", "edited/saved cid:$cid, type:$type", array());
  1195. if (isset($form['#redirect'])) {
  1196. if ($form['#redirect']['path'] == 'content/new') {
  1197. $form['#redirect']['path'] = 'content/' . $content->cid;
  1198. }
  1199. }
  1200. fp_add_message(t('Saved successfully.'));
  1201. } // ... submit
  1202. /**
  1203. * bool_replace_existing means should we replace an existing file (based on filename) with this new one? Instead of creating an INSERT, that is.
  1204. */
  1205. function content_add_new_uploaded_file($file, $cid = 0, $bool_override_original_filename = TRUE, $attributes = CONTENT_PRIVATE_FILE, $bool_replace_exsting_filename = FALSE) {
  1206. // Store the file information in the content_files table, and then THIS field contains the fid reference number.
  1207. $original_filename = @$file["name"];
  1208. $type = @$file["type"];
  1209. $tmp_name = @$file["tmp_name"];
  1210. if (!strstr($tmp_name, "/")) {
  1211. // Doesn't contain the temp location. Pre-prend to the tmp_name.
  1212. $tmp_name = sys_get_temp_dir() . "/" . $tmp_name;
  1213. }
  1214. // Figure out the extension of the original filename.
  1215. $temp = explode(".", $original_filename);
  1216. $ext = $temp[count($temp) - 1];
  1217. $filename = $original_filename;
  1218. if ($bool_override_original_filename) {
  1219. $filename = 'file__' . time() . '' . mt_rand(9,9999) . '_' . $original_filename;
  1220. }
  1221. // If the filename is too long, shorten it. Linux won't allow more than 255 bytes (usually corresponds to chars, depending on file system
  1222. // it might be as low as ),
  1223. // Windows its 260 chars. Let's be safe and stop at 100 chars + ext.
  1224. if (strlen($filename) > 100) {
  1225. $filename = substr($filename, 0, 100) . ".$ext";
  1226. }
  1227. $extra = "";
  1228. if ($attributes === CONTENT_PUBLIC_FILE) {
  1229. // If this is a "public" file, then put in the correct public files dir.
  1230. $extra .= "public_uploads/";
  1231. }
  1232. $full_filename = fp_get_files_path() . '/content_uploads/' . $extra . $filename;
  1233. $is_encrypted = 0;
  1234. // If we should be encrypting files, so do here, and set the is_encrypted value correctly.
  1235. if (module_enabled('encryption') && variable_get("encryption_files_encryption", "yes") === 'yes' && encryption_get_key() && $attributes != CONTENT_PUBLIC_FILE) {
  1236. // (note: public files are never encrypted)
  1237. // We need to do that by loading the file into memory, then getting the encrypted version, then writing it
  1238. // out to the destination.
  1239. $file_contents = file_get_contents($tmp_name);
  1240. $enc_file_contents = encryption_encrypt($file_contents);
  1241. $full_filename .= ".enc";
  1242. $filename .= ".enc";
  1243. if (!file_put_contents($full_filename, $enc_file_contents)) {
  1244. fp_add_message(t("Could not upload file. Possibly because of permission issues on the destination directory,
  1245. the disk is full, or some other reason."), "error");
  1246. return;
  1247. }
  1248. $is_encrypted = 1;
  1249. } // encryption module is enabled and we should be encrypting.
  1250. else {
  1251. // No encryption, just copy the normal way.
  1252. $is_encrypted = 0;
  1253. if (!copy($tmp_name, $full_filename)) {
  1254. fp_add_message(t("Could not upload file. Possibly because of permission issues on the destination directory,
  1255. the disk is full, or some other reason."), "error");
  1256. }
  1257. }
  1258. if ($bool_replace_exsting_filename) {
  1259. $fid = db_result(db_query("SELECT fid FROM content_files
  1260. WHERE filename = ?
  1261. AND cid = ?
  1262. AND `attributes` = ?", array($filename, $cid, $attributes)));
  1263. if ($fid) {
  1264. db_query("UPDATE content_files SET posted = ? WHERE fid = ?", array(time(), $fid));
  1265. return $fid;
  1266. }
  1267. else {
  1268. // File wasn't in there. Just do nothing here and proceed to a normal insert.
  1269. }
  1270. }
  1271. // Add to the content_files table.
  1272. db_query("INSERT INTO content_files (cid, original_filename, filename, mimetype, is_encrypted, posted, `attributes`)
  1273. VALUES (?, ?, ?, ?, ?, ?, ?)", array($cid, $original_filename, $filename, $type, $is_encrypted, time(), $attributes));
  1274. return db_insert_id();
  1275. }
  1276. /**
  1277. * Save the content to the database.
  1278. *
  1279. * @param unknown_type $cid
  1280. */
  1281. function content_save(&$content) {
  1282. // Remove from our globals cache
  1283. if ($content->cid != 'new') {
  1284. unset($GLOBALS['content_cache'][$content->cid]);
  1285. }
  1286. $modules = modules_implement_hook("content_save");
  1287. foreach ($modules as $module) {
  1288. call_user_func_array($module . "_content_save", array(&$content));
  1289. }
  1290. // Save to globals cache
  1291. $GLOBALS['content_cache'][$content->cid] = $content;
  1292. }
  1293. /**
  1294. * Implements hook_content_save. We will save the content object to the database.
  1295. */
  1296. function content_content_save(&$content) {
  1297. global $user;
  1298. $types = content_get_types();
  1299. $type = $content->type;
  1300. if (!isset($content->title) || !$content->title || $content->title === NULL || $content->title === FALSE) {
  1301. $content->title = t("Content") . " " . time(); // give a default title, since title cannot be null.
  1302. }
  1303. $log = @trim($content->log);
  1304. // If this is a NEW content, then insert once into content table to get cid. Then, insert into content_versions to get vid. Then update content table with vid.
  1305. if ($content->cid == 'new') {
  1306. db_query('INSERT INTO content (user_id, `type`, title, posted, updated, published, delete_flag, `log`)
  1307. VALUES (?, ?, ?, ?, ?, ?, ?, ?)', array($user->id, $type, $content->title, time(), time(), $content->published, 0, $log));
  1308. $content->cid = db_insert_id();
  1309. // insert into content_versions to get the vid.
  1310. db_query("INSERT INTO content_versions (cid, `user_id`) VALUES (?,?)", array($content->cid, $user->id));
  1311. $content->vid = db_insert_id();
  1312. }
  1313. else {
  1314. // If this is an EXISTING content, then insert once into content_versions to get new vid. Update title and vid in content table.
  1315. // insert into content_versions to get the vid.
  1316. db_query("INSERT INTO content_versions (`cid`, `user_id`) VALUES (?,?)", array($content->cid, $user->id));
  1317. $content->vid = db_insert_id();
  1318. }
  1319. if (!$content->delete_flag) $content->delete_flag = 0; // force content->delete_flag to equal a value.
  1320. // Now we should have a fully created $content object. Let's update the title and other values in the content table.
  1321. db_query("UPDATE content SET vid = ?, title = ?, updated = ?, published = ?, delete_flag = ?, `log` = ? WHERE
  1322. cid = ?", array($content->vid, $content->title, time(), $content->published, $content->delete_flag, $log, $content->cid));
  1323. // TODO: If deleting, should we delete associated files too? Maybe we do this on a cron after X number of months?
  1324. // Now we are going to insert all of the field values. We assume there is a table ALREADY CREATED with our field names
  1325. // as field__the_name.
  1326. // Insert new row into content__$type table, including all the extra fields and such. It should
  1327. // look like cid | vid | field__name | field__name | and so on.
  1328. //
  1329. // Add in our new field values.
  1330. $params = array();
  1331. $all_fields = array();
  1332. $param_names = array();
  1333. $all_fields[] = 'cid';
  1334. $all_fields[] = 'vid';
  1335. $param_names[] = ':cid';
  1336. $param_names[] = ':vid';
  1337. $params[':cid'] = $content->cid;
  1338. $params[':vid'] = $content->vid;
  1339. foreach ($types[$type]['fields'] as $fieldname => $details) {
  1340. if ($details['type'] != 'cfieldset' && $details['type'] != '' && !strstr($details['type'], 'markup')) { // skip markup and fieldsets
  1341. $db_field_name = "field__$fieldname";
  1342. $all_fields[] = $db_field_name;
  1343. $param_names[] = ":$db_field_name";
  1344. $val = NULL;
  1345. if (isset($content->{$db_field_name}) && isset($content->{$db_field_name}['value'])) {
  1346. $val = $content->{$db_field_name}['value'];
  1347. }
  1348. if ($details['type'] == 'checkboxes' && is_array($val)) {
  1349. // Since we came from "checkboxes", and val is an array, we will save the following way:
  1350. // ~~key~~key~~key~~. This lets us query like so: LIKE '%~~key~~%' to find elements with that key. Not the most
  1351. // elegant way to do it, but it should work for now.
  1352. $new_val = "~~"; // always begin with ~~
  1353. foreach ($val as $k => $v) {
  1354. $new_val .= $k . "~~"; // always end with ~~.
  1355. }
  1356. $val = $new_val;
  1357. }
  1358. else if (is_array($val)) {
  1359. // Not sure what this is, but we will serialize it.
  1360. $val = serialize($val);
  1361. }
  1362. $params[":field__$fieldname"] = $val;
  1363. }
  1364. else if ($details['type'] == 'cfieldset'){
  1365. // This is a fieldset, so now we need to descend into its elements to find the fields we need to save.
  1366. foreach($details['elements'] as $c => $v) {
  1367. foreach ($details['elements'][$c] as $fieldname => $edetails) {
  1368. if (strstr($edetails['type'], 'markup') || $edetails['type'] == '') continue; // skip markup
  1369. $db_field_name = "field__$fieldname";
  1370. $all_fields[] = $db_field_name;
  1371. $param_names[] = ":$db_field_name";
  1372. $val = NULL;
  1373. if (isset($content->{$db_field_name}) && isset($content->{$db_field_name}['value'])) {
  1374. $val = $content->{$db_field_name}['value'];
  1375. }
  1376. if ($edetails['type'] == 'checkboxes' && is_array($val)) {
  1377. // Since we came from "checkboxes", and val is an array, we will save the following way:
  1378. // ~~key~~key~~key~~. This lets us query like so: LIKE '%~~key~~%' to find elements with that key. Not the most
  1379. // elegant way to do it, but it should work for now.
  1380. $new_val = "~~"; // always begin with ~~
  1381. foreach ($val as $k => $v) {
  1382. $new_val .= $k . "~~"; // always end with ~~.
  1383. }
  1384. $val = $new_val;
  1385. }
  1386. else if (is_array($val)) {
  1387. // Not sure what this is, but we will serialize it.
  1388. $val = serialize($val);
  1389. }
  1390. $params[":field__$fieldname"] = $val;
  1391. }
  1392. }
  1393. } //else/if
  1394. } // foreach
  1395. $sql = "INSERT INTO content__$type
  1396. (" . join(", " , $all_fields) . ")
  1397. VALUES (" . join(", " , $param_names) . ")";
  1398. db_query($sql, $params);
  1399. // Re-calc our alert counts.
  1400. fp_recalculate_alert_count_by_type();
  1401. watchdog('content', "content_save cid:$content->cid");
  1402. } // content_content_save
  1403. /**
  1404. * Load the content from the database and return an array, by calling hook_content_load.
  1405. *
  1406. * @param unknown_type $cid
  1407. */
  1408. function content_load($cid, $reset = FALSE) {
  1409. if (!$cid) return null;
  1410. if ($reset) {
  1411. unset($GLOBALS['content_cache'][$cid]);
  1412. }
  1413. if (isset($GLOBALS['content_cache'][$cid])) {
  1414. return $GLOBALS['content_cache'][$cid];
  1415. }
  1416. $content = new stdClass();
  1417. $content->cid = $cid;
  1418. $modules = modules_implement_hook("content_load");
  1419. foreach ($modules as $module) {
  1420. call_user_func_array($module . "_content_load", array(&$content));
  1421. }
  1422. // Add to our globals cache
  1423. $GLOBALS['content_cache'][$cid] = $content;
  1424. return $content;
  1425. }
  1426. /**
  1427. * Hook form alter, to set some defaults.
  1428. */
  1429. function content_form_alter(&$form, $form_id) {
  1430. if ($form_id == 'content_edit_content_form') {
  1431. // This is a NEW form.
  1432. if ($form['cid']['value'] === 'new') {
  1433. // Always set the published = TRUE, and hide.
  1434. $form['published']['value'] = TRUE;
  1435. $form['published']['attributes'] = array('class' => 'hidden');
  1436. // Set visibility to 'faculty' by default, if it exists.
  1437. if (isset($form['visibility'])) {
  1438. $form['visibility']['value'] = 'faculty';
  1439. }
  1440. } // if "new"
  1441. } // if form_id
  1442. } // hook_form_alter
  1443. /**
  1444. * This function will look for content (in the content tables) for a field called field__faculty_id, and return an array
  1445. * of fully loaded content objects.
  1446. */
  1447. function content_get_content_for_faculty_id($content_type, $faculty_id, $order_by = "title ASC", $bool_published_only = TRUE) {
  1448. $rtn = array();
  1449. $published_line = " AND n.published = 1 ";
  1450. if (!$bool_published_only) $published_line = "";
  1451. // Display all of the event types for this faculty member.
  1452. $res = db_query("SELECT DISTINCT(a.cid) FROM content__$content_type a, content n
  1453. WHERE field__faculty_id = ?
  1454. AND a.vid = n.vid
  1455. AND a.cid = n.cid
  1456. AND n.delete_flag = 0
  1457. $published_line
  1458. ORDER BY $order_by, a.vid DESC", array($faculty_id));
  1459. while ($cur = db_fetch_object($res)) {
  1460. $cid = $cur->cid;
  1461. $content = content_load($cid);
  1462. $rtn[$cid] = $content;
  1463. }
  1464. return $rtn;
  1465. }
  1466. /**
  1467. * Implementation of content's hook_content_load
  1468. *
  1469. * We simply wish to load from our db table. The cid is in
  1470. * $content->cid.
  1471. *
  1472. * @param unknown_type $content
  1473. */
  1474. function content_content_load(&$content) {
  1475. $res = db_query("SELECT * FROM content WHERE cid = ?
  1476. ORDER BY vid DESC
  1477. LIMIT 1", array( $content->cid));
  1478. $content = db_fetch_object($res);
  1479. if (!$content) return FALSE;
  1480. // Populate our field values in the content object
  1481. $res = db_query("SELECT * FROM content__$content->type WHERE vid = ?", array($content->vid));
  1482. $fieldvals = db_fetch_array($res);
  1483. $types = content_get_types();
  1484. // This condition can occur if we have content from a module which is no longer enables.
  1485. if (!isset($types[$content->type])) {
  1486. return;
  1487. }
  1488. foreach ($types[$content->type]['fields'] as $fieldname => $details) {
  1489. if ($details['type'] != 'cfieldset') {
  1490. $val = @$fieldvals['field__' . $fieldname];
  1491. if (is_serialized_string($val)) {
  1492. $val = unserialize($val);
  1493. }
  1494. $content->{'field__' . $fieldname} = array('value' => $val, 'field' => $details);
  1495. $display_value = $val;
  1496. // Figure out or clean up the "display_value" for this field.
  1497. if (isset($details['filter']) && $details['filter'] != 'plain') {
  1498. $display_value = filter_markup(trim($display_value), $details['filter']);
  1499. }
  1500. else {
  1501. // Always assumed to be "plain" if no filter is set.
  1502. $display_value = filter_markup($display_value, "plain");
  1503. // Need to add in line breaks if this was plain.
  1504. $display_value = nl2br(trim($display_value));
  1505. }
  1506. // If this is a datetime-local field, then the value needs to be adjusted for it to work correctly.
  1507. if ($details['type'] == 'datetime-local') {
  1508. if (trim($display_value) != '') {
  1509. //$value = date('Y-m-d\TH:i', strtotime($value));
  1510. $format = 'standard';
  1511. if (isset($details['format_date'])) {
  1512. $format = $details['format_date'];
  1513. }
  1514. $display_value = format_date(convert_time(strtotime($display_value)), $format);
  1515. }
  1516. }
  1517. // if this is a "time" field, then similar to datetime-local, it needs to be adjusted. That's because
  1518. // in the database we saved it as UTC. We ned to now convert into the user's timezone.
  1519. if ($details['type'] == 'time') {
  1520. if (trim($display_value) != '') {
  1521. $format = 'standard';
  1522. if (isset($details['format_date'])) {
  1523. $format = $details['format_date'];
  1524. }
  1525. $display_value = format_date(convert_time(strtotime("2001-01-01 $display_value")), 'custom', 'g:ia');
  1526. }
  1527. }
  1528. // if this is a select list, we want to display the option label this value went with.
  1529. if ($details['type'] == 'select' || $details['type'] == 'radios') {
  1530. $temp = @$details['options'][$val];
  1531. if ($temp != "") $display_value = $temp;
  1532. }
  1533. // If this is a single checkbox, then the possible values are 1 or blank. Let's make the display_value a little more interesting
  1534. if ($details['type'] == 'checkbox') {
  1535. $val = intval($val);
  1536. if ($val === 1) {
  1537. $display_value = t("Yes / Checked");
  1538. }
  1539. else {
  1540. $display_value = t("No / Unchecked");
  1541. }
  1542. }
  1543. // If this was a set of checkboxes ($val is an array), we want to display the label the values went to.
  1544. if ($details['type'] == 'checkboxes') {
  1545. $display_value = "<div class='checkbox-values checkbox-values-field__$fieldname'>";
  1546. // Convert val into an array. It will look like: ~~key~~key~~key~~ etc.
  1547. @$temp = explode("~~", $val);
  1548. if (!is_array($temp)) $temp = array();
  1549. $val = array();
  1550. foreach ($temp as $k) {
  1551. if (trim($k) == "") continue;
  1552. $val[$k] = $k;
  1553. }
  1554. if (is_array($val)) {
  1555. foreach ($val as $k => $v) {
  1556. $temp = @$details['options'][$v];
  1557. if ($temp != "") $display_value .= "<div class='checkbox-val'>$temp</div>";
  1558. }
  1559. }
  1560. $display_value .= "</div>";
  1561. $content->{'field__' . $fieldname}['value'] = $val;
  1562. }
  1563. if ($details['type'] == 'file') {
  1564. if ($val != "") {
  1565. $display_value = "";
  1566. $fids = explode(",", $val);
  1567. foreach ($fids as $fid) {
  1568. if ($fid == '') continue;
  1569. $fid = intval($fid);
  1570. $file = content_get_uploaded_file($fid);
  1571. if ($file) {
  1572. $url = $file['url'];
  1573. $display_value .= "<div class='field-file-attachment-wrapper'><a href='$url'>" . $file['original_filename'] . "</a></div>";
  1574. }
  1575. }
  1576. }
  1577. }
  1578. $content->{'field__' . $fieldname}['display_value'] = $display_value;
  1579. } // type != cfieldset
  1580. else {
  1581. // This is a fieldset, so we need to go into all its elements.
  1582. foreach ($details['elements'] as $c => $v) {
  1583. foreach ($details['elements'][$c] as $fieldname => $edetails) {
  1584. $val = @$fieldvals['field__' . $fieldname];
  1585. if (is_serialized_string($val)) {
  1586. $val = unserialize($val);
  1587. }
  1588. $content->{'field__' . $fieldname} = array('value' => $val, 'field' => $details);
  1589. $display_value = $val;
  1590. if (isset($edetails['filter']) && $edetails['filter'] != 'plain') {
  1591. $display_value = filter_markup(trim($display_value), $edetails['filter']);
  1592. }
  1593. else {
  1594. // Always assumed to be "plain" if no filter is set.
  1595. $display_value = filter_markup($display_value, "plain");
  1596. // Need to add in line breaks if this was plain.
  1597. $display_value = nl2br(trim($display_value));
  1598. }
  1599. // If this is a datetime-local field, then the value needs to be adjusted for it to work correctly.
  1600. if ($edetails['type'] == 'datetime-local') {
  1601. if (trim($display_value) != '') {
  1602. //$value = date('Y-m-d\TH:i', strtotime($value));
  1603. $format = 'standard';
  1604. if (isset($details['format_date'])) {
  1605. $format = $details['format_date'];
  1606. }
  1607. $display_value = format_date(convert_time(strtotime($display_value)), $format);
  1608. }
  1609. }
  1610. // if this is a "time" field, then similar to datetime-local, it needs to be adjusted. That's because
  1611. // in the database we saved it as UTC. We ned to now convert into the user's timezone.
  1612. if ($edetails['type'] == 'time') {
  1613. if (trim($display_value) != '') {
  1614. $format = 'standard';
  1615. if (isset($details['format_date'])) {
  1616. $format = $details['format_date'];
  1617. }
  1618. $display_value = format_date(convert_time(strtotime("2001-01-01 $display_value")), 'custom', 'g:ia');
  1619. }
  1620. }
  1621. // if this is a select list, we want to display the option label this value went with.
  1622. if ($edetails['type'] == 'select') {
  1623. $temp = @$edetails['options'][$val];
  1624. if ($temp != "") $display_value = $temp;
  1625. }
  1626. if ($edetails['type'] == 'file') {
  1627. $fid = intval($val);
  1628. $file = content_get_uploaded_file($fid);
  1629. if ($file) {
  1630. $url = $file['url'];
  1631. $display_value = "<a href='$url'>" . $file['original_filename'] . "</a>";
  1632. }
  1633. }
  1634. // If this was a set of checkboxes ($val is an array), we want to display the label the values went to.
  1635. if ($edetails['type'] == 'checkboxes') {
  1636. $display_value = "<div class='checkbox-values checkbox-values-field__$fieldname'>";
  1637. // Convert val into an array. It will look like: ~~key~~key~~key~~ etc.
  1638. $temp = explode("~~", $val);
  1639. $val = array();
  1640. foreach ($temp as $k) {
  1641. if (trim($k) == "") continue;
  1642. $val[$k] = $k;
  1643. }
  1644. if (is_array($val)) {
  1645. foreach ($val as $k => $v) {
  1646. $temp = @$details['options'][$v];
  1647. if ($temp != "") $display_value .= "<div class='checkbox-val'>$temp</div>";
  1648. }
  1649. }
  1650. $display_value .= "</div>";
  1651. $content->{'field__' . $fieldname}['value'] = $val;
  1652. }
  1653. $content->{'field__' . $fieldname}['display_value'] = $display_value;
  1654. }
  1655. }
  1656. } // else type == cfielset
  1657. } // foreach
  1658. }
  1659. /**
  1660. * hook_cron
  1661. */
  1662. function content_cron() {
  1663. // We want to delete any content (and files!) which have been scheduled for deletion.
  1664. // Check to see when the last time we did this. If less than X number of days, we don't do it.
  1665. $last_run = intval(variable_get("content_last_run_delete_flag_removal", 0));
  1666. $check_against = strtotime("NOW - 7 DAYS"); // don't run any more often than once every 7 DAYS.
  1667. $delete_threshold = strtotime("NOW - 7 DAYS"); // delete content older than 7 days.
  1668. $c = 0;
  1669. if ($check_against > $last_run) {
  1670. $res = db_query("SELECT * FROM content
  1671. WHERE delete_flag = 1
  1672. AND `updated` < ?", array($delete_threshold));
  1673. while ($cur = db_fetch_object($res)) {
  1674. $cid = $cur->cid;
  1675. $type = $cur->type;
  1676. // Sanitize $type as best we can. Should be fine coming from database, but just in case.
  1677. $type = str_replace("`", "", $type);
  1678. $type = str_replace("'", "", $type);
  1679. $type = str_replace('"', "", $type);
  1680. $type = str_replace(' ', "", $type);
  1681. $type = str_replace('(', "", $type);
  1682. // First, are there any files associated with this cid we need to delete?
  1683. $res2 = db_query("SELECT * FROM content_files WHERE cid = ?", array($cid));
  1684. while ($cur2 = db_fetch_object($res2)) {
  1685. $fid = $cur2->fid;
  1686. $full_filename = fp_get_files_path() . '/content_uploads/' . $cur2->filename;
  1687. if (!unlink($full_filename)) {
  1688. watchdog("content", "Could not delete $full_filename for content $cid", array(), WATCHDOG_ERROR);
  1689. fpm("Could not delete $full_filename for content $cid");
  1690. }
  1691. db_query("DELETE FROM content_files WHERE fid = ?", array($fid));
  1692. }
  1693. // Now, delete the content from the table it belongs to...
  1694. db_query("DELETE FROM `content__$type` WHERE cid = ?", array($cid));
  1695. // Delete from content_versions and content_last_access, engagements_tracking
  1696. db_query("DELETE FROM content_versions WHERE cid = ?", array($cid));
  1697. db_query("DELETE FROM content_last_access WHERE cid = ?", array($cid));
  1698. db_query("DELETE FROM engagements_tracking WHERE cid = ?", array($cid));
  1699. // Lastly, delete from content table itself.
  1700. db_query("DELETE FROM content WHERE cid = ?", array($cid));
  1701. watchdog("content", "Deleted from db deleted content $cid", array(), WATCHDOG_DEBUG);
  1702. $c++;
  1703. } // while cur
  1704. watchdog("content", "Delete from db complete. $c items removed.", array(), WATCHDOG_DEBUG);
  1705. variable_set("content_last_run_delete_flag_removal", time());
  1706. } // check against > last_run, so we should do it.
  1707. } // hook_cron
  1708. /**
  1709. * Display a list of content for the administrator
  1710. */
  1711. function content_display_content_admin_list() {
  1712. global $user;
  1713. $rtn = "";
  1714. //fp_add_css(fp_get_module_path("content") . "/css/content.css");
  1715. fp_set_title('');
  1716. $rtn .= "<br><b>" .t("Create new content:") . "</b>
  1717. <table border='1' width='100%' cellpadding='4'>";
  1718. // Go through all available content types and provide a link.
  1719. $types = content_get_types();
  1720. // We expect types to be returned like this:
  1721. // ["page"]["title"] = "Basic Page";
  1722. // ["page"]["description"] = "This is a standard page.";
  1723. foreach ($types as $type => $details) {
  1724. if (user_has_permission("add_$type" . "_content")) {
  1725. $rtn .= "<tr>
  1726. <td>" . l($details["title"], "content/add/$type") . "</td>
  1727. <td>" . $details["description"] . "</td></tr>";
  1728. }
  1729. }
  1730. $rtn .= "</table>
  1731. <hr>";
  1732. // Show a table of current content in the system.
  1733. $rtn .= "<b>" . t("Existing content:") . "</b>";
  1734. @$filter = $_GET["filter"];
  1735. $rtn .= "<form action='" . fp_url("admin-tools/content") . "' method='GET'>
  1736. " . t("Display type:") . " &nbsp; <select name='filter'>
  1737. <option value=''>" . t(" -- Any --") . "</option>";
  1738. foreach ($types as $type => $details) {
  1739. $sel = ($type == $filter) ? "selected" : "";
  1740. $rtn .= "<option value='$type' $sel>" . $details["title"] . "</option>";
  1741. }
  1742. $rtn .= "</select><input type='submit' value='-&gt;'> </form>";
  1743. $table_headers = array();
  1744. $table_headers[] = array("label" => "Actions");
  1745. $table_headers[] = array("label" => "Title", "field" => "n.title");
  1746. $table_headers[] = array("label" => "Type", "field" => "n.type");
  1747. $table_headers[] = array("label" => "Published", "field" => "n.published");
  1748. $table_headers[] = array("label" => "Author");
  1749. $table_headers[] = array("label" => "Updated", "field" => "n.updated");
  1750. // Set our initial sort, if none is already set.
  1751. theme_table_header_sortable_set_initial_sort('n.updated', 'DESC');
  1752. $rtn .= "<table width='100%' border='1'>";
  1753. // Draw our our table headers, with links....
  1754. $rtn .= theme_table_header_sortable($table_headers);
  1755. $params = array();
  1756. $filter_line = "";
  1757. if ($filter != "") {
  1758. $filter_line = "AND type = :ftype";
  1759. $params[":ftype"] = $filter;
  1760. }
  1761. $content_types = content_get_types();
  1762. // Only look for types which have been defined (nothing for modules we have since disabled)
  1763. $types_line = "AND `type` IN ('" . join("','", array_keys($content_types)) . "')";
  1764. // Get our order by clause based on selected table header, if any.
  1765. $order_by = theme_table_header_sortable_order_by($table_headers);
  1766. $res = pager_query("SELECT DISTINCT(cid) FROM content n
  1767. WHERE delete_flag = 0
  1768. $types_line
  1769. $filter_line
  1770. $order_by", $params, 20, 0, "SELECT COUNT(DISTINCT(cid)) FROM content n
  1771. WHERE delete_flag = 0
  1772. $types_line
  1773. $filter_line
  1774. $order_by", $params);
  1775. while ($cur = db_fetch_array($res)) {
  1776. $content = content_load($cur['cid']);
  1777. $author = fp_load_user($content->user_id);
  1778. $author_name = "";
  1779. if ($author && isset($author->name)) {
  1780. $author_name = $author->name;
  1781. }
  1782. $updated = format_date(convert_time($content->updated));
  1783. $view_link = $edit_link = "";
  1784. $view_link = l("<i class='fa fa-eye'></i>", "content/$content->cid");
  1785. if ((user_has_permission("edit_any_{$content->type}" . "_content")) || (user_has_permission("edit_own_{$content->type}" . "_content") && intval($content->user_id) === intval($user->id) && intval($user->id) > 0)) {
  1786. $edit_link = l("<i class='fa fa-pencil'></i>", "content/{$cur["cid"]}/edit");
  1787. }
  1788. $rtn .= "<tr>
  1789. <td>$view_link &nbsp; $edit_link</td>
  1790. <td valign='top'>" . $content->title . "</td>";
  1791. $rtn .= "
  1792. <td valign='top'>" . $content->type . "</td>
  1793. <td valign='top'>" . $content->published . "</td>
  1794. <td valign='top'>" . $author_name . "</td>
  1795. <td valign='top'>" . $updated . "</td>
  1796. </tr>";
  1797. }
  1798. $rtn .= "</table>";
  1799. // Display the pager that was generated by the pager_query above!
  1800. $rtn .= theme_pager();
  1801. return $rtn;
  1802. }
  1803. /**
  1804. * Return an array with all the possible content types known to FlightPath
  1805. *
  1806. * Modules may declare their content types using the hook_content_register_content_type hook.
  1807. * You should clear the cache after creating a new content type.
  1808. *
  1809. */
  1810. function content_get_types() {
  1811. $rtn = array();
  1812. // Save effort by saving to a globals cache.
  1813. if (isset($GLOBALS['content_types_cache'])) {
  1814. return $GLOBALS['content_types_cache'];
  1815. }
  1816. $modules = modules_implement_hook("content_register_content_type");
  1817. foreach($modules as $module) {
  1818. $types = call_user_func($module . '_content_register_content_type');
  1819. $rtn += $types;
  1820. }
  1821. $GLOBALS['content_types_cache'] = $rtn;
  1822. return $rtn;
  1823. }
  1824. /**
  1825. * Implementation of this module's hook_content_register_content_type.
  1826. *
  1827. * I mainly just want to register the "page" content type, for a basic
  1828. * web page set up.
  1829. *
  1830. */
  1831. function content_content_register_content_type() {
  1832. $arr['page'] = array(
  1833. 'title' => 'Page',
  1834. 'description' => 'This is a basic, publicly visible web page.',
  1835. );
  1836. $fields['body'] = array(
  1837. 'type' => 'textarea_editor',
  1838. 'label' => '',
  1839. 'filter' => 'basic',
  1840. );
  1841. $arr['page']['fields'] = $fields;
  1842. return $arr;
  1843. }
  1844. /**
  1845. * Return the HTML rendering the content we have in the database.
  1846. */
  1847. function content_render_content($bool_show_delete = FALSE) {
  1848. $rtn = "";
  1849. $res = db_query("SELECT * FROM content WHERE delete_flag = 0
  1850. ORDER BY posted DESC");
  1851. while ($cur = db_fetch_array($res)) {
  1852. $rtn .= "<div class='content'>
  1853. <div class='content-text'>" . filter_markup($cur["content"], "full") . "</div>
  1854. <div class='content-posted'>" . t("Posted") . " " . date("D, M jS Y - g:ia", $cur["posted"]) . "</div>";
  1855. if ($bool_show_delete) {
  1856. $delete_link = "";
  1857. $delete_link = fp_get_js_confirm_link(t("Are you sure you wish to delete this content?"), 'window.location="' . fp_url("content/delete", "aid=" . $cur["aid"]) . '"', t("Delete?"));
  1858. $rtn .= "<div class='content-delete'>$delete_link</div>";
  1859. }
  1860. $rtn .= "</div>";
  1861. }
  1862. return $rtn;
  1863. }
  1864. /**
  1865. * Implementation of hook_perm
  1866. *
  1867. * We want to create a permission for every content type.
  1868. *
  1869. */
  1870. function content_perm() {
  1871. $rtn = array();
  1872. $rtn["admin_content"] = array(
  1873. "title" => t("Administer Content"),
  1874. "description" => t("The user needs this permission to access the content page at all, in
  1875. addition to specific permissions listed below for editing
  1876. different content types."),
  1877. );
  1878. $rtn["admin_public_files"] = array(
  1879. "title" => t("Administer Public Files"),
  1880. "description" => t("The user is allowed to upload, modify, or delete 'public files' which are stored in a special location
  1881. on the web server. Only give this permission to admin or developer users."),
  1882. );
  1883. $types = content_get_types();
  1884. foreach ($types as $type => $details) {
  1885. $rtn ["add_$type" . "_content"] = array(
  1886. "title" => t("Add new") . " " . $details["title"] . " " . t("content"),
  1887. );
  1888. $rtn ["view_$type" . "_content"] = array(
  1889. "title" => "&nbsp; &nbsp; " . t("View published") . " " . $details["title"] . " " . t("content"),
  1890. );
  1891. $rtn ["edit_any_$type" . "_content"] = array(
  1892. "title" => "&nbsp; &nbsp; " . t("Edit any") . " " . $details["title"] . " " . t("content"),
  1893. );
  1894. $rtn ["edit_own_$type" . "_content"] = array(
  1895. "title" => "&nbsp; &nbsp; " . t("Edit own") . " " . $details["title"] . " " . t("content"),
  1896. );
  1897. $rtn ["delete_any_$type" . "_content"] = array(
  1898. "title" => "&nbsp; &nbsp; " . t("Delete any") . " " . $details["title"] . " " . t("content"),
  1899. );
  1900. $rtn ["delete_own_$type" . "_content"] = array(
  1901. "title" => "&nbsp; &nbsp; " . t("Delete own") . " " . $details["title"] . " " . t("content"),
  1902. );
  1903. }
  1904. return $rtn;
  1905. }
  1906. /**
  1907. * hook_blocks. Returns an array of available blocks offered by this module in this format:
  1908. * array(
  1909. * delta => "This is the title of the block.",
  1910. * ),
  1911. * );
  1912. *
  1913. * Delta can be just about anything machine-readable. Alphanumeric and underscores only.
  1914. * Ex: 0, 1, fun_2, etc.
  1915. *
  1916. */
  1917. function content_blocks() {
  1918. return array(
  1919. "primary" => t("Primary content block"),
  1920. );
  1921. }
  1922. /**
  1923. * Called when it is time to render the block in question.
  1924. * Expected to return an array which looks like this:
  1925. * array(
  1926. * "title" => "Some title goes here.",
  1927. * "body" => "this is the primary body of the block",
  1928. * );
  1929. */
  1930. function content_render_block($delta) {
  1931. $rtn = array();
  1932. if ($delta == "primary") {
  1933. fp_add_css(fp_get_module_path("content") . "/css/content.css");
  1934. $rtn["title"] = t("content");
  1935. $rtn["body"] = content_render_content();
  1936. }
  1937. return $rtn;
  1938. }

Functions

Namesort descending Description
content_add_new_uploaded_file bool_replace_existing means should we replace an existing file (based on filename) with this new one? Instead of creating an INSERT, that is.
content_blocks hook_blocks. Returns an array of available blocks offered by this module in this format: array( delta => "This is the title of the block.", ), );
content_content_load Implementation of content's hook_content_load
content_content_register_content_type Implementation of this module's hook_content_register_content_type.
content_content_save Implements hook_content_save. We will save the content object to the database.
content_cron hook_cron
content_dialog_handle_after_save This is the URL we redirect to after saving a piece of content in a dialog, so all we really want to do is close the dialog and reload the parent page.
content_display_content_admin_list Display a list of content for the administrator
content_display_devel Simply returns the fpm() results for the supplied content.
content_edit_content_form This form lets the user edit some piece of content
content_edit_content_form_submit Submit handler for the edit content form.
content_edit_content_form_validate
content_files_handle_download This actually finds and downloads the file for the user, decrypting if necessary.
content_files_user_may_download_file Returns TRUE or FALSE if the user has access to download this particular student's file.
content_form_alter Hook form alter, to set some defaults.
content_get_content_for_faculty_id This function will look for content (in the content tables) for a field called field__faculty_id, and return an array of fully loaded content objects.
content_get_fontawesome_icon_for_mimetype Find by mime type OR file extension.
content_get_last_access Returns the timestamp of last access, or ZERO if never accessed
content_get_types Return an array with all the possible content types known to FlightPath
content_get_uploaded_file
content_load Load the content from the database and return an array, by calling hook_content_load.
content_menu
content_menu_handle_replacement_pattern This is an implementation of hook_menu_handle_replacement_pattern. It will search for and replace replacement patterns which we are aware of it in $str.
content_perm Implementation of hook_perm
content_public_files_form This screen lets the user upload/manage/delete "public files" stored at custom/files/content_uploads/public_uploads/
content_public_files_form_submit
content_public_files_form_validate
content_render_block Called when it is time to render the block in question. Expected to return an array which looks like this: array( "title" => "Some title goes here.", "body" => "this is the primary body of the block", );
content_render_content Return the HTML rendering the content we have in the database.
content_save Save the content to the database.
content_set_last_access Sets the content_last_access timestamp for this user and content. Should be called whenever a "content" node is viewed by the user.
content_unpublish_content_form
content_unpublish_content_form_submit
content_user_access Custom user access function to determine if the user can add, edit, etc, the content
content_view_content Display the content specified in the GET's cid.
_content_generate_create_table_sql Meant to be run by admin, this generates table creation sql for a given type.

Constants