Index: trunk/extensions/CodeReview/CodeReview.i18n.php |
— | — | @@ -76,6 +76,11 @@ |
77 | 77 | 'code-rev-submit' => 'Save changes', |
78 | 78 | 'code-rev-submit-next' => 'Save & next unresolved', |
79 | 79 | |
| 80 | + 'code-releasenotes' => 'release notes', |
| 81 | + 'code-release-legend' => 'Generate release notes', |
| 82 | + 'code-release-startrev' => 'Start rev:', |
| 83 | + 'code-release-endrev' => 'Last rev:', |
| 84 | + |
80 | 85 | 'codereview-subtitle' => 'For $1', |
81 | 86 | |
82 | 87 | 'codereview-reply-link' => 'reply', |
Index: trunk/extensions/CodeReview/SpecialCode.php |
— | — | @@ -25,12 +25,7 @@ |
26 | 26 | $view = new CodeRevisionListView( $params[0] ); |
27 | 27 | break; |
28 | 28 | case 2: |
29 | | - if( $wgRequest->wasPosted() && !$wgRequest->getCheck('wpPreview') ) { |
30 | | - # Add any tags, Set status, Adds comments |
31 | | - $submit = new CodeRevisionCommitter( $params[0], $params[1] ); |
32 | | - $submit->execute(); |
33 | | - return; |
34 | | - } else if( $params[1] === 'tag' ) { |
| 29 | + if( $params[1] === 'tag' ) { |
35 | 30 | $view = new CodeTagListView( $params[0] ); |
36 | 31 | break; |
37 | 32 | } elseif( $params[1] === 'author' ) { |
— | — | @@ -42,6 +37,14 @@ |
43 | 38 | } elseif( $params[1] === 'comments' ) { |
44 | 39 | $view = new CodeCommentsListView( $params[0] ); |
45 | 40 | break; |
| 41 | + } elseif( $params[1] === 'releasenotes' ) { |
| 42 | + $view = new CodeReleaseNotes( $params[0] ); |
| 43 | + break; |
| 44 | + } else if( $wgRequest->wasPosted() && !$wgRequest->getCheck('wpPreview') ) { |
| 45 | + # Add any tags, Set status, Adds comments |
| 46 | + $submit = new CodeRevisionCommitter( $params[0], $params[1] ); |
| 47 | + $submit->execute(); |
| 48 | + return; |
46 | 49 | } else { // revision details |
47 | 50 | $view = new CodeRevisionView( $params[0], $params[1] ); |
48 | 51 | break; |
— | — | @@ -86,11 +89,8 @@ |
87 | 90 | |
88 | 91 | // Add subtitle for easy navigation |
89 | 92 | global $wgOut; |
90 | | - if ( $view instanceof CodeView ) { |
91 | | - $repo = $view->getRepo(); |
92 | | - if ( $repo ) |
93 | | - $wgOut->setSubtitle( wfMsgExt( 'codereview-subtitle', 'parse', |
94 | | - CodeRepoListView::getNavItem( $repo->getName() ) ) ); |
| 93 | + if( $view instanceof CodeView && ($repo = $view->getRepo()) ) { |
| 94 | + $wgOut->setSubtitle( wfMsgExt( 'codereview-subtitle', 'parse', CodeRepoListView::getNavItem( $repo->getName() ) ) ); |
95 | 95 | } |
96 | 96 | } |
97 | 97 | } |
Index: trunk/extensions/CodeReview/CodeReleaseNotes.php |
— | — | @@ -0,0 +1,122 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class CodeReleaseNotes extends CodeView { |
| 5 | + function __construct( $repoName ) { |
| 6 | + global $wgRequest; |
| 7 | + parent::__construct( $repoName ); |
| 8 | + $this->mRepo = CodeRepository::newFromName( $repoName ); |
| 9 | + $this->mPath = htmlspecialchars( trim( $wgRequest->getVal( 'path' ) ) ); |
| 10 | + if( strlen($this->mPath) && $this->mPath[0] !== '/' ) { |
| 11 | + $this->mPath = "/{$this->mPath}"; // make sure this is a valid path |
| 12 | + } |
| 13 | + $this->mPath = preg_replace( '/\/$/', '', $this->mPath ); // kill last slash |
| 14 | + $this->mStartRev = $wgRequest->getIntOrNull('startrev'); |
| 15 | + $this->mEndRev = $wgRequest->getIntOrNull('endrev'); |
| 16 | + } |
| 17 | + |
| 18 | + function execute() { |
| 19 | + if( !$this->mRepo ) { |
| 20 | + $view = new CodeRepoListView(); |
| 21 | + $view->execute(); |
| 22 | + return; |
| 23 | + } |
| 24 | + $this->showForm(); |
| 25 | + # Sanity/performance check... |
| 26 | + $lastRev = $this->mRepo->getLastStoredRev(); |
| 27 | + if( $this->mStartRev < ($lastRev - 5000) ) $this->mStartRev = NULL; |
| 28 | + # Show notes if we have at least a starting revision |
| 29 | + if( $this->mStartRev ) { |
| 30 | + $this->showReleaseNotes(); |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + protected function showForm() { |
| 35 | + global $wgOut, $wgScript, $wgUser; |
| 36 | + $special = SpecialPage::getTitleFor( 'Code', $this->mRepo->getName().'/releasenotes' ); |
| 37 | + $wgOut->addHTML( |
| 38 | + Xml::openElement( 'form', array( 'action' => $wgScript, 'method' => 'get' ) ) . |
| 39 | + "<fieldset><legend>".wfMsgHtml('code-release-legend')."</legend>" . |
| 40 | + Xml::hidden( 'title', $special->getPrefixedDBKey() ) . |
| 41 | + Xml::inputlabel( wfMsg("code-release-startrev"), 'startrev', 'startrev', 10, $this->mStartRev ) . |
| 42 | + ' ' . |
| 43 | + Xml::inputlabel( wfMsg("code-release-endrev"), 'endrev', 'endrev', 10, $this->mEndRev ) . |
| 44 | + ' ' . |
| 45 | + Xml::inputlabel( wfMsg("code-pathsearch-path"), 'path', 'path', 45, $this->mPath ) . |
| 46 | + ' ' . |
| 47 | + Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" . |
| 48 | + "</fieldset>" . Xml::closeElement( 'form' ) |
| 49 | + ); |
| 50 | + } |
| 51 | + |
| 52 | + protected function showReleaseNotes() { |
| 53 | + global $wgOut; |
| 54 | + $linker = new CodeCommentLinkerWiki( $this->mRepo ); |
| 55 | + $dbr = wfGetDB( DB_SLAVE ); |
| 56 | + if( $this->mEndRev ) { |
| 57 | + $where = 'cr_id BETWEEN '.intval($this->mStartRev).' AND '.intval($this->mEndRev); |
| 58 | + } else { |
| 59 | + $where = 'cr_id >= '.intval($this->mStartRev); |
| 60 | + } |
| 61 | + if( $this->mPath ) { |
| 62 | + $where .= ' AND (cr_path LIKE '.$dbr->addQuotes( $dbr->escapeLike("{$this->mPath}/").'%'); |
| 63 | + $where .= ' OR cr_path = '.$dbr->addQuotes($this->mPath).')'; |
| 64 | + } |
| 65 | + # Select commits within this range... |
| 66 | + $res = $dbr->select( 'code_rev', array('cr_message','cr_author'), |
| 67 | + array( |
| 68 | + 'cr_repo_id' => $this->mRepo->getId(), // this repo |
| 69 | + "cr_status NOT IN('reverted','deferred','fixme')", // not reverted/deferred/fixme |
| 70 | + "cr_message != ''", |
| 71 | + $where // in range |
| 72 | + ), |
| 73 | + __METHOD__, |
| 74 | + array( 'ORDER BY' => 'cr_id DESC' ) |
| 75 | + ); |
| 76 | + $wgOut->addHTML( '<ul>' ); |
| 77 | + # Output any relevant seeming commits... |
| 78 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 79 | + $summary = htmlspecialchars($row->cr_message); |
| 80 | + # Add this commit summary if needed |
| 81 | + if( $this->isRelevant( $summary ) ) { |
| 82 | + # Asterixs often used as point-by-point bullets |
| 83 | + $summary = strtr( $summary, '*', "\n" ); |
| 84 | + # Keep it short if possible... |
| 85 | + $blurbs = explode("\n",$summary); |
| 86 | + $summary = ""; |
| 87 | + foreach( $blurbs as $blurb ) { |
| 88 | + if( $this->isRelevant( $blurb ) ) { |
| 89 | + $summary .= trim($blurb) . "\n"; |
| 90 | + } |
| 91 | + } |
| 92 | + # Anything left? (this can happen with some heuristics) |
| 93 | + if( $summary ) { |
| 94 | + $summary = nl2br( trim($summary) ); // Newlines -> <br/> |
| 95 | + $wgOut->addHTML( "<li>" ); |
| 96 | + $wgOut->addWikiText( $linker->link($summary)." ''(".htmlspecialchars($row->cr_author).")''" ); |
| 97 | + $wgOut->addHTML( "</li>\n" ); |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + $wgOut->addHTML( '</ul>' ); |
| 102 | + } |
| 103 | + |
| 104 | + private function isRelevant( $summary ) { |
| 105 | + # Fixed a bug? |
| 106 | + if( preg_match( '/\bbug #?(\d+)\b/i', $summary, $m ) ) |
| 107 | + return true; |
| 108 | + # Config var? |
| 109 | + if( preg_match( '/\b\$wg[a-bA-Z]{3,100}\b/', $summary, $m ) ) |
| 110 | + return true; |
| 111 | + # Sanity check: summary cannot be *too* short to be useful |
| 112 | + if( mb_strlen($summary) < 40 ) |
| 113 | + return false; |
| 114 | + # All caps words? like "BREAKING CHANGE" |
| 115 | + if( preg_match( '/\b[A-Z]{8,20}\b/', $summary, $m ) ) |
| 116 | + return true; |
| 117 | + # Longish summary :) |
| 118 | + if( mb_strlen($summary) > 250 ) |
| 119 | + return true; |
| 120 | + # Otherwise false |
| 121 | + return false; |
| 122 | + } |
| 123 | +} |
Property changes on: trunk/extensions/CodeReview/CodeReleaseNotes.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 124 | + native |
Index: trunk/extensions/CodeReview/CodeRepoListView.php |
— | — | @@ -20,12 +20,12 @@ |
21 | 21 | |
22 | 22 | public static function getNavItem( $name ) { |
23 | 23 | global $wgLang; |
24 | | - |
25 | 24 | $text = "'''[[Special:Code/$name|$name]]''' ("; |
26 | 25 | $links[] = "[[Special:Code/$name/comments|" . wfMsgHtml( 'code-notes' ) . "]]"; |
27 | 26 | $links[] = "[[Special:Code/$name/status|" . wfMsgHtml( 'code-status' ) . "]]"; |
28 | 27 | $links[] = "[[Special:Code/$name/tag|" . wfMsgHtml( 'code-tags' ) . "]]"; |
29 | 28 | $links[] = "[[Special:Code/$name/author|" . wfMsgHtml( 'code-authors' ) . "]]"; |
| 29 | + $links[] = "[[Special:Code/$name/releasenotes|" . wfMsgHtml( 'code-releasenotes' ) . "]]"; |
30 | 30 | $text .= $wgLang->pipeList( $links ); |
31 | 31 | $text .= ")"; |
32 | 32 | return $text; |
Index: trunk/extensions/CodeReview/CodeReview.php |
— | — | @@ -56,6 +56,7 @@ |
57 | 57 | $wgAutoloadClasses['CodeStatusListView'] = $dir . 'CodeStatusListView.php'; |
58 | 58 | $wgAutoloadClasses['CodeTagListView'] = $dir . 'CodeTagListView.php'; |
59 | 59 | $wgAutoloadClasses['CodeCommentsListView'] = $dir . 'CodeCommentsListView.php'; |
| 60 | +$wgAutoloadClasses['CodeReleaseNotes'] = $dir . 'CodeReleaseNotes.php'; |
60 | 61 | $wgAutoloadClasses['CodeComment'] = $dir . 'CodeComment.php'; |
61 | 62 | $wgAutoloadClasses['CodePropChange'] = $dir . 'CodePropChange.php'; |
62 | 63 | $wgAutoloadClasses['SpecialCode'] = $dir . 'SpecialCode.php'; |