From 761a9f4505d1757ef600d0addf232559ab780ccf Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Fri, 21 Jun 2013 20:17:18 +0100 Subject: [PATCH] ICErepo v0.8 --- plugins/ice-repo/.gitignore | 1 + plugins/ice-repo/LICENSE.md | 2 +- plugins/ice-repo/README.md | 24 +- plugins/ice-repo/contents.php | 94 ++- plugins/ice-repo/file-control.php | 65 +-- plugins/ice-repo/ice-repo.css | 43 +- plugins/ice-repo/ice-repo.js | 17 +- plugins/ice-repo/icon.png | Bin 1994 -> 0 bytes plugins/ice-repo/images/file-folder-icons.png | Bin 0 -> 7313 bytes plugins/ice-repo/index.php | 112 ++-- plugins/ice-repo/lib/difflib.js | 547 +++++++++++++++++- plugins/ice-repo/lib/github.js | 387 ++++++++++++- plugins/ice-repo/php.ini | 1 + plugins/ice-repo/settings.php | 121 ++++ 14 files changed, 1215 insertions(+), 199 deletions(-) create mode 100644 plugins/ice-repo/.gitignore delete mode 100644 plugins/ice-repo/icon.png create mode 100644 plugins/ice-repo/images/file-folder-icons.png create mode 100644 plugins/ice-repo/php.ini create mode 100644 plugins/ice-repo/settings.php diff --git a/plugins/ice-repo/.gitignore b/plugins/ice-repo/.gitignore new file mode 100644 index 0000000..43c661a --- /dev/null +++ b/plugins/ice-repo/.gitignore @@ -0,0 +1 @@ +error-log.txt \ No newline at end of file diff --git a/plugins/ice-repo/LICENSE.md b/plugins/ice-repo/LICENSE.md index fb21f97..5d6d102 100644 --- a/plugins/ice-repo/LICENSE.md +++ b/plugins/ice-repo/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (C) 2012 Matt Pass +Copyright (C) 2013 Matt Pass Website: mattpass.com Email: matt@mattpass.com Twitter: @mattpass diff --git a/plugins/ice-repo/README.md b/plugins/ice-repo/README.md index daca2a6..6f8fe5a 100644 --- a/plugins/ice-repo/README.md +++ b/plugins/ice-repo/README.md @@ -15,7 +15,7 @@ Cool huh? **Current screnshot:** -ICErepo screenshot +ICErepo screenshot This lib uses customised & minified versions of these brilliant and time tested repos: @@ -33,29 +33,15 @@ $ git clone git@github:mattpass/ICErepo ####Step 2: Enter your auth settings ``` -Open index.php and enter either your Github oauth token or username & password +Open settings.php and enter either your Github oauth token or username & password oauth is recommended here, view http://developer.github.com/v3/oauth/ for info (If using oauth ensure you have repo scope & your app is granted the URL you'll run under) ``` ####Step 3: Enter your repo & server dir settings ``` -Also in index.php, enter the repo & corresponding server paths +Also in settings.php, enter the repo & corresponding server paths Enter 'selected' as a 3rd param next to your default repo/server option to autoload that -Finally, set $_SESSION['userLevel'] to be > 0 with your own login system, or simply uncomment line 3 +Finally, set $_SESSION['userLevel'] to be > 0 with your own login system Upload ICErepo, visit in a web browser & enjoy -``` - -**Dev schedule:** - -**v0.8** -Bug testing, refactoring & optimisation - -**v0.9** -Alpha testing - -**v0.95** -Beta testing - -**v1.0** -Version 1 released \ No newline at end of file +``` \ No newline at end of file diff --git a/plugins/ice-repo/contents.php b/plugins/ice-repo/contents.php index 796b323..2defdbb 100644 --- a/plugins/ice-repo/contents.php +++ b/plugins/ice-repo/contents.php @@ -1,23 +1,8 @@ - + -ICErepo v<?php echo $version;?> +ICErepo v <?php echo $version;?> @@ -29,24 +14,6 @@ function numClean($var) { getPathname(), $b->getPathname()); -} - -// Class to put forward the values for sorting -class SortingIterator implements IteratorAggregate { - private $iterator = null; - public function __construct(Traversable $iterator, $callback) { - $array = iterator_to_array($iterator); - usort($array, $callback); - $this->iterator = new ArrayIterator($array); - } - public function getIterator() { - return $this->iterator; - } -} - // Get a full list of dirs & files and begin sorting using above class & function $repoPath = explode("@",strClean($_POST['repo'])); $repo = $repoPath[0]; @@ -55,15 +22,24 @@ $objectList = new SortingIterator(new RecursiveIteratorIterator(new RecursiveDir // Finally, we have our ordered list, so display $i=0; -$dirListArray = array(); -$dirSHAArray = array(); -$dirTypeArray = array(); -$finfo = finfo_open(FILEINFO_MIME_TYPE); +$dirListArray = $dirSHAArray = $dirTypeArray = array(); foreach ($objectList as $objectRef) { - $fileFolderName = rtrim(substr($objectRef->getPathname(), strlen($path)),".."); - if ($objectRef->getFilename()!="." && $fileFolderName[strlen($fileFolderName)-1]!="/") { + $fileFolderName = @rtrim(substr(str_replace("\\","/",$objectRef->getPathname()), strlen($path)),".."); + if (!is_dir($path.$fileFolderName)) { $contents = file_get_contents($path.$fileFolderName); - if (strpos(finfo_file($finfo, $path.$fileFolderName),"text")===0) { + $finfo = ""; + // Determine what to do based on mime type + if (function_exists('finfo_open')) { + $finfoMIME = finfo_open(FILEINFO_MIME_TYPE); + $finfo = finfo_file($finfoMIME, $path.$fileFolderName); + finfo_close($finfoMIME); + } else { + $fileExt = explode(" ",pathinfo($path.$fileFolderName, PATHINFO_EXTENSION)); + $fileExt = $fileExt[0]; + if (array_search($fileExt,array("coffee","css","htm","html","js","less","md","php","py","rb","ruby","txt","xml"))!==false) {$finfo = "text";}; + if (array_search($fileExt,array("gif","jpg","jpeg","png"))!==false) {$finfo = "image";}; + } + if (strpos($finfo,"text")===0 || strpos($finfo,"empty")!==false) { $contents = str_replace("\r","",$contents); }; $store = "blob ".strlen($contents)."\000".$contents; @@ -74,7 +50,6 @@ foreach ($objectList as $objectRef) { array_push($dirTypeArray,$type); } } -finfo_close($finfo); ?> -
- '; - echo ''; - echo ''; - echo ''; - echo ''; - for ($i=0;$i'; + + '; + echo ''; + echo ''; + echo ''; + echo ''; + for ($i=0;$i'; + } } - } - ?> + ?>
'; - echo htmlentities(file_get_contents($dirArray[$i])); - echo ''; + for ($i=0;$i'; + echo htmlentities(file_get_contents($dirArray[$i])); + echo ''; + } } - } ?> @@ -35,40 +10,61 @@ $repos = array( - - -
-
-
- WORKING... -
-
-
+ -
- - -
Pull selected from Github
-
- -
- -
- - - - + + Token:
+ +

+ or:

+ Username:
+
+ Password:
+
+
- + + + +
+
+ +
+
+ +
+ + +
Pull selected from Github
+
+ +
+ +
+ +
+ + + diff --git a/plugins/ice-repo/lib/difflib.js b/plugins/ice-repo/lib/difflib.js index 761bdd7..7bfc708 100644 --- a/plugins/ice-repo/lib/difflib.js +++ b/plugins/ice-repo/lib/difflib.js @@ -28,4 +28,549 @@ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF S DAMAGE. ***/ /* Author: Chas Emerick */ -__whitespace={" ":!0," ":!0,"\n":!0,"\f":!0,"\r":!0},difflib={defaultJunkFunction:function(e){return __whitespace.hasOwnProperty(e)},stripLinebreaks:function(e){return e.replace(/^[\n\r]*|[\n\r]*$/g,"")},stringAsLines:function(e){var t=e.indexOf("\n"),n=e.indexOf("\r"),r=t>-1&&n>-1||n<0?"\n":"\r",i=e.split(r);for(var s=0;st[r])return 1}return e.length==t.length?0:e.length=200&&o.length*100>t?(r[s]=1,delete n[s]):o.push(i)}else n[s]=[i]}for(var s in r)r.hasOwnProperty(s)&&delete n[s];var u=this.isjunk,a={};if(u){for(var s in r)r.hasOwnProperty(s)&&u(s)&&(a[s]=1,delete r[s]);for(var s in n)n.hasOwnProperty(s)&&u(s)&&(a[s]=1,delete n[s])}this.isbjunk=difflib.__isindict(a),this.isbpopular=difflib.__isindict(r)},this.find_longest_match=function(e,t,n,r){var i=this.a,s=this.b,o=this.b2j,u=this.isbjunk,a=e,f=n,l=0,c=null,h={},p=[];for(var d=e;d=r)break;v[c]=k=difflib.__dictget(h,c-1,0)+1,k>l&&(a=d-k+1,f=c-k+1,l=k)}h=v}while(a>e&&f>n&&!u(s[f-1])&&i[a-1]==s[f-1])a--,f--,l++;while(a+le&&f>n&&u(s[f-1])&&i[a-1]==s[f-1])a--,f--,l++;while(a+la&&(f.push([r,i,Math.min(s,i+e),o,Math.min(u,o+e)]),i=Math.max(i,s-e),o=Math.max(o,u-e)),f.push([r,i,s,o,u]));return f&&f[f.length-1][0]=="equal"&&f.pop(),f},this.ratio=function(){return matches=difflib.__reduce(function(e,t){return e+t[t.length-1]},this.get_matching_blocks(),0),difflib.__calculate_ratio(matches,this.a.length+this.b.length)},this.quick_ratio=function(){var e,t;if(this.fullbcount==null){this.fullbcount=e={};for(var n=0;n0&&s++;return difflib.__calculate_ratio(s,this.a.length+this.b.length)},this.real_quick_ratio=function(){var e=this.a.length,t=this.b.length;return _calculate_ratio(Math.min(e,t),e+t)},this.isjunk=n?n:difflib.defaultJunkFunction,this.a=this.b=null,this.set_seqs(e,t)}},diffview={buildView:function(e){function a(e,t){var n=document.createElement(e);return n.className=t,n}function f(e,t){var n=document.createElement(e);return n.appendChild(document.createTextNode(t)),n}function l(e,t,n){var r=document.createElement(e);return r.className=t,r.appendChild(document.createTextNode(n)),r}function v(e,t,n,r,i){return t1&&(g>0&&N==o||g==0&&N==0)&&change=="equal"){var C=S-(g==0?1:2)*o;if(C>1){x.push(h=document.createElement("tr")),y+=C,w+=C,N+=C-1,h.appendChild(f("th","...")),u||h.appendChild(l("td","skip","")),h.appendChild(f("th","...")),h.appendChild(l("td","skip",""));if(g+1==r.length)break;continue}}x.push(h=document.createElement("tr")),u?change=="insert"?m(h,null,w++,n,change):change=="replace"?(T.push(d=document.createElement("tr")),y -1 && crpos > -1) || crpos < 0) ? "\n" : "\r"; + + var lines = str.split(linebreak); + for (var i = 0; i < lines.length; i++) { + lines[i] = difflib.stripLinebreaks(lines[i]); + } + + return lines; + }, + + // iteration-based reduce implementation + __reduce: function (func, list, initial) { + if (initial != null) { + var value = initial; + var idx = 0; + } else if (list) { + var value = list[0]; + var idx = 1; + } else { + return null; + } + + for (; idx < list.length; idx++) { + value = func(value, list[idx]); + } + + return value; + }, + + // comparison function for sorting lists of numeric tuples + __ntuplecomp: function (a, b) { + var mlen = Math.max(a.length, b.length); + for (var i = 0; i < mlen; i++) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + + return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1); + }, + + __calculate_ratio: function (matches, length) { + return length ? 2.0 * matches / length : 1.0; + }, + + // returns a function that returns true if a key passed to the returned function + // is in the dict (js object) provided to this function; replaces being able to + // carry around dict.has_key in python... + __isindict: function (dict) { + return function (key) { return dict.hasOwnProperty(key); }; + }, + + // replacement for python's dict.get function -- need easy default values + __dictget: function (dict, key, defaultValue) { + return dict.hasOwnProperty(key) ? dict[key] : defaultValue; + }, + + SequenceMatcher: function (a, b, isjunk) { + this.set_seqs = function (a, b) { + this.set_seq1(a); + this.set_seq2(b); + } + + this.set_seq1 = function (a) { + if (a == this.a) return; + this.a = a; + this.matching_blocks = this.opcodes = null; + } + + this.set_seq2 = function (b) { + if (b == this.b) return; + this.b = b; + this.matching_blocks = this.opcodes = this.fullbcount = null; + this.__chain_b(); + } + + this.__chain_b = function () { + var b = this.b; + var n = b.length; + var b2j = this.b2j = {}; + var populardict = {}; + for (var i = 0; i < b.length; i++) { + var elt = b[i]; + if (b2j.hasOwnProperty(elt)) { + var indices = b2j[elt]; + if (n >= 200 && indices.length * 100 > n) { + populardict[elt] = 1; + delete b2j[elt]; + } else { + indices.push(i); + } + } else { + b2j[elt] = [i]; + } + } + + for (var elt in populardict) { + if (populardict.hasOwnProperty(elt)) { + delete b2j[elt]; + } + } + + var isjunk = this.isjunk; + var junkdict = {}; + if (isjunk) { + for (var elt in populardict) { + if (populardict.hasOwnProperty(elt) && isjunk(elt)) { + junkdict[elt] = 1; + delete populardict[elt]; + } + } + for (var elt in b2j) { + if (b2j.hasOwnProperty(elt) && isjunk(elt)) { + junkdict[elt] = 1; + delete b2j[elt]; + } + } + } + + this.isbjunk = difflib.__isindict(junkdict); + this.isbpopular = difflib.__isindict(populardict); + } + + this.find_longest_match = function (alo, ahi, blo, bhi) { + var a = this.a; + var b = this.b; + var b2j = this.b2j; + var isbjunk = this.isbjunk; + var besti = alo; + var bestj = blo; + var bestsize = 0; + var j = null; + + var j2len = {}; + var nothing = []; + for (var i = alo; i < ahi; i++) { + var newj2len = {}; + var jdict = difflib.__dictget(b2j, a[i], nothing); + for (var jkey in jdict) { + if (jdict.hasOwnProperty(jkey)) { + j = jdict[jkey]; + if (j < blo) continue; + if (j >= bhi) break; + newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1; + if (k > bestsize) { + besti = i - k + 1; + bestj = j - k + 1; + bestsize = k; + } + } + } + j2len = newj2len; + } + + while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) { + besti--; + bestj--; + bestsize++; + } + + while (besti + bestsize < ahi && bestj + bestsize < bhi && + !isbjunk(b[bestj + bestsize]) && + a[besti + bestsize] == b[bestj + bestsize]) { + bestsize++; + } + + while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) { + besti--; + bestj--; + bestsize++; + } + + while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) && + a[besti + bestsize] == b[bestj + bestsize]) { + bestsize++; + } + + return [besti, bestj, bestsize]; + } + + this.get_matching_blocks = function () { + if (this.matching_blocks != null) return this.matching_blocks; + var la = this.a.length; + var lb = this.b.length; + + var queue = [[0, la, 0, lb]]; + var matching_blocks = []; + var alo, ahi, blo, bhi, qi, i, j, k, x; + while (queue.length) { + qi = queue.pop(); + alo = qi[0]; + ahi = qi[1]; + blo = qi[2]; + bhi = qi[3]; + x = this.find_longest_match(alo, ahi, blo, bhi); + i = x[0]; + j = x[1]; + k = x[2]; + + if (k) { + matching_blocks.push(x); + if (alo < i && blo < j) + queue.push([alo, i, blo, j]); + if (i+k < ahi && j+k < bhi) + queue.push([i + k, ahi, j + k, bhi]); + } + } + + matching_blocks.sort(difflib.__ntuplecomp); + + var i1 = j1 = k1 = block = 0; + var non_adjacent = []; + for (var idx in matching_blocks) { + if (matching_blocks.hasOwnProperty(idx)) { + block = matching_blocks[idx]; + i2 = block[0]; + j2 = block[1]; + k2 = block[2]; + if (i1 + k1 == i2 && j1 + k1 == j2) { + k1 += k2; + } else { + if (k1) non_adjacent.push([i1, j1, k1]); + i1 = i2; + j1 = j2; + k1 = k2; + } + } + } + + if (k1) non_adjacent.push([i1, j1, k1]); + + non_adjacent.push([la, lb, 0]); + this.matching_blocks = non_adjacent; + return this.matching_blocks; + } + + this.get_opcodes = function () { + if (this.opcodes != null) return this.opcodes; + var i = 0; + var j = 0; + var answer = []; + this.opcodes = answer; + var block, ai, bj, size, tag; + var blocks = this.get_matching_blocks(); + for (var idx in blocks) { + if (blocks.hasOwnProperty(idx)) { + block = blocks[idx]; + ai = block[0]; + bj = block[1]; + size = block[2]; + tag = ''; + if (i < ai && j < bj) { + tag = 'replace'; + } else if (i < ai) { + tag = 'delete'; + } else if (j < bj) { + tag = 'insert'; + } + if (tag) answer.push([tag, i, ai, j, bj]); + i = ai + size; + j = bj + size; + + if (size) answer.push(['equal', ai, i, bj, j]); + } + } + + return answer; + } + + // this is a generator function in the python lib, which of course is not supported in javascript + // the reimplementation builds up the grouped opcodes into a list in their entirety and returns that. + this.get_grouped_opcodes = function (n) { + if (!n) n = 3; + var codes = this.get_opcodes(); + if (!codes) codes = [["equal", 0, 1, 0, 1]]; + var code, tag, i1, i2, j1, j2; + if (codes[0][0] == 'equal') { + code = codes[0]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2]; + } + if (codes[codes.length - 1][0] == 'equal') { + code = codes[codes.length - 1]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]; + } + + var nn = n + n; + var groups = []; + for (var idx in codes) { + if (codes.hasOwnProperty(idx)) { + code = codes[idx]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + if (tag == 'equal' && i2 - i1 > nn) { + groups.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]); + i1 = Math.max(i1, i2-n); + j1 = Math.max(j1, j2-n); + } + + groups.push([tag, i1, i2, j1, j2]); + } + } + + if (groups && groups[groups.length - 1][0] == 'equal') groups.pop(); + + return groups; + } + + this.ratio = function () { + matches = difflib.__reduce( + function (sum, triple) { return sum + triple[triple.length - 1]; }, + this.get_matching_blocks(), 0); + return difflib.__calculate_ratio(matches, this.a.length + this.b.length); + } + + this.quick_ratio = function () { + var fullbcount, elt; + if (this.fullbcount == null) { + this.fullbcount = fullbcount = {}; + for (var i = 0; i < this.b.length; i++) { + elt = this.b[i]; + fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1; + } + } + fullbcount = this.fullbcount; + + var avail = {}; + var availhas = difflib.__isindict(avail); + var matches = numb = 0; + for (var i = 0; i < this.a.length; i++) { + elt = this.a[i]; + if (availhas(elt)) { + numb = avail[elt]; + } else { + numb = difflib.__dictget(fullbcount, elt, 0); + } + avail[elt] = numb - 1; + if (numb > 0) matches++; + } + + return difflib.__calculate_ratio(matches, this.a.length + this.b.length); + } + + this.real_quick_ratio = function () { + var la = this.a.length; + var lb = this.b.length; + return _calculate_ratio(Math.min(la, lb), la + lb); + } + + this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction; + this.a = this.b = null; + this.set_seqs(a, b); + } +} + +diffview = { + /** + * Builds and returns a visual diff view. The single parameter, `params', should contain + * the following values: + * + * - baseTextLines: the array of strings that was used as the base text input to SequenceMatcher + * - newTextLines: the array of strings that was used as the new text input to SequenceMatcher + * - opcodes: the array of arrays returned by SequenceMatcher.get_opcodes() + * - baseTextName: the title to be displayed above the base text listing in the diff view; defaults + * to "Base Text" + * - newTextName: the title to be displayed above the new text listing in the diff view; defaults + * to "New Text" + * - contextSize: the number of lines of context to show around differences; by default, all lines + * are shown + * - viewType: if 0, a side-by-side diff view is generated (default); if 1, an inline diff view is + * generated + */ + buildView: function (params) { + var baseTextLines = params.baseTextLines; + var newTextLines = params.newTextLines; + var opcodes = params.opcodes; + var baseTextName = params.baseTextName ? params.baseTextName : "Base Text"; + var newTextName = params.newTextName ? params.newTextName : "New Text"; + var contextSize = params.contextSize; + var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0; + + if (baseTextLines == null) + throw "Cannot build diff view; baseTextLines is not defined."; + if (newTextLines == null) + throw "Cannot build diff view; newTextLines is not defined."; + if (!opcodes) + throw "Canno build diff view; opcodes is not defined."; + + function celt (name, clazz) { + var e = document.createElement(name); + e.className = clazz; + return e; + } + + function telt (name, text) { + var e = document.createElement(name); + e.appendChild(document.createTextNode(text)); + return e; + } + + function ctelt (name, clazz, text) { + var e = document.createElement(name); + e.className = clazz; + e.appendChild(document.createTextNode(text)); + return e; + } + + var tdata = document.createElement("thead"); + var node = document.createElement("tr"); + tdata.appendChild(node); + if (inline) { + node.appendChild(document.createElement("th")); + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName)); + } else { + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", baseTextName)); + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", newTextName)); + } + tdata = [tdata]; + + var rows = []; + var node2; + + /** + * Adds two cells to the given row; if the given row corresponds to a real + * line number (based on the line index tidx and the endpoint of the + * range in question tend), then the cells will contain the line number + * and the line of text from textLines at position tidx (with the class of + * the second cell set to the name of the change represented), and tidx + 1 will + * be returned. Otherwise, tidx is returned, and two empty cells are added + * to the given row. + */ + function addCells (row, tidx, tend, textLines, change) { + if (tidx < tend) { + row.appendChild(telt("th", (tidx + 1).toString())); + row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))); + return tidx + 1; + } else { + row.appendChild(document.createElement("th")); + row.appendChild(celt("td", "empty")); + return tidx; + } + } + + function addCellsInline (row, tidx, tidx2, textLines, change) { + row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString())); + row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString())); + row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))); + } + + for (var idx = 0; idx < opcodes.length; idx++) { + code = opcodes[idx]; + change = code[0]; + var b = code[1]; + var be = code[2]; + var n = code[3]; + var ne = code[4]; + var rowcnt = Math.max(be - b, ne - n); + var toprows = []; + var botrows = []; + for (var i = 0; i < rowcnt; i++) { + // jump ahead if we've alredy provided leading context or if this is the first range + if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change=="equal") { + var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize); + if (jump > 1) { + toprows.push(node = document.createElement("tr")); + + b += jump; + n += jump; + i += jump - 1; + node.appendChild(telt("th", "...")); + if (!inline) node.appendChild(ctelt("td", "skip", "")); + node.appendChild(telt("th", "...")); + node.appendChild(ctelt("td", "skip", "")); + + // skip last lines if they're all equal + if (idx + 1 == opcodes.length) { + break; + } else { + continue; + } + } + } + + toprows.push(node = document.createElement("tr")); + if (inline) { + if (change == "insert") { + addCellsInline(node, null, n++, newTextLines, change); + } else if (change == "replace") { + botrows.push(node2 = document.createElement("tr")); + if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete"); + if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert"); + } else if (change == "delete") { + addCellsInline(node, b++, null, baseTextLines, change); + } else { + // equal + addCellsInline(node, b++, n++, baseTextLines, change); + } + } else { + b = addCells(node, b, be, baseTextLines, change); + n = addCells(node, n, ne, newTextLines, change); + } + } + + for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]); + for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]); + } + + rows.push(node = ctelt("th", "author", "diff view generated by ")); + node.setAttribute("colspan", inline ? 3 : 4); + node.appendChild(node2 = telt("a", "jsdifflib")); + node2.setAttribute("href", "http://github.com/cemerick/jsdifflib"); + + tdata.push(node = document.createElement("tbody")); + for (var idx in rows) node.appendChild(rows[idx]); + + node = celt("table", "diff" + (inline ? " inlinediff" : "")); + for (var idx in tdata) node.appendChild(tdata[idx]); + return node; + } +} \ No newline at end of file diff --git a/plugins/ice-repo/lib/github.js b/plugins/ice-repo/lib/github.js index 9bfd61c..02caf95 100644 --- a/plugins/ice-repo/lib/github.js +++ b/plugins/ice-repo/lib/github.js @@ -8,4 +8,389 @@ // For all details and documentation: // http://substance.io/michael/github -(function(){var e,t="https://api.github.com";e=window.Github=function(n){function r(e,r,i,s,o){function u(){var e=t+r;return e+(/\?/.test(e)?"&":"?")+(new Date).getTime()}var a=new XMLHttpRequest;o||(a.dataType="json"),a.open(e,u()),a.onreadystatechange=function(){this.readyState==4&&(this.status>=200&&this.status<300||this.status===304?s(null,o?this.responseText:this.responseText?JSON.parse(this.responseText):!0):s({request:this,error:this.status}))},a.setRequestHeader("Accept","application/vnd.github.raw"),a.setRequestHeader("Content-Type","application/json;charset=UTF-8"),(n.auth=="oauth"&&n.token||n.auth=="basic"&&n.username&&n.password)&&a.setRequestHeader("Authorization",n.auth=="oauth"?"token "+n.token:"Basic "+Base64.encode(n.username+":"+n.password)),i?a.send(JSON.stringify(i)):a.send()}e.Repository=function(e){function u(e,t){if(e===o.branch&&o.sha)return t(null,o.sha);i.getRef("heads/"+e,function(n,r){o.branch=e,o.sha=r,t(n,r)})}var t=e.name,n=e.user,i=this,s="/repos/"+n+"/"+t,o={branch:null,sha:null};this.getRef=function(e,t){r("GET",s+"/git/refs/"+e,null,function(e,n){if(e)return t(e);t(null,n.object.sha)})},this.createRef=function(e,t){r("POST",s+"/git/refs",e,t)},this.deleteRef=function(t,n){r("DELETE",s+"/git/refs/"+t,e,n)},this.listBranches=function(e){r("GET",s+"/git/refs/heads",null,function(t,n){if(t)return e(t);e(null,_.map(n,function(e){return _.last(e.ref.split("/"))}))})},this.getBlob=function(e,t){r("GET",s+"/git/blobs/"+e,null,t,"raw")},this.getSha=function(e,t,n){if(t==="")return i.getRef("heads/"+e,n);i.getTree(e+"?recursive=true",function(e,r){var i=_.select(r,function(e){return e.path===t})[0];n(null,i?i.sha:null)})},this.getTree=function(e,t){r("GET",s+"/git/trees/"+e,null,function(e,n){if(e)return t(e);t(null,n.tree)})},this.postBlob=function(e,t){typeof e=="string"&&(e={content:e,encoding:"utf-8"}),r("POST",s+"/git/blobs",e,function(e,n){if(e)return t(e);t(null,n.sha)})},this.updateTree=function(e,t,n,i){var o={base_tree:e,tree:[{path:t,mode:"100644",type:"blob",sha:n}]};r("POST",s+"/git/trees",o,function(e,t){if(e)return i(e);i(null,t.sha)})},this.postTree=function(e,t){r("POST",s+"/git/trees",{tree:e},function(e,n){if(e)return t(e);t(null,n.sha)})},this.commit=function(t,n,i,u){var a={message:i,author:{name:e.username},parents:[t],tree:n};r("POST",s+"/git/commits",a,function(e,t){o.sha=t.sha;if(e)return u(e);u(null,t.sha)})},this.updateHead=function(e,t,n){r("PATCH",s+"/git/refs/heads/"+e,{sha:t},function(e,t){n(e)})},this.show=function(e){r("GET",s,null,e)},this.contents=function(e,t){r("GET",s+"/contents",{path:e},t)},this.fork=function(e){r("POST",s+"/forks",null,e)},this.createPullRequest=function(e,t){r("POST",s+"/pulls",e,t)},this.read=function(e,t,n){i.getSha(e,t,function(e,t){if(!t)return n("not found",null);i.getBlob(t,function(e,r){n(e,r,t)})})},this.remove=function(e,t,n){u(e,function(r,s){i.getTree(s+"?recursive=true",function(r,o){var u=_.reject(o,function(e){return e.path===t});_.each(u,function(e){e.type==="tree"&&delete e.sha}),i.postTree(u,function(r,o){i.commit(s,o,"Deleted "+t,function(t,r){i.updateHead(e,r,function(e){n(e)})})})})})},this.move=function(e,t,n,r){u(e,function(s,o){i.getTree(o+"?recursive=true",function(s,u){_.each(u,function(e){e.path===t&&(e.path=n),e.type==="tree"&&delete e.sha}),i.postTree(u,function(n,s){i.commit(o,s,"Deleted "+t,function(t,n){i.updateHead(e,n,function(e){r(e)})})})})})},this.write=function(e,t,n,r,s){u(e,function(o,u){if(o)return s(o);i.postBlob(n,function(n,o){if(n)return s(n);i.updateTree(u,t,o,function(t,n){if(t)return s(t);i.commit(u,n,r,function(t,n){if(t)return s(t);i.updateHead(e,n,s)})})})})}},e.Gist=function(e){var t=e.id,n=this,i="/gists/"+t;this.read=function(e){r("GET",i,null,function(t,n){e(t,n)})},this.delete=function(e){r("DELETE",i,null,function(t,n){e(t,n)})},this.fork=function(e){r("POST",i+"/fork",null,function(t,n){e(t,n)})},this.update=function(e,t){r("PATCH",i,e,function(e,n){t(e,n)})}},this.getRepo=function(t,n){return new e.Repository({user:t,name:n})}}}).call(this) \ No newline at end of file +(function() { + var Github; + var API_URL = 'https://api.github.com'; + + Github = window.Github = function(options) { + + // HTTP Request Abstraction + // ======= + // + // I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec. + + function _request(method, path, data, cb, raw) { + function getURL() { + var url = API_URL + path; + return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(); + } + + var xhr = new XMLHttpRequest(); + if (!raw) {xhr.dataType = "json"} + + xhr.open(method, getURL()); + xhr.onreadystatechange = function () { + if (this.readyState == 4) { + if (this.status >= 200 && this.status < 300 || this.status === 304) { + cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true); + } else { + cb({request: this, error: this.status}); + } + } + } + xhr.setRequestHeader('Accept','application/vnd.github.raw'); + xhr.setRequestHeader('Content-Type','application/json;charset=UTF-8'); + if ( + (options.auth == 'oauth' && options.token) || + (options.auth == 'basic' && options.username && options.password) + ) { + xhr.setRequestHeader('Authorization',options.auth == 'oauth' + ? 'token '+ options.token + : 'Basic ' + Base64.encode(options.username + ':' + options.password) + ); + } + data ? xhr.send(JSON.stringify(data)) : xhr.send(); + } + + // Repository API + // ======= + + Github.Repository = function(options) { + var repo = options.name; + var user = options.user; + + var that = this; + var repoPath = "/repos/" + user + "/" + repo; + + var currentTree = { + "branch": null, + "sha": null + }; + + // Uses the cache if branch has not been changed + // ------- + + function updateTree(branch, cb) { + if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha); + that.getRef("heads/"+branch, function(err, sha) { + currentTree.branch = branch; + currentTree.sha = sha; + cb(err, sha); + }); + } + + // Get a particular reference + // ------- + + this.getRef = function(ref, cb) { + _request("GET", repoPath + "/git/refs/" + ref, null, function(err, res) { + if (err) return cb(err); + cb(null, res.object.sha); + }); + }; + + // Create a new reference + // -------- + // + // { + // "ref": "refs/heads/my-new-branch-name", + // "sha": "827efc6d56897b048c772eb4087f854f46256132" + // } + + this.createRef = function(options, cb) { + _request("POST", repoPath + "/git/refs", options, cb); + }; + + // Delete a reference + // -------- + // + // repo.deleteRef('heads/gh-pages') + // repo.deleteRef('tags/v1.0') + + this.deleteRef = function(ref, cb) { + _request("DELETE", repoPath + "/git/refs/"+ref, options, cb); + }; + + // List all branches of a repository + // ------- + + this.listBranches = function(cb) { + _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) { + if (err) return cb(err); + cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); })); + }); + }; + + // Retrieve the contents of a blob + // ------- + + this.getBlob = function(sha, cb) { + _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw'); + }; + + // For a given file path, get the corresponding sha (blob for files, tree for dirs) + // ------- + + this.getSha = function(branch, path, cb) { + // Just use head if path is empty + if (path === "") return that.getRef("heads/"+branch, cb); + that.getTree(branch+"?recursive=true", function(err, tree) { + var file = _.select(tree, function(file) { + return file.path === path; + })[0]; + cb(null, file ? file.sha : null); + }); + }; + + // Retrieve the tree a commit points to + // ------- + + this.getTree = function(tree, cb) { + _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) { + if (err) return cb(err); + cb(null, res.tree); + }); + }; + + // Post a new blob object, getting a blob SHA back + // ------- + + this.postBlob = function(content, cb) { + if (typeof(content) === "string") { + content = { + "content": content, + "encoding": "utf-8" + }; + } + + _request("POST", repoPath + "/git/blobs", content, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); + }; + + // Update an existing tree adding a new blob object getting a tree SHA back + // ------- + + this.updateTree = function(baseTree, path, blob, cb) { + var data = { + "base_tree": baseTree, + "tree": [ + { + "path": path, + "mode": "100644", + "type": "blob", + "sha": blob + } + ] + }; + _request("POST", repoPath + "/git/trees", data, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); + }; + + // Post a new tree object having a file path pointer replaced + // with a new blob SHA getting a tree SHA back + // ------- + + this.postTree = function(tree, cb) { + _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); + }; + + // Create a new commit object with the current commit SHA as the parent + // and the new tree SHA, getting a commit SHA back + // ------- + + this.commit = function(parent, tree, message, cb) { + var data = { + "message": message, + "author": { + "name": options.username + }, + "parents": [ + parent + ], + "tree": tree + }; + + _request("POST", repoPath + "/git/commits", data, function(err, res) { + currentTree.sha = res.sha; // update latest commit + if (err) return cb(err); + cb(null, res.sha); + }); + }; + + // Update the reference of your head to point to the new commit SHA + // ------- + + this.updateHead = function(head, commit, cb) { + _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) { + cb(err); + }); + }; + + // Show repository information + // ------- + + this.show = function(cb) { + _request("GET", repoPath, null, cb); + }; + + // Get contents + // -------- + + this.contents = function(path, cb) { + _request("GET", repoPath + "/contents", { path: path }, cb); + }; + + // Fork repository + // ------- + + this.fork = function(cb) { + _request("POST", repoPath + "/forks", null, cb); + }; + + // Create pull request + // -------- + + this.createPullRequest = function(options, cb) { + _request("POST", repoPath + "/pulls", options, cb); + }; + + // Read file at given path + // ------- + + this.read = function(branch, path, cb) { + that.getSha(branch, path, function(err, sha) { + if (!sha) return cb("not found", null); + that.getBlob(sha, function(err, content) { + cb(err, content, sha); + }); + }); + }; + + // Remove a file from the tree + // ------- + + this.remove = function(branch, path, cb) { + updateTree(branch, function(err, latestCommit) { + that.getTree(latestCommit+"?recursive=true", function(err, tree) { + // Update Tree + var newTree = _.reject(tree, function(ref) { return ref.path === path }); + _.each(newTree, function(ref) { + if (ref.type === "tree") delete ref.sha; + }); + + that.postTree(newTree, function(err, rootTree) { + that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) { + that.updateHead(branch, commit, function(err) { + cb(err); + }); + }); + }); + }); + }); + }; + + // Move a file to a new location + // ------- + + this.move = function(branch, path, newPath, cb) { + updateTree(branch, function(err, latestCommit) { + that.getTree(latestCommit+"?recursive=true", function(err, tree) { + // Update Tree + _.each(tree, function(ref) { + if (ref.path === path) ref.path = newPath; + if (ref.type === "tree") delete ref.sha; + }); + + that.postTree(tree, function(err, rootTree) { + that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) { + that.updateHead(branch, commit, function(err) { + cb(err); + }); + }); + }); + }); + }); + }; + + // Write file contents to a given branch and path + // ------- + + this.write = function(branch, path, content, message, cb) { + updateTree(branch, function(err, latestCommit) { + if (err) return cb(err); + that.postBlob(content, function(err, blob) { + if (err) return cb(err); + that.updateTree(latestCommit, path, blob, function(err, tree) { + if (err) return cb(err); + that.commit(latestCommit, tree, message, function(err, commit) { + if (err) return cb(err); + that.updateHead(branch, commit, cb); + }); + }); + }); + }); + }; + }; + + // Gists API + // ======= + + Github.Gist = function(options) { + var id = options.id; + var that = this; + var gistPath = "/gists/"+id; + + // Read the gist + // -------- + + this.read = function(cb) { + _request("GET", gistPath, null, function(err, gist) { + cb(err, gist); + }); + }; + + + // Delete the gist + // -------- + + this.delete = function(cb) { + _request("DELETE", gistPath, null, function(err,res) { + cb(err,res); + }); + }; + + // Fork a gist + // -------- + + this.fork = function(cb) { + _request("POST", gistPath+"/fork", null, function(err,res) { + cb(err,res); + }); + }; + + // Update a gist with the new stuff + // -------- + + this.update = function(options, cb) { + _request("PATCH", gistPath, options, function(err,res) { + cb(err,res); + }); + }; + }; + + // Top Level API + // ------- + + this.getRepo = function(user, repo) { + return new Github.Repository({user: user, name: repo}); + }; + + }; +}).call(this); \ No newline at end of file diff --git a/plugins/ice-repo/php.ini b/plugins/ice-repo/php.ini new file mode 100644 index 0000000..702ced9 --- /dev/null +++ b/plugins/ice-repo/php.ini @@ -0,0 +1 @@ +magic_quotes_gpc = Off \ No newline at end of file diff --git a/plugins/ice-repo/settings.php b/plugins/ice-repo/settings.php new file mode 100644 index 0000000..50b72b9 --- /dev/null +++ b/plugins/ice-repo/settings.php @@ -0,0 +1,121 @@ + + array( + 'timeout' => 60 // secs + ) +)); + +// Start a session if we haven't already +if(!isset($_SESSION)) {@session_start();} + +if (!isset($_SESSION['userLevel']) || $_SESSION['userLevel'] < 1) { + die("Sorry, you need to be logged in to use ICErepo"); +} + +// Set session vars if we're logging in via a session +if (isset($_REQUEST['token']) && $_REQUEST['token']!="") { + $_SESSION['token'] = $_REQUEST['token']; +} +if (isset($_REQUEST['username']) && $_REQUEST['username']!="") { + $_SESSION['username'] = $_REQUEST['username']; + $_SESSION['password'] = $_REQUEST['password']; +} + +// Reestablish those session vars in an ongoing way +if (isset($_SESSION['token'])) { + $_SESSION['token'] = $_SESSION['token']; +} +if (isset($_SESSION['username'])) { + $username = $_SESSION['username'] = $_SESSION['username']; + $password = $_SESSION['password'] = $_SESSION['password']; +} + +if ($token=="" && $username=="" && !isset($_GET['sessionLogin'])) { + header("Location: ?sessionLogin=true"); +} + +// returns converted entities where there are HTML entity equivalents +function strClean($var) { + return htmlentities($var, ENT_QUOTES, "UTF-8"); +} + +// returns a number, whole or decimal or null +function numClean($var) { + return is_numeric($var) ? floatval($var) : false; +} + +// Function to sort given values alphabetically +function alphasort($a, $b) { + return strcmp($a->getPathname(), $b->getPathname()); +} + +// Class to put forward the values for sorting +class SortingIterator implements IteratorAggregate { + private $iterator = null; + public function __construct(Traversable $iterator, $callback) { + $array = iterator_to_array($iterator); + usort($array, $callback); + $this->iterator = new ArrayIterator($array); + } + public function getIterator() { + return $this->iterator; + } +} + +// If magic quotes are still on (attempted to switch off in php.ini) +if (get_magic_quotes_gpc ()) { + function stripslashes_deep($value) { + $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); + return $value; + } + $_POST = (isset($_POST) && !empty($_POST)) ? array_map('stripslashes_deep', $_POST) : array(); + $_GET = (isset($_GET) && !empty($_GET)) ? array_map('stripslashes_deep', $_GET) : array(); + $_COOKIE = (isset($_COOKIE) && !empty($_COOKIE)) ? array_map('stripslashes_deep', $_COOKIE) : array(); + $_REQUEST = (isset($_REQUEST) && !empty($_REQUEST)) ? array_map('stripslashes_deep', $_REQUEST) : array(); +} +?> \ No newline at end of file