##// END OF EJS Templates
Reimplemented file-browser using partial-ajax...
marcink -
r2686:269c6e0b beta
parent child Browse files
Show More
@@ -0,0 +1,1 b''
1 window.JSON||(window.JSON={}),function(){function f(a){return a<10?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)d=rep[c],typeof d=="string"&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var JSON=window.JSON,cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),function(a,b){"use strict";var c=a.History=a.History||{};if(typeof c.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");c.Adapter={handlers:{},_uid:1,uid:function(a){return a._uid||(a._uid=c.Adapter._uid++)},bind:function(a,b,d){var e=c.Adapter.uid(a);c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[],c.Adapter.handlers[e][b].push(d),a["on"+b]=function(a,b){return function(d){c.Adapter.trigger(a,b,d)}}(a,b)},trigger:function(a,b,d){d=d||{};var e=c.Adapter.uid(a),f,g;c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[];for(f=0,g=c.Adapter.handlers[e][b].length;f<g;++f)c.Adapter.handlers[e][b][f].apply(this,[d])},extractEventData:function(a,c){var d=c&&c[a]||b;return d},onDomLoad:function(b){var c=a.setTimeout(function(){b()},2e3);a.onload=function(){clearTimeout(c),b()}}},typeof c.init!="undefined"&&c.init()}(window),function(a,b){"use strict";var c=a.document,d=a.setTimeout||d,e=a.clearTimeout||e,f=a.setInterval||f,g=a.History=a.History||{};if(typeof g.initHtml4!="undefined")throw new Error("History.js HTML4 Support has already been loaded...");g.initHtml4=function(){if(typeof g.initHtml4.initialized!="undefined")return!1;g.initHtml4.initialized=!0,g.enabled=!0,g.savedHashes=[],g.isLastHash=function(a){var b=g.getHashByIndex(),c;return c=a===b,c},g.saveHash=function(a){return g.isLastHash(a)?!1:(g.savedHashes.push(a),!0)},g.getHashByIndex=function(a){var b=null;return typeof a=="undefined"?b=g.savedHashes[g.savedHashes.length-1]:a<0?b=g.savedHashes[g.savedHashes.length+a]:b=g.savedHashes[a],b},g.discardedHashes={},g.discardedStates={},g.discardState=function(a,b,c){var d=g.getHashByState(a),e;return e={discardedState:a,backState:c,forwardState:b},g.discardedStates[d]=e,!0},g.discardHash=function(a,b,c){var d={discardedHash:a,backState:c,forwardState:b};return g.discardedHashes[a]=d,!0},g.discardedState=function(a){var b=g.getHashByState(a),c;return c=g.discardedStates[b]||!1,c},g.discardedHash=function(a){var b=g.discardedHashes[a]||!1;return b},g.recycleState=function(a){var b=g.getHashByState(a);return g.discardedState(a)&&delete g.discardedStates[b],!0},g.emulated.hashChange&&(g.hashChangeInit=function(){g.checkerFunction=null;var b="",d,e,h,i;return g.isInternetExplorer()?(d="historyjs-iframe",e=c.createElement("iframe"),e.setAttribute("id",d),e.style.display="none",c.body.appendChild(e),e.contentWindow.document.open(),e.contentWindow.document.close(),h="",i=!1,g.checkerFunction=function(){if(i)return!1;i=!0;var c=g.getHash()||"",d=g.unescapeHash(e.contentWindow.document.location.hash)||"";return c!==b?(b=c,d!==c&&(h=d=c,e.contentWindow.document.open(),e.contentWindow.document.close(),e.contentWindow.document.location.hash=g.escapeHash(c)),g.Adapter.trigger(a,"hashchange")):d!==h&&(h=d,g.setHash(d,!1)),i=!1,!0}):g.checkerFunction=function(){var c=g.getHash();return c!==b&&(b=c,g.Adapter.trigger(a,"hashchange")),!0},g.intervalList.push(f(g.checkerFunction,g.options.hashChangeInterval)),!0},g.Adapter.onDomLoad(g.hashChangeInit)),g.emulated.pushState&&(g.onHashChange=function(b){var d=b&&b.newURL||c.location.href,e=g.getHashByUrl(d),f=null,h=null,i=null,j;return g.isLastHash(e)?(g.busy(!1),!1):(g.doubleCheckComplete(),g.saveHash(e),e&&g.isTraditionalAnchor(e)?(g.Adapter.trigger(a,"anchorchange"),g.busy(!1),!1):(f=g.extractState(g.getFullUrl(e||c.location.href,!1),!0),g.isLastSavedState(f)?(g.busy(!1),!1):(h=g.getHashByState(f),j=g.discardedState(f),j?(g.getHashByIndex(-2)===g.getHashByState(j.forwardState)?g.back(!1):g.forward(!1),!1):(g.pushState(f.data,f.title,f.url,!1),!0))))},g.Adapter.bind(a,"hashchange",g.onHashChange),g.pushState=function(b,d,e,f){if(g.getHashByUrl(e))throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(f!==!1&&g.busy())return g.pushQueue({scope:g,callback:g.pushState,args:arguments,queue:f}),!1;g.busy(!0);var h=g.createStateObject(b,d,e),i=g.getHashByState(h),j=g.getState(!1),k=g.getHashByState(j),l=g.getHash();return g.storeState(h),g.expectedStateId=h.id,g.recycleState(h),g.setTitle(h),i===k?(g.busy(!1),!1):i!==l&&i!==g.getShortUrl(c.location.href)?(g.setHash(i,!1),!1):(g.saveState(h),g.Adapter.trigger(a,"statechange"),g.busy(!1),!0)},g.replaceState=function(a,b,c,d){if(g.getHashByUrl(c))throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(d!==!1&&g.busy())return g.pushQueue({scope:g,callback:g.replaceState,args:arguments,queue:d}),!1;g.busy(!0);var e=g.createStateObject(a,b,c),f=g.getState(!1),h=g.getStateByIndex(-2);return g.discardState(f,e,h),g.pushState(e.data,e.title,e.url,!1),!0}),g.emulated.pushState&&g.getHash()&&!g.emulated.hashChange&&g.Adapter.onDomLoad(function(){g.Adapter.trigger(a,"hashchange")})},typeof g.init!="undefined"&&g.init()}(window),function(a,b){"use strict";var c=a.console||b,d=a.document,e=a.navigator,f=a.sessionStorage||!1,g=a.setTimeout,h=a.clearTimeout,i=a.setInterval,j=a.clearInterval,k=a.JSON,l=a.alert,m=a.History=a.History||{},n=a.history;k.stringify=k.stringify||k.encode,k.parse=k.parse||k.decode;if(typeof m.init!="undefined")throw new Error("History.js Core has already been loaded...");m.init=function(){return typeof m.Adapter=="undefined"?!1:(typeof m.initCore!="undefined"&&m.initCore(),typeof m.initHtml4!="undefined"&&m.initHtml4(),!0)},m.initCore=function(){if(typeof m.initCore.initialized!="undefined")return!1;m.initCore.initialized=!0,m.options=m.options||{},m.options.hashChangeInterval=m.options.hashChangeInterval||100,m.options.safariPollInterval=m.options.safariPollInterval||500,m.options.doubleCheckInterval=m.options.doubleCheckInterval||500,m.options.storeInterval=m.options.storeInterval||1e3,m.options.busyDelay=m.options.busyDelay||250,m.options.debug=m.options.debug||!1,m.options.initialTitle=m.options.initialTitle||d.title,m.intervalList=[],m.clearAllIntervals=function(){var a,b=m.intervalList;if(typeof b!="undefined"&&b!==null){for(a=0;a<b.length;a++)j(b[a]);m.intervalList=null}},m.debug=function(){(m.options.debug||!1)&&m.log.apply(m,arguments)},m.log=function(){var a=typeof c!="undefined"&&typeof c.log!="undefined"&&typeof c.log.apply!="undefined",b=d.getElementById("log"),e,f,g,h,i;a?(h=Array.prototype.slice.call(arguments),e=h.shift(),typeof c.debug!="undefined"?c.debug.apply(c,[e,h]):c.log.apply(c,[e,h])):e="\n"+arguments[0]+"\n";for(f=1,g=arguments.length;f<g;++f){i=arguments[f];if(typeof i=="object"&&typeof k!="undefined")try{i=k.stringify(i)}catch(j){}e+="\n"+i+"\n"}return b?(b.value+=e+"\n-----\n",b.scrollTop=b.scrollHeight-b.clientHeight):a||l(e),!0},m.getInternetExplorerMajorVersion=function(){var a=m.getInternetExplorerMajorVersion.cached=typeof m.getInternetExplorerMajorVersion.cached!="undefined"?m.getInternetExplorerMajorVersion.cached:function(){var a=3,b=d.createElement("div"),c=b.getElementsByTagName("i");while((b.innerHTML="<!--[if gt IE "+ ++a+"]><i></i><![endif]-->")&&c[0]);return a>4?a:!1}();return a},m.isInternetExplorer=function(){var a=m.isInternetExplorer.cached=typeof m.isInternetExplorer.cached!="undefined"?m.isInternetExplorer.cached:Boolean(m.getInternetExplorerMajorVersion());return a},m.emulated={pushState:!Boolean(a.history&&a.history.pushState&&a.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(e.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(e.userAgent)),hashChange:Boolean(!("onhashchange"in a||"onhashchange"in d)||m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8)},m.enabled=!m.emulated.pushState,m.bugs={setHash:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),safariPoll:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),ieDoubleCheck:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<7)},m.isEmptyObject=function(a){for(var b in a)return!1;return!0},m.cloneObject=function(a){var b,c;return a?(b=k.stringify(a),c=k.parse(b)):c={},c},m.getRootUrl=function(){var a=d.location.protocol+"//"+(d.location.hostname||d.location.host);if(d.location.port||!1)a+=":"+d.location.port;return a+="/",a},m.getBaseHref=function(){var a=d.getElementsByTagName("base"),b=null,c="";return a.length===1&&(b=a[0],c=b.href.replace(/[^\/]+$/,"")),c=c.replace(/\/+$/,""),c&&(c+="/"),c},m.getBaseUrl=function(){var a=m.getBaseHref()||m.getBasePageUrl()||m.getRootUrl();return a},m.getPageUrl=function(){var a=m.getState(!1,!1),b=(a||{}).url||d.location.href,c;return c=b.replace(/\/+$/,"").replace(/[^\/]+$/,function(a,b,c){return/\./.test(a)?a:a+"/"}),c},m.getBasePageUrl=function(){var a=d.location.href.replace(/[#\?].*/,"").replace(/[^\/]+$/,function(a,b,c){return/[^\/]$/.test(a)?"":a}).replace(/\/+$/,"")+"/";return a},m.getFullUrl=function(a,b){var c=a,d=a.substring(0,1);return b=typeof b=="undefined"?!0:b,/[a-z]+\:\/\//.test(a)||(d==="/"?c=m.getRootUrl()+a.replace(/^\/+/,""):d==="#"?c=m.getPageUrl().replace(/#.*/,"")+a:d==="?"?c=m.getPageUrl().replace(/[\?#].*/,"")+a:b?c=m.getBaseUrl()+a.replace(/^(\.\/)+/,""):c=m.getBasePageUrl()+a.replace(/^(\.\/)+/,"")),c.replace(/\#$/,"")},m.getShortUrl=function(a){var b=a,c=m.getBaseUrl(),d=m.getRootUrl();return m.emulated.pushState&&(b=b.replace(c,"")),b=b.replace(d,"/"),m.isTraditionalAnchor(b)&&(b="./"+b),b=b.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),b},m.store={},m.idToState=m.idToState||{},m.stateToId=m.stateToId||{},m.urlToId=m.urlToId||{},m.storedStates=m.storedStates||[],m.savedStates=m.savedStates||[],m.normalizeStore=function(){m.store.idToState=m.store.idToState||{},m.store.urlToId=m.store.urlToId||{},m.store.stateToId=m.store.stateToId||{}},m.getState=function(a,b){typeof a=="undefined"&&(a=!0),typeof b=="undefined"&&(b=!0);var c=m.getLastSavedState();return!c&&b&&(c=m.createStateObject()),a&&(c=m.cloneObject(c),c.url=c.cleanUrl||c.url),c},m.getIdByState=function(a){var b=m.extractId(a.url),c;if(!b){c=m.getStateString(a);if(typeof m.stateToId[c]!="undefined")b=m.stateToId[c];else if(typeof m.store.stateToId[c]!="undefined")b=m.store.stateToId[c];else{for(;;){b=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof m.idToState[b]=="undefined"&&typeof m.store.idToState[b]=="undefined")break}m.stateToId[c]=b,m.idToState[b]=a}}return b},m.normalizeState=function(a){var b,c;if(!a||typeof a!="object")a={};if(typeof a.normalized!="undefined")return a;if(!a.data||typeof a.data!="object")a.data={};b={},b.normalized=!0,b.title=a.title||"",b.url=m.getFullUrl(m.unescapeString(a.url||d.location.href)),b.hash=m.getShortUrl(b.url),b.data=m.cloneObject(a.data),b.id=m.getIdByState(b),b.cleanUrl=b.url.replace(/\??\&_suid.*/,""),b.url=b.cleanUrl,c=!m.isEmptyObject(b.data);if(b.title||c)b.hash=m.getShortUrl(b.url).replace(/\??\&_suid.*/,""),/\?/.test(b.hash)||(b.hash+="?"),b.hash+="&_suid="+b.id;return b.hashedUrl=m.getFullUrl(b.hash),(m.emulated.pushState||m.bugs.safariPoll)&&m.hasUrlDuplicate(b)&&(b.url=b.hashedUrl),b},m.createStateObject=function(a,b,c){var d={data:a,title:b,url:c};return d=m.normalizeState(d),d},m.getStateById=function(a){a=String(a);var c=m.idToState[a]||m.store.idToState[a]||b;return c},m.getStateString=function(a){var b,c,d;return b=m.normalizeState(a),c={data:b.data,title:a.title,url:a.url},d=k.stringify(c),d},m.getStateId=function(a){var b,c;return b=m.normalizeState(a),c=b.id,c},m.getHashByState=function(a){var b,c;return b=m.normalizeState(a),c=b.hash,c},m.extractId=function(a){var b,c,d;return c=/(.*)\&_suid=([0-9]+)$/.exec(a),d=c?c[1]||a:a,b=c?String(c[2]||""):"",b||!1},m.isTraditionalAnchor=function(a){var b=!/[\/\?\.]/.test(a);return b},m.extractState=function(a,b){var c=null,d,e;return b=b||!1,d=m.extractId(a),d&&(c=m.getStateById(d)),c||(e=m.getFullUrl(a),d=m.getIdByUrl(e)||!1,d&&(c=m.getStateById(d)),!c&&b&&!m.isTraditionalAnchor(a)&&(c=m.createStateObject(null,null,e))),c},m.getIdByUrl=function(a){var c=m.urlToId[a]||m.store.urlToId[a]||b;return c},m.getLastSavedState=function(){return m.savedStates[m.savedStates.length-1]||b},m.getLastStoredState=function(){return m.storedStates[m.storedStates.length-1]||b},m.hasUrlDuplicate=function(a){var b=!1,c;return c=m.extractState(a.url),b=c&&c.id!==a.id,b},m.storeState=function(a){return m.urlToId[a.url]=a.id,m.storedStates.push(m.cloneObject(a)),a},m.isLastSavedState=function(a){var b=!1,c,d,e;return m.savedStates.length&&(c=a.id,d=m.getLastSavedState(),e=d.id,b=c===e),b},m.saveState=function(a){return m.isLastSavedState(a)?!1:(m.savedStates.push(m.cloneObject(a)),!0)},m.getStateByIndex=function(a){var b=null;return typeof a=="undefined"?b=m.savedStates[m.savedStates.length-1]:a<0?b=m.savedStates[m.savedStates.length+a]:b=m.savedStates[a],b},m.getHash=function(){var a=m.unescapeHash(d.location.hash);return a},m.unescapeString=function(b){var c=b,d;for(;;){d=a.unescape(c);if(d===c)break;c=d}return c},m.unescapeHash=function(a){var b=m.normalizeHash(a);return b=m.unescapeString(b),b},m.normalizeHash=function(a){var b=a.replace(/[^#]*#/,"").replace(/#.*/,"");return b},m.setHash=function(a,b){var c,e,f;return b!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.setHash,args:arguments,queue:b}),!1):(c=m.escapeHash(a),m.busy(!0),e=m.extractState(a,!0),e&&!m.emulated.pushState?m.pushState(e.data,e.title,e.url,!1):d.location.hash!==c&&(m.bugs.setHash?(f=m.getPageUrl(),m.pushState(null,null,f+"#"+c,!1)):d.location.hash=c),m)},m.escapeHash=function(b){var c=m.normalizeHash(b);return c=a.escape(c),m.bugs.hashEscape||(c=c.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),c},m.getHashByUrl=function(a){var b=String(a).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return b=m.unescapeHash(b),b},m.setTitle=function(a){var b=a.title,c;b||(c=m.getStateByIndex(0),c&&c.url===a.url&&(b=c.title||m.options.initialTitle));try{d.getElementsByTagName("title")[0].innerHTML=b.replace("<","&lt;").replace(">","&gt;").replace(" & "," &amp; ")}catch(e){}return d.title=b,m},m.queues=[],m.busy=function(a){typeof a!="undefined"?m.busy.flag=a:typeof m.busy.flag=="undefined"&&(m.busy.flag=!1);if(!m.busy.flag){h(m.busy.timeout);var b=function(){var a,c,d;if(m.busy.flag)return;for(a=m.queues.length-1;a>=0;--a){c=m.queues[a];if(c.length===0)continue;d=c.shift(),m.fireQueueItem(d),m.busy.timeout=g(b,m.options.busyDelay)}};m.busy.timeout=g(b,m.options.busyDelay)}return m.busy.flag},m.busy.flag=!1,m.fireQueueItem=function(a){return a.callback.apply(a.scope||m,a.args||[])},m.pushQueue=function(a){return m.queues[a.queue||0]=m.queues[a.queue||0]||[],m.queues[a.queue||0].push(a),m},m.queue=function(a,b){return typeof a=="function"&&(a={callback:a}),typeof b!="undefined"&&(a.queue=b),m.busy()?m.pushQueue(a):m.fireQueueItem(a),m},m.clearQueue=function(){return m.busy.flag=!1,m.queues=[],m},m.stateChanged=!1,m.doubleChecker=!1,m.doubleCheckComplete=function(){return m.stateChanged=!0,m.doubleCheckClear(),m},m.doubleCheckClear=function(){return m.doubleChecker&&(h(m.doubleChecker),m.doubleChecker=!1),m},m.doubleCheck=function(a){return m.stateChanged=!1,m.doubleCheckClear(),m.bugs.ieDoubleCheck&&(m.doubleChecker=g(function(){return m.doubleCheckClear(),m.stateChanged||a(),!0},m.options.doubleCheckInterval)),m},m.safariStatePoll=function(){var b=m.extractState(d.location.href),c;if(!m.isLastSavedState(b))c=b;else return;return c||(c=m.createStateObject()),m.Adapter.trigger(a,"popstate"),m},m.back=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.back,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.back(!1)}),n.go(-1),!0)},m.forward=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.forward,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.forward(!1)}),n.go(1),!0)},m.go=function(a,b){var c;if(a>0)for(c=1;c<=a;++c)m.forward(b);else{if(!(a<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(c=-1;c>=a;--c)m.back(b)}return m};if(m.emulated.pushState){var o=function(){};m.pushState=m.pushState||o,m.replaceState=m.replaceState||o}else m.onPopState=function(b,c){var e=!1,f=!1,g,h;return m.doubleCheckComplete(),g=m.getHash(),g?(h=m.extractState(g||d.location.href,!0),h?m.replaceState(h.data,h.title,h.url,!1):(m.Adapter.trigger(a,"anchorchange"),m.busy(!1)),m.expectedStateId=!1,!1):(e=m.Adapter.extractEventData("state",b,c)||!1,e?f=m.getStateById(e):m.expectedStateId?f=m.getStateById(m.expectedStateId):f=m.extractState(d.location.href),f||(f=m.createStateObject(null,null,d.location.href)),m.expectedStateId=!1,m.isLastSavedState(f)?(m.busy(!1),!1):(m.storeState(f),m.saveState(f),m.setTitle(f),m.Adapter.trigger(a,"statechange"),m.busy(!1),!0))},m.Adapter.bind(a,"popstate",m.onPopState),m.pushState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.pushState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.pushState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0},m.replaceState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.replaceState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.replaceState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0};if(f){try{m.store=k.parse(f.getItem("History.store"))||{}}catch(p){m.store={}}m.normalizeStore()}else m.store={},m.normalizeStore();m.Adapter.bind(a,"beforeunload",m.clearAllIntervals),m.Adapter.bind(a,"unload",m.clearAllIntervals),m.saveState(m.storeState(m.extractState(d.location.href,!0))),f&&(m.onUnload=function(){var a,b;try{a=k.parse(f.getItem("History.store"))||{}}catch(c){a={}}a.idToState=a.idToState||{},a.urlToId=a.urlToId||{},a.stateToId=a.stateToId||{};for(b in m.idToState){if(!m.idToState.hasOwnProperty(b))continue;a.idToState[b]=m.idToState[b]}for(b in m.urlToId){if(!m.urlToId.hasOwnProperty(b))continue;a.urlToId[b]=m.urlToId[b]}for(b in m.stateToId){if(!m.stateToId.hasOwnProperty(b))continue;a.stateToId[b]=m.stateToId[b]}m.store=a,m.normalizeStore(),f.setItem("History.store",k.stringify(a))},m.intervalList.push(i(m.onUnload,m.options.storeInterval)),m.Adapter.bind(a,"beforeunload",m.onUnload),m.Adapter.bind(a,"unload",m.onUnload));if(!m.emulated.pushState){m.bugs.safariPoll&&m.intervalList.push(i(m.safariStatePoll,m.options.safariPollInterval));if(e.vendor==="Apple Computer, Inc."||(e.appCodeName||"")==="Mozilla")m.Adapter.bind(a,"hashchange",function(){m.Adapter.trigger(a,"popstate")}),m.getHash()&&m.Adapter.onDomLoad(function(){m.Adapter.trigger(a,"hashchange")})}},m.init()}(window) No newline at end of file
@@ -1,502 +1,505 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import tempfile
29 import tempfile
30
30
31 from pylons import request, response, tmpl_context as c, url
31 from pylons import request, response, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.decorators import jsonify
34 from pylons.decorators import jsonify
35
35
36 from rhodecode.lib import diffs
36 from rhodecode.lib import diffs
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38
38
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44 from rhodecode.lib.vcs.conf import settings
44 from rhodecode.lib.vcs.conf import settings
45 from rhodecode.lib.vcs.exceptions import RepositoryError, \
45 from rhodecode.lib.vcs.exceptions import RepositoryError, \
46 ChangesetDoesNotExistError, EmptyRepositoryError, \
46 ChangesetDoesNotExistError, EmptyRepositoryError, \
47 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
47 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 from rhodecode.lib.vcs.nodes import FileNode
48 from rhodecode.lib.vcs.nodes import FileNode
49
49
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.scm import ScmModel
51 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.db import Repository
52 from rhodecode.model.db import Repository
53
53
54 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
54 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
55 _context_url, get_line_ctx, get_ignore_ws
55 _context_url, get_line_ctx, get_ignore_ws
56
56
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class FilesController(BaseRepoController):
61 class FilesController(BaseRepoController):
62
62
63 def __before__(self):
63 def __before__(self):
64 super(FilesController, self).__before__()
64 super(FilesController, self).__before__()
65 c.cut_off_limit = self.cut_off_limit
65 c.cut_off_limit = self.cut_off_limit
66
66
67 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
67 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
68 """
68 """
69 Safe way to get changeset if error occur it redirects to tip with
69 Safe way to get changeset if error occur it redirects to tip with
70 proper message
70 proper message
71
71
72 :param rev: revision to fetch
72 :param rev: revision to fetch
73 :param repo_name: repo name to redirect after
73 :param repo_name: repo name to redirect after
74 """
74 """
75
75
76 try:
76 try:
77 return c.rhodecode_repo.get_changeset(rev)
77 return c.rhodecode_repo.get_changeset(rev)
78 except EmptyRepositoryError, e:
78 except EmptyRepositoryError, e:
79 if not redirect_after:
79 if not redirect_after:
80 return None
80 return None
81 url_ = url('files_add_home',
81 url_ = url('files_add_home',
82 repo_name=c.repo_name,
82 repo_name=c.repo_name,
83 revision=0, f_path='')
83 revision=0, f_path='')
84 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
84 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
85 h.flash(h.literal(_('There are no files yet %s') % add_new),
85 h.flash(h.literal(_('There are no files yet %s') % add_new),
86 category='warning')
86 category='warning')
87 redirect(h.url('summary_home', repo_name=repo_name))
87 redirect(h.url('summary_home', repo_name=repo_name))
88
88
89 except RepositoryError, e:
89 except RepositoryError, e:
90 h.flash(str(e), category='warning')
90 h.flash(str(e), category='warning')
91 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
91 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
92
92
93 def __get_filenode_or_redirect(self, repo_name, cs, path):
93 def __get_filenode_or_redirect(self, repo_name, cs, path):
94 """
94 """
95 Returns file_node, if error occurs or given path is directory,
95 Returns file_node, if error occurs or given path is directory,
96 it'll redirect to top level path
96 it'll redirect to top level path
97
97
98 :param repo_name: repo_name
98 :param repo_name: repo_name
99 :param cs: given changeset
99 :param cs: given changeset
100 :param path: path to lookup
100 :param path: path to lookup
101 """
101 """
102
102
103 try:
103 try:
104 file_node = cs.get_node(path)
104 file_node = cs.get_node(path)
105 if file_node.is_dir():
105 if file_node.is_dir():
106 raise RepositoryError('given path is a directory')
106 raise RepositoryError('given path is a directory')
107 except RepositoryError, e:
107 except RepositoryError, e:
108 h.flash(str(e), category='warning')
108 h.flash(str(e), category='warning')
109 redirect(h.url('files_home', repo_name=repo_name,
109 redirect(h.url('files_home', repo_name=repo_name,
110 revision=cs.raw_id))
110 revision=cs.raw_id))
111
111
112 return file_node
112 return file_node
113
113
114 @LoginRequired()
114 @LoginRequired()
115 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
115 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
116 'repository.admin')
116 'repository.admin')
117 def index(self, repo_name, revision, f_path, annotate=False):
117 def index(self, repo_name, revision, f_path, annotate=False):
118 # redirect to given revision from form if given
118 # redirect to given revision from form if given
119 post_revision = request.POST.get('at_rev', None)
119 post_revision = request.POST.get('at_rev', None)
120 if post_revision:
120 if post_revision:
121 cs = self.__get_cs_or_redirect(post_revision, repo_name)
121 cs = self.__get_cs_or_redirect(post_revision, repo_name)
122 redirect(url('files_home', repo_name=c.repo_name,
122 redirect(url('files_home', repo_name=c.repo_name,
123 revision=cs.raw_id, f_path=f_path))
123 revision=cs.raw_id, f_path=f_path))
124
124
125 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
125 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 c.branch = request.GET.get('branch', None)
126 c.branch = request.GET.get('branch', None)
127 c.f_path = f_path
127 c.f_path = f_path
128 c.annotate = annotate
128 c.annotate = annotate
129 cur_rev = c.changeset.revision
129 cur_rev = c.changeset.revision
130
130
131 # prev link
131 # prev link
132 try:
132 try:
133 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
133 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
134 c.url_prev = url('files_home', repo_name=c.repo_name,
134 c.url_prev = url('files_home', repo_name=c.repo_name,
135 revision=prev_rev.raw_id, f_path=f_path)
135 revision=prev_rev.raw_id, f_path=f_path)
136 if c.branch:
136 if c.branch:
137 c.url_prev += '?branch=%s' % c.branch
137 c.url_prev += '?branch=%s' % c.branch
138 except (ChangesetDoesNotExistError, VCSError):
138 except (ChangesetDoesNotExistError, VCSError):
139 c.url_prev = '#'
139 c.url_prev = '#'
140
140
141 # next link
141 # next link
142 try:
142 try:
143 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
143 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
144 c.url_next = url('files_home', repo_name=c.repo_name,
144 c.url_next = url('files_home', repo_name=c.repo_name,
145 revision=next_rev.raw_id, f_path=f_path)
145 revision=next_rev.raw_id, f_path=f_path)
146 if c.branch:
146 if c.branch:
147 c.url_next += '?branch=%s' % c.branch
147 c.url_next += '?branch=%s' % c.branch
148 except (ChangesetDoesNotExistError, VCSError):
148 except (ChangesetDoesNotExistError, VCSError):
149 c.url_next = '#'
149 c.url_next = '#'
150
150
151 # files or dirs
151 # files or dirs
152 try:
152 try:
153 c.file = c.changeset.get_node(f_path)
153 c.file = c.changeset.get_node(f_path)
154
154
155 if c.file.is_file():
155 if c.file.is_file():
156 _hist = c.changeset.get_file_history(f_path)
156 _hist = c.changeset.get_file_history(f_path)
157 c.file_history = self._get_node_history(c.changeset, f_path,
157 c.file_history = self._get_node_history(c.changeset, f_path,
158 _hist)
158 _hist)
159 c.authors = []
159 c.authors = []
160 for a in set([x.author for x in _hist]):
160 for a in set([x.author for x in _hist]):
161 c.authors.append((h.email(a), h.person(a)))
161 c.authors.append((h.email(a), h.person(a)))
162 else:
162 else:
163 c.authors = c.file_history = []
163 c.authors = c.file_history = []
164 except RepositoryError, e:
164 except RepositoryError, e:
165 h.flash(str(e), category='warning')
165 h.flash(str(e), category='warning')
166 redirect(h.url('files_home', repo_name=repo_name,
166 redirect(h.url('files_home', repo_name=repo_name,
167 revision='tip'))
167 revision='tip'))
168
168
169 if request.environ.get('HTTP_X_PARTIAL_XHR'):
170 return render('files/files_ypjax.html')
171
169 return render('files/files.html')
172 return render('files/files.html')
170
173
171 @LoginRequired()
174 @LoginRequired()
172 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
175 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
173 'repository.admin')
176 'repository.admin')
174 def rawfile(self, repo_name, revision, f_path):
177 def rawfile(self, repo_name, revision, f_path):
175 cs = self.__get_cs_or_redirect(revision, repo_name)
178 cs = self.__get_cs_or_redirect(revision, repo_name)
176 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
179 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
177
180
178 response.content_disposition = 'attachment; filename=%s' % \
181 response.content_disposition = 'attachment; filename=%s' % \
179 safe_str(f_path.split(Repository.url_sep())[-1])
182 safe_str(f_path.split(Repository.url_sep())[-1])
180
183
181 response.content_type = file_node.mimetype
184 response.content_type = file_node.mimetype
182 return file_node.content
185 return file_node.content
183
186
184 @LoginRequired()
187 @LoginRequired()
185 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
188 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
186 'repository.admin')
189 'repository.admin')
187 def raw(self, repo_name, revision, f_path):
190 def raw(self, repo_name, revision, f_path):
188 cs = self.__get_cs_or_redirect(revision, repo_name)
191 cs = self.__get_cs_or_redirect(revision, repo_name)
189 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
192 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
190
193
191 raw_mimetype_mapping = {
194 raw_mimetype_mapping = {
192 # map original mimetype to a mimetype used for "show as raw"
195 # map original mimetype to a mimetype used for "show as raw"
193 # you can also provide a content-disposition to override the
196 # you can also provide a content-disposition to override the
194 # default "attachment" disposition.
197 # default "attachment" disposition.
195 # orig_type: (new_type, new_dispo)
198 # orig_type: (new_type, new_dispo)
196
199
197 # show images inline:
200 # show images inline:
198 'image/x-icon': ('image/x-icon', 'inline'),
201 'image/x-icon': ('image/x-icon', 'inline'),
199 'image/png': ('image/png', 'inline'),
202 'image/png': ('image/png', 'inline'),
200 'image/gif': ('image/gif', 'inline'),
203 'image/gif': ('image/gif', 'inline'),
201 'image/jpeg': ('image/jpeg', 'inline'),
204 'image/jpeg': ('image/jpeg', 'inline'),
202 'image/svg+xml': ('image/svg+xml', 'inline'),
205 'image/svg+xml': ('image/svg+xml', 'inline'),
203 }
206 }
204
207
205 mimetype = file_node.mimetype
208 mimetype = file_node.mimetype
206 try:
209 try:
207 mimetype, dispo = raw_mimetype_mapping[mimetype]
210 mimetype, dispo = raw_mimetype_mapping[mimetype]
208 except KeyError:
211 except KeyError:
209 # we don't know anything special about this, handle it safely
212 # we don't know anything special about this, handle it safely
210 if file_node.is_binary:
213 if file_node.is_binary:
211 # do same as download raw for binary files
214 # do same as download raw for binary files
212 mimetype, dispo = 'application/octet-stream', 'attachment'
215 mimetype, dispo = 'application/octet-stream', 'attachment'
213 else:
216 else:
214 # do not just use the original mimetype, but force text/plain,
217 # do not just use the original mimetype, but force text/plain,
215 # otherwise it would serve text/html and that might be unsafe.
218 # otherwise it would serve text/html and that might be unsafe.
216 # Note: underlying vcs library fakes text/plain mimetype if the
219 # Note: underlying vcs library fakes text/plain mimetype if the
217 # mimetype can not be determined and it thinks it is not
220 # mimetype can not be determined and it thinks it is not
218 # binary.This might lead to erroneous text display in some
221 # binary.This might lead to erroneous text display in some
219 # cases, but helps in other cases, like with text files
222 # cases, but helps in other cases, like with text files
220 # without extension.
223 # without extension.
221 mimetype, dispo = 'text/plain', 'inline'
224 mimetype, dispo = 'text/plain', 'inline'
222
225
223 if dispo == 'attachment':
226 if dispo == 'attachment':
224 dispo = 'attachment; filename=%s' % \
227 dispo = 'attachment; filename=%s' % \
225 safe_str(f_path.split(os.sep)[-1])
228 safe_str(f_path.split(os.sep)[-1])
226
229
227 response.content_disposition = dispo
230 response.content_disposition = dispo
228 response.content_type = mimetype
231 response.content_type = mimetype
229 return file_node.content
232 return file_node.content
230
233
231 @LoginRequired()
234 @LoginRequired()
232 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
235 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
233 def edit(self, repo_name, revision, f_path):
236 def edit(self, repo_name, revision, f_path):
234 r_post = request.POST
237 r_post = request.POST
235
238
236 c.cs = self.__get_cs_or_redirect(revision, repo_name)
239 c.cs = self.__get_cs_or_redirect(revision, repo_name)
237 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
240 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
238
241
239 if c.file.is_binary:
242 if c.file.is_binary:
240 return redirect(url('files_home', repo_name=c.repo_name,
243 return redirect(url('files_home', repo_name=c.repo_name,
241 revision=c.cs.raw_id, f_path=f_path))
244 revision=c.cs.raw_id, f_path=f_path))
242
245
243 c.f_path = f_path
246 c.f_path = f_path
244
247
245 if r_post:
248 if r_post:
246
249
247 old_content = c.file.content
250 old_content = c.file.content
248 sl = old_content.splitlines(1)
251 sl = old_content.splitlines(1)
249 first_line = sl[0] if sl else ''
252 first_line = sl[0] if sl else ''
250 # modes: 0 - Unix, 1 - Mac, 2 - DOS
253 # modes: 0 - Unix, 1 - Mac, 2 - DOS
251 mode = detect_mode(first_line, 0)
254 mode = detect_mode(first_line, 0)
252 content = convert_line_endings(r_post.get('content'), mode)
255 content = convert_line_endings(r_post.get('content'), mode)
253
256
254 message = r_post.get('message') or (_('Edited %s via RhodeCode')
257 message = r_post.get('message') or (_('Edited %s via RhodeCode')
255 % (f_path))
258 % (f_path))
256 author = self.rhodecode_user.full_contact
259 author = self.rhodecode_user.full_contact
257
260
258 if content == old_content:
261 if content == old_content:
259 h.flash(_('No changes'),
262 h.flash(_('No changes'),
260 category='warning')
263 category='warning')
261 return redirect(url('changeset_home', repo_name=c.repo_name,
264 return redirect(url('changeset_home', repo_name=c.repo_name,
262 revision='tip'))
265 revision='tip'))
263
266
264 try:
267 try:
265 self.scm_model.commit_change(repo=c.rhodecode_repo,
268 self.scm_model.commit_change(repo=c.rhodecode_repo,
266 repo_name=repo_name, cs=c.cs,
269 repo_name=repo_name, cs=c.cs,
267 user=self.rhodecode_user,
270 user=self.rhodecode_user,
268 author=author, message=message,
271 author=author, message=message,
269 content=content, f_path=f_path)
272 content=content, f_path=f_path)
270 h.flash(_('Successfully committed to %s') % f_path,
273 h.flash(_('Successfully committed to %s') % f_path,
271 category='success')
274 category='success')
272
275
273 except Exception:
276 except Exception:
274 log.error(traceback.format_exc())
277 log.error(traceback.format_exc())
275 h.flash(_('Error occurred during commit'), category='error')
278 h.flash(_('Error occurred during commit'), category='error')
276 return redirect(url('changeset_home',
279 return redirect(url('changeset_home',
277 repo_name=c.repo_name, revision='tip'))
280 repo_name=c.repo_name, revision='tip'))
278
281
279 return render('files/files_edit.html')
282 return render('files/files_edit.html')
280
283
281 @LoginRequired()
284 @LoginRequired()
282 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
285 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
283 def add(self, repo_name, revision, f_path):
286 def add(self, repo_name, revision, f_path):
284 r_post = request.POST
287 r_post = request.POST
285 c.cs = self.__get_cs_or_redirect(revision, repo_name,
288 c.cs = self.__get_cs_or_redirect(revision, repo_name,
286 redirect_after=False)
289 redirect_after=False)
287 if c.cs is None:
290 if c.cs is None:
288 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
291 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
289
292
290 c.f_path = f_path
293 c.f_path = f_path
291
294
292 if r_post:
295 if r_post:
293 unix_mode = 0
296 unix_mode = 0
294 content = convert_line_endings(r_post.get('content'), unix_mode)
297 content = convert_line_endings(r_post.get('content'), unix_mode)
295
298
296 message = r_post.get('message') or (_('Added %s via RhodeCode')
299 message = r_post.get('message') or (_('Added %s via RhodeCode')
297 % (f_path))
300 % (f_path))
298 location = r_post.get('location')
301 location = r_post.get('location')
299 filename = r_post.get('filename')
302 filename = r_post.get('filename')
300 file_obj = r_post.get('upload_file', None)
303 file_obj = r_post.get('upload_file', None)
301
304
302 if file_obj is not None and hasattr(file_obj, 'filename'):
305 if file_obj is not None and hasattr(file_obj, 'filename'):
303 filename = file_obj.filename
306 filename = file_obj.filename
304 content = file_obj.file
307 content = file_obj.file
305
308
306 node_path = os.path.join(location, filename)
309 node_path = os.path.join(location, filename)
307 author = self.rhodecode_user.full_contact
310 author = self.rhodecode_user.full_contact
308
311
309 if not content:
312 if not content:
310 h.flash(_('No content'), category='warning')
313 h.flash(_('No content'), category='warning')
311 return redirect(url('changeset_home', repo_name=c.repo_name,
314 return redirect(url('changeset_home', repo_name=c.repo_name,
312 revision='tip'))
315 revision='tip'))
313 if not filename:
316 if not filename:
314 h.flash(_('No filename'), category='warning')
317 h.flash(_('No filename'), category='warning')
315 return redirect(url('changeset_home', repo_name=c.repo_name,
318 return redirect(url('changeset_home', repo_name=c.repo_name,
316 revision='tip'))
319 revision='tip'))
317
320
318 try:
321 try:
319 self.scm_model.create_node(repo=c.rhodecode_repo,
322 self.scm_model.create_node(repo=c.rhodecode_repo,
320 repo_name=repo_name, cs=c.cs,
323 repo_name=repo_name, cs=c.cs,
321 user=self.rhodecode_user,
324 user=self.rhodecode_user,
322 author=author, message=message,
325 author=author, message=message,
323 content=content, f_path=node_path)
326 content=content, f_path=node_path)
324 h.flash(_('Successfully committed to %s') % node_path,
327 h.flash(_('Successfully committed to %s') % node_path,
325 category='success')
328 category='success')
326 except NodeAlreadyExistsError, e:
329 except NodeAlreadyExistsError, e:
327 h.flash(_(e), category='error')
330 h.flash(_(e), category='error')
328 except Exception:
331 except Exception:
329 log.error(traceback.format_exc())
332 log.error(traceback.format_exc())
330 h.flash(_('Error occurred during commit'), category='error')
333 h.flash(_('Error occurred during commit'), category='error')
331 return redirect(url('changeset_home',
334 return redirect(url('changeset_home',
332 repo_name=c.repo_name, revision='tip'))
335 repo_name=c.repo_name, revision='tip'))
333
336
334 return render('files/files_add.html')
337 return render('files/files_add.html')
335
338
336 @LoginRequired()
339 @LoginRequired()
337 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
340 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
338 'repository.admin')
341 'repository.admin')
339 def archivefile(self, repo_name, fname):
342 def archivefile(self, repo_name, fname):
340
343
341 fileformat = None
344 fileformat = None
342 revision = None
345 revision = None
343 ext = None
346 ext = None
344 subrepos = request.GET.get('subrepos') == 'true'
347 subrepos = request.GET.get('subrepos') == 'true'
345
348
346 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
349 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
347 archive_spec = fname.split(ext_data[1])
350 archive_spec = fname.split(ext_data[1])
348 if len(archive_spec) == 2 and archive_spec[1] == '':
351 if len(archive_spec) == 2 and archive_spec[1] == '':
349 fileformat = a_type or ext_data[1]
352 fileformat = a_type or ext_data[1]
350 revision = archive_spec[0]
353 revision = archive_spec[0]
351 ext = ext_data[1]
354 ext = ext_data[1]
352
355
353 try:
356 try:
354 dbrepo = RepoModel().get_by_repo_name(repo_name)
357 dbrepo = RepoModel().get_by_repo_name(repo_name)
355 if dbrepo.enable_downloads is False:
358 if dbrepo.enable_downloads is False:
356 return _('downloads disabled')
359 return _('downloads disabled')
357
360
358 if c.rhodecode_repo.alias == 'hg':
361 if c.rhodecode_repo.alias == 'hg':
359 # patch and reset hooks section of UI config to not run any
362 # patch and reset hooks section of UI config to not run any
360 # hooks on fetching archives with subrepos
363 # hooks on fetching archives with subrepos
361 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
364 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
362 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
365 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
363
366
364 cs = c.rhodecode_repo.get_changeset(revision)
367 cs = c.rhodecode_repo.get_changeset(revision)
365 content_type = settings.ARCHIVE_SPECS[fileformat][0]
368 content_type = settings.ARCHIVE_SPECS[fileformat][0]
366 except ChangesetDoesNotExistError:
369 except ChangesetDoesNotExistError:
367 return _('Unknown revision %s') % revision
370 return _('Unknown revision %s') % revision
368 except EmptyRepositoryError:
371 except EmptyRepositoryError:
369 return _('Empty repository')
372 return _('Empty repository')
370 except (ImproperArchiveTypeError, KeyError):
373 except (ImproperArchiveTypeError, KeyError):
371 return _('Unknown archive type')
374 return _('Unknown archive type')
372
375
373 fd, archive = tempfile.mkstemp()
376 fd, archive = tempfile.mkstemp()
374 t = open(archive, 'wb')
377 t = open(archive, 'wb')
375 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
378 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
376 t.close()
379 t.close()
377
380
378 def get_chunked_archive(archive):
381 def get_chunked_archive(archive):
379 stream = open(archive, 'rb')
382 stream = open(archive, 'rb')
380 while True:
383 while True:
381 data = stream.read(16 * 1024)
384 data = stream.read(16 * 1024)
382 if not data:
385 if not data:
383 stream.close()
386 stream.close()
384 os.close(fd)
387 os.close(fd)
385 os.remove(archive)
388 os.remove(archive)
386 break
389 break
387 yield data
390 yield data
388
391
389 response.content_disposition = str('attachment; filename=%s-%s%s' \
392 response.content_disposition = str('attachment; filename=%s-%s%s' \
390 % (repo_name, revision[:12], ext))
393 % (repo_name, revision[:12], ext))
391 response.content_type = str(content_type)
394 response.content_type = str(content_type)
392 return get_chunked_archive(archive)
395 return get_chunked_archive(archive)
393
396
394 @LoginRequired()
397 @LoginRequired()
395 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
398 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
396 'repository.admin')
399 'repository.admin')
397 def diff(self, repo_name, f_path):
400 def diff(self, repo_name, f_path):
398 ignore_whitespace = request.GET.get('ignorews') == '1'
401 ignore_whitespace = request.GET.get('ignorews') == '1'
399 line_context = request.GET.get('context', 3)
402 line_context = request.GET.get('context', 3)
400 diff1 = request.GET.get('diff1', '')
403 diff1 = request.GET.get('diff1', '')
401 diff2 = request.GET.get('diff2', '')
404 diff2 = request.GET.get('diff2', '')
402 c.action = request.GET.get('diff')
405 c.action = request.GET.get('diff')
403 c.no_changes = diff1 == diff2
406 c.no_changes = diff1 == diff2
404 c.f_path = f_path
407 c.f_path = f_path
405 c.big_diff = False
408 c.big_diff = False
406 c.anchor_url = anchor_url
409 c.anchor_url = anchor_url
407 c.ignorews_url = _ignorews_url
410 c.ignorews_url = _ignorews_url
408 c.context_url = _context_url
411 c.context_url = _context_url
409 c.changes = OrderedDict()
412 c.changes = OrderedDict()
410 c.changes[diff2] = []
413 c.changes[diff2] = []
411 try:
414 try:
412 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
415 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
413 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
416 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
414 node1 = c.changeset_1.get_node(f_path)
417 node1 = c.changeset_1.get_node(f_path)
415 else:
418 else:
416 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
419 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
417 node1 = FileNode('.', '', changeset=c.changeset_1)
420 node1 = FileNode('.', '', changeset=c.changeset_1)
418
421
419 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
422 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
420 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
423 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
421 node2 = c.changeset_2.get_node(f_path)
424 node2 = c.changeset_2.get_node(f_path)
422 else:
425 else:
423 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
426 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
424 node2 = FileNode('.', '', changeset=c.changeset_2)
427 node2 = FileNode('.', '', changeset=c.changeset_2)
425 except RepositoryError:
428 except RepositoryError:
426 return redirect(url('files_home', repo_name=c.repo_name,
429 return redirect(url('files_home', repo_name=c.repo_name,
427 f_path=f_path))
430 f_path=f_path))
428
431
429 if c.action == 'download':
432 if c.action == 'download':
430 _diff = diffs.get_gitdiff(node1, node2,
433 _diff = diffs.get_gitdiff(node1, node2,
431 ignore_whitespace=ignore_whitespace,
434 ignore_whitespace=ignore_whitespace,
432 context=line_context)
435 context=line_context)
433 diff = diffs.DiffProcessor(_diff, format='gitdiff')
436 diff = diffs.DiffProcessor(_diff, format='gitdiff')
434
437
435 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
438 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
436 response.content_type = 'text/plain'
439 response.content_type = 'text/plain'
437 response.content_disposition = (
440 response.content_disposition = (
438 'attachment; filename=%s' % diff_name
441 'attachment; filename=%s' % diff_name
439 )
442 )
440 return diff.raw_diff()
443 return diff.raw_diff()
441
444
442 elif c.action == 'raw':
445 elif c.action == 'raw':
443 _diff = diffs.get_gitdiff(node1, node2,
446 _diff = diffs.get_gitdiff(node1, node2,
444 ignore_whitespace=ignore_whitespace,
447 ignore_whitespace=ignore_whitespace,
445 context=line_context)
448 context=line_context)
446 diff = diffs.DiffProcessor(_diff, format='gitdiff')
449 diff = diffs.DiffProcessor(_diff, format='gitdiff')
447 response.content_type = 'text/plain'
450 response.content_type = 'text/plain'
448 return diff.raw_diff()
451 return diff.raw_diff()
449
452
450 else:
453 else:
451 fid = h.FID(diff2, node2.path)
454 fid = h.FID(diff2, node2.path)
452 line_context_lcl = get_line_ctx(fid, request.GET)
455 line_context_lcl = get_line_ctx(fid, request.GET)
453 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
456 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
454
457
455 lim = request.GET.get('fulldiff') or self.cut_off_limit
458 lim = request.GET.get('fulldiff') or self.cut_off_limit
456 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
459 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
457 filenode_new=node2,
460 filenode_new=node2,
458 cut_off_limit=lim,
461 cut_off_limit=lim,
459 ignore_whitespace=ign_whitespace_lcl,
462 ignore_whitespace=ign_whitespace_lcl,
460 line_context=line_context_lcl,
463 line_context=line_context_lcl,
461 enable_comments=False)
464 enable_comments=False)
462
465
463 c.changes = [('', node2, diff, cs1, cs2, st,)]
466 c.changes = [('', node2, diff, cs1, cs2, st,)]
464
467
465 return render('files/file_diff.html')
468 return render('files/file_diff.html')
466
469
467 def _get_node_history(self, cs, f_path, changesets=None):
470 def _get_node_history(self, cs, f_path, changesets=None):
468 if changesets is None:
471 if changesets is None:
469 changesets = cs.get_file_history(f_path)
472 changesets = cs.get_file_history(f_path)
470 hist_l = []
473 hist_l = []
471
474
472 changesets_group = ([], _("Changesets"))
475 changesets_group = ([], _("Changesets"))
473 branches_group = ([], _("Branches"))
476 branches_group = ([], _("Branches"))
474 tags_group = ([], _("Tags"))
477 tags_group = ([], _("Tags"))
475 _hg = cs.repository.alias == 'hg'
478 _hg = cs.repository.alias == 'hg'
476 for chs in changesets:
479 for chs in changesets:
477 _branch = '(%s)' % chs.branch if _hg else ''
480 _branch = '(%s)' % chs.branch if _hg else ''
478 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
481 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
479 changesets_group[0].append((chs.raw_id, n_desc,))
482 changesets_group[0].append((chs.raw_id, n_desc,))
480
483
481 hist_l.append(changesets_group)
484 hist_l.append(changesets_group)
482
485
483 for name, chs in c.rhodecode_repo.branches.items():
486 for name, chs in c.rhodecode_repo.branches.items():
484 branches_group[0].append((chs, name),)
487 branches_group[0].append((chs, name),)
485 hist_l.append(branches_group)
488 hist_l.append(branches_group)
486
489
487 for name, chs in c.rhodecode_repo.tags.items():
490 for name, chs in c.rhodecode_repo.tags.items():
488 tags_group[0].append((chs, name),)
491 tags_group[0].append((chs, name),)
489 hist_l.append(tags_group)
492 hist_l.append(tags_group)
490
493
491 return hist_l
494 return hist_l
492
495
493 @LoginRequired()
496 @LoginRequired()
494 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
497 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
495 'repository.admin')
498 'repository.admin')
496 @jsonify
499 @jsonify
497 def nodelist(self, repo_name, revision, f_path):
500 def nodelist(self, repo_name, revision, f_path):
498 if request.environ.get('HTTP_X_PARTIAL_XHR'):
501 if request.environ.get('HTTP_X_PARTIAL_XHR'):
499 cs = self.__get_cs_or_redirect(revision, repo_name)
502 cs = self.__get_cs_or_redirect(revision, repo_name)
500 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
503 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
501 flat=False)
504 flat=False)
502 return {'nodes': _d + _f}
505 return {'nodes': _d + _f}
@@ -1,1043 +1,1045 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11 import logging
11 import logging
12 import re
12 import re
13
13
14 from datetime import datetime
14 from datetime import datetime
15 from pygments.formatters.html import HtmlFormatter
15 from pygments.formatters.html import HtmlFormatter
16 from pygments import highlight as code_highlight
16 from pygments import highlight as code_highlight
17 from pylons import url, request, config
17 from pylons import url, request, config
18 from pylons.i18n.translation import _, ungettext
18 from pylons.i18n.translation import _, ungettext
19 from hashlib import md5
19 from hashlib import md5
20
20
21 from webhelpers.html import literal, HTML, escape
21 from webhelpers.html import literal, HTML, escape
22 from webhelpers.html.tools import *
22 from webhelpers.html.tools import *
23 from webhelpers.html.builder import make_tag
23 from webhelpers.html.builder import make_tag
24 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
24 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
25 end_form, file, form, hidden, image, javascript_link, link_to, \
25 end_form, file, form, hidden, image, javascript_link, link_to, \
26 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
26 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
27 submit, text, password, textarea, title, ul, xml_declaration, radio
27 submit, text, password, textarea, title, ul, xml_declaration, radio
28 from webhelpers.html.tools import auto_link, button_to, highlight, \
28 from webhelpers.html.tools import auto_link, button_to, highlight, \
29 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
29 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
30 from webhelpers.number import format_byte_size, format_bit_size
30 from webhelpers.number import format_byte_size, format_bit_size
31 from webhelpers.pylonslib import Flash as _Flash
31 from webhelpers.pylonslib import Flash as _Flash
32 from webhelpers.pylonslib.secure_form import secure_form
32 from webhelpers.pylonslib.secure_form import secure_form
33 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
33 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
34 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
34 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
35 replace_whitespace, urlify, truncate, wrap_paragraphs
35 replace_whitespace, urlify, truncate, wrap_paragraphs
36 from webhelpers.date import time_ago_in_words
36 from webhelpers.date import time_ago_in_words
37 from webhelpers.paginate import Page
37 from webhelpers.paginate import Page
38 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
38 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
39 convert_boolean_attrs, NotGiven, _make_safe_id_component
39 convert_boolean_attrs, NotGiven, _make_safe_id_component
40
40
41 from rhodecode.lib.annotate import annotate_highlight
41 from rhodecode.lib.annotate import annotate_highlight
42 from rhodecode.lib.utils import repo_name_slug
42 from rhodecode.lib.utils import repo_name_slug
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
44 get_changeset_safe
44 get_changeset_safe
45 from rhodecode.lib.markup_renderer import MarkupRenderer
45 from rhodecode.lib.markup_renderer import MarkupRenderer
46 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
46 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
48 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
49 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.changeset_status import ChangesetStatusModel
50 from rhodecode.model.db import URL_SEP, Permission
50 from rhodecode.model.db import URL_SEP, Permission
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 html_escape_table = {
55 html_escape_table = {
56 "&": "&amp;",
56 "&": "&amp;",
57 '"': "&quot;",
57 '"': "&quot;",
58 "'": "&apos;",
58 "'": "&apos;",
59 ">": "&gt;",
59 ">": "&gt;",
60 "<": "&lt;",
60 "<": "&lt;",
61 }
61 }
62
62
63
63
64 def html_escape(text):
64 def html_escape(text):
65 """Produce entities within text."""
65 """Produce entities within text."""
66 return "".join(html_escape_table.get(c,c) for c in text)
66 return "".join(html_escape_table.get(c,c) for c in text)
67
67
68
68
69 def shorter(text, size=20):
69 def shorter(text, size=20):
70 postfix = '...'
70 postfix = '...'
71 if len(text) > size:
71 if len(text) > size:
72 return text[:size - len(postfix)] + postfix
72 return text[:size - len(postfix)] + postfix
73 return text
73 return text
74
74
75
75
76 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
76 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
77 """
77 """
78 Reset button
78 Reset button
79 """
79 """
80 _set_input_attrs(attrs, type, name, value)
80 _set_input_attrs(attrs, type, name, value)
81 _set_id_attr(attrs, id, name)
81 _set_id_attr(attrs, id, name)
82 convert_boolean_attrs(attrs, ["disabled"])
82 convert_boolean_attrs(attrs, ["disabled"])
83 return HTML.input(**attrs)
83 return HTML.input(**attrs)
84
84
85 reset = _reset
85 reset = _reset
86 safeid = _make_safe_id_component
86 safeid = _make_safe_id_component
87
87
88
88
89 def FID(raw_id, path):
89 def FID(raw_id, path):
90 """
90 """
91 Creates a uniqe ID for filenode based on it's hash of path and revision
91 Creates a uniqe ID for filenode based on it's hash of path and revision
92 it's safe to use in urls
92 it's safe to use in urls
93
93
94 :param raw_id:
94 :param raw_id:
95 :param path:
95 :param path:
96 """
96 """
97
97
98 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
98 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
99
99
100
100
101 def get_token():
101 def get_token():
102 """Return the current authentication token, creating one if one doesn't
102 """Return the current authentication token, creating one if one doesn't
103 already exist.
103 already exist.
104 """
104 """
105 token_key = "_authentication_token"
105 token_key = "_authentication_token"
106 from pylons import session
106 from pylons import session
107 if not token_key in session:
107 if not token_key in session:
108 try:
108 try:
109 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
109 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
110 except AttributeError: # Python < 2.4
110 except AttributeError: # Python < 2.4
111 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
111 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
112 session[token_key] = token
112 session[token_key] = token
113 if hasattr(session, 'save'):
113 if hasattr(session, 'save'):
114 session.save()
114 session.save()
115 return session[token_key]
115 return session[token_key]
116
116
117
117
118 class _GetError(object):
118 class _GetError(object):
119 """Get error from form_errors, and represent it as span wrapped error
119 """Get error from form_errors, and represent it as span wrapped error
120 message
120 message
121
121
122 :param field_name: field to fetch errors for
122 :param field_name: field to fetch errors for
123 :param form_errors: form errors dict
123 :param form_errors: form errors dict
124 """
124 """
125
125
126 def __call__(self, field_name, form_errors):
126 def __call__(self, field_name, form_errors):
127 tmpl = """<span class="error_msg">%s</span>"""
127 tmpl = """<span class="error_msg">%s</span>"""
128 if form_errors and field_name in form_errors:
128 if form_errors and field_name in form_errors:
129 return literal(tmpl % form_errors.get(field_name))
129 return literal(tmpl % form_errors.get(field_name))
130
130
131 get_error = _GetError()
131 get_error = _GetError()
132
132
133
133
134 class _ToolTip(object):
134 class _ToolTip(object):
135
135
136 def __call__(self, tooltip_title, trim_at=50):
136 def __call__(self, tooltip_title, trim_at=50):
137 """
137 """
138 Special function just to wrap our text into nice formatted
138 Special function just to wrap our text into nice formatted
139 autowrapped text
139 autowrapped text
140
140
141 :param tooltip_title:
141 :param tooltip_title:
142 """
142 """
143 tooltip_title = escape(tooltip_title)
143 tooltip_title = escape(tooltip_title)
144 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
144 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
145 return tooltip_title
145 return tooltip_title
146 tooltip = _ToolTip()
146 tooltip = _ToolTip()
147
147
148
148
149 class _FilesBreadCrumbs(object):
149 class _FilesBreadCrumbs(object):
150
150
151 def __call__(self, repo_name, rev, paths):
151 def __call__(self, repo_name, rev, paths):
152 if isinstance(paths, str):
152 if isinstance(paths, str):
153 paths = safe_unicode(paths)
153 paths = safe_unicode(paths)
154 url_l = [link_to(repo_name, url('files_home',
154 url_l = [link_to(repo_name, url('files_home',
155 repo_name=repo_name,
155 repo_name=repo_name,
156 revision=rev, f_path=''))]
156 revision=rev, f_path=''),
157 class_='ypjax-link')]
157 paths_l = paths.split('/')
158 paths_l = paths.split('/')
158 for cnt, p in enumerate(paths_l):
159 for cnt, p in enumerate(paths_l):
159 if p != '':
160 if p != '':
160 url_l.append(link_to(p,
161 url_l.append(link_to(p,
161 url('files_home',
162 url('files_home',
162 repo_name=repo_name,
163 repo_name=repo_name,
163 revision=rev,
164 revision=rev,
164 f_path='/'.join(paths_l[:cnt + 1])
165 f_path='/'.join(paths_l[:cnt + 1])
165 )
166 ),
167 class_='ypjax-link'
166 )
168 )
167 )
169 )
168
170
169 return literal('/'.join(url_l))
171 return literal('/'.join(url_l))
170
172
171 files_breadcrumbs = _FilesBreadCrumbs()
173 files_breadcrumbs = _FilesBreadCrumbs()
172
174
173
175
174 class CodeHtmlFormatter(HtmlFormatter):
176 class CodeHtmlFormatter(HtmlFormatter):
175 """
177 """
176 My code Html Formatter for source codes
178 My code Html Formatter for source codes
177 """
179 """
178
180
179 def wrap(self, source, outfile):
181 def wrap(self, source, outfile):
180 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
182 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
181
183
182 def _wrap_code(self, source):
184 def _wrap_code(self, source):
183 for cnt, it in enumerate(source):
185 for cnt, it in enumerate(source):
184 i, t = it
186 i, t = it
185 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
187 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
186 yield i, t
188 yield i, t
187
189
188 def _wrap_tablelinenos(self, inner):
190 def _wrap_tablelinenos(self, inner):
189 dummyoutfile = StringIO.StringIO()
191 dummyoutfile = StringIO.StringIO()
190 lncount = 0
192 lncount = 0
191 for t, line in inner:
193 for t, line in inner:
192 if t:
194 if t:
193 lncount += 1
195 lncount += 1
194 dummyoutfile.write(line)
196 dummyoutfile.write(line)
195
197
196 fl = self.linenostart
198 fl = self.linenostart
197 mw = len(str(lncount + fl - 1))
199 mw = len(str(lncount + fl - 1))
198 sp = self.linenospecial
200 sp = self.linenospecial
199 st = self.linenostep
201 st = self.linenostep
200 la = self.lineanchors
202 la = self.lineanchors
201 aln = self.anchorlinenos
203 aln = self.anchorlinenos
202 nocls = self.noclasses
204 nocls = self.noclasses
203 if sp:
205 if sp:
204 lines = []
206 lines = []
205
207
206 for i in range(fl, fl + lncount):
208 for i in range(fl, fl + lncount):
207 if i % st == 0:
209 if i % st == 0:
208 if i % sp == 0:
210 if i % sp == 0:
209 if aln:
211 if aln:
210 lines.append('<a href="#%s%d" class="special">%*d</a>' %
212 lines.append('<a href="#%s%d" class="special">%*d</a>' %
211 (la, i, mw, i))
213 (la, i, mw, i))
212 else:
214 else:
213 lines.append('<span class="special">%*d</span>' % (mw, i))
215 lines.append('<span class="special">%*d</span>' % (mw, i))
214 else:
216 else:
215 if aln:
217 if aln:
216 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
218 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
217 else:
219 else:
218 lines.append('%*d' % (mw, i))
220 lines.append('%*d' % (mw, i))
219 else:
221 else:
220 lines.append('')
222 lines.append('')
221 ls = '\n'.join(lines)
223 ls = '\n'.join(lines)
222 else:
224 else:
223 lines = []
225 lines = []
224 for i in range(fl, fl + lncount):
226 for i in range(fl, fl + lncount):
225 if i % st == 0:
227 if i % st == 0:
226 if aln:
228 if aln:
227 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
229 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
228 else:
230 else:
229 lines.append('%*d' % (mw, i))
231 lines.append('%*d' % (mw, i))
230 else:
232 else:
231 lines.append('')
233 lines.append('')
232 ls = '\n'.join(lines)
234 ls = '\n'.join(lines)
233
235
234 # in case you wonder about the seemingly redundant <div> here: since the
236 # in case you wonder about the seemingly redundant <div> here: since the
235 # content in the other cell also is wrapped in a div, some browsers in
237 # content in the other cell also is wrapped in a div, some browsers in
236 # some configurations seem to mess up the formatting...
238 # some configurations seem to mess up the formatting...
237 if nocls:
239 if nocls:
238 yield 0, ('<table class="%stable">' % self.cssclass +
240 yield 0, ('<table class="%stable">' % self.cssclass +
239 '<tr><td><div class="linenodiv" '
241 '<tr><td><div class="linenodiv" '
240 'style="background-color: #f0f0f0; padding-right: 10px">'
242 'style="background-color: #f0f0f0; padding-right: 10px">'
241 '<pre style="line-height: 125%">' +
243 '<pre style="line-height: 125%">' +
242 ls + '</pre></div></td><td id="hlcode" class="code">')
244 ls + '</pre></div></td><td id="hlcode" class="code">')
243 else:
245 else:
244 yield 0, ('<table class="%stable">' % self.cssclass +
246 yield 0, ('<table class="%stable">' % self.cssclass +
245 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
247 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
246 ls + '</pre></div></td><td id="hlcode" class="code">')
248 ls + '</pre></div></td><td id="hlcode" class="code">')
247 yield 0, dummyoutfile.getvalue()
249 yield 0, dummyoutfile.getvalue()
248 yield 0, '</td></tr></table>'
250 yield 0, '</td></tr></table>'
249
251
250
252
251 def pygmentize(filenode, **kwargs):
253 def pygmentize(filenode, **kwargs):
252 """pygmentize function using pygments
254 """pygmentize function using pygments
253
255
254 :param filenode:
256 :param filenode:
255 """
257 """
256
258
257 return literal(code_highlight(filenode.content,
259 return literal(code_highlight(filenode.content,
258 filenode.lexer, CodeHtmlFormatter(**kwargs)))
260 filenode.lexer, CodeHtmlFormatter(**kwargs)))
259
261
260
262
261 def pygmentize_annotation(repo_name, filenode, **kwargs):
263 def pygmentize_annotation(repo_name, filenode, **kwargs):
262 """
264 """
263 pygmentize function for annotation
265 pygmentize function for annotation
264
266
265 :param filenode:
267 :param filenode:
266 """
268 """
267
269
268 color_dict = {}
270 color_dict = {}
269
271
270 def gen_color(n=10000):
272 def gen_color(n=10000):
271 """generator for getting n of evenly distributed colors using
273 """generator for getting n of evenly distributed colors using
272 hsv color and golden ratio. It always return same order of colors
274 hsv color and golden ratio. It always return same order of colors
273
275
274 :returns: RGB tuple
276 :returns: RGB tuple
275 """
277 """
276
278
277 def hsv_to_rgb(h, s, v):
279 def hsv_to_rgb(h, s, v):
278 if s == 0.0:
280 if s == 0.0:
279 return v, v, v
281 return v, v, v
280 i = int(h * 6.0) # XXX assume int() truncates!
282 i = int(h * 6.0) # XXX assume int() truncates!
281 f = (h * 6.0) - i
283 f = (h * 6.0) - i
282 p = v * (1.0 - s)
284 p = v * (1.0 - s)
283 q = v * (1.0 - s * f)
285 q = v * (1.0 - s * f)
284 t = v * (1.0 - s * (1.0 - f))
286 t = v * (1.0 - s * (1.0 - f))
285 i = i % 6
287 i = i % 6
286 if i == 0:
288 if i == 0:
287 return v, t, p
289 return v, t, p
288 if i == 1:
290 if i == 1:
289 return q, v, p
291 return q, v, p
290 if i == 2:
292 if i == 2:
291 return p, v, t
293 return p, v, t
292 if i == 3:
294 if i == 3:
293 return p, q, v
295 return p, q, v
294 if i == 4:
296 if i == 4:
295 return t, p, v
297 return t, p, v
296 if i == 5:
298 if i == 5:
297 return v, p, q
299 return v, p, q
298
300
299 golden_ratio = 0.618033988749895
301 golden_ratio = 0.618033988749895
300 h = 0.22717784590367374
302 h = 0.22717784590367374
301
303
302 for _ in xrange(n):
304 for _ in xrange(n):
303 h += golden_ratio
305 h += golden_ratio
304 h %= 1
306 h %= 1
305 HSV_tuple = [h, 0.95, 0.95]
307 HSV_tuple = [h, 0.95, 0.95]
306 RGB_tuple = hsv_to_rgb(*HSV_tuple)
308 RGB_tuple = hsv_to_rgb(*HSV_tuple)
307 yield map(lambda x: str(int(x * 256)), RGB_tuple)
309 yield map(lambda x: str(int(x * 256)), RGB_tuple)
308
310
309 cgenerator = gen_color()
311 cgenerator = gen_color()
310
312
311 def get_color_string(cs):
313 def get_color_string(cs):
312 if cs in color_dict:
314 if cs in color_dict:
313 col = color_dict[cs]
315 col = color_dict[cs]
314 else:
316 else:
315 col = color_dict[cs] = cgenerator.next()
317 col = color_dict[cs] = cgenerator.next()
316 return "color: rgb(%s)! important;" % (', '.join(col))
318 return "color: rgb(%s)! important;" % (', '.join(col))
317
319
318 def url_func(repo_name):
320 def url_func(repo_name):
319
321
320 def _url_func(changeset):
322 def _url_func(changeset):
321 author = changeset.author
323 author = changeset.author
322 date = changeset.date
324 date = changeset.date
323 message = tooltip(changeset.message)
325 message = tooltip(changeset.message)
324
326
325 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
327 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
326 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
328 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
327 "</b> %s<br/></div>")
329 "</b> %s<br/></div>")
328
330
329 tooltip_html = tooltip_html % (author, date, message)
331 tooltip_html = tooltip_html % (author, date, message)
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
332 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
331 short_id(changeset.raw_id))
333 short_id(changeset.raw_id))
332 uri = link_to(
334 uri = link_to(
333 lnk_format,
335 lnk_format,
334 url('changeset_home', repo_name=repo_name,
336 url('changeset_home', repo_name=repo_name,
335 revision=changeset.raw_id),
337 revision=changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
338 style=get_color_string(changeset.raw_id),
337 class_='tooltip',
339 class_='tooltip',
338 title=tooltip_html
340 title=tooltip_html
339 )
341 )
340
342
341 uri += '\n'
343 uri += '\n'
342 return uri
344 return uri
343 return _url_func
345 return _url_func
344
346
345 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
347 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
346
348
347
349
348 def is_following_repo(repo_name, user_id):
350 def is_following_repo(repo_name, user_id):
349 from rhodecode.model.scm import ScmModel
351 from rhodecode.model.scm import ScmModel
350 return ScmModel().is_following_repo(repo_name, user_id)
352 return ScmModel().is_following_repo(repo_name, user_id)
351
353
352 flash = _Flash()
354 flash = _Flash()
353
355
354 #==============================================================================
356 #==============================================================================
355 # SCM FILTERS available via h.
357 # SCM FILTERS available via h.
356 #==============================================================================
358 #==============================================================================
357 from rhodecode.lib.vcs.utils import author_name, author_email
359 from rhodecode.lib.vcs.utils import author_name, author_email
358 from rhodecode.lib.utils2 import credentials_filter, age as _age
360 from rhodecode.lib.utils2 import credentials_filter, age as _age
359 from rhodecode.model.db import User, ChangesetStatus
361 from rhodecode.model.db import User, ChangesetStatus
360
362
361 age = lambda x: _age(x)
363 age = lambda x: _age(x)
362 capitalize = lambda x: x.capitalize()
364 capitalize = lambda x: x.capitalize()
363 email = author_email
365 email = author_email
364 short_id = lambda x: x[:12]
366 short_id = lambda x: x[:12]
365 hide_credentials = lambda x: ''.join(credentials_filter(x))
367 hide_credentials = lambda x: ''.join(credentials_filter(x))
366
368
367
369
368 def fmt_date(date):
370 def fmt_date(date):
369 if date:
371 if date:
370 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
372 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
371 return date.strftime(_fmt).decode('utf8')
373 return date.strftime(_fmt).decode('utf8')
372
374
373 return ""
375 return ""
374
376
375
377
376 def is_git(repository):
378 def is_git(repository):
377 if hasattr(repository, 'alias'):
379 if hasattr(repository, 'alias'):
378 _type = repository.alias
380 _type = repository.alias
379 elif hasattr(repository, 'repo_type'):
381 elif hasattr(repository, 'repo_type'):
380 _type = repository.repo_type
382 _type = repository.repo_type
381 else:
383 else:
382 _type = repository
384 _type = repository
383 return _type == 'git'
385 return _type == 'git'
384
386
385
387
386 def is_hg(repository):
388 def is_hg(repository):
387 if hasattr(repository, 'alias'):
389 if hasattr(repository, 'alias'):
388 _type = repository.alias
390 _type = repository.alias
389 elif hasattr(repository, 'repo_type'):
391 elif hasattr(repository, 'repo_type'):
390 _type = repository.repo_type
392 _type = repository.repo_type
391 else:
393 else:
392 _type = repository
394 _type = repository
393 return _type == 'hg'
395 return _type == 'hg'
394
396
395
397
396 def email_or_none(author):
398 def email_or_none(author):
397 _email = email(author)
399 _email = email(author)
398 if _email != '':
400 if _email != '':
399 return _email
401 return _email
400
402
401 # See if it contains a username we can get an email from
403 # See if it contains a username we can get an email from
402 user = User.get_by_username(author_name(author), case_insensitive=True,
404 user = User.get_by_username(author_name(author), case_insensitive=True,
403 cache=True)
405 cache=True)
404 if user is not None:
406 if user is not None:
405 return user.email
407 return user.email
406
408
407 # No valid email, not a valid user in the system, none!
409 # No valid email, not a valid user in the system, none!
408 return None
410 return None
409
411
410
412
411 def person(author):
413 def person(author):
412 # attr to return from fetched user
414 # attr to return from fetched user
413 person_getter = lambda usr: usr.username
415 person_getter = lambda usr: usr.username
414
416
415 # Valid email in the attribute passed, see if they're in the system
417 # Valid email in the attribute passed, see if they're in the system
416 _email = email(author)
418 _email = email(author)
417 if _email != '':
419 if _email != '':
418 user = User.get_by_email(_email, case_insensitive=True, cache=True)
420 user = User.get_by_email(_email, case_insensitive=True, cache=True)
419 if user is not None:
421 if user is not None:
420 return person_getter(user)
422 return person_getter(user)
421 return _email
423 return _email
422
424
423 # Maybe it's a username?
425 # Maybe it's a username?
424 _author = author_name(author)
426 _author = author_name(author)
425 user = User.get_by_username(_author, case_insensitive=True,
427 user = User.get_by_username(_author, case_insensitive=True,
426 cache=True)
428 cache=True)
427 if user is not None:
429 if user is not None:
428 return person_getter(user)
430 return person_getter(user)
429
431
430 # Still nothing? Just pass back the author name then
432 # Still nothing? Just pass back the author name then
431 return _author
433 return _author
432
434
433
435
434 def desc_stylize(value):
436 def desc_stylize(value):
435 """
437 """
436 converts tags from value into html equivalent
438 converts tags from value into html equivalent
437
439
438 :param value:
440 :param value:
439 """
441 """
440 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
442 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
441 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
443 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
442 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
444 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
443 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
445 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
444 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
446 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
445 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
447 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
446 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
448 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
447 '<div class="metatag" tag="lang">\\2</div>', value)
449 '<div class="metatag" tag="lang">\\2</div>', value)
448 value = re.sub(r'\[([a-z]+)\]',
450 value = re.sub(r'\[([a-z]+)\]',
449 '<div class="metatag" tag="\\1">\\1</div>', value)
451 '<div class="metatag" tag="\\1">\\1</div>', value)
450
452
451 return value
453 return value
452
454
453
455
454 def bool2icon(value):
456 def bool2icon(value):
455 """Returns True/False values represented as small html image of true/false
457 """Returns True/False values represented as small html image of true/false
456 icons
458 icons
457
459
458 :param value: bool value
460 :param value: bool value
459 """
461 """
460
462
461 if value is True:
463 if value is True:
462 return HTML.tag('img', src=url("/images/icons/accept.png"),
464 return HTML.tag('img', src=url("/images/icons/accept.png"),
463 alt=_('True'))
465 alt=_('True'))
464
466
465 if value is False:
467 if value is False:
466 return HTML.tag('img', src=url("/images/icons/cancel.png"),
468 return HTML.tag('img', src=url("/images/icons/cancel.png"),
467 alt=_('False'))
469 alt=_('False'))
468
470
469 return value
471 return value
470
472
471
473
472 def action_parser(user_log, feed=False):
474 def action_parser(user_log, feed=False):
473 """
475 """
474 This helper will action_map the specified string action into translated
476 This helper will action_map the specified string action into translated
475 fancy names with icons and links
477 fancy names with icons and links
476
478
477 :param user_log: user log instance
479 :param user_log: user log instance
478 :param feed: use output for feeds (no html and fancy icons)
480 :param feed: use output for feeds (no html and fancy icons)
479 """
481 """
480
482
481 action = user_log.action
483 action = user_log.action
482 action_params = ' '
484 action_params = ' '
483
485
484 x = action.split(':')
486 x = action.split(':')
485
487
486 if len(x) > 1:
488 if len(x) > 1:
487 action, action_params = x
489 action, action_params = x
488
490
489 def get_cs_links():
491 def get_cs_links():
490 revs_limit = 3 # display this amount always
492 revs_limit = 3 # display this amount always
491 revs_top_limit = 50 # show upto this amount of changesets hidden
493 revs_top_limit = 50 # show upto this amount of changesets hidden
492 revs_ids = action_params.split(',')
494 revs_ids = action_params.split(',')
493 deleted = user_log.repository is None
495 deleted = user_log.repository is None
494 if deleted:
496 if deleted:
495 return ','.join(revs_ids)
497 return ','.join(revs_ids)
496
498
497 repo_name = user_log.repository.repo_name
499 repo_name = user_log.repository.repo_name
498
500
499 repo = user_log.repository.scm_instance
501 repo = user_log.repository.scm_instance
500
502
501 def lnk(rev, repo_name):
503 def lnk(rev, repo_name):
502
504
503 if isinstance(rev, BaseChangeset):
505 if isinstance(rev, BaseChangeset):
504 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
506 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
505 _url = url('changeset_home', repo_name=repo_name,
507 _url = url('changeset_home', repo_name=repo_name,
506 revision=rev.raw_id)
508 revision=rev.raw_id)
507 title = tooltip(rev.message)
509 title = tooltip(rev.message)
508 else:
510 else:
509 lbl = '%s' % rev
511 lbl = '%s' % rev
510 _url = '#'
512 _url = '#'
511 title = _('Changeset not found')
513 title = _('Changeset not found')
512
514
513 return link_to(lbl, _url, title=title, class_='tooltip',)
515 return link_to(lbl, _url, title=title, class_='tooltip',)
514
516
515 revs = []
517 revs = []
516 if len(filter(lambda v: v != '', revs_ids)) > 0:
518 if len(filter(lambda v: v != '', revs_ids)) > 0:
517 for rev in revs_ids[:revs_top_limit]:
519 for rev in revs_ids[:revs_top_limit]:
518 try:
520 try:
519 rev = repo.get_changeset(rev)
521 rev = repo.get_changeset(rev)
520 revs.append(rev)
522 revs.append(rev)
521 except ChangesetDoesNotExistError:
523 except ChangesetDoesNotExistError:
522 log.error('cannot find revision %s in this repo' % rev)
524 log.error('cannot find revision %s in this repo' % rev)
523 revs.append(rev)
525 revs.append(rev)
524 continue
526 continue
525 cs_links = []
527 cs_links = []
526 cs_links.append(" " + ', '.join(
528 cs_links.append(" " + ', '.join(
527 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
529 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
528 )
530 )
529 )
531 )
530
532
531 compare_view = (
533 compare_view = (
532 ' <div class="compare_view tooltip" title="%s">'
534 ' <div class="compare_view tooltip" title="%s">'
533 '<a href="%s">%s</a> </div>' % (
535 '<a href="%s">%s</a> </div>' % (
534 _('Show all combined changesets %s->%s') % (
536 _('Show all combined changesets %s->%s') % (
535 revs_ids[0], revs_ids[-1]
537 revs_ids[0], revs_ids[-1]
536 ),
538 ),
537 url('changeset_home', repo_name=repo_name,
539 url('changeset_home', repo_name=repo_name,
538 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
540 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
539 ),
541 ),
540 _('compare view')
542 _('compare view')
541 )
543 )
542 )
544 )
543
545
544 # if we have exactly one more than normally displayed
546 # if we have exactly one more than normally displayed
545 # just display it, takes less space than displaying
547 # just display it, takes less space than displaying
546 # "and 1 more revisions"
548 # "and 1 more revisions"
547 if len(revs_ids) == revs_limit + 1:
549 if len(revs_ids) == revs_limit + 1:
548 rev = revs[revs_limit]
550 rev = revs[revs_limit]
549 cs_links.append(", " + lnk(rev, repo_name))
551 cs_links.append(", " + lnk(rev, repo_name))
550
552
551 # hidden-by-default ones
553 # hidden-by-default ones
552 if len(revs_ids) > revs_limit + 1:
554 if len(revs_ids) > revs_limit + 1:
553 uniq_id = revs_ids[0]
555 uniq_id = revs_ids[0]
554 html_tmpl = (
556 html_tmpl = (
555 '<span> %s <a class="show_more" id="_%s" '
557 '<span> %s <a class="show_more" id="_%s" '
556 'href="#more">%s</a> %s</span>'
558 'href="#more">%s</a> %s</span>'
557 )
559 )
558 if not feed:
560 if not feed:
559 cs_links.append(html_tmpl % (
561 cs_links.append(html_tmpl % (
560 _('and'),
562 _('and'),
561 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
563 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
562 _('revisions')
564 _('revisions')
563 )
565 )
564 )
566 )
565
567
566 if not feed:
568 if not feed:
567 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
569 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
568 else:
570 else:
569 html_tmpl = '<span id="%s"> %s </span>'
571 html_tmpl = '<span id="%s"> %s </span>'
570
572
571 morelinks = ', '.join(
573 morelinks = ', '.join(
572 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
574 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
573 )
575 )
574
576
575 if len(revs_ids) > revs_top_limit:
577 if len(revs_ids) > revs_top_limit:
576 morelinks += ', ...'
578 morelinks += ', ...'
577
579
578 cs_links.append(html_tmpl % (uniq_id, morelinks))
580 cs_links.append(html_tmpl % (uniq_id, morelinks))
579 if len(revs) > 1:
581 if len(revs) > 1:
580 cs_links.append(compare_view)
582 cs_links.append(compare_view)
581 return ''.join(cs_links)
583 return ''.join(cs_links)
582
584
583 def get_fork_name():
585 def get_fork_name():
584 repo_name = action_params
586 repo_name = action_params
585 return _('fork name ') + str(link_to(action_params, url('summary_home',
587 return _('fork name ') + str(link_to(action_params, url('summary_home',
586 repo_name=repo_name,)))
588 repo_name=repo_name,)))
587
589
588 def get_user_name():
590 def get_user_name():
589 user_name = action_params
591 user_name = action_params
590 return user_name
592 return user_name
591
593
592 def get_users_group():
594 def get_users_group():
593 group_name = action_params
595 group_name = action_params
594 return group_name
596 return group_name
595
597
596 def get_pull_request():
598 def get_pull_request():
597 pull_request_id = action_params
599 pull_request_id = action_params
598 repo_name = user_log.repository.repo_name
600 repo_name = user_log.repository.repo_name
599 return link_to(_('Pull request #%s') % pull_request_id,
601 return link_to(_('Pull request #%s') % pull_request_id,
600 url('pullrequest_show', repo_name=repo_name,
602 url('pullrequest_show', repo_name=repo_name,
601 pull_request_id=pull_request_id))
603 pull_request_id=pull_request_id))
602
604
603 # action : translated str, callback(extractor), icon
605 # action : translated str, callback(extractor), icon
604 action_map = {
606 action_map = {
605 'user_deleted_repo': (_('[deleted] repository'),
607 'user_deleted_repo': (_('[deleted] repository'),
606 None, 'database_delete.png'),
608 None, 'database_delete.png'),
607 'user_created_repo': (_('[created] repository'),
609 'user_created_repo': (_('[created] repository'),
608 None, 'database_add.png'),
610 None, 'database_add.png'),
609 'user_created_fork': (_('[created] repository as fork'),
611 'user_created_fork': (_('[created] repository as fork'),
610 None, 'arrow_divide.png'),
612 None, 'arrow_divide.png'),
611 'user_forked_repo': (_('[forked] repository'),
613 'user_forked_repo': (_('[forked] repository'),
612 get_fork_name, 'arrow_divide.png'),
614 get_fork_name, 'arrow_divide.png'),
613 'user_updated_repo': (_('[updated] repository'),
615 'user_updated_repo': (_('[updated] repository'),
614 None, 'database_edit.png'),
616 None, 'database_edit.png'),
615 'admin_deleted_repo': (_('[delete] repository'),
617 'admin_deleted_repo': (_('[delete] repository'),
616 None, 'database_delete.png'),
618 None, 'database_delete.png'),
617 'admin_created_repo': (_('[created] repository'),
619 'admin_created_repo': (_('[created] repository'),
618 None, 'database_add.png'),
620 None, 'database_add.png'),
619 'admin_forked_repo': (_('[forked] repository'),
621 'admin_forked_repo': (_('[forked] repository'),
620 None, 'arrow_divide.png'),
622 None, 'arrow_divide.png'),
621 'admin_updated_repo': (_('[updated] repository'),
623 'admin_updated_repo': (_('[updated] repository'),
622 None, 'database_edit.png'),
624 None, 'database_edit.png'),
623 'admin_created_user': (_('[created] user'),
625 'admin_created_user': (_('[created] user'),
624 get_user_name, 'user_add.png'),
626 get_user_name, 'user_add.png'),
625 'admin_updated_user': (_('[updated] user'),
627 'admin_updated_user': (_('[updated] user'),
626 get_user_name, 'user_edit.png'),
628 get_user_name, 'user_edit.png'),
627 'admin_created_users_group': (_('[created] users group'),
629 'admin_created_users_group': (_('[created] users group'),
628 get_users_group, 'group_add.png'),
630 get_users_group, 'group_add.png'),
629 'admin_updated_users_group': (_('[updated] users group'),
631 'admin_updated_users_group': (_('[updated] users group'),
630 get_users_group, 'group_edit.png'),
632 get_users_group, 'group_edit.png'),
631 'user_commented_revision': (_('[commented] on revision in repository'),
633 'user_commented_revision': (_('[commented] on revision in repository'),
632 get_cs_links, 'comment_add.png'),
634 get_cs_links, 'comment_add.png'),
633 'user_commented_pull_request': (_('[commented] on pull request for'),
635 'user_commented_pull_request': (_('[commented] on pull request for'),
634 get_pull_request, 'comment_add.png'),
636 get_pull_request, 'comment_add.png'),
635 'user_closed_pull_request': (_('[closed] pull request for'),
637 'user_closed_pull_request': (_('[closed] pull request for'),
636 get_pull_request, 'tick.png'),
638 get_pull_request, 'tick.png'),
637 'push': (_('[pushed] into'),
639 'push': (_('[pushed] into'),
638 get_cs_links, 'script_add.png'),
640 get_cs_links, 'script_add.png'),
639 'push_local': (_('[committed via RhodeCode] into repository'),
641 'push_local': (_('[committed via RhodeCode] into repository'),
640 get_cs_links, 'script_edit.png'),
642 get_cs_links, 'script_edit.png'),
641 'push_remote': (_('[pulled from remote] into repository'),
643 'push_remote': (_('[pulled from remote] into repository'),
642 get_cs_links, 'connect.png'),
644 get_cs_links, 'connect.png'),
643 'pull': (_('[pulled] from'),
645 'pull': (_('[pulled] from'),
644 None, 'down_16.png'),
646 None, 'down_16.png'),
645 'started_following_repo': (_('[started following] repository'),
647 'started_following_repo': (_('[started following] repository'),
646 None, 'heart_add.png'),
648 None, 'heart_add.png'),
647 'stopped_following_repo': (_('[stopped following] repository'),
649 'stopped_following_repo': (_('[stopped following] repository'),
648 None, 'heart_delete.png'),
650 None, 'heart_delete.png'),
649 }
651 }
650
652
651 action_str = action_map.get(action, action)
653 action_str = action_map.get(action, action)
652 if feed:
654 if feed:
653 action = action_str[0].replace('[', '').replace(']', '')
655 action = action_str[0].replace('[', '').replace(']', '')
654 else:
656 else:
655 action = action_str[0]\
657 action = action_str[0]\
656 .replace('[', '<span class="journal_highlight">')\
658 .replace('[', '<span class="journal_highlight">')\
657 .replace(']', '</span>')
659 .replace(']', '</span>')
658
660
659 action_params_func = lambda: ""
661 action_params_func = lambda: ""
660
662
661 if callable(action_str[1]):
663 if callable(action_str[1]):
662 action_params_func = action_str[1]
664 action_params_func = action_str[1]
663
665
664 def action_parser_icon():
666 def action_parser_icon():
665 action = user_log.action
667 action = user_log.action
666 action_params = None
668 action_params = None
667 x = action.split(':')
669 x = action.split(':')
668
670
669 if len(x) > 1:
671 if len(x) > 1:
670 action, action_params = x
672 action, action_params = x
671
673
672 tmpl = """<img src="%s%s" alt="%s"/>"""
674 tmpl = """<img src="%s%s" alt="%s"/>"""
673 ico = action_map.get(action, ['', '', ''])[2]
675 ico = action_map.get(action, ['', '', ''])[2]
674 return literal(tmpl % ((url('/images/icons/')), ico, action))
676 return literal(tmpl % ((url('/images/icons/')), ico, action))
675
677
676 # returned callbacks we need to call to get
678 # returned callbacks we need to call to get
677 return [lambda: literal(action), action_params_func, action_parser_icon]
679 return [lambda: literal(action), action_params_func, action_parser_icon]
678
680
679
681
680
682
681 #==============================================================================
683 #==============================================================================
682 # PERMS
684 # PERMS
683 #==============================================================================
685 #==============================================================================
684 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
686 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
685 HasRepoPermissionAny, HasRepoPermissionAll
687 HasRepoPermissionAny, HasRepoPermissionAll
686
688
687
689
688 #==============================================================================
690 #==============================================================================
689 # GRAVATAR URL
691 # GRAVATAR URL
690 #==============================================================================
692 #==============================================================================
691
693
692 def gravatar_url(email_address, size=30):
694 def gravatar_url(email_address, size=30):
693 if (not str2bool(config['app_conf'].get('use_gravatar')) or
695 if (not str2bool(config['app_conf'].get('use_gravatar')) or
694 not email_address or email_address == 'anonymous@rhodecode.org'):
696 not email_address or email_address == 'anonymous@rhodecode.org'):
695 f = lambda a, l: min(l, key=lambda x: abs(x - a))
697 f = lambda a, l: min(l, key=lambda x: abs(x - a))
696 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
698 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
697
699
698 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
700 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
699 default = 'identicon'
701 default = 'identicon'
700 baseurl_nossl = "http://www.gravatar.com/avatar/"
702 baseurl_nossl = "http://www.gravatar.com/avatar/"
701 baseurl_ssl = "https://secure.gravatar.com/avatar/"
703 baseurl_ssl = "https://secure.gravatar.com/avatar/"
702 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
704 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
703
705
704 if isinstance(email_address, unicode):
706 if isinstance(email_address, unicode):
705 #hashlib crashes on unicode items
707 #hashlib crashes on unicode items
706 email_address = safe_str(email_address)
708 email_address = safe_str(email_address)
707 # construct the url
709 # construct the url
708 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
710 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
709 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
711 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
710
712
711 return gravatar_url
713 return gravatar_url
712
714
713
715
714 #==============================================================================
716 #==============================================================================
715 # REPO PAGER, PAGER FOR REPOSITORY
717 # REPO PAGER, PAGER FOR REPOSITORY
716 #==============================================================================
718 #==============================================================================
717 class RepoPage(Page):
719 class RepoPage(Page):
718
720
719 def __init__(self, collection, page=1, items_per_page=20,
721 def __init__(self, collection, page=1, items_per_page=20,
720 item_count=None, url=None, **kwargs):
722 item_count=None, url=None, **kwargs):
721
723
722 """Create a "RepoPage" instance. special pager for paging
724 """Create a "RepoPage" instance. special pager for paging
723 repository
725 repository
724 """
726 """
725 self._url_generator = url
727 self._url_generator = url
726
728
727 # Safe the kwargs class-wide so they can be used in the pager() method
729 # Safe the kwargs class-wide so they can be used in the pager() method
728 self.kwargs = kwargs
730 self.kwargs = kwargs
729
731
730 # Save a reference to the collection
732 # Save a reference to the collection
731 self.original_collection = collection
733 self.original_collection = collection
732
734
733 self.collection = collection
735 self.collection = collection
734
736
735 # The self.page is the number of the current page.
737 # The self.page is the number of the current page.
736 # The first page has the number 1!
738 # The first page has the number 1!
737 try:
739 try:
738 self.page = int(page) # make it int() if we get it as a string
740 self.page = int(page) # make it int() if we get it as a string
739 except (ValueError, TypeError):
741 except (ValueError, TypeError):
740 self.page = 1
742 self.page = 1
741
743
742 self.items_per_page = items_per_page
744 self.items_per_page = items_per_page
743
745
744 # Unless the user tells us how many items the collections has
746 # Unless the user tells us how many items the collections has
745 # we calculate that ourselves.
747 # we calculate that ourselves.
746 if item_count is not None:
748 if item_count is not None:
747 self.item_count = item_count
749 self.item_count = item_count
748 else:
750 else:
749 self.item_count = len(self.collection)
751 self.item_count = len(self.collection)
750
752
751 # Compute the number of the first and last available page
753 # Compute the number of the first and last available page
752 if self.item_count > 0:
754 if self.item_count > 0:
753 self.first_page = 1
755 self.first_page = 1
754 self.page_count = int(math.ceil(float(self.item_count) /
756 self.page_count = int(math.ceil(float(self.item_count) /
755 self.items_per_page))
757 self.items_per_page))
756 self.last_page = self.first_page + self.page_count - 1
758 self.last_page = self.first_page + self.page_count - 1
757
759
758 # Make sure that the requested page number is the range of
760 # Make sure that the requested page number is the range of
759 # valid pages
761 # valid pages
760 if self.page > self.last_page:
762 if self.page > self.last_page:
761 self.page = self.last_page
763 self.page = self.last_page
762 elif self.page < self.first_page:
764 elif self.page < self.first_page:
763 self.page = self.first_page
765 self.page = self.first_page
764
766
765 # Note: the number of items on this page can be less than
767 # Note: the number of items on this page can be less than
766 # items_per_page if the last page is not full
768 # items_per_page if the last page is not full
767 self.first_item = max(0, (self.item_count) - (self.page *
769 self.first_item = max(0, (self.item_count) - (self.page *
768 items_per_page))
770 items_per_page))
769 self.last_item = ((self.item_count - 1) - items_per_page *
771 self.last_item = ((self.item_count - 1) - items_per_page *
770 (self.page - 1))
772 (self.page - 1))
771
773
772 self.items = list(self.collection[self.first_item:self.last_item + 1])
774 self.items = list(self.collection[self.first_item:self.last_item + 1])
773
775
774 # Links to previous and next page
776 # Links to previous and next page
775 if self.page > self.first_page:
777 if self.page > self.first_page:
776 self.previous_page = self.page - 1
778 self.previous_page = self.page - 1
777 else:
779 else:
778 self.previous_page = None
780 self.previous_page = None
779
781
780 if self.page < self.last_page:
782 if self.page < self.last_page:
781 self.next_page = self.page + 1
783 self.next_page = self.page + 1
782 else:
784 else:
783 self.next_page = None
785 self.next_page = None
784
786
785 # No items available
787 # No items available
786 else:
788 else:
787 self.first_page = None
789 self.first_page = None
788 self.page_count = 0
790 self.page_count = 0
789 self.last_page = None
791 self.last_page = None
790 self.first_item = None
792 self.first_item = None
791 self.last_item = None
793 self.last_item = None
792 self.previous_page = None
794 self.previous_page = None
793 self.next_page = None
795 self.next_page = None
794 self.items = []
796 self.items = []
795
797
796 # This is a subclass of the 'list' type. Initialise the list now.
798 # This is a subclass of the 'list' type. Initialise the list now.
797 list.__init__(self, reversed(self.items))
799 list.__init__(self, reversed(self.items))
798
800
799
801
800 def changed_tooltip(nodes):
802 def changed_tooltip(nodes):
801 """
803 """
802 Generates a html string for changed nodes in changeset page.
804 Generates a html string for changed nodes in changeset page.
803 It limits the output to 30 entries
805 It limits the output to 30 entries
804
806
805 :param nodes: LazyNodesGenerator
807 :param nodes: LazyNodesGenerator
806 """
808 """
807 if nodes:
809 if nodes:
808 pref = ': <br/> '
810 pref = ': <br/> '
809 suf = ''
811 suf = ''
810 if len(nodes) > 30:
812 if len(nodes) > 30:
811 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
813 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
812 return literal(pref + '<br/> '.join([safe_unicode(x.path)
814 return literal(pref + '<br/> '.join([safe_unicode(x.path)
813 for x in nodes[:30]]) + suf)
815 for x in nodes[:30]]) + suf)
814 else:
816 else:
815 return ': ' + _('No Files')
817 return ': ' + _('No Files')
816
818
817
819
818 def repo_link(groups_and_repos):
820 def repo_link(groups_and_repos):
819 """
821 """
820 Makes a breadcrumbs link to repo within a group
822 Makes a breadcrumbs link to repo within a group
821 joins &raquo; on each group to create a fancy link
823 joins &raquo; on each group to create a fancy link
822
824
823 ex::
825 ex::
824 group >> subgroup >> repo
826 group >> subgroup >> repo
825
827
826 :param groups_and_repos:
828 :param groups_and_repos:
827 """
829 """
828 groups, repo_name = groups_and_repos
830 groups, repo_name = groups_and_repos
829
831
830 if not groups:
832 if not groups:
831 return repo_name
833 return repo_name
832 else:
834 else:
833 def make_link(group):
835 def make_link(group):
834 return link_to(group.name, url('repos_group_home',
836 return link_to(group.name, url('repos_group_home',
835 group_name=group.group_name))
837 group_name=group.group_name))
836 return literal(' &raquo; '.join(map(make_link, groups)) + \
838 return literal(' &raquo; '.join(map(make_link, groups)) + \
837 " &raquo; " + repo_name)
839 " &raquo; " + repo_name)
838
840
839
841
840 def fancy_file_stats(stats):
842 def fancy_file_stats(stats):
841 """
843 """
842 Displays a fancy two colored bar for number of added/deleted
844 Displays a fancy two colored bar for number of added/deleted
843 lines of code on file
845 lines of code on file
844
846
845 :param stats: two element list of added/deleted lines of code
847 :param stats: two element list of added/deleted lines of code
846 """
848 """
847
849
848 a, d, t = stats[0], stats[1], stats[0] + stats[1]
850 a, d, t = stats[0], stats[1], stats[0] + stats[1]
849 width = 100
851 width = 100
850 unit = float(width) / (t or 1)
852 unit = float(width) / (t or 1)
851
853
852 # needs > 9% of width to be visible or 0 to be hidden
854 # needs > 9% of width to be visible or 0 to be hidden
853 a_p = max(9, unit * a) if a > 0 else 0
855 a_p = max(9, unit * a) if a > 0 else 0
854 d_p = max(9, unit * d) if d > 0 else 0
856 d_p = max(9, unit * d) if d > 0 else 0
855 p_sum = a_p + d_p
857 p_sum = a_p + d_p
856
858
857 if p_sum > width:
859 if p_sum > width:
858 #adjust the percentage to be == 100% since we adjusted to 9
860 #adjust the percentage to be == 100% since we adjusted to 9
859 if a_p > d_p:
861 if a_p > d_p:
860 a_p = a_p - (p_sum - width)
862 a_p = a_p - (p_sum - width)
861 else:
863 else:
862 d_p = d_p - (p_sum - width)
864 d_p = d_p - (p_sum - width)
863
865
864 a_v = a if a > 0 else ''
866 a_v = a if a > 0 else ''
865 d_v = d if d > 0 else ''
867 d_v = d if d > 0 else ''
866
868
867 def cgen(l_type):
869 def cgen(l_type):
868 mapping = {'tr': 'top-right-rounded-corner-mid',
870 mapping = {'tr': 'top-right-rounded-corner-mid',
869 'tl': 'top-left-rounded-corner-mid',
871 'tl': 'top-left-rounded-corner-mid',
870 'br': 'bottom-right-rounded-corner-mid',
872 'br': 'bottom-right-rounded-corner-mid',
871 'bl': 'bottom-left-rounded-corner-mid'}
873 'bl': 'bottom-left-rounded-corner-mid'}
872 map_getter = lambda x: mapping[x]
874 map_getter = lambda x: mapping[x]
873
875
874 if l_type == 'a' and d_v:
876 if l_type == 'a' and d_v:
875 #case when added and deleted are present
877 #case when added and deleted are present
876 return ' '.join(map(map_getter, ['tl', 'bl']))
878 return ' '.join(map(map_getter, ['tl', 'bl']))
877
879
878 if l_type == 'a' and not d_v:
880 if l_type == 'a' and not d_v:
879 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
881 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
880
882
881 if l_type == 'd' and a_v:
883 if l_type == 'd' and a_v:
882 return ' '.join(map(map_getter, ['tr', 'br']))
884 return ' '.join(map(map_getter, ['tr', 'br']))
883
885
884 if l_type == 'd' and not a_v:
886 if l_type == 'd' and not a_v:
885 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
887 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
886
888
887 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
889 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
888 cgen('a'), a_p, a_v
890 cgen('a'), a_p, a_v
889 )
891 )
890 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
892 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
891 cgen('d'), d_p, d_v
893 cgen('d'), d_p, d_v
892 )
894 )
893 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
895 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
894
896
895
897
896 def urlify_text(text_):
898 def urlify_text(text_):
897 import re
899 import re
898
900
899 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
901 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
900 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
902 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
901
903
902 def url_func(match_obj):
904 def url_func(match_obj):
903 url_full = match_obj.groups()[0]
905 url_full = match_obj.groups()[0]
904 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
906 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
905
907
906 return literal(url_pat.sub(url_func, text_))
908 return literal(url_pat.sub(url_func, text_))
907
909
908
910
909 def urlify_changesets(text_, repository):
911 def urlify_changesets(text_, repository):
910 """
912 """
911 Extract revision ids from changeset and make link from them
913 Extract revision ids from changeset and make link from them
912
914
913 :param text_:
915 :param text_:
914 :param repository:
916 :param repository:
915 """
917 """
916 import re
918 import re
917 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
919 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
918
920
919 def url_func(match_obj):
921 def url_func(match_obj):
920 rev = match_obj.groups()[0]
922 rev = match_obj.groups()[0]
921 pref = ''
923 pref = ''
922 if match_obj.group().startswith(' '):
924 if match_obj.group().startswith(' '):
923 pref = ' '
925 pref = ' '
924 tmpl = (
926 tmpl = (
925 '%(pref)s<a class="%(cls)s" href="%(url)s">'
927 '%(pref)s<a class="%(cls)s" href="%(url)s">'
926 '%(rev)s'
928 '%(rev)s'
927 '</a>'
929 '</a>'
928 )
930 )
929 return tmpl % {
931 return tmpl % {
930 'pref': pref,
932 'pref': pref,
931 'cls': 'revision-link',
933 'cls': 'revision-link',
932 'url': url('changeset_home', repo_name=repository, revision=rev),
934 'url': url('changeset_home', repo_name=repository, revision=rev),
933 'rev': rev,
935 'rev': rev,
934 }
936 }
935
937
936 newtext = URL_PAT.sub(url_func, text_)
938 newtext = URL_PAT.sub(url_func, text_)
937
939
938 return newtext
940 return newtext
939
941
940
942
941 def urlify_commit(text_, repository=None, link_=None):
943 def urlify_commit(text_, repository=None, link_=None):
942 """
944 """
943 Parses given text message and makes proper links.
945 Parses given text message and makes proper links.
944 issues are linked to given issue-server, and rest is a changeset link
946 issues are linked to given issue-server, and rest is a changeset link
945 if link_ is given, in other case it's a plain text
947 if link_ is given, in other case it's a plain text
946
948
947 :param text_:
949 :param text_:
948 :param repository:
950 :param repository:
949 :param link_: changeset link
951 :param link_: changeset link
950 """
952 """
951 import re
953 import re
952 import traceback
954 import traceback
953
955
954 def escaper(string):
956 def escaper(string):
955 return string.replace('<', '&lt;').replace('>', '&gt;')
957 return string.replace('<', '&lt;').replace('>', '&gt;')
956
958
957 def linkify_others(t, l):
959 def linkify_others(t, l):
958 urls = re.compile(r'(\<a.*?\<\/a\>)',)
960 urls = re.compile(r'(\<a.*?\<\/a\>)',)
959 links = []
961 links = []
960 for e in urls.split(t):
962 for e in urls.split(t):
961 if not urls.match(e):
963 if not urls.match(e):
962 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
964 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
963 else:
965 else:
964 links.append(e)
966 links.append(e)
965
967
966 return ''.join(links)
968 return ''.join(links)
967
969
968 # urlify changesets - extrac revisions and make link out of them
970 # urlify changesets - extrac revisions and make link out of them
969 text_ = urlify_changesets(escaper(text_), repository)
971 text_ = urlify_changesets(escaper(text_), repository)
970
972
971 try:
973 try:
972 conf = config['app_conf']
974 conf = config['app_conf']
973
975
974 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
976 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
975
977
976 if URL_PAT:
978 if URL_PAT:
977 ISSUE_SERVER_LNK = conf.get('issue_server_link')
979 ISSUE_SERVER_LNK = conf.get('issue_server_link')
978 ISSUE_PREFIX = conf.get('issue_prefix')
980 ISSUE_PREFIX = conf.get('issue_prefix')
979
981
980 def url_func(match_obj):
982 def url_func(match_obj):
981 pref = ''
983 pref = ''
982 if match_obj.group().startswith(' '):
984 if match_obj.group().startswith(' '):
983 pref = ' '
985 pref = ' '
984
986
985 issue_id = ''.join(match_obj.groups())
987 issue_id = ''.join(match_obj.groups())
986 tmpl = (
988 tmpl = (
987 '%(pref)s<a class="%(cls)s" href="%(url)s">'
989 '%(pref)s<a class="%(cls)s" href="%(url)s">'
988 '%(issue-prefix)s%(id-repr)s'
990 '%(issue-prefix)s%(id-repr)s'
989 '</a>'
991 '</a>'
990 )
992 )
991 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
993 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
992 if repository:
994 if repository:
993 url = url.replace('{repo}', repository)
995 url = url.replace('{repo}', repository)
994 repo_name = repository.split(URL_SEP)[-1]
996 repo_name = repository.split(URL_SEP)[-1]
995 url = url.replace('{repo_name}', repo_name)
997 url = url.replace('{repo_name}', repo_name)
996 return tmpl % {
998 return tmpl % {
997 'pref': pref,
999 'pref': pref,
998 'cls': 'issue-tracker-link',
1000 'cls': 'issue-tracker-link',
999 'url': url,
1001 'url': url,
1000 'id-repr': issue_id,
1002 'id-repr': issue_id,
1001 'issue-prefix': ISSUE_PREFIX,
1003 'issue-prefix': ISSUE_PREFIX,
1002 'serv': ISSUE_SERVER_LNK,
1004 'serv': ISSUE_SERVER_LNK,
1003 }
1005 }
1004
1006
1005 newtext = URL_PAT.sub(url_func, text_)
1007 newtext = URL_PAT.sub(url_func, text_)
1006
1008
1007 if link_:
1009 if link_:
1008 # wrap not links into final link => link_
1010 # wrap not links into final link => link_
1009 newtext = linkify_others(newtext, link_)
1011 newtext = linkify_others(newtext, link_)
1010
1012
1011 return literal(newtext)
1013 return literal(newtext)
1012 except:
1014 except:
1013 log.error(traceback.format_exc())
1015 log.error(traceback.format_exc())
1014 pass
1016 pass
1015
1017
1016 return text_
1018 return text_
1017
1019
1018
1020
1019 def rst(source):
1021 def rst(source):
1020 return literal('<div class="rst-block">%s</div>' %
1022 return literal('<div class="rst-block">%s</div>' %
1021 MarkupRenderer.rst(source))
1023 MarkupRenderer.rst(source))
1022
1024
1023
1025
1024 def rst_w_mentions(source):
1026 def rst_w_mentions(source):
1025 """
1027 """
1026 Wrapped rst renderer with @mention highlighting
1028 Wrapped rst renderer with @mention highlighting
1027
1029
1028 :param source:
1030 :param source:
1029 """
1031 """
1030 return literal('<div class="rst-block">%s</div>' %
1032 return literal('<div class="rst-block">%s</div>' %
1031 MarkupRenderer.rst_with_mentions(source))
1033 MarkupRenderer.rst_with_mentions(source))
1032
1034
1033
1035
1034 def changeset_status(repo, revision):
1036 def changeset_status(repo, revision):
1035 return ChangesetStatusModel().get_status(repo, revision)
1037 return ChangesetStatusModel().get_status(repo, revision)
1036
1038
1037
1039
1038 def changeset_status_lbl(changeset_status):
1040 def changeset_status_lbl(changeset_status):
1039 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1041 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1040
1042
1041
1043
1042 def get_permission_name(key):
1044 def get_permission_name(key):
1043 return dict(Permission.PERMS).get(key)
1045 return dict(Permission.PERMS).get(key)
@@ -1,1712 +1,1713 b''
1 /**
1 /**
2 RhodeCode JS Files
2 RhodeCode JS Files
3 **/
3 **/
4
4
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 console = { log: function() {} }
6 console = { log: function() {} }
7 }
7 }
8
8
9
9
10 var str_repeat = function(i, m) {
10 var str_repeat = function(i, m) {
11 for (var o = []; m > 0; o[--m] = i);
11 for (var o = []; m > 0; o[--m] = i);
12 return o.join('');
12 return o.join('');
13 };
13 };
14
14
15 /**
15 /**
16 * INJECT .format function into String
16 * INJECT .format function into String
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
18 * Return "My name is Johny Bravo"
18 * Return "My name is Johny Bravo"
19 * Inspired by https://gist.github.com/1049426
19 * Inspired by https://gist.github.com/1049426
20 */
20 */
21 String.prototype.format = function() {
21 String.prototype.format = function() {
22
22
23 function format() {
23 function format() {
24 var str = this;
24 var str = this;
25 var len = arguments.length+1;
25 var len = arguments.length+1;
26 var safe = undefined;
26 var safe = undefined;
27 var arg = undefined;
27 var arg = undefined;
28
28
29 // For each {0} {1} {n...} replace with the argument in that position. If
29 // For each {0} {1} {n...} replace with the argument in that position. If
30 // the argument is an object or an array it will be stringified to JSON.
30 // the argument is an object or an array it will be stringified to JSON.
31 for (var i=0; i < len; arg = arguments[i++]) {
31 for (var i=0; i < len; arg = arguments[i++]) {
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
34 }
34 }
35 return str;
35 return str;
36 }
36 }
37
37
38 // Save a reference of what may already exist under the property native.
38 // Save a reference of what may already exist under the property native.
39 // Allows for doing something like: if("".format.native) { /* use native */ }
39 // Allows for doing something like: if("".format.native) { /* use native */ }
40 format.native = String.prototype.format;
40 format.native = String.prototype.format;
41
41
42 // Replace the prototype property
42 // Replace the prototype property
43 return format;
43 return format;
44
44
45 }();
45 }();
46
46
47 String.prototype.strip = function(char) {
47 String.prototype.strip = function(char) {
48 if(char === undefined){
48 if(char === undefined){
49 char = '\\s';
49 char = '\\s';
50 }
50 }
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
52 }
52 }
53 String.prototype.lstrip = function(char) {
53 String.prototype.lstrip = function(char) {
54 if(char === undefined){
54 if(char === undefined){
55 char = '\\s';
55 char = '\\s';
56 }
56 }
57 return this.replace(new RegExp('^'+char+'+'),'');
57 return this.replace(new RegExp('^'+char+'+'),'');
58 }
58 }
59 String.prototype.rstrip = function(char) {
59 String.prototype.rstrip = function(char) {
60 if(char === undefined){
60 if(char === undefined){
61 char = '\\s';
61 char = '\\s';
62 }
62 }
63 return this.replace(new RegExp(''+char+'+$'),'');
63 return this.replace(new RegExp(''+char+'+$'),'');
64 }
64 }
65
65
66
66
67 if(!Array.prototype.indexOf) {
67 if(!Array.prototype.indexOf) {
68 Array.prototype.indexOf = function(needle) {
68 Array.prototype.indexOf = function(needle) {
69 for(var i = 0; i < this.length; i++) {
69 for(var i = 0; i < this.length; i++) {
70 if(this[i] === needle) {
70 if(this[i] === needle) {
71 return i;
71 return i;
72 }
72 }
73 }
73 }
74 return -1;
74 return -1;
75 };
75 };
76 }
76 }
77
77
78 /**
78 /**
79 * SmartColorGenerator
79 * SmartColorGenerator
80 *
80 *
81 *usage::
81 *usage::
82 * var CG = new ColorGenerator();
82 * var CG = new ColorGenerator();
83 * var col = CG.getColor(key); //returns array of RGB
83 * var col = CG.getColor(key); //returns array of RGB
84 * 'rgb({0})'.format(col.join(',')
84 * 'rgb({0})'.format(col.join(',')
85 *
85 *
86 * @returns {ColorGenerator}
86 * @returns {ColorGenerator}
87 */
87 */
88 var ColorGenerator = function(){
88 var ColorGenerator = function(){
89 this.GOLDEN_RATIO = 0.618033988749895;
89 this.GOLDEN_RATIO = 0.618033988749895;
90 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
90 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
91 this.HSV_1 = 0.75;//saturation
91 this.HSV_1 = 0.75;//saturation
92 this.HSV_2 = 0.95;
92 this.HSV_2 = 0.95;
93 this.color;
93 this.color;
94 this.cacheColorMap = {};
94 this.cacheColorMap = {};
95 };
95 };
96
96
97 ColorGenerator.prototype = {
97 ColorGenerator.prototype = {
98 getColor:function(key){
98 getColor:function(key){
99 if(this.cacheColorMap[key] !== undefined){
99 if(this.cacheColorMap[key] !== undefined){
100 return this.cacheColorMap[key];
100 return this.cacheColorMap[key];
101 }
101 }
102 else{
102 else{
103 this.cacheColorMap[key] = this.generateColor();
103 this.cacheColorMap[key] = this.generateColor();
104 return this.cacheColorMap[key];
104 return this.cacheColorMap[key];
105 }
105 }
106 },
106 },
107 _hsvToRgb:function(h,s,v){
107 _hsvToRgb:function(h,s,v){
108 if (s == 0.0)
108 if (s == 0.0)
109 return [v, v, v];
109 return [v, v, v];
110 i = parseInt(h * 6.0)
110 i = parseInt(h * 6.0)
111 f = (h * 6.0) - i
111 f = (h * 6.0) - i
112 p = v * (1.0 - s)
112 p = v * (1.0 - s)
113 q = v * (1.0 - s * f)
113 q = v * (1.0 - s * f)
114 t = v * (1.0 - s * (1.0 - f))
114 t = v * (1.0 - s * (1.0 - f))
115 i = i % 6
115 i = i % 6
116 if (i == 0)
116 if (i == 0)
117 return [v, t, p]
117 return [v, t, p]
118 if (i == 1)
118 if (i == 1)
119 return [q, v, p]
119 return [q, v, p]
120 if (i == 2)
120 if (i == 2)
121 return [p, v, t]
121 return [p, v, t]
122 if (i == 3)
122 if (i == 3)
123 return [p, q, v]
123 return [p, q, v]
124 if (i == 4)
124 if (i == 4)
125 return [t, p, v]
125 return [t, p, v]
126 if (i == 5)
126 if (i == 5)
127 return [v, p, q]
127 return [v, p, q]
128 },
128 },
129 generateColor:function(){
129 generateColor:function(){
130 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
130 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
131 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
131 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
132 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
132 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
133 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
133 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
134 function toRgb(v){
134 function toRgb(v){
135 return ""+parseInt(v*256)
135 return ""+parseInt(v*256)
136 }
136 }
137 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
137 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
138
138
139 }
139 }
140 }
140 }
141
141
142
142
143
143
144
144
145
145
146 /**
146 /**
147 * GLOBAL YUI Shortcuts
147 * GLOBAL YUI Shortcuts
148 */
148 */
149 var YUC = YAHOO.util.Connect;
149 var YUC = YAHOO.util.Connect;
150 var YUD = YAHOO.util.Dom;
150 var YUD = YAHOO.util.Dom;
151 var YUE = YAHOO.util.Event;
151 var YUE = YAHOO.util.Event;
152 var YUQ = YAHOO.util.Selector.query;
152 var YUQ = YAHOO.util.Selector.query;
153
153
154 // defines if push state is enabled for this browser ?
154 // defines if push state is enabled for this browser ?
155 var push_state_enabled = Boolean(
155 var push_state_enabled = Boolean(
156 window.history && window.history.pushState && window.history.replaceState
156 window.history && window.history.pushState && window.history.replaceState
157 && !( /* disable for versions of iOS before version 4.3 (8F190) */
157 && !( /* disable for versions of iOS before version 4.3 (8F190) */
158 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
158 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
159 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
159 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
160 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
160 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
161 )
161 )
162 );
162 );
163
163
164 var _run_callbacks = function(callbacks){
164 var _run_callbacks = function(callbacks){
165 if (callbacks !== undefined){
165 if (callbacks !== undefined){
166 var _l = callbacks.length;
166 var _l = callbacks.length;
167 for (var i=0;i<_l;i++){
167 for (var i=0;i<_l;i++){
168 var func = callbacks[i];
168 var func = callbacks[i];
169 if(typeof(func)=='function'){
169 if(typeof(func)=='function'){
170 try{
170 try{
171 func();
171 func();
172 }catch (err){};
172 }catch (err){};
173 }
173 }
174 }
174 }
175 }
175 }
176 }
176 }
177
177
178 /**
178 /**
179 * Partial Ajax Implementation
179 * Partial Ajax Implementation
180 *
180 *
181 * @param url: defines url to make partial request
181 * @param url: defines url to make partial request
182 * @param container: defines id of container to input partial result
182 * @param container: defines id of container to input partial result
183 * @param s_call: success callback function that takes o as arg
183 * @param s_call: success callback function that takes o as arg
184 * o.tId
184 * o.tId
185 * o.status
185 * o.status
186 * o.statusText
186 * o.statusText
187 * o.getResponseHeader[ ]
187 * o.getResponseHeader[ ]
188 * o.getAllResponseHeaders
188 * o.getAllResponseHeaders
189 * o.responseText
189 * o.responseText
190 * o.responseXML
190 * o.responseXML
191 * o.argument
191 * o.argument
192 * @param f_call: failure callback
192 * @param f_call: failure callback
193 * @param args arguments
193 * @param args arguments
194 */
194 */
195 function ypjax(url,container,s_call,f_call,args){
195 function ypjax(url,container,s_call,f_call,args){
196 var method='GET';
196 var method='GET';
197 if(args===undefined){
197 if(args===undefined){
198 args=null;
198 args=null;
199 }
199 }
200
200
201 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
201 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
202 YUC.initHeader('X-PARTIAL-XHR',true);
202 YUC.initHeader('X-PARTIAL-XHR',true);
203
203
204 // wrapper of passed callback
204 // wrapper of passed callback
205 var s_wrapper = (function(o){
205 var s_wrapper = (function(o){
206 return function(o){
206 return function(o){
207 YUD.get(container).innerHTML=o.responseText;
207 YUD.get(container).innerHTML=o.responseText;
208 YUD.setStyle(container,'opacity','1.0');
208 YUD.setStyle(container,'opacity','1.0');
209 //execute the given original callback
209 //execute the given original callback
210 if (s_call !== undefined){
210 if (s_call !== undefined){
211 s_call(o);
211 s_call(o);
212 }
212 }
213 }
213 }
214 })()
214 })()
215 YUD.setStyle(container,'opacity','0.3');
215 YUD.setStyle(container,'opacity','0.3');
216 YUC.asyncRequest(method,url,{
216 YUC.asyncRequest(method,url,{
217 success:s_wrapper,
217 success:s_wrapper,
218 failure:function(o){
218 failure:function(o){
219 console.log(o);
219 console.log(o);
220 YUD.get(container).innerHTML='ERROR '+o.status;
220 YUD.get(container).innerHTML='ERROR '+o.status;
221 YUD.setStyle(container,'opacity','1.0');
221 YUD.setStyle(container,'opacity','1.0');
222 YUD.setStyle(container,'color','red');
222 YUD.setStyle(container,'color','red');
223 }
223 }
224 },args);
224 },args);
225
225
226 };
226 };
227
227
228 var ajaxPOST = function(url,postData,success) {
228 var ajaxPOST = function(url,postData,success) {
229 // Set special header for ajax == HTTP_X_PARTIAL_XHR
229 // Set special header for ajax == HTTP_X_PARTIAL_XHR
230 YUC.initHeader('X-PARTIAL-XHR',true);
230 YUC.initHeader('X-PARTIAL-XHR',true);
231
231
232 var toQueryString = function(o) {
232 var toQueryString = function(o) {
233 if(typeof o !== 'object') {
233 if(typeof o !== 'object') {
234 return false;
234 return false;
235 }
235 }
236 var _p, _qs = [];
236 var _p, _qs = [];
237 for(_p in o) {
237 for(_p in o) {
238 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
238 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
239 }
239 }
240 return _qs.join('&');
240 return _qs.join('&');
241 };
241 };
242
242
243 var sUrl = url;
243 var sUrl = url;
244 var callback = {
244 var callback = {
245 success: success,
245 success: success,
246 failure: function (o) {
246 failure: function (o) {
247 alert("error");
247 alert("error");
248 },
248 },
249 };
249 };
250 var postData = toQueryString(postData);
250 var postData = toQueryString(postData);
251 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
251 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
252 return request;
252 return request;
253 };
253 };
254
254
255
255
256 /**
256 /**
257 * tooltip activate
257 * tooltip activate
258 */
258 */
259 var tooltip_activate = function(){
259 var tooltip_activate = function(){
260 function toolTipsId(){
260 function toolTipsId(){
261 var ids = [];
261 var ids = [];
262 var tts = YUQ('.tooltip');
262 var tts = YUQ('.tooltip');
263 for (var i = 0; i < tts.length; i++) {
263 for (var i = 0; i < tts.length; i++) {
264 // if element doesn't not have and id
264 // if element doesn't not have and id
265 // autogenerate one for tooltip
265 // autogenerate one for tooltip
266 if (!tts[i].id){
266 if (!tts[i].id){
267 tts[i].id='tt'+((i*100)+tts.length);
267 tts[i].id='tt'+((i*100)+tts.length);
268 }
268 }
269 ids.push(tts[i].id);
269 ids.push(tts[i].id);
270 }
270 }
271 return ids
271 return ids
272 };
272 };
273 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
273 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
274 context: [[toolTipsId()],"tl","bl",null,[0,5]],
274 context: [[toolTipsId()],"tl","bl",null,[0,5]],
275 monitorresize:false,
275 monitorresize:false,
276 xyoffset :[0,0],
276 xyoffset :[0,0],
277 autodismissdelay:300000,
277 autodismissdelay:300000,
278 hidedelay:5,
278 hidedelay:5,
279 showdelay:20,
279 showdelay:20,
280 });
280 });
281 };
281 };
282
282
283 /**
283 /**
284 * show more
284 * show more
285 */
285 */
286 var show_more_event = function(){
286 var show_more_event = function(){
287 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
287 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
288 var el = e.target;
288 var el = e.target;
289 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
289 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
290 YUD.setStyle(el.parentNode,'display','none');
290 YUD.setStyle(el.parentNode,'display','none');
291 });
291 });
292 };
292 };
293
293
294
294
295 /**
295 /**
296 * Quick filter widget
296 * Quick filter widget
297 *
297 *
298 * @param target: filter input target
298 * @param target: filter input target
299 * @param nodes: list of nodes in html we want to filter.
299 * @param nodes: list of nodes in html we want to filter.
300 * @param display_element function that takes current node from nodes and
300 * @param display_element function that takes current node from nodes and
301 * does hide or show based on the node
301 * does hide or show based on the node
302 *
302 *
303 */
303 */
304 var q_filter = function(target,nodes,display_element){
304 var q_filter = function(target,nodes,display_element){
305
305
306 var nodes = nodes;
306 var nodes = nodes;
307 var q_filter_field = YUD.get(target);
307 var q_filter_field = YUD.get(target);
308 var F = YAHOO.namespace(target);
308 var F = YAHOO.namespace(target);
309
309
310 YUE.on(q_filter_field,'click',function(){
310 YUE.on(q_filter_field,'click',function(){
311 q_filter_field.value = '';
311 q_filter_field.value = '';
312 });
312 });
313
313
314 YUE.on(q_filter_field,'keyup',function(e){
314 YUE.on(q_filter_field,'keyup',function(e){
315 clearTimeout(F.filterTimeout);
315 clearTimeout(F.filterTimeout);
316 F.filterTimeout = setTimeout(F.updateFilter,600);
316 F.filterTimeout = setTimeout(F.updateFilter,600);
317 });
317 });
318
318
319 F.filterTimeout = null;
319 F.filterTimeout = null;
320
320
321 var show_node = function(node){
321 var show_node = function(node){
322 YUD.setStyle(node,'display','')
322 YUD.setStyle(node,'display','')
323 }
323 }
324 var hide_node = function(node){
324 var hide_node = function(node){
325 YUD.setStyle(node,'display','none');
325 YUD.setStyle(node,'display','none');
326 }
326 }
327
327
328 F.updateFilter = function() {
328 F.updateFilter = function() {
329 // Reset timeout
329 // Reset timeout
330 F.filterTimeout = null;
330 F.filterTimeout = null;
331
331
332 var obsolete = [];
332 var obsolete = [];
333
333
334 var req = q_filter_field.value.toLowerCase();
334 var req = q_filter_field.value.toLowerCase();
335
335
336 var l = nodes.length;
336 var l = nodes.length;
337 var i;
337 var i;
338 var showing = 0;
338 var showing = 0;
339
339
340 for (i=0;i<l;i++ ){
340 for (i=0;i<l;i++ ){
341 var n = nodes[i];
341 var n = nodes[i];
342 var target_element = display_element(n)
342 var target_element = display_element(n)
343 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
343 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
344 hide_node(target_element);
344 hide_node(target_element);
345 }
345 }
346 else{
346 else{
347 show_node(target_element);
347 show_node(target_element);
348 showing+=1;
348 showing+=1;
349 }
349 }
350 }
350 }
351
351
352 // if repo_count is set update the number
352 // if repo_count is set update the number
353 var cnt = YUD.get('repo_count');
353 var cnt = YUD.get('repo_count');
354 if(cnt){
354 if(cnt){
355 YUD.get('repo_count').innerHTML = showing;
355 YUD.get('repo_count').innerHTML = showing;
356 }
356 }
357
357
358 }
358 }
359 };
359 };
360
360
361 var tableTr = function(cls,body){
361 var tableTr = function(cls,body){
362 var tr = document.createElement('tr');
362 var tr = document.createElement('tr');
363 YUD.addClass(tr, cls);
363 YUD.addClass(tr, cls);
364
364
365
365
366 var cont = new YAHOO.util.Element(body);
366 var cont = new YAHOO.util.Element(body);
367 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
367 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
368 tr.id = 'comment-tr-{0}'.format(comment_id);
368 tr.id = 'comment-tr-{0}'.format(comment_id);
369 tr.innerHTML = '<td class="lineno-inline new-inline"></td>'+
369 tr.innerHTML = '<td class="lineno-inline new-inline"></td>'+
370 '<td class="lineno-inline old-inline"></td>'+
370 '<td class="lineno-inline old-inline"></td>'+
371 '<td>{0}</td>'.format(body);
371 '<td>{0}</td>'.format(body);
372 return tr;
372 return tr;
373 };
373 };
374
374
375 /** comments **/
375 /** comments **/
376 var removeInlineForm = function(form) {
376 var removeInlineForm = function(form) {
377 form.parentNode.removeChild(form);
377 form.parentNode.removeChild(form);
378 };
378 };
379
379
380 var createInlineForm = function(parent_tr, f_path, line) {
380 var createInlineForm = function(parent_tr, f_path, line) {
381 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
381 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
382 tmpl = tmpl.format(f_path, line);
382 tmpl = tmpl.format(f_path, line);
383 var form = tableTr('comment-form-inline',tmpl)
383 var form = tableTr('comment-form-inline',tmpl)
384
384
385 // create event for hide button
385 // create event for hide button
386 form = new YAHOO.util.Element(form);
386 form = new YAHOO.util.Element(form);
387 var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
387 var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
388 form_hide_button.on('click', function(e) {
388 form_hide_button.on('click', function(e) {
389 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
389 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
390 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
390 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
391 YUD.setStyle(newtr.nextElementSibling,'display','');
391 YUD.setStyle(newtr.nextElementSibling,'display','');
392 }
392 }
393 removeInlineForm(newtr);
393 removeInlineForm(newtr);
394 YUD.removeClass(parent_tr, 'form-open');
394 YUD.removeClass(parent_tr, 'form-open');
395
395
396 });
396 });
397
397
398 return form
398 return form
399 };
399 };
400
400
401 /**
401 /**
402 * Inject inline comment for on given TR this tr should be always an .line
402 * Inject inline comment for on given TR this tr should be always an .line
403 * tr containing the line. Code will detect comment, and always put the comment
403 * tr containing the line. Code will detect comment, and always put the comment
404 * block at the very bottom
404 * block at the very bottom
405 */
405 */
406 var injectInlineForm = function(tr){
406 var injectInlineForm = function(tr){
407 if(!YUD.hasClass(tr, 'line')){
407 if(!YUD.hasClass(tr, 'line')){
408 return
408 return
409 }
409 }
410 var submit_url = AJAX_COMMENT_URL;
410 var submit_url = AJAX_COMMENT_URL;
411 var _td = tr.getElementsByClassName('code')[0];
411 var _td = tr.getElementsByClassName('code')[0];
412 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
412 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
413 return
413 return
414 }
414 }
415 YUD.addClass(tr,'form-open');
415 YUD.addClass(tr,'form-open');
416 var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
416 var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
417 var f_path = YUD.getAttribute(node,'path');
417 var f_path = YUD.getAttribute(node,'path');
418 var lineno = getLineNo(tr);
418 var lineno = getLineNo(tr);
419 var form = createInlineForm(tr, f_path, lineno, submit_url);
419 var form = createInlineForm(tr, f_path, lineno, submit_url);
420
420
421 var parent = tr;
421 var parent = tr;
422 while (1){
422 while (1){
423 var n = parent.nextElementSibling;
423 var n = parent.nextElementSibling;
424 // next element are comments !
424 // next element are comments !
425 if(YUD.hasClass(n,'inline-comments')){
425 if(YUD.hasClass(n,'inline-comments')){
426 parent = n;
426 parent = n;
427 }
427 }
428 else{
428 else{
429 break;
429 break;
430 }
430 }
431 }
431 }
432 YUD.insertAfter(form,parent);
432 YUD.insertAfter(form,parent);
433
433
434 var f = YUD.get(form);
434 var f = YUD.get(form);
435
435
436 var overlay = f.getElementsByClassName('overlay')[0];
436 var overlay = f.getElementsByClassName('overlay')[0];
437 var _form = f.getElementsByClassName('inline-form')[0];
437 var _form = f.getElementsByClassName('inline-form')[0];
438
438
439 form.on('submit',function(e){
439 form.on('submit',function(e){
440 YUE.preventDefault(e);
440 YUE.preventDefault(e);
441
441
442 //ajax submit
442 //ajax submit
443 var text = YUD.get('text_'+lineno).value;
443 var text = YUD.get('text_'+lineno).value;
444 var postData = {
444 var postData = {
445 'text':text,
445 'text':text,
446 'f_path':f_path,
446 'f_path':f_path,
447 'line':lineno
447 'line':lineno
448 };
448 };
449
449
450 if(lineno === undefined){
450 if(lineno === undefined){
451 alert('missing line !');
451 alert('missing line !');
452 return
452 return
453 }
453 }
454 if(f_path === undefined){
454 if(f_path === undefined){
455 alert('missing file path !');
455 alert('missing file path !');
456 return
456 return
457 }
457 }
458
458
459 if(text == ""){
459 if(text == ""){
460 return
460 return
461 }
461 }
462
462
463 var success = function(o){
463 var success = function(o){
464 YUD.removeClass(tr, 'form-open');
464 YUD.removeClass(tr, 'form-open');
465 removeInlineForm(f);
465 removeInlineForm(f);
466 var json_data = JSON.parse(o.responseText);
466 var json_data = JSON.parse(o.responseText);
467 renderInlineComment(json_data);
467 renderInlineComment(json_data);
468 };
468 };
469
469
470 if (YUD.hasClass(overlay,'overlay')){
470 if (YUD.hasClass(overlay,'overlay')){
471 var w = _form.offsetWidth;
471 var w = _form.offsetWidth;
472 var h = _form.offsetHeight;
472 var h = _form.offsetHeight;
473 YUD.setStyle(overlay,'width',w+'px');
473 YUD.setStyle(overlay,'width',w+'px');
474 YUD.setStyle(overlay,'height',h+'px');
474 YUD.setStyle(overlay,'height',h+'px');
475 }
475 }
476 YUD.addClass(overlay, 'submitting');
476 YUD.addClass(overlay, 'submitting');
477
477
478 ajaxPOST(submit_url, postData, success);
478 ajaxPOST(submit_url, postData, success);
479 });
479 });
480
480
481 setTimeout(function(){
481 setTimeout(function(){
482 // callbacks
482 // callbacks
483 tooltip_activate();
483 tooltip_activate();
484 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
484 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
485 _USERS_AC_DATA, _GROUPS_AC_DATA);
485 _USERS_AC_DATA, _GROUPS_AC_DATA);
486 YUD.get('text_'+lineno).focus();
486 YUD.get('text_'+lineno).focus();
487 },10)
487 },10)
488 };
488 };
489
489
490 var deleteComment = function(comment_id){
490 var deleteComment = function(comment_id){
491 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
491 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
492 var postData = {'_method':'delete'};
492 var postData = {'_method':'delete'};
493 var success = function(o){
493 var success = function(o){
494 var n = YUD.get('comment-tr-'+comment_id);
494 var n = YUD.get('comment-tr-'+comment_id);
495 var root = n.previousElementSibling.previousElementSibling;
495 var root = n.previousElementSibling.previousElementSibling;
496 n.parentNode.removeChild(n);
496 n.parentNode.removeChild(n);
497
497
498 // scann nodes, and attach add button to last one
498 // scann nodes, and attach add button to last one
499 placeAddButton(root);
499 placeAddButton(root);
500 }
500 }
501 ajaxPOST(url,postData,success);
501 ajaxPOST(url,postData,success);
502 }
502 }
503
503
504 var updateReviewers = function(reviewers_ids){
504 var updateReviewers = function(reviewers_ids){
505 var url = AJAX_UPDATE_PULLREQUEST;
505 var url = AJAX_UPDATE_PULLREQUEST;
506 var postData = {'_method':'put',
506 var postData = {'_method':'put',
507 'reviewers_ids': reviewers_ids};
507 'reviewers_ids': reviewers_ids};
508 var success = function(o){
508 var success = function(o){
509 window.location.reload();
509 window.location.reload();
510 }
510 }
511 ajaxPOST(url,postData,success);
511 ajaxPOST(url,postData,success);
512 }
512 }
513
513
514 var createInlineAddButton = function(tr){
514 var createInlineAddButton = function(tr){
515
515
516 var label = TRANSLATION_MAP['add another comment'];
516 var label = TRANSLATION_MAP['add another comment'];
517
517
518 var html_el = document.createElement('div');
518 var html_el = document.createElement('div');
519 YUD.addClass(html_el, 'add-comment');
519 YUD.addClass(html_el, 'add-comment');
520 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
520 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
521
521
522 var add = new YAHOO.util.Element(html_el);
522 var add = new YAHOO.util.Element(html_el);
523 add.on('click', function(e) {
523 add.on('click', function(e) {
524 injectInlineForm(tr);
524 injectInlineForm(tr);
525 });
525 });
526 return add;
526 return add;
527 };
527 };
528
528
529 var getLineNo = function(tr) {
529 var getLineNo = function(tr) {
530 var line;
530 var line;
531 var o = tr.children[0].id.split('_');
531 var o = tr.children[0].id.split('_');
532 var n = tr.children[1].id.split('_');
532 var n = tr.children[1].id.split('_');
533
533
534 if (n.length >= 2) {
534 if (n.length >= 2) {
535 line = n[n.length-1];
535 line = n[n.length-1];
536 } else if (o.length >= 2) {
536 } else if (o.length >= 2) {
537 line = o[o.length-1];
537 line = o[o.length-1];
538 }
538 }
539
539
540 return line
540 return line
541 };
541 };
542
542
543 var placeAddButton = function(target_tr){
543 var placeAddButton = function(target_tr){
544 if(!target_tr){
544 if(!target_tr){
545 return
545 return
546 }
546 }
547 var last_node = target_tr;
547 var last_node = target_tr;
548 //scann
548 //scann
549 while (1){
549 while (1){
550 var n = last_node.nextElementSibling;
550 var n = last_node.nextElementSibling;
551 // next element are comments !
551 // next element are comments !
552 if(YUD.hasClass(n,'inline-comments')){
552 if(YUD.hasClass(n,'inline-comments')){
553 last_node = n;
553 last_node = n;
554 //also remove the comment button from previos
554 //also remove the comment button from previos
555 var comment_add_buttons = last_node.getElementsByClassName('add-comment');
555 var comment_add_buttons = last_node.getElementsByClassName('add-comment');
556 for(var i=0;i<comment_add_buttons.length;i++){
556 for(var i=0;i<comment_add_buttons.length;i++){
557 var b = comment_add_buttons[i];
557 var b = comment_add_buttons[i];
558 b.parentNode.removeChild(b);
558 b.parentNode.removeChild(b);
559 }
559 }
560 }
560 }
561 else{
561 else{
562 break;
562 break;
563 }
563 }
564 }
564 }
565
565
566 var add = createInlineAddButton(target_tr);
566 var add = createInlineAddButton(target_tr);
567 // get the comment div
567 // get the comment div
568 var comment_block = last_node.getElementsByClassName('comment')[0];
568 var comment_block = last_node.getElementsByClassName('comment')[0];
569 // attach add button
569 // attach add button
570 YUD.insertAfter(add,comment_block);
570 YUD.insertAfter(add,comment_block);
571 }
571 }
572
572
573 /**
573 /**
574 * Places the inline comment into the changeset block in proper line position
574 * Places the inline comment into the changeset block in proper line position
575 */
575 */
576 var placeInline = function(target_container,lineno,html){
576 var placeInline = function(target_container,lineno,html){
577 var lineid = "{0}_{1}".format(target_container,lineno);
577 var lineid = "{0}_{1}".format(target_container,lineno);
578 var target_line = YUD.get(lineid);
578 var target_line = YUD.get(lineid);
579 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
579 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
580
580
581 // check if there are comments already !
581 // check if there are comments already !
582 var parent = target_line.parentNode;
582 var parent = target_line.parentNode;
583 var root_parent = parent;
583 var root_parent = parent;
584 while (1){
584 while (1){
585 var n = parent.nextElementSibling;
585 var n = parent.nextElementSibling;
586 // next element are comments !
586 // next element are comments !
587 if(YUD.hasClass(n,'inline-comments')){
587 if(YUD.hasClass(n,'inline-comments')){
588 parent = n;
588 parent = n;
589 }
589 }
590 else{
590 else{
591 break;
591 break;
592 }
592 }
593 }
593 }
594 // put in the comment at the bottom
594 // put in the comment at the bottom
595 YUD.insertAfter(comment,parent);
595 YUD.insertAfter(comment,parent);
596
596
597 // scann nodes, and attach add button to last one
597 // scann nodes, and attach add button to last one
598 placeAddButton(root_parent);
598 placeAddButton(root_parent);
599
599
600 return target_line;
600 return target_line;
601 }
601 }
602
602
603 /**
603 /**
604 * make a single inline comment and place it inside
604 * make a single inline comment and place it inside
605 */
605 */
606 var renderInlineComment = function(json_data){
606 var renderInlineComment = function(json_data){
607 try{
607 try{
608 var html = json_data['rendered_text'];
608 var html = json_data['rendered_text'];
609 var lineno = json_data['line_no'];
609 var lineno = json_data['line_no'];
610 var target_id = json_data['target_id'];
610 var target_id = json_data['target_id'];
611 placeInline(target_id, lineno, html);
611 placeInline(target_id, lineno, html);
612
612
613 }catch(e){
613 }catch(e){
614 console.log(e);
614 console.log(e);
615 }
615 }
616 }
616 }
617
617
618 /**
618 /**
619 * Iterates over all the inlines, and places them inside proper blocks of data
619 * Iterates over all the inlines, and places them inside proper blocks of data
620 */
620 */
621 var renderInlineComments = function(file_comments){
621 var renderInlineComments = function(file_comments){
622 for (f in file_comments){
622 for (f in file_comments){
623 // holding all comments for a FILE
623 // holding all comments for a FILE
624 var box = file_comments[f];
624 var box = file_comments[f];
625
625
626 var target_id = YUD.getAttribute(box,'target_id');
626 var target_id = YUD.getAttribute(box,'target_id');
627 // actually comments with line numbers
627 // actually comments with line numbers
628 var comments = box.children;
628 var comments = box.children;
629 for(var i=0; i<comments.length; i++){
629 for(var i=0; i<comments.length; i++){
630 var data = {
630 var data = {
631 'rendered_text': comments[i].outerHTML,
631 'rendered_text': comments[i].outerHTML,
632 'line_no': YUD.getAttribute(comments[i],'line'),
632 'line_no': YUD.getAttribute(comments[i],'line'),
633 'target_id': target_id
633 'target_id': target_id
634 }
634 }
635 renderInlineComment(data);
635 renderInlineComment(data);
636 }
636 }
637 }
637 }
638 }
638 }
639
639
640 var removeReviewer = function(reviewer_id){
640 var removeReviewer = function(reviewer_id){
641 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
641 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
642 if (el.parentNode !== undefined){
642 if (el.parentNode !== undefined){
643 el.parentNode.removeChild(el);
643 el.parentNode.removeChild(el);
644 }
644 }
645 }
645 }
646
646
647 var fileBrowserListeners = function(current_url, node_list_url, url_base){
647 var fileBrowserListeners = function(current_url, node_list_url, url_base){
648
648
649 var current_url_branch = +"?branch=__BRANCH__";
649 var current_url_branch = +"?branch=__BRANCH__";
650 var url = url_base;
650 var url = url_base;
651 var node_url = node_list_url;
651 var node_url = node_list_url;
652
652
653 YUE.on('stay_at_branch','click',function(e){
653 YUE.on('stay_at_branch','click',function(e){
654 if(e.target.checked){
654 if(e.target.checked){
655 var uri = current_url_branch;
655 var uri = current_url_branch;
656 uri = uri.replace('__BRANCH__',e.target.value);
656 uri = uri.replace('__BRANCH__',e.target.value);
657 window.location = uri;
657 window.location = uri;
658 }
658 }
659 else{
659 else{
660 window.location = current_url;
660 window.location = current_url;
661 }
661 }
662 })
662 })
663
663
664 var n_filter = YUD.get('node_filter');
664 var n_filter = YUD.get('node_filter');
665 var F = YAHOO.namespace('node_filter');
665 var F = YAHOO.namespace('node_filter');
666
666
667 F.filterTimeout = null;
667 F.filterTimeout = null;
668 var nodes = null;
668 var nodes = null;
669
669
670 F.initFilter = function(){
670 F.initFilter = function(){
671 YUD.setStyle('node_filter_box_loading','display','');
671 YUD.setStyle('node_filter_box_loading','display','');
672 YUD.setStyle('search_activate_id','display','none');
672 YUD.setStyle('search_activate_id','display','none');
673 YUD.setStyle('add_node_id','display','none');
673 YUD.setStyle('add_node_id','display','none');
674 YUC.initHeader('X-PARTIAL-XHR',true);
674 YUC.initHeader('X-PARTIAL-XHR',true);
675 YUC.asyncRequest('GET',url,{
675 YUC.asyncRequest('GET',url,{
676 success:function(o){
676 success:function(o){
677 nodes = JSON.parse(o.responseText).nodes;
677 nodes = JSON.parse(o.responseText).nodes;
678 YUD.setStyle('node_filter_box_loading','display','none');
678 YUD.setStyle('node_filter_box_loading','display','none');
679 YUD.setStyle('node_filter_box','display','');
679 YUD.setStyle('node_filter_box','display','');
680 n_filter.focus();
680 n_filter.focus();
681 if(YUD.hasClass(n_filter,'init')){
681 if(YUD.hasClass(n_filter,'init')){
682 n_filter.value = '';
682 n_filter.value = '';
683 YUD.removeClass(n_filter,'init');
683 YUD.removeClass(n_filter,'init');
684 }
684 }
685 },
685 },
686 failure:function(o){
686 failure:function(o){
687 console.log('failed to load');
687 console.log('failed to load');
688 }
688 }
689 },null);
689 },null);
690 }
690 }
691
691
692 F.updateFilter = function(e) {
692 F.updateFilter = function(e) {
693
693
694 return function(){
694 return function(){
695 // Reset timeout
695 // Reset timeout
696 F.filterTimeout = null;
696 F.filterTimeout = null;
697 var query = e.target.value.toLowerCase();
697 var query = e.target.value.toLowerCase();
698 var match = [];
698 var match = [];
699 var matches = 0;
699 var matches = 0;
700 var matches_max = 20;
700 var matches_max = 20;
701 if (query != ""){
701 if (query != ""){
702 for(var i=0;i<nodes.length;i++){
702 for(var i=0;i<nodes.length;i++){
703
703
704 var pos = nodes[i].name.toLowerCase().indexOf(query)
704 var pos = nodes[i].name.toLowerCase().indexOf(query)
705 if(query && pos != -1){
705 if(query && pos != -1){
706
706
707 matches++
707 matches++
708 //show only certain amount to not kill browser
708 //show only certain amount to not kill browser
709 if (matches > matches_max){
709 if (matches > matches_max){
710 break;
710 break;
711 }
711 }
712
712
713 var n = nodes[i].name;
713 var n = nodes[i].name;
714 var t = nodes[i].type;
714 var t = nodes[i].type;
715 var n_hl = n.substring(0,pos)
715 var n_hl = n.substring(0,pos)
716 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
716 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
717 +n.substring(pos+query.length)
717 +n.substring(pos+query.length)
718 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url.replace('__FPATH__',n),n_hl));
718 node_url = node_url.replace('__FPATH__',n);
719 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url,n_hl));
719 }
720 }
720 if(match.length >= matches_max){
721 if(match.length >= matches_max){
721 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['search truncated']));
722 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['search truncated']));
722 }
723 }
723 }
724 }
724 }
725 }
725 if(query != ""){
726 if(query != ""){
726 YUD.setStyle('tbody','display','none');
727 YUD.setStyle('tbody','display','none');
727 YUD.setStyle('tbody_filtered','display','');
728 YUD.setStyle('tbody_filtered','display','');
728
729
729 if (match.length==0){
730 if (match.length==0){
730 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['no matching files']));
731 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['no matching files']));
731 }
732 }
732
733
733 YUD.get('tbody_filtered').innerHTML = match.join("");
734 YUD.get('tbody_filtered').innerHTML = match.join("");
734 }
735 }
735 else{
736 else{
736 YUD.setStyle('tbody','display','');
737 YUD.setStyle('tbody','display','');
737 YUD.setStyle('tbody_filtered','display','none');
738 YUD.setStyle('tbody_filtered','display','none');
738 }
739 }
739
740
740 }
741 }
741 };
742 };
742
743
743 YUE.on(YUD.get('filter_activate'),'click',function(){
744 YUE.on(YUD.get('filter_activate'),'click',function(){
744 F.initFilter();
745 F.initFilter();
745 })
746 })
746 YUE.on(n_filter,'click',function(){
747 YUE.on(n_filter,'click',function(){
747 if(YUD.hasClass(n_filter,'init')){
748 if(YUD.hasClass(n_filter,'init')){
748 n_filter.value = '';
749 n_filter.value = '';
749 YUD.removeClass(n_filter,'init');
750 YUD.removeClass(n_filter,'init');
750 }
751 }
751 });
752 });
752 YUE.on(n_filter,'keyup',function(e){
753 YUE.on(n_filter,'keyup',function(e){
753 clearTimeout(F.filterTimeout);
754 clearTimeout(F.filterTimeout);
754 F.filterTimeout = setTimeout(F.updateFilter(e),600);
755 F.filterTimeout = setTimeout(F.updateFilter(e),600);
755 });
756 });
756 };
757 };
757
758
758
759
759 var initCodeMirror = function(textAreadId,resetUrl){
760 var initCodeMirror = function(textAreadId,resetUrl){
760 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
761 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
761 mode: "null",
762 mode: "null",
762 lineNumbers:true
763 lineNumbers:true
763 });
764 });
764 YUE.on('reset','click',function(e){
765 YUE.on('reset','click',function(e){
765 window.location=resetUrl
766 window.location=resetUrl
766 });
767 });
767
768
768 YUE.on('file_enable','click',function(){
769 YUE.on('file_enable','click',function(){
769 YUD.setStyle('editor_container','display','');
770 YUD.setStyle('editor_container','display','');
770 YUD.setStyle('upload_file_container','display','none');
771 YUD.setStyle('upload_file_container','display','none');
771 YUD.setStyle('filename_container','display','');
772 YUD.setStyle('filename_container','display','');
772 });
773 });
773
774
774 YUE.on('upload_file_enable','click',function(){
775 YUE.on('upload_file_enable','click',function(){
775 YUD.setStyle('editor_container','display','none');
776 YUD.setStyle('editor_container','display','none');
776 YUD.setStyle('upload_file_container','display','');
777 YUD.setStyle('upload_file_container','display','');
777 YUD.setStyle('filename_container','display','none');
778 YUD.setStyle('filename_container','display','none');
778 });
779 });
779 };
780 };
780
781
781
782
782
783
783 var getIdentNode = function(n){
784 var getIdentNode = function(n){
784 //iterate thru nodes untill matched interesting node !
785 //iterate thru nodes untill matched interesting node !
785
786
786 if (typeof n == 'undefined'){
787 if (typeof n == 'undefined'){
787 return -1
788 return -1
788 }
789 }
789
790
790 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
791 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
791 return n
792 return n
792 }
793 }
793 else{
794 else{
794 return getIdentNode(n.parentNode);
795 return getIdentNode(n.parentNode);
795 }
796 }
796 };
797 };
797
798
798 var getSelectionLink = function(selection_link_label) {
799 var getSelectionLink = function(selection_link_label) {
799 return function(){
800 return function(){
800 //get selection from start/to nodes
801 //get selection from start/to nodes
801 if (typeof window.getSelection != "undefined") {
802 if (typeof window.getSelection != "undefined") {
802 s = window.getSelection();
803 s = window.getSelection();
803
804
804 from = getIdentNode(s.anchorNode);
805 from = getIdentNode(s.anchorNode);
805 till = getIdentNode(s.focusNode);
806 till = getIdentNode(s.focusNode);
806
807
807 f_int = parseInt(from.id.replace('L',''));
808 f_int = parseInt(from.id.replace('L',''));
808 t_int = parseInt(till.id.replace('L',''));
809 t_int = parseInt(till.id.replace('L',''));
809
810
810 if (f_int > t_int){
811 if (f_int > t_int){
811 //highlight from bottom
812 //highlight from bottom
812 offset = -35;
813 offset = -35;
813 ranges = [t_int,f_int];
814 ranges = [t_int,f_int];
814
815
815 }
816 }
816 else{
817 else{
817 //highligth from top
818 //highligth from top
818 offset = 35;
819 offset = 35;
819 ranges = [f_int,t_int];
820 ranges = [f_int,t_int];
820 }
821 }
821
822
822 if (ranges[0] != ranges[1]){
823 if (ranges[0] != ranges[1]){
823 if(YUD.get('linktt') == null){
824 if(YUD.get('linktt') == null){
824 hl_div = document.createElement('div');
825 hl_div = document.createElement('div');
825 hl_div.id = 'linktt';
826 hl_div.id = 'linktt';
826 }
827 }
827 anchor = '#L'+ranges[0]+'-'+ranges[1];
828 anchor = '#L'+ranges[0]+'-'+ranges[1];
828 hl_div.innerHTML = '';
829 hl_div.innerHTML = '';
829 l = document.createElement('a');
830 l = document.createElement('a');
830 l.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
831 l.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
831 l.innerHTML = selection_link_label;
832 l.innerHTML = selection_link_label;
832 hl_div.appendChild(l);
833 hl_div.appendChild(l);
833
834
834 YUD.get('body').appendChild(hl_div);
835 YUD.get('body').appendChild(hl_div);
835
836
836 xy = YUD.getXY(till.id);
837 xy = YUD.getXY(till.id);
837
838
838 YUD.addClass('linktt','yui-tt');
839 YUD.addClass('linktt','yui-tt');
839 YUD.setStyle('linktt','top',xy[1]+offset+'px');
840 YUD.setStyle('linktt','top',xy[1]+offset+'px');
840 YUD.setStyle('linktt','left',xy[0]+'px');
841 YUD.setStyle('linktt','left',xy[0]+'px');
841 YUD.setStyle('linktt','visibility','visible');
842 YUD.setStyle('linktt','visibility','visible');
842 }
843 }
843 else{
844 else{
844 YUD.setStyle('linktt','visibility','hidden');
845 YUD.setStyle('linktt','visibility','hidden');
845 }
846 }
846 }
847 }
847 }
848 }
848 };
849 };
849
850
850 var deleteNotification = function(url, notification_id,callbacks){
851 var deleteNotification = function(url, notification_id,callbacks){
851 var callback = {
852 var callback = {
852 success:function(o){
853 success:function(o){
853 var obj = YUD.get(String("notification_"+notification_id));
854 var obj = YUD.get(String("notification_"+notification_id));
854 if(obj.parentNode !== undefined){
855 if(obj.parentNode !== undefined){
855 obj.parentNode.removeChild(obj);
856 obj.parentNode.removeChild(obj);
856 }
857 }
857 _run_callbacks(callbacks);
858 _run_callbacks(callbacks);
858 },
859 },
859 failure:function(o){
860 failure:function(o){
860 alert("error");
861 alert("error");
861 },
862 },
862 };
863 };
863 var postData = '_method=delete';
864 var postData = '_method=delete';
864 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
865 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
865 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
866 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
866 callback, postData);
867 callback, postData);
867 };
868 };
868
869
869 var readNotification = function(url, notification_id,callbacks){
870 var readNotification = function(url, notification_id,callbacks){
870 var callback = {
871 var callback = {
871 success:function(o){
872 success:function(o){
872 var obj = YUD.get(String("notification_"+notification_id));
873 var obj = YUD.get(String("notification_"+notification_id));
873 YUD.removeClass(obj, 'unread');
874 YUD.removeClass(obj, 'unread');
874 var r_button = obj.children[0].getElementsByClassName('read-notification')[0]
875 var r_button = obj.children[0].getElementsByClassName('read-notification')[0]
875
876
876 if(r_button.parentNode !== undefined){
877 if(r_button.parentNode !== undefined){
877 r_button.parentNode.removeChild(r_button);
878 r_button.parentNode.removeChild(r_button);
878 }
879 }
879 _run_callbacks(callbacks);
880 _run_callbacks(callbacks);
880 },
881 },
881 failure:function(o){
882 failure:function(o){
882 alert("error");
883 alert("error");
883 },
884 },
884 };
885 };
885 var postData = '_method=put';
886 var postData = '_method=put';
886 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
887 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
887 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
888 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
888 callback, postData);
889 callback, postData);
889 };
890 };
890
891
891 /** MEMBERS AUTOCOMPLETE WIDGET **/
892 /** MEMBERS AUTOCOMPLETE WIDGET **/
892
893
893 var MembersAutoComplete = function (users_list, groups_list) {
894 var MembersAutoComplete = function (users_list, groups_list) {
894 var myUsers = users_list;
895 var myUsers = users_list;
895 var myGroups = groups_list;
896 var myGroups = groups_list;
896
897
897 // Define a custom search function for the DataSource of users
898 // Define a custom search function for the DataSource of users
898 var matchUsers = function (sQuery) {
899 var matchUsers = function (sQuery) {
899 // Case insensitive matching
900 // Case insensitive matching
900 var query = sQuery.toLowerCase();
901 var query = sQuery.toLowerCase();
901 var i = 0;
902 var i = 0;
902 var l = myUsers.length;
903 var l = myUsers.length;
903 var matches = [];
904 var matches = [];
904
905
905 // Match against each name of each contact
906 // Match against each name of each contact
906 for (; i < l; i++) {
907 for (; i < l; i++) {
907 contact = myUsers[i];
908 contact = myUsers[i];
908 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
909 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
909 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
910 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
910 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
911 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
911 matches[matches.length] = contact;
912 matches[matches.length] = contact;
912 }
913 }
913 }
914 }
914 return matches;
915 return matches;
915 };
916 };
916
917
917 // Define a custom search function for the DataSource of usersGroups
918 // Define a custom search function for the DataSource of usersGroups
918 var matchGroups = function (sQuery) {
919 var matchGroups = function (sQuery) {
919 // Case insensitive matching
920 // Case insensitive matching
920 var query = sQuery.toLowerCase();
921 var query = sQuery.toLowerCase();
921 var i = 0;
922 var i = 0;
922 var l = myGroups.length;
923 var l = myGroups.length;
923 var matches = [];
924 var matches = [];
924
925
925 // Match against each name of each contact
926 // Match against each name of each contact
926 for (; i < l; i++) {
927 for (; i < l; i++) {
927 matched_group = myGroups[i];
928 matched_group = myGroups[i];
928 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
929 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
929 matches[matches.length] = matched_group;
930 matches[matches.length] = matched_group;
930 }
931 }
931 }
932 }
932 return matches;
933 return matches;
933 };
934 };
934
935
935 //match all
936 //match all
936 var matchAll = function (sQuery) {
937 var matchAll = function (sQuery) {
937 u = matchUsers(sQuery);
938 u = matchUsers(sQuery);
938 g = matchGroups(sQuery);
939 g = matchGroups(sQuery);
939 return u.concat(g);
940 return u.concat(g);
940 };
941 };
941
942
942 // DataScheme for members
943 // DataScheme for members
943 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
944 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
944 memberDS.responseSchema = {
945 memberDS.responseSchema = {
945 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
946 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
946 };
947 };
947
948
948 // DataScheme for owner
949 // DataScheme for owner
949 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
950 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
950 ownerDS.responseSchema = {
951 ownerDS.responseSchema = {
951 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
952 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
952 };
953 };
953
954
954 // Instantiate AutoComplete for perms
955 // Instantiate AutoComplete for perms
955 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
956 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
956 membersAC.useShadow = false;
957 membersAC.useShadow = false;
957 membersAC.resultTypeList = false;
958 membersAC.resultTypeList = false;
958 membersAC.animVert = false;
959 membersAC.animVert = false;
959 membersAC.animHoriz = false;
960 membersAC.animHoriz = false;
960 membersAC.animSpeed = 0.1;
961 membersAC.animSpeed = 0.1;
961
962
962 // Instantiate AutoComplete for owner
963 // Instantiate AutoComplete for owner
963 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
964 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
964 ownerAC.useShadow = false;
965 ownerAC.useShadow = false;
965 ownerAC.resultTypeList = false;
966 ownerAC.resultTypeList = false;
966 ownerAC.animVert = false;
967 ownerAC.animVert = false;
967 ownerAC.animHoriz = false;
968 ownerAC.animHoriz = false;
968 ownerAC.animSpeed = 0.1;
969 ownerAC.animSpeed = 0.1;
969
970
970 // Helper highlight function for the formatter
971 // Helper highlight function for the formatter
971 var highlightMatch = function (full, snippet, matchindex) {
972 var highlightMatch = function (full, snippet, matchindex) {
972 return full.substring(0, matchindex)
973 return full.substring(0, matchindex)
973 + "<span class='match'>"
974 + "<span class='match'>"
974 + full.substr(matchindex, snippet.length)
975 + full.substr(matchindex, snippet.length)
975 + "</span>" + full.substring(matchindex + snippet.length);
976 + "</span>" + full.substring(matchindex + snippet.length);
976 };
977 };
977
978
978 // Custom formatter to highlight the matching letters
979 // Custom formatter to highlight the matching letters
979 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
980 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
980 var query = sQuery.toLowerCase();
981 var query = sQuery.toLowerCase();
981 var _gravatar = function(res, em, group){
982 var _gravatar = function(res, em, group){
982 if (group !== undefined){
983 if (group !== undefined){
983 em = '/images/icons/group.png'
984 em = '/images/icons/group.png'
984 }
985 }
985 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
986 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
986 return tmpl.format(em,res)
987 return tmpl.format(em,res)
987 }
988 }
988 // group
989 // group
989 if (oResultData.grname != undefined) {
990 if (oResultData.grname != undefined) {
990 var grname = oResultData.grname;
991 var grname = oResultData.grname;
991 var grmembers = oResultData.grmembers;
992 var grmembers = oResultData.grmembers;
992 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
993 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
993 var grprefix = "{0}: ".format(_TM['Group']);
994 var grprefix = "{0}: ".format(_TM['Group']);
994 var grsuffix = " (" + grmembers + " )";
995 var grsuffix = " (" + grmembers + " )";
995 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
996 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
996
997
997 if (grnameMatchIndex > -1) {
998 if (grnameMatchIndex > -1) {
998 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
999 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
999 }
1000 }
1000 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1001 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1001 // Users
1002 // Users
1002 } else if (oResultData.nname != undefined) {
1003 } else if (oResultData.nname != undefined) {
1003 var fname = oResultData.fname || "";
1004 var fname = oResultData.fname || "";
1004 var lname = oResultData.lname || "";
1005 var lname = oResultData.lname || "";
1005 var nname = oResultData.nname;
1006 var nname = oResultData.nname;
1006
1007
1007 // Guard against null value
1008 // Guard against null value
1008 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1009 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1009 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1010 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1010 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1011 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1011 displayfname, displaylname, displaynname;
1012 displayfname, displaylname, displaynname;
1012
1013
1013 if (fnameMatchIndex > -1) {
1014 if (fnameMatchIndex > -1) {
1014 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1015 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1015 } else {
1016 } else {
1016 displayfname = fname;
1017 displayfname = fname;
1017 }
1018 }
1018
1019
1019 if (lnameMatchIndex > -1) {
1020 if (lnameMatchIndex > -1) {
1020 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1021 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1021 } else {
1022 } else {
1022 displaylname = lname;
1023 displaylname = lname;
1023 }
1024 }
1024
1025
1025 if (nnameMatchIndex > -1) {
1026 if (nnameMatchIndex > -1) {
1026 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1027 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1027 } else {
1028 } else {
1028 displaynname = nname ? "(" + nname + ")" : "";
1029 displaynname = nname ? "(" + nname + ")" : "";
1029 }
1030 }
1030
1031
1031 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1032 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1032 } else {
1033 } else {
1033 return '';
1034 return '';
1034 }
1035 }
1035 };
1036 };
1036 membersAC.formatResult = custom_formatter;
1037 membersAC.formatResult = custom_formatter;
1037 ownerAC.formatResult = custom_formatter;
1038 ownerAC.formatResult = custom_formatter;
1038
1039
1039 var myHandler = function (sType, aArgs) {
1040 var myHandler = function (sType, aArgs) {
1040
1041
1041 var myAC = aArgs[0]; // reference back to the AC instance
1042 var myAC = aArgs[0]; // reference back to the AC instance
1042 var elLI = aArgs[1]; // reference to the selected LI element
1043 var elLI = aArgs[1]; // reference to the selected LI element
1043 var oData = aArgs[2]; // object literal of selected item's result data
1044 var oData = aArgs[2]; // object literal of selected item's result data
1044 //fill the autocomplete with value
1045 //fill the autocomplete with value
1045 if (oData.nname != undefined) {
1046 if (oData.nname != undefined) {
1046 //users
1047 //users
1047 myAC.getInputEl().value = oData.nname;
1048 myAC.getInputEl().value = oData.nname;
1048 YUD.get('perm_new_member_type').value = 'user';
1049 YUD.get('perm_new_member_type').value = 'user';
1049 } else {
1050 } else {
1050 //groups
1051 //groups
1051 myAC.getInputEl().value = oData.grname;
1052 myAC.getInputEl().value = oData.grname;
1052 YUD.get('perm_new_member_type').value = 'users_group';
1053 YUD.get('perm_new_member_type').value = 'users_group';
1053 }
1054 }
1054 };
1055 };
1055
1056
1056 membersAC.itemSelectEvent.subscribe(myHandler);
1057 membersAC.itemSelectEvent.subscribe(myHandler);
1057 if(ownerAC.itemSelectEvent){
1058 if(ownerAC.itemSelectEvent){
1058 ownerAC.itemSelectEvent.subscribe(myHandler);
1059 ownerAC.itemSelectEvent.subscribe(myHandler);
1059 }
1060 }
1060
1061
1061 return {
1062 return {
1062 memberDS: memberDS,
1063 memberDS: memberDS,
1063 ownerDS: ownerDS,
1064 ownerDS: ownerDS,
1064 membersAC: membersAC,
1065 membersAC: membersAC,
1065 ownerAC: ownerAC,
1066 ownerAC: ownerAC,
1066 };
1067 };
1067 }
1068 }
1068
1069
1069
1070
1070 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1071 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1071 var myUsers = users_list;
1072 var myUsers = users_list;
1072 var myGroups = groups_list;
1073 var myGroups = groups_list;
1073
1074
1074 // Define a custom search function for the DataSource of users
1075 // Define a custom search function for the DataSource of users
1075 var matchUsers = function (sQuery) {
1076 var matchUsers = function (sQuery) {
1076 var org_sQuery = sQuery;
1077 var org_sQuery = sQuery;
1077 if(this.mentionQuery == null){
1078 if(this.mentionQuery == null){
1078 return []
1079 return []
1079 }
1080 }
1080 sQuery = this.mentionQuery;
1081 sQuery = this.mentionQuery;
1081 // Case insensitive matching
1082 // Case insensitive matching
1082 var query = sQuery.toLowerCase();
1083 var query = sQuery.toLowerCase();
1083 var i = 0;
1084 var i = 0;
1084 var l = myUsers.length;
1085 var l = myUsers.length;
1085 var matches = [];
1086 var matches = [];
1086
1087
1087 // Match against each name of each contact
1088 // Match against each name of each contact
1088 for (; i < l; i++) {
1089 for (; i < l; i++) {
1089 contact = myUsers[i];
1090 contact = myUsers[i];
1090 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1091 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1091 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1092 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1092 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1093 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1093 matches[matches.length] = contact;
1094 matches[matches.length] = contact;
1094 }
1095 }
1095 }
1096 }
1096 return matches
1097 return matches
1097 };
1098 };
1098
1099
1099 //match all
1100 //match all
1100 var matchAll = function (sQuery) {
1101 var matchAll = function (sQuery) {
1101 u = matchUsers(sQuery);
1102 u = matchUsers(sQuery);
1102 return u
1103 return u
1103 };
1104 };
1104
1105
1105 // DataScheme for owner
1106 // DataScheme for owner
1106 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1107 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1107
1108
1108 ownerDS.responseSchema = {
1109 ownerDS.responseSchema = {
1109 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1110 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1110 };
1111 };
1111
1112
1112 // Instantiate AutoComplete for mentions
1113 // Instantiate AutoComplete for mentions
1113 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1114 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1114 ownerAC.useShadow = false;
1115 ownerAC.useShadow = false;
1115 ownerAC.resultTypeList = false;
1116 ownerAC.resultTypeList = false;
1116 ownerAC.suppressInputUpdate = true;
1117 ownerAC.suppressInputUpdate = true;
1117 ownerAC.animVert = false;
1118 ownerAC.animVert = false;
1118 ownerAC.animHoriz = false;
1119 ownerAC.animHoriz = false;
1119 ownerAC.animSpeed = 0.1;
1120 ownerAC.animSpeed = 0.1;
1120
1121
1121 // Helper highlight function for the formatter
1122 // Helper highlight function for the formatter
1122 var highlightMatch = function (full, snippet, matchindex) {
1123 var highlightMatch = function (full, snippet, matchindex) {
1123 return full.substring(0, matchindex)
1124 return full.substring(0, matchindex)
1124 + "<span class='match'>"
1125 + "<span class='match'>"
1125 + full.substr(matchindex, snippet.length)
1126 + full.substr(matchindex, snippet.length)
1126 + "</span>" + full.substring(matchindex + snippet.length);
1127 + "</span>" + full.substring(matchindex + snippet.length);
1127 };
1128 };
1128
1129
1129 // Custom formatter to highlight the matching letters
1130 // Custom formatter to highlight the matching letters
1130 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1131 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1131 var org_sQuery = sQuery;
1132 var org_sQuery = sQuery;
1132 if(this.dataSource.mentionQuery != null){
1133 if(this.dataSource.mentionQuery != null){
1133 sQuery = this.dataSource.mentionQuery;
1134 sQuery = this.dataSource.mentionQuery;
1134 }
1135 }
1135
1136
1136 var query = sQuery.toLowerCase();
1137 var query = sQuery.toLowerCase();
1137 var _gravatar = function(res, em, group){
1138 var _gravatar = function(res, em, group){
1138 if (group !== undefined){
1139 if (group !== undefined){
1139 em = '/images/icons/group.png'
1140 em = '/images/icons/group.png'
1140 }
1141 }
1141 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1142 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1142 return tmpl.format(em,res)
1143 return tmpl.format(em,res)
1143 }
1144 }
1144 if (oResultData.nname != undefined) {
1145 if (oResultData.nname != undefined) {
1145 var fname = oResultData.fname || "";
1146 var fname = oResultData.fname || "";
1146 var lname = oResultData.lname || "";
1147 var lname = oResultData.lname || "";
1147 var nname = oResultData.nname;
1148 var nname = oResultData.nname;
1148
1149
1149 // Guard against null value
1150 // Guard against null value
1150 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1151 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1151 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1152 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1152 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1153 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1153 displayfname, displaylname, displaynname;
1154 displayfname, displaylname, displaynname;
1154
1155
1155 if (fnameMatchIndex > -1) {
1156 if (fnameMatchIndex > -1) {
1156 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1157 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1157 } else {
1158 } else {
1158 displayfname = fname;
1159 displayfname = fname;
1159 }
1160 }
1160
1161
1161 if (lnameMatchIndex > -1) {
1162 if (lnameMatchIndex > -1) {
1162 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1163 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1163 } else {
1164 } else {
1164 displaylname = lname;
1165 displaylname = lname;
1165 }
1166 }
1166
1167
1167 if (nnameMatchIndex > -1) {
1168 if (nnameMatchIndex > -1) {
1168 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1169 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1169 } else {
1170 } else {
1170 displaynname = nname ? "(" + nname + ")" : "";
1171 displaynname = nname ? "(" + nname + ")" : "";
1171 }
1172 }
1172
1173
1173 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1174 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1174 } else {
1175 } else {
1175 return '';
1176 return '';
1176 }
1177 }
1177 };
1178 };
1178
1179
1179 if(ownerAC.itemSelectEvent){
1180 if(ownerAC.itemSelectEvent){
1180 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1181 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1181
1182
1182 var myAC = aArgs[0]; // reference back to the AC instance
1183 var myAC = aArgs[0]; // reference back to the AC instance
1183 var elLI = aArgs[1]; // reference to the selected LI element
1184 var elLI = aArgs[1]; // reference to the selected LI element
1184 var oData = aArgs[2]; // object literal of selected item's result data
1185 var oData = aArgs[2]; // object literal of selected item's result data
1185 //fill the autocomplete with value
1186 //fill the autocomplete with value
1186 if (oData.nname != undefined) {
1187 if (oData.nname != undefined) {
1187 //users
1188 //users
1188 //Replace the mention name with replaced
1189 //Replace the mention name with replaced
1189 var re = new RegExp();
1190 var re = new RegExp();
1190 var org = myAC.getInputEl().value;
1191 var org = myAC.getInputEl().value;
1191 var chunks = myAC.dataSource.chunks
1192 var chunks = myAC.dataSource.chunks
1192 // replace middle chunk(the search term) with actuall match
1193 // replace middle chunk(the search term) with actuall match
1193 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1194 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1194 '@'+oData.nname+' ');
1195 '@'+oData.nname+' ');
1195 myAC.getInputEl().value = chunks.join('')
1196 myAC.getInputEl().value = chunks.join('')
1196 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1197 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1197 } else {
1198 } else {
1198 //groups
1199 //groups
1199 myAC.getInputEl().value = oData.grname;
1200 myAC.getInputEl().value = oData.grname;
1200 YUD.get('perm_new_member_type').value = 'users_group';
1201 YUD.get('perm_new_member_type').value = 'users_group';
1201 }
1202 }
1202 });
1203 });
1203 }
1204 }
1204
1205
1205 // in this keybuffer we will gather current value of search !
1206 // in this keybuffer we will gather current value of search !
1206 // since we need to get this just when someone does `@` then we do the
1207 // since we need to get this just when someone does `@` then we do the
1207 // search
1208 // search
1208 ownerAC.dataSource.chunks = [];
1209 ownerAC.dataSource.chunks = [];
1209 ownerAC.dataSource.mentionQuery = null;
1210 ownerAC.dataSource.mentionQuery = null;
1210
1211
1211 ownerAC.get_mention = function(msg, max_pos) {
1212 ownerAC.get_mention = function(msg, max_pos) {
1212 var org = msg;
1213 var org = msg;
1213 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1214 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1214 var chunks = [];
1215 var chunks = [];
1215
1216
1216
1217
1217 // cut first chunk until curret pos
1218 // cut first chunk until curret pos
1218 var to_max = msg.substr(0, max_pos);
1219 var to_max = msg.substr(0, max_pos);
1219 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1220 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1220 var msg2 = to_max.substr(at_pos);
1221 var msg2 = to_max.substr(at_pos);
1221
1222
1222 chunks.push(org.substr(0,at_pos))// prefix chunk
1223 chunks.push(org.substr(0,at_pos))// prefix chunk
1223 chunks.push(msg2) // search chunk
1224 chunks.push(msg2) // search chunk
1224 chunks.push(org.substr(max_pos)) // postfix chunk
1225 chunks.push(org.substr(max_pos)) // postfix chunk
1225
1226
1226 // clean up msg2 for filtering and regex match
1227 // clean up msg2 for filtering and regex match
1227 var msg2 = msg2.lstrip(' ').lstrip('\n');
1228 var msg2 = msg2.lstrip(' ').lstrip('\n');
1228
1229
1229 if(re.test(msg2)){
1230 if(re.test(msg2)){
1230 var unam = re.exec(msg2)[1];
1231 var unam = re.exec(msg2)[1];
1231 return [unam, chunks];
1232 return [unam, chunks];
1232 }
1233 }
1233 return [null, null];
1234 return [null, null];
1234 };
1235 };
1235
1236
1236 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1237 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1237
1238
1238 var ac_obj = args[0];
1239 var ac_obj = args[0];
1239 var currentMessage = args[1];
1240 var currentMessage = args[1];
1240 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1241 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1241
1242
1242 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1243 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1243 var curr_search = null;
1244 var curr_search = null;
1244 if(unam[0]){
1245 if(unam[0]){
1245 curr_search = unam[0];
1246 curr_search = unam[0];
1246 }
1247 }
1247
1248
1248 ownerAC.dataSource.chunks = unam[1];
1249 ownerAC.dataSource.chunks = unam[1];
1249 ownerAC.dataSource.mentionQuery = curr_search;
1250 ownerAC.dataSource.mentionQuery = curr_search;
1250
1251
1251 })
1252 })
1252
1253
1253 return {
1254 return {
1254 ownerDS: ownerDS,
1255 ownerDS: ownerDS,
1255 ownerAC: ownerAC,
1256 ownerAC: ownerAC,
1256 };
1257 };
1257 }
1258 }
1258
1259
1259
1260
1260 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1261 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1261 var myUsers = users_list;
1262 var myUsers = users_list;
1262 var myGroups = groups_list;
1263 var myGroups = groups_list;
1263
1264
1264 // Define a custom search function for the DataSource of users
1265 // Define a custom search function for the DataSource of users
1265 var matchUsers = function (sQuery) {
1266 var matchUsers = function (sQuery) {
1266 // Case insensitive matching
1267 // Case insensitive matching
1267 var query = sQuery.toLowerCase();
1268 var query = sQuery.toLowerCase();
1268 var i = 0;
1269 var i = 0;
1269 var l = myUsers.length;
1270 var l = myUsers.length;
1270 var matches = [];
1271 var matches = [];
1271
1272
1272 // Match against each name of each contact
1273 // Match against each name of each contact
1273 for (; i < l; i++) {
1274 for (; i < l; i++) {
1274 contact = myUsers[i];
1275 contact = myUsers[i];
1275 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1276 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1276 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1277 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1277 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1278 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1278 matches[matches.length] = contact;
1279 matches[matches.length] = contact;
1279 }
1280 }
1280 }
1281 }
1281 return matches;
1282 return matches;
1282 };
1283 };
1283
1284
1284 // Define a custom search function for the DataSource of usersGroups
1285 // Define a custom search function for the DataSource of usersGroups
1285 var matchGroups = function (sQuery) {
1286 var matchGroups = function (sQuery) {
1286 // Case insensitive matching
1287 // Case insensitive matching
1287 var query = sQuery.toLowerCase();
1288 var query = sQuery.toLowerCase();
1288 var i = 0;
1289 var i = 0;
1289 var l = myGroups.length;
1290 var l = myGroups.length;
1290 var matches = [];
1291 var matches = [];
1291
1292
1292 // Match against each name of each contact
1293 // Match against each name of each contact
1293 for (; i < l; i++) {
1294 for (; i < l; i++) {
1294 matched_group = myGroups[i];
1295 matched_group = myGroups[i];
1295 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1296 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1296 matches[matches.length] = matched_group;
1297 matches[matches.length] = matched_group;
1297 }
1298 }
1298 }
1299 }
1299 return matches;
1300 return matches;
1300 };
1301 };
1301
1302
1302 //match all
1303 //match all
1303 var matchAll = function (sQuery) {
1304 var matchAll = function (sQuery) {
1304 u = matchUsers(sQuery);
1305 u = matchUsers(sQuery);
1305 return u
1306 return u
1306 };
1307 };
1307
1308
1308 // DataScheme for owner
1309 // DataScheme for owner
1309 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1310 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1310
1311
1311 ownerDS.responseSchema = {
1312 ownerDS.responseSchema = {
1312 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1313 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1313 };
1314 };
1314
1315
1315 // Instantiate AutoComplete for mentions
1316 // Instantiate AutoComplete for mentions
1316 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1317 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1317 reviewerAC.useShadow = false;
1318 reviewerAC.useShadow = false;
1318 reviewerAC.resultTypeList = false;
1319 reviewerAC.resultTypeList = false;
1319 reviewerAC.suppressInputUpdate = true;
1320 reviewerAC.suppressInputUpdate = true;
1320 reviewerAC.animVert = false;
1321 reviewerAC.animVert = false;
1321 reviewerAC.animHoriz = false;
1322 reviewerAC.animHoriz = false;
1322 reviewerAC.animSpeed = 0.1;
1323 reviewerAC.animSpeed = 0.1;
1323
1324
1324 // Helper highlight function for the formatter
1325 // Helper highlight function for the formatter
1325 var highlightMatch = function (full, snippet, matchindex) {
1326 var highlightMatch = function (full, snippet, matchindex) {
1326 return full.substring(0, matchindex)
1327 return full.substring(0, matchindex)
1327 + "<span class='match'>"
1328 + "<span class='match'>"
1328 + full.substr(matchindex, snippet.length)
1329 + full.substr(matchindex, snippet.length)
1329 + "</span>" + full.substring(matchindex + snippet.length);
1330 + "</span>" + full.substring(matchindex + snippet.length);
1330 };
1331 };
1331
1332
1332 // Custom formatter to highlight the matching letters
1333 // Custom formatter to highlight the matching letters
1333 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1334 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1334 var org_sQuery = sQuery;
1335 var org_sQuery = sQuery;
1335 if(this.dataSource.mentionQuery != null){
1336 if(this.dataSource.mentionQuery != null){
1336 sQuery = this.dataSource.mentionQuery;
1337 sQuery = this.dataSource.mentionQuery;
1337 }
1338 }
1338
1339
1339 var query = sQuery.toLowerCase();
1340 var query = sQuery.toLowerCase();
1340 var _gravatar = function(res, em, group){
1341 var _gravatar = function(res, em, group){
1341 if (group !== undefined){
1342 if (group !== undefined){
1342 em = '/images/icons/group.png'
1343 em = '/images/icons/group.png'
1343 }
1344 }
1344 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1345 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1345 return tmpl.format(em,res)
1346 return tmpl.format(em,res)
1346 }
1347 }
1347 if (oResultData.nname != undefined) {
1348 if (oResultData.nname != undefined) {
1348 var fname = oResultData.fname || "";
1349 var fname = oResultData.fname || "";
1349 var lname = oResultData.lname || "";
1350 var lname = oResultData.lname || "";
1350 var nname = oResultData.nname;
1351 var nname = oResultData.nname;
1351
1352
1352 // Guard against null value
1353 // Guard against null value
1353 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1354 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1354 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1355 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1355 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1356 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1356 displayfname, displaylname, displaynname;
1357 displayfname, displaylname, displaynname;
1357
1358
1358 if (fnameMatchIndex > -1) {
1359 if (fnameMatchIndex > -1) {
1359 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1360 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1360 } else {
1361 } else {
1361 displayfname = fname;
1362 displayfname = fname;
1362 }
1363 }
1363
1364
1364 if (lnameMatchIndex > -1) {
1365 if (lnameMatchIndex > -1) {
1365 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1366 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1366 } else {
1367 } else {
1367 displaylname = lname;
1368 displaylname = lname;
1368 }
1369 }
1369
1370
1370 if (nnameMatchIndex > -1) {
1371 if (nnameMatchIndex > -1) {
1371 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1372 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1372 } else {
1373 } else {
1373 displaynname = nname ? "(" + nname + ")" : "";
1374 displaynname = nname ? "(" + nname + ")" : "";
1374 }
1375 }
1375
1376
1376 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1377 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1377 } else {
1378 } else {
1378 return '';
1379 return '';
1379 }
1380 }
1380 };
1381 };
1381
1382
1382 //members cache to catch duplicates
1383 //members cache to catch duplicates
1383 reviewerAC.dataSource.cache = [];
1384 reviewerAC.dataSource.cache = [];
1384 // hack into select event
1385 // hack into select event
1385 if(reviewerAC.itemSelectEvent){
1386 if(reviewerAC.itemSelectEvent){
1386 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1387 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1387
1388
1388 var myAC = aArgs[0]; // reference back to the AC instance
1389 var myAC = aArgs[0]; // reference back to the AC instance
1389 var elLI = aArgs[1]; // reference to the selected LI element
1390 var elLI = aArgs[1]; // reference to the selected LI element
1390 var oData = aArgs[2]; // object literal of selected item's result data
1391 var oData = aArgs[2]; // object literal of selected item's result data
1391 var members = YUD.get('review_members');
1392 var members = YUD.get('review_members');
1392 //fill the autocomplete with value
1393 //fill the autocomplete with value
1393
1394
1394 if (oData.nname != undefined) {
1395 if (oData.nname != undefined) {
1395 if (myAC.dataSource.cache.indexOf(oData.id) != -1){
1396 if (myAC.dataSource.cache.indexOf(oData.id) != -1){
1396 return
1397 return
1397 }
1398 }
1398
1399
1399 var tmpl = '<li id="reviewer_{2}">'+
1400 var tmpl = '<li id="reviewer_{2}">'+
1400 '<div class="reviewers_member">'+
1401 '<div class="reviewers_member">'+
1401 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1402 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1402 '<div style="float:left">{1}</div>'+
1403 '<div style="float:left">{1}</div>'+
1403 '<input type="hidden" value="{2}" name="review_members" />'+
1404 '<input type="hidden" value="{2}" name="review_members" />'+
1404 '<span class="delete_icon action_button" onclick="removeReviewer({2})"></span>'+
1405 '<span class="delete_icon action_button" onclick="removeReviewer({2})"></span>'+
1405 '</div>'+
1406 '</div>'+
1406 '</li>'
1407 '</li>'
1407
1408
1408 var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname);
1409 var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname);
1409 var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id);
1410 var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id);
1410 members.innerHTML += element;
1411 members.innerHTML += element;
1411 myAC.dataSource.cache.push(oData.id);
1412 myAC.dataSource.cache.push(oData.id);
1412 YUD.get('user').value = ''
1413 YUD.get('user').value = ''
1413 }
1414 }
1414 });
1415 });
1415 }
1416 }
1416 return {
1417 return {
1417 ownerDS: ownerDS,
1418 ownerDS: ownerDS,
1418 reviewerAC: reviewerAC,
1419 reviewerAC: reviewerAC,
1419 };
1420 };
1420 }
1421 }
1421
1422
1422
1423
1423 /**
1424 /**
1424 * QUICK REPO MENU
1425 * QUICK REPO MENU
1425 */
1426 */
1426 var quick_repo_menu = function(){
1427 var quick_repo_menu = function(){
1427 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1428 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1428 var menu = e.currentTarget.firstElementChild.firstElementChild;
1429 var menu = e.currentTarget.firstElementChild.firstElementChild;
1429 if(YUD.hasClass(menu,'hidden')){
1430 if(YUD.hasClass(menu,'hidden')){
1430 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1431 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1431 YUD.replaceClass(menu, 'hidden', 'active');
1432 YUD.replaceClass(menu, 'hidden', 'active');
1432 }
1433 }
1433 })
1434 })
1434 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1435 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1435 var menu = e.currentTarget.firstElementChild.firstElementChild;
1436 var menu = e.currentTarget.firstElementChild.firstElementChild;
1436 if(YUD.hasClass(menu,'active')){
1437 if(YUD.hasClass(menu,'active')){
1437 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1438 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1438 YUD.replaceClass(menu, 'active', 'hidden');
1439 YUD.replaceClass(menu, 'active', 'hidden');
1439 }
1440 }
1440 })
1441 })
1441 };
1442 };
1442
1443
1443
1444
1444 /**
1445 /**
1445 * TABLE SORTING
1446 * TABLE SORTING
1446 */
1447 */
1447
1448
1448 // returns a node from given html;
1449 // returns a node from given html;
1449 var fromHTML = function(html){
1450 var fromHTML = function(html){
1450 var _html = document.createElement('element');
1451 var _html = document.createElement('element');
1451 _html.innerHTML = html;
1452 _html.innerHTML = html;
1452 return _html;
1453 return _html;
1453 }
1454 }
1454 var get_rev = function(node){
1455 var get_rev = function(node){
1455 var n = node.firstElementChild.firstElementChild;
1456 var n = node.firstElementChild.firstElementChild;
1456
1457
1457 if (n===null){
1458 if (n===null){
1458 return -1
1459 return -1
1459 }
1460 }
1460 else{
1461 else{
1461 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1462 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1462 return parseInt(out);
1463 return parseInt(out);
1463 }
1464 }
1464 }
1465 }
1465
1466
1466 var get_name = function(node){
1467 var get_name = function(node){
1467 var name = node.firstElementChild.children[2].innerHTML;
1468 var name = node.firstElementChild.children[2].innerHTML;
1468 return name
1469 return name
1469 }
1470 }
1470 var get_group_name = function(node){
1471 var get_group_name = function(node){
1471 var name = node.firstElementChild.children[1].innerHTML;
1472 var name = node.firstElementChild.children[1].innerHTML;
1472 return name
1473 return name
1473 }
1474 }
1474 var get_date = function(node){
1475 var get_date = function(node){
1475 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1476 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1476 return date_
1477 return date_
1477 }
1478 }
1478
1479
1479 var get_age = function(node){
1480 var get_age = function(node){
1480 return node
1481 return node
1481 }
1482 }
1482
1483
1483 var get_link = function(node){
1484 var get_link = function(node){
1484 return node.firstElementChild.text;
1485 return node.firstElementChild.text;
1485 }
1486 }
1486
1487
1487 var revisionSort = function(a, b, desc, field) {
1488 var revisionSort = function(a, b, desc, field) {
1488
1489
1489 var a_ = fromHTML(a.getData(field));
1490 var a_ = fromHTML(a.getData(field));
1490 var b_ = fromHTML(b.getData(field));
1491 var b_ = fromHTML(b.getData(field));
1491
1492
1492 // extract revisions from string nodes
1493 // extract revisions from string nodes
1493 a_ = get_rev(a_)
1494 a_ = get_rev(a_)
1494 b_ = get_rev(b_)
1495 b_ = get_rev(b_)
1495
1496
1496 var comp = YAHOO.util.Sort.compare;
1497 var comp = YAHOO.util.Sort.compare;
1497 var compState = comp(a_, b_, desc);
1498 var compState = comp(a_, b_, desc);
1498 return compState;
1499 return compState;
1499 };
1500 };
1500 var ageSort = function(a, b, desc, field) {
1501 var ageSort = function(a, b, desc, field) {
1501 var a_ = fromHTML(a.getData(field));
1502 var a_ = fromHTML(a.getData(field));
1502 var b_ = fromHTML(b.getData(field));
1503 var b_ = fromHTML(b.getData(field));
1503
1504
1504 // extract name from table
1505 // extract name from table
1505 a_ = get_date(a_)
1506 a_ = get_date(a_)
1506 b_ = get_date(b_)
1507 b_ = get_date(b_)
1507
1508
1508 var comp = YAHOO.util.Sort.compare;
1509 var comp = YAHOO.util.Sort.compare;
1509 var compState = comp(a_, b_, desc);
1510 var compState = comp(a_, b_, desc);
1510 return compState;
1511 return compState;
1511 };
1512 };
1512
1513
1513 var nameSort = function(a, b, desc, field) {
1514 var nameSort = function(a, b, desc, field) {
1514 var a_ = fromHTML(a.getData(field));
1515 var a_ = fromHTML(a.getData(field));
1515 var b_ = fromHTML(b.getData(field));
1516 var b_ = fromHTML(b.getData(field));
1516
1517
1517 // extract name from table
1518 // extract name from table
1518 a_ = get_name(a_)
1519 a_ = get_name(a_)
1519 b_ = get_name(b_)
1520 b_ = get_name(b_)
1520
1521
1521 var comp = YAHOO.util.Sort.compare;
1522 var comp = YAHOO.util.Sort.compare;
1522 var compState = comp(a_, b_, desc);
1523 var compState = comp(a_, b_, desc);
1523 return compState;
1524 return compState;
1524 };
1525 };
1525
1526
1526 var permNameSort = function(a, b, desc, field) {
1527 var permNameSort = function(a, b, desc, field) {
1527 var a_ = fromHTML(a.getData(field));
1528 var a_ = fromHTML(a.getData(field));
1528 var b_ = fromHTML(b.getData(field));
1529 var b_ = fromHTML(b.getData(field));
1529 // extract name from table
1530 // extract name from table
1530
1531
1531 a_ = a_.children[0].innerHTML;
1532 a_ = a_.children[0].innerHTML;
1532 b_ = b_.children[0].innerHTML;
1533 b_ = b_.children[0].innerHTML;
1533
1534
1534 var comp = YAHOO.util.Sort.compare;
1535 var comp = YAHOO.util.Sort.compare;
1535 var compState = comp(a_, b_, desc);
1536 var compState = comp(a_, b_, desc);
1536 return compState;
1537 return compState;
1537 };
1538 };
1538
1539
1539 var groupNameSort = function(a, b, desc, field) {
1540 var groupNameSort = function(a, b, desc, field) {
1540 var a_ = fromHTML(a.getData(field));
1541 var a_ = fromHTML(a.getData(field));
1541 var b_ = fromHTML(b.getData(field));
1542 var b_ = fromHTML(b.getData(field));
1542
1543
1543 // extract name from table
1544 // extract name from table
1544 a_ = get_group_name(a_)
1545 a_ = get_group_name(a_)
1545 b_ = get_group_name(b_)
1546 b_ = get_group_name(b_)
1546
1547
1547 var comp = YAHOO.util.Sort.compare;
1548 var comp = YAHOO.util.Sort.compare;
1548 var compState = comp(a_, b_, desc);
1549 var compState = comp(a_, b_, desc);
1549 return compState;
1550 return compState;
1550 };
1551 };
1551 var dateSort = function(a, b, desc, field) {
1552 var dateSort = function(a, b, desc, field) {
1552 var a_ = fromHTML(a.getData(field));
1553 var a_ = fromHTML(a.getData(field));
1553 var b_ = fromHTML(b.getData(field));
1554 var b_ = fromHTML(b.getData(field));
1554
1555
1555 // extract name from table
1556 // extract name from table
1556 a_ = get_date(a_)
1557 a_ = get_date(a_)
1557 b_ = get_date(b_)
1558 b_ = get_date(b_)
1558
1559
1559 var comp = YAHOO.util.Sort.compare;
1560 var comp = YAHOO.util.Sort.compare;
1560 var compState = comp(a_, b_, desc);
1561 var compState = comp(a_, b_, desc);
1561 return compState;
1562 return compState;
1562 };
1563 };
1563
1564
1564 var linkSort = function(a, b, desc, field) {
1565 var linkSort = function(a, b, desc, field) {
1565 var a_ = fromHTML(a.getData(field));
1566 var a_ = fromHTML(a.getData(field));
1566 var b_ = fromHTML(a.getData(field));
1567 var b_ = fromHTML(a.getData(field));
1567
1568
1568 // extract url text from string nodes
1569 // extract url text from string nodes
1569 a_ = get_link(a_)
1570 a_ = get_link(a_)
1570 b_ = get_link(b_)
1571 b_ = get_link(b_)
1571
1572
1572 var comp = YAHOO.util.Sort.compare;
1573 var comp = YAHOO.util.Sort.compare;
1573 var compState = comp(a_, b_, desc);
1574 var compState = comp(a_, b_, desc);
1574 return compState;
1575 return compState;
1575 }
1576 }
1576
1577
1577
1578
1578 /* Multi selectors */
1579 /* Multi selectors */
1579
1580
1580 var MultiSelectWidget = function(selected_id, available_id, form_id){
1581 var MultiSelectWidget = function(selected_id, available_id, form_id){
1581
1582
1582
1583
1583 //definition of containers ID's
1584 //definition of containers ID's
1584 var selected_container = selected_id;
1585 var selected_container = selected_id;
1585 var available_container = available_id;
1586 var available_container = available_id;
1586
1587
1587 //temp container for selected storage.
1588 //temp container for selected storage.
1588 var cache = new Array();
1589 var cache = new Array();
1589 var av_cache = new Array();
1590 var av_cache = new Array();
1590 var c = YUD.get(selected_container);
1591 var c = YUD.get(selected_container);
1591 var ac = YUD.get(available_container);
1592 var ac = YUD.get(available_container);
1592
1593
1593 //get only selected options for further fullfilment
1594 //get only selected options for further fullfilment
1594 for(var i = 0;node =c.options[i];i++){
1595 for(var i = 0;node =c.options[i];i++){
1595 if(node.selected){
1596 if(node.selected){
1596 //push selected to my temp storage left overs :)
1597 //push selected to my temp storage left overs :)
1597 cache.push(node);
1598 cache.push(node);
1598 }
1599 }
1599 }
1600 }
1600
1601
1601 //get all available options to cache
1602 //get all available options to cache
1602 for(var i = 0;node =ac.options[i];i++){
1603 for(var i = 0;node =ac.options[i];i++){
1603 //push selected to my temp storage left overs :)
1604 //push selected to my temp storage left overs :)
1604 av_cache.push(node);
1605 av_cache.push(node);
1605 }
1606 }
1606
1607
1607 //fill available only with those not in choosen
1608 //fill available only with those not in choosen
1608 ac.options.length=0;
1609 ac.options.length=0;
1609 tmp_cache = new Array();
1610 tmp_cache = new Array();
1610
1611
1611 for(var i = 0;node = av_cache[i];i++){
1612 for(var i = 0;node = av_cache[i];i++){
1612 var add = true;
1613 var add = true;
1613 for(var i2 = 0;node_2 = cache[i2];i2++){
1614 for(var i2 = 0;node_2 = cache[i2];i2++){
1614 if(node.value == node_2.value){
1615 if(node.value == node_2.value){
1615 add=false;
1616 add=false;
1616 break;
1617 break;
1617 }
1618 }
1618 }
1619 }
1619 if(add){
1620 if(add){
1620 tmp_cache.push(new Option(node.text, node.value, false, false));
1621 tmp_cache.push(new Option(node.text, node.value, false, false));
1621 }
1622 }
1622 }
1623 }
1623
1624
1624 for(var i = 0;node = tmp_cache[i];i++){
1625 for(var i = 0;node = tmp_cache[i];i++){
1625 ac.options[i] = node;
1626 ac.options[i] = node;
1626 }
1627 }
1627
1628
1628 function prompts_action_callback(e){
1629 function prompts_action_callback(e){
1629
1630
1630 var choosen = YUD.get(selected_container);
1631 var choosen = YUD.get(selected_container);
1631 var available = YUD.get(available_container);
1632 var available = YUD.get(available_container);
1632
1633
1633 //get checked and unchecked options from field
1634 //get checked and unchecked options from field
1634 function get_checked(from_field){
1635 function get_checked(from_field){
1635 //temp container for storage.
1636 //temp container for storage.
1636 var sel_cache = new Array();
1637 var sel_cache = new Array();
1637 var oth_cache = new Array();
1638 var oth_cache = new Array();
1638
1639
1639 for(var i = 0;node = from_field.options[i];i++){
1640 for(var i = 0;node = from_field.options[i];i++){
1640 if(node.selected){
1641 if(node.selected){
1641 //push selected fields :)
1642 //push selected fields :)
1642 sel_cache.push(node);
1643 sel_cache.push(node);
1643 }
1644 }
1644 else{
1645 else{
1645 oth_cache.push(node)
1646 oth_cache.push(node)
1646 }
1647 }
1647 }
1648 }
1648
1649
1649 return [sel_cache,oth_cache]
1650 return [sel_cache,oth_cache]
1650 }
1651 }
1651
1652
1652 //fill the field with given options
1653 //fill the field with given options
1653 function fill_with(field,options){
1654 function fill_with(field,options){
1654 //clear firtst
1655 //clear firtst
1655 field.options.length=0;
1656 field.options.length=0;
1656 for(var i = 0;node = options[i];i++){
1657 for(var i = 0;node = options[i];i++){
1657 field.options[i]=new Option(node.text, node.value,
1658 field.options[i]=new Option(node.text, node.value,
1658 false, false);
1659 false, false);
1659 }
1660 }
1660
1661
1661 }
1662 }
1662 //adds to current field
1663 //adds to current field
1663 function add_to(field,options){
1664 function add_to(field,options){
1664 for(var i = 0;node = options[i];i++){
1665 for(var i = 0;node = options[i];i++){
1665 field.appendChild(new Option(node.text, node.value,
1666 field.appendChild(new Option(node.text, node.value,
1666 false, false));
1667 false, false));
1667 }
1668 }
1668 }
1669 }
1669
1670
1670 // add action
1671 // add action
1671 if (this.id=='add_element'){
1672 if (this.id=='add_element'){
1672 var c = get_checked(available);
1673 var c = get_checked(available);
1673 add_to(choosen,c[0]);
1674 add_to(choosen,c[0]);
1674 fill_with(available,c[1]);
1675 fill_with(available,c[1]);
1675 }
1676 }
1676 // remove action
1677 // remove action
1677 if (this.id=='remove_element'){
1678 if (this.id=='remove_element'){
1678 var c = get_checked(choosen);
1679 var c = get_checked(choosen);
1679 add_to(available,c[0]);
1680 add_to(available,c[0]);
1680 fill_with(choosen,c[1]);
1681 fill_with(choosen,c[1]);
1681 }
1682 }
1682 // add all elements
1683 // add all elements
1683 if(this.id=='add_all_elements'){
1684 if(this.id=='add_all_elements'){
1684 for(var i=0; node = available.options[i];i++){
1685 for(var i=0; node = available.options[i];i++){
1685 choosen.appendChild(new Option(node.text,
1686 choosen.appendChild(new Option(node.text,
1686 node.value, false, false));
1687 node.value, false, false));
1687 }
1688 }
1688 available.options.length = 0;
1689 available.options.length = 0;
1689 }
1690 }
1690 //remove all elements
1691 //remove all elements
1691 if(this.id=='remove_all_elements'){
1692 if(this.id=='remove_all_elements'){
1692 for(var i=0; node = choosen.options[i];i++){
1693 for(var i=0; node = choosen.options[i];i++){
1693 available.appendChild(new Option(node.text,
1694 available.appendChild(new Option(node.text,
1694 node.value, false, false));
1695 node.value, false, false));
1695 }
1696 }
1696 choosen.options.length = 0;
1697 choosen.options.length = 0;
1697 }
1698 }
1698
1699
1699 }
1700 }
1700
1701
1701 YUE.addListener(['add_element','remove_element',
1702 YUE.addListener(['add_element','remove_element',
1702 'add_all_elements','remove_all_elements'],'click',
1703 'add_all_elements','remove_all_elements'],'click',
1703 prompts_action_callback)
1704 prompts_action_callback)
1704 if (form_id !== undefined) {
1705 if (form_id !== undefined) {
1705 YUE.addListener(form_id,'submit',function(){
1706 YUE.addListener(form_id,'submit',function(){
1706 var choosen = YUD.get(selected_container);
1707 var choosen = YUD.get(selected_container);
1707 for (var i = 0; i < choosen.options.length; i++) {
1708 for (var i = 0; i < choosen.options.length; i++) {
1708 choosen.options[i].selected = 'selected';
1709 choosen.options[i].selected = 'selected';
1709 }
1710 }
1710 });
1711 });
1711 }
1712 }
1712 } No newline at end of file
1713 }
@@ -1,159 +1,170 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3 <html xmlns="http://www.w3.org/1999/xhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
4 <head>
5 <title>${self.title()}</title>
5 <title>${self.title()}</title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <meta name="robots" content="index, nofollow"/>
7 <meta name="robots" content="index, nofollow"/>
8 <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
8 <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" />
9
9
10 ## CSS ###
10 ## CSS ###
11 <%def name="css()">
11 <%def name="css()">
12 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen"/>
12 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css')}" media="screen"/>
13 <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css')}"/>
13 <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css')}"/>
14 ## EXTRA FOR CSS
14 ## EXTRA FOR CSS
15 ${self.css_extra()}
15 ${self.css_extra()}
16 </%def>
16 </%def>
17 <%def name="css_extra()">
17 <%def name="css_extra()">
18 </%def>
18 </%def>
19
19
20 ${self.css()}
20 ${self.css()}
21
21
22 %if c.ga_code:
22 %if c.ga_code:
23 <!-- Analytics -->
23 <!-- Analytics -->
24 <script type="text/javascript">
24 <script type="text/javascript">
25 var _gaq = _gaq || [];
25 var _gaq = _gaq || [];
26 _gaq.push(['_setAccount', '${c.ga_code}']);
26 _gaq.push(['_setAccount', '${c.ga_code}']);
27 _gaq.push(['_trackPageview']);
27 _gaq.push(['_trackPageview']);
28
28
29 (function() {
29 (function() {
30 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
30 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
31 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
31 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
32 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
32 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
33 })();
33 })();
34 </script>
34 </script>
35 %endif
35 %endif
36
36
37 ## JAVASCRIPT ##
37 ## JAVASCRIPT ##
38 <%def name="js()">
38 <%def name="js()">
39 <script type="text/javascript">
39 <script type="text/javascript">
40 //JS translations map
40 //JS translations map
41 var TRANSLATION_MAP = {
41 var TRANSLATION_MAP = {
42 'add another comment':'${_("add another comment")}',
42 'add another comment':'${_("add another comment")}',
43 'Stop following this repository':"${_('Stop following this repository')}",
43 'Stop following this repository':"${_('Stop following this repository')}",
44 'Start following this repository':"${_('Start following this repository')}",
44 'Start following this repository':"${_('Start following this repository')}",
45 'Group':"${_('Group')}",
45 'Group':"${_('Group')}",
46 'members':"${_('members')}",
46 'members':"${_('members')}",
47 'search truncated': "${_('search truncated')}",
47 'search truncated': "${_('search truncated')}",
48 'no matching files': "${_('no matching files')}"
48 'no matching files': "${_('no matching files')}"
49
49
50 };
50 };
51 var _TM = TRANSLATION_MAP;
51 var _TM = TRANSLATION_MAP;
52 </script>
52 </script>
53 <script type="text/javascript" src="${h.url('/js/yui.2.9.js')}"></script>
53 <script type="text/javascript" src="${h.url('/js/yui.2.9.js')}"></script>
54 <!--[if lt IE 9]>
54 <!--[if lt IE 9]>
55 <script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script>
55 <script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script>
56 <![endif]-->
56 <![endif]-->
57 <script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
57 <script type="text/javascript" src="${h.url('/js/yui.flot.js')}"></script>
58 <script type="text/javascript" src="${h.url('/js/native.history.js')}"></script>
58 <script type="text/javascript" src="${h.url('/js/rhodecode.js')}"></script>
59 <script type="text/javascript" src="${h.url('/js/rhodecode.js')}"></script>
59 ## EXTRA FOR JS
60 ## EXTRA FOR JS
60 ${self.js_extra()}
61 ${self.js_extra()}
61
62
62 <script type="text/javascript">
63 <script type="text/javascript">
64 (function(window,undefined){
65
66 // Prepare
67 var History = window.History; // Note: We are using a capital H instead of a lower h
68 if ( !History.enabled ) {
69 // History.js is disabled for this browser.
70 // This is because we can optionally choose to support HTML4 browsers or not.
71 return false;
72 }
73 })(window);
63 var follow_base_url = "${h.url('toggle_following')}";
74 var follow_base_url = "${h.url('toggle_following')}";
64
75
65 var onSuccessFollow = function(target){
76 var onSuccessFollow = function(target){
66 var f = YUD.get(target.id);
77 var f = YUD.get(target.id);
67 var f_cnt = YUD.get('current_followers_count');
78 var f_cnt = YUD.get('current_followers_count');
68
79
69 if(f.getAttribute('class')=='follow'){
80 if(f.getAttribute('class')=='follow'){
70 f.setAttribute('class','following');
81 f.setAttribute('class','following');
71 f.setAttribute('title',_TM['Stop following this repository']);
82 f.setAttribute('title',_TM['Stop following this repository']);
72
83
73 if(f_cnt){
84 if(f_cnt){
74 var cnt = Number(f_cnt.innerHTML)+1;
85 var cnt = Number(f_cnt.innerHTML)+1;
75 f_cnt.innerHTML = cnt;
86 f_cnt.innerHTML = cnt;
76 }
87 }
77 }
88 }
78 else{
89 else{
79 f.setAttribute('class','follow');
90 f.setAttribute('class','follow');
80 f.setAttribute('title',_TM['Start following this repository']);
91 f.setAttribute('title',_TM['Start following this repository']);
81 if(f_cnt){
92 if(f_cnt){
82 var cnt = Number(f_cnt.innerHTML)+1;
93 var cnt = Number(f_cnt.innerHTML)+1;
83 f_cnt.innerHTML = cnt;
94 f_cnt.innerHTML = cnt;
84 }
95 }
85 }
96 }
86 }
97 }
87
98
88 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
99 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
89 args = 'follows_user_id='+fallows_user_id;
100 args = 'follows_user_id='+fallows_user_id;
90 args+= '&amp;auth_token='+token;
101 args+= '&amp;auth_token='+token;
91 if(user_id != undefined){
102 if(user_id != undefined){
92 args+="&amp;user_id="+user_id;
103 args+="&amp;user_id="+user_id;
93 }
104 }
94 YUC.asyncRequest('POST',follow_base_url,{
105 YUC.asyncRequest('POST',follow_base_url,{
95 success:function(o){
106 success:function(o){
96 onSuccessFollow(target);
107 onSuccessFollow(target);
97 }
108 }
98 },args);
109 },args);
99 return false;
110 return false;
100 }
111 }
101
112
102 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
113 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
103
114
104 args = 'follows_repo_id='+fallows_repo_id;
115 args = 'follows_repo_id='+fallows_repo_id;
105 args+= '&amp;auth_token='+token;
116 args+= '&amp;auth_token='+token;
106 if(user_id != undefined){
117 if(user_id != undefined){
107 args+="&amp;user_id="+user_id;
118 args+="&amp;user_id="+user_id;
108 }
119 }
109 YUC.asyncRequest('POST',follow_base_url,{
120 YUC.asyncRequest('POST',follow_base_url,{
110 success:function(o){
121 success:function(o){
111 onSuccessFollow(target);
122 onSuccessFollow(target);
112 }
123 }
113 },args);
124 },args);
114 return false;
125 return false;
115 }
126 }
116 YUE.onDOMReady(function(){
127 YUE.onDOMReady(function(){
117 tooltip_activate();
128 tooltip_activate();
118 show_more_event();
129 show_more_event();
119
130
120 YUE.on('quick_login_link','click',function(e){
131 YUE.on('quick_login_link','click',function(e){
121 // make sure we don't redirect
132 // make sure we don't redirect
122 YUE.preventDefault(e);
133 YUE.preventDefault(e);
123
134
124 if(YUD.hasClass('quick_login_link','enabled')){
135 if(YUD.hasClass('quick_login_link','enabled')){
125 YUD.setStyle('quick_login','display','none');
136 YUD.setStyle('quick_login','display','none');
126 YUD.removeClass('quick_login_link','enabled');
137 YUD.removeClass('quick_login_link','enabled');
127 }
138 }
128 else{
139 else{
129 YUD.setStyle('quick_login','display','');
140 YUD.setStyle('quick_login','display','');
130 YUD.addClass('quick_login_link','enabled');
141 YUD.addClass('quick_login_link','enabled');
131 var usr = YUD.get('username');
142 var usr = YUD.get('username');
132 if(usr){
143 if(usr){
133 usr.focus();
144 usr.focus();
134 }
145 }
135 }
146 }
136 });
147 });
137 })
148 })
138 </script>
149 </script>
139 </%def>
150 </%def>
140 <%def name="js_extra()"></%def>
151 <%def name="js_extra()"></%def>
141 ${self.js()}
152 ${self.js()}
142 <%def name="head_extra()"></%def>
153 <%def name="head_extra()"></%def>
143 ${self.head_extra()}
154 ${self.head_extra()}
144 </head>
155 </head>
145 <body id="body">
156 <body id="body">
146 ## IE hacks
157 ## IE hacks
147 <!--[if IE 7]>
158 <!--[if IE 7]>
148 <script>YUD.addClass(document.body,'ie7')</script>
159 <script>YUD.addClass(document.body,'ie7')</script>
149 <![endif]-->
160 <![endif]-->
150 <!--[if IE 8]>
161 <!--[if IE 8]>
151 <script>YUD.addClass(document.body,'ie8')</script>
162 <script>YUD.addClass(document.body,'ie8')</script>
152 <![endif]-->
163 <![endif]-->
153 <!--[if IE 9]>
164 <!--[if IE 9]>
154 <script>YUD.addClass(document.body,'ie9')</script>
165 <script>YUD.addClass(document.body,'ie9')</script>
155 <![endif]-->
166 <![endif]-->
156
167
157 ${next.body()}
168 ${next.body()}
158 </body>
169 </body>
159 </html>
170 </html>
@@ -1,46 +1,132 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('files')}
12 ${_('files')}
13 %if c.file:
13 %if c.file:
14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
15 %endif
15 %endif
16 </%def>
16 </%def>
17
17
18 <%def name="page_nav()">
18 <%def name="page_nav()">
19 ${self.menu('files')}
19 ${self.menu('files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <!-- box / title -->
24 <!-- box / title -->
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 <ul class="links">
27 <ul class="links">
28 <li>
28 <li>
29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
30 </li>
30 </li>
31 </ul>
31 </ul>
32 </div>
32 </div>
33 <div class="table">
33 <div class="table">
34 <div id="files_data">
34 <div id="files_data">
35 <%include file='files_ypjax.html'/>
35 <%include file='files_ypjax.html'/>
36 </div>
36 </div>
37 </div>
37 </div>
38 </div>
38 </div>
39
39 <script type="text/javascript">
40 <script type="text/javascript">
40 var YPJAX_TITLE = "${c.repo_name} ${_('Files')} - ${c.rhodecode_name}";
41 var CACHE = {};
41 var current_url = "${h.url.current()}";
42 var CACHE_EXPIRE = 60*1000; //cache for 60s
42 var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision=c.changeset.raw_id,f_path='__FPATH__')}';
43
43 var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.path)}';
44 var ypjax_links = function(){
44 fileBrowserListeners(current_url, node_list_url, url_base);
45 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
46
47 //don't do ypjax on middle click
48 if(e.which == 2){
49 return true;
50 }
51 var el = e.currentTarget;
52 var url = el.href;
53
54 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
55 _base_url = _base_url.replace('//','/')
56
57 //extract rev and the f_path from url.
58 parts = url.split(_base_url)
59 if(parts.length != 2){
60 return false;
61 }
62
63 var parts2 = parts[1].split('/');
64 var rev = parts2.shift(); // pop the first element which is the revision
65 var f_path = parts2.join('/');
66
67 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
68
69 //used to construct links from the search list
70 var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
71 node_list_url = node_list_url.replace('__REV__',rev);
72 //send the nodelist request to this url
73 var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
74 url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path)
75
76 // Change our States and save some data for handling events
77 var data = {url:url,title:title, url_base:url_base,
78 node_list_url:node_list_url};
79 History.pushState(data, title, url);
80
81 //now we're sure that we can do ypjax things
82 YUE.preventDefault(e)
83 return false;
84 });
85 }
86
87 var callbacks = function(State){
88 ypjax_links();
89 tooltip_activate();
90 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
91 // Inform Google Analytics of the change
92 if ( typeof window.pageTracker !== 'undefined' ) {
93 window.pageTracker._trackPageview(State.url);
94 }
95 }
96
97 YUE.onDOMReady(function(){
98 ypjax_links();
99 var container = 'files_data';
100 //Bind to StateChange Event
101 History.Adapter.bind(window,'statechange',function(){
102 var State = History.getState();
103 cache_key = State.url;
104 //check if we have this request in cache maybe ?
105 var _cache_obj = CACHE[cache_key];
106 var _cur_time = new Date().getTime();
107 // get from cache if it's there and not yet expired !
108 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
109 YUD.get(container).innerHTML=_cache_obj[1];
110 YUD.setStyle(container,'opacity','1.0');
111
112 //callbacks after ypjax call
113 callbacks(State);
114 }
115 else{
116 ypjax(State.url,container,function(o){
117 //callbacks after ypjax call
118 callbacks(State);
119 if (o !== undefined){
120 //store our request in cache
121 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
122 CACHE[cache_key] = [_expire_on, o.responseText];
123 }
124 });
125 }
126 });
127
128 });
129
45 </script>
130 </script>
131
46 </%def>
132 </%def>
@@ -1,116 +1,116 b''
1 <%def name="file_class(node)">
1 <%def name="file_class(node)">
2 %if node.is_file():
2 %if node.is_file():
3 <%return "browser-file" %>
3 <%return "browser-file" %>
4 %else:
4 %else:
5 <%return "browser-dir"%>
5 <%return "browser-dir"%>
6 %endif
6 %endif
7 </%def>
7 </%def>
8 <div id="body" class="browserblock">
8 <div id="body" class="browserblock">
9 <div class="browser-header">
9 <div class="browser-header">
10 <div class="browser-nav">
10 <div class="browser-nav">
11 ${h.form(h.url.current())}
11 ${h.form(h.url.current())}
12 <div class="info_box">
12 <div class="info_box">
13 <span class="rev">${_('view')}@rev</span>
13 <span class="rev">${_('view')}@rev</span>
14 <a class="ui-btn" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
14 <a class="ui-btn ypjax-link" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
16 <a class="ui-btn" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
16 <a class="ui-btn ypjax-link" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
17 ## ${h.submit('view',_('view'),class_="ui-btn")}
17 ## ${h.submit('view',_('view'),class_="ui-btn")}
18 </div>
18 </div>
19 ${h.end_form()}
19 ${h.end_form()}
20 </div>
20 </div>
21 <div class="browser-branch">
21 <div class="browser-branch">
22 ${h.checkbox('stay_at_branch',c.changeset.branch,c.changeset.branch==c.branch)}
22 ${h.checkbox('stay_at_branch',c.changeset.branch,c.changeset.branch==c.branch)}
23 <label>${_('follow current branch')}</label>
23 <label>${_('follow current branch')}</label>
24 </div>
24 </div>
25 <div class="browser-search">
25 <div class="browser-search">
26 <div id="search_activate_id" class="search_activate">
26 <div id="search_activate_id" class="search_activate">
27 <a class="ui-btn" id="filter_activate" href="#">${_('search file list')}</a>
27 <a class="ui-btn" id="filter_activate" href="#">${_('search file list')}</a>
28 </div>
28 </div>
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 <div id="add_node_id" class="add_node">
30 <div id="add_node_id" class="add_node">
31 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
31 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
32 </div>
32 </div>
33 % endif
33 % endif
34 <div>
34 <div>
35 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
35 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
36 <div id="node_filter_box" style="display:none">
36 <div id="node_filter_box" style="display:none">
37 ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input class="init" type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off">
37 ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input class="init" type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off">
38 </div>
38 </div>
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42
42
43 <div class="browser-body">
43 <div class="browser-body">
44 <table class="code-browser">
44 <table class="code-browser">
45 <thead>
45 <thead>
46 <tr>
46 <tr>
47 <th>${_('Name')}</th>
47 <th>${_('Name')}</th>
48 <th>${_('Size')}</th>
48 <th>${_('Size')}</th>
49 <th>${_('Mimetype')}</th>
49 <th>${_('Mimetype')}</th>
50 <th>${_('Last Revision')}</th>
50 <th>${_('Last Revision')}</th>
51 <th>${_('Last modified')}</th>
51 <th>${_('Last modified')}</th>
52 <th>${_('Last commiter')}</th>
52 <th>${_('Last commiter')}</th>
53 </tr>
53 </tr>
54 </thead>
54 </thead>
55
55
56 <tbody id="tbody">
56 <tbody id="tbody">
57 %if c.file.parent:
57 %if c.file.parent:
58 <tr class="parity0">
58 <tr class="parity0">
59 <td>
59 <td>
60 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
60 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
61 </td>
61 </td>
62 <td></td>
62 <td></td>
63 <td></td>
63 <td></td>
64 <td></td>
64 <td></td>
65 <td></td>
65 <td></td>
66 <td></td>
66 <td></td>
67 </tr>
67 </tr>
68 %endif
68 %endif
69
69
70 %for cnt,node in enumerate(c.file):
70 %for cnt,node in enumerate(c.file):
71 <tr class="parity${cnt%2}">
71 <tr class="parity${cnt%2}">
72 <td>
72 <td>
73 %if node.is_submodule():
73 %if node.is_submodule():
74 ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")}
74 ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")}
75 %else:
75 %else:
76 ${h.link_to(node.name, h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
76 ${h.link_to(node.name, h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
77 %endif:
77 %endif:
78 </td>
78 </td>
79 <td>
79 <td>
80 %if node.is_file():
80 %if node.is_file():
81 ${h.format_byte_size(node.size,binary=True)}
81 ${h.format_byte_size(node.size,binary=True)}
82 %endif
82 %endif
83 </td>
83 </td>
84 <td>
84 <td>
85 %if node.is_file():
85 %if node.is_file():
86 ${node.mimetype}
86 ${node.mimetype}
87 %endif
87 %endif
88 </td>
88 </td>
89 <td>
89 <td>
90 %if node.is_file():
90 %if node.is_file():
91 <div class="tooltip" title="${h.tooltip(node.last_changeset.message)}">
91 <div class="tooltip" title="${h.tooltip(node.last_changeset.message)}">
92 <pre>${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</pre>
92 <pre>${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</pre>
93 </div>
93 </div>
94 %endif
94 %endif
95 </td>
95 </td>
96 <td>
96 <td>
97 %if node.is_file():
97 %if node.is_file():
98 <span class="tooltip" title="${h.tooltip(h.fmt_date(node.last_changeset.date))}">
98 <span class="tooltip" title="${h.tooltip(h.fmt_date(node.last_changeset.date))}">
99 ${h.age(node.last_changeset.date)}</span>
99 ${h.age(node.last_changeset.date)}</span>
100 %endif
100 %endif
101 </td>
101 </td>
102 <td>
102 <td>
103 %if node.is_file():
103 %if node.is_file():
104 <span title="${node.last_changeset.author}">
104 <span title="${node.last_changeset.author}">
105 ${h.person(node.last_changeset.author)}
105 ${h.person(node.last_changeset.author)}
106 </span>
106 </span>
107 %endif
107 %endif
108 </td>
108 </td>
109 </tr>
109 </tr>
110 %endfor
110 %endfor
111 </tbody>
111 </tbody>
112 <tbody id="tbody_filtered" style="display:none">
112 <tbody id="tbody_filtered" style="display:none">
113 </tbody>
113 </tbody>
114 </table>
114 </table>
115 </div>
115 </div>
116 </div>
116 </div>
@@ -1,18 +1,18 b''
1 %if c.file:
1 %if c.file:
2 <h3 class="files_location">
2 <h3 class="files_location">
3 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
3 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
4 %if c.annotate:
4 %if c.annotate:
5 - ${_('annotation')}
5 - ${_('annotation')}
6 %endif
6 %endif
7 </h3>
7 </h3>
8 %if c.file.is_dir():
8 %if c.file.is_dir():
9 <%include file='files_browser.html'/>
9 <%include file='files_browser.html'/>
10 %else:
10 %else:
11 <%include file='files_source.html'/>
11 <%include file='files_source.html'/>
12 %endif
12 %endif
13 %else:
13 %else:
14 <h2>
14 <h2>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
16 ${_('No files at given path')}: "${c.f_path or "/"}"
16 ${_('No files at given path')}: "${c.f_path or "/"}"
17 </h2>
17 </h2>
18 %endif
18 %endif
@@ -1,209 +1,211 b''
1 <%page args="parent" />
1 <%page args="parent" />
2 <div class="box">
2 <div class="box">
3 <!-- box / title -->
3 <!-- box / title -->
4 <div class="title">
4 <div class="title">
5 <h5>
5 <h5>
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 </h5>
7 </h5>
8 %if c.rhodecode_user.username != 'default':
8 %if c.rhodecode_user.username != 'default':
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
10 <ul class="links">
10 <ul class="links">
11 <li>
11 <li>
12 %if c.group:
12 %if c.group:
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
14 %else:
14 %else:
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
16 %endif
16 %endif
17 </li>
17 </li>
18 </ul>
18 </ul>
19 %endif
19 %endif
20 %endif
20 %endif
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23 <div class="table">
23 <div class="table">
24 % if c.groups:
24 % if c.groups:
25 <div id='groups_list_wrap' class="yui-skin-sam">
25 <div id='groups_list_wrap' class="yui-skin-sam">
26 <table id="groups_list">
26 <table id="groups_list">
27 <thead>
27 <thead>
28 <tr>
28 <tr>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
32 </tr>
32 </tr>
33 </thead>
33 </thead>
34
34
35 ## REPO GROUPS
35 ## REPO GROUPS
36 % for gr in c.groups:
36 % for gr in c.groups:
37 <tr>
37 <tr>
38 <td>
38 <td>
39 <div style="white-space: nowrap">
39 <div style="white-space: nowrap">
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
42 </div>
42 </div>
43 </td>
43 </td>
44 %if c.visual.stylify_metatags:
44 %if c.visual.stylify_metatags:
45 <td>${h.desc_stylize(gr.group_description)}</td>
45 <td>${h.desc_stylize(gr.group_description)}</td>
46 %else:
46 %else:
47 <td>${gr.group_description}</td>
47 <td>${gr.group_description}</td>
48 %endif
48 %endif
49 ## this is commented out since for multi nested repos can be HEAVY!
49 ## this is commented out since for multi nested repos can be HEAVY!
50 ## in number of executed queries during traversing uncomment at will
50 ## in number of executed queries during traversing uncomment at will
51 ##<td><b>${gr.repositories_recursive_count}</b></td>
51 ##<td><b>${gr.repositories_recursive_count}</b></td>
52 </tr>
52 </tr>
53 % endfor
53 % endfor
54
54
55 </table>
55 </table>
56 </div>
56 </div>
57 <div style="height: 20px"></div>
57 <div style="height: 20px"></div>
58 % endif
58 % endif
59 <div id="welcome" style="display:none;text-align:center">
59 <div id="welcome" style="display:none;text-align:center">
60 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
60 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
61 </div>
61 </div>
62 <div id='repos_list_wrap' class="yui-skin-sam">
62 <div id='repos_list_wrap' class="yui-skin-sam">
63 <%cnt=0%>
63 <%cnt=0%>
64 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
64 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
65
65
66 <table id="repos_list">
66 <table id="repos_list">
67 <thead>
67 <thead>
68 <tr>
68 <tr>
69 <th class="left"></th>
69 <th class="left"></th>
70 <th class="left">${_('Name')}</th>
70 <th class="left">${_('Name')}</th>
71 <th class="left">${_('Description')}</th>
71 <th class="left">${_('Description')}</th>
72 <th class="left">${_('Last change')}</th>
72 <th class="left">${_('Last change')}</th>
73 <th class="left">${_('Tip')}</th>
73 <th class="left">${_('Tip')}</th>
74 <th class="left">${_('Owner')}</th>
74 <th class="left">${_('Owner')}</th>
75 <th class="left">${_('RSS')}</th>
75 <th class="left">${_('RSS')}</th>
76 <th class="left">${_('Atom')}</th>
76 <th class="left">${_('Atom')}</th>
77 </tr>
77 </tr>
78 </thead>
78 </thead>
79 <tbody>
79 <tbody>
80 %for cnt,repo in enumerate(c.repos_list):
80 %for cnt,repo in enumerate(c.repos_list):
81 <tr class="parity${(cnt+1)%2}">
81 <tr class="parity${(cnt+1)%2}">
82 ##QUICK MENU
82 ##QUICK MENU
83 <td class="quick_repo_menu">
83 <td class="quick_repo_menu">
84 ${dt.quick_menu(repo['name'])}
84 ${dt.quick_menu(repo['name'])}
85 </td>
85 </td>
86 ##REPO NAME AND ICONS
86 ##REPO NAME AND ICONS
87 <td class="reponame">
87 <td class="reponame">
88 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
88 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
89 </td>
89 </td>
90 ##DESCRIPTION
90 ##DESCRIPTION
91 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
91 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
92 %if c.visual.stylify_metatags:
92 %if c.visual.stylify_metatags:
93 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
93 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
94 %else:
94 %else:
95 ${h.truncate(repo['description'],60)}</span>
95 ${h.truncate(repo['description'],60)}</span>
96 %endif
96 %endif
97 </td>
97 </td>
98 ##LAST CHANGE DATE
98 ##LAST CHANGE DATE
99 <td>
99 <td>
100 <span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
100 <span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
101 </td>
101 </td>
102 ##LAST REVISION
102 ##LAST REVISION
103 <td>
103 <td>
104 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
104 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
105 </td>
105 </td>
106 ##
106 ##
107 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
107 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
108 <td>
108 <td>
109 %if c.rhodecode_user.username != 'default':
109 %if c.rhodecode_user.username != 'default':
110 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
110 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
111 %else:
111 %else:
112 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
112 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
113 %endif:
113 %endif:
114 </td>
114 </td>
115 <td>
115 <td>
116 %if c.rhodecode_user.username != 'default':
116 %if c.rhodecode_user.username != 'default':
117 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
117 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
118 %else:
118 %else:
119 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
119 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
120 %endif:
120 %endif:
121 </td>
121 </td>
122 </tr>
122 </tr>
123 %endfor
123 %endfor
124 </tbody>
124 </tbody>
125 </table>
125 </table>
126 </div>
126 </div>
127 </div>
127 </div>
128 </div>
128 </div>
129 <script>
129 <script>
130 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
130 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
131 var func = function(node){
131 var func = function(node){
132 return node.parentNode.parentNode.parentNode.parentNode;
132 return node.parentNode.parentNode.parentNode.parentNode;
133 }
133 }
134
134
135 var sort_by = "name";
136 var sort_dir = "asc";
135
137
136 // groups table sorting
138 // groups table sorting
137 var myColumnDefs = [
139 var myColumnDefs = [
138 {key:"name",label:"${_('Group Name')}",sortable:true,
140 {key:"name",label:"${_('Group Name')}",sortable:true,
139 sortOptions: { sortFunction: groupNameSort }},
141 sortOptions: { sortFunction: groupNameSort }},
140 {key:"desc",label:"${_('Description')}",sortable:true},
142 {key:"desc",label:"${_('Description')}",sortable:true},
141 ];
143 ];
142
144
143 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
145 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
144
146
145 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
147 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
146 myDataSource.responseSchema = {
148 myDataSource.responseSchema = {
147 fields: [
149 fields: [
148 {key:"name"},
150 {key:"name"},
149 {key:"desc"},
151 {key:"desc"},
150 ]
152 ]
151 };
153 };
152
154
153 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
155 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
154 {
156 {
155 sortedBy:{key:"name",dir:"asc"},
157 sortedBy:{key:"name",dir:"asc"},
156 MSG_SORTASC:"${_('Click to sort ascending')}",
158 MSG_SORTASC:"${_('Click to sort ascending')}",
157 MSG_SORTDESC:"${_('Click to sort descending')}"
159 MSG_SORTDESC:"${_('Click to sort descending')}"
158 }
160 }
159 );
161 );
160
162
161 // main table sorting
163 // main table sorting
162 var myColumnDefs = [
164 var myColumnDefs = [
163 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
165 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
164 {key:"name",label:"${_('Name')}",sortable:true,
166 {key:"name",label:"${_('Name')}",sortable:true,
165 sortOptions: { sortFunction: nameSort }},
167 sortOptions: { sortFunction: nameSort }},
166 {key:"desc",label:"${_('Description')}",sortable:true},
168 {key:"desc",label:"${_('Description')}",sortable:true},
167 {key:"last_change",label:"${_('Last Change')}",sortable:true,
169 {key:"last_change",label:"${_('Last Change')}",sortable:true,
168 sortOptions: { sortFunction: ageSort }},
170 sortOptions: { sortFunction: ageSort }},
169 {key:"tip",label:"${_('Tip')}",sortable:true,
171 {key:"tip",label:"${_('Tip')}",sortable:true,
170 sortOptions: { sortFunction: revisionSort }},
172 sortOptions: { sortFunction: revisionSort }},
171 {key:"owner",label:"${_('Owner')}",sortable:true},
173 {key:"owner",label:"${_('Owner')}",sortable:true},
172 {key:"rss",label:"",sortable:false},
174 {key:"rss",label:"",sortable:false},
173 {key:"atom",label:"",sortable:false},
175 {key:"atom",label:"",sortable:false},
174 ];
176 ];
175
177
176 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
178 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
177
179
178 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
180 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
179
181
180 myDataSource.responseSchema = {
182 myDataSource.responseSchema = {
181 fields: [
183 fields: [
182 {key:"menu"},
184 {key:"menu"},
183 {key:"name"},
185 {key:"name"},
184 {key:"desc"},
186 {key:"desc"},
185 {key:"last_change"},
187 {key:"last_change"},
186 {key:"tip"},
188 {key:"tip"},
187 {key:"owner"},
189 {key:"owner"},
188 {key:"rss"},
190 {key:"rss"},
189 {key:"atom"},
191 {key:"atom"},
190 ]
192 ]
191 };
193 };
192
194
193 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
195 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
194 {
196 {
195 sortedBy:{key:"name",dir:"asc"},
197 sortedBy:{key:sort_by,dir:sort_dir},
196 MSG_SORTASC:"${_('Click to sort ascending')}",
198 MSG_SORTASC:"${_('Click to sort ascending')}",
197 MSG_SORTDESC:"${_('Click to sort descending')}",
199 MSG_SORTDESC:"${_('Click to sort descending')}",
198 MSG_EMPTY:"${_('No records found.')}",
200 MSG_EMPTY:"${_('No records found.')}",
199 MSG_ERROR:"${_('Data error.')}",
201 MSG_ERROR:"${_('Data error.')}",
200 MSG_LOADING:"${_('Loading...')}",
202 MSG_LOADING:"${_('Loading...')}",
201 }
203 }
202 );
204 );
203 myDataTable.subscribe('postRenderEvent',function(oArgs) {
205 myDataTable.subscribe('postRenderEvent',function(oArgs) {
204 tooltip_activate();
206 tooltip_activate();
205 quick_repo_menu();
207 quick_repo_menu();
206 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
208 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
207 });
209 });
208
210
209 </script>
211 </script>
@@ -1,298 +1,298 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode - Pylons environment configuration #
3 # RhodeCode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 pdebug = false
10 pdebug = false
11 ################################################################################
11 ################################################################################
12 ## Uncomment and replace with the address which should receive ##
12 ## Uncomment and replace with the address which should receive ##
13 ## any error reports after application crash ##
13 ## any error reports after application crash ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 ################################################################################
15 ################################################################################
16 #email_to = admin@localhost
16 #email_to = admin@localhost
17 #error_email_from = paste_error@localhost
17 #error_email_from = paste_error@localhost
18 #app_email_from = rhodecode-noreply@localhost
18 #app_email_from = rhodecode-noreply@localhost
19 #error_message =
19 #error_message =
20 #email_prefix = [RhodeCode]
20 #email_prefix = [RhodeCode]
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
27 #smtp_use_ssl = true
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 #smtp_auth =
29 #smtp_auth =
30
30
31 [server:main]
31 [server:main]
32 ##nr of threads to spawn
32 ##nr of threads to spawn
33 #threadpool_workers = 5
33 #threadpool_workers = 5
34
34
35 ##max request before thread respawn
35 ##max request before thread respawn
36 #threadpool_max_requests = 2
36 #threadpool_max_requests = 2
37
37
38 ##option to use threads of process
38 ##option to use threads of process
39 #use_threadpool = true
39 #use_threadpool = true
40
40
41 #use = egg:Paste#http
41 #use = egg:Paste#http
42 use = egg:waitress#main
42 use = egg:waitress#main
43 host = 127.0.0.1
43 host = 127.0.0.1
44 port = 5000
44 port = 5000
45
45
46 [app:main]
46 [app:main]
47 use = egg:rhodecode
47 use = egg:rhodecode
48 full_stack = true
48 full_stack = true
49 static_files = true
49 static_files = true
50 lang=en
50 lang=en
51 cache_dir = /tmp/data
51 cache_dir = /tmp/data
52 index_dir = /tmp/index
52 index_dir = /tmp/index
53 app_instance_uuid = develop-test
53 app_instance_uuid = develop-test
54 cut_off_limit = 256000
54 cut_off_limit = 256000
55 force_https = false
55 force_https = false
56 commit_parse_limit = 25
56 commit_parse_limit = 25
57 use_gravatar = true
57 use_gravatar = true
58 container_auth_enabled = false
58 container_auth_enabled = false
59 proxypass_auth_enabled = false
59 proxypass_auth_enabled = false
60
60
61
61
62 ## overwrite schema of clone url
62 ## overwrite schema of clone url
63 ## available vars:
63 ## available vars:
64 ## scheme - http/https
64 ## scheme - http/https
65 ## user - current user
65 ## user - current user
66 ## pass - password
66 ## pass - password
67 ## netloc - network location
67 ## netloc - network location
68 ## path - usually repo_name
68 ## path - usually repo_name
69
69
70 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
70 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
71
71
72 ## issue tracking mapping for commits messages
72 ## issue tracking mapping for commits messages
73 ## comment out issue_pat, issue_server, issue_prefix to enable
73 ## comment out issue_pat, issue_server, issue_prefix to enable
74
74
75 ## pattern to get the issues from commit messages
75 ## pattern to get the issues from commit messages
76 ## default one used here is #<numbers> with a regex passive group for `#`
76 ## default one used here is #<numbers> with a regex passive group for `#`
77 ## {id} will be all groups matched from this pattern
77 ## {id} will be all groups matched from this pattern
78
78
79 issue_pat = (?:\s*#)(\d+)
79 issue_pat = (?:\s*#)(\d+)
80
80
81 ## server url to the issue, each {id} will be replaced with match
81 ## server url to the issue, each {id} will be replaced with match
82 ## fetched from the regex and {repo} is replaced with repository name
82 ## fetched from the regex and {repo} is replaced with repository name
83
83
84 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
84 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
85
85
86 ## prefix to add to link to indicate it's an url
86 ## prefix to add to link to indicate it's an url
87 ## #314 will be replaced by <issue_prefix><id>
87 ## #314 will be replaced by <issue_prefix><id>
88
88
89 issue_prefix = #
89 issue_prefix = #
90
90
91 ## instance-id prefix
91 ## instance-id prefix
92 ## a prefix key for this instance used for cache invalidation when running
92 ## a prefix key for this instance used for cache invalidation when running
93 ## multiple instances of rhodecode, make sure it's globally unique for
93 ## multiple instances of rhodecode, make sure it's globally unique for
94 ## all running rhodecode instances. Leave empty if you don't use it
94 ## all running rhodecode instances. Leave empty if you don't use it
95 instance_id =
95 instance_id =
96
96
97 ## alternative return HTTP header for failed authentication. Default HTTP
97 ## alternative return HTTP header for failed authentication. Default HTTP
98 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
98 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
99 ## handling that. Set this variable to 403 to return HTTPForbidden
99 ## handling that. Set this variable to 403 to return HTTPForbidden
100 auth_ret_code =
100 auth_ret_code =
101
101
102 ####################################
102 ####################################
103 ### CELERY CONFIG ####
103 ### CELERY CONFIG ####
104 ####################################
104 ####################################
105 use_celery = false
105 use_celery = false
106 broker.host = localhost
106 broker.host = localhost
107 broker.vhost = rabbitmqhost
107 broker.vhost = rabbitmqhost
108 broker.port = 5672
108 broker.port = 5672
109 broker.user = rabbitmq
109 broker.user = rabbitmq
110 broker.password = qweqwe
110 broker.password = qweqwe
111
111
112 celery.imports = rhodecode.lib.celerylib.tasks
112 celery.imports = rhodecode.lib.celerylib.tasks
113
113
114 celery.result.backend = amqp
114 celery.result.backend = amqp
115 celery.result.dburi = amqp://
115 celery.result.dburi = amqp://
116 celery.result.serialier = json
116 celery.result.serialier = json
117
117
118 #celery.send.task.error.emails = true
118 #celery.send.task.error.emails = true
119 #celery.amqp.task.result.expires = 18000
119 #celery.amqp.task.result.expires = 18000
120
120
121 celeryd.concurrency = 2
121 celeryd.concurrency = 2
122 #celeryd.log.file = celeryd.log
122 #celeryd.log.file = celeryd.log
123 celeryd.log.level = debug
123 celeryd.log.level = debug
124 celeryd.max.tasks.per.child = 1
124 celeryd.max.tasks.per.child = 1
125
125
126 #tasks will never be sent to the queue, but executed locally instead.
126 #tasks will never be sent to the queue, but executed locally instead.
127 celery.always.eager = false
127 celery.always.eager = false
128
128
129 ####################################
129 ####################################
130 ### BEAKER CACHE ####
130 ### BEAKER CACHE ####
131 ####################################
131 ####################################
132 beaker.cache.data_dir=/tmp/data/cache/data
132 beaker.cache.data_dir=/tmp/data/cache/data
133 beaker.cache.lock_dir=/tmp/data/cache/lock
133 beaker.cache.lock_dir=/tmp/data/cache/lock
134
134
135 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
135 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
136
136
137 beaker.cache.super_short_term.type=memory
137 beaker.cache.super_short_term.type=memory
138 beaker.cache.super_short_term.expire=1
138 beaker.cache.super_short_term.expire=1
139 beaker.cache.super_short_term.key_length = 256
139 beaker.cache.super_short_term.key_length = 256
140
140
141 beaker.cache.short_term.type=memory
141 beaker.cache.short_term.type=memory
142 beaker.cache.short_term.expire=60
142 beaker.cache.short_term.expire=60
143 beaker.cache.short_term.key_length = 256
143 beaker.cache.short_term.key_length = 256
144
144
145 beaker.cache.long_term.type=memory
145 beaker.cache.long_term.type=memory
146 beaker.cache.long_term.expire=36000
146 beaker.cache.long_term.expire=36000
147 beaker.cache.long_term.key_length = 256
147 beaker.cache.long_term.key_length = 256
148
148
149 beaker.cache.sql_cache_short.type=memory
149 beaker.cache.sql_cache_short.type=memory
150 beaker.cache.sql_cache_short.expire=1
150 beaker.cache.sql_cache_short.expire=1
151 beaker.cache.sql_cache_short.key_length = 256
151 beaker.cache.sql_cache_short.key_length = 256
152
152
153 beaker.cache.sql_cache_med.type=memory
153 beaker.cache.sql_cache_med.type=memory
154 beaker.cache.sql_cache_med.expire=360
154 beaker.cache.sql_cache_med.expire=360
155 beaker.cache.sql_cache_med.key_length = 256
155 beaker.cache.sql_cache_med.key_length = 256
156
156
157 beaker.cache.sql_cache_long.type=file
157 beaker.cache.sql_cache_long.type=file
158 beaker.cache.sql_cache_long.expire=3600
158 beaker.cache.sql_cache_long.expire=3600
159 beaker.cache.sql_cache_long.key_length = 256
159 beaker.cache.sql_cache_long.key_length = 256
160
160
161 ####################################
161 ####################################
162 ### BEAKER SESSION ####
162 ### BEAKER SESSION ####
163 ####################################
163 ####################################
164 ## Type of storage used for the session, current types are
164 ## Type of storage used for the session, current types are
165 ## dbm, file, memcached, database, and memory.
165 ## dbm, file, memcached, database, and memory.
166 ## The storage uses the Container API
166 ## The storage uses the Container API
167 ## that is also used by the cache system.
167 ## that is also used by the cache system.
168
168
169 ## db session example
169 ## db session example
170
170
171 #beaker.session.type = ext:database
171 #beaker.session.type = ext:database
172 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
172 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
173 #beaker.session.table_name = db_session
173 #beaker.session.table_name = db_session
174
174
175 ## encrypted cookie session, good for many instances
175 ## encrypted cookie session, good for many instances
176 #beaker.session.type = cookie
176 #beaker.session.type = cookie
177
177
178 beaker.session.type = file
178 beaker.session.type = file
179 beaker.session.key = rhodecode
179 beaker.session.key = rhodecode
180 # secure cookie requires AES python libraries
180 # secure cookie requires AES python libraries
181 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
181 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
182 #beaker.session.validate_key = 9712sds2212c--zxc123
182 #beaker.session.validate_key = 9712sds2212c--zxc123
183 beaker.session.timeout = 36000
183 beaker.session.timeout = 36000
184 beaker.session.httponly = true
184 beaker.session.httponly = true
185
185
186 ## uncomment for https secure cookie
186 ## uncomment for https secure cookie
187 beaker.session.secure = false
187 beaker.session.secure = false
188
188
189 ##auto save the session to not to use .save()
189 ##auto save the session to not to use .save()
190 beaker.session.auto = False
190 beaker.session.auto = False
191
191
192 ##true exire at browser close
192 ##true exire at browser close
193 #beaker.session.cookie_expires = 3600
193 #beaker.session.cookie_expires = 3600
194
194
195
195
196 ################################################################################
196 ################################################################################
197 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
197 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
198 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
198 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
199 ## execute malicious code after an exception is raised. ##
199 ## execute malicious code after an exception is raised. ##
200 ################################################################################
200 ################################################################################
201 #set debug = false
201 #set debug = false
202
202
203 ##################################
203 ##################################
204 ### LOGVIEW CONFIG ###
204 ### LOGVIEW CONFIG ###
205 ##################################
205 ##################################
206 logview.sqlalchemy = #faa
206 logview.sqlalchemy = #faa
207 logview.pylons.templating = #bfb
207 logview.pylons.templating = #bfb
208 logview.pylons.util = #eee
208 logview.pylons.util = #eee
209
209
210 #########################################################
210 #########################################################
211 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
211 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
212 #########################################################
212 #########################################################
213 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.sqlite
213 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.sqlite
214 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_test
214 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_test
215 #sqlalchemy.db1.url = mysql://root:qwe@localhost/rhodecode_test
215 #sqlalchemy.db1.url = mysql://root:qwe@localhost/rhodecode_test
216
216
217 sqlalchemy.db1.echo = false
217 sqlalchemy.db1.echo = false
218 sqlalchemy.db1.pool_recycle = 3600
218 sqlalchemy.db1.pool_recycle = 3600
219 sqlalchemy.db1.convert_unicode = true
219 sqlalchemy.db1.convert_unicode = true
220
220
221 ################################
221 ################################
222 ### LOGGING CONFIGURATION ####
222 ### LOGGING CONFIGURATION ####
223 ################################
223 ################################
224 [loggers]
224 [loggers]
225 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
225 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
226
226
227 [handlers]
227 [handlers]
228 keys = console
228 keys = console
229
229
230 [formatters]
230 [formatters]
231 keys = generic, color_formatter
231 keys = generic, color_formatter
232
232
233 #############
233 #############
234 ## LOGGERS ##
234 ## LOGGERS ##
235 #############
235 #############
236 [logger_root]
236 [logger_root]
237 level = ERROR
237 level = DEBUG
238 handlers = console
238 handlers = console
239
239
240 [logger_routes]
240 [logger_routes]
241 level = ERROR
241 level = DEBUG
242 handlers =
242 handlers =
243 qualname = routes.middleware
243 qualname = routes.middleware
244 # "level = DEBUG" logs the route matched and routing variables.
244 # "level = DEBUG" logs the route matched and routing variables.
245 propagate = 1
245 propagate = 1
246
246
247 [logger_beaker]
247 [logger_beaker]
248 level = DEBUG
248 level = DEBUG
249 handlers =
249 handlers =
250 qualname = beaker.container
250 qualname = beaker.container
251 propagate = 1
251 propagate = 1
252
252
253 [logger_templates]
253 [logger_templates]
254 level = INFO
254 level = INFO
255 handlers =
255 handlers =
256 qualname = pylons.templating
256 qualname = pylons.templating
257 propagate = 1
257 propagate = 1
258
258
259 [logger_rhodecode]
259 [logger_rhodecode]
260 level = ERROR
260 level = DEBUG
261 handlers =
261 handlers =
262 qualname = rhodecode
262 qualname = rhodecode
263 propagate = 1
263 propagate = 1
264
264
265 [logger_sqlalchemy]
265 [logger_sqlalchemy]
266 level = ERROR
266 level = ERROR
267 handlers = console
267 handlers = console
268 qualname = sqlalchemy.engine
268 qualname = sqlalchemy.engine
269 propagate = 0
269 propagate = 0
270
270
271 [logger_whoosh_indexer]
271 [logger_whoosh_indexer]
272 level = DEBUG
272 level = DEBUG
273 handlers =
273 handlers =
274 qualname = whoosh_indexer
274 qualname = whoosh_indexer
275 propagate = 1
275 propagate = 1
276
276
277 ##############
277 ##############
278 ## HANDLERS ##
278 ## HANDLERS ##
279 ##############
279 ##############
280
280
281 [handler_console]
281 [handler_console]
282 class = StreamHandler
282 class = StreamHandler
283 args = (sys.stderr,)
283 args = (sys.stderr,)
284 level = NOTSET
284 level = NOTSET
285 formatter = generic
285 formatter = generic
286
286
287 ################
287 ################
288 ## FORMATTERS ##
288 ## FORMATTERS ##
289 ################
289 ################
290
290
291 [formatter_generic]
291 [formatter_generic]
292 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
292 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
293 datefmt = %Y-%m-%d %H:%M:%S
293 datefmt = %Y-%m-%d %H:%M:%S
294
294
295 [formatter_color_formatter]
295 [formatter_color_formatter]
296 class=rhodecode.lib.colored_formatter.ColorFormatter
296 class=rhodecode.lib.colored_formatter.ColorFormatter
297 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
297 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
298 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
298 datefmt = %Y-%m-%d %H:%M:%S
General Comments 0
You need to be logged in to leave comments. Login now