admin.module

  1. 7.x modules/admin/admin.module
  2. 6.x modules/admin/admin.module
  3. 5.x modules/admin/admin.module

The administrative configurations for FlightPath.

File

modules/admin/admin.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * The administrative configurations for FlightPath.
  5. *
  6. *
  7. */
  8. /**
  9. * Implementation of hook_menu
  10. *
  11. */
  12. function admin_menu() {
  13. $items = array();
  14. $items["admin-tools"] = array(
  15. "title" => "Admin Tools",
  16. "page_callback" => "admin_display_tools_screen",
  17. "access_arguments" => array("can_access_admin_tools"),
  18. "tab_family" => "admin",
  19. "page_settings" => array(
  20. "page_hide_report_error" => TRUE,
  21. ),
  22. "type" => MENU_TYPE_NORMAL_ITEM,
  23. );
  24. $items["admin-tools/admin"] = array(
  25. "title" => t("FlightPath Admin Console"),
  26. "description" => t("This area contains the bulk of settings, degree entry, and other configurations for FlightPath."),
  27. "page_callback" => "admin_display_main",
  28. "access_arguments" => array("can_access_admin"),
  29. "page_settings" => array(
  30. "menu_links" => array(
  31. 0 => array(
  32. "text" => "Admin Tools",
  33. "path" => "admin-tools",
  34. ),
  35. ),
  36. "menu_icon" => fp_get_module_path('system') . "/icons/FlightPath_Academics_plane_only.png",
  37. ),
  38. 'weight' => 999,
  39. "type" => MENU_TYPE_NORMAL_ITEM,
  40. );
  41. $items["admin/config/urgent-message"] = array(
  42. "title" => "Edit urgent message",
  43. "description" => "Set a message which will be displayed to all users on every page",
  44. "page_callback" => "fp_render_form",
  45. "page_arguments" => array("admin_urgent_message_form", "system_settings"),
  46. "access_arguments" => array("can_edit_urgent_message"),
  47. "page_settings" => array(
  48. "menu_icon" => fp_get_module_path('admin') . "/icons/error.png",
  49. "page_hide_report_error" => TRUE,
  50. "menu_links" => array(
  51. 0 => array(
  52. "text" => "Admin Console",
  53. "path" => "admin-tools/admin",
  54. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  55. ),
  56. ),
  57. ),
  58. "type" => MENU_TYPE_NORMAL_ITEM,
  59. );
  60. $items["admin/config/watchdog"] = array(
  61. "title" => "Watchdog (logs)",
  62. "description" => "View log messages from throughout the system",
  63. "page_callback" => "admin_display_watchdog",
  64. "access_arguments" => array("display_watchdog"),
  65. "page_settings" => array(
  66. "menu_icon" => fp_get_module_path('system') . "/icons/application_view_list.png",
  67. "page_show_title" => TRUE,
  68. "page_hide_report_error" => TRUE,
  69. "menu_links" => array(
  70. 0 => array(
  71. "text" => "Admin Console",
  72. "path" => "admin-tools/admin",
  73. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  74. ),
  75. ),
  76. ),
  77. "type" => MENU_TYPE_NORMAL_ITEM,
  78. );
  79. $items["admin/config/watchdog/%"] = array(
  80. "title" => "View Watchdog Entry",
  81. "page_callback" => "admin_display_watchdog_entry",
  82. "page_arguments" => array(3),
  83. "access_arguments" => array("display_watchdog"),
  84. "page_settings" => array(
  85. "page_show_title" => TRUE,
  86. "page_hide_report_error" => TRUE,
  87. "menu_links" => array(
  88. 0 => array(
  89. "text" => "Admin Console",
  90. "path" => "admin-tools/admin",
  91. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  92. ),
  93. 1 => array(
  94. "text" => "Watchdog",
  95. "path" => "admin/config/watchdog",
  96. "query" => "de_catalog_year=%DE_CATALOG_YEAR%&sev_filter=%SEV_FILTER%&page=%PAGE%&type_filter=%TYPE_FILTER%",
  97. ),
  98. ),
  99. ),
  100. "type" => MENU_TYPE_CALLBACK,
  101. );
  102. $items["admin/duplicate-year"] = array(
  103. "title" => "Duplicate entire catalog year",
  104. "page_callback" => "fp_render_form",
  105. "page_arguments" => array("admin_duplicate_year_form"),
  106. "access_arguments" => array("can_edit_data_entry"),
  107. "page_settings" => array(
  108. "page_hide_report_error" => TRUE,
  109. "menu_links" => array(
  110. 0 => array(
  111. "text" => "Admin Console",
  112. "path" => "admin-tools/admin",
  113. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  114. ),
  115. ),
  116. ),
  117. "type" => MENU_TYPE_NORMAL_ITEM,
  118. "tab_parent" => "admin-tools/admin",
  119. );
  120. $items["admin/edit-advising-settings"] = array(
  121. "title" => "Edit advising settings",
  122. "page_callback" => "fp_render_form",
  123. "page_arguments" => array("admin_advising_settings_form", "system_settings"),
  124. "access_arguments" => array("can_edit_advising_settings"),
  125. "page_settings" => array(
  126. "page_hide_report_error" => TRUE,
  127. "menu_links" => array(
  128. 0 => array(
  129. "text" => "Admin Console",
  130. "path" => "admin-tools/admin",
  131. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  132. ),
  133. ),
  134. ),
  135. "type" => MENU_TYPE_NORMAL_ITEM,
  136. "tab_parent" => "admin-tools/admin",
  137. );
  138. $items["admin/apply-draft-changes"] = array(
  139. "title" => "Apply draft changes",
  140. "page_callback" => "fp_render_form",
  141. "page_arguments" => array("admin_apply_draft_changes_form"),
  142. "access_arguments" => array("can_apply_draft_changes"),
  143. "page_settings" => array(
  144. "page_hide_report_error" => TRUE,
  145. "menu_links" => array(
  146. 0 => array(
  147. "text" => "Admin Console",
  148. "path" => "admin-tools/admin",
  149. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  150. ),
  151. ),
  152. ),
  153. "type" => MENU_TYPE_NORMAL_ITEM,
  154. "tab_parent" => "admin-tools/admin",
  155. );
  156. ///////////////////////////////////////////////////////
  157. ////////////////////// Degree Editing ///////////////////
  158. $items["admin/degrees"] = array(
  159. "title" => "Degrees",
  160. "page_callback" => "admin_display_degrees",
  161. "access_arguments" => array("can_access_data_entry"),
  162. "page_settings" => array(
  163. "page_hide_report_error" => TRUE,
  164. "menu_links" => array(
  165. 0 => array(
  166. "text" => "Admin Console",
  167. "path" => "admin-tools/admin",
  168. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  169. ),
  170. ),
  171. ),
  172. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  173. "type" => MENU_TYPE_NORMAL_ITEM,
  174. );
  175. $items["admin/degrees/edit-degree/%/%"] = array(
  176. "title" => "Edit Degree",
  177. "page_callback" => "fp_render_form",
  178. "page_arguments" => array("admin_edit_degree_form", "normal", 3, 4),
  179. "access_arguments" => array("can_edit_data_entry"),
  180. "page_settings" => array(
  181. "page_hide_report_error" => TRUE,
  182. "menu_links" => array(
  183. 0 => array(
  184. "text" => "Admin Console",
  185. "path" => "admin-tools/admin",
  186. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  187. ),
  188. 1 => array(
  189. "text" => "Degrees",
  190. "path" => "admin/degrees",
  191. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  192. ),
  193. ),
  194. ),
  195. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  196. "type" => MENU_TYPE_NORMAL_ITEM,
  197. );
  198. $items["admin/degrees/popup-add-group"] = array(
  199. "title" => "Add Group",
  200. "page_callback" => "admin_display_degrees_popup_add_group",
  201. "access_arguments" => array("can_edit_data_entry"),
  202. "page_settings" => array(
  203. "page_is_popup" => TRUE,
  204. "page_hide_report_error" => TRUE,
  205. ),
  206. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  207. "type" => MENU_TYPE_CALLBACK,
  208. );
  209. $items["admin/degrees/popup-add-group2"] = array(
  210. "title" => "Add Group",
  211. "page_callback" => "admin_display_degrees_popup_add_group2",
  212. "access_arguments" => array("can_edit_data_entry"),
  213. "page_settings" => array(
  214. "page_is_popup" => TRUE,
  215. "page_hide_report_error" => TRUE,
  216. ),
  217. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  218. "type" => MENU_TYPE_CALLBACK,
  219. );
  220. $items["admin/degrees/add-degree"] = array(
  221. "title" => "Add Degree",
  222. "page_callback" => "fp_render_form",
  223. "page_arguments" => array("admin_add_degree_form"),
  224. "access_arguments" => array("can_edit_data_entry"),
  225. "page_settings" => array(
  226. "page_hide_report_error" => TRUE,
  227. "menu_links" => array(
  228. 0 => array(
  229. "text" => "Admin Console",
  230. "path" => "admin-tools/admin",
  231. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  232. ),
  233. 1 => array(
  234. "text" => "Degrees",
  235. "path" => "admin/degrees",
  236. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  237. ),
  238. ),
  239. ),
  240. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  241. "type" => MENU_TYPE_NORMAL_ITEM,
  242. );
  243. $items["admin/degrees/copy-degree"] = array(
  244. "title" => "Copy Degree",
  245. "page_callback" => "fp_render_form",
  246. "page_arguments" => array("admin_copy_degree_form"),
  247. "access_arguments" => array("can_edit_data_entry"),
  248. "page_settings" => array(
  249. "page_hide_report_error" => TRUE,
  250. "menu_links" => array(
  251. 0 => array(
  252. "text" => "Admin Console",
  253. "path" => "admin-tools/admin",
  254. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  255. ),
  256. 1 => array(
  257. "text" => "Degrees",
  258. "path" => "admin/degrees",
  259. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  260. ),
  261. ),
  262. ),
  263. "file" => menu_get_module_path("admin") . "/admin.degrees.inc",
  264. "type" => MENU_TYPE_CALLBACK,
  265. );
  266. ///////////////////////////////////////////////////////
  267. ////////////////////// Group Editing ///////////////////
  268. $items["admin/groups"] = array(
  269. "title" => "Groups",
  270. "page_callback" => "admin_display_groups",
  271. "access_arguments" => array("can_access_data_entry"),
  272. "page_settings" => array(
  273. "page_hide_report_error" => TRUE,
  274. "menu_links" => array(
  275. 0 => array(
  276. "text" => "Admin Console",
  277. "path" => "admin-tools/admin",
  278. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  279. ),
  280. ),
  281. ),
  282. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  283. "type" => MENU_TYPE_NORMAL_ITEM
  284. );
  285. $items["admin/groups/edit-group"] = array(
  286. "title" => "Edit Group",
  287. "page_callback" => "fp_render_form",
  288. "page_arguments" => array("admin_edit_group_form"),
  289. "access_arguments" => array("can_edit_data_entry"),
  290. "page_settings" => array(
  291. "page_hide_report_error" => TRUE,
  292. "menu_links" => array(
  293. 0 => array(
  294. "text" => "Admin Console",
  295. "path" => "admin-tools/admin",
  296. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  297. ),
  298. 1 => array(
  299. "text" => "Groups",
  300. "path" => "admin/groups",
  301. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  302. ),
  303. ),
  304. ),
  305. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  306. "type" => MENU_TYPE_NORMAL_ITEM,
  307. );
  308. $items["admin/groups/popup-select-icon"] = array(
  309. "title" => "Select Icon",
  310. "page_callback" => "admin_display_groups_popup_select_icon",
  311. "access_arguments" => array("can_edit_data_entry"),
  312. "page_settings" => array(
  313. "page_is_popup" => TRUE,
  314. "page_hide_report_error" => TRUE,
  315. ),
  316. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  317. "type" => MENU_TYPE_CALLBACK,
  318. );
  319. $items["admin/groups/popup-edit-definition"] = array(
  320. "title" => "Edit Definition",
  321. "page_callback" => "admin_display_groups_popup_edit_definition",
  322. "access_arguments" => array("can_edit_data_entry"),
  323. "page_settings" => array(
  324. "page_is_popup" => TRUE,
  325. "page_hide_report_error" => TRUE,
  326. ),
  327. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  328. "type" => MENU_TYPE_CALLBACK,
  329. );
  330. $items["admin/groups/popup-show-group-use"] = array(
  331. "title" => "Group Use",
  332. "page_callback" => "admin_display_groups_popup_show_group_use",
  333. "access_arguments" => array("can_edit_data_entry"),
  334. "page_settings" => array(
  335. "page_is_popup" => TRUE,
  336. "page_hide_report_error" => TRUE,
  337. ),
  338. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  339. "type" => MENU_TYPE_CALLBACK,
  340. );
  341. $items["admin/groups/process-all-definitions"] = array(
  342. "title" => "Process all Definitions",
  343. "page_callback" => "fp_render_form",
  344. "page_arguments" => array("admin_process_all_definitions_form"),
  345. "access_arguments" => array("can_edit_data_entry"),
  346. "page_settings" => array(
  347. "page_hide_report_error" => TRUE,
  348. "menu_links" => array(
  349. 0 => array(
  350. "text" => "Admin Console",
  351. "path" => "admin-tools/admin",
  352. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  353. ),
  354. 1 => array(
  355. "text" => "Groups",
  356. "path" => "admin/groups",
  357. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  358. ),
  359. ),
  360. ),
  361. "file" => menu_get_module_path("admin") . "/admin.groups.inc",
  362. "type" => MENU_TYPE_NORMAL_ITEM,
  363. );
  364. /////////////////////////////////////////////////
  365. /////////////////// Courses editing ////////////////
  366. $items["admin/courses"] = array(
  367. "title" => "Courses",
  368. "page_callback" => "admin_display_courses",
  369. "access_arguments" => array("can_access_data_entry"),
  370. "page_settings" => array(
  371. "page_hide_report_error" => TRUE,
  372. "menu_links" => array(
  373. 0 => array(
  374. "text" => "Admin Console",
  375. "path" => "admin-tools/admin",
  376. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  377. ),
  378. ),
  379. ),
  380. "file" => menu_get_module_path("admin") . "/admin.courses.inc",
  381. "type" => MENU_TYPE_NORMAL_ITEM,
  382. );
  383. $items["admin/courses/edit-course"] = array(
  384. "title" => "Edit Course",
  385. "page_callback" => "fp_render_form",
  386. "page_arguments" => array("admin_edit_course_form"),
  387. "access_arguments" => array("can_edit_data_entry"),
  388. "page_settings" => array(
  389. "page_hide_report_error" => TRUE,
  390. "menu_links" => array(
  391. 0 => array(
  392. "text" => "Admin Console",
  393. "path" => "admin-tools/admin",
  394. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  395. ),
  396. 1 => array(
  397. "text" => "Courses",
  398. "path" => "admin/courses",
  399. "query" => "de_catalog_year=%DE_CATALOG_YEAR%",
  400. ),
  401. ),
  402. ),
  403. "file" => menu_get_module_path("admin") . "/admin.courses.inc",
  404. "type" => MENU_TYPE_NORMAL_ITEM,
  405. );
  406. return $items;
  407. }
  408. /**
  409. * This page displays all the tools that the user has access to use.
  410. */
  411. function admin_display_tools_screen() {
  412. fp_add_css(fp_get_module_path("admin") . '/css/admin.css');
  413. $rtn = "";
  414. $rtn .= "<div class='display-admin-tools-page'>";
  415. $rtn .= fp_render_menu_block("", "admin-tools");
  416. $rtn .= "</div>";
  417. return $rtn;
  418. }
  419. /**
  420. * hook_cron
  421. */
  422. function admin_cron() {
  423. // Delete courses and groups which has been marked with "delete_flag = 1"
  424. $last_run = intval(variable_get("admin_last_run_delete_flag_removal", 0));
  425. $check_against = strtotime("NOW - 7 DAYS"); // don't run any more often than once every 7 days
  426. $c = 0;
  427. if ($check_against > $last_run) {
  428. $res = db_query("DELETE FROM draft_courses
  429. WHERE delete_flag = 1 ");
  430. $c = db_affected_rows($res);
  431. watchdog("admin", "Delete from flagged draft_courses db complete. $c items removed.", array(), WATCHDOG_DEBUG);
  432. $res = db_query("SELECT * FROM draft_groups
  433. WHERE delete_flag = 1 ");
  434. while ($cur = db_fetch_object($res)) {
  435. $group_id = $cur->group_id;
  436. $res2 = db_query("DELETE FROM draft_group_requirements WHERE group_id = ?", array($group_id));
  437. db_query("DELETE FROM draft_groups WHERE group_id = ?", array($group_id));
  438. $c++;
  439. }
  440. watchdog("admin", "Delete from flagged draft_groups db complete. $c items removed.", array(), WATCHDOG_DEBUG);
  441. variable_set("admin_last_run_delete_flag_removal", time());
  442. } // check against > last_run, so we should do it.
  443. } // hook_cron
  444. /**
  445. * This form will allow the user to duplicate an entire year's worth of data to a new
  446. * catalog year.
  447. *
  448. */
  449. function admin_duplicate_year_form() {
  450. $form = array();
  451. $m = 0;
  452. $form["mark" . $m++] = array(
  453. "value" => "<p>" . t("This form will allow you to duplicate an entire year's worth of data
  454. (courses, degrees, groups) into a new year. This is used to create a new
  455. year of data, based on the previous year, for example. <b>It will completely
  456. delete the 'destination' year data, if it already exists!</b> Use carefully.
  457. <br><br>
  458. The new year will be placed in DRAFT, and will not be visible in FlightPath
  459. until draft changes are applied.
  460. <br><br>
  461. <u>It would be wise</u> to back up your database before proceeding.") . "</p><hr>",
  462. );
  463. $form["source_year"] = array(
  464. "type" => "textfield",
  465. "label" => t("Source year:"),
  466. "size" => 10,
  467. "required" => TRUE,
  468. "description" => t("Enter the 4-digit source year you are copying FROM. For example,
  469. 2007. If you are copying from '2007-2008' then just enter
  470. 2007."),
  471. );
  472. $form["destination_year"] = array(
  473. "type" => "textfield",
  474. "label" => t("Destination year:"),
  475. "size" => 10,
  476. "required" => TRUE,
  477. "description" => t("Enter the 4-digit source year you are copying TO. <b><u>If this year
  478. all ready exists in the system, all data for this year will be DELETED.</u></b>
  479. If you are copying to '2009-2010' then just enter
  480. 2009."),
  481. );
  482. $form["passcode"] = array(
  483. "type" => "password",
  484. "label" => t("For added security, you must enter the Apply Draft Changes password:"),
  485. "required" => TRUE,
  486. "description" => t("This is a password set up in FlightPath's settings, to provide an extra layer
  487. of security for this powerful action. Draft changes will not actually be applied,
  488. this is simply used as an added layer of security."),
  489. );
  490. $form["submit"] = array(
  491. "type" => "submit",
  492. "value" => t("Submit"),
  493. "spinner" => TRUE,
  494. "description" => t("This action may take <b><u>several minutes</u></b> to complete."),
  495. "prefix" => "<hr>",
  496. );
  497. return $form;
  498. }
  499. /**
  500. * Before we duplicate the year, make sure the password is correct.
  501. */
  502. function admin_duplicate_year_form_validate($form, &$form_state) {
  503. $values = $form_state["values"];
  504. // Check to make sure they entered the transfer passcode correctly.
  505. if ($values["passcode"] != variable_get("admin_transfer_passcode", sha1(mt_rand()))) {
  506. form_error("passcode", t("Sorry, but the password you entered is not correct. Check with the FlightPath administrator
  507. (or check the admin settings pages) to learn the password."));
  508. return;
  509. }
  510. }
  511. /**
  512. * This function will set up a batch operation so we can duplicate an entire year with less chance of time or memory
  513. * running out.
  514. *
  515. * @param array $form
  516. * @param array $form_state
  517. */
  518. function admin_duplicate_year_form_submit($form, &$form_state) {
  519. $values = $form_state["values"];
  520. $new_year = $values["destination_year"];
  521. $old_year = $values["source_year"];
  522. // We are going to keep track of all the operations we need to do in this $ops array.
  523. $ops = array();
  524. $ops[]['delete_new_year'] = TRUE;
  525. // We need to break up how many courses we copy at a time, so let's query to get a count.
  526. $count = db_result(db_query("SELECT COUNT(*) as mycount FROM draft_courses WHERE catalog_year = ? ", array($old_year)));
  527. for ($t = 0; $t < $count; $t = $t + 150) { // 150 is how many to do on each pass.
  528. $ops[]['copy_courses'] = $t;
  529. }
  530. // We need to break up the groups the same way we did the courses...
  531. $count = db_result(db_query("SELECT count(*) FROM draft_groups WHERE catalog_year = ? ", array($old_year)));
  532. for ($t = 0; $t < $count; $t = $t + 3) { // 3 is how many to do on each pass, because groups are much more detailed.
  533. $ops[]['copy_groups'] = $t;
  534. }
  535. $ops[]['copy_degree_tracks'] = TRUE;
  536. // We also want to break up the degrees for the same reasons as the others.
  537. // We need to break up the groups the same way we did the courses...
  538. $count = db_result(db_query("SELECT count(*) FROM draft_degrees WHERE catalog_year = ? ", array($old_year)));
  539. for ($t = 0; $t < $count; $t = $t + 15) { // 15 is how many to do on each pass.
  540. $ops[]['copy_degrees'] = $t;
  541. }
  542. $ops[]['hook_admin_duplicate_year'] = TRUE;
  543. // Okay, set up the batch....
  544. $batch = array(
  545. "operation" => array("admin_duplicate_year_perform_batch_operation", array($ops, $old_year, $new_year)),
  546. "title" => t("Duplicate catalog year"),
  547. "success_message" => t("Duplication from $old_year to $new_year completed successfully."),
  548. "progress_message" => "Working on @current of @total operations. This may take some time, do not close this window/tab.",
  549. "display_percent" => TRUE,
  550. );
  551. // Set the batch...
  552. batch_set($batch);
  553. }
  554. /**
  555. * The is the batch operation to duplicate from $old_year to $new_year
  556. */
  557. function admin_duplicate_year_perform_batch_operation(&$batch, $ops, $old_year, $new_year) {
  558. set_time_limit (300); // Extend time limit since this may take a little while.
  559. // if this is our first time through, let's init our values.
  560. if (!isset($batch["results"]["total"])) {
  561. // Our first time through. Let's start up.
  562. $batch["results"]["total"] = count($ops);
  563. $batch["results"]["current"] = 0;
  564. $batch["results"]["finished"] = FALSE;
  565. unset($_SESSION['admin_duplicate_year']);
  566. $_SESSION['admin_duplicate_year'] = array();
  567. $_SESSION['admin_duplicate_year']['courses'] = array();
  568. $_SESSION['admin_duplicate_year']['groups'] = array();
  569. $_SESSION['admin_duplicate_year']['degrees'] = array();
  570. }
  571. $op_command = key($ops[$batch['results']['current']]);
  572. $op_val = $ops[$batch['results']['current']][$op_command];
  573. $db = get_global_database_handler();
  574. watchdog('debug', "$op_command");
  575. if ($op_command == 'delete_new_year') {
  576. ///////////////////// DELETION ///////////////////////////////////////////
  577. // We must first begin by deleting any entries for the new_year
  578. // from our tables. This is because we may have to run this
  579. // more than once while debugging and such.
  580. $res = db_query("DELETE FROM draft_courses WHERE catalog_year = ? ", $new_year);
  581. $res = db_query("DELETE FROM draft_degree_tracks WHERE catalog_year = ? ", $new_year);
  582. // For degrees, we first need to select all of the new_year degrees.
  583. $res = db_query("SELECT * FROM draft_degrees WHERE catalog_year = ? ", $new_year);
  584. while ($cur = db_fetch_array($res)) {
  585. $res2 = db_query("DELETE FROM draft_degree_requirements WHERE degree_id = ? ", $cur["degree_id"]);
  586. }
  587. $res = db_query("DELETE FROM draft_degrees WHERE catalog_year = ? ", $new_year);
  588. // For groups, begin by selecting all the groups in that year...
  589. $res = db_query("SELECT * FROM draft_groups WHERE catalog_year = ? ", $new_year);
  590. while ($cur = db_fetch_array($res))
  591. {
  592. // Now, select all the requirements and see if there are any sub groups...
  593. $res2 = db_query("SELECT * FROM draft_group_requirements WHERE group_id = ? ", $cur["group_id"]);
  594. while ($cur2 = db_fetch_array($res2))
  595. {
  596. if ($cur2["child_group_id"] > 0)
  597. {
  598. // Delete the child group.
  599. $res3 = db_query("DELETE FROM draft_group_requirements WHERE group_id = ? ", $cur2["child_group_id"]);
  600. }
  601. }
  602. // Now, delete the original requirement.
  603. $res3 = db_query("DELETE FROM draft_group_requirements WHERE group_id = ? ", $cur["group_id"]);
  604. }
  605. $res = db_query("DELETE FROM draft_groups WHERE catalog_year = ? ", $new_year);
  606. } // delete_new_year
  607. /////////////
  608. /////////////
  609. if ($op_command == 'copy_courses') {
  610. $start_position = intval($op_val);
  611. /////////////////////// COPY COURSES //////////////////////////////////////////////////
  612. // Copy all of the courses from the old_year to the new_year.
  613. $res = db_query("SELECT * FROM draft_courses WHERE catalog_year = ?
  614. LIMIT $start_position, 150 ", array($old_year));
  615. while ($cur = db_fetch_array($res))
  616. {
  617. $course = new stdClass();
  618. $course->course_id = $cur['course_id'];
  619. $course->subject_id = $cur['subject_id'];
  620. $course->course_num = $cur['course_num'];
  621. $course->db_exclude = $cur['exclude'];
  622. $course->min_hours = $cur['min_hours'];
  623. $course->max_hours = $cur['max_hours'];
  624. $course->repeat_hours = $cur['repeat_hours'];
  625. $course->school_id = $cur['school_id'];
  626. $course->title = $cur['title'];
  627. $course->description = $cur['description'];
  628. //$course_id, $c->subject_id,$c->course_num,$catalog_year,$c->title,$c->description,$min_hours,$max_hours,
  629. // $c->repeat_hours,$c->db_exclude,$c->school_id)
  630. // Now, duplicate it for the new_year.
  631. // The FALSE at the end means don't bother deleting any existing course, since we have already done that.
  632. $db->duplicate_course_for_year($course, $new_year, FALSE);
  633. // Store what courses we are working on, for other modules.
  634. $_SESSION['admin_duplicate_year']['courses'][] = $cur['course_id'];
  635. }
  636. } // copy_courses
  637. //////////////
  638. //////////////
  639. if ($op_command == 'copy_groups') {
  640. //////////////////////// COPY GROUPS ///////////////////////////////////////////////
  641. // Now, let's copy over the groups.
  642. $group_id_array = array();
  643. $subgroup_id_array = array();
  644. $start_position = intval($op_val);
  645. $res = db_query("SELECT * FROM draft_groups WHERE catalog_year = ?
  646. LIMIT $start_position, 3 ", $old_year);
  647. while ($cur = db_fetch_array($res))
  648. {
  649. $db_group_id = $cur['group_id'];
  650. $db_definition = $cur['definition'];
  651. $db_title = $cur['title'];
  652. $db_group_name = $cur['group_name'];
  653. $db_priority = $cur['priority'];
  654. $db_icon_filename = $cur['icon_filename'];
  655. $db_public_note = $cur['public_note'];
  656. $db_delete_flag = $cur['delete_flag'];
  657. $db_data_entry_comment = $cur['data_entry_comment'];
  658. $db_catalog_repeat = $cur['catalog_repeat'];
  659. $db_school_id = $cur['school_id'];
  660. // First, let's request a new group ID for this new group.
  661. $new_group_id = $db->request_new_group_id();
  662. $group_id_array[$db_group_id] = $new_group_id;
  663. // Now, let's insert this top-level group back into the table
  664. // as the new_year, with the new_group_id.
  665. $query = "INSERT INTO draft_groups(`group_id`,`group_name`,
  666. `title`,`public_note`,`definition`,`icon_filename`,`catalog_year`,
  667. `priority`,`delete_flag`,`data_entry_comment`, `catalog_repeat`,school_id)
  668. VALUES (?,?,?,?,?,?,?,?,?,?,?,?) ";
  669. $res2 = db_query($query, array($new_group_id, $db_group_name, $db_title, $db_public_note,
  670. $db_definition, $db_icon_filename, $new_year,
  671. $db_priority, $db_delete_flag, $db_data_entry_comment,
  672. $db_catalog_repeat, $db_school_id));
  673. // Okay, now we need to go through the requirements for the group, and copy
  674. // those over to the new_year.
  675. $res3 = db_query("SELECT * FROM draft_group_requirements
  676. WHERE group_id = ? ", $db_group_id);
  677. while($cur3 = db_fetch_array($res3))
  678. {
  679. $child_group_id = 0;
  680. // Was there a child_group (a branch)? If so, we need to copy that
  681. // over too, with a new child_group_id.
  682. if ($cur3['child_group_id'] > 0) {
  683. // First, create the child group...
  684. $new_sub_group_id = $db->request_new_group_id();
  685. $subgroup_id_array[$cur3['child_group_id']] = $new_sub_group_id;
  686. $res4 = db_query("SELECT * FROM draft_group_requirements
  687. WHERE group_id = ? ", $cur3['child_group_id']);
  688. while($cur4 = db_fetch_array($res4))
  689. {
  690. $res5 = db_query("INSERT INTO draft_group_requirements
  691. (`group_id`,`course_id`,`course_min_grade`,
  692. `course_repeats`,`attributes`,`data_entry_value`)
  693. VALUES (?,?,?,?,?,?) ", array($new_sub_group_id, $cur4['course_id'],
  694. $cur4['course_min_grade'],$cur4['course_repeats'],$cur4['attributes'],
  695. $cur4['data_entry_value']));
  696. }
  697. // Now, add the replace the db3_child_group_id with this new id
  698. // so it will get added as a requirement.
  699. $child_group_id = $new_sub_group_id;
  700. }
  701. // Add the row into the table...
  702. $res5 = db_query("INSERT INTO draft_group_requirements
  703. (`group_id`,`course_id`,`course_min_grade`,
  704. `course_repeats`,`attributes`,`data_entry_value`,`child_group_id`)
  705. VALUES (?,?,?,?,?,?,?) ", array($new_group_id, $cur3['course_id'],
  706. $cur3['course_min_grade'], $cur3['course_repeats'], $cur3['attributes'],
  707. $cur3['data_entry_value'], $child_group_id));
  708. }
  709. }
  710. // Store what groups we are working on, for other modules.
  711. $_SESSION['admin_duplicate_year']['groups'] += $group_id_array; // Since we are going in multiple rounds, add to the array.
  712. } // copy_groups
  713. ////////////////////////////
  714. ////////////////////////////
  715. if ($op_command == 'copy_degree_tracks') {
  716. $res = db_query("SELECT * FROM draft_degree_tracks WHERE catalog_year = ? ", $old_year);
  717. while ($cur = db_fetch_array($res))
  718. {
  719. $res2 = db_query("INSERT INTO draft_degree_tracks
  720. (`catalog_year`,`major_code`,`track_code`,`track_title`,
  721. `track_short_title`,`track_description`,school_id)
  722. VALUES
  723. (?,?,?,?,?,?,?) ", array($new_year, $cur['major_code'],$cur['track_code'],$cur['track_title'],
  724. $cur['track_short_title'],$cur['track_description'],$cur['school_id']));
  725. }
  726. }
  727. ////////////////
  728. ////////////////
  729. if ($op_command == 'copy_degrees') {
  730. //////////////////////// COPY DEGREES ///////////////////////////////////////////
  731. // Now, on to transfering the degrees.
  732. // We will have to use the groupIDArray we constructed earlier, which
  733. // looks like $arr[old_id] = new_id.
  734. // Do the tracks first, since they are easier and straight forward...
  735. $start_position = intval($op_val);
  736. $group_id_array = $_SESSION['admin_duplicate_year']['groups']; // Load from last time, when we copied groups.
  737. // Now, let's do the degrees themselves.
  738. $res = db_query("SELECT * FROM draft_degrees WHERE catalog_year = ?
  739. LIMIT $start_position, 15 ", $old_year);
  740. while ($cur = db_fetch_array($res)) {
  741. //extract($cur, 3, "db");
  742. $db_degree_id = $cur['degree_id'];
  743. $db_school_id = $cur['school_id'];
  744. $db_major_code = $cur['major_code'];
  745. $db_degree_type = $cur['degree_type'];
  746. $db_degree_level = $cur['degree_level'];
  747. $db_degree_class = $cur['degree_clas'];
  748. $db_title = $cur['title'];
  749. $db_semester_titles_csv = $cur['semester_titles_csv'];
  750. $db_exclude = $cur['exclude'];
  751. $db_public_note = $cur['public_note'];
  752. $db_allow_dynamic = $cur['allow_dynamic'];
  753. $db_advising_weight = $cur['advising_weight'];
  754. $db_override_degree_hours = $cur['override_degree_hours'];
  755. $db_min_tracks = $cur['min_tracks'];
  756. $db_max_tracks = $cur['max_tracks'];
  757. $db_default_tracks = $cur['default_tracks'];
  758. $db_track_selection_config = $cur['track_selection_config'];
  759. $new_degree_id = $db->request_new_degree_id();
  760. // Store what degrees we are working on, for other modules.
  761. $_SESSION['admin_duplicate_year']['degrees'][] = array(
  762. 'src' => $db_degree_id,
  763. 'src_catalog_year' => $old_year,
  764. 'school_id' => $db_school_id,
  765. 'dest' => $new_degree_id,
  766. 'dest_major_code' => $db_major_code,
  767. 'dest_catalog_year' => $new_year,
  768. );
  769. // add in the top-level degree to the table.
  770. $res2 = db_query("INSERT INTO draft_degrees
  771. (degree_id, major_code, degree_type, degree_level, degree_class, title,
  772. semester_titles_csv, catalog_year, exclude, public_note,
  773. allow_dynamic, advising_weight, override_degree_hours, min_tracks, max_tracks, default_tracks, track_selection_config, school_id)
  774. VALUES
  775. (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  776. ", array($new_degree_id, $db_major_code, $db_degree_type, $db_degree_level, $db_degree_class, $db_title,
  777. $db_semester_titles_csv, $new_year, $db_exclude, $db_public_note,
  778. $db_allow_dynamic, $db_advising_weight, $db_override_degree_hours, $db_min_tracks, $db_max_tracks,
  779. $db_default_tracks, $db_track_selection_config, $db_school_id));
  780. // now, pull out all of the degree_requirements.
  781. $res3 = db_query("SELECT * FROM draft_degree_requirements
  782. WHERE degree_id = ? ", array($db_degree_id));
  783. while($cur3 = db_fetch_array($res3))
  784. {
  785. //extract($cur3, 3, "db3");
  786. // If there is a required group, we must convert it to the
  787. // new groupID !
  788. $required_group_id = intval($cur3['group_id']);
  789. if ($cur3['group_id'] > 0 && trim($cur3['group_id']) != "")
  790. {
  791. $o_group_id = $cur3['group_id'];
  792. $required_group_id = $group_id_array[$cur3['group_id']];
  793. if ($required_group_id < 1 || $required_group_id == "")
  794. {
  795. //die("could not find new group for $old_year $db3_group_id! Group id is $required_group_id");
  796. fp_add_message(t("Couldn't find the specified group with ID {$cur3['group_id']} in catalog year $old_year, required by degree with ID $db_degree_id ($db_major_code). The group was possibly deleted?
  797. Be aware that any degrees in the new year will be missing this group requirement."), 'error', TRUE);
  798. }
  799. }
  800. $res4 = db_query("INSERT INTO draft_degree_requirements
  801. (degree_id, semester_num, group_id, group_requirement_type,
  802. group_hours_required, group_min_hours_allowed, group_min_grade, course_id,
  803. course_min_grade, course_requirement_type, data_entry_value)
  804. VALUES
  805. (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  806. ", array($new_degree_id, $cur3['semester_num'], $required_group_id, $cur3['group_requirement_type'],
  807. $cur3['group_hours_required'], $cur3['group_min_hours_allowed'], $cur3['group_min_grade'], $cur3['course_id'],
  808. $cur3['course_min_grade'], $cur3['course_requirement_type'], $cur3['data_entry_value']));
  809. }
  810. } // while draft_degrees
  811. } // copy_degrees
  812. ////////////////////
  813. ////////////////////
  814. // Allows other modules to act.
  815. if ($op_command == 'hook_admin_duplicate_year') {
  816. invoke_hook('admin_duplicate_year', array($old_year, $new_year));
  817. }
  818. $batch['results']['current']++;
  819. // Have we finished?
  820. if ($batch["results"]["current"] >= $batch["results"]["total"]) {
  821. $batch["results"]["finished"] = TRUE;
  822. watchdog("admin", "Duplicated all degree/group/courses data from $old_year to $new_year");
  823. fp_add_message(t("The copy operation is now completed. You may view the new year
  824. by using the Data Entry links on the admin main menu. Remember, you must
  825. apply draft changes before they will be visible in FlightPath."));
  826. }
  827. } // admin_duplicate_year_perform_batch_operation
  828. /**
  829. * This is a systems settings form, which lets
  830. * the user edit advising variabled for FlightPath.
  831. */
  832. function admin_advising_settings_form($school_id = 0) {
  833. $form = array();
  834. $school_id = intval($school_id);
  835. $fs = ""; // The field name suffix. We will add this to the end of all of our field names. If this is the default school, leave blank.
  836. if (module_enabled("schools")) {
  837. $school_name = schools_get_school_name_for_id($school_id);
  838. fp_set_title(t("Edit %school school advising settings", array('%school' => $school_name)));
  839. if ($school_id !== 0) {
  840. $fs = "~~school_" . $school_id;
  841. }
  842. }
  843. $form['school_id'] = array(
  844. 'type' => 'hidden',
  845. 'value' => $school_id,
  846. );
  847. $form["available_advising_term_ids" . $fs] = array(
  848. "type" => "textarea",
  849. "rows" => 2,
  850. "label" => t("Available advising term id(s)"),
  851. "value" => fp_space_csv(variable_get_for_school("available_advising_term_ids", "", $school_id, TRUE)),
  852. "description" => t("These are the terms for which advisors may advise a student to take
  853. a course. They should follow the structure established on the
  854. school data settings page.
  855. Separate term id's by commas. Ex: 200940, 200941, 200960. Make sure to enter
  856. in the order that they should appear in FlightPath."),
  857. );
  858. $form["advising_term_id" . $fs] = array(
  859. "type" => "textarea",
  860. "rows" => 2,
  861. "label" => t("Default advising term id"),
  862. "value" => fp_space_csv(variable_get_for_school("advising_term_id", "", $school_id, TRUE)),
  863. "description" => t("Of the available term ids above, this is the default that FlightPath
  864. is set to when an advisor logs in. Ex: 200940.
  865. <br><strong>Only enter one Term ID in this box.</strong>"),
  866. );
  867. $form["current_catalog_year" . $fs] = array(
  868. "type" => "textfield",
  869. "size" => 10,
  870. "label" => t("Current catalog year"),
  871. "value" => variable_get_for_school("current_catalog_year", "", $school_id, TRUE),
  872. "description" => t("This is the year which What If loads degrees from, as well as other important functions.
  873. Only change this once you have fully loaded a new catalog year."),
  874. );
  875. $form["current_draft_catalog_year" . $fs] = array(
  876. "type" => "textfield",
  877. "size" => 10,
  878. "label" => t("Current DRAFT catalog year"),
  879. "value" => variable_get_for_school("current_draft_catalog_year", "", $school_id, TRUE),
  880. "description" => t("While in Draft mode, this is the year which Blank Degree Search loads degrees from. What If
  881. mode will always use the Current catalog year, set above.
  882. You may change this while working on a new catalog. It will not affect any other
  883. users of the system. While not working on a new catalog, set this
  884. to the same as the Current catalog year."),
  885. );
  886. $form["not_released_grades_terms" . $fs] = array(
  887. "type" => "textarea",
  888. "rows" => 2,
  889. "label" => t("NOT released grades terms"),
  890. "value" => fp_space_csv(variable_get_for_school("not_released_grades_terms", "", $school_id, TRUE)),
  891. "description" => t("Enter the term_id's, separated by commas,
  892. which are NOT released and should NOT be visible to either advisors or students.
  893. Coures in these terms will only show midterm grades or no grades at all.
  894. Ex: 201240, 201240:S1") . "<br>" . t("If left blank, grades will be visible to
  895. students and advisors as soon as they appear in the database."),
  896. );
  897. return $form;
  898. }
  899. /**
  900. * Convienence function to get semester's default regular name.
  901. */
  902. function admin_get_semester_name($semester_num) {
  903. $semester = new Semester($semester_num);
  904. $title = $semester->title;
  905. /**
  906. $ar = array(t("Freshman Year"), t("Sophomore Year"), t("Junior Year"), t("Senior Year"));
  907. $s = $ar[$semester_num];
  908. if ($s == "")
  909. {
  910. $s = t("Year") . " " . ($semester_num + 1);
  911. }
  912. */
  913. return $title;
  914. }
  915. /**
  916. * This form lets the user apply draft changes (if they can supply the passcode)
  917. */
  918. function admin_apply_draft_changes_form() {
  919. $form = array();
  920. $m = 0;
  921. $form["mark" . $m++] = array(
  922. "type" => "markup",
  923. "value" => t("You can use this form to apply your draft changes to the production database, making changes
  924. to degrees, courses, and groups visible to all users of the system."),
  925. );
  926. $form["passcode"] = array(
  927. "type" => "password",
  928. "label" => t("For added security, you must enter the Apply Draft Changes password:"),
  929. "required" => TRUE,
  930. "description" => t("This is a password set up in FlightPath's System Settings, to provide an extra layer
  931. of security for this powerful action."),
  932. );
  933. $form["submit"] = array(
  934. "type" => "submit",
  935. "value" => t("Submit"),
  936. "spinner" => TRUE,
  937. "description" => t("Note: This action may take several seconds to complete. Do not close this tab or refresh the browser."),
  938. "prefix" => "<hr>",
  939. );
  940. return $form;
  941. }
  942. /**
  943. * Before we apply changes, make sure the password is correct.
  944. */
  945. function admin_apply_draft_changes_form_validate($form, &$form_state) {
  946. $values = $form_state["values"];
  947. // Check to make sure they entered the transfer passcode correctly.
  948. if ($values["passcode"] != variable_get("admin_transfer_passcode", sha1(mt_rand()))) {
  949. form_error("passcode", t("Sorry, but the password you entered is not correct. Check with the FlightPath administrator
  950. (or check the admin settings pages) to learn the password."));
  951. return;
  952. }
  953. }
  954. /**
  955. * We are going to set up a batch operation for apply draft changes, to reduce the chance of it timing out with large
  956. * amounts of data.
  957. */
  958. function admin_apply_draft_changes_form_submit($form, $form_submit) {
  959. // We are going to keep track of all the operations we need to do in this $ops array.
  960. $ops = array();
  961. $ops[]['truncate_prod_and_copy'] = 'courses';
  962. $ops[]['truncate_prod_and_copy'] = 'degree_requirements';
  963. $ops[]['truncate_prod_and_copy'] = 'degree_tracks';
  964. $ops[]['truncate_prod_and_copy'] = 'degrees';
  965. $ops[]['truncate_prod_and_copy'] = 'group_requirements';
  966. $ops[]['truncate_prod_and_copy'] = 'groups';
  967. $ops[]['perform_draft_instructions'] = TRUE;
  968. $ops[]['invoke_apply_draft_changes'] = TRUE;
  969. $ops[]['rebuild_cache_course_inventory'] = TRUE;
  970. // Okay, set up the batch....
  971. $batch = array(
  972. "operation" => array("admin_apply_draft_changes_perform_batch_operation", array($ops)),
  973. "title" => t("Apply draft changes"),
  974. "success_message" => t("Draft changes have been applied successfully."),
  975. "progress_message" => "Working on @current of @total operations...",
  976. "display_percent" => TRUE,
  977. );
  978. // Set the batch...
  979. batch_set($batch);
  980. }
  981. function admin_apply_draft_changes_perform_batch_operation(&$batch, $ops) {
  982. // if this is our first time through, let's init our values.
  983. if (!isset($batch["results"]["total"])) {
  984. // Our first time through. Let's start up.
  985. $batch["results"]["total"] = count($ops);
  986. $batch["results"]["current"] = 0;
  987. $batch["results"]["finished"] = FALSE;
  988. // First, set maintenance mode...
  989. variable_set("maintenance_mode", TRUE);
  990. }
  991. $op_command = @key($ops[$batch['results']['current']]);
  992. $op_val = $ops[$batch['results']['current']][$op_command];
  993. if ($op_command == 'truncate_prod_and_copy') {
  994. $table_name = $op_val;
  995. $draft_table_name = "draft_$table_name";
  996. // First, truncate existing...
  997. $query = "truncate table `$table_name`";
  998. $res = db_query($query);
  999. // Now, copy in draft changes...
  1000. $query = "INSERT INTO `$table_name`
  1001. SELECT * FROM `$draft_table_name` ";
  1002. $res = db_query($query);
  1003. } // truncate prod and copy
  1004. /////////////
  1005. /////////////
  1006. if ($op_command == 'perform_draft_instructions') {
  1007. $db = get_global_database_handler();
  1008. // Now, we need to go through the draft_instructions table,
  1009. // and perform each instruction one at a time.
  1010. $res = db_query("SELECT * FROM draft_instructions
  1011. ORDER BY `id` ");
  1012. while($cur = db_fetch_array($res))
  1013. {
  1014. $instruction = trim($cur["instruction"]);
  1015. $temp = explode(",",$instruction);
  1016. if (trim($temp[0]) == "update_course_id") {
  1017. $db->update_course_id(trim($temp[1]), trim($temp[2]), trim($temp[3]));
  1018. }
  1019. if (trim($temp[0]) == "update_course_requirement_from_name") {
  1020. $db->update_course_requirement_from_name(trim($temp[1]), trim($temp[2]), trim($temp[3]), trim($temp[4]));
  1021. }
  1022. // TODO: Maybe invoke another hook here, to let other modules act on the instruction?
  1023. } // while
  1024. // Once this is done, truncate the draft_instructions table.
  1025. $res = db_query("TRUNCATE TABLE draft_instructions");
  1026. } // perform_draft_instructions
  1027. /////////////////
  1028. /////////////////
  1029. if ($op_command == 'invoke_apply_draft_changes') {
  1030. // Invoke a hook to allow other modules to apply_draft_changes as well
  1031. invoke_hook("apply_draft_changes");
  1032. }
  1033. ///////////////
  1034. ///////////////
  1035. if ($op_command == 'rebuild_cache_course_inventory') {
  1036. // Rebuild our course cache file.
  1037. unset($_SESSION['fp_cache_course_inventory_last_generated']);
  1038. if (file_exists(fp_get_files_path() . "/cache_data/courses_serialized.info")) {
  1039. $x = unlink(fp_get_files_path() . "/cache_data/courses_serialized.info");
  1040. if (!$x) {
  1041. fpm("Cannot delete cache_data/courses_serialized.info under custom/files. Permission error or file does not exist?");
  1042. watchdog("system", "Cannot delete cache_data/courses_serialized.info under custom/files. Permission error or file does not exist?", array(), WATCHDOG_ERROR);
  1043. }
  1044. }
  1045. variable_set('cache_course_inventory_last_generated', 0); // reset this so it will be regenerated.
  1046. // Builds new courses cache file on the hard drive.
  1047. system_reload_and_cache_course_inventory();
  1048. } // rebuild cache course inventory
  1049. $batch['results']['current']++;
  1050. // Have we finished?
  1051. if ($batch["results"]["current"] >= $batch["results"]["total"]) {
  1052. $batch["results"]["finished"] = TRUE;
  1053. // Send emails to notify programmers
  1054. $notify = variable_get("notify_apply_draft_changes_email_address", "");
  1055. if ($notify != "") {
  1056. mail($notify, "FlightPath Apply Draft Changes", "Someone has applied draft changes to FlightPath, which updated degree plans, groups, and courses.");
  1057. }
  1058. fp_add_message(t("Successfully updated the production database with draft changes. Your changes are now live and visible on production for all users."));
  1059. watchdog("admin", "Draft changes applied.");
  1060. // Remove maintenance mode...
  1061. variable_set("maintenance_mode", FALSE);
  1062. }
  1063. } // admin_apply_draft_changes_perform_batch_operation
  1064. /**
  1065. * Get the "de_catalog_year" from the REQUEST.
  1066. * If it's not there or invalid, pull it from our system settings.
  1067. */
  1068. function admin_get_de_catalog_year($bool_set_earliest_if_blank = TRUE, $school_id = 0) {
  1069. global $current_student_id;
  1070. $db = get_global_database_handler();
  1071. if ($current_student_id) {
  1072. $school_id = $db->get_school_id_for_student_id($current_student_id);
  1073. }
  1074. $de_catalog_year = @$_REQUEST["de_catalog_year"];
  1075. if ($bool_set_earliest_if_blank) {
  1076. if (!$de_catalog_year || $de_catalog_year < variable_get_for_school("earliest_catalog_year", 2006, $school_id)) {
  1077. $de_catalog_year = variable_get_for_school("earliest_catalog_year", 2006, $school_id);
  1078. }
  1079. }
  1080. return $de_catalog_year;
  1081. }
  1082. /**
  1083. * This is an implementation of hook_menu_handle_replacement_pattern.
  1084. * It will search for and replace replacement patterns which we are aware of it in $str.
  1085. */
  1086. function admin_menu_handle_replacement_pattern($str) {
  1087. if (!$str) $str = ""; // Compat for PHP 8.2, force it to be a string if it was NULL or FALSE.
  1088. if (strpos($str, "%DE_CATALOG_YEAR%") !== 0) {
  1089. // It contains this replacement pattern!
  1090. $str = str_replace("%DE_CATALOG_YEAR%", admin_get_de_catalog_year(), $str);
  1091. }
  1092. if (strpos($str, "%SEV_FILTER%") !== 0) {
  1093. // It contains this replacement pattern!
  1094. $sev_filter = (string) @$_GET["sev_filter"];
  1095. $str = str_replace("%SEV_FILTER%", $sev_filter, $str);
  1096. }
  1097. if (strpos($str, "%TYPE_FILTER%") !== 0) {
  1098. // It contains this replacement pattern!
  1099. $type_filter = (string) @$_GET["type_filter"];
  1100. $str = str_replace("%TYPE_FILTER%", $type_filter, $str);
  1101. }
  1102. if (strpos($str, "%PAGE%") !== 0) {
  1103. // It contains this replacement pattern!
  1104. $page = "";
  1105. if (isset($_GET['page']) && !is_array($_GET["page"]) && !is_object($_GET['page'])) {
  1106. $page = (string) $_GET["page"];
  1107. }
  1108. $str = str_replace("%PAGE%", $page, $str);
  1109. }
  1110. return $str;
  1111. }
  1112. function admin_perm() {
  1113. return array(
  1114. "can_access_admin_tools" => array(
  1115. "title" => t("Access Admin Tools"),
  1116. "description" => t("Allow the user to access the Admin Tools screen."),
  1117. ),
  1118. "can_access_admin" => array(
  1119. "title" => t("Access administrative console"),
  1120. "description" => t("This is a powerful permission! This allows a
  1121. user to access the 'admin console' for FlightPath."),
  1122. ),
  1123. "can_edit_urgent_message" => array(
  1124. "title" => t("Edit urgent message"),
  1125. "description" => t("The user may edit the 'Urgent Message' which appears at the top of every page, if set."),
  1126. ),
  1127. "can_access_data_entry" => array(
  1128. "title" => t("Access Data Entry"),
  1129. "description" => t("The user can access (view) the data-entry portion of the admin console. Degree plans, groups, and courses."),
  1130. ),
  1131. "can_edit_data_entry" => array(
  1132. "title" => t("Edit Data Entry"),
  1133. "description" => t("This is a powerful permission! The user can edit degree plans, groups, and courses."),
  1134. ),
  1135. "can_delete_data_entry" => array(
  1136. "title" => t("Delete Data Entry items"),
  1137. "description" => t("This is a VERY powerful permission! The user can delete degree plans, groups, and courses."),
  1138. ),
  1139. "can_view_advanced" => array(
  1140. "title" => t("View advanced"),
  1141. "description" => t("The user may see advanced information on-screen, for example, internal ID numbers for degrees and courses."),
  1142. ),
  1143. "can_apply_draft_changes" => array(
  1144. "title" => t("Apply draft changes"),
  1145. "description" => t("The user may move draft changes into production."),
  1146. ),
  1147. "can_edit_advising_settings" => array(
  1148. "title" => t("Edit advising settings"),
  1149. "description" => t("The user may edit advising settings, like available term IDs and the current catalog year."),
  1150. ),
  1151. "display_watchdog" => array(
  1152. "title" => t("View watchdog (log) entries"),
  1153. "description" => t("The user may view the recent watchdog log entries. This represents a security concern, so give this only to trusted users."),
  1154. ),
  1155. );
  1156. }
  1157. /**
  1158. * Meant to be fed into "fp_system_settings_form()", this function
  1159. * returns an array which will automatically save values to our "variables" table.
  1160. */
  1161. function admin_urgent_message_form() {
  1162. $form = array();
  1163. $form["mark_top"] = array(
  1164. "type" => "markup",
  1165. "value" => "<p>" . t("Any message you enter here will be displayed at the top of every page <b>for all users</b> in the system.
  1166. This is used to alert users that the system is about to be taken offline, or any other urgently-needed information.") . "</p>",
  1167. );
  1168. $form["urgent_msg"] = array(
  1169. "type" => "textarea",
  1170. "label" => "Global Urgent Message:",
  1171. "value" => variable_get("urgent_msg", ""),
  1172. 'description' => t("To delete this message, simple delete all the text in this box and save."),
  1173. );
  1174. return $form;
  1175. }
  1176. /**
  1177. * Display the details of a particular watchdog entry, specified by its table id.
  1178. */
  1179. function admin_display_watchdog_entry($wid) {
  1180. $rtn = "";
  1181. fp_add_css(fp_get_module_path("admin") . '/css/admin.css');
  1182. $severity_array = array(
  1183. 5 => "notice",
  1184. 1 => "alert",
  1185. 3 => "error",
  1186. 7 => "debug",
  1187. );
  1188. $res = db_query("SELECT * FROM watchdog WHERE wid = '?' ", $wid);
  1189. $cur = db_fetch_object($res);
  1190. $msg = t($cur->message, unserialize($cur->variables));
  1191. $severity = $severity_array[$cur->severity];
  1192. $when = format_date(convert_time($cur->timestamp), "short");
  1193. // Display
  1194. $rtn .= "
  1195. <br>
  1196. <div class='watchdog-entry'>
  1197. <div class='watchdog-field'>
  1198. <label>ID:</label>
  1199. $cur->wid
  1200. </div>
  1201. <div class='watchdog-field'>
  1202. <label>Type:</label>
  1203. $cur->type
  1204. </div>
  1205. <div class='watchdog-field'>
  1206. <label>Time:</label>
  1207. $when ($cur->timestamp)
  1208. </div>
  1209. <div class='watchdog-field'>
  1210. <label>Severity:</label>
  1211. $severity
  1212. </div>
  1213. <div class='watchdog-field'>
  1214. <label>User:</label>
  1215. $cur->user_name | id: $cur->user_id | cwid: $cur->cwid | is_student: $cur->is_student | is_faculty: $cur->is_faculty
  1216. </div>
  1217. <div class='watchdog-field'>
  1218. <label>IP:</label>
  1219. $cur->ip
  1220. </div>
  1221. <div class='watchdog-field'>
  1222. <label>Location:</label>
  1223. " . str_replace("&curren", "&amp;curren", fp_trim($cur->location)) . "
  1224. </div>
  1225. <div class='watchdog-field'>
  1226. <label>Ref:</label>
  1227. " . str_replace("&curren", "&amp;curren", fp_trim($cur->referer)) . "
  1228. </div>
  1229. <hr>
  1230. <div class='watchdog-field'>
  1231. <label class='block'>Message:</label>
  1232. $msg
  1233. </div>
  1234. <div class='watchdog-field'>
  1235. <label class='block'>Extra:</label>
  1236. $cur->extra_data
  1237. </div>
  1238. </div>
  1239. ";
  1240. return $rtn;
  1241. }
  1242. function admin_display_watchdog() {
  1243. fp_add_css(fp_get_module_path("admin") . '/css/admin.css');
  1244. $rtn = "";
  1245. $rtn .= "<p>View recent watchdog entries</p>";
  1246. $severity_array = array(
  1247. 5 => "notice",
  1248. 1 => "alert",
  1249. 3 => "error",
  1250. 7 => "debug",
  1251. );
  1252. $results_per_page = 50;
  1253. $type_line = $severity_line = $limit_start = "";
  1254. @$sev_filter = $_GET["sev_filter"];
  1255. @$type_filter = $_GET["type_filter"];
  1256. @$page = intval($_GET["page"]);
  1257. $limit_start = 0;
  1258. if ($page > 0) {
  1259. $limit_start = $results_per_page * ($page - 1);
  1260. }
  1261. // Filters:
  1262. $rtn .= "<form method='GET' action='" . fp_url("admin/config/watchdog") . "' class='watchdog-filters'>
  1263. ";
  1264. $rtn .= "Filter by:
  1265. <span class='type-filter'>
  1266. Type:
  1267. <select name='type_filter'>
  1268. <option value=''>-Any-</option>";
  1269. $res = db_query("SELECT distinct(type) as type
  1270. FROM watchdog
  1271. ORDER BY type");
  1272. while ($cur = db_fetch_object($res)) {
  1273. $sel = ($type_filter == $cur->type) ? "selected" : "";
  1274. $rtn .= "<option value='$cur->type' $sel>$cur->type</option>";
  1275. }
  1276. $rtn .= "</select>
  1277. </span>";
  1278. $rtn .= "
  1279. <span class='severity-filter'>
  1280. Severity:
  1281. <select name='sev_filter'>
  1282. <option value=''>-Any-</option>";
  1283. foreach ($severity_array as $key => $value) {
  1284. $sel = "";
  1285. if ($sev_filter == $key) $sel = "selected";
  1286. $rtn .= "<option value='$key' $sel>$value</option>";
  1287. }
  1288. $rtn .= "</select>
  1289. </span>";
  1290. $rtn .= "<input type='submit' value='&gt;'>";
  1291. // Hack if clean URLs are not enabled:
  1292. $rtn .= "<input type='hidden' name='q' value='admin/config/watchdog'>";
  1293. $rtn .= "</form>";
  1294. $params = array();
  1295. if (@$_GET["type_filter"] != "") {
  1296. //$type_line = " AND type = '" . mysql_real_escape_string($_GET["type_filter"]) . "' ";
  1297. $type_line = " AND `type` = :type ";
  1298. $params[":type"] = $_GET["type_filter"];
  1299. }
  1300. if (@$_GET["sev_filter"] != "") {
  1301. //$severity_line = " AND severity = '" . mysql_real_escape_string($_GET["sev_filter"]) . "' ";
  1302. $severity_line = " AND severity = :sev ";
  1303. $params[":sev"] = $_GET["sev_filter"];
  1304. }
  1305. $pol = "even";
  1306. $table_headers = array();
  1307. $table_headers[] = array("label" => "ID", "field" => "wid");
  1308. $table_headers[] = array("label" => "When", "field" => "a.timestamp");
  1309. $table_headers[] = array("label" => "Type", "field" => "a.type");
  1310. $table_headers[] = array("label" => "Msg");
  1311. $table_headers[] = array("label" => "Severity", "field" => "a.severity");
  1312. $table_headers[] = array("label" => "User", "field" => "user_name");
  1313. // Set our initial sort, if none is already set.
  1314. theme_table_header_sortable_set_initial_sort("wid", 'DESC');
  1315. $rtn .= "<table class='watchdog-table' cellspacing='0' cellpadding='4'>";
  1316. // Draw our our table headers, with links....
  1317. $rtn .= theme_table_header_sortable($table_headers);
  1318. // Get our order by clause based on selected table header, if any.
  1319. $order_by = theme_table_header_sortable_order_by($table_headers);
  1320. $res = pager_query("SELECT * FROM watchdog a
  1321. WHERE 1
  1322. $type_line
  1323. $severity_line
  1324. $order_by", $params, 50, 0, "SELECT COUNT(wid) FROM watchdog a
  1325. WHERE 1
  1326. $type_line
  1327. $severity_line
  1328. $order_by", $params);
  1329. while ($cur = db_fetch_object($res)) {
  1330. $pol = ($pol == "even") ? "odd" : "even";
  1331. $o_msg = t($cur->message, unserialize($cur->variables));
  1332. $o_type = $cur->type;
  1333. // shorten msg if necessary
  1334. $msg = substr(strip_tags($o_msg), 0, 130);
  1335. if ($msg != $o_msg) {
  1336. $msg = trim($msg) . "...";
  1337. }
  1338. $type = substr($o_type, 0, 20);
  1339. if ($type != $o_type) {
  1340. $type = trim($type) . "...";
  1341. }
  1342. $severity = $severity_array[$cur->severity];
  1343. $when = format_date(convert_time($cur->timestamp), "short");
  1344. $rtn .= "
  1345. <tr class='row-$pol row-$severity'>
  1346. <td valign='top'>" . l($cur->wid, "admin/config/watchdog/$cur->wid", "sev_filter=$sev_filter&type_filter=$type_filter&page=$page") . "</td>
  1347. <td valign='top'>$when</td>
  1348. <td valign='top'>$type</td>
  1349. <td valign='top'>$msg</td>
  1350. <td valign='top'>$severity</td>
  1351. <td valign='top'>$cur->user_name</td>
  1352. </tr>
  1353. ";
  1354. }
  1355. $rtn .= "</table>";
  1356. // Display the pager that was generated by the pager_query above!
  1357. $rtn .= theme_pager();
  1358. return $rtn;
  1359. } // admin display watchdog
  1360. /**
  1361. * This is the "main" page for the admin module. It's what the user
  1362. * first sees when the click to go to the Admin page.
  1363. */
  1364. function admin_display_main() {
  1365. // Use default_school.
  1366. $de_catalog_year = admin_get_de_catalog_year(FALSE, 0);
  1367. // Has the system cron been run recently?
  1368. $cron_last_run = variable_get("cron_last_run", 0);
  1369. if ($cron_last_run < strtotime("NOW -7 DAYS")) {
  1370. // Warn admin that they need to have cron set up! It's been over a week!
  1371. fpm(t("Your system's cron process hasn't run in over a week. In order for FlightPath
  1372. to continue to function properly, a cron process much be configured.
  1373. You may @run_link or see the @status_link page for instructions.",
  1374. array("@status_link" => l(t("System status"), "admin/config/status"),
  1375. "@run_link" => l(t("run cron now"), "admin/config/run-cron"))));
  1376. }
  1377. $rtn = "";
  1378. fp_add_css(fp_get_module_path("admin") . "/css/admin.css");
  1379. fp_add_js(fp_get_module_path("admin") . "/js/admin.js");
  1380. $rtn .= "<table class='fp-semester-table admin-display-main-table' cellpadding='5'>
  1381. <tr>
  1382. <td valign='top' class='system-configuration-left'>
  1383. " . fp_render_menu_block(t("System Configuration"), "admin/config") . "
  1384. </td>
  1385. <td valign='top' class='advising-settings-right'>
  1386. ";
  1387. // Advising Settings
  1388. $rtn .= fp_render_section_title("Advising Settings", "admin-advising-settings");
  1389. $defs = array(0 => '');
  1390. if (module_enabled("schools")) {
  1391. $defs = schools_get_school_definitions();
  1392. }
  1393. foreach ($defs as $school_id => $school_name) {
  1394. $rtn .= "<div class='admin-advising-settings'><!--SCHOOL_NAME-->
  1395. <ul>
  1396. <li>" . t("Available terms for advising: %v", array("%v" => fp_space_csv(variable_get_for_school("available_advising_term_ids", "", $school_id)))) . "</li>
  1397. <li>" . t("Default advising term: %v", array("%v" => fp_space_csv(variable_get_for_school("advising_term_id", "", $school_id)))) . "</li>
  1398. <li>" . t("Current catalog year: %v", array("%v" => variable_get_for_school("current_catalog_year", "", $school_id))) . "</li>
  1399. <li>" . t("Current draft catalog year: %v", array("%v" => variable_get_for_school("current_draft_catalog_year", "", $school_id))) . "</li>
  1400. <li>" . t("Not released grades terms: %v", array("%v" => fp_space_csv(variable_get_for_school("not_released_grades_terms", "", $school_id)))) . "</li>
  1401. </ul>";
  1402. $url_suffix = "";
  1403. if (intval($school_id) != 0) {
  1404. $url_suffix .= "/" . $school_id;
  1405. }
  1406. if (user_has_permission("can_edit_advising_settings")) {
  1407. $rtn .= l("<i class='fa fa-pencil'></i> " . t("Edit Advising Settings") . "<!--SCHOOL_EDIT_LINK-->", "admin/edit-advising-settings" . $url_suffix);
  1408. }
  1409. if ($school_name) {
  1410. $rtn = str_replace("<!--SCHOOL_NAME-->", "<label>" . $school_name . "</label>", $rtn);
  1411. $rtn = str_replace("<!--SCHOOL_EDIT_LINK-->", t(" for ") . "<em>$school_name</em>", $rtn);
  1412. }
  1413. $rtn .= "</div>";
  1414. } // foreach
  1415. $rtn .= "</td>
  1416. </tr>";
  1417. $rtn .= "</table><br>"; // close table
  1418. $current_catalog_year = variable_get("current_catalog_year", 2006);
  1419. $earliest_catalog_year = variable_get("earliest_catalog_year", 2006);
  1420. if (user_has_permission("can_access_data_entry")) {
  1421. $rtn .= fp_render_section_title("Degree & Course Management", "degree-course-management");
  1422. $rtn .= "<div class='data-entry-cats'>";
  1423. // Have a pulldown here of years, then use javascript to hide/show relavant groups.
  1424. $rtn .= "&nbsp; &nbsp; &nbsp; " . t("Select a catalog year") . ": <select id='data-entry-select-cats' onChange='adminHideDECats();'>";
  1425. $selected_cat = ($de_catalog_year != "") ? $de_catalog_year : $current_catalog_year;
  1426. for ($t = $current_catalog_year +1; $t >= $earliest_catalog_year; $t--) {
  1427. $sel = ($t == $selected_cat) ? "selected" : "";
  1428. $rtn .= "<option value='$t' $sel>$t-" . ($t+1) . "</option>";
  1429. }
  1430. $rtn .= "</select> &nbsp; &nbsp;" . fp_get_js_alert_link(t("Newly duplicated year not showing up?<br><br>Make sure you have the correct 'Current Catalog Year' set under Edit Advising Settings."));
  1431. for ($t = $current_catalog_year +1; $t >= $earliest_catalog_year; $t--) {
  1432. $rtn .= "<ul class='data-entry-for-cat data-entry-for-cat-$t'>";
  1433. $rtn .= "<li>" . l(t("Degree plans for @year", array("@year" => "$t-" . ($t+1))), "admin/degrees", "de_catalog_year=$t") . "</li>";
  1434. $rtn .= "<li>" . l(t("Groups for @year", array("@year" => "$t-" . ($t+1))), "admin/groups", "de_catalog_year=$t") . "</li>";
  1435. $rtn .= "<li>" . l(t("Courses for @year", array("@year" => "$t-" . ($t+1))), "admin/courses", "de_catalog_year=$t") . "</li>";
  1436. $rtn .= "</ul>";
  1437. }
  1438. if (user_has_permission("can_edit_data_entry")) {
  1439. $rtn .= "<b>" . t("Administrator function:") . "</b><ul>
  1440. <li>" . l(t("Duplicate entire year worth of data to new year"), "admin/duplicate-year") . "</li>
  1441. </ul>";
  1442. }
  1443. $rtn .= "</div>";
  1444. // Draft changes?
  1445. $res = db_query("SELECT * FROM draft_instructions");
  1446. if (db_num_rows($res) > 0) {
  1447. $rtn .= "<div class='draft-changes-waiting-msg'>
  1448. " . t("Draft changes have been made which have yet to be applied.
  1449. When you are ready for your draft changes to appear in
  1450. production, click the link below.") . "</div>";
  1451. }
  1452. $rtn .= "<ul class='data-entry-draft-waiting'>
  1453. <li>" . l(t("Apply draft changes"), "admin/apply-draft-changes") . "</li>
  1454. </ul>";
  1455. }
  1456. return $rtn;
  1457. }

Functions

Namesort descending Description
admin_advising_settings_form This is a systems settings form, which lets the user edit advising variabled for FlightPath.
admin_apply_draft_changes_form This form lets the user apply draft changes (if they can supply the passcode)
admin_apply_draft_changes_form_submit We are going to set up a batch operation for apply draft changes, to reduce the chance of it timing out with large amounts of data.
admin_apply_draft_changes_form_validate Before we apply changes, make sure the password is correct.
admin_apply_draft_changes_perform_batch_operation
admin_cron hook_cron
admin_display_main This is the "main" page for the admin module. It's what the user first sees when the click to go to the Admin page.
admin_display_tools_screen This page displays all the tools that the user has access to use.
admin_display_watchdog
admin_display_watchdog_entry Display the details of a particular watchdog entry, specified by its table id.
admin_duplicate_year_form This form will allow the user to duplicate an entire year's worth of data to a new catalog year.
admin_duplicate_year_form_submit This function will set up a batch operation so we can duplicate an entire year with less chance of time or memory running out.
admin_duplicate_year_form_validate Before we duplicate the year, make sure the password is correct.
admin_duplicate_year_perform_batch_operation The is the batch operation to duplicate from $old_year to $new_year
admin_get_de_catalog_year Get the "de_catalog_year" from the REQUEST. If it's not there or invalid, pull it from our system settings.
admin_get_semester_name Convienence function to get semester's default regular name.
admin_menu Implementation of hook_menu
admin_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.
admin_perm
admin_urgent_message_form Meant to be fed into "fp_system_settings_form()", this function returns an array which will automatically save values to our "variables" table.