protected function _upgrade() { switch ($this->_version) { case "1.0": // Upgrade tables \b2db\Core::getTable('TBGVCSIntegrationCommitsTable')->create(); \b2db\Core::getTable('TBGVCSIntegrationFilesTable')->create(); \b2db\Core::getTable('TBGVCSIntegrationIssueLinksTable')->create(); TBGVCSIntegrationCommitsTable::getTable()->createIndexes(); TBGVCSIntegrationFilesTable::getTable()->createIndexes(); TBGVCSIntegrationIssueLinksTable::getTable()->createIndexes(); // Migrate data from old table to new tables $crit = new \b2db\Criteria(); $crit->addOrderBy(TBGVCSIntegrationTable::DATE, \b2db\Criteria::SORT_DESC); $results = TBGVCSIntegrationTable::getTable()->doSelect($crit); if ($results instanceof \b2db\Resultset && $results->count() > 0) { $commits = array(); while ($row = $results->getNextRow()) { $rev = $row->get(TBGVCSIntegrationTable::NEW_REV); if (array_key_exists($rev, $commits)) { // Add a new file or issue to the commit data $commits[$rev]['files'][$row->get(TBGVCSIntegrationTable::FILE_NAME)] = array('file_name' => $row->get(TBGVCSIntegrationTable::FILE_NAME), 'action' => $row->get(TBGVCSIntegrationTable::ACTION)); $commits[$rev]['issues'][$row->get(TBGVCSIntegrationTable::ISSUE_NO)] = $row->get(TBGVCSIntegrationTable::ISSUE_NO); } else { // All issues will be of the same project, so use one issue $issue = TBGContext::factory()->TBGIssue($results->get(TBGVCSIntegrationTable::ISSUE_NO)); // Add details of a new commit $commits[$rev] = array('commit' => array(), 'files' => array(), 'issues' => array()); $commits[$rev]['commit'] = array('new_rev' => $rev, 'old_rev' => $row->get(TBGVCSIntegrationTable::OLD_REV), 'author' => $row->get(TBGVCSIntegrationTable::AUTHOR), 'date' => $row->get(TBGVCSIntegrationTable::DATE), 'log' => $row->get(TBGVCSIntegrationTable::LOG), 'scope' => $row->get(TBGVCSIntegrationTable::SCOPE), 'project' => $issue->getProject()); $commits[$rev]['files'][$row->get(TBGVCSIntegrationTable::FILE_NAME)] = array('file_name' => $row->get(TBGVCSIntegrationTable::FILE_NAME), 'action' => $row->get(TBGVCSIntegrationTable::ACTION)); $commits[$rev]['issues'][$row->get(TBGVCSIntegrationTable::ISSUE_NO)] = $row->get(TBGVCSIntegrationTable::ISSUE_NO); } } foreach ($commits as $commit) { $files = array(); $issues = array(); $scope = TBGContext::factory()->TBGScope($commit['commit']['scope']); try { $author = TBGContext::factory()->TBGUser($commit['commit']['author']); } catch (Exception $e) { $author = TBGContext::factory()->TBGUser(TBGSettings::getDefaultUserID()); } if (!$author instanceof TBGUser) { $author = TBGContext::factory()->TBGUser(TBGSettings::getDefaultUserID()); } // Add the commit $inst = new TBGVCSIntegrationCommit(); $inst->setAuthor($author); $inst->setDate($commit['commit']['date']); $inst->setLog($commit['commit']['log']); $inst->setPreviousRevision($commit['commit']['old_rev']); $inst->setRevision($commit['commit']['new_rev']); $inst->setProject($commit['commit']['project']); $inst->setScope($scope); $inst->save(); // Process issue list, remove duplicates $issues = $commit['issues']; $files = $commit['files']; $commit = $inst; foreach ($files as $file) { // Add affected files $inst = new TBGVCSIntegrationFile(); $inst->setCommit($commit); $inst->setFile($file['file_name']); $inst->setAction($file['action']); $inst->setScope($scope); $inst->save(); } foreach ($issues as $issue) { // Add affected issues $issue = TBGContext::factory()->TBGIssue($issue); $inst = new TBGVCSIntegrationIssueLink(); $inst->setIssue($issue); $inst->setCommit($commit); $inst->setScope($scope); $inst->save(); } } } // Migrate settings to new format $access_method = $this->getSetting('use_web_interface'); $passkey = $this->getSetting('vcs_passkey'); foreach (TBGProject::getAll() as $project) { $projectId = $project->getID(); $web_path = $this->getSetting('web_path_' . $projectId); $web_repo = $this->getSetting('web_repo_' . $projectId); // Check if enabled if ($web_path == '') { continue; } switch ($this->getSetting('web_type_' . $projectId)) { case 'viewvc': $base_url = $web_path . '/' . '?root=' . $web_repo; $link_rev = '&view=rev&revision=%revno'; $link_file = '&view=log'; $link_diff = '&r1=%revno&r2=%oldrev'; $link_view = '&revision=%revno&view=markup'; break; case 'viewvc_repo': $base_url = $web_path; $link_rev = '/?view=rev&revision=%revno'; $link_file = '/%file?view=log'; $link_diff = '/%file?r1=%revno&r2=%oldrev'; $link_view = '/%file?revision=%revno&view=markup'; break; case 'websvn': $base_url = $web_path; $link_rev = '/revision.php?repname=' . $web_repo . '&isdir=1&rev=%revno'; $link_file = '/log.php?repname=' . $web_repo . '&path=/$%file'; $link_diff = '/comp.php?repname=' . $web_repo . '&compare[]=/%file@%revno&compare[]=/%file@%oldrev'; $link_view = '/filedetails.php?repname=' . $web_repo . '&path=/%file&rev=%revno'; break; case 'websvn_mv': $base_url = $web_path; $link_rev = '/' . '?repname=' . $web_repo . '&op=log&isdir=1&rev=%revno'; $link_file = '/%file?repname=' . $web_repo; $link_diff = '/%file?repname=' . $web_repo . '&compare[]=/%file@%revno&compare[]=/%file@%oldrev'; $link_view = '/%file?repname=' . $web_repo . '&rev=%revno'; break; case 'loggerhead': $base_url = $web_path . '/' . $web_repo; $link_rev = '/revision/%revno'; $link_file = '/changes'; $link_diff = '/revision/%revno?compare_revid=%oldrev'; $link_view = '/annotate/head:/%file'; break; case 'gitweb': $base_url = $web_path . '/' . '?p=' . $web_repo; $link_rev = ';a=commitdiff;h=%revno'; $link_file = ';a=history;f=%file;hb=HEAD'; $link_diff = ';a=blobdiff;f=%file;hb=%revno;hpb=%oldrev'; $link_view = ';a=blob;f=%file;hb=%revno'; break; case 'cgit': $base_url = $web_path . '/' . $web_repo; $link_rev = '/commit/?id=%revno'; $link_file = '/log'; $link_diff = '/diff/%file?id=%revno?id2=%oldrev'; $link_view = '/tree/%file?id=%revno'; break; case 'hgweb': $base_url = $web_path . '/' . $web_repo; $link_rev = '/rev/%revno'; $link_file = '/log/tip/%file'; $link_diff = '/diff/%revno/%file'; $link_view = '/file/%revno/%file'; break; case 'github': $base_url = 'http://github.com/' . $web_repo; $link_rev = '/commit/%revno'; $link_file = '/commits/master/%file'; $link_diff = '/commit/%revno'; $link_view = '/blob/%revno/%file'; break; case 'gitlab': $base_url = $web_path . '/' . $web_repo; $link_rev = '/commit/%revno'; $link_file = '/commits/%branch/%file'; $link_diff = '/commit/%revno'; $link_view = '/blob/%revno/%file'; break; case 'bitbucket': $base_url = 'https://bitbucket.org/' . $web_repo; $link_rev = '/changeset/%revno'; $link_file = '/history/%file'; $link_diff = '/changeset/%revno#chg-%file'; $link_view = '/src/%revno/%file'; break; case 'gitorious': $base_url = $web_path . '/' . $web_repo; $link_rev = '/commit/%revno'; $link_file = '/blobs/history/master/%file'; $link_diff = '/commit/%revno'; $link_view = '/blobs/%revno/%file'; break; case 'rhodecode': $base_url = $web_path . '/' . $web_repo; $link_rev = '/changeset/%revno'; $link_file = '/changelog/%revno/%file'; $link_diff = '/diff/%file?diff2=%revno&diff1=%oldrev&fulldiff=1&diff=diff'; $link_view = '/files/%revno/%file'; break; } $this->saveSetting('browser_url_' . $projectId, $base_url); $this->saveSetting('log_url_' . $projectId, $link_file); $this->saveSetting('blob_url_' . $projectId, $link_diff); $this->saveSetting('diff_url_' . $projectId, $link_view); $this->saveSetting('commit_url_' . $projectId, $link_rev); // Access method $this->saveSetting('access_method_' . $projectId, $access_method); if ($access_method == self::ACCESS_HTTP) { $this->saveSetting('access_passkey_' . $projectId, $passkey); } // Enable VCS Integration $this->saveSetting('vcs_mode_' . $projectId, self::MODE_ISSUECOMMITS); // Remove old settings $this->deleteSetting('web_type_' . $projectId); $this->deleteSetting('web_path_' . $projectId); $this->deleteSetting('web_repo_' . $projectId); } // Remove old settings $this->deleteSetting('use_web_interface'); $this->deleteSetting('vcs_passkey'); // Upgrade module version $this->_version = $this->_module_version; $this->save(); break; } }
private function _fixTimestamps() { // Unlimited execution time set_time_limit(0); foreach (TBGScope::getAll() as $scope) { TBGContext::setScope($scope); // The first job is to work out the offsets that need applying $offsets = array('system', 'users'); $offsets['users'] = array(); $offsets['system'] = (int) TBGSettings::getGMToffset() * 3600; $settingstable = TBGSettingsTable::getTable(); $crit = $settingstable->getCriteria(); $crit->addWhere(TBGSettingsTable::NAME, 'timezone'); $crit->addWhere(TBGSettingsTable::MODULE, 'core'); $crit->addWhere(TBGSettingsTable::UID, 0, \b2db\Criteria::DB_NOT_EQUALS); $crit->addWhere(TBGSettingsTable::VALUE, 0, \b2db\Criteria::DB_NOT_EQUALS); $crit->addWhere(TBGSettingsTable::VALUE, 'sys', \b2db\Criteria::DB_NOT_EQUALS); $crit->addWhere(TBGSettingsTable::SCOPE, $scope->getID()); $res = $settingstable->doSelect($crit); if ($res instanceof \b2db\Resultset) { while ($user = $res->getNextRow()) { $offsets['users']['uid_' . $user->get(TBGSettingsTable::UID)] = (int) $user->get(TBGSettingsTable::VALUE) * 3600; } } // Now go through every thing which requires updating TBGContext::addAutoloaderClassPath(THEBUGGENIE_MODULES_PATH . 'publish' . DS . 'classes' . DS . 'B2DB'); TBGContext::addAutoloaderClassPath(THEBUGGENIE_MODULES_PATH . 'publish' . DS . 'classes'); // ARTICLE HISTORY $this->_fixUserDependentTimezone($offsets, TBGArticleHistoryTable::getTable(), TBGArticleHistoryTable::AUTHOR, TBGArticleHistoryTable::DATE, $scope); // ARTICLES $this->_fixUserDependentTimezone($offsets, TBGArticlesTable::getTable(), TBGArticlesTable::AUTHOR, TBGArticlesTable::DATE, $scope); // BUILDS $this->_fixNonUserDependentTimezone($offsets, TBGBuildsTable::getTable(), TBGBuildsTable::RELEASE_DATE, $scope, TBGBuildsTable::RELEASED); // COMMENTS $this->_fixUserDependentTimezone($offsets, TBGCommentsTable::getTable(), array('a' => TBGCommentsTable::POSTED_BY, 'b' => TBGCommentsTable::UPDATED_BY), array('a' => TBGCommentsTable::POSTED, 'b' => TBGCommentsTable::UPDATED), $scope); // EDITIONS $this->_fixNonUserDependentTimezone($offsets, TBGEditionsTable::getTable(), TBGEditionsTable::RELEASE_DATE, $scope, TBGEditionsTable::RELEASED); // ISSUES // This is a bit more complex so do this manually - we have to poke around with the issue log $table = TBGIssuesTable::getTable(); $crit = $table->getCriteria(); $crit->addWhere(TBGIssuesTable::SCOPE, $scope->getID()); $crit->addWhere(TBGIssuesTable::DELETED, false); $res = $table->doSelect($crit); if ($res) { while ($row = $res->getNextRow()) { $crit = TBGLogTable::getTable()->getCriteria(); $crit->addSelectionColumn(TBGLogTable::UID); $crit->addWhere(TBGLogTable::CHANGE_TYPE, TBGLogTable::LOG_ISSUE_ASSIGNED); $crit->addWhere(TBGLogTable::TARGET, $row->get(TBGIssuesTable::ID)); $crit->addWhere(TBGLogTable::TARGET_TYPE, TBGLogTable::TYPE_ISSUE); $crit->addOrderBy(TBGLogTable::TIME, b2db\Criteria::SORT_DESC); $crit->addOrderBy(TBGLogTable::ID, b2db\Criteria::SORT_DESC); if ($row2 = TBGLogTable::getTable()->doSelectOne($crit)) { $assigned_by = $row2->get(TBGLogTable::UID); } $crit = TBGLogTable::getTable()->getCriteria(); $crit->addSelectionColumn(TBGLogTable::UID); $crit->addWhere(TBGLogTable::TARGET, $row->get(TBGIssuesTable::ID)); $crit->addWhere(TBGLogTable::TARGET_TYPE, TBGLogTable::TYPE_ISSUE); $crit->addOrderBy(TBGLogTable::TIME, b2db\Criteria::SORT_DESC); $crit->addOrderBy(TBGLogTable::ID, b2db\Criteria::SORT_DESC); if ($row2 = TBGLogTable::getTable()->doSelectOne($crit)) { $updated_by = $row2->get(TBGLogTable::UID); } unset($crit); unset($row2); if (array_key_exists('uid_' . $row->get(TBGIssuesTable::POSTED_BY), $offsets['users'])) { $offset = $offsets['users']['uid_' . $row->get(TBGIssuesTable::POSTED_BY)]; } else { $offset = $offsets['system']; } if (isset($updated_by) && array_key_exists('uid_' . $updated_by, $offsets['users'])) { $offset2 = $offsets['users']['uid_' . $updated_by]; } elseif (isset($updated_by)) { $offset2 = $offsets['system']; } if (isset($assigned_by) && array_key_exists('uid_' . $assigned_by, $offsets['users'])) { $offset3 = $offsets['users']['uid_' . $assigned_by]; } elseif (isset($assigned_by)) { $offset3 = $offsets['system']; } $crit2 = $table->getCriteria(); $crit2->addUpdate(TBGIssuesTable::POSTED, (int) $row->get(TBGIssuesTable::POSTED) + $offset); if (isset($offset2)) { $crit2->addUpdate(TBGIssuesTable::LAST_UPDATED, (int) $row->get(TBGIssuesTable::LAST_UPDATED) + $offset2); unset($offset2); } if (isset($offset3)) { $crit2->addUpdate(TBGIssuesTable::BEING_WORKED_ON_BY_USER_SINCE, (int) $row->get(TBGIssuesTable::BEING_WORKED_ON_BY_USER_SINCE) + $offset3); unset($offset3); } $crit2->addWhere(TBGIssuesTable::ID, $row->get(TBGIssuesTable::ID)); $table->doUpdate($crit2); } } // LOG $this->_fixUserDependentTimezone($offsets, TBGLogTable::getTable(), TBGLogTable::UID, TBGLogTable::TIME, $scope); // MILESTONES // The conditions are a bit different here so do it manually $table = TBGMilestonesTable::getTable(); $crit = $table->getCriteria(); $crit->addWhere(TBGMilestonesTable::SCOPE, $scope->getID()); $res = $table->doSelect($crit); if ($res) { while ($row = $res->getNextRow()) { $offset = $offsets['system']; $crit2 = $table->getCriteria(); $added = 0; if ($row->get(TBGMilestonesTable::REACHED) > 0) { $crit2->addUpdate(TBGMilestonesTable::REACHED, (int) $row->get(TBGMilestonesTable::REACHED) + $offset); $added = 1; } if ($row->get(TBGMilestonesTable::SCHEDULED) > 0) { $crit2->addUpdate(TBGMilestonesTable::SCHEDULED, (int) $row->get(TBGMilestonesTable::SCHEDULED) + $offset); $added = 1; } if ($row->get(TBGMilestonesTable::STARTING) > 0) { $crit2->addUpdate(TBGMilestonesTable::STARTING, (int) $row->get(TBGMilestonesTable::STARTING) + $offset); $added = 1; } // Only do something if at least one call to addUpdate is done if ($added == 1) { $crit2->addWhere(TBGMilestonesTable::ID, $row->get(TBGMilestonesTable::ID)); $table->doUpdate($crit2); } } } // PROJECTS $this->_fixNonUserDependentTimezone($offsets, TBGProjectsTable::getTable(), TBGProjectsTable::RELEASE_DATE, $scope, TBGProjectsTable::RELEASED); // VCS INTEGRATION // check if module is loaded $modules = TBGModulesTable::getTable()->getModulesForScope($scope->getID()); if ($modules['vcs_integration'] == true) { TBGContext::addAutoloaderClassPath(THEBUGGENIE_MODULES_PATH . 'vcs_integration' . DS . 'classes' . DS . 'B2DB'); TBGContext::addAutoloaderClassPath(THEBUGGENIE_MODULES_PATH . 'vcs_integration' . DS . 'classes'); $this->_fixUserDependentTimezone($offsets, TBGVCSIntegrationTable::getTable(), TBGVCSIntegrationTable::AUTHOR, TBGVCSIntegrationTable::DATE, $scope); } } }
public function addNewCommit($project, $commit_msg, $old_rev, $new_rev, $date = null, $changed, $author) { /* Find issues to update */ $fixes_grep = "#((bug|issue|ticket|fix|fixes|fixed|fixing|applies to|closes|references|ref|addresses|re|see|according to|also see)\\s\\#?(([A-Z0-9]+\\-)?\\d+))#ie"; $output = ''; $f_issues = array(); try { TBGContext::getI18n(); } catch (Exception $e) { TBGContext::reinitializei18n(); } try { $project = new TBGProject($project); } catch (Exception $e) { return TBGContext::getI18n()->__('Error: Invalid project ID'); } if (preg_match_all($fixes_grep, $commit_msg, $f_issues)) { // Github if (is_array($changed)) { $entries = $changed; $changed = ''; // Now handle changed files foreach ($entries[0] as $file) { $changed .= 'M' . $file . "\n"; } // Now handle new files foreach ($entries[1] as $file) { $changed .= 'A' . $file . "\n"; } // Now handle deleted files foreach ($entries[2] as $file) { $changed .= 'D' . $file . "\n"; } } $f_issues = array_unique($f_issues[3]); $file_lines = preg_split('/[\\n\\r]+/', $changed); $files = array(); foreach ($file_lines as $aline) { $action = substr($aline, 0, 1); if ($action == "A" || $action == "U" || $action == "D" || $action == "M") { $theline = trim(substr($aline, 1)); $files[] = array($action, $theline); } } foreach ($f_issues as $issue_no) { TBGContext::setCurrentProject($project); $theIssue = TBGIssue::getIssueFromLink($issue_no, true); if ($theIssue instanceof TBGIssue) { $uid = 0; /* * Some VCSes use a different format of storing the committer's name. Systems like bzr, git and hg use the format * Joe Bloggs <*****@*****.**>, instead of a classic username. Therefore a user will be found via 4 queries: * a) First we extract the email if there is one, and find a user with that email * b) If one is not found - or if no email was specified, then instead test against the real name (using the name part if there was an email) * c) the username or full name is checked against the friendly name field * d) and if we still havent found one, then we check against the username * e) and if we STILL havent found one, we just say the user is id 0 (unknown user). */ if (preg_match("/(?<=<)(.*)(?=>)/", $author, $matches)) { $email = $matches[0]; // a) $crit = new B2DBCriteria(); $crit->setFromTable(TBGUsersTable::getTable()); $crit->addSelectionColumn(TBGUsersTable::ID); $crit->addWhere(TBGUsersTable::EMAIL, $email); $row = TBGUsersTable::getTable()->doSelectOne($crit); if ($row != null) { $uid = $row->get(TBGUsersTable::ID); } else { // Not found by email preg_match("/(?<=^)(.*)(?= <)/", $author, $matches); $author = $matches[0]; } } // b) if ($uid == 0) { $crit = new B2DBCriteria(); $crit->setFromTable(TBGUsersTable::getTable()); $crit->addSelectionColumn(TBGUsersTable::ID); $crit->addWhere(TBGUsersTable::REALNAME, $author); $row = TBGUsersTable::getTable()->doSelectOne($crit); if ($row != null) { $uid = $row->get(TBGUsersTable::ID); } } // c) if ($uid == 0) { $crit = new B2DBCriteria(); $crit->setFromTable(TBGUsersTable::getTable()); $crit->addSelectionColumn(TBGUsersTable::ID); $crit->addWhere(TBGUsersTable::BUDDYNAME, $author); $row = TBGUsersTable::getTable()->doSelectOne($crit); if ($row != null) { $uid = $row->get(TBGUsersTable::ID); } } // d) if ($uid == 0) { $crit = new B2DBCriteria(); $crit->setFromTable(TBGUsersTable::getTable()); $crit->addSelectionColumn(TBGUsersTable::ID); $crit->addWhere(TBGUsersTable::UNAME, $author); $row = TBGUsersTable::getTable()->doSelectOne($crit); if ($row != null) { $uid = $row->get(TBGUsersTable::ID); } } $theIssue->addSystemComment(TBGContext::getI18n()->__('Issue updated from code repository'), TBGContext::getI18n()->__('This issue has been updated with the latest changes from the code repository.<source>%commit_msg%</source>', array('%commit_msg%' => $commit_msg)), $uid); foreach ($files as $afile) { if ($date == null) { $date = time(); } TBGVCSIntegrationTable::addEntry($theIssue->getID(), $afile[0], $commit_msg, $afile[1], $new_rev, $old_rev, $uid, $date); } $output .= 'Updated ' . $theIssue->getFormattedIssueNo() . "\n"; } else { $output .= 'Can\'t find ' . $issue_no . ' so not updating that one.' . "\n"; } } } return $output; }