Ticket #2041: trac-0.9b1-intertrac.patch
| File trac-0.9b1-intertrac.patch, 117.8 KB (added by cboos, 6 years ago) |
|---|
-
htdocs/css/browser.css
diff -urN -x .svn trac-0.9b1/htdocs/css/browser.css intertrac-branch/htdocs/css/browser.css
old new 45 45 #dirlist td.name a, #dirlist td.rev a { border-bottom: none; display: block } 46 46 #dirlist td.change * { font-size: 9px } 47 47 48 /* Log */ 49 tr.diff input { 50 padding: 0 1em 0 1em; 51 margin: 0; 52 } 53 54 div.buttons { 55 clear: left; 56 } 57 58 #anydiff { 59 margin: 0 0 1em; 60 float: left; 61 } 62 #anydiff form, #anydiff div, #anydiff h2 { 63 display: inline; 64 } 65 #anydiff input { 66 vertical-align: baseline; 67 margin: 0 -0.5em 0 1em; 68 } 69 70 48 71 /* Styles for the revision log table 49 72 (extends the styles for "table.listing") */ 50 73 #chglist { margin-top: 0 } -
htdocs/css/changeset.css
diff -urN -x .svn trac-0.9b1/htdocs/css/changeset.css intertrac-branch/htdocs/css/changeset.css
old new 26 26 27 27 .diff ul.props { font-size: 90%; list-style: disc; margin: .5em 0 0; padding: 0 .5em 1em 2em } 28 28 .diff ul.props li { margin: 0; padding: 0 } 29 30 31 #title dl { 32 display: inline; 33 font-size: 110% 34 } 35 #title dt { 36 font-size: 110%; 37 font-weight: bold; 38 display: inline; 39 margin-left: 3em; 40 } 41 #title dd { 42 display: inline; 43 margin-left: 0.4em; 44 } -
templates/anydiff.cs
diff -urN -x .svn trac-0.9b1/templates/anydiff.cs intertrac-branch/templates/anydiff.cs
old new 1 <?cs include "header.cs"?> 2 3 <div id="ctxtnav" class="nav"> 4 <h2>Navigation</h2><?cs 5 with:links = chrome.links ?> 6 <ul> 7 </ul><?cs 8 /with ?> 9 </div> 10 11 <div id="content" class="changeset"> 12 <div id="title"> 13 <h1>Select Base and Target for Diff:</h1> 14 </div> 15 16 <div id="anydiff"> 17 <form action="<?cs var:anydiff.diff_href ?>" method="post"> 18 <table> 19 <tr> 20 <th><label for="old_path">From:</label></th> 21 <td> 22 <input type="text" id="old_path" name="old_path" value="<?cs 23 var:anydiff.old_path ?>" size="44" /> 24 <label for="old_rev">at Revision:</label> 25 <input type="text" id="old_rev" name="old" value="<?cs 26 var:anydiff.old_rev ?>" size="4" /> 27 </td> 28 </tr> 29 <tr> 30 <th><label for="new_path">To:</label></th> 31 <td> 32 <input type="text" id="new_path" name="path" value="<?cs 33 var:anydiff.new_path ?>" size="44" /> 34 <label for="new_rev">at Revision:</label> 35 <input type="text" id="new_rev" name="new" value="<?cs 36 var:anydiff.new_rev ?>" size="4" /> 37 </td> 38 </tr> 39 </table> 40 <div class="buttons"> 41 <input type="submit" value="View changes" /> 42 </div> 43 </form> 44 </div> 45 </div> 46 47 <?cs include "footer.cs"?> -
templates/browser.cs
diff -urN -x .svn trac-0.9b1/templates/browser.cs intertrac-branch/templates/browser.cs
old new 3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <ul> 6 <li class="last"><a href="<?cs var:browser.log_href ?>">Revision Log</a></li> 6 <li class="first"><a href="<?cs var:browser.restricted_changeset_href ?>"> 7 Last Change</a></li> 8 <li class="last"><a href="<?cs var:browser.log_href ?>"> 9 Revision Log</a></li> 7 10 </ul> 8 11 </div> 9 12 13 10 14 <div id="content" class="browser"> 11 15 <h1><?cs call:browser_path_links(browser.path, browser) ?></h1> 12 16 13 17 <div id="jumprev"> 14 <form action="" method="get"><div> 15 <label for="rev">View revision:</label> 16 <input type="text" id="rev" name="rev" value="<?cs 17 var:browser.revision ?>" size="4" /> 18 </div></form> 18 <form action="" method="get"> 19 <div> 20 <label for="rev">View revision:</label> 21 <input type="text" id="rev" name="rev" value="<?cs 22 var:browser.revision ?>" size="4" /> 23 </div> 24 </form> 19 25 </div> 20 26 21 27 <?cs if:browser.is_dir ?> … … 114 120 ?>/TracBrowser">TracBrowser</a> for help on using the browser. 115 121 </div> 116 122 123 <div id="anydiff"><?cs 124 if len(browser.path) > #1 ?> 125 <form action="<?cs var:browser.anydiff_href ?>" method="get"> 126 <input type="hidden" name="new_path" value="<?cs var:browser.path ?>" /> 127 <input type="hidden" name="old_path" value="<?cs var:browser.path ?>" /> 128 <input type="hidden" name="new_rev" value="<?cs var:browser.revision ?>" /> 129 <input type="hidden" name="old_rev" value="<?cs var:browser.revision ?>" /> 130 <div class="buttons"> 131 <input type="submit" value="View changes..." title="Prepare an Arbitrary Diff" /> 132 </div> 133 </form><?cs 134 /if ?> 135 </div> 136 117 137 </div> 118 138 <?cs include:"footer.cs"?> -
templates/changeset.cs
diff -urN -x .svn trac-0.9b1/templates/changeset.cs intertrac-branch/templates/changeset.cs
old new 1 <?cs include "header.cs"?>2 <?cs include "macros.cs"?>3 4 <div id="ctxtnav" class="nav">5 <h2>Changeset Navigation</h2><?cs6 with:links = chrome.links ?>7 <ul><?cs8 if:len(links.prev) ?>9 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>">10 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs11 var:links.prev.0.title ?>">Previous Changeset</a>12 </li><?cs13 /if ?><?cs14 if:len(links.next) ?>15 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last">16 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs17 var:links.next.0.title ?>">Next Changeset</a>18 </li><?cs19 /if ?>20 </ul><?cs21 /with ?>22 </div>23 24 <div id="content" class="changeset">25 <h1>Changeset <?cs var:changeset.revision ?></h1>26 27 <?cs each:change = changeset.changes ?><?cs28 if:len(change.diff) ?><?cs29 set:has_diffs = 1 ?><?cs30 /if ?><?cs31 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines32 || diff.options.ignorecase || diff.options.ignorewhitespace ?>33 <form method="post" id="prefs" action="">34 <div>35 <label for="style">View differences</label>36 <select id="style" name="style">37 <option value="inline"<?cs38 if:diff.style == 'inline' ?> selected="selected"<?cs39 /if ?>>inline</option>40 <option value="sidebyside"<?cs41 if:diff.style == 'sidebyside' ?> selected="selected"<?cs42 /if ?>>side by side</option>43 </select>44 <div class="field">45 Show <input type="text" name="contextlines" id="contextlines" size="2"46 maxlength="2" value="<?cs var:diff.options.contextlines ?>" />47 <label for="contextlines">lines around each change</label>48 </div>49 <fieldset id="ignore">50 <legend>Ignore:</legend>51 <div class="field">52 <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs53 if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> />54 <label for="blanklines">Blank lines</label>55 </div>56 <div class="field">57 <input type="checkbox" id="case" name="ignorecase"<?cs58 if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> />59 <label for="case">Case changes</label>60 </div>61 <div class="field">62 <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs63 if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> />64 <label for="whitespace">White space changes</label>65 </div>66 </fieldset>67 <div class="buttons">68 <input type="submit" name="update" value="Update" />69 </div>70 </div>71 </form><?cs /if ?>72 73 <?cs def:node_change(item,cl,kind) ?><?cs74 set:ndiffs = len(item.diff) ?><?cs75 set:nprops = len(item.props) ?>76 <div class="<?cs var:cl ?>"></div><?cs77 if:cl == "rem" ?>78 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs79 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs80 else ?>81 <a title="Show entry in browser" href="<?cs82 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs83 /if ?>84 <span class="comment">(<?cs var:kind ?>)</span><?cs85 if:item.path.old && item.change == 'copy' || item.change == 'move' ?>86 <small><em>(<?cs var:kind ?> from <a href="<?cs87 var:item.browser_href.old ?>" title="Show original file (rev. <?cs88 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs89 /if ?><?cs90 if:$ndiffs + $nprops > #0 ?>91 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs92 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs93 /if ?><?cs94 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs95 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs96 /if ?></a>)<?cs97 elif:cl == "mod" ?>98 (<a href="<?cs var:item.browser_href.old ?>"99 title="Show previous version in browser">previous</a>)<?cs100 /if ?>101 <?cs /def ?>102 103 <dl id="overview">104 <dt class="time">Timestamp:</dt>105 <dd class="time"><?cs var:changeset.time ?></dd>106 <dt class="author">Author:</dt>107 <dd class="author"><?cs var:changeset.author ?></dd>108 <dt class="message">Message:</dt>109 <dd class="message" id="searchable"><?cs var:changeset.message ?></dd>110 <dt class="files">Files:</dt>111 <dd class="files">112 <ul><?cs each:item = changeset.changes ?>113 <li><?cs114 if:item.change == 'add' ?><?cs115 call:node_change(item, 'add', 'added') ?><?cs116 elif:item.change == 'delete' ?><?cs117 call:node_change(item, 'rem', 'deleted') ?><?cs118 elif:item.change == 'copy' ?><?cs119 call:node_change(item, 'cp', 'copied') ?><?cs120 elif:item.change == 'move' ?><?cs121 call:node_change(item, 'mv', 'moved') ?><?cs122 elif:item.change == 'edit' ?><?cs123 call:node_change(item, 'mod', 'modified') ?><?cs124 /if ?>125 </li>126 <?cs /each ?></ul>127 </dd>128 </dl>129 130 <div class="diff">131 <div id="legend">132 <h3>Legend:</h3>133 <dl>134 <dt class="unmod"></dt><dd>Unmodified</dd>135 <dt class="add"></dt><dd>Added</dd>136 <dt class="rem"></dt><dd>Removed</dd>137 <dt class="mod"></dt><dd>Modified</dd>138 <dt class="cp"></dt><dd>Copied</dd>139 <dt class="mv"></dt><dd>Moved</dd>140 </dl>141 </div>142 <ul class="entries"><?cs143 each:item = changeset.changes ?><?cs144 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs145 var:name(item) ?>"><h2><a href="<?cs146 var:item.browser_href.new ?>" title="Show new revision <?cs147 var:item.rev.new ?> of this file in browser"><?cs148 var:item.path.new ?></a></h2><?cs149 if:len(item.props) ?><ul class="props"><?cs150 each:prop = item.props ?><li>Property <strong><?cs151 var:name(prop) ?></strong> <?cs152 if:prop.old && prop.new ?>changed from <?cs153 elif:!prop.old ?>set<?cs154 else ?>deleted<?cs155 /if ?><?cs156 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs157 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs158 /each ?></ul><?cs159 /if ?><?cs160 if:len(item.diff) ?><table class="<?cs161 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs162 if:diff.style == 'sidebyside' ?>163 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup>164 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup>165 <thead><tr>166 <th colspan="2"><a href="<?cs167 var:item.browser_href.old ?>" title="Show old rev. <?cs168 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs169 var:item.rev.old ?></a></th>170 <th colspan="2"><a href="<?cs171 var:item.browser_href.new ?>" title="Show new rev. <?cs172 var:item.rev.old ?> of <?cs var:item.path.new ?>">Revision <?cs173 var:item.rev.new ?></a></th>174 </tr>175 </thead><?cs176 each:change = item.diff ?><tbody><?cs177 call:diff_display(change, diff.style) ?></tbody><?cs178 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>179 <th>…</th><td> </td><th>…</th><td> </td>180 </tr></tbody><?cs /if ?><?cs181 /each ?><?cs182 else ?>183 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup>184 <thead><tr>185 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs186 var:item.browser_href.old ?>" title="Show old version of <?cs187 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th>188 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs189 var:item.browser_href.new ?>" title="Show new version of <?cs190 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th>191 <th> </th></tr>192 </thead><?cs193 each:change = item.diff ?><?cs194 call:diff_display(change, diff.style) ?><?cs195 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>196 <th>…</th><th>…</th><td> </td>197 </tr></tbody><?cs /if ?><?cs198 /each ?><?cs199 /if ?></table><?cs200 /if ?></li><?cs201 /if ?><?cs202 /each ?></ul>203 </div>204 205 </div>206 <?cs include "footer.cs"?> -
templates/diff.cs
diff -urN -x .svn trac-0.9b1/templates/diff.cs intertrac-branch/templates/diff.cs
old new 1 <?cs include "header.cs"?> 2 <?cs include "macros.cs"?> 3 4 <div id="ctxtnav" class="nav"> 5 <h2>Navigation</h2><?cs 6 with:links = chrome.links ?> 7 <ul><?cs 8 if:diff.chgset ?><?cs 9 if:len(links.prev) ?> 10 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 11 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 12 var:links.prev.0.title ?>">Previous <?cs 13 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> 14 </li><?cs 15 /if ?><?cs 16 if:len(links.next) ?> 17 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last"> 18 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 19 var:links.next.0.title ?>">Next <?cs 20 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> 21 </li><?cs 22 /if ?><?cs 23 else ?> 24 <li class="first"><a href="<?cs var:diff.reverse_href ?>">Reverse Diff</a></li><?cs 25 /if ?> 26 </ul><?cs 27 /with ?> 28 </div> 29 30 <div id="content" class="changeset"> 31 <div id="title"><?cs 32 if:diff.chgset ?><?cs 33 if:diff.restricted ?> 34 <h1>Changeset <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 35 <?cs var:diff.new_rev ?></a> 36 for <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 37 <?cs var:diff.new_path ?></a> 38 </h1><?cs 39 else ?> 40 <h1>Changeset <?cs var:diff.new_rev ?></h1><?cs 41 /if ?><?cs 42 else ?><?cs 43 if:diff.restricted ?> 44 <h1>Changes in <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 45 <?cs var:diff.new_path ?></a> 46 from revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 47 <?cs var:diff.old_rev ?></a> 48 to <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 49 <?cs var:diff.new_rev ?></a> 50 </h1><?cs 51 else ?> 52 <h1>Changes from <a title="Show entry in browser" href="<?cs var:diff.href.old_path ?>"> 53 <?cs var:diff.old_path ?></a> 54 at revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 55 <?cs var:diff.old_rev ?></a> 56 to <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 57 <?cs var:diff.new_path ?></a> 58 at revision <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 59 <?cs var:diff.new_rev ?></a> 60 </h1><?cs 61 /if ?><?cs 62 /if ?> 63 </div> 64 65 <?cs each:change = diff.changes ?><?cs 66 if:len(change.diff) ?><?cs 67 set:has_diffs = 1 ?><?cs 68 /if ?><?cs 69 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines 70 || diff.options.ignorecase || diff.options.ignorewhitespace ?> 71 <form method="post" id="prefs" action=""> 72 <div><?cs 73 if:!diff.chgset ?> 74 <input type="hidden" name="old_path" value="<?cs var:diff.old_path ?>" /> 75 <input type="hidden" name="path" value="<?cs var:diff.new_path ?>" /> 76 <input type="hidden" name="old" value="<?cs var:diff.old_rev ?>" /> 77 <input type="hidden" name="new" value="<?cs var:diff.new_rev ?>" /><?cs 78 /if ?> 79 <label for="style">View differences</label> 80 <select id="style" name="style"> 81 <option value="inline"<?cs 82 if:diff.style == 'inline' ?> selected="selected"<?cs 83 /if ?>>inline</option> 84 <option value="sidebyside"<?cs 85 if:diff.style == 'sidebyside' ?> selected="selected"<?cs 86 /if ?>>side by side</option> 87 </select> 88 <div class="field"> 89 Show <input type="text" name="contextlines" id="contextlines" size="2" 90 maxlength="2" value="<?cs var:diff.options.contextlines ?>" /> 91 <label for="contextlines">lines around each change</label> 92 </div> 93 <fieldset id="ignore"> 94 <legend>Ignore:</legend> 95 <div class="field"> 96 <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs 97 if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> /> 98 <label for="blanklines">Blank lines</label> 99 </div> 100 <div class="field"> 101 <input type="checkbox" id="case" name="ignorecase"<?cs 102 if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> /> 103 <label for="case">Case changes</label> 104 </div> 105 <div class="field"> 106 <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs 107 if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> /> 108 <label for="whitespace">White space changes</label> 109 </div> 110 </fieldset> 111 <div class="buttons"> 112 <input type="submit" name="update" value="Update" /> 113 </div> 114 </div> 115 </form><?cs /if ?> 116 117 <?cs def:node_change(item,cl,kind) ?><?cs 118 set:ndiffs = len(item.diff) ?><?cs 119 set:nprops = len(item.props) ?> 120 <div class="<?cs var:cl ?>"></div><?cs 121 if:cl == "rem" ?> 122 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs 123 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs 124 else ?> 125 <a title="Show entry in browser" href="<?cs 126 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs 127 /if ?> 128 <span class="comment">(<?cs var:kind ?>)</span><?cs 129 if:item.path.old && item.change == 'copy' || item.change == 'move' ?> 130 <small><em>(<?cs var:kind ?> from <a href="<?cs 131 var:item.browser_href.old ?>" title="Show original file (rev. <?cs 132 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs 133 /if ?><?cs 134 if:$ndiffs + $nprops > #0 ?> 135 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs 136 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs 137 /if ?><?cs 138 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs 139 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs 140 /if ?></a>)<?cs 141 elif:cl == "mod" ?> 142 (<a href="<?cs var:item.browser_href.old ?>" 143 title="Show previous version in browser">previous</a>)<?cs 144 /if ?> 145 <?cs /def ?> 146 147 <dl id="overview"><?cs 148 if:diff.chgset ?> 149 <dt class="time">Timestamp:</dt> 150 <dd class="time"><?cs var:changeset.time ?></dd> 151 <dt class="author">Author:</dt> 152 <dd class="author"><?cs var:changeset.author ?></dd> 153 <dt class="message">Message:</dt> 154 <dd class="message" id="searchable"><?cs var:changeset.message ?></dd><?cs 155 /if ?> 156 <dt class="files"><?cs 157 if:len(diff.changes) > #0 ?> 158 Files:<?cs 159 else ?> 160 (None)<?cs 161 /if ?> 162 </dt> 163 <dd class="files"> 164 <ul><?cs each:item = diff.changes ?> 165 <li><?cs 166 if:item.change == 'add' ?><?cs 167 call:node_change(item, 'add', 'added') ?><?cs 168 elif:item.change == 'delete' ?><?cs 169 call:node_change(item, 'rem', 'deleted') ?><?cs 170 elif:item.change == 'copy' ?><?cs 171 call:node_change(item, 'cp', 'copied') ?><?cs 172 elif:item.change == 'move' ?><?cs 173 call:node_change(item, 'mv', 'moved') ?><?cs 174 elif:item.change == 'edit' ?><?cs 175 call:node_change(item, 'mod', 'modified') ?><?cs 176 /if ?> 177 </li> 178 <?cs /each ?></ul> 179 </dd> 180 </dl> 181 182 <div class="diff"> 183 <div id="legend"> 184 <h3>Legend:</h3> 185 <dl> 186 <dt class="unmod"></dt><dd>Unmodified</dd> 187 <dt class="add"></dt><dd>Added</dd> 188 <dt class="rem"></dt><dd>Removed</dd> 189 <dt class="mod"></dt><dd>Modified</dd> 190 <dt class="cp"></dt><dd>Copied</dd> 191 <dt class="mv"></dt><dd>Moved</dd> 192 </dl> 193 </div> 194 <ul class="entries"><?cs 195 each:item = diff.changes ?><?cs 196 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs 197 var:name(item) ?>"><h2><a href="<?cs 198 var:item.browser_href.new ?>" title="Show new revision <?cs 199 var:item.rev.new ?> of this file in browser"><?cs 200 var:item.path.new ?></a></h2><?cs 201 if:len(item.props) ?><ul class="props"><?cs 202 each:prop = item.props ?><li>Property <strong><?cs 203 var:name(prop) ?></strong> <?cs 204 if:prop.old && prop.new ?>changed from <?cs 205 elif:!prop.old ?>set<?cs 206 else ?>deleted<?cs 207 /if ?><?cs 208 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs 209 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs 210 /each ?></ul><?cs 211 /if ?><?cs 212 if:len(item.diff) ?><table class="<?cs 213 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs 214 if:diff.style == 'sidebyside' ?> 215 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup> 216 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup> 217 <thead><tr> 218 <th colspan="2"><a href="<?cs 219 var:item.browser_href.old ?>" title="Show old rev. <?cs 220 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs 221 var:item.rev.old ?></a></th> 222 <th colspan="2"><a href="<?cs 223 var:item.browser_href.new ?>" title="Show new rev. <?cs 224 var:item.rev.old ?> of <?cs var:item.path.new ?>">Revision <?cs 225 var:item.rev.new ?></a></th> 226 </tr> 227 </thead><?cs 228 each:change = item.diff ?><tbody><?cs 229 call:diff_display(change, diff.style) ?></tbody><?cs 230 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 231 <th>…</th><td> </td><th>…</th><td> </td> 232 </tr></tbody><?cs /if ?><?cs 233 /each ?><?cs 234 else ?> 235 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup> 236 <thead><tr> 237 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs 238 var:item.browser_href.old ?>" title="Show old version of <?cs 239 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th> 240 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs 241 var:item.browser_href.new ?>" title="Show new version of <?cs 242 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th> 243 <th> </th></tr> 244 </thead><?cs 245 each:change = item.diff ?><?cs 246 call:diff_display(change, diff.style) ?><?cs 247 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 248 <th>…</th><th>…</th><td> </td> 249 </tr></tbody><?cs /if ?><?cs 250 /each ?><?cs 251 /if ?></table><?cs 252 /if ?></li><?cs 253 /if ?><?cs 254 /each ?></ul> 255 </div> 256 257 </div> 258 <?cs include "footer.cs"?> -
templates/log.cs
diff -urN -x .svn trac-0.9b1/templates/log.cs intertrac-branch/templates/log.cs
old new 3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <ul> 6 <li class="last"><a href="<?cs 7 var:log.browser_href ?>">View Latest Revision</a></li><?cs 6 <li class="last"> 7 <a href="<?cs var:log.browser_href ?>">View Latest Revision</a> 8 </li><?cs 8 9 if:len(chrome.links.prev) ?> 9 10 <li class="first<?cs if:!len(chrome.links.next) ?> last<?cs /if ?>"> 10 11 ← <a href="<?cs var:chrome.links.prev.0.href ?>" title="<?cs … … 61 62 title="Warning: by updating, you will clear the page history" /> 62 63 </div> 63 64 </form> 65 64 66 <div class="diff"> 65 67 <div id="legend"> 66 68 <h3>Legend:</h3> … … 74 76 </dl> 75 77 </div> 76 78 </div> 79 80 <form action="<?cs var:log.href ?>" method="post"> 81 <div class="buttons"><input type="submit" value="View changes" 82 title="Diff from Old Revision to New Revision (select them below)" /> 83 </div> 77 84 <table id="chglist" class="listing"> 78 85 <thead> 79 86 <tr> 87 <th>Old</th> 88 <th>New</th> 80 89 <th class="change"></th> 81 90 <th class="data">Date</th> 82 91 <th class="rev">Rev</th> … … 87 96 </thead> 88 97 <tbody><?cs 89 98 set:indent = #1 ?><?cs 99 set:idx = #0 ?><?cs 90 100 each:item = log.items ?><?cs 91 101 if:item.copyfrom_path ?> 92 102 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 93 <td class="copyfrom_path" colspan=" 6" style="padding-left: <?cs var:indent ?>em">103 <td class="copyfrom_path" colspan="8" style="padding-left: <?cs var:indent ?>em"> 94 104 copied from <a href="<?cs var:item.browser_href ?>"?><?cs var:item.copyfrom_path ?></a>: 95 105 </td> 96 106 </tr><?cs … … 99 109 set:indent = #1 ?><?cs 100 110 /if ?> 101 111 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 112 <td><input type="radio" name="old" 113 value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 114 if:idx == #1 ?> checked="checked" <?cs /if ?> /></td> 115 <td><input type="radio" name="new" 116 value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 117 if:idx == #0 ?> checked="checked" <?cs /if ?> /></td> 102 118 <td class="change" style="padding-left:<?cs var:indent ?>em"> 103 119 <a title="View log starting at this revision" href="<?cs var:item.log_href ?>"> 104 120 <span class="<?cs var:item.change ?>"></span> … … 115 131 <td class="author"><?cs var:log.changes[item.rev].author ?></td> 116 132 <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 117 133 </tr><?cs 134 set:idx = idx + 1 ?><?cs 118 135 /each ?> 119 136 </tbody> 120 </table><?cs 137 </table> 138 <div class="buttons"><input type="submit" value="View changes" 139 title="Diff from Old Revision to New Revision (select them above)" /> 140 </div> 141 </form><?cs 121 142 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 122 143 if:len(links.prev) ?><li class="first<?cs 123 144 if:!len(links.next) ?> last<?cs /if ?>">← <a href="<?cs -
templates/wiki.cs
diff -urN -x .svn trac-0.9b1/templates/wiki.cs intertrac-branch/templates/wiki.cs
old new 151 151 var:wiki.page_name ?></a></h1> 152 152 <?cs if:len(wiki.history) ?><form method="get" action=""> 153 153 <input type="hidden" name="action" value="diff" /> 154 <div class="buttons"> 155 <input type="submit" value="View changes" /> 156 </div> 154 157 <table id="wikihist" class="listing" summary="Change history"> 155 158 <thead><tr> 156 159 <th class="diff"></th> -
trac/db_default.py
diff -urN -x .svn trac-0.9b1/trac/db_default.py intertrac-branch/trac/db_default.py
old new 432 432 ('timeline', 'default_daysback', '30'), 433 433 ('browser', 'hide_properties', 'svk:merge'), 434 434 ('wiki', 'ignore_missing_pages', 'false'), 435 ('disabled_components', 'trac.wiki.api.StandardWikiPageNames', 'no'), 436 ('disabled_components', 'trac.wiki.api.FlexibleWikiPageNames', 'yes'), 437 ('disabled_components', 'trac.wiki.api.SubWikiPageNames', 'yes'), 435 438 ) 436 439 437 440 default_components = ('trac.About', 'trac.attachment', -
trac/env.py
diff -urN -x .svn trac-0.9b1/trac/env.py intertrac-branch/trac/env.py
old new 73 73 pass 74 74 75 75 self.path = path 76 self.siblings = {} 76 77 self.__cnx_pool = None 77 78 if create: 78 79 self.create(db_str) -
trac/ticket/api.py
diff -urN -x .svn trac-0.9b1/trac/ticket/api.py intertrac-branch/trac/ticket/api.py
old new 149 149 ('ticket', self._format_link)] 150 150 151 151 def get_wiki_syntax(self): 152 yield (r"!?# \d+",153 lambda x, y, z: self._format_link(x, 'ticket', y[1:], y ))152 yield (r"!?#(?P<it_ticket>[a-zA-Z_-]{0,3})\d+", 153 lambda x, y, z: self._format_link(x, 'ticket', y[1:], y, z)) 154 154 155 def _format_link(self, formatter, ns, target, label): 155 def _format_link(self, formatter, ns, target, label, fullmatch=None): 156 intertrac = formatter.shorthand_intertrac_helper(ns, target, label, 157 fullmatch) 158 if intertrac: 159 return intertrac 156 160 cursor = formatter.db.cursor() 157 161 cursor.execute("SELECT summary,status FROM ticket WHERE id=%s", 158 162 (target,)) -
trac/versioncontrol/api.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/api.py intertrac-branch/trac/versioncontrol/api.py
old new 38 38 """ 39 39 raise NotImplementedError 40 40 41 def has_node(self, path, rev): 42 """ 43 Tell if there's a node at the specified (path,rev) combination. 44 """ 45 raise NotImplementedError 46 41 47 def get_node(self, path, rev=None): 42 48 """ 43 49 Retrieve a Node (directory or file) from the repository at the … … 110 116 'None' is a valid revision value and represents the youngest revision. 111 117 """ 112 118 return NotImplementedError 113 119 120 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 121 """ 122 Generator that yields change tuples (old_node, new_node, kind, change) 123 for each node change between the two arbitrary (path,rev) pairs. 124 125 The old_node is assumed to be None when the change is an ADD, 126 the new_node is assumed to be None when the change is a DELETE. 127 """ 128 raise NotImplementedError 129 114 130 115 131 class Node(object): 116 132 """ … … 148 164 node (if the underlying version control system supports that), which 149 165 will be indicated by the first element of the tuple (i.e. the path) 150 166 changing. 167 Starts with an entry for the current revision. 151 168 """ 152 169 raise NotImplementedError 153 170 171 def get_previous(self): 172 """ 173 Return the (path, rev, chg) tuple corresponding to the previous 174 revision for that node. 175 """ 176 skip = True 177 for p in self.get_history(2): 178 if skip: 179 skip = False 180 else: 181 return p 182 154 183 def get_properties(self): 155 184 """ 156 185 Returns a dictionary containing the properties (meta-data) of the node. -
trac/versioncontrol/cache.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/cache.py intertrac-branch/trac/versioncontrol/cache.py
old new 84 84 def get_node(self, path, rev=None): 85 85 return self.repos.get_node(path, rev) 86 86 87 def has_node(self, path, rev): 88 return self.repos.has_node(path, rev) 89 87 90 def get_oldest_rev(self): 88 91 return self.repos.oldest_rev 89 92 … … 108 111 def normalize_rev(self, rev): 109 112 return self.repos.normalize_rev(rev) 110 113 114 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 115 return self.repos.get_deltas(old_path, old_rev, new_path, new_rev, ignore_ancestry) 116 111 117 112 118 class CachedChangeset(Changeset): 113 119 -
trac/versioncontrol/diff.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/diff.py intertrac-branch/trac/versioncontrol/diff.py
old new 215 215 ignore_space_changes) 216 216 for group in _group_opcodes(opcodes, context): 217 217 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 218 if i1 == 0 and i2 == 0: 219 i1, i2 = -1, -1 # support for 'A'dd changes 218 220 yield '@@ -%d,%d +%d,%d @@' % (i1 + 1, i2 - i1, j1 + 1, j2 - j1) 219 221 for tag, i1, i2, j1, j2 in group: 220 222 if tag == 'equal': -
trac/versioncontrol/svn_fs.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/svn_fs.py intertrac-branch/trac/versioncontrol/svn_fs.py
old new 22 22 import os.path 23 23 import time 24 24 import weakref 25 import posixpath 25 26 26 27 from svn import fs, repos, core, delta 27 28 … … 203 204 def __del__(self): 204 205 self.close() 205 206 207 def has_node(self, path, rev): 208 rev_root = fs.revision_root(self.fs_ptr, rev, self.pool()) 209 node_type = fs.check_path(rev_root, path, self.pool()) 210 return node_type in _kindmap 211 206 212 def normalize_path(self, path): 207 return path == '/' and path or path .strip('/')213 return path == '/' and path or path and path.strip('/') or '' 208 214 209 215 def normalize_rev(self, rev): 210 216 try: … … 293 299 subpool = Pool(self.pool) 294 300 while rev: 295 301 subpool.clear() 296 rev_root = fs.revision_root(self.fs_ptr, rev, subpool()) 297 node_type = fs.check_path(rev_root, path, subpool()) 298 if node_type in _kindmap: # then path exists at that rev 302 if self.has_node(path, rev): 299 303 if expect_deletion: 300 304 # it was missing, now it's there again: rev+1 must be a delete 301 305 yield path, rev+1, Changeset.DELETE … … 320 324 expect_deletion = True 321 325 rev = self.previous_rev(rev) 322 326 327 def get_deltas(self, old_path, old_rev, new_path, new_rev, 328 ignore_ancestry=0): 329 old_node = new_node = None 330 old_rev = self.normalize_rev(old_rev) 331 new_rev = self.normalize_rev(new_rev) 332 if self.has_node(old_path, old_rev): 333 old_node = self.get_node(old_path, old_rev) 334 else: 335 raise TracError, ('The Base for Diff is invalid: path %s' 336 ' doesn\'t exist in revision %s' \ 337 % (old_path, old_rev)) 338 if self.has_node(new_path, new_rev): 339 new_node = self.get_node(new_path, new_rev) 340 else: 341 raise TracError, ('The Target for Diff is invalid: path %s' 342 ' doesn\'t exist in revision %s' \ 343 % (new_path, new_rev)) 344 if new_node.kind != old_node.kind: 345 raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 346 'and Target is a %s (%s in revision %s).' \ 347 % (old_node.kind, old_path, old_rev, 348 new_node.kind, new_path, new_rev)) 349 subpool = Pool(self.pool) 350 if new_node.isdir: 351 editor = DiffChangeEditor() 352 e_ptr, e_baton = delta.make_editor(editor, subpool()) 353 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 354 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 355 def authz_cb(root, path, pool): return 1 356 text_deltas = 0 # as this is anyway re-done in Diff.py... 357 entry_props = 0 # "... typically used only for working copy updates" 358 repos.svn_repos_dir_delta(old_root, old_path, '', 359 new_root, new_path, 360 e_ptr, e_baton, authz_cb, 361 text_deltas, 362 1, # directory 363 entry_props, 364 ignore_ancestry, 365 subpool()) 366 for path, kind, change in editor.deltas: 367 old_node = new_node = None 368 if change != Changeset.ADD: 369 old_node = self.get_node(posixpath.join(old_path, path), 370 old_rev) 371 if change != Changeset.DELETE: 372 new_node = self.get_node(posixpath.join(new_path, path), 373 new_rev) 374 else: 375 kind = _kindmap[fs.check_path(old_root, old_node.path, 376 subpool())] 377 yield (old_node, new_node, kind, change) 378 else: 379 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 380 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 381 if fs.contents_changed(old_root, old_path, new_root, new_path, 382 subpool()): 383 yield (old_node, new_node, Node.FILE, Changeset.EDIT) 384 323 385 324 386 class SubversionNode(Node): 325 387 … … 340 402 raise TracError, "No node at %s in revision %s" % (path, rev) 341 403 self.created_rev = fs.node_created_rev(self.root, self.scoped_path, self.pool()) 342 404 self.created_path = fs.node_created_path(self.root, self.scoped_path, self.pool()) 343 # 'created_path' differs from 'path' if the last operation is a copy, 344 # and furthermore, 'path' might not exist at 'create_rev' 405 # Note: 'created_path' differs from 'path' if the last change was a copy, 406 # and furthermore, 'path' might not exist at 'create_rev'. 407 # The only guarantees are: 408 # * this node exists at (path,rev) 409 # * the node existed at (created_path,created_rev) 410 # TODO: check node id 345 411 self.rev = self.created_rev 346 412 347 413 Node.__init__(self, path, self.rev, _kindmap[node_type]) … … 380 446 if newer: 381 447 yield newer 382 448 449 # def get_previous(self): 450 # # FIXME: redo it with fs.node_history 451 383 452 def get_properties(self): 384 453 props = fs.node_proplist(self.root, self.scoped_path, self.pool()) 385 454 for name,value in props.items(): … … 474 543 475 544 def _get_prop(self, name): 476 545 return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool()) 546 547 548 # 549 # Delta editor for diffs between arbitrary nodes 550 # 551 # Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 552 # because 'repos.svn_repos_dir_delta' *doesn't* provide it. 553 # 554 # Note 2: the 'dir_baton' is the path of the parent directory 555 # 556 557 class DiffChangeEditor(delta.Editor): 558 559 def __init__(self): 560 self.deltas = [] 561 562 # -- svn.delta.Editor callbacks 563 564 def open_root(self, base_revision, dir_pool): 565 return ('/', Changeset.EDIT) 566 567 def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, 568 dir_pool): 569 self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 570 return (path, Changeset.ADD) 571 572 def open_directory(self, path, dir_baton, base_revision, dir_pool): 573 return (path, dir_baton[1]) 574 575 def change_dir_prop(self, dir_baton, name, value, pool): 576 path, change = dir_baton 577 if change != Changeset.ADD: 578 self.deltas.append((path, Node.DIRECTORY, change)) 579 580 def delete_entry(self, path, revision, dir_baton, pool): 581 self.deltas.append((path, None, Changeset.DELETE)) 582 583 def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, 584 dir_pool): 585 self.deltas.append((path, Node.FILE, Changeset.ADD)) 586 587 def open_file(self, path, dir_baton, dummy_rev, file_pool): 588 self.deltas.append((path, Node.FILE, Changeset.EDIT)) 589 -
trac/versioncontrol/tests/svn_fs.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/tests/svn_fs.py intertrac-branch/trac/versioncontrol/tests/svn_fs.py
old new 198 198 self.assertEqual(('tags/v1', 7, 'unknown'), history.next()) 199 199 self.assertRaises(StopIteration, history.next) 200 200 201 # Diffs 202 203 def _cmp_diff(self, expected, got): 204 if expected[0]: 205 old = self.repos.get_node(*expected[0]) 206 self.assertEqual((old.path, old.rev), (got[0].path, got[0].rev)) 207 if expected[1]: 208 new = self.repos.get_node(*expected[1]) 209 self.assertEqual((new.path, new.rev), (got[1].path, got[1].rev)) 210 self.assertEqual(expected[2], (got[2], got[3])) 211 212 def test_diff_file_different_revs(self): 213 diffs = self.repos.get_deltas('trunk/README.txt', 2, 'trunk/README.txt', 3) 214 self._cmp_diff((('trunk/README.txt', 2), 215 ('trunk/README.txt', 3), 216 (Node.FILE, Changeset.EDIT)), diffs.next()) 217 self.assertRaises(StopIteration, diffs.next) 218 219 def test_diff_file_different_files(self): 220 diffs = self.repos.get_deltas('branches/v1x/README.txt', 12, 221 'branches/v1x/README2.txt', 12) 222 self._cmp_diff((('branches/v1x/README.txt', 12), 223 ('branches/v1x/README2.txt', 12), 224 (Node.FILE, Changeset.EDIT)), diffs.next()) 225 self.assertRaises(StopIteration, diffs.next) 226 227 def test_diff_file_no_change(self): 228 diffs = self.repos.get_deltas('trunk/README.txt', 7, 229 'tags/v1/README.txt', 7) 230 self.assertRaises(StopIteration, diffs.next) 231 232 def test_diff_dir_different_revs(self): 233 diffs = self.repos.get_deltas('trunk', 4, 'trunk', 8) 234 self._cmp_diff((None, ('trunk/dir1/dir2', 8), 235 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 236 self._cmp_diff((None, ('trunk/dir1/dir3', 8), 237 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 238 self._cmp_diff((None, ('trunk/README2.txt', 6), 239 (Node.FILE, Changeset.ADD)), diffs.next()) 240 self._cmp_diff((('trunk/dir2', 4), None, 241 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 242 self._cmp_diff((('trunk/dir3', 4), None, 243 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 244 self.assertRaises(StopIteration, diffs.next) 245 246 def test_diff_dir_different_dirs(self): 247 diffs = self.repos.get_deltas('trunk', 1, 'branches/v1x', 12) 248 self._cmp_diff((None, ('branches/v1x/dir1', 12), 249 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 250 self._cmp_diff((None, ('branches/v1x/dir1/dir2', 12), 251 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 252 self._cmp_diff((None, ('branches/v1x/dir1/dir3', 12), 253 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 254 self._cmp_diff((None, ('branches/v1x/README.txt', 12), 255 (Node.FILE, Changeset.ADD)), diffs.next()) 256 self._cmp_diff((None, ('branches/v1x/README2.txt', 12), 257 (Node.FILE, Changeset.ADD)), diffs.next()) 258 self.assertRaises(StopIteration, diffs.next) 259 260 def test_diff_dir_no_change(self): 261 diffs = self.repos.get_deltas('trunk', 7, 262 'tags/v1', 7) 263 self.assertRaises(StopIteration, diffs.next) 264 265 # Changesets 266 201 267 def test_changeset_repos_creation(self): 202 268 chgset = self.repos.get_changeset(0) 203 269 self.assertEqual(0, chgset.rev) -
trac/versioncontrol/web_ui/browser.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/browser.py intertrac-branch/trac/versioncontrol/web_ui/browser.py
old new 89 89 90 90 repos = self.env.get_repository(req.authname) 91 91 node = repos.get_node(path, rev) 92 rev = repos.normalize_rev(rev) 92 93 93 94 hidden_properties = [p.strip() for p 94 95 in self.config.get('browser', 'hide_properties', 95 96 'svk:merge').split(',')] 97 96 98 req.hdf['title'] = path 97 req.hdf['browser']= {99 browser_hdf = { 98 100 'path': path, 99 'revision': rev or repos.youngest_rev,101 'revision': rev, 100 102 'props': dict([(util.escape(name), util.escape(value)) 101 103 for name, value in node.get_properties().items() 102 if not name in hidden_properties]), 103 'href': util.escape(self.env.href.browser(path, rev=rev or 104 repos.youngest_rev)), 105 'log_href': util.escape(self.env.href.log(path)) 106 } 104 if not name in hidden_properties]) 105 } 106 browser_hrefs = { 107 'href': self.env.href.browser(path,rev=rev), 108 'restricted_changeset_href': self.env.href.changeset(node.rev, 109 path=path), 110 'anydiff_href': self.env.href.anydiff(), 111 'log_href': self.env.href.log(path) 112 } 113 browser_hdf.update(dict([(key, util.escape(href)) for key, href in 114 browser_hrefs.items()])) 115 req.hdf['browser'] = browser_hdf 107 116 108 117 path_links = get_path_links(self.env.href, path, rev) 109 118 if len(path_links) > 1: … … 163 172 164 173 req.hdf['browser.items'] = info 165 174 req.hdf['browser.changes'] = changes 166 175 if node.path != '': 176 zip_href = self.env.href.diff(node.path, new=rev, old=rev, 177 old_path='/', # special case (#238) 178 format='zip') 179 add_link(req, 'alternate', zip_href, 'Zip Archive', 180 'application/zip', 'zip') 181 182 167 183 def _render_file(self, req, repos, node, rev=None): 168 184 req.perm.assert_permission('FILE_VIEW') 169 185 -
trac/versioncontrol/web_ui/changeset.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/changeset.py intertrac-branch/trac/versioncontrol/web_ui/changeset.py
old new 22 22 23 23 from trac import mimeview, util 24 24 from trac.core import * 25 from trac.perm import IPermissionRequestor26 25 from trac.Search import ISearchSource, query_to_sql, shorten_result 27 26 from trac.Timeline import ITimelineEventProvider 28 27 from trac.versioncontrol import Changeset, Node 29 28 from trac.versioncontrol.svn_authz import SubversionAuthorizer 30 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff31 29 from trac.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet,INavigationContributor30 from trac.web.chrome import INavigationContributor 33 31 from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider 32 from trac.versioncontrol.web_ui.diff import AbstractDiffModule 34 33 34 class ChangesetModule(AbstractDiffModule): 35 35 36 class ChangesetModule(Component): 37 38 implements(INavigationContributor, IPermissionRequestor, IRequestHandler, 36 implements(INavigationContributor, 39 37 ITimelineEventProvider, IWikiSyntaxProvider, ISearchSource) 40 38 41 39 # INavigationContributor methods … … 46 44 def get_navigation_items(self, req): 47 45 return [] 48 46 49 # IPermissionRequestor methods 50 51 def get_permission_actions(self): 52 return ['CHANGESET_VIEW'] 53 54 # IRequestHandler methods 47 # (reimplemented) IRequestHandler methods 55 48 56 49 def match_request(self, req): 57 50 match = re.match(r'/changeset/([0-9]+)$', req.path_info) … … 59 52 req.args['rev'] = match.group(1) 60 53 return 1 61 54 62 def process_request(self, req):63 req.perm.assert_permission('CHANGESET_VIEW')64 65 rev = req.args.get('rev')66 repos = self.env.get_repository(req.authname)67 68 diff_options = get_diff_options(req)69 if req.args.has_key('update'):70 req.redirect(self.env.href.changeset(rev))71 72 chgset = repos.get_changeset(rev)73 req.check_modified(chgset.date,74 diff_options[0] + ''.join(diff_options[1]))75 76 format = req.args.get('format')77 if format == 'diff':78 self._render_diff(req, repos, chgset, diff_options)79 return80 elif format == 'zip':81 self._render_zip(req, repos, chgset)82 return83 84 self._render_html(req, repos, chgset, diff_options)85 add_link(req, 'alternate', '?format=diff', 'Unified Diff',86 'text/plain', 'diff')87 add_link(req, 'alternate', '?format=zip', 'Zip Archive',88 'application/zip', 'zip')89 add_stylesheet(req, 'common/css/changeset.css')90 add_stylesheet(req, 'common/css/diff.css')91 add_stylesheet(req, 'common/css/code.css')92 return 'changeset.cs', None93 94 55 # ITimelineEventProvider methods 95 56 96 57 def get_timeline_filters(self, req): … … 136 97 message 137 98 rev = repos.previous_rev(rev) 138 99 139 # Internal methods140 141 def _render_html(self, req, repos, chgset, diff_options):142 """HTML version"""143 req.hdf['title'] = '[%s]' % chgset.rev144 req.hdf['changeset'] = {145 'revision': chgset.rev,146 'time': time.strftime('%c', time.localtime(chgset.date)),147 'author': util.escape(chgset.author or 'anonymous'),148 'message': wiki_to_html(chgset.message or '--', self.env, req,149 escape_newlines=True)150 }151 152 oldest_rev = repos.oldest_rev153 if chgset.rev != oldest_rev:154 add_link(req, 'first', self.env.href.changeset(oldest_rev),155 'Changeset %s' % oldest_rev)156 previous_rev = repos.previous_rev(chgset.rev)157 add_link(req, 'prev', self.env.href.changeset(previous_rev),158 'Changeset %s' % previous_rev)159 youngest_rev = repos.youngest_rev160 if str(chgset.rev) != str(youngest_rev):161 next_rev = repos.next_rev(chgset.rev)162 add_link(req, 'next', self.env.href.changeset(next_rev),163 'Changeset %s' % next_rev)164 add_link(req, 'last', self.env.href.changeset(youngest_rev),165 'Changeset %s' % youngest_rev)166 167 edits = []168 idx = 0169 for path, kind, change, base_path, base_rev in chgset.get_changes():170 info = {'change': change}171 if base_path:172 info['path.old'] = base_path173 info['rev.old'] = base_rev174 info['browser_href.old'] = self.env.href.browser(base_path,175 rev=base_rev)176 if path:177 info['path.new'] = path178 info['rev.new'] = chgset.rev179 info['browser_href.new'] = self.env.href.browser(path,180 rev=chgset.rev)181 if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):182 edits.append((idx, path, kind, base_path, base_rev))183 req.hdf['changeset.changes.%d' % idx] = info184 idx += 1185 186 hidden_properties = [p.strip() for p187 in self.config.get('browser', 'hide_properties',188 'svk:merge').split(',')]189 190 for idx, path, kind, base_path, base_rev in edits:191 old_node = repos.get_node(base_path or path, base_rev)192 new_node = repos.get_node(path, chgset.rev)193 194 # Property changes195 old_props = old_node.get_properties()196 new_props = new_node.get_properties()197 changed_props = {}198 if old_props != new_props:199 for k,v in old_props.items():200 if not k in new_props:201 changed_props[k] = {'old': v}202 elif v != new_props[k]:203 changed_props[k] = {'old': v, 'new': new_props[k]}204 for k,v in new_props.items():205 if not k in old_props:206 changed_props[k] = {'new': v}207 for k in hidden_properties:208 if k in changed_props:209 del changed_props[k]210 req.hdf['changeset.changes.%d.props' % idx] = changed_props211 212 if kind == Node.DIRECTORY:213 continue214 215 # Content changes216 default_charset = self.config.get('trac', 'default_charset')217 old_content = old_node.get_content().read()218 if mimeview.is_binary(old_content):219 continue220 charset = mimeview.get_charset(old_node.content_type) or \221 default_charset222 old_content = util.to_utf8(old_content, charset)223 224 new_content = new_node.get_content().read()225 if mimeview.is_binary(new_content):226 continue227 charset = mimeview.get_charset(new_node.content_type) or \228 default_charset229 new_content = util.to_utf8(new_content, charset)230 231 if old_content != new_content:232 context = 3233 for option in diff_options[1]:234 if option.startswith('-U'):235 context = int(option[2:])236 break237 tabwidth = int(self.config.get('diff', 'tab_width',238 self.config.get('mimeviewer',239 'tab_width')))240 changes = hdf_diff(old_content.splitlines(),241 new_content.splitlines(),242 context, tabwidth,243 ignore_blank_lines='-B' in diff_options[1],244 ignore_case='-i' in diff_options[1],245 ignore_space_changes='-b' in diff_options[1])246 req.hdf['changeset.changes.%d.diff' % idx] = changes247 248 def _render_diff(self, req, repos, chgset, diff_options):249 """Raw Unified Diff version"""250 req.send_response(200)251 req.send_header('Content-Type', 'text/plain;charset=utf-8')252 req.send_header('Content-Disposition',253 'filename=Changeset%s.diff' % req.args.get('rev'))254 req.end_headers()255 256 for path, kind, change, base_path, base_rev in chgset.get_changes():257 if change == Changeset.ADD:258 old_node = None259 else:260 old_node = repos.get_node(base_path or path, base_rev)261 if change == Changeset.DELETE:262 new_node = None263 else:264 new_node = repos.get_node(path, chgset.rev)265 266 # TODO: Property changes267 268 # Content changes269 if kind == 'dir':270 continue271 272 default_charset = self.config.get('trac', 'default_charset')273 new_content = old_content = ''274 new_node_info = old_node_info = ('','')275 276 if old_node:277 charset = mimeview.get_charset(old_node.content_type) or \278 default_charset279 old_content = util.to_utf8(old_node.get_content().read(),280 charset)281 old_node_info = (old_node.path, old_node.rev)282 if mimeview.is_binary(old_content):283 continue284 285 if new_node:286 charset = mimeview.get_charset(new_node.content_type) or \287 default_charset288 new_content = util.to_utf8(new_node.get_content().read(),289 charset)290 new_node_info = (new_node.path, new_node.rev)291 if mimeview.is_binary(new_content):292 continue293 294 if old_content != new_content:295 context = 3296 for option in diff_options[1]:297 if option.startswith('-U'):298 context = int(option[2:])299 break300 req.write('Index: ' + path + util.CRLF)301 req.write('=' * 67 + util.CRLF)302 req.write('--- %s (revision %s)' % old_node_info +303 util.CRLF)304 req.write('+++ %s (revision %s)' % new_node_info +305 util.CRLF)306 for line in unified_diff(old_content.splitlines(),307 new_content.splitlines(), context,308 ignore_blank_lines='-B' in diff_options[1],309 ignore_case='-i' in diff_options[1],310 ignore_space_changes='-b' in diff_options[1]):311 req.write(line + util.CRLF)312 313 def _render_zip(self, req, repos, chgset):314 """ZIP archive with all the added and/or modified files."""315 req.send_response(200)316 req.send_header('Content-Type', 'application/zip')317 req.send_header('Content-Disposition',318 'filename=Changeset%s.zip' % chgset.rev)319 req.end_headers()320 321 try:322 from cStringIO import StringIO323 except ImportError:324 from StringIO import StringIO325 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED326 327 buf = StringIO()328 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED)329 for path, kind, change, base_path, base_rev in chgset.get_changes():330 if kind == Node.FILE and change != Changeset.DELETE:331 node = repos.get_node(path, chgset.rev)332 zipinfo = ZipInfo()333 zipinfo.filename = node.path334 zipinfo.date_time = time.gmtime(node.last_modified)[:6]335 zipinfo.compress_type = ZIP_DEFLATED336 zipfile.writestr(zipinfo, node.get_content().read())337 zipfile.close()338 req.write(buf.getvalue())339 100 340 101 # IWikiSyntaxProvider methods 341 102 342 103 def get_wiki_syntax(self): 343 yield (r"!?\[\d+\]|(?:\b|!)r\d+\b(?!:\d)", 104 yield (r"!?\[(?P<it_changeset>[a-zA-Z_-]{0,3})\d+\]|" # [1] or [T1] 105 r"(?:\b|!)r\d+\b(?!:\d)", # r1 but not r1:2 344 106 lambda x, y, z: self._format_link(x, 'changeset', 345 107 y[0] == 'r' and y[1:] 346 or y[1:-1], y ))108 or y[1:-1], y, z)) 347 109 348 110 def get_link_resolvers(self): 349 111 yield ('changeset', self._format_link) 350 112 351 def _format_link(self, formatter, ns, rev, label): 113 def _format_link(self, formatter, ns, rev, label, fullmatch=None): 114 intertrac = formatter.shorthand_intertrac_helper(ns, rev, label, 115 fullmatch) 116 if intertrac: 117 return intertrac 352 118 cursor = formatter.db.cursor() 353 119 cursor.execute('SELECT message FROM revision WHERE rev=%s', (rev,)) 354 120 row = cursor.fetchone() -
trac/versioncontrol/web_ui/diff.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/diff.py intertrac-branch/trac/versioncontrol/web_ui/diff.py
old new 1 # -*- coding: iso8859-1 -*- 2 # 3 # Copyright (C) 2003-2005 Edgewall Software 4 # Copyright (C) 2003-2005 Jonas Borgström <jonas@edgewall.com> 5 # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de> 6 # All rights reserved. 7 # 8 # This software is licensed as described in the file COPYING, which 9 # you should have received as part of this distribution. The terms 10 # are also available at http://trac.edgewall.com/license.html. 11 # 12 # This software consists of voluntary contributions made by many 13 # individuals. For the exact contribution history, see the revision 14 # history and logs, available at http://projects.edgewall.com/trac/. 15 # 16 # Author: Jonas Borgström <jonas@edgewall.com> 17 # Christopher Lenz <cmlenz@gmx.de> 18 # Christian Boos <cboos@neuf.fr> 19 20 from __future__ import generators 21 import time 22 import re 23 import posixpath 24 from urllib import urlencode 25 26 from trac import mimeview, util 27 from trac.core import * 28 from trac.perm import IPermissionRequestor 29 from trac.versioncontrol import Changeset, Node 30 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff 31 from trac.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet 33 from trac.wiki import wiki_to_html, IWikiSyntaxProvider 34 35 class DiffArgs(dict): 36 def __getattr__(self,str): 37 return self[str] 38 39 40 class ChangesPermission(Component): 41 """Simple permission provider for changes related modules.""" 42 43 implements(IPermissionRequestor) 44 45 def get_permission_actions(self): 46 return ['CHANGESET_VIEW'] 47 48 49 class AbstractDiffModule(Component): 50 """Provide flexible functionality for showing sets of differences. 51 52 If the differences shown are coming from a specific changeset, 53 then that changeset informations can be shown too. 54 55 In addition, it is possible to show only a subset of the changeset: 56 Only the changes affecting a given path will be shown. 57 This is called the ''restricted'' changeset. 58 59 But the differences can also be computed in a more general way, 60 between two arbitrary paths and/or between two arbitrary revisions. 61 In that case, there's no changeset information displayed. 62 """ 63 64 abstract = True 65 66 implements(IRequestHandler) 67 68 # IRequestHandler methods 69 70 def match_request(self, req): 71 raise NotImplementedError 72 73 def process_request(self, req): 74 """The appropriate mode of operation is inferred from 75 the request parameters: 76 * If `old` and `new` parameters are given, it will be an 77 arbitrary set of differences: `chgset` is False. 78 * If `old_path` is given and is different from `path`, 79 it's a generalized diff, from `old_path@old` 80 to `path@new`: `restricted` is False. 81 * Otherwise those are differences between two arbitrary revisions 82 of a given path: `restricted` is True. 83 * Otherwise, we are dealing with a changeset only: `chgset` is True. 84 * If the `path` is not empty or not the root, then only 85 the changes affecting that path (i.e. itself, children or 86 ancestors) will be considered: `restricted` is True. 87 * Otherwise, it's the full changeset: `restricted` is False. 88 89 In any case, the given path@rev pair must exist. 90 """ 91 req.perm.assert_permission('CHANGESET_VIEW') 92 93 # -- retrieve arguments 94 path = req.args.get('path') 95 rev = req.args.get('rev') 96 old = req.args.get('old') 97 new = req.args.get('new') 98 old_path = req.args.get('old_path') 99 100 # -- normalize and check for special case 101 repos = self.env.get_repository(req.authname) 102 path = repos.normalize_path(path) 103 rev = repos.normalize_rev(rev) 104 old_path = repos.normalize_path(old_path) 105 106 if old_path == path and old and old == new: # revert to Changeset 107 rev = old 108 old_path = old = new = None 109 110 diff_options = get_diff_options(req) 111 112 # -- setup the `chgset` and `restricted` flags, see docstring above. 113 chgset = not old and not new and not old_path 114 if chgset: 115 restricted = path != '' and path != '/' # (subset or not) 116 else: 117 restricted = old_path == path # (same path or not) 118 119 # -- redirect if changing the diff options 120 if req.args.has_key('update'): 121 if chgset: 122 if restricted: 123 req.redirect(self.env.href.diff(path, rev=rev)) 124 else: 125 req.redirect(self.env.href.changeset(rev)) 126 else: 127 req.redirect(self.env.href.diff(path, new=new, 128 old_path=old_path, old=old)) 129 130 # -- preparing the diff arguments 131 if chgset: 132 prev = repos.get_node(path, rev).get_previous() 133 if prev: 134 prev_path, prev_rev = prev[:2] 135 else: 136 prev_path, prev_rev = path, repos.previous_rev(rev) 137 diff_args = DiffArgs(old_path=prev_path, old_rev=prev_rev, 138 new_path=path, new_rev=rev) 139 else: 140 if not new: 141 new = repos.youngest_rev 142 elif not old: 143 old = repos.youngest_rev 144 if not old_path: 145 old_path = path 146 diff_args = DiffArgs(old_path=old_path, old_rev=old, 147 new_path=path, new_rev=new) 148 if chgset: 149 chgset = repos.get_changeset(rev) 150 req.check_modified(chgset.date, 151 diff_options[0] + ''.join(diff_options[1])) 152 else: 153 pass # FIXME: what date should we choose for a diff? 154 155 req.hdf['diff'] = diff_args 156 157 format = req.args.get('format') 158 159 if format in ['diff', 'zip']: 160 # choosing an appropriate filename 161 rpath = path.replace('/','_') 162 if chgset: 163 if restricted: 164 filename = 'changeset_%s_r%s' % (rpath, rev) 165 else: 166 filename = 'changeset_r%s' % rev 167 else: 168 if restricted: 169 filename = 'diff-%s-from-r%s-to-r%s' \ 170 % (rpath, old, new) 171 elif old_path == '/': # special case for download (#238) 172 filename = '%s-r%s' % (rpath, old) 173 else: 174 filename = 'diff-from-%s-r%s-to-%s-r%s' \ 175 % (old_path.replace('/','_'), old, rpath, new) 176 if format == 'diff': 177 self._render_diff(req, filename, repos, diff_args, 178 diff_options) 179 return 180 elif format == 'zip': 181 self._render_zip(req, filename, repos, diff_args) 182 return 183 184 # -- HTML format 185 self._render_html(req, repos, chgset, restricted, 186 diff_args, diff_options) 187 if chgset: 188 diff_params = 'rev=%s' % rev 189 else: 190 diff_params = urlencode({'path': path, 191 'new': new, 192 'old_path': old_path, 193 'old': old}) 194 add_link(req, 'alternate', '?format=diff&'+diff_params, 'Unified Diff', 195 'text/plain', 'diff') 196 add_link(req, 'alternate', '?format=zip&'+diff_params, 'Zip Archive', 197 'application/zip', 'zip') 198 add_stylesheet(req, 'common/css/changeset.css') 199 add_stylesheet(req, 'common/css/diff.css') 200 add_stylesheet(req, 'common/css/code.css') 201 return 'diff.cs', None 202 203 204 # Internal methods 205 206 def _render_html(self, req, repos, chgset, restricted, diff, diff_options): 207 """ 208 HTML version 209 """ 210 req.hdf['diff'] = { 211 'chgset': chgset and True, 212 'restricted': restricted, 213 'href': { 'new_rev': self.env.href.changeset(diff.new_rev), 214 'old_rev': self.env.href.changeset(diff.old_rev), 215 'new_path': self.env.href.browser(diff.new_path, 216 rev=diff.new_rev), 217 'old_path': self.env.href.browser(diff.old_path, 218 rev=diff.old_rev) 219 } 220 } 221 222 if chgset: # Changeset Mode (possibly restricted on a path) 223 path, rev = diff.new_path, diff.new_rev 224 225 # -- getting the deltas from the Changeset.get_changes method 226 def get_deltas(): 227 old_node = new_node = None 228 for npath, kind, change, opath, orev in chgset.get_changes(): 229 if restricted and \ 230 not (npath.startswith(path) # npath is below 231 or path.startswith(npath)): # npath is above 232 continue 233 if change != Changeset.ADD: 234 old_node = repos.get_node(opath, orev) 235 if change != Changeset.DELETE: 236 new_node = repos.get_node(npath, rev) 237 yield old_node, new_node, kind, change 238 239 def _changeset_title(rev): 240 if restricted: 241 return 'Changeset %s for %s' % (rev, path) 242 else: 243 return 'Changeset %s' % rev 244 245 title = _changeset_title(rev) 246 req.hdf['changeset'] = { 247 'revision': chgset.rev, 248 'time': time.strftime('%c', time.localtime(chgset.date)), 249 'author': util.escape(chgset.author or 'anonymous'), 250 'message': wiki_to_html(chgset.message or '--', self.env, req, 251 escape_newlines=True) 252 } 253 oldest_rev = repos.oldest_rev 254 if chgset.rev != oldest_rev: 255 if restricted: 256 prev = repos.get_node(path, rev).get_previous() 257 if prev: 258 prev_path, prev_rev = prev[:2] 259 prev_href = self.env.href.changeset(prev_rev, 260 path=prev_path) 261 else: 262 prev_path = prev_rev = None 263 else: 264 prev_path = diff.old_path 265 prev_rev = repos.previous_rev(chgset.rev) 266 add_link(req, 'first', self.env.href.changeset(oldest_rev), 267 'Changeset %s' % oldest_rev) 268 prev_href = self.env.href.changeset(prev_rev) 269 if prev_rev: 270 add_link(req, 'prev', prev_href, _changeset_title(prev_rev)) 271 youngest_rev = repos.youngest_rev 272 if str(chgset.rev) != str(youngest_rev): 273 if restricted: 274 next_rev = next_href = None 275 # FIXME: find an effective way to find the next rev 276 else: 277 next_rev = repos.next_rev(chgset.rev) 278 next_href = self.env.href.changeset(next_rev) 279 add_link(req, 'last', 280 self.env.href.diff(path, rev=youngest_rev), 281 'Changeset %s' % youngest_rev) 282 if next_rev: 283 add_link(req, 'next', next_href, _changeset_title(next_rev)) 284 285 else: # Diff Mode 286 # -- getting the deltas from the Repository.get_deltas method 287 def get_deltas(): 288 for d in repos.get_deltas(**diff): 289 yield d 290 291 reverse_href = self.env.href.diff(diff.old_path, 292 new=diff.old_rev, 293 old_path=diff.new_path, 294 old=diff.new_rev) 295 req.hdf['diff.reverse_href'] = reverse_href 296 if restricted: # 'diff between 2 revisions' mode 297 title = 'Diff r%s:%s for %s' % (diff.old_rev, diff.new_rev, 298 diff.new_path) 299 else: # 'arbitrary diff' mode 300 title = 'Diff from %s @ %s to %s @ %s' % (diff.old_path, 301 diff.old_rev, 302 diff.new_path, 303 diff.new_rev) 304 req.hdf['title'] = title 305 306 def _change_info(old_node, new_node, change): 307 info = {'change': change} 308 if old_node: 309 info['path.old'] = old_node.path 310 info['rev.old'] = old_node.rev # this is the created rev. 311 old_href = self.env.href.browser(old_node.path, 312 rev=diff.old_rev) 313 # Reminder: old_node.path may not exist at old_node.rev 314 info['browser_href.old'] = old_href 315 if new_node: 316 info['path.new'] = new_node.path 317 info['rev.new'] = new_node.rev # created rev. 318 new_href = self.env.href.browser(new_node.path, 319 rev=diff.new_rev) 320 # (same remark as above) 321 info['browser_href.new'] = new_href 322 return info 323 324 hidden_properties = [p.strip() for p 325 in self.config.get('browser', 'hide_properties', 326 'svk:merge').split(',')] 327 328 def _prop_changes(old_node, new_node): 329 old_props = old_node.get_properties() 330 new_props = new_node.get_properties() 331 changed_props = {} 332 if old_props != new_props: 333 for k,v in old_props.items(): 334 if not k in new_props: 335 changed_props[k] = {'old': v} 336 elif v != new_props[k]: 337 changed_props[k] = {'old': v, 'new': new_props[k]} 338 for k,v in new_props.items(): 339 if not k in old_props: 340 changed_props[k] = {'new': v} 341 for k in hidden_properties: 342 if k in changed_props: 343 del changed_props[k] 344 return changed_props 345 346 def _content_changes(old_node, new_node): 347 """ 348 Returns the list of differences. 349 The list is empty when no differences between comparable files 350 are detected, but the return value is None for non-comparable files. 351 """ 352 default_charset = self.config.get('trac', 'default_charset') 353 old_content = old_node.get_content().read() 354 if mimeview.is_binary(old_content): 355 return None 356 charset = mimeview.get_charset(old_node.content_type) or \ 357 default_charset 358 old_content = util.to_utf8(old_content, charset) 359 360 new_content = new_node.get_content().read() 361 if mimeview.is_binary(new_content): 362 return None 363 charset = mimeview.get_charset(new_node.content_type) or \ 364 default_charset 365 new_content = util.to_utf8(new_content, charset) 366 367 if old_content != new_content: 368 context = 3 369 options = diff_options[1] 370 for option in options: 371 if option.startswith('-U'): 372 context = int(option[2:]) 373 break 374 tabwidth = int(self.config.get('diff', 'tab_width', 375 self.config.get('mimeviewer', 376 'tab_width'))) 377 return hdf_diff(old_content.splitlines(), 378 new_content.splitlines(), 379 context, tabwidth, 380 ignore_blank_lines='-B' in options, 381 ignore_case='-i' in options, 382 ignore_space_changes='-b' in options) 383 else: 384 return [] 385 386 idx = 0 387 for old_node, new_node, kind, change in get_deltas(): 388 if change != Changeset.EDIT: 389 show_entry = True 390 else: 391 show_entry = False 392 assert old_node and new_node 393 props = _prop_changes(old_node, new_node) 394 if props: 395 req.hdf['diff.changes.%d.props' % idx] = props 396 show_entry = True 397 if kind == Node.FILE: 398 diffs = _content_changes(old_node, new_node) 399 if diffs != []: 400 if diffs: 401 req.hdf['diff.changes.%d.diff' % idx] = diffs 402 # elif None (means: manually compare to (previous)) 403 show_entry = True 404 if show_entry: 405 info = _change_info(old_node, new_node, change) 406 req.hdf['diff.changes.%d' % idx] = info 407 idx += 1 # the sequence should be immutable 408 409 def _render_diff(self, req, filename, repos, diff, diff_options): 410 """Raw Unified Diff version""" 411 req.send_response(200) 412 req.send_header('Content-Type', 'text/plain;charset=utf-8') 413 req.send_header('Content-Disposition', 414 'filename=%s.diff' % filename) 415 req.end_headers() 416 417 for old_node, new_node, kind, change in repos.get_deltas(**diff): 418 # TODO: Property changes 419 420 # Content changes 421 if kind == Node.DIRECTORY: 422 continue 423 424 default_charset = self.config.get('trac', 'default_charset') 425 new_content = old_content = '' 426 new_node_info = old_node_info = ('','') 427 428 if old_node: 429 charset = mimeview.get_charset(old_node.content_type) or \ 430 default_charset 431 old_content = util.to_utf8(old_node.get_content().read(), 432 charset) 433 old_node_info = (old_node.path, old_node.rev) 434 if mimeview.is_binary(old_content): 435 continue 436 437 if new_node: 438 charset = mimeview.get_charset(new_node.content_type) or \ 439 default_charset 440 new_content = util.to_utf8(new_node.get_content().read(), 441 charset) 442 new_node_info = (new_node.path, new_node.rev) 443 if mimeview.is_binary(new_content): 444 continue 445 new_path = new_node.path 446 else: 447 old_node_path = repos.normalize_path(old_node.path) 448 diff_old_path = repos.normalize_path(diff.old_path) 449 new_path = posixpath.join(diff.new_path, 450 old_node_path[len(diff_old_path)+1:]) 451 452 if old_content != new_content: 453 context = 3 454 options = diff_options[1] 455 for option in options: 456 if option.startswith('-U'): 457 context = int(option[2:]) 458 break 459 if not old_node_info[0]: 460 old_node_info = new_node_info # support for 'A'dd changes 461 req.write('Index: ' + new_path + util.CRLF) 462 req.write('=' * 67 + util.CRLF) 463 req.write('--- %s (revision %s)' % old_node_info + 464 util.CRLF) 465 req.write('+++ %s (revision %s)' % new_node_info + 466 util.CRLF) 467 for line in unified_diff(old_content.splitlines(), 468 new_content.splitlines(), context, 469 ignore_blank_lines='-B' in options, 470 ignore_case='-i' in options, 471 ignore_space_changes='-b' in options): 472 req.write(line + util.CRLF) 473 474 def _render_zip(self, req, filename, repos, diff): 475 """ZIP archive with all the added and/or modified files.""" 476 new_rev = diff.new_rev 477 req.send_response(200) 478 req.send_header('Content-Type', 'application/zip') 479 req.send_header('Content-Disposition', 480 'filename=%s.zip' % filename) 481 482 try: 483 from cStringIO import StringIO 484 except ImportError: 485 from StringIO import StringIO 486 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED 487 488 buf = StringIO() 489 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 490 for old_node, new_node, kind, change in repos.get_deltas(**diff): 491 if kind == Node.FILE and change != Changeset.DELETE: 492 assert new_node 493 zipinfo = ZipInfo() 494 zipinfo.filename = new_node.path 495 zipinfo.date_time = time.gmtime(new_node.last_modified)[:6] 496 zipinfo.compress_type = ZIP_DEFLATED 497 zipfile.writestr(zipinfo, new_node.get_content().read()) 498 zipfile.close() 499 500 buf.seek(0, 2) # be sure to be at the end 501 req.send_header("Content-Length", buf.tell()) 502 req.end_headers() 503 504 req.write(buf.getvalue()) 505 506 507 class DiffModule(AbstractDiffModule): 508 509 implements(IWikiSyntaxProvider) 510 511 # (reimplemented) IRequestHandler methods 512 513 def match_request(self, req): 514 match = re.match(r'/diff(?:(/.*)|$)', req.path_info) 515 if match: 516 if match.group(1): 517 req.args['path'] = match.group(1) 518 return 1 519 520 # IWikiSyntaxProvider methods 521 522 def get_wiki_syntax(self): 523 return [] 524 525 def get_link_resolvers(self): 526 yield ('diff', self._format_link) 527 528 def _format_link(self, formatter, ns, params, label): 529 def pathrev(path): 530 irev = path.find('#') 531 if irev > 0: 532 return (path[:irev], path[irev+1:]) 533 else: 534 return (path, None) 535 ianydiff = params.find('//') 536 if ianydiff > 0: 537 old_path, old_rev = pathrev(params[:ianydiff]) 538 new_path, new_rev = pathrev(params[ianydiff+2:]) 539 else: 540 old_path, old_rev = pathrev(params) 541 new_path = old_path 542 new_rev = None 543 if old_rev: 544 isep = old_rev.find(':') 545 if isep > 0: 546 old_rev = old_rev[:isep] 547 new_rev = old_rev[isep+1:] 548 href = formatter.href.diff(new_path, new=new_rev, 549 old_path=old_path, old=old_rev) 550 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 551 % ('Diff', href, label) 552 553 554 class AnyDiffModule(Component): 555 556 implements(IRequestHandler) 557 558 # IRequestHandler methods 559 560 def match_request(self, req): 561 return re.match(r'/anydiff$', req.path_info) 562 563 def process_request(self, req): 564 # -- retrieve arguments 565 new_path = req.args.get('new_path') 566 new_rev = req.args.get('new_rev') 567 old_path = req.args.get('old_path') 568 old_rev = req.args.get('old_rev') 569 570 # -- normalize 571 repos = self.env.get_repository(req.authname) 572 new_path = repos.normalize_path(new_path) 573 new_rev = repos.normalize_rev(new_rev) 574 old_path = repos.normalize_path(old_path) 575 old_rev = repos.normalize_rev(old_rev) 576 577 # -- prepare rendering 578 req.hdf['anydiff'] = { 579 'new_path': new_path, 580 'new_rev': new_rev, 581 'old_path': old_path, 582 'old_rev': old_rev, 583 'diff_href': self.env.href.diff(), 584 } 585 586 return 'anydiff.cs', None -
trac/versioncontrol/web_ui/__init__.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/__init__.py intertrac-branch/trac/versioncontrol/web_ui/__init__.py
old new 1 from trac.versioncontrol.web_ui.browser import * 2 from trac.versioncontrol.web_ui.changeset import * 3 from trac.versioncontrol.web_ui.log import * 1 from trac.versioncontrol.web_ui.browser import * 2 from trac.versioncontrol.web_ui.changeset import * 3 from trac.versioncontrol.web_ui.log import * 4 from trac.versioncontrol.web_ui.diff import * 5 -
trac/versioncontrol/web_ui/log.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/log.py intertrac-branch/trac/versioncontrol/web_ui/log.py
old new 67 67 stop_rev = req.args.get('stop_rev') 68 68 verbose = req.args.get('verbose') 69 69 limit = LOG_LIMIT 70 old = req.args.get('old') 71 new = req.args.get('new') 72 73 repos = self.env.get_repository(req.authname) 74 normpath = repos.normalize_path(path) 75 rev = str(repos.normalize_rev(rev)) 76 77 if old and new: 78 osep = util.unescape(old).rindex('#') 79 nsep = util.unescape(new).rindex('#') 80 old_path, old_rev = old[:osep], old[osep+1:] 81 new_path, new_rev = new[:nsep], new[nsep+1:] 82 req.redirect(self.env.href.diff(new_path, new=new_rev, 83 old_path=old_path, old=old_rev)) 70 84 71 85 req.hdf['title'] = path + ' (log)' 72 86 req.hdf['log'] = { … … 83 97 if path_links: 84 98 add_link(req, 'up', path_links[-1]['href'], 'Parent directory') 85 99 86 repos = self.env.get_repository(req.authname)87 normpath = repos.normalize_path(path)88 rev = str(repos.normalize_rev(rev))89 100 90 101 # ''Node'' history uses `get_node()`, 91 102 # ''Path'' history uses `get_path_history()` -
trac/web/standalone.py
diff -urN -x .svn trac-0.9b1/trac/web/standalone.py intertrac-branch/trac/web/standalone.py
old new 139 139 self.auths = auths 140 140 141 141 self.projects = {} 142 siblings = {} 142 143 for env_path in env_paths: 143 144 # Remove trailing slashes 144 145 while env_path and not os.path.split(env_path)[1]: 145 146 env_path = os.path.split(env_path)[0] 146 147 project = os.path.split(env_path)[1] 147 148 self.projects[project] = env_path 149 env = get_environment(None, self.get_env_opts(project)) 150 siblings[project] = env 151 for p in siblings.values(): 152 p.siblings = siblings 148 153 149 154 def get_env_opts(self, project=None): 150 155 if self.env_parent_dir: 151 156 opts = self.env_parent_dir.items() 152 157 else: 153 158 opts = [('TRAC_ENV', self.projects[project])] 154 return dict(o pts + os.environ.items())159 return dict(os.environ.items() + opts) # TODO: backport 155 160 156 161 def send_project_index(self, req): 157 162 if self.env_parent_dir: -
trac/wiki/api.py
diff -urN -x .svn trac-0.9b1/trac/wiki/api.py intertrac-branch/trac/wiki/api.py
old new 23 23 import dummy_threading as threading 24 24 import time 25 25 import urllib 26 import re 26 27 27 28 from trac.core import * 28 29 from trac.util import to_utf8, TRUE … … 65 66 def get_link_resolvers(): 66 67 """Return an iterable over (namespace, formatter) tuples.""" 67 68 69 class IWikiPageNameSyntaxProvider(Interface): 70 71 def get_wiki_page_names_syntax(): 72 """ 73 Return an iterable that provides a regular expression for 74 matching wiki page names (see WikiPageNames) 75 76 Be careful to only allow __one__ implementation 77 (others should be listed in the ![disabled_components] 78 section of the TracIni) 79 """ 80 68 81 69 82 class WikiSystem(Component): 70 83 """Represents the wiki system.""" … … 74 87 change_listeners = ExtensionPoint(IWikiChangeListener) 75 88 macro_providers = ExtensionPoint(IWikiMacroProvider) 76 89 syntax_providers = ExtensionPoint(IWikiSyntaxProvider) 90 wikipagenames_providers = ExtensionPoint(IWikiPageNameSyntaxProvider) 77 91 78 92 INDEX_UPDATE_INTERVAL = 5 # seconds 79 93 … … 81 95 self._index = None 82 96 self._last_index_update = 0 83 97 self._index_lock = threading.RLock() 98 self._compiled_rules = None 99 self._link_resolvers = None 100 self._helper_patterns = None 101 self._external_handlers = None 84 102 85 103 def _update_index(self): 86 104 self._index_lock.acquire() … … 116 134 self._update_index() 117 135 return pagename in self._index.keys() 118 136 137 def _get_rules(self): 138 self._prepare_rules() 139 return self._compiled_rules 140 rules = property(_get_rules) 141 142 def _get_helper_patterns(self): 143 self._prepare_rules() 144 return self._helper_patterns 145 helper_patterns = property(_get_helper_patterns) 146 147 def _get_external_handlers(self): 148 self._prepare_rules() 149 return self._external_handlers 150 external_handlers = property(_get_external_handlers) 151 152 def _prepare_rules(self): 153 from trac.wiki.formatter import Formatter 154 if not self._compiled_rules: 155 helpers = [] 156 handlers = {} 157 syntax = Formatter._pre_rules[:] 158 i = 0 159 for resolver in self.syntax_providers: 160 for regexp, handler in resolver.get_wiki_syntax(): 161 handlers['i'+str(i)] = handler 162 syntax.append('(?P<i%d>%s)' % (i, regexp)) 163 i += 1 164 syntax += Formatter._post_rules[:] 165 helper_re = re.compile(r'\?P<([a-z\d_]+)>') 166 for rule in syntax: 167 helpers += helper_re.findall(rule)[1:] 168 rules = re.compile('(?:' + '|'.join(syntax) + ')') 169 self._external_handlers = handlers 170 self._helper_patterns = helpers 171 self._compiled_rules = rules 172 173 def _get_link_resolvers(self): 174 if not self._link_resolvers: 175 resolvers = {} 176 for resolver in self.syntax_providers: 177 for namespace, handler in resolver.get_link_resolvers(): 178 resolvers[namespace] = handler 179 self._link_resolvers = resolvers 180 return self._link_resolvers 181 link_resolvers = property(_get_link_resolvers) 182 119 183 # IWikiChangeListener methods 120 184 121 185 def wiki_page_added(self, page): … … 136 200 def get_wiki_syntax(self): 137 201 ignore_missing = self.config.get('wiki', 'ignore_missing_pages') 138 202 ignore_missing = ignore_missing in TRUE 139 yield (r"!?(?<!/)\b[A-Z][a-z]+(?:[A-Z][a-z]*[a-z/])+" 140 "(?:#[A-Za-z0-9]+)?(?=\Z|\s|[.,;:!?\)}\]])", 141 lambda x, y, z: self._format_link(x, 'wiki', y, y, 142 ignore_missing)) 203 providers = [] 204 for p in self.wikipagenames_providers: 205 if not providers: 206 yield (p.get_wiki_page_names_syntax(), 207 lambda x, y, z: self._format_link(x, 'wiki', y, y, 208 ignore_missing)) 209 pc = p.__class__ 210 providers.append('# %s\n%s.%s = yes' % (pc.__doc__.split('\n')[0], 211 pc.__module__, pc.__name__) 212 ) 213 if len(providers) > 1: 214 self.log.warning('More than one IWikiPageNameSyntaxProvider ' 215 'implementation available:\n' 216 'You should set one of the following to "no" ' 217 'in your trac.ini:\n\n' 218 '[disabled_components]\n' + 219 '\n'.join(providers) +'\n') 143 220 144 221 def get_link_resolvers(self): 145 222 yield ('wiki', self._format_fancy_link) … … 163 240 else: 164 241 return '<a class="wiki" href="%s">%s</a>' \ 165 242 % (formatter.href.wiki(page) + anchor, label) 243 244 245 WIKI_START = r"!?(?<!/)\b" 246 WIKI_TARGET = r"(?:#[A-Za-z0-9]+)?" 247 WIKI_END = r"(?=\Z|\s|[.,;:!?\)}\]])" 248 WIKI_INTERWIKI = r"(?!:\S)" 249 250 class StandardWikiPageNames(Component): 251 """Standard Trac WikiPageNames rule""" 252 253 implements(IWikiPageNameSyntaxProvider) 254 255 def get_wiki_page_names_syntax(self): 256 return (WIKI_START + # where to start 257 r"[A-Z][a-z]+" # initial WikiPageNames word 258 r"(?:[A-Z][a-z]*[a-z/])+" + # additional WikiPageNames word 259 WIKI_TARGET + # optional trailing section link 260 WIKI_END + # where to end 261 WIKI_INTERWIKI) # InterWiki support 262 263 class FlexibleWikiPageNames(Component): 264 """Standard Trac WikiPageNames rule, plus digits 265 and consecutive upper-case characters allowed. 266 267 More precisely, WikiPageNames are: 268 * either 2 or more starting upper case letter or digits, 269 followed by lower case letters 270 * either 1 or more starting upper case letter or digits, 271 followed by lower case letters, repeated at least 2 times 272 (with optionally '/' between repetitions) 273 """ 274 275 implements(IWikiPageNameSyntaxProvider) 276 277 def get_wiki_page_names_syntax(self): 278 return (WIKI_START + 279 r"(?:[A-Z\d]{2,}[a-z]+" # 1st way 280 r"|[A-Z\d]+[a-z]+(?:/?[A-Z\d]+[a-z]*)+)" + # 2nd way 281 WIKI_TARGET + WIKI_END + WIKI_INTERWIKI) 282 283 class SubWikiPageNames(Component): 284 """SubWiki-like rules for WikiPageNames. 285 286 See http://www.webdav.org/wiki/projects/TextFormattingRules 287 288 Note that '/' in this style of WikiPageNames are not supported. 289 """ 290 291 implements(IWikiPageNameSyntaxProvider) 292 293 def get_wiki_page_names_syntax(self): 294 return (WIKI_START + 295 r"(?:[A-Z][A-Z]+[a-z\d]+[A-Z]*" # 1st and 3rd way 296 r"|[A-Z][a-z]+(?:[A-Z][a-z]+)+)" + # 2nd way 297 WIKI_TARGET + WIKI_END + WIKI_INTERWIKI) 298 -
trac/wiki/formatter.py
diff -urN -x .svn trac-0.9b1/trac/wiki/formatter.py intertrac-branch/trac/wiki/formatter.py
old new 19 19 from __future__ import generators 20 20 import re 21 21 import os 22 import string23 22 import urllib 24 23 25 24 try: … … 28 27 from StringIO import StringIO 29 28 30 29 from trac import util 30 from trac.core import * 31 31 from trac.mimeview import * 32 from trac.wiki.api import WikiSystem 32 from trac.wiki.api import WikiSystem, IWikiChangeListener, IWikiMacroProvider 33 33 34 34 __all__ = ['wiki_to_html', 'wiki_to_oneliner', 'wiki_to_outline'] 35 35 … … 136 136 class Formatter(object): 137 137 flavor = 'default' 138 138 139 _link_resolvers = None140 139 # Rules provided by IWikiSyntaxProviders are inserted between pre_rules and post_rules 141 140 _pre_rules = [r"(?P<bolditalic>%s)" % BOLDITALIC_TOKEN, 142 141 r"(?P<bold>%s)" % BOLD_TOKEN, … … 165 164 r"(?P<last_table_cell>\|\|\s*$)", 166 165 r"(?P<table_cell>\|\|)"] 167 166 168 _compiled_rules = None169 _helper_patterns = None170 _external_handlers = None171 167 _processor_re = re.compile('#\!([\w+-][\w+-/]*)') 172 168 _anchor_re = re.compile('[^\w\d\.-:]+', re.UNICODE) 173 169 … … 194 190 db = property(fget=_get_db) 195 191 196 192 def _get_rules(self): 197 if not Formatter._compiled_rules: 198 helpers = [] 199 handlers = {} 200 syntax = Formatter._pre_rules[:] 201 wiki = WikiSystem(self.env) 202 i = 0 203 for resolver in wiki.syntax_providers: 204 for regexp, handler in resolver.get_wiki_syntax(): 205 handlers['i'+str(i)] = handler 206 syntax.append('(?P<i%d>%s)' % (i, regexp)) 207 i += 1 208 syntax += Formatter._post_rules[:] 209 helper_re = re.compile(r'\?P<([a-z\d]+)>') 210 for rule in syntax: 211 helpers += helper_re.findall(rule)[1:] 212 rules = re.compile('(?:' + string.join(syntax, '|') + ')') 213 Formatter._external_handlers = handlers 214 Formatter._helper_patterns = helpers 215 Formatter._compiled_rules = rules 216 return Formatter._compiled_rules 193 return WikiSystem(self.env).rules 217 194 rules = property(_get_rules) 218 195 219 196 def _get_link_resolvers(self): 220 if not self._link_resolvers: 221 resolvers = {} 222 wiki = WikiSystem(self.env) 223 for resolver in wiki.syntax_providers: 224 for namespace, handler in resolver.get_link_resolvers(): 225 resolvers[namespace] = handler 226 self._link_resolvers = resolvers 227 return self._link_resolvers 197 return WikiSystem(self.env).link_resolvers 228 198 link_resolvers = property(_get_link_resolvers) 229 199 230 200 def replace(self, fullmatch): 201 wiki = WikiSystem(self.env) 231 202 for itype, match in fullmatch.groupdict().items(): 232 if match and not itype in Formatter._helper_patterns:203 if match and not itype in wiki.helper_patterns: 233 204 # Check for preceding escape character '!' 234 205 if match[0] == '!': 235 206 return match[1:] 236 if itype in self._external_handlers:237 return self._external_handlers[itype](self, match, fullmatch)207 if itype in wiki.external_handlers: 208 return wiki.external_handlers[itype](self, match, fullmatch) 238 209 else: 239 210 return getattr(self, '_' + itype + '_formatter')(match, fullmatch) 240 211 … … 297 268 return self._make_link(ns, target, match, label) 298 269 299 270 def _make_link(self, ns, target, match, label): 271 # check first for an alias defined in trac.ini 272 ns = self.env.config.get('intertrac', ns.upper(), ns) 300 273 if ns in self.link_resolvers: 301 return self. _link_resolvers[ns](self, ns, target, label)274 return self.link_resolvers[ns](self, ns, target, label) 302 275 elif target.startswith('//') or ns == "mailto": 303 276 return self._make_ext_link(ns+':'+target, label) 304 277 else: 305 return match 278 intertrac = self._make_intertrac_link(ns, target, label) 279 if intertrac: 280 return intertrac 281 else: 282 interwiki = self._make_interwiki_link(ns, target, label) 283 if interwiki: 284 return interwiki 285 else: 286 return match 287 288 def _make_intertrac_link(self, ns, target, label): 289 if self.env.siblings.has_key(ns): 290 sibling = self.env.siblings[ns] 291 if not hasattr(sibling, 'href'): 292 from trac.web.href import Href 293 def xchg_base(base): 294 return '/'.join(base.split('/')[:-1] + [ns]) 295 sibling.href = Href(xchg_base(self.env.href.base)) 296 sibling.abs_href = Href(xchg_base(self.env.abs_href.base)) 297 ref = wiki_to_oneliner(target, sibling) 298 return ref.replace('>%s' % target, '>%s' % label) 299 url = self.env.config.get('intertrac', ns.upper()+'.url') 300 if url: 301 name = self.env.config.get('intertrac', ns.upper()+'.title', 302 'Trac project %s' % ns) 303 sep = target.find(':') 304 if sep != -1: 305 url = '%s/%s/%s' % (url, target[:sep], target[sep+1:]) 306 else: 307 url = '%s/search?q=%s' % (url, urllib.quote_plus(target)) 308 return self._make_ext_link(url, label, '%s in %s' % (target, name)) 309 else: 310 return None 311 312 def shorthand_intertrac_helper(self, ns, target, label, fullmatch): 313 if fullmatch: # short form 314 alias = fullmatch.group('it_%s' % ns) 315 if alias: 316 intertrac = self.env.config.get('intertrac', alias.upper(), alias) 317 target = '%s:%s' % (ns, target[len(alias):]) 318 it = self._make_intertrac_link(intertrac, target, label) 319 return it or label 320 return None 321 322 def _make_interwiki_link(self, ns, target, label): 323 interwiki = InterWikiMap(self.env) 324 if interwiki.has_key(ns): 325 url, title = interwiki.url(ns, target) 326 return self._make_ext_link(url, label, '%s in %s' % (target, title)) 327 else: 328 return None 306 329 307 330 def _make_ext_link(self, url, text, title=''): 308 331 title_attr = title and ' title="%s"' % title or '' … … 332 355 if match[0] == '!': 333 356 return match[1:] 334 357 else: 335 return self.simple_tag_handler('<span class="underline">', '</span>') 358 return self.simple_tag_handler('<span class="underline">', 359 '</span>') 336 360 337 361 def _strike_formatter(self, match, fullmatch): 338 362 if match[0] == '!': … … 723 747 out = StringIO() 724 748 OutlineFormatter(env, absurls, db).format(wikitext, out, max_depth) 725 749 return out.getvalue() 750 751 752 # -- InterWiki support 753 754 class InterWikiMap(Component): 755 756 implements(IWikiChangeListener, IWikiMacroProvider) 757 758 _page_name = 'InterMapTxt' 759 _interwiki_re = re.compile(r"(\w+)[ \t]+([^ \t]+)(?:[ \t]+#(.*))?", 760 re.UNICODE) 761 _argspec_re = re.compile(r"\$\d") 762 763 def __init__(self): 764 self._interwiki_map = None 765 # This dictionary maps upper-cased namespaces 766 # to (namespace, prefix, title) values 767 768 def has_key(self, ns): 769 if not self._interwiki_map: 770 self._update() 771 return self._interwiki_map.has_key(ns.upper()) 772 773 def url(self, ns, target): 774 ns, url, title = self._interwiki_map[ns.upper()] 775 args = target.split(':') 776 def setarg(match): 777 num = int(match.group()[1:]) 778 return 0 < num <= len(args) and args[num-1] or '' 779 url_with_args = re.sub(InterWikiMap._argspec_re, setarg, url) 780 if url_with_args == url: 781 return url + target, title 782 else: 783 return url_with_args, title 784 785 # IWikiChangeListener methods 786 787 def wiki_page_added(self, page): 788 if page.name == InterWikiMap._page_name: 789 self._update() 790 791 def wiki_page_changed(self, page, version, t, comment, author, ipnr): 792 if page.name == InterWikiMap._page_name: 793 self._update() 794 795 def wiki_page_deleted(self, page): 796 if page.name == InterWikiMap._page_name: 797 self._interwiki_map.clear() 798 799 def _update(self): 800 from trac.wiki.model import WikiPage 801 self._interwiki_map = {} 802 content = WikiPage(self.env, InterWikiMap._page_name).text 803 in_map = False 804 for line in content.split('\n'): 805 if in_map: 806 if line.startswith('----'): 807 in_map = False 808 else: 809 m = re.match(InterWikiMap._interwiki_re, line) 810 if m: 811 prefix, url, title = m.groups() 812 url = url.strip() 813 title = title and title.strip() or prefix 814 self._interwiki_map[prefix.upper()] = (prefix, url, 815 title) 816 elif line.startswith('----'): 817 in_map = True 818 819 # IWikiMacroProvider 820 821 def get_macros(self): 822 yield 'InterWiki' 823 824 def get_macro_description(self, name): 825 return "Provide a description list for the known InterWiki prefixes." 826 827 def render_macro(self, req, name, content): 828 if not self._interwiki_map: 829 self._update() 830 keys = self._interwiki_map.keys() 831 keys.sort() 832 buf = StringIO() 833 buf.write('<table><tr><th>Prefix</th><td>Site</td></tr>\n') 834 for k in keys: 835 prefix, url, title = self._interwiki_map[k] 836 shortened_url = url and url[:-1] 837 description = title == prefix and shortened_url or title 838 buf.write('<tr>\n' + 839 ('<td><a href="%sRecentChanges">%s</a></td>' 840 '<td><a href="%s">%s</a></td>\n') \ 841 % (url, prefix, shortened_url, description) + 842 '</tr>\n') 843 buf.write('</table>\n') 844 return buf.getvalue() -
trac/wiki/tests/formatter.py
diff -urN -x .svn trac-0.9b1/trac/wiki/tests/formatter.py intertrac-branch/trac/wiki/tests/formatter.py
old new 53 53 self.config = Configuration(None) 54 54 self.href = Href('/') 55 55 self.abs_href = Href('http://www.example.com/') 56 self._wiki_pages = {}57 56 self.path = '' 57 self.siblings = {} 58 58 def component_activated(self, component): 59 59 component.env = self 60 60 component.config = self.config -
trac/wiki/tests/wiki-tests.txt
diff -urN -x .svn trac-0.9b1/trac/wiki/tests/wiki-tests.txt intertrac-branch/trac/wiki/tests/wiki-tests.txt
old new 80 80 <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a> AlabamA ABc AlaBamA <a class="missing wiki" href="/wiki/FooBar" rel="nofollow">FooBar?</a> 81 81 </p> 82 82 ============================== 83 CamelCase,CamelCase.CamelCase: CamelCase83 CamelCase,CamelCase.CamelCase: CamelCase 84 84 ------------------------------ 85 85 <p> 86 <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>,<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>. CamelCase:CamelCase86 <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>,<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>.<a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a>: <a class="missing wiki" href="/wiki/CamelCase" rel="nofollow">CamelCase?</a> 87 87 </p> 88 88 ============================== 89 89 !CamelCase -
wiki-default/checkwiki.py
diff -urN -x .svn trac-0.9b1/wiki-default/checkwiki.py intertrac-branch/wiki-default/checkwiki.py
old new 1 #!/usr/bin/python 2 # 3 # Check/update default wiki pages from the Trac project website. 4 # 5 # Note: This is a development tool used in Trac packaging/QA, not something 6 # particularly useful for end-users. 7 # 8 # Author: Daniel Lundin <daniel@edgewall.com> 9 10 import httplib 11 import re 12 import sys 13 import getopt 14 15 # Pages to include in distribution 16 wiki_pages = [ 17 "CamelCase", 18 "RecentChanges", 19 "TitleIndex", 20 "TracAccessibility", 21 "TracAdmin", 22 "TracBackup", 23 "TracBrowser", 24 "TracCgi", 25 "TracChangeset", 26 "TracEnvironment", 27 "TracFastCgi", 28 "TracGuide", 29 "TracIni", 30 "TracInstall", 31 "TracImport", 32 "TracLinks", 33 "TracLogging", 34 "TracModPython", 35 "TracMultipleProjects", 36 "TracNotification", 37 "TracPermissions", 38 "TracPlugins", 39 "TracQuery", 40 "TracReports", 41 "TracRoadmap", 42 "TracRss", 43 "TracSearch", 44 "TracStandalone", 45 "TracSupport", 46 "TracSyntaxColoring", 47 "TracTickets", 48 "TracTicketsCustomFields", 49 "TracTimeline", 50 "TracUnicode", 51 "TracUpgrade", 52 "TracWiki", 53 "WikiDeletePage", 54 "WikiFormatting", 55 "WikiHtml", 56 "WikiMacros", 57 "WikiNewPage", 58 "WikiPageNames", 59 "WikiProcessors", 60 "WikiRestructuredText", 61 "WikiRestructuredTextLinks" 62 ] 63 64 def get_page_from_file (pname): 65 d = '' 66 try: 67 f = open(pname ,'r') 68 d = f.read() 69 f.close() 70 except: 71 print "Missing page: %s" % pname 72 return d 73 74 def get_page_from_web (pname): 75 host = "projects.edgewall.com" 76 rfile = "/trac/wiki/%s?format=txt" % pname 77 c = httplib.HTTPConnection(host) 78 c.request("GET", rfile) 79 r = c.getresponse() 80 d = r.read() 81 if r.status != 200 or d == ("describe %s here\n" % pname): 82 c.close() 83 print "Missing page: %s" % pname 84 c.close() 85 f = open(pname, 'w+') 86 f.write(d) 87 f.close() 88 return d 89 90 def check_links (data): 91 def get_refs(t, refs=[]): 92 r = "(?P<wikilink>(^|(?<=[^A-Za-z]))[!]?[A-Z][a-z/]+(?:[A-Z][a-z/]+)+)" 93 m = re.search (r, t) 94 if not m: 95 refs.sort() 96 result = [] 97 orf = None 98 for rf in refs: 99 if rf != orf: 100 result.append(rf) 101 orf = rf 102 return result 103 refs.append(m.group()) 104 return get_refs( t[m.end():], refs) 105 for p in data.keys(): 106 links = get_refs(data[p], []) 107 for l in links: 108 if l not in data.keys(): 109 print "Broken link: %s -> %s" % (p, l) 110 111 if __name__ == '__main__': 112 try: 113 opts, args = getopt.getopt(sys.argv[1:], "ds") 114 except getopt.GetoptError: 115 # print help information and exit: 116 print "%s [-d]" % sys.argv[0] 117 print "\t-d -- Download pages from the main project wiki." 118 sys.exit() 119 get_page = get_page_from_file 120 for o,a in opts: 121 if o == '-d': 122 get_page = get_page_from_web 123 data = {} 124 for p in wiki_pages: 125 data[p] = get_page (p) 126 check_links(data) 127 -
wiki-default/InterMapTxt
diff -urN -x .svn trac-0.9b1/wiki-default/InterMapTxt intertrac-branch/wiki-default/InterMapTxt
old new 1 = InterMapTxt = 2 3 This is the InterMapTxt wiki page, modelled after the MeatBall:InterMapTxt page. 4 5 This page is interpreted in a special way by Trac, in order to support 6 !InterWiki links in a flexible and dynamic way. 7 8 The code block after the first line separator in this page 9 will be interpreted as a list of !InterWiki specifications: 10 {{{ 11 prefix <space> URL [<space> # comment] 12 }}} 13 14 By using `$1`, `$2`, etc. within the URL, it is possible to create 15 InterWiki links which support multiple arguments, e.g. Trac:ticket:40. 16 The URL itself can be optionally followed by a comment, 17 which will subsequently be used for decorating the links 18 using that prefix. 19 20 New !InterWiki links can be created by adding to that list, in real time. 21 Note however that ''deletions'' are also taken into account immediately, 22 so it may be better to use comments for disabling prefixes. 23 24 Also note that !InterWiki prefixes are case insensitive. 25 26 27 == List of Active Prefixes == 28 29 [[InterWiki]] 30 31 ---- 32 33 == Prefix Definitions == 34 35 {{{ 36 Trac http://projects.edgewall.com/trac/$1/$2 # The Official Trac for Trac 37 TracML http://thread.gmane.org/gmane.comp.version-control.subversion.trac.general/ # Trac Mailing List Archive 38 39 # 40 # From MeatBall:InterMapTxt, rev 276 (19th of August 2005): 41 # 42 # TODO: this list requires some cleanup... many wikis are not active anymore 43 # 44 AbbeNormal http://ourpla.net/cgi/pikie? 45 AcadWiki http://xarch.tu-graz.ac.at/autocad/wiki/ 46 Acronym http://www.acronymfinder.com/af-query.asp?String=exact&Acronym= 47 Advogato http://www.advogato.org/ 48 ALife http://news.alife.org/wiki/index.php? 49 AnnotationWiki http://www.seedwiki.com/page.cfm?wikiid=368&doc= 50 AtomWiki http://intertwingly.net/wiki/pie/ 51 BeatWiki: http://wiki.doebe.li/ 52 BookShelved http://bookshelved.org/cgi-bin/wiki.pl? 53 BridgesWiki http://c2.com:8000/ 54 C2find http://c2.com/cgi/wiki?FindPage&value= 55 Cache http://www.google.com/search?q=cache: 56 CarpeWiki http://carpe.com/wiki/wiki.pl? 57 CommunityWiki http://www.emacswiki.org/cgi-bin/community? 58 CPAN http://search.cpan.org/perldoc? 59 CraoWiki http://wiki.crao.net/index.php/ 60 CreationMatters http://www.ourpla.net/cgi-bin/wiki.pl? 61 DebianBug http://bugs.debian.org/ 62 DebianPackage http://packages.debian.org/ 63 Dictionary http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query= 64 DseWiki http://www.wikiservice.at/dse/wiki.cgi? 65 EmacsWiki http://www.emacswiki.org/cgi-bin/wiki.pl? 66 FIRSTwiki http://www.firstwiki.org/ 67 Foldoc http://www.foldoc.org/foldoc/foldoc.cgi? 68 FoxWiki http://fox.wikis.com/wc.dll?Wiki~ 69 FractalWiki http://www.wikiservice.at/fractal/wikidev.cgi? 70 Google http://www.google.com/search?q= 71 GoogleGroups http://groups.google.com/groups?q= 72 HammondWiki http://www.dairiki.org/HammondWiki/ 73 HerzKinderWiki http://www.herzkinderinfo.de/Mediawiki/index.php/ 74 h2g2 http://www.bbc.co.uk/dna/h2g2/ 75 IAWiki http://www.IAwiki.net/ 76 IMDB http://us.imdb.com/Title? 77 JargonFile http://downlode.org/perl/jargon-redirect.cgi?term= 78 JspWiki http://www.ecyrd.com/JSPWiki/Wiki.jsp?page= 79 JuraWiki http://jurawiki.de/ 80 KnowHow http://www2.iro.umontreal.ca/~paquetse/cgi-bin/wiki.cgi? 81 LinuxWiki http://linuxwiki.org/ 82 LiveJournal http://livejournal.com/users/ 83 Login http://www.usemod.com/cgi-bin/mb.pl?action=login&p_userid= 84 MbTest http://www.usemod.com/cgi-bin/mbtest.pl? 85 MeatBall http://www.usemod.com/cgi-bin/mb.pl? 86 MetaWiki http://sunir.org/apps/meta.pl? 87 MetaWikiPedia http://meta.wikipedia.org/wiki/ 88 Mineralienatlas http://www.min
