'admin/settings/journal', 'title' => t('Journal'), 'description' => t('Administer journal settings.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('journal_settings_form'), 'access' => $access, ); $items[] = array( 'path' => 'admin/logs/journal', 'title' => t('Journal entries'), 'description' => t('View journal entries.'), 'callback' => 'journal_view', 'access' => $access, ); } return $items; } /** * Add Journal fields to all forms. * * Any form, except a few pre-defined form_ids, will be extended by a fieldset * to enter a journal entry. * * @see journal_skip_form() */ function journal_form_alter($form_id, &$form) { if (!user_access('access journal')) { return; } // Do not extend this form, if it is in the list of form_ids to skip. if (journal_skip_form($form_id)) { return; } $entry_required = FALSE; // Shift system_settings_form buttons. if (isset($form['#base']) && $form['#base'] == 'system_settings_form') { $weight = $form['buttons']['#weight']; $form['buttons']['#weight'] = $weight + 1; $entry_required = TRUE; } // Prepend our journal submit handler, so we can eliminate the form value of // journal_entry, which would be saved as a variable for example in // system_settings_form() otherwise. $form['#submit'] = array('journal_form_submit' => array()) + (array)$form['#submit']; // Add journal entry field. $form['journal']['journal_entry'] = array( '#type' => 'textarea', '#title' => t('Journal entry'), '#description' => t('If not empty, contents of this field will be logged to the system journal.'), '#required' => $entry_required, ); } /** * Save a new journal entry and clean out form values. */ function journal_form_submit($form_id, $form_values) { if (!empty($form_values['journal_entry'])) { journal_add_entry($form_values['journal_entry']); } unset($form_values['journal_entry']); } /** * Indicate if a form must not extended. * * @param string $form_id * A form_id to check against. * * @return bool * True if form should be skipped, false if form can be extended. * * @todo Fetch custom form_ids from a variable. * @todo Allow to define custom form_ids in a settings page? * @todo Introduce a new FAPI attribute #journal = TRUE to require a journal * entry if Journal module is enabled - OR - introduce a new hook_journal? */ function journal_skip_form($form_id) { $skip_ids = array( 'search_block_form', 'user_login_block', 'devel_switch_user_form', ); if (in_array($form_id, $skip_ids)) { return TRUE; } return FALSE; } /** * Implementation of hook_user(). */ function journal_user($op, &$edit, &$user) { if ($op == 'delete') { db_query('UPDATE {journal} SET uid = 0 WHERE uid = %d', $user->uid); } } /** * Output a sortable table containing all journal entries. */ function journal_view() { $sql = "SELECT * FROM {journal} j INNER JOIN {users} u ON j.uid = u.uid"; $header = array( array('data' => t('Date'), 'field' => 'j.timestamp', 'sort' => 'asc'), array('data' => t('User'), 'field' => 'u.name'), t('Message'), t('Location'), ); $tablesort = tablesort_sql($header); $result = pager_query($sql . $tablesort, 50); return journal_output($result, $header); } /** * Render journal entries. * * Use this function to render and return * - a journal provided as a database query result resource or * - a custom journal provided as an array containing journal entry objects. * * This function may look insane to some, but it ensures that implementation of * journal module into other modules is as easy as possible. * * @param array $journal * A database query result resource or an array containing journal entry * objects to output. * @param array $header * An array containing table header data for HTML output. * @param string $format * Whether to output all log entries as 'html' or plain 'text'. * * @todo Add XML output format. */ function journal_output($journal, $header = array(), $format = 'html') { $type = gettype($journal); switch ($format) { case 'text': // Output delimiter in first line, since this may chance. $output = '\t' . "\n"; while ($entry = ($type == 'resource' ? db_fetch_object($journal) : array_shift($journal))) { $row = array( $entry->timestamp, $entry->uid, $entry->message, $entry->location, ); $output .= implode("\t", $row) ."\n"; } break; case 'html': default: while ($entry = ($type == 'resource' ? db_fetch_object($journal) : array_shift($journal))) { $rows[] = array( format_date($entry->timestamp, 'small'), theme('username', $entry), $entry->message, l($entry->location, $entry->location), ); } if (empty($rows)) { $rows[] = array(array('data' => t('No journal entries available.'), 'colspan' => 4)); } $output = theme('table', $header, $rows); $output .= theme('pager', NULL, 50, 0); break; } return $output; } /** * Convert a journal into an array. * * @param mixed $data * Journal data. * @param string $type * The type of passed-in journal data. * * @return array $journal * An array containing one or more journal entry objects. */ function journal_convert($data, $type = 'text') { $journal = array(); switch ($type) { case 'text': default: $data = explode('\n', $data); // Determine delimiter string. $delimiter = array_shift($data); while ($row = array_shift($data)) { $row = explode($delimiter, $row); $journal[] = (object)$row; } break; } return $journal; } /** * Store a new journal entry in the database. * * @param string $description */ function journal_add_entry($description) { global $user; $jid = db_next_id("{journal}_jid"); db_query("INSERT INTO {journal} (jid, uid, message, location, timestamp) VALUES (%d, %d, '%s', '%s', %d)", $jid, $user->uid, $description, $_GET['q'], time()); }