uid = 1; node_delete($nid); $user->uid = 0; // revert faked login watchdog('issue_tracker', $log_msg); return TRUE; } function _issue_get_unique_file_name($basename) { $dir = file_directory_path(); return file_create_filename($basename, $dir); } /** * Utility function for processing RPC calls from the backend for deleting an issue's attachment. * Note that we don't delete the file and database entries directly as in previous versions, * instead we just mark the file in the node for removal and use the upload modules API functions for this. * * @param $issue_id The ID of the issue. * @param $filename The filename of the file to delete. * @return Boolean indicating a successful removal. */ function _issue_remove_attachment($issue_id, $filename) { $node = _node_from_issue_id($issue_id); foreach ($node->files as $fid => $file) { $file = (object)$file; if ($file->filename == $filename) { // mark for removal $file->remove = 1; } } upload_save($node); return TRUE; } function _issue_add_attachment($issue_id, $filename, $filepath, $description) { $result = db_query("SELECT nid FROM {issues} WHERE issue_id=%d", $issue_id); $row = db_fetch_object($result); $node = node_load($row->nid); $fid = db_last_insert_id('files', 'fid'); $mime = mime_content_type($filepath); $size = filesize($filepath); db_query("INSERT INTO `files`(`fid`,`nid`,`filename`,`filepath`,`filemime`,`filesize`) VALUES (%d,%d,'%s', '%s', '%s',%d)", $fid, $node->nid, $filename, $filepath, $mime, $size); db_query("INSERT INTO `file_revisions`(`fid`,`vid`,`description`,`list`) VALUES (%d,%d,'%s',1)", $fid, $node->vid, $filename); return TRUE; } /** * Creates an issue reply (comment) in the drupal db * * @param int $issueId * @param string $origo_user * @param string $description * @param string $tags The updated tags of the issue itself (comments do not have tags themselves) - must contain status tag */ function _issue_comment($issue_id, $origo_user, $description, $tags) { global $user; $user = user_load(array('name' => $origo_user)); if (! _is_authorized()) { $error_msg = 'issue_comment :: Blocked external client: '. ip_address(); watchdog('issue_tracker', $error_msg); return FALSE; } // fetch the id of the issue node we want to comment $result = db_query("SELECT nid FROM {issues} WHERE issue_id=%d", $issue_id); $row = db_fetch_object($result); $nid = $row->nid; $node = node_load($nid); // remove the tag annotations ("--- Issue ... ---" lines) $count = 1; while ($count > 0) { $description = preg_replace("/--- (.)+ ---/", "", $description, 1, $count); // TODO improve regex: /^--- (.)+ ---/ } $description = trim($description); // add the comment $values = array( 'nid' => $nid, 'pid' => 0, // the id of the parent comment is always 0 because nesting is not supported 'comment' => $description, 'author' => $origo_user, 'subject' => "Issue Reply", 'format' => _issue_tracker_get_filter_format_id(), 'op' => "Post comment", 'submit' => "Post comment", 'form_id' => 'comment_form' ); // special tags (assigned and status) have to be removed from the regular tags array and be stored separately $tag_array = preg_split("/\s?,\s?/", $tags, -1, PREG_SPLIT_NO_EMPTY); $values['assignedto'] = '-'; $values['issuestatus'] = 'status::open'; $values['resolution'] = '-'; foreach ($tag_array as $tag) { if (strpos($tag, "status::") === 0) { $values['issuestatus'] = $tag; } elseif (strpos($tag, "assigned::") === 0) { $values['assignedto'] = $tag; } elseif (strpos($tag, "resolution::") === 0) { $values['resolution'] = $tag; } else { if (isset($filtered_tags)) { $filtered_tags .= ', '. $tag; } else { $filtered_tags = $tag; } } } $values['taxonomy']['tags'][_issue_tracker_get_issue_vocabulary_id()] = $filtered_tags; $form_values = array('values' => $values); // We need to tell the hooks that it does not need to call the backend again $form_values['storage'][ISSUE_TRACKER_STORAGE_COMMENT_SOURCE] = 'api'; // Simulate form submit $result = drupal_execute('comment_form', $form_values, $values); return TRUE; } /** * Updates an issue in the drupal db. * * @param int $issue_id * @param string $title * @param string $description * @param string $tags (MUST contain status tag) * @param boolean $is_private */ function _issue_update($issue_id, $title, $description, $tags, $is_private) { global $user; if (! _is_authorized()) { $error_msg = 'issue_update :: Blocked external client: '. ip_address(); watchdog('issue_tracker', $error_msg); return FALSE; } if (strpos($tags, 'status::') === FALSE) { // status tag must be set watchdog('issue_tracker', 'issue_update rejected - status tag not set'); return FALSE; } // fetch the corresponding issue node $result = db_query("SELECT nid FROM {issues} WHERE issue_id=%d", $issue_id); $row = db_fetch_object($result); $node = node_load($row->nid); // update the node $node->title = $title; $node->body = $description; $node->taxonomy = NULL; // clear existing tags $node->taxonomy['tags'][_issue_tracker_get_issue_vocabulary_id()] = $tags; $node->private = (($is_private) ? 1 : 0); // tell the update hook that it does not need to call the backend again $node->source = "api"; // save the changes $user = user_load(array('uid' => $node->uid)); if ($user == NULL) { watchdog('issue_tracker', 'issue_update :: Could not load user'); return FALSE; } else { node_save($node); // triggers update hook } return TRUE; } /** * Creates new issue node and updates the drupal db accordingly. * * @param int $issue_id * @param string $origo_user * @param string $title * @param string $description * @param string $tags (MUST contain status tag) * @param boolean $is_private * @returns boolean TRUE if everything went fine, FALSE if an error occurred */ function _issue_add($issue_id, $origo_user, $title, $description, $tags, $is_private) { global $user; if (! _is_authorized()) { $error_msg = 'issue_add :: Blocked external client: '. ip_address(); watchdog('issue_tracker', $error_msg); return FALSE; } if (strpos($tags, 'status::') === FALSE) { // precondition violated: status tag must be set watchdog('issue_tracker', 'issue_add rejected - status tag not set'); return FALSE; } if ($issue_id == 0) { watchdog('issue_tracker', 'issue_add rejected - back-end issue ID is 0 (zero).'); return FALSE; } if ($title == '') { watchdog('issue_tracker', 'issue_add rejected - empty title.'); return FALSE; } $user = user_load(array('name' => $origo_user)); if (($user == NULL) || ($user->uid == 0)) { watchdog('issue_tracker', 'issue_add rejected - user not found.'); return FALSE; } $node = array( 'title' => $title, 'name' => $user->name, 'uid' => $user->uid, 'body' => $description, 'private' => (int)$is_private, 'type' => 'issue', 'format' => _issue_tracker_get_filter_format_id(), 'promote' => 1, 'status' => 1, 'comment' => 2, 'issue_id' => $issue_id ); $node['taxonomy']['tags'][_issue_tracker_get_issue_vocabulary_id()] = $tags; // Note: The issue_id field is only used as marker to let the insert hook // know that the issue has already been created in the backend so there is // no need to call it again. This field is not handled by drupal. if ($node = node_submit($node)) { node_save($node); // Note: triggers insert hook return TRUE; } else { watchdog('issue_tracker', 'issue_add :: node_submit failed'); return FALSE; } }