static function getFromItem($item) { ### get changes ### $all_changes = $all_changes = ItemChange::getItemChanges(array('item' => $item->id)); $versions = array(new ItemVersion(array('version_number' => 1, 'date_from' => $item->created, 'author' => $item->created_by))); $last_version = $versions[0]; $version_number = 2; $modified_last = NULL; foreach ($all_changes as $cf) { $flag_new = false; if ($cf->modified != $modified_last) { $flag_new = true; } if (isset($last_version) && $last_version->author != $cf->modified_by) { $flag_new; } if ($flag_new) { $version = new ItemVersion(array('version_number' => $version_number++, 'date_from' => $cf->modified, 'author' => $cf->modified_by)); $modified_last = $cf->modified; $versions[] = $version; $last_version = $versions[count($versions) - 2]; $last_version->date_to = $cf->modified; } $last_version->values[$cf->field] = $cf->value_old; #$versions[count($versions)-1]->values[$cf->field]= 'bla'; } ### finally fill out latest values ### if (count($versions) > 1) { foreach ($versions[count($versions) - 2]->values as $fname => $value) { $versions[count($versions) - 1]->values[$fname] = $item->{$fname}; } $versions[count($versions) - 1]->date_to = getGMTString(); ### fill in next values ### $changed = array(); foreach (array_reverse($versions) as $v) { foreach ($v->values as $name => $value) { if (isset($changed[$name])) { $v->values_next[$name] = $changed[$name]; } else { $v->values_next[$name] = $item->{$name}; } $changed[$name] = $value; } } } return $versions; }
/** * revert changes of a person * * Notes: * - This function is only available of people with RIGHT_PROJECT_EDIT. * - This will only effect changes to fields. * - Following changes will not be reverted: * - Creation of new items (Tasks, Topis, Efforts, Projects, etc.) * - Task-assignments * - Uploading of files * * person - id of person who did the changes * data - date to with revert changes * delete_history (Default off) - Reverting can't be undone! The person's modification are lost forever! * This can be useful on massive changes to avoid sending huge * notification mails. */ function personRevertChanges() { global $PH; global $auth; ### check rights ### if (!$auth->cur_user->user_rights & RIGHT_PROJECT_EDIT) { $PH->abortWarning("You require the right to edit projects."); } ### get person ### $person_id = getOnePassedId('person', 'people_*'); if (!($person = Person::getVisibleById($person_id))) { $PH->abortWarning(sprintf(__("invalid Person #%s"), $person_id)); return; } $page = new Page(); $page->tabs['admin'] = array('target' => "index.php?go=systemInfo", 'title' => __('Admin', 'top navigation tab'), 'bg' => "misc"); $page->cur_tab = 'admin'; $page->crumbs[] = new NaviCrumb(array('target_id' => 'systemInfo')); $page->title = __("Reverting user changes"); $page->type = __("Admin"); #$page->title_minor=get('go'); echo new PageHeader(); echo new PageContentOpen(); $block = new PageBlock(array('title' => __('Overview'), 'id' => 'overview')); $block->render_blockStart(); echo "<div class=text>"; echo "<ul>"; ### get changes of person ### $count_reverted_fields = 0; $changes = ItemChange::getItemChanges(array('person' => $person_id, 'order_by' => 'id DESC')); foreach ($changes as $c) { if (!($project_item = DbProjectItem::getObjectById($c->item))) { #print "unable to get item %s" % $c->item; } else { ### Only revert changes, if item has not be editted by other person if ($project_item->modified_by == $person_id) { $field_name = $c->field; echo "<li>" . "<strong>" . asHtml($project_item->name) . "." . asHtml($field_name) . "</strong>" . " '" . asHtml($project_item->{$field_name}) . "' = '" . asHtml($c->value_old) . "'" . "</li>"; $count_reverted_fields++; if ($field_name == 'state') { if ($project_item->state == -1 && $c->value_old == 1) { $project_item->deleted_by = "0"; $project_item->deleted = "0000-00-00 00-00-00"; } } $project_item->{$field_name} = $c->value_old; $project_item->update(array($field_name, 'deleted_by', 'deleted'), false, false); } else { echo "<li>" . sprintf(__("Skipped recently editted item #%s: <b>%s<b>"), $project_item->id, asHtml($project_item->name)) . "</li>"; } $c->deleteFull(); } } echo "</ul>"; echo "<p>" . sprintf(__("Reverted all changes (%s) of user %s"), $count_reverted_fields, asHtml($person->nickname)) . "</p>"; echo "<p>" . __("newly created items by this user remain unaffected.") . "</p>"; echo "</div>"; $block->render_blockEnd(); ### close page echo new PageContentClose(); echo new PageHtmlEnd(); }
/** * internal function to add update markers to wiki texts * these markers will later be replaced by render_wiki (see render_wiki.inc.php FormatBlockChangemarks) */ public function getTextfieldWithUpdateNotes($fieldname) { require_once confGet('DIR_STREBER') . "db/db_itemchange.inc.php"; require_once confGet('DIR_STREBER') . 'db/db_itemperson.inc.php'; global $auth; ### has user last edited this item? ### if ($this->modified_by == $auth->cur_user->id) { return $this->{$fieldname}; } else { if ($item_people = ItemPerson::getAll(array('person' => $auth->cur_user->id, 'item' => $this->id))) { $ip = $item_people[0]; if ($ip->viewed_last > $this->modified) { return $this->{$fieldname}; } } else { return $this->{$fieldname}; } } $new_version = $this->{$fieldname}; $changes = ItemChange::getItemChanges(array('item' => $this->id, 'field' => $fieldname, 'order_by' => 'modified', 'date_min' => $ip->viewed_last)); if (!$changes) { return $this->{$fieldname}; } $old_version = $changes[0]->value_old; require_once confGet('DIR_STREBER') . "std/difference_engine.inc.php"; $ota = explode("\n", str_replace("\r\n", "\n", $old_version)); $nta = explode("\n", str_replace("\r\n", "\n", $new_version)); $diffs = new Diff($ota, $nta); $new_lines = array(); foreach ($diffs as $d) { foreach ($d as $do) { if ($do->type == 'copy') { $new_lines[] .= join("\n", $do->orig); } else { if ($do->type == 'add') { $new_lines[] = "[added word]" . join("\n", $do->closing) . "[/added word]"; } else { if ($do->type == 'delete') { $new_lines[] = '[deleted word]' . join("\n", $do->orig) . '[/deleted word]'; } else { if ($do->type == 'change') { ### render word level differences $wld = new WordLevelDiff($do->orig, $do->closing); $change_ratio = DbProjectItem::getEditRatioOfWordLevelDiff($wld); if ($change_ratio > 0.1) { $new_lines[] = "[deleted word]" . join("\n", $do->orig) . "[/deleted word]"; $new_lines[] = "[added word]" . join("\n", $do->closing) . "[/added word]"; } else { $new_lines[] = formatWordLevelDiff($wld); } } } } } } } $buffer = join($new_lines, "\n"); #debugMessage($old_version); #debugMessage( htmlspecialchars($buffer)); return $buffer; }
static function getChangeLines($query_options) { global $PH; global $auth; fillMissingValues($query_options, array('alive_only' => false)); $date_compare = isset($query_options['date_min']) ? $query_options['date_min'] : "0000-00-00"; /** * get list of items touched by other people */ $changed_items = DbProjectItem::getAll($query_options); /** * go through list */ $changes = array(); foreach ($changed_items as $i) { $change_type = NULL; if (!isset($query_options['project'])) { $project = Project::getVisibleById($i->project); } else { $project = NULL; } /** * get item-change-type depeding on dates */ if ($i->deleted >= $i->modified) { $change_type = ITEM_DELETED; } else { if ($i->modified > $i->created) { $change_type = ITEM_MODIFIED; } else { $change_type = ITEM_NEW; } } /** * build up change-list */ switch ($change_type) { case ITEM_NEW: if ($i->type == ITEM_TASK) { if (!($task = Task::getVisibleById($i->id))) { continue; } if ($assigned_people = $task->getAssignedPeople()) { $tmp = array(); foreach ($assigned_people as $ap) { $tmp[] = $ap->getLink(); } $html_assignment = __('to', 'very short for assigned tasks TO...') . ' ' . implode(', ', $tmp); } else { $html_assignment = ''; } $html_details = ''; if ($tmp = $task->getFolderLinks(true, $project)) { $html_details .= __('in', 'very short for IN folder...') . ' ' . $tmp; } if ($task->prio != PRIO_NORMAL && $task->prio != PRIO_UNDEFINED) { global $g_prio_names; $html_details .= ' / ' . $g_prio_names[$task->prio]; } $change = new ChangeLine(array('item' => $task, 'person_by' => $i->created_by, 'timestamp' => $i->created, 'item_id' => $i->id, 'html_what' => '<span class=new>' . __('new') . ' ' . $task->getLabel() . '</span>', 'txt_what' => __('new') . ' ' . $task->getLabel(), 'type' => ChangeLine::NEW_TASK, 'html_assignment' => $html_assignment, 'html_details' => $html_details)); $changes[] = $change; } else { if ($i->type == ITEM_FILE) { require_once confGet('DIR_STREBER') . 'db/class_file.inc.php'; if ($file = File::getVisibleById($i->id)) { $change = new ChangeLine(array('item' => $file, 'person_by' => $i->created_by, 'timestamp' => $i->created, 'item_id' => $i->id, 'html_what' => __('New file'), 'txt_what' => __('New file'), 'type' => ChangeLine::NEW_FILE, 'html_details' => $file->name)); $changes[] = $change; } } } break; case ITEM_MODIFIED: $timestamp_last_change = $date_compare; # make sure we use the last occured change type /** * modified tasks */ $type = ChangeLine::UPDATED; if ($i->type == ITEM_TASK) { if (!($task = Task::getVisibleById($i->id))) { continue; } if ($assigned_people = $task->getAssignedPeople()) { $tmp = array(); foreach ($assigned_people as $ap) { $tmp[] = $ap->getLink(); } $html_assignment = __('to', 'very short for assigned tasks TO...') . ' ' . implode(', ', $tmp); } else { $html_assignment = ''; } $html_details = ''; if ($tmp = $task->getFolderLinks(true, $project)) { $html_details .= __('in', 'very short for IN folder...') . ' ' . $tmp; } $txt_what = $html_what = __('modified'); $type = ChangeLine::UPDATED; $html_comment = ''; if ($comments = Comment::getAll(array('person' => $i->modified_by, 'task' => $task->id, 'date_min' => $timestamp_last_change, 'order_by' => 'created ASC'))) { $last_comment = $comments[count($comments) - 1]; $timestamp_last_change = $last_comment->created; if ($last_comment->name && $last_comment->name != __('New Comment')) { # ignore default title $html_comment = strip_tags($last_comment->name) . ': '; } $html_comment .= strip_tags($last_comment->description); $html_comment = asHtml($html_comment); } ### get changed fields ### $changed_fields_hash = array(); $html_functions = false; if ($changed_fields_list = ItemChange::getItemChanges(array('item' => $i->id, 'person' => $i->modified_by, 'date_min' => $date_compare))) { foreach ($changed_fields_list as $cf) { $changed_fields_hash[$cf->field] = $cf; } if (isset($changed_fields_hash['status'])) { $status_old = $changed_fields_hash['status']->value_old; if ($task->status == STATUS_COMPLETED && $task->status > $status_old) { $txt_what = $html_what = __('completed') . ' ' . $task->getLabel(); $html_functions = $PH->getLink('tasksApproved', __('Approve Task'), array('tsk' => $task->id)); unset($changed_fields_hash['status']); } else { if ($task->status == STATUS_APPROVED && $task->status > $status_old) { $txt_what = $html_what = __('approved'); unset($changed_fields_hash['status']); } else { if ($task->status == STATUS_CLOSED && $task->status > $status_old) { $txt_what = $html_what = __('closed'); unset($changed_fields_hash['status']); } else { if ($task->status == STATUS_OPEN && $task->status < $status_old) { $txt_what = $html_what = __('reopened'); unset($changed_fields_hash['status']); } else { if ($task->status == STATUS_OPEN) { unset($changed_fields_hash['status']); } else { if ($task->status == STATUS_BLOCKED) { $txt_what = $html_what = __('is blocked'); unset($changed_fields_hash['status']); } } } } } } } } if (isset($changed_fields_hash['parent_task'])) { $txt_what = $html_what = __('moved'); $type = ChangeLine::MOVED; unset($changed_fields_hash['parent_task']); } else { if (count($changed_fields_hash) == 1 && isset($changed_fields_hash['name'])) { $txt_what = $html_what = __('renamed'); $type = ChangeLine::RENAMED; } else { if (count($changed_fields_hash) == 1 && isset($changed_fields_hash['description'])) { $txt_what = $html_what = __('edit wiki'); $type = ChangeLine::EDITED_WIKI; } else { if (count($changed_fields_hash)) { # status does not count $html_details .= ' / ' . __('changed:') . ' ' . implode(', ', array_keys($changed_fields_hash)); } else { if ($html_comment) { $txt_what = $html_what = __('commented'); $type = ChangeLine::COMMENTED; } } } } } if ($html_comment) { $html_details .= ' / ' . $html_comment; } /** * any recents assignments ? * - to avoid confusion only list assignmets if it was to last action, * */ require_once "db/class_taskperson.inc.php"; $count_assignments = 0; if ($assignments = TaskPerson::getTaskPeople(array('task' => $task->id, 'project' => $task->project, 'date_min' => $task->modified))) { $t_timestamp = ''; foreach ($assignments as $a) { if ($a->person != $task->modified_by && $a->created_by == $task->modified_by && $a->assigntype != ASSIGNTYPE_INITIAL) { $t_timestamp = $a->created; $count_assignments++; } } if ($count_assignments && $timestamp_last_change < $t_timestamp) { $type = ChangeLine::ASSIGNED; $txt_what = $html_what = __('assigned'); $timestamp_last_change = $t_timestamp; } if ($html_comment) { $html_details .= ' / ' . $html_comment; } } /** * any recents attachments by last editor ? */ require_once "db/class_file.inc.php"; if ($files = File::getAll(array('parent_item' => $task->id, 'project' => $task->project, 'date_min' => $date_compare, 'created_by' => $task->modified_by))) { $count_attached_files = 0; $html_attached = __("attached") . ": "; $t_timestamp = ''; $separator = ''; foreach ($files as $f) { if ($task->modified_by == $f->modified_by) { $t_timestamp = $f->created; $count_attached_files++; $html_attached .= $separator . $PH->getLink('fileView', $f->name, array('file' => $f->id)); $separator = ', '; } } if ($count_attached_files) { $type = ChangeLine::ATTACHED_FILE; $txt_what = $html_what = __('attached file to'); if ($timestamp_last_change < $t_timestamp) { $html_details .= ' / ' . $html_attached; $timestamp_last_change = $t_timestamp; } } } if (count($changed_fields_hash)) { $html_details .= " / " . $PH->getLink('itemViewDiff', NULL, array('item' => $task->id, 'date1' => $date_compare, 'date2' => gmdate("Y-m-d H:i:s"))); } if ($html_functions) { $html_details .= " | " . $html_functions; } $change = new ChangeLine(array('person_by' => $i->modified_by, 'timestamp' => $i->modified, 'item_id' => $i->id, 'item' => $task, 'type' => $type, 'txt_what' => $txt_what, 'html_what' => $html_what, 'html_assignment' => $html_assignment, 'html_details' => $html_details)); $changes[] = $change; } else { if ($i->type == ITEM_FILE) { require_once confGet('DIR_STREBER') . 'db/class_file.inc.php'; if ($file = File::getVisibleById($i->id)) { $change = new ChangeLine(array('item' => $file, 'person_by' => $i->created_by, 'timestamp' => $i->created, 'item_id' => $i->id, 'html_what' => __('changed File'), 'txt_what' => __('changed File'), 'type' => ChangeLine::NEW_FILE, 'html_details' => $file->name)); $changes[] = $change; } } } break; case ITEM_DELETED: /** * deleted tasks */ if ($i->type == ITEM_TASK) { if (!($task = Task::getVisibleById($i->id))) { continue; } if ($assigned_people = $task->getAssignedPeople()) { $tmp = array(); foreach ($assigned_people as $ap) { $tmp[] = $ap->getLink(); } $html_assignment = __('to', 'very short for assigned tasks TO...') . ' ' . implode(', ', $tmp); } else { $html_assignment = ''; } $html_details = ''; if ($tmp = $task->getFolderLinks(true, $project)) { $html_details .= __('in', 'very short for IN folder...') . ' ' . $tmp; } $html_details .= '|' . $PH->getLink('itemsRestore', __('restore'), array('item' => $task->id)); $txt_what = $html_what = __('deleted'); $change = new ChangeLine(array('item' => $task, 'person_by' => $i->deleted_by, 'timestamp' => $i->deleted, 'item_id' => $i->id, 'type' => ChangeLine::DELETED, 'txt_what' => $txt_what, 'html_what' => $html_what, 'html_assignment' => $html_assignment, 'html_details' => $html_details)); $changes[] = $change; } else { if ($i->type == ITEM_FILE) { require_once confGet('DIR_STREBER') . 'db/class_file.inc.php'; if ($file = File::getVisibleById($i->id)) { $change = new ChangeLine(array('item' => $file, 'person_by' => $i->created_by, 'timestamp' => $i->created, 'item_id' => $i->id, 'html_what' => __('deleted File'), 'txt_what' => ChangeLine::DELETED, 'html_details' => $file->name)); $changes[] = $change; } } } break; default: trigger_error("unknown change-type {$change_type}", E_USER_WARNING); break; } } return $changes; }