| Current Path : /home/w/u/e/wuectly/www/03cbe/ |
| Current File : /home/w/u/e/wuectly/www/03cbe/com_contenthistory.tar |
css/jquery.pretty-text-diff.css 0000604 00000000214 15245531301 0012573 0 ustar 00 /* colors for pretty-text-diff*/
ins {
background-color: #c6ffc6;
text-decoration: none;
}
del {
background-color: #ffc6c6;
}
js/admin-history-versions.js 0000604 00000000461 15245531301 0012147 0 ustar 00 /**
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
window.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(document.getElementById('versionsModal'));
});
js/diff_match_patch.js 0000604 00000045370 15245531301 0010765 0 ustar 00 (function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,
d):this.diff_bisect_(a,b,d)};
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,q=0!=k%2,r=0,t=0,p=0,w=0,v=0;v<f&&!((new Date).getTime()>c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var s=m-n;m<d&&s<e&&a.charAt(m)==b.charAt(s);)m++,s++;j[l]=m;if(m>d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var u=d-i[l];if(m>=
u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=u-n;u<d&&m<e&&a.charAt(d-u-1)==b.charAt(e-m-1);)u++,m++;i[l]=u;if(u>d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l<h&&-1!=j[l])&&(m=j[l],s=g+m-l,u=d-u,m>=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var r=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(r):void 0!==e[r])?b+=String.fromCharCode(e[r]):(b+=String.fromCharCode(g),e[r]=g,d[g++]=r)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),s=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<s+m&&(g=b.substring(e-s,e)+b.substring(e,e+m),h=a.substring(0,c-s),j=a.substring(c+m),n=b.substring(0,e-s),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&(e.length<=Math.max(g,h)&&e.length<=Math.max(j,i))&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case 1:b[g]='<ins style="background:#e6ffe6;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffe6e6;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,q=b.length+a.length,r,t=0;t<b.length;t++){i=0;for(k=q;i<k;)d(t,c+
k)<=g?i=k:q=k,k=Math.floor((q-i)/2+i);q=k;i=Math.max(1,c-k+1);var p=Math.min(c+k,a.length)+b.length;k=Array(p+2);for(k[p+1]=(1<<t)-1;p>=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h};
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
c.length+d.length;a.length2+=c.length+d.length}};
diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];!e&&0!==i&&(a.start1=f,a.start2=g);switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var q=a[f].diffs[i];0!==q[0]&&(k=this.diff_xIndex(g,h));1===q[0]?b=b.substring(0,
j+k)+q[1]+b.substring(j+k):-1===q[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+q[1].length)));-1!==q[0]&&(h+=q[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([0,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
js/jquery.pretty-text-diff.js 0000604 00000005766 15245531301 0012264 0 ustar 00 // Generated by CoffeeScript 1.4.0
/*
@preserve jQuery.PrettyTextDiff 1.0.2
See https://github.com/arnab/jQuery.PrettyTextDiff/
Modified to show with and without HTML: Mark Dexter, Joomla Project.
*/
(function() {
var $;
$ = jQuery;
$.fn.extend({
prettyTextDiff: function(options) {
var dmp, settings;
settings = {
originalContainer: ".original",
changedContainer: ".changed",
diffContainer: ".diff",
originalHtmlContainer: ".originalhtml",
changedHtmlContainer: ".changedhtml",
diffHtmlContainer: ".diffhtml",
cleanup: true,
debug: false
};
settings = $.extend(settings, options);
$.fn.prettyTextDiff.debug("Options: ", settings, settings);
dmp = new diff_match_patch();
return this.each(function() {
var changed, diff_as_html, diffs, original, changedhtml, originalhtml, diffshtml;
original = $(settings.originalContainer, this).text();
$.fn.prettyTextDiff.debug("Original text found: ", original, settings);
changed = $(settings.changedContainer, this).text();
$.fn.prettyTextDiff.debug("Changed text found: ", changed, settings);
originalhtml = $(settings.originalHtmlContainer, this).text();
$.fn.prettyTextDiff.debug("Original text found: ", original, settings);
changedhtml = $(settings.changedHtmlContainer, this).text();
$.fn.prettyTextDiff.debug("Changed text found: ", changed, settings);
diffs = dmp.diff_main(original, changed);
diffshtml = dmp.diff_main(originalhtml, changedhtml);
if (settings.cleanup) {
dmp.diff_cleanupSemantic(diffs);
dmp.diff_cleanupSemantic(diffshtml);
}
$.fn.prettyTextDiff.debug("Diffs: ", diffs, settings);
diff_as_html = diffs.map(function(diff) {
return $.fn.prettyTextDiff.createHTML(diff);
});
diffhtml_as_html = diffshtml.map(function(diff) {
return $.fn.prettyTextDiff.createHTML(diff);
});
$(settings.diffContainer, this).html(diff_as_html.join(''));
$(settings.diffHtmlContainer, this).html(diffhtml_as_html.join(''));
return this;
});
}
});
$.fn.prettyTextDiff.debug = function(message, object, settings) {
if (settings.debug) {
return console.log(message, object);
}
};
$.fn.prettyTextDiff.createHTML = function(diff) {
var data, html, operation, pattern_amp, pattern_gt, pattern_lt, pattern_para, text;
html = [];
pattern_amp = /&/g;
pattern_lt = /</g;
pattern_gt = />/g;
pattern_para = /\n/g;
operation = diff[0], data = diff[1];
text = data.replace(pattern_amp, '&').replace(pattern_lt, '<').replace(pattern_gt, '>').replace(pattern_para, '<br>');
switch (operation) {
case DIFF_INSERT:
return '<ins>' + text + '</ins>';
case DIFF_DELETE:
return '<del>' + text + '</del>';
case DIFF_EQUAL:
return '<span>' + text + '</span>';
}
};
}).call(this);
js/jquery.pretty-text-diff.min.js 0000604 00000004237 15245531301 0013036 0 ustar 00 (function(){var $;$=jQuery;$.fn.extend({prettyTextDiff:function(options){var dmp,settings;settings={originalContainer:".original",changedContainer:".changed",diffContainer:".diff",originalHtmlContainer:".originalhtml",changedHtmlContainer:".changedhtml",diffHtmlContainer:".diffhtml",cleanup:true,debug:false};settings=$.extend(settings,options);$.fn.prettyTextDiff.debug("Options: ",settings,settings);dmp=new diff_match_patch;return this.each(function(){var changed,diff_as_html,diffs,original,changedhtml,
originalhtml,diffshtml;original=$(settings.originalContainer,this).text();$.fn.prettyTextDiff.debug("Original text found: ",original,settings);changed=$(settings.changedContainer,this).text();$.fn.prettyTextDiff.debug("Changed text found: ",changed,settings);originalhtml=$(settings.originalHtmlContainer,this).text();$.fn.prettyTextDiff.debug("Original text found: ",original,settings);changedhtml=$(settings.changedHtmlContainer,this).text();$.fn.prettyTextDiff.debug("Changed text found: ",changed,
settings);diffs=dmp.diff_main(original,changed);diffshtml=dmp.diff_main(originalhtml,changedhtml);if(settings.cleanup){dmp.diff_cleanupSemantic(diffs);dmp.diff_cleanupSemantic(diffshtml)}$.fn.prettyTextDiff.debug("Diffs: ",diffs,settings);diff_as_html=diffs.map(function(diff){return $.fn.prettyTextDiff.createHTML(diff)});diffhtml_as_html=diffshtml.map(function(diff){return $.fn.prettyTextDiff.createHTML(diff)});$(settings.diffContainer,this).html(diff_as_html.join(""));$(settings.diffHtmlContainer,
this).html(diffhtml_as_html.join(""));return this})}});$.fn.prettyTextDiff.debug=function(message,object,settings){if(settings.debug)return console.log(message,object)};$.fn.prettyTextDiff.createHTML=function(diff){var data,html,operation,pattern_amp,pattern_gt,pattern_lt,pattern_para,text;html=[];pattern_amp=/&/g;pattern_lt=/</g;pattern_gt=/>/g;pattern_para=/\n/g;operation=diff[0],data=diff[1];text=data.replace(pattern_amp,"&").replace(pattern_lt,"<").replace(pattern_gt,">").replace(pattern_para,
"<br>");switch(operation){case DIFF_INSERT:return"<ins>"+text+"</ins>";case DIFF_DELETE:return"<del>"+text+"</del>";case DIFF_EQUAL:return"<span>"+text+"</span>"}}}).call(this); contenthistory.php 0000604 00000001171 15245531302 0010345 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
// Disallow unauthenticated users
if (JFactory::getUser()->guest)
{
throw new JAccessExceptionNotallowed(JText::_('JERROR_ALERTNOAUTHOR'), 403);
}
$controller = JControllerLegacy::getInstance('Contenthistory', array('base_path' => JPATH_COMPONENT_ADMINISTRATOR));
$controller->execute(JFactory::getApplication()->input->get('task'));
$controller->redirect();
models/history.php 0000604 00000024204 15245557327 0010256 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelHistory extends JModelList
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see JControllerLegacy
* @since 3.2
*/
public function __construct($config = array())
{
if (empty($config['filter_fields']))
{
$config['filter_fields'] = array(
'version_id',
'h.version_id',
'version_note',
'h.version_note',
'save_date',
'h.save_date',
'editor_user_id',
'h.editor_user_id',
);
}
parent::__construct($config);
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable object.
*
* @return boolean True if allowed to edit the record. Defaults to the permission set in the component.
*
* @since 3.2
*/
protected function canEdit($record)
{
$result = false;
if (!empty($record->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias = JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype', 'JTable');
if ($contentTypeTable->getTypeId($typeAlias) == $record->ucm_type_id)
{
/**
* Make sure user has edit privileges for this content item. Note that we use edit permissions
* for the content item, not delete permissions for the content history row.
*/
$user = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias . '.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array) JFactory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_id, $typeEditables);
}
}
return $result;
}
/**
* Method to test whether a history record can be deleted. Note that we check whether we have edit permissions
* for the content item row.
*
* @param JTableContenthistory $record A JTable object.
*
* @return boolean True if allowed to delete the record. Defaults to the permission set in the component.
*
* @since 3.6
*/
protected function canDelete($record)
{
return $this->canEdit($record);
}
/**
* Method to delete one or more records from content history table.
*
* @param array $pks An array of record primary keys.
*
* @return boolean True if successful, false if an error occurs.
*
* @since 3.2
*/
public function delete(&$pks)
{
$pks = (array) $pks;
$table = $this->getTable();
// Iterate the items to delete each one.
foreach ($pks as $i => $pk)
{
if ($table->load($pk))
{
if ((int) $table->keep_forever === 1)
{
unset($pks[$i]);
continue;
}
if ($this->canEdit($table))
{
if (!$table->delete($pk))
{
$this->setError($table->getError());
return false;
}
}
else
{
// Prune items that you can't change.
unset($pks[$i]);
$error = $this->getError();
if ($error)
{
try
{
JLog::add($error, JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage($error, 'warning');
}
return false;
}
else
{
try
{
JLog::add(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), 'warning');
}
return false;
}
}
}
else
{
$this->setError($table->getError());
return false;
}
}
// Clear the component's cache
$this->cleanCache();
return true;
}
/**
* Method to get an array of data items.
*
* @return mixed An array of data items on success, false on failure.
*
* @since 3.4.5
*/
public function getItems()
{
$items = parent::getItems();
$user = JFactory::getUser();
if ($items === false)
{
return false;
}
// This should be an array with at least one element
if (!is_array($items) || !isset($items[0]))
{
return $items;
}
// Get the content type's record so we can check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
$ucmTypeId = $items[0]->ucm_type_id;
if (!$contentTypeTable->load($ucmTypeId))
{
// Assume a failure to load the content type means broken data, abort mission
return false;
}
// Access check
if ($user->authorise('core.edit', $contentTypeTable->type_alias . '.' . (int) $items[0]->ucm_item_id) || $this->canEdit($items[0]))
{
return $items;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return JTable A JTable object
*
* @since 3.2
*/
public function getTable($type = 'Contenthistory', $prefix = 'JTable', $config = array())
{
return JTable::getInstance($type, $prefix, $config);
}
/**
* Method to toggle on and off the keep forever value for one or more records from content history table.
*
* @param array $pks An array of record primary keys.
*
* @return boolean True if successful, false if an error occurs.
*
* @since 3.2
*/
public function keep(&$pks)
{
$pks = (array) $pks;
$table = $this->getTable();
// Iterate the items to delete each one.
foreach ($pks as $i => $pk)
{
if ($table->load($pk))
{
if ($this->canEdit($table))
{
$table->keep_forever = $table->keep_forever ? 0 : 1;
if (!$table->store())
{
$this->setError($table->getError());
return false;
}
}
else
{
// Prune items that you can't change.
unset($pks[$i]);
$error = $this->getError();
if ($error)
{
try
{
JLog::add($error, JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage($error, 'warning');
}
return false;
}
else
{
try
{
JLog::add(JText::_('COM_CONTENTHISTORY_ERROR_KEEP_NOT_PERMITTED'), JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage(JText::_('COM_CONTENTHISTORY_ERROR_KEEP_NOT_PERMITTED'), 'warning');
}
return false;
}
}
}
else
{
$this->setError($table->getError());
return false;
}
}
// Clear the component's cache
$this->cleanCache();
return true;
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 3.2
*/
protected function populateState($ordering = 'h.save_date', $direction = 'DESC')
{
$input = JFactory::getApplication()->input;
$itemId = $input->get('item_id', 0, 'integer');
$typeId = $input->get('type_id', 0, 'integer');
$typeAlias = $input->get('type_alias', '', 'string');
$this->setState('item_id', $itemId);
$this->setState('type_id', $typeId);
$this->setState('type_alias', $typeAlias);
$this->setState('sha1_hash', $this->getSha1Hash());
// Load the parameters.
$params = JComponentHelper::getParams('com_contenthistory');
$this->setState('params', $params);
// List state information.
parent::populateState($ordering, $direction);
}
/**
* Build an SQL query to load the list data.
*
* @return JDatabaseQuery
*
* @since 3.2
*/
protected function getListQuery()
{
// Create a new query object.
$db = $this->getDbo();
$query = $db->getQuery(true);
// Select the required fields from the table.
$query->select(
$this->getState(
'list.select',
'h.version_id, h.ucm_item_id, h.ucm_type_id, h.version_note, h.save_date, h.editor_user_id,' .
'h.character_count, h.sha1_hash, h.version_data, h.keep_forever'
)
)
->from($db->quoteName('#__ucm_history') . ' AS h')
->where($db->quoteName('h.ucm_item_id') . ' = ' . (int) $this->getState('item_id'))
->where($db->quoteName('h.ucm_type_id') . ' = ' . (int) $this->getState('type_id'))
// Join over the users for the editor
->select('uc.name AS editor')
->join('LEFT', '#__users AS uc ON uc.id = h.editor_user_id');
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering');
$orderDirn = $this->state->get('list.direction');
$query->order($db->quoteName($orderCol) . $orderDirn);
return $query;
}
/**
* Get the sha1 hash value for the current item being edited.
*
* @return string sha1 hash of row data
*
* @since 3.2
*/
protected function getSha1Hash()
{
$result = false;
$typeTable = JTable::getInstance('Contenttype', 'JTable');
$typeId = JFactory::getApplication()->input->getInteger('type_id', 0);
$typeTable->load($typeId);
$typeAliasArray = explode('.', $typeTable->type_alias);
JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/' . $typeAliasArray[0] . '/tables');
$contentTable = $typeTable->getContentTable();
$keyValue = JFactory::getApplication()->input->getInteger('item_id', 0);
if ($contentTable && $contentTable->load($keyValue))
{
$helper = new JHelper;
$dataObject = $helper->getDataObject($contentTable);
$result = $this->getTable('Contenthistory', 'JTable')->getSha1(json_encode($dataObject), $typeTable);
}
return $result;
}
}
models/compare.php 0000604 00000010635 15245557327 0010206 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JLoader::register('ContenthistoryHelper', JPATH_ADMINISTRATOR . '/components/com_contenthistory/helpers/contenthistory.php');
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelCompare extends JModelItem
{
/**
* Method to get a version history row.
*
* @return array|boolean On success, array of populated tables. False on failure.
*
* @since 3.2
*/
public function getItems()
{
$input = JFactory::getApplication()->input;
/** @var JTableContenthistory $table1 */
$table1 = JTable::getInstance('Contenthistory');
/** @var JTableContenthistory $table2 */
$table2 = JTable::getInstance('Contenthistory');
$id1 = $input->getInt('id1');
$id2 = $input->getInt('id2');
if (!$id1 || \is_array($id1) || !$id2 || \is_array($id2))
{
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_INVALID_ID'));
return false;
}
$result = array();
if ($table1->load($id1) && $table2->load($id2))
{
// Get the first history record's content type record so we can check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
$ucmTypeId = $table1->ucm_type_id;
if (!$contentTypeTable->load($ucmTypeId))
{
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_FAILED_LOADING_CONTENT_TYPE'));
// Assume a failure to load the content type means broken data, abort mission
return false;
}
$user = JFactory::getUser();
// Access check
if ($user->authorise('core.edit', $contentTypeTable->type_alias . '.' . (int) $table1->ucm_item_id) || $this->canEdit($table1))
{
$return = true;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
// All's well, process the records
if ($return == true)
{
foreach (array($table1, $table2) as $table)
{
$object = new stdClass;
$object->data = ContenthistoryHelper::prepareData($table);
$object->version_note = $table->version_note;
// Let's use custom calendars when present
$object->save_date = JHtml::_('date', $table->save_date, JText::_('DATE_FORMAT_LC6'));
$dateProperties = array (
'modified_time',
'created_time',
'modified',
'created',
'checked_out_time',
'publish_up',
'publish_down',
);
foreach ($dateProperties as $dateProperty)
{
if (property_exists($object->data, $dateProperty) && $object->data->$dateProperty->value != '0000-00-00 00:00:00')
{
$object->data->$dateProperty->value = JHtml::_('date', $object->data->$dateProperty->value, JText::_('DATE_FORMAT_LC6'));
}
}
$result[] = $object;
}
return $result;
}
}
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_VERSION_NOT_FOUND'));
return false;
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable object.
*
* @return boolean True if allowed to edit the record. Defaults to the permission set in the component.
*
* @since 3.6
*/
protected function canEdit($record)
{
$result = false;
if (!empty($record->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias = JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype', 'JTable');
if ($contentTypeTable->getTypeId($typeAlias) == $record->ucm_type_id)
{
/**
* Make sure user has edit privileges for this content item. Note that we use edit permissions
* for the content item, not delete permissions for the content history row.
*/
$user = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias . '.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array) JFactory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_id, $typeEditables);
}
}
return $result;
}
}
models/preview.php 0000604 00000007344 15245557327 0010244 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JLoader::register('ContenthistoryHelper', JPATH_ADMINISTRATOR . '/components/com_contenthistory/helpers/contenthistory.php');
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelPreview extends JModelItem
{
/**
* Method to get a version history row.
*
* @return stdClass|boolean On success, standard object with row data. False on failure.
*
* @since 3.2
*/
public function getItem()
{
/** @var JTableContenthistory $table */
$table = JTable::getInstance('Contenthistory');
$versionId = JFactory::getApplication()->input->getInt('version_id');
if (!$table->load($versionId))
{
return false;
}
// Get the content type's record so we can check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
if (!$contentTypeTable->load($table->ucm_type_id))
{
// Assume a failure to load the content type means broken data, abort mission
return false;
}
$user = JFactory::getUser();
// Access check
if ($user->authorise('core.edit', $contentTypeTable->type_alias . '.' . (int) $table->ucm_item_id) || $this->canEdit($table))
{
$return = true;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
// Good to go, finish processing the data
if ($return == true)
{
$result = new stdClass;
$result->version_note = $table->version_note;
$result->data = ContenthistoryHelper::prepareData($table);
// Let's use custom calendars when present
$result->save_date = JHtml::_('date', $table->save_date, JText::_('DATE_FORMAT_LC6'));
$dateProperties = array (
'modified_time',
'created_time',
'modified',
'created',
'checked_out_time',
'publish_up',
'publish_down',
);
foreach ($dateProperties as $dateProperty)
{
if (property_exists($result->data, $dateProperty) && $result->data->$dateProperty->value != '0000-00-00 00:00:00')
{
$result->data->$dateProperty->value = JHtml::_('date', $result->data->$dateProperty->value, JText::_('DATE_FORMAT_LC6'));
}
}
return $result;
}
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable object.
*
* @return boolean True if allowed to edit the record. Defaults to the permission set in the component.
*
* @since 3.6
*/
protected function canEdit($record)
{
$result = false;
if (!empty($record->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias = JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype', 'JTable');
if ($contentTypeTable->getTypeId($typeAlias) == $record->ucm_type_id)
{
/**
* Make sure user has edit privileges for this content item. Note that we use edit permissions
* for the content item, not delete permissions for the content history row.
*/
$user = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias . '.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array) JFactory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_id, $typeEditables);
}
}
return $result;
}
}
views/history/view.html.php 0000604 00000001733 15245557327 0012047 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 3.2
*/
class ContenthistoryViewHistory extends JViewLegacy
{
protected $items;
protected $pagination;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @since 3.2
*/
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
views/history/tmpl/modal.php 0000604 00000022240 15245557327 0012176 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or die(JText::_('JINVALID_TOKEN'));
JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
JHtml::_('bootstrap.tooltip', '.hasTooltip', array('placement' => 'bottom'));
JHtml::_('behavior.multiselect');
JHtml::_('jquery.framework');
$input = JFactory::getApplication()->input;
$field = $input->getCmd('field');
$function = 'jSelectContenthistory_' . $field;
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$message = JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_ONE', true);
$compareMessage = JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_TWO', true);
JText::script('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST');
$deleteMessage = "alert(Joomla.JText._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST'));";
$aliasArray = explode('.', $this->state->type_alias);
$option = (end($aliasArray) == 'category') ? 'com_categories&extension=' . implode('.', array_slice($aliasArray, 0, count($aliasArray) - 1)) : $aliasArray[0];
$filter = JFilterInput::getInstance();
$task = $filter->clean(end($aliasArray)) . '.loadhistory';
$loadUrl = JRoute::_('index.php?option=' . $filter->clean($option) . '&task=' . $task);
$deleteUrl = JRoute::_('index.php?option=com_contenthistory&task=history.delete');
$hash = $this->state->get('sha1_hash');
$formUrl = 'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id=' . $this->state->get('item_id') . '&type_id='
. $this->state->get('type_id') . '&type_alias=' . $this->state->get('type_alias') . '&' . JSession::getFormToken() . '=1';
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
$('#toolbar-load').click(function() {
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-load').attr('data-url') + '&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.parent.location = url;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-preview').click(function() {
var windowSizeArray = ['width=800, height=600, resizable=yes, scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-preview').attr('data-url') + '&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-compare').click(function() {
var windowSizeArray = ['width=1000, height=600, resizable=yes, scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 2) {
// Add version item ids to URL
var url = $('#toolbar-compare').attr('data-url') + '&id1=' + ids[0].value + '&id2=' + ids[1].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $compareMessage . "');
}
});
});
})(jQuery);
"
);
?>
<div class="container-popup">
<div class="btn-group pull-right">
<button id="toolbar-load" type="submit" class="btn hasTooltip" aria-label="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_LOAD_DESC'); ?>" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_LOAD_DESC'); ?>" data-url="<?php echo JRoute::_($loadUrl); ?>">
<span class="icon-upload" aria-hidden="true"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_LOAD'); ?></span></button>
<button id="toolbar-preview" type="button" class="btn hasTooltip" aria-label="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW_DESC'); ?>" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW_DESC'); ?>" data-url="<?php echo JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&' . JSession::getFormToken() . '=1'); ?>">
<span class="icon-search" aria-hidden="true"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW'); ?></span></button>
<button id="toolbar-compare" type="button" class="btn hasTooltip" aria-label="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_DESC'); ?>" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_DESC'); ?>" data-url="<?php echo JRoute::_('index.php?option=com_contenthistory&view=compare&layout=compare&tmpl=component&' . JSession::getFormToken() . '=1'); ?>">
<span class="icon-zoom-in" aria-hidden="true"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE'); ?></span></button>
<button onclick="if (document.adminForm.boxchecked.value==0){<?php echo $deleteMessage; ?>}else{ Joomla.submitbutton('history.keep')}" class="btn pointer hasTooltip" aria-label="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_DESC'); ?>" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_DESC'); ?>">
<span class="icon-lock" aria-hidden="true"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP'); ?></span></button>
<button onclick="if (document.adminForm.boxchecked.value==0){<?php echo $deleteMessage; ?>}else{ Joomla.submitbutton('history.delete')}" class="btn pointer hasTooltip" aria-label="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_DELETE_DESC'); ?>" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_DELETE_DESC'); ?>">
<span class="icon-delete" aria-hidden="true"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_DELETE'); ?></span></button>
</div>
<div class="clearfix"></div>
<hr class="hr-condensed" />
<form action="<?php echo JRoute::_($formUrl); ?>" method="post" name="adminForm" id="adminForm">
<table class="table table-striped table-condensed">
<thead>
<tr>
<th width="1%" class="center">
<input type="checkbox" name="checkall-toggle" value="" title="<?php echo JText::_('JGLOBAL_CHECK_ALL'); ?>" onclick="Joomla.checkAll(this)" />
</th>
<th width="15%">
<?php echo JText::_('JDATE'); ?>
</th>
<th width="15%" class="nowrap hidden-phone">
<?php echo JText::_('COM_CONTENTHISTORY_VERSION_NOTE'); ?>
</th>
<th width="10%" class="nowrap">
<?php echo JText::_('COM_CONTENTHISTORY_KEEP_VERSION'); ?>
</th>
<th width="15%" class="nowrap hidden-phone">
<?php echo JText::_('JAUTHOR'); ?>
</th>
<th width="10%" class="nowrap center">
<?php echo JText::_('COM_CONTENTHISTORY_CHARACTER_COUNT'); ?>
</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="15">
<?php echo $this->pagination->getListFooter(); ?>
</td>
</tr>
</tfoot>
<tbody>
<?php $i = 0; ?>
<?php foreach ($this->items as $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="center">
<?php echo JHtml::_('grid.id', $i, $item->version_id); ?>
</td>
<td>
<a class="save-date" onclick="window.open(this.href,'win2','width=800,height=600,resizable=yes,scrollbars=yes'); return false;"
href="<?php echo JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&' . JSession::getFormToken() . '=1&version_id=' . $item->version_id); ?>">
<?php echo JHtml::_('date', $item->save_date, JText::_('DATE_FORMAT_LC6')); ?>
</a>
<?php if ($item->sha1_hash == $hash) : ?>
<span class="icon-featured" aria-hidden="true"><span class="element-invisible"><?php echo JText::_('JFEATURED'); ?></span></span>
<?php endif; ?>
</td>
<td class="hidden-phone">
<?php echo htmlspecialchars($item->version_note); ?>
</td>
<td>
<?php if ($item->keep_forever) : ?>
<a class="btn btn-mini active" rel="tooltip" href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i; ?>','history.keep')"
data-original-title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_OFF'); ?>">
<?php echo JText::_('JYES'); ?> <span class="icon-lock" aria-hidden="true"></span>
</a>
<?php else : ?>
<a class="btn btn-mini active" rel="tooltip" href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i; ?>','history.keep')"
data-original-title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_ON'); ?>">
<?php echo JText::_('JNO'); ?>
</a>
<?php endif; ?>
</td>
<td class="hidden-phone">
<?php echo htmlspecialchars($item->editor); ?>
</td>
<td class="center">
<?php echo number_format((int) $item->character_count, 0, JText::_('DECIMALS_SEPARATOR'), JText::_('THOUSANDS_SEPARATOR')); ?>
</td>
</tr>
<?php $i++; ?>
<?php endforeach; ?>
</tbody>
</table>
<input type="hidden" name="task" value="" />
<input type="hidden" name="boxchecked" value="0" />
<?php echo JHtml::_('form.token'); ?>
</form>
</div>
views/preview/tmpl/preview.php 0000604 00000002657 15245557327 0012555 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or die(JText::_('JINVALID_TOKEN'));
?>
<h3>
<?php echo JText::sprintf('COM_CONTENTHISTORY_PREVIEW_SUBTITLE_DATE', $this->item->save_date); ?>
<?php if ($this->item->version_note) : ?>
<?php echo JText::sprintf('COM_CONTENTHISTORY_PREVIEW_SUBTITLE', $this->item->version_note); ?>
<?php endif; ?>
</h3>
<table class="table table-striped" >
<thead><tr>
<th width="25%"><?php echo JText::_('COM_CONTENTHISTORY_PREVIEW_FIELD'); ?></th>
<th><?php echo JText::_('COM_CONTENTHISTORY_PREVIEW_VALUE'); ?></th>
</tr></thead>
<tbody>
<?php foreach ($this->item->data as $name => $value) : ?>
<tr>
<?php if (is_object($value->value)) : ?>
<td><strong><?php echo $value->label; ?></strong></td>
<td></td><tr>
<?php foreach ($value->value as $subName => $subValue) : ?>
<?php if ($subValue) : ?>
<tr>
<td><i> <?php echo $subValue->label; ?></i></td>
<td><?php echo $subValue->value; ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<td><strong><?php echo $value->label; ?></strong></td>
<td><?php echo $value->value; ?></td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
views/preview/view.html.php 0000604 00000002140 15245557327 0012020 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 1.5
*/
class ContenthistoryViewPreview extends JViewLegacy
{
protected $items;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @since 3.2
*/
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->item = $this->get('Item');
if (false === $this->item)
{
JFactory::getLanguage()->load('com_content', JPATH_SITE, null, true);
JError::raiseError(404, JText::_('COM_CONTENT_ERROR_ARTICLE_NOT_FOUND'));
return false;
}
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
views/compare/view.html.php 0000604 00000001622 15245557327 0011771 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 3.2
*/
class ContenthistoryViewCompare extends JViewLegacy
{
protected $items;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @since 3.2
*/
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->items = $this->get('Items');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
views/compare/tmpl/compare.php 0000604 00000011402 15245557327 0012453 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or die(JText::_('JINVALID_TOKEN'));
$version2 = $this->items[0];
$version1 = $this->items[1];
$object1 = $version1->data;
$object2 = $version2->data;
JHtml::addIncludePath(JPATH_COMPONENT_ADMINISTRATOR . '/helpers/html');
JHtml::_('textdiff.textdiff', 'diff');
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
jQuery('.diffhtml, .diffhtml-header').hide();
});
})(jQuery);
"
);
?>
<fieldset>
<legend>
<?php echo JText::sprintf('COM_CONTENTHISTORY_COMPARE_TITLE'); ?>
<div class="btn-group pull-right">
<button id="toolbar-all-rows" class="btn hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_ALL_ROWS_DESC'); ?>"
onclick="jQuery('.items-equal').show(); jQuery('#toolbar-all-rows').hide(); jQuery('#toolbar-changed-rows').show()"
style="display:none" >
<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_ALL_ROWS'); ?></button>
<button id="toolbar-changed-rows" class="btn hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_CHANGED_ROWS_DESC'); ?>"
onclick="jQuery('.items-equal').hide(); jQuery('#toolbar-all-rows').show(); jQuery('#toolbar-changed-rows').hide()">
<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_CHANGED_ROWS'); ?></button>
<button class="diff-header btn hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_HTML_DESC'); ?>"
onclick="jQuery('.diffhtml, .diffhtml-header').show(); jQuery('.diff, .diff-header').hide()">
<span class="icon-wrench" aria-hidden="true"></span> <?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_HTML'); ?></button>
<button class="diffhtml-header btn hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_TEXT_DESC'); ?>"
onclick="jQuery('.diffhtml, .diffhtml-header').hide(); jQuery('.diff, .diff-header').show()">
<span class="icon-pencil" aria-hidden="true"></span> <?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_TEXT'); ?></button>
</div>
</legend>
<table id="diff" class="table table-striped table-condensed">
<thead><tr>
<th width="25%"><?php echo JText::_('COM_CONTENTHISTORY_PREVIEW_FIELD'); ?></th>
<th style="display:none" />
<th style="display:none" />
<th><?php echo JText::sprintf('COM_CONTENTHISTORY_COMPARE_VALUE1', $version1->save_date, $version1->version_note); ?></th>
<th><?php echo JText::sprintf('COM_CONTENTHISTORY_COMPARE_VALUE2', $version2->save_date, $version2->version_note); ?></th>
<th class="diff-header"><?php echo JText::_('COM_CONTENTHISTORY_COMPARE_DIFF'); ?></th>
<th class="diffhtml-header"><?php echo JText::_('COM_CONTENTHISTORY_COMPARE_DIFF'); ?></th>
</tr></thead>
<tbody>
<?php foreach ($object1 as $name => $value) : ?>
<?php $rowClass = ($value->value == $object2->$name->value) ? 'items-equal' : 'items-not-equal'; ?>
<tr class="<?php echo $rowClass; ?>">
<?php if (is_object($value->value)) : ?>
<td><strong><?php echo $value->label; ?></strong></td>
<td /><td /><td />
<?php foreach ($value->value as $subName => $subValue) : ?>
<?php $newSubValue = isset($object2->$name->value->$subName->value) ? $object2->$name->value->$subName->value : ''; ?>
<?php if ($subValue->value || $newSubValue) : ?>
<?php $rowClass = ($subValue->value == $newSubValue) ? 'items-equal' : 'items-not-equal'; ?>
<tr class="<?php echo $rowClass; ?>">
<td><i> <?php echo $subValue->label; ?></i></td>
<td class="originalhtml" style="display:none" ><?php echo htmlspecialchars($subValue->value, ENT_COMPAT, 'UTF-8'); ?></td>
<td class="changedhtml" style="display:none" ><?php echo htmlspecialchars($newSubValue, ENT_COMPAT, 'UTF-8'); ?></td>
<td class="original"><?php echo $subValue->value; ?></td>
<td class="changed"><?php echo $newSubValue; ?></td>
<td class="diff" />
<td class="diffhtml" />
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<td><strong><?php echo $value->label; ?></strong></td>
<td class="originalhtml" style="display:none" ><?php echo htmlspecialchars($value->value); ?></td>
<?php $object2->$name->value = is_object($object2->$name->value) ? json_encode($object2->$name->value) : $object2->$name->value; ?>
<td class="changedhtml" style="display:none" ><?php echo htmlspecialchars($object2->$name->value, ENT_COMPAT, 'UTF-8'); ?></td>
<td class="original"><?php echo $value->value; ?></td>
<td class="changed"><?php echo $object2->$name->value; ?></td>
<td class="diff" />
<td class="diffhtml" />
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</fieldset>
controllers/history.php 0000604 00000005723 15245557327 0011346 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Contenthistory list controller class.
*
* @since 3.2
*/
class ContenthistoryControllerHistory extends JControllerAdmin
{
/**
* Deletes and returns correctly.
*
* @return void
*
* @since 3.2
*/
public function delete()
{
$this->checkToken();
// Get items to remove from the request.
$cid = (array) $this->input->get('cid', array(), 'int');
// Remove zero values resulting from input filter
$cid = array_filter($cid);
if (empty($cid))
{
JError::raiseWarning(500, JText::_('COM_CONTENTHISTORY_NO_ITEM_SELECTED'));
}
else
{
// Get the model.
$model = $this->getModel();
// Remove the items.
if ($model->delete($cid))
{
$this->setMessage(JText::plural('COM_CONTENTHISTORY_N_ITEMS_DELETED', count($cid)));
}
else
{
$this->setMessage($model->getError());
}
}
$this->setRedirect(
JRoute::_(
'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id='
. $this->input->getInt('item_id') . '&type_id=' . $this->input->getInt('type_id')
. '&type_alias=' . $this->input->getCmd('type_alias') . '&' . JSession::getFormToken() . '=1', false
)
);
}
/**
* Proxy for getModel.
*
* @param string $name The name of the model
* @param string $prefix The prefix for the model
* @param array $config An additional array of parameters
*
* @return JModelLegacy The model
*
* @since 3.2
*/
public function getModel($name = 'History', $prefix = 'ContenthistoryModel', $config = array('ignore_request' => true))
{
return parent::getModel($name, $prefix, $config);
}
/**
* Toggles the keep forever value for one or more history rows. If it was Yes, changes to No. If No, changes to Yes.
*
* @return void
*
* @since 3.2
*/
public function keep()
{
$this->checkToken();
// Get items to remove from the request.
$cid = (array) $this->input->get('cid', array(), 'int');
// Remove zero values resulting from input filter
$cid = array_filter($cid);
if (empty($cid))
{
JError::raiseWarning(500, JText::_('COM_CONTENTHISTORY_NO_ITEM_SELECTED'));
}
else
{
// Get the model.
$model = $this->getModel();
// Remove the items.
if ($model->keep($cid))
{
$this->setMessage(JText::plural('COM_CONTENTHISTORY_N_ITEMS_KEEP_TOGGLE', count($cid)));
}
else
{
$this->setMessage($model->getError());
}
}
$this->setRedirect(
JRoute::_(
'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id='
. $this->input->getInt('item_id') . '&type_id=' . $this->input->getInt('type_id')
. '&type_alias=' . $this->input->getCmd('type_alias') . '&' . JSession::getFormToken() . '=1', false
)
);
}
}
controllers/preview.php 0000604 00000001531 15245557327 0011317 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Contenthistory list controller class.
*
* @since 3.2
*/
class ContenthistoryControllerPreview extends JControllerLegacy
{
/**
* Proxy for getModel.
*
* @param string $name The name of the model
* @param string $prefix The prefix for the model
* @param array $config An additional array of parameters
*
* @return JModelLegacy The model
*
* @since 3.2
*/
public function getModel($name = 'Preview', $prefix = 'ContenthistoryModel', $config = array('ignore_request' => true))
{
return parent::getModel($name, $prefix, $config);
}
}
helpers/contenthistory.php 0000604 00000026020 15245557327 0012026 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Categories helper.
*
* @since 3.2
*/
class ContenthistoryHelper
{
/**
* Method to put all field names, including nested ones, in a single array for easy lookup.
*
* @param stdClass $object Standard class object that may contain one level of nested objects.
*
* @return array Associative array of all field names, including ones in a nested object.
*
* @since 3.2
*/
public static function createObjectArray($object)
{
$result = array();
if ($object === null)
{
return $result;
}
foreach ($object as $name => $value)
{
$result[$name] = $value;
if (is_object($value))
{
foreach ($value as $subName => $subValue)
{
$result[$subName] = $subValue;
}
}
}
return $result;
}
/**
* Method to decode JSON-encoded fields in a standard object. Used to unpack JSON strings in the content history data column.
*
* @param stdClass $jsonString Standard class object that may contain one or more JSON-encoded fields.
*
* @return stdClass Object with any JSON-encoded fields unpacked.
*
* @since 3.2
*/
public static function decodeFields($jsonString)
{
$object = json_decode($jsonString);
if (is_object($object))
{
foreach ($object as $name => $value)
{
if ($subObject = json_decode($value))
{
$object->$name = $subObject;
}
}
}
return $object;
}
/**
* Method to get field labels for the fields in the JSON-encoded object.
* First we see if we can find translatable labels for the fields in the object.
* We translate any we can find and return an array in the format object->name => label.
*
* @param stdClass $object Standard class object in the format name->value.
* @param JTableContenttype $typesTable Table object with content history options.
*
* @return stdClass Contains two associative arrays.
* $formValues->labels in the format name => label (for example, 'id' => 'Article ID').
* $formValues->values in the format name => value (for example, 'state' => 'Published'.
* This translates the text from the selected option in the form.
*
* @since 3.2
*/
public static function getFormValues($object, JTableContenttype $typesTable)
{
$labels = array();
$values = array();
$expandedObjectArray = static::createObjectArray($object);
static::loadLanguageFiles($typesTable->type_alias);
if ($formFile = static::getFormFile($typesTable))
{
if ($xml = simplexml_load_file($formFile))
{
// Now we need to get all of the labels from the form
$fieldArray = $xml->xpath('//field');
$fieldArray = array_merge($fieldArray, $xml->xpath('//fields'));
foreach ($fieldArray as $field)
{
if ($label = (string) $field->attributes()->label)
{
$labels[(string) $field->attributes()->name] = JText::_($label);
}
}
// Get values for any list type fields
$listFieldArray = $xml->xpath('//field[@type="list" or @type="radio"]');
foreach ($listFieldArray as $field)
{
$name = (string) $field->attributes()->name;
if (isset($expandedObjectArray[$name]))
{
$optionFieldArray = $field->xpath('option[@value="' . $expandedObjectArray[$name] . '"]');
$valueText = null;
if (is_array($optionFieldArray) && count($optionFieldArray))
{
$valueText = trim((string) $optionFieldArray[0]);
}
$values[(string) $field->attributes()->name] = JText::_($valueText);
}
}
}
}
$result = new stdClass;
$result->labels = $labels;
$result->values = $values;
return $result;
}
/**
* Method to get the XML form file for this component. Used to get translated field names for history preview.
*
* @param JTableContenttype $typesTable Table object with content history options.
*
* @return mixed JModel object if successful, false if no model found.
*
* @since 3.2
*/
public static function getFormFile(JTableContenttype $typesTable)
{
$result = false;
jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
// First, see if we have a file name in the $typesTable
$options = json_decode($typesTable->content_history_options);
if (is_object($options) && isset($options->formFile) && JFile::exists(JPATH_ROOT . '/' . $options->formFile))
{
$result = JPATH_ROOT . '/' . $options->formFile;
}
else
{
$aliasArray = explode('.', $typesTable->type_alias);
if (count($aliasArray) == 2)
{
$component = ($aliasArray[1] == 'category') ? 'com_categories' : $aliasArray[0];
$path = JFolder::makeSafe(JPATH_ADMINISTRATOR . '/components/' . $component . '/models/forms/');
$file = JFile::makeSafe($aliasArray[1] . '.xml');
$result = JFile::exists($path . $file) ? $path . $file : false;
}
}
return $result;
}
/**
* Method to query the database using values from lookup objects.
*
* @param stdClass $lookup The std object with the values needed to do the query.
* @param mixed $value The value used to find the matching title or name. Typically the id.
*
* @return mixed Value from database (for example, name or title) on success, false on failure.
*
* @since 3.2
*/
public static function getLookupValue($lookup, $value)
{
$result = false;
if (isset($lookup->sourceColumn) && isset($lookup->targetTable) && isset($lookup->targetColumn)&& isset($lookup->displayColumn))
{
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName($lookup->displayColumn))
->from($db->quoteName($lookup->targetTable))
->where($db->quoteName($lookup->targetColumn) . ' = ' . $db->quote($value));
$db->setQuery($query);
try
{
$result = $db->loadResult();
}
catch (Exception $e)
{
// Ignore any errors and just return false
return false;
}
}
return $result;
}
/**
* Method to remove fields from the object based on values entered in the #__content_types table.
*
* @param stdClass $object Object to be passed to view layout file.
* @param JTableContenttype $typeTable Table object with content history options.
*
* @return stdClass object with hidden fields removed.
*
* @since 3.2
*/
public static function hideFields($object, JTableContenttype $typeTable)
{
if ($options = json_decode($typeTable->content_history_options))
{
if (isset($options->hideFields) && is_array($options->hideFields))
{
foreach ($options->hideFields as $field)
{
unset($object->$field);
}
}
}
return $object;
}
/**
* Method to load the language files for the component whose history is being viewed.
*
* @param string $typeAlias The type alias, for example 'com_content.article'.
*
* @return void
*
* @since 3.2
*/
public static function loadLanguageFiles($typeAlias)
{
$aliasArray = explode('.', $typeAlias);
if (is_array($aliasArray) && count($aliasArray) == 2)
{
$component = ($aliasArray[1] == 'category') ? 'com_categories' : $aliasArray[0];
$lang = JFactory::getLanguage();
/**
* Loading language file from the administrator/language directory then
* loading language file from the administrator/components/extension/language directory
*/
$lang->load($component, JPATH_ADMINISTRATOR, null, false, true)
|| $lang->load($component, JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $component), null, false, true);
// Force loading of backend global language file
$lang->load('joomla', JPath::clean(JPATH_ADMINISTRATOR), null, false, true);
}
}
/**
* Method to create object to pass to the layout. Format is as follows:
* field is std object with name, value.
*
* Value can be a std object with name, value pairs.
*
* @param stdClass $object The std object from the JSON string. Can be nested 1 level deep.
* @param stdClass $formValues Standard class of label and value in an associative array.
*
* @return stdClass Object with translated labels where available
*
* @since 3.2
*/
public static function mergeLabels($object, $formValues)
{
$result = new stdClass;
if ($object === null)
{
return $result;
}
$labelsArray = $formValues->labels;
$valuesArray = $formValues->values;
foreach ($object as $name => $value)
{
$result->$name = new stdClass;
$result->$name->name = $name;
$result->$name->value = isset($valuesArray[$name]) ? $valuesArray[$name] : $value;
$result->$name->label = isset($labelsArray[$name]) ? $labelsArray[$name] : $name;
if (is_object($value))
{
$subObject = new stdClass;
foreach ($value as $subName => $subValue)
{
$subObject->$subName = new stdClass;
$subObject->$subName->name = $subName;
$subObject->$subName->value = isset($valuesArray[$subName]) ? $valuesArray[$subName] : $subValue;
$subObject->$subName->label = isset($labelsArray[$subName]) ? $labelsArray[$subName] : $subName;
$result->$name->value = $subObject;
}
}
}
return $result;
}
/**
* Method to prepare the object for the preview and compare views.
*
* @param JTableContenthistory $table Table object loaded with data.
*
* @return stdClass Object ready for the views.
*
* @since 3.2
*/
public static function prepareData(JTableContenthistory $table)
{
$object = static::decodeFields($table->version_data);
$typesTable = JTable::getInstance('Contenttype');
$typesTable->load(array('type_id' => $table->ucm_type_id));
$formValues = static::getFormValues($object, $typesTable);
$object = static::mergeLabels($object, $formValues);
$object = static::hideFields($object, $typesTable);
$object = static::processLookupFields($object, $typesTable);
return $object;
}
/**
* Method to process any lookup values found in the content_history_options column for this table.
* This allows category title and user name to be displayed instead of the id column.
*
* @param stdClass $object The std object from the JSON string. Can be nested 1 level deep.
* @param JTableContenttype $typesTable Table object loaded with data.
*
* @return stdClass Object with lookup values inserted.
*
* @since 3.2
*/
public static function processLookupFields($object, JTableContenttype $typesTable)
{
if ($options = json_decode($typesTable->content_history_options))
{
if (isset($options->displayLookup) && is_array($options->displayLookup))
{
foreach ($options->displayLookup as $lookup)
{
$sourceColumn = isset($lookup->sourceColumn) ? $lookup->sourceColumn : false;
$sourceValue = isset($object->$sourceColumn->value) ? $object->$sourceColumn->value : false;
if ($sourceColumn && $sourceValue && ($lookupValue = static::getLookupValue($lookup, $sourceValue)))
{
$object->$sourceColumn->value = $lookupValue;
}
}
}
}
return $object;
}
}
helpers/html/textdiff.php 0000604 00000003216 15245557327 0011515 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML utility class for creating text diffs using jQuery, diff_patch_match.js and jquery.pretty-text-diff.js JavaScript libraries.
*
* @since 3.2
*
* @deprecated 4.0 No replacement
*/
abstract class JHtmlTextdiff
{
/**
* @var array Array containing information for loaded files
* @since 3.2
*/
protected static $loaded = array();
/**
* Method to load Javascript text diff
*
* @param string $containerId DOM id of the element where the diff will be rendered
*
* @return void
*
* @since 3.2
*/
public static function textdiff($containerId)
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Depends on jQuery UI
JHtml::_('bootstrap.framework');
JHtml::_('script', 'com_contenthistory/diff_match_patch.js', array('version' => 'auto', 'relative' => true));
JHtml::_('script', 'com_contenthistory/jquery.pretty-text-diff.min.js', array('version' => 'auto', 'relative' => true));
JHtml::_('stylesheet', 'com_contenthistory/jquery.pretty-text-diff.css', array('version' => 'auto', 'relative' => true));
// Attach diff to document
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
$('#" . $containerId . " tr').prettyTextDiff();
});
})(jQuery);
"
);
// Set static array
static::$loaded[__METHOD__] = true;
return;
}
}
contenthistory.xml 0000604 00000002155 15245557327 0010400 0 ustar 00 <?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.2" method="upgrade">
<name>com_contenthistory</name>
<author>Joomla! Project</author>
<creationDate>May 2013</creationDate>
<copyright>(C) 2013 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.2.0</version>
<description>COM_CONTENTHISTORY_XML_DESCRIPTION</description>
<files folder="site">
<filename>contenthistory.php</filename>
<filename>index.html</filename>
</files>
<administration>
<files folder="admin">
<filename>controller.php</filename>
<filename>contenthistory.php</filename>
<folder>controllers</folder>
<folder>helpers</folder>
<folder>media</folder>
<folder>models</folder>
<folder>views</folder>
</files>
<languages folder="admin">
<language tag="en-GB">language/en-GB.com_contenthistory.ini</language>
<language tag="en-GB">language/en-GB.com_contenthistory.sys.ini</language>
</languages>
</administration>
</extension>
controller.php 0000604 00000000604 15245557327 0007453 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Contenthistory Controller
*
* @since 3.2
*/
class ContenthistoryController extends JControllerLegacy
{
}
history/modal.php 0000604 00000021022 15245721523 0010051 0 ustar 00 <?php
/**
* @package Joomla.Administrator
* @subpackage Template.hathor
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or die(JText::_('JINVALID_TOKEN'));
JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
JHtml::_('bootstrap.tooltip', '.hasTooltip', array('placement' => 'bottom'));
JHtml::_('behavior.multiselect');
JHtml::_('jquery.framework');
$input = JFactory::getApplication()->input;
$field = $input->getCmd('field');
$function = 'jSelectContenthistory_' . $field;
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$message = JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_ONE', true);
$compareMessage = JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_TWO', true);
JText::script('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST');
$deleteMessage = "alert(Joomla.JText._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST'));";
$aliasArray = explode('.', $this->state->type_alias);
$option = (end($aliasArray) == 'category') ? 'com_categories&extension=' . implode('.', array_slice($aliasArray, 0, count($aliasArray) - 1)) : $aliasArray[0];
$filter = JFilterInput::getInstance();
$task = $filter->clean(end($aliasArray)) . '.loadhistory';
$loadUrl = JRoute::_('index.php?option=' . $filter->clean($option) . '&task=' . $task);
$deleteUrl = JRoute::_('index.php?option=com_contenthistory&task=history.delete');
$hash = $this->state->get('sha1_hash');
$formUrl = 'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id=' . $this->state->get('item_id') . '&type_id='
. $this->state->get('type_id') . '&type_alias=' . $this->state->get('type_alias') . '&' . JSession::getFormToken() . '=1';
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
$('#toolbar-load').click(function() {
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-load').attr('data-url') + '&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.parent.location = url;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-preview').click(function() {
var windowSizeArray = ['width=800, height=600, resizable=yes, scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-preview').attr('data-url') + '&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-compare').click(function() {
var windowSizeArray = ['width=1000, height=600, resizable=yes, scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 2) {
// Add version item ids to URL
var url = $('#toolbar-compare').attr('data-url') + '&id1=' + ids[0].value + '&id2=' + ids[1].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $compareMessage . "');
}
});
});
})(jQuery);
"
);
?>
<div class="modal-header">
<h3><?php echo JText::_('COM_CONTENTHISTORY_MODAL_TITLE'); ?></h3>
</div>
<div class="modal-body">
<div class="modal-buttons">
<button id="toolbar-load" type="submit" class="btn pointer hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_LOAD_DESC'); ?>" data-url="<?php echo JRoute::_($loadUrl);?>">
<span class="icon-upload"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_LOAD'); ?></span></button>
<button id="toolbar-preview" type="button" class="btn pointer hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW_DESC'); ?>" data-url="<?php echo JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&' . JSession::getFormToken() . '=1');?>">
<span class="icon-search"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW'); ?></span></button>
<button id="toolbar-compare" type="button" class="btn pointer hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_DESC'); ?>" data-url="<?php echo JRoute::_('index.php?option=com_contenthistory&view=compare&layout=compare&tmpl=component&' . JSession::getFormToken() . '=1');?>">
<span class="icon-zoom-in"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE'); ?></span></button>
<button onclick="if (document.adminForm.boxchecked.value==0){<?php echo $deleteMessage; ?>}else{ Joomla.submitbutton('history.keep')}" class="btn pointer hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_DESC'); ?>">
<span class="icon-lock"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP'); ?></span></button>
<button onclick="if (document.adminForm.boxchecked.value==0){<?php echo $deleteMessage; ?>}else{ Joomla.submitbutton('history.delete')}" class="btn pointer hasTooltip" title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_DELETE_DESC'); ?>">
<span class="icon-delete"></span><span class="hidden-phone"><?php echo JText::_('COM_CONTENTHISTORY_BUTTON_DELETE'); ?></span></button>
</div>
<form action="<?php echo JRoute::_($formUrl);?>" method="post" name="adminForm" id="adminForm">
<table class="adminlist modal">
<thead>
<tr>
<th width="1%" class="title">
<input type="checkbox" name="checkall-toggle" value="" title="<?php echo JText::_('JGLOBAL_CHECK_ALL'); ?>" onclick="Joomla.checkAll(this)" />
</th>
<th width="15%" class="title">
<?php echo JText::_('JDATE'); ?>
</th>
<th width="15%" class="title">
<?php echo JText::_('COM_CONTENTHISTORY_VERSION_NOTE'); ?>
</th>
<th width="10%" class="title">
<?php echo JText::_('COM_CONTENTHISTORY_KEEP_VERSION'); ?>
</th>
<th width="15%" class="title">
<?php echo JText::_('JAUTHOR'); ?>
</th>
<th width="10%" class="title">
<?php echo JText::_('COM_CONTENTHISTORY_CHARACTER_COUNT'); ?>
</th>
</tr>
</thead>
<tbody>
<?php $i = 0; ?>
<?php foreach ($this->items as $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="center">
<?php echo JHtml::_('grid.id', $i, $item->version_id); ?>
</td>
<td class="nowrap">
<a class="save-date" onclick="window.open(this.href,'win2','width=800,height=600,resizable=yes,scrollbars=yes'); return false;"
href="<?php echo JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&' . JSession::getFormToken() . '=1&version_id=' . $item->version_id);?>">
<?php echo JHtml::_('date', $item->save_date, JText::_('DATE_FORMAT_LC6')); ?>
</a>
<?php if ($item->sha1_hash == $hash) :?>
<span class="icon-featured"></span>
<?php endif; ?>
</td>
<td class="center">
<?php echo htmlspecialchars($item->version_note); ?>
</td>
<td class="center">
<?php if ($item->keep_forever) : ?>
<a class="btn btn-mini active" rel="tooltip" href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i; ?>','history.keep')"
data-original-title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_OFF'); ?>">
<?php echo JText::_('JYES'); ?> <span class="icon-lock"></span>
</a>
<?php else : ?>
<a class="btn btn-mini active" rel="tooltip" href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i; ?>','history.keep')"
data-original-title="<?php echo JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_ON'); ?>">
<?php echo JText::_('JNO'); ?>
</a>
<?php endif; ?>
</td>
<td class="center">
<?php echo htmlspecialchars($item->editor); ?>
</td>
<td class="center">
<?php echo number_format((int) $item->character_count, 0, JText::_('DECIMALS_SEPARATOR'), JText::_('THOUSANDS_SEPARATOR')); ?>
</td>
</tr>
<?php $i++; ?>
<?php endforeach; ?>
</tbody>
</table>
<?php echo $this->pagination->getListFooter(); ?>
<input type="hidden" name="task" value="" />
<input type="hidden" name="boxchecked" value="0" />
<?php echo JHtml::_('form.token'); ?>
</form>
</div>