diff options
author | Nathan Kinkade <nkinkade@nkinka.de> | 2010-02-02 02:59:20 +0000 |
---|---|---|
committer | Nathan Kinkade <nkinkade@nkinka.de> | 2010-02-02 02:59:20 +0000 |
commit | 9d0927dda936756f1f5003813f437d714fe481f8 (patch) | |
tree | fe1b887345b37387ab0ddcfd78bf344f6150b6cc | |
parent | a6f794c20dc3592bcaef17c622413c1b670a20d8 (diff) | |
parent | 43985ea2fb137aa7d532617271e37d7c20def3c5 (diff) |
Merge branch 'master' of git://github.com/gallery/gallery3
166 files changed, 3464 insertions, 3349 deletions
@@ -27,7 +27,8 @@ version_compare(PHP_VERSION, "5.2.3", "<") and // Gallery requires short_tags to be on !ini_get("short_open_tag") and exit("Gallery requires short_open_tag to be on."); -// Set the error reporting level. Use E_ALL unless you have a special need. +// Suppress errors. For information on how to debug Gallery 3, see: +// http://codex.gallery2.org/Gallery3:FAQ#How_do_I_see_debug_information.3F error_reporting(0); // Disabling display_errors will effectively disable Kohana error display diff --git a/installer/init_var.php b/installer/init_var.php index c010888c..353665a3 100755 --- a/installer/init_var.php +++ b/installer/init_var.php @@ -7,3 +7,6 @@ !file_exists(VARPATH . "thumbs") && mkdir(VARPATH . "thumbs"); !file_exists(VARPATH . "tmp") && mkdir(VARPATH . "tmp"); !file_exists(VARPATH . "uploads") && mkdir(VARPATH . "uploads"); +file_put_contents(VARPATH . "logs/.htaccess", base64_decode("RGlyZWN0b3J5SW5kZXggLmh0YWNjZXNzClNldEhhbmRsZXIgR2FsbGVyeV9TZWN1cml0eV9Eb19Ob3RfUmVtb3ZlCk9wdGlvbnMgTm9uZQo8SWZNb2R1bGUgbW9kX3Jld3JpdGUuYz4KUmV3cml0ZUVuZ2luZSBvZmYKPC9JZk1vZHVsZT4KT3JkZXIgYWxsb3csZGVueQpEZW55IGZyb20gYWxsCg==")); +file_put_contents(VARPATH . "tmp/.htaccess", base64_decode("RGlyZWN0b3J5SW5kZXggLmh0YWNjZXNzClNldEhhbmRsZXIgR2FsbGVyeV9TZWN1cml0eV9Eb19Ob3RfUmVtb3ZlCk9wdGlvbnMgTm9uZQo8SWZNb2R1bGUgbW9kX3Jld3JpdGUuYz4KUmV3cml0ZUVuZ2luZSBvZmYKPC9JZk1vZHVsZT4KT3JkZXIgYWxsb3csZGVueQpEZW55IGZyb20gYWxsCg==")); +file_put_contents(VARPATH . "uploads/.htaccess", base64_decode("RGlyZWN0b3J5SW5kZXggLmh0YWNjZXNzClNldEhhbmRsZXIgR2FsbGVyeV9TZWN1cml0eV9Eb19Ob3RfUmVtb3ZlCk9wdGlvbnMgTm9uZQo8SWZNb2R1bGUgbW9kX3Jld3JpdGUuYz4KUmV3cml0ZUVuZ2luZSBvZmYKPC9JZk1vZHVsZT4KT3JkZXIgYWxsb3csZGVueQpEZW55IGZyb20gYWxsCg==")); diff --git a/installer/install.sql b/installer/install.sql index 95a57d86..20b632fa 100644 --- a/installer/install.sql +++ b/installer/install.sql @@ -74,6 +74,17 @@ CREATE TABLE {comments} ( PRIMARY KEY (`id`) ) DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; +DROP TABLE IF EXISTS {failed_logins}; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +CREATE TABLE {failed_logins} ( + `id` int(9) NOT NULL auto_increment, + `count` int(9) NOT NULL, + `name` varchar(255) NOT NULL, + `time` int(9) NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; +SET character_set_client = @saved_cs_client; DROP TABLE IF EXISTS {graphics_rules}; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; @@ -228,7 +239,7 @@ CREATE TABLE {modules} ( UNIQUE KEY `name` (`name`) ) AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -INSERT INTO {modules} VALUES (1,1,'gallery',21); +INSERT INTO {modules} VALUES (1,1,'gallery',25); INSERT INTO {modules} VALUES (2,1,'user',2); INSERT INTO {modules} VALUES (3,1,'comment',2); INSERT INTO {modules} VALUES (4,1,'organize',1); @@ -355,7 +366,7 @@ CREATE TABLE {users} ( ) AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; INSERT INTO {users} VALUES (1,'guest','Guest User','',0,0,NULL,0,1,NULL,NULL,NULL); -INSERT INTO {users} VALUES (2,'admin','Gallery Administrator','',0,0,NULL,1,0,NULL,NULL,NULL); +INSERT INTO {users} VALUES (2,'admin','Gallery Administrator','',0,0,'unknown@unknown.com',1,0,NULL,NULL,NULL); DROP TABLE IF EXISTS {vars}; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; @@ -366,7 +377,7 @@ CREATE TABLE {vars} ( `value` text, PRIMARY KEY (`id`), UNIQUE KEY `module_name` (`module_name`,`name`) -) AUTO_INCREMENT=37 DEFAULT CHARSET=utf8; +) AUTO_INCREMENT=39 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; INSERT INTO {vars} VALUES (NULL,'gallery','active_site_theme','wind'); INSERT INTO {vars} VALUES (NULL,'gallery','active_admin_theme','admin_wind'); @@ -385,8 +396,8 @@ INSERT INTO {vars} VALUES (NULL,'gallery','date_time_format','Y-M-d H:i:s'); INSERT INTO {vars} VALUES (NULL,'gallery','date_format','Y-M-d'); INSERT INTO {vars} VALUES (NULL,'gallery','blocks_dashboard_center','a:3:{i:6;a:2:{i:0;s:7:\"gallery\";i:1;s:7:\"welcome\";}i:7;a:2:{i:0;s:7:\"gallery\";i:1;s:12:\"photo_stream\";}i:8;a:2:{i:0;s:7:\"gallery\";i:1;s:11:\"log_entries\";}}'); INSERT INTO {vars} VALUES (NULL,'gallery','choose_default_tookit','1'); +INSERT INTO {vars} VALUES (NULL,'gallery','identity_provider','user'); INSERT INTO {vars} VALUES (NULL,'user','mininum_password_length','5'); INSERT INTO {vars} VALUES (NULL,'comment','spam_caught','0'); -INSERT INTO {vars} VALUES (NULL,'gallery','identity_provider','user'); INSERT INTO {vars} VALUES (NULL,'gallery','blocks_site_sidebar','a:3:{i:9;a:2:{i:0;s:4:\"info\";i:1;s:8:\"metadata\";}i:10;a:2:{i:0;s:3:\"rss\";i:1;s:9:\"rss_feeds\";}i:11;a:2:{i:0;s:3:\"tag\";i:1;s:3:\"tag\";}}'); INSERT INTO {vars} VALUES (NULL,'slideshow','max_scale','0'); diff --git a/lib/flowplayer.js b/lib/flowplayer.js index b1c33150..d4b619c3 100644 --- a/lib/flowplayer.js +++ b/lib/flowplayer.js @@ -1,24 +1,131 @@ -/** - * flowplayer.js 3.0.5. The Flowplayer API - * - * Copyright 2009 Flowplayer Oy - * - * This file is part of Flowplayer. - * - * Flowplayer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Flowplayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Flowplayer. If not, see <http://www.gnu.org/licenses/>. - * - * Version: 3.0.5 - Tue Feb 03 2009 13:14:17 GMT-0000 (GMT+00:00) - */ -(function(){function log(args){console.log("$f.fireEvent",[].slice.call(args));}function clone(obj){if(!obj||typeof obj!='object'){return obj;}var temp=new obj.constructor();for(var key in obj){if(obj.hasOwnProperty(key)){temp[key]=clone(obj[key]);}}return temp;}function each(obj,fn){if(!obj){return;}var name,i=0,length=obj.length;if(length===undefined){for(name in obj){if(fn.call(obj[name],name,obj[name])===false){break;}}}else{for(var value=obj[0];i<length&&fn.call(value,i,value)!==false;value=obj[++i]){}}return obj;}function el(id){return document.getElementById(id);}function extend(to,from,skipFuncs){if(to&&from){each(from,function(name,value){if(!skipFuncs||typeof value!='function'){to[name]=value;}});}}function select(query){var index=query.indexOf(".");if(index!=-1){var tag=query.substring(0,index)||"*";var klass=query.substring(index+1,query.length);var els=[];each(document.getElementsByTagName(tag),function(){if(this.className&&this.className.indexOf(klass)!=-1){els.push(this);}});return els;}}function stopEvent(e){e=e||window.event;if(e.preventDefault){e.stopPropagation();e.preventDefault();}else{e.returnValue=false;e.cancelBubble=true;}return false;}function bind(to,evt,fn){to[evt]=to[evt]||[];to[evt].push(fn);}function makeId(){return"_"+(""+Math.random()).substring(2,10);}var Clip=function(json,index,player){var self=this;var cuepoints={};var listeners={};self.index=index;if(typeof json=='string'){json={url:json};}extend(this,json,true);each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),function(){var evt="on"+this;if(evt.indexOf("*")!=-1){evt=evt.substring(0,evt.length-1);var before="onBefore"+evt.substring(2);self[before]=function(fn){bind(listeners,before,fn);return self;};}self[evt]=function(fn){bind(listeners,evt,fn);return self;};if(index==-1){if(self[before]){player[before]=self[before];}if(self[evt]){player[evt]=self[evt];}}});extend(this,{onCuepoint:function(points,fn){if(arguments.length==1){cuepoints.embedded=[null,points];return self;}if(typeof points=='number'){points=[points];}var fnId=makeId();cuepoints[fnId]=[points,fn];if(player.isLoaded()){player._api().fp_addCuepoints(points,index,fnId);}return self;},update:function(json){extend(self,json);if(player.isLoaded()){player._api().fp_updateClip(json,index);}var conf=player.getConfig();var clip=(index==-1)?conf.clip:conf.playlist[index];extend(clip,json,true);},_fireEvent:function(evt,arg1,arg2,target){if(evt=='onLoad'){each(cuepoints,function(key,val){if(val[0]){player._api().fp_addCuepoints(val[0],index,key);}});return false;}target=target||self;if(evt=='onCuepoint'){var fn=cuepoints[arg1];if(fn){return fn[1].call(player,target,arg2);}}if(evt=='onStart'||evt=='onUpdate'){extend(target,arg1);if(!target.duration){target.duration=arg1.metaData.duration;}else{target.fullDuration=arg1.metaData.duration;}}var ret=true;each(listeners[evt],function(){ret=this.call(player,target,arg1,arg2);});return ret;}});if(json.onCuepoint){var arg=json.onCuepoint;self.onCuepoint.apply(self,typeof arg=='function'?[arg]:arg);delete json.onCuepoint;}each(json,function(key,val){if(typeof val=='function'){bind(listeners,key,val);delete json[key];}});if(index==-1){player.onCuepoint=this.onCuepoint;}};var Plugin=function(name,json,player,fn){var listeners={};var self=this;var hasMethods=false;if(fn){extend(listeners,fn);}each(json,function(key,val){if(typeof val=='function'){listeners[key]=val;delete json[key];}});extend(this,{animate:function(props,speed,fn){if(!props){return self;}if(typeof speed=='function'){fn=speed;speed=500;}if(typeof props=='string'){var key=props;props={};props[key]=speed;speed=500;}if(fn){var fnId=makeId();listeners[fnId]=fn;}if(speed===undefined){speed=500;}json=player._api().fp_animate(name,props,speed,fnId);return self;},css:function(props,val){if(val!==undefined){var css={};css[props]=val;props=css;}json=player._api().fp_css(name,props);extend(self,json);return self;},show:function(){this.display='block';player._api().fp_showPlugin(name);return self;},hide:function(){this.display='none';player._api().fp_hidePlugin(name);return self;},toggle:function(){this.display=player._api().fp_togglePlugin(name);return self;},fadeTo:function(o,speed,fn){if(typeof speed=='function'){fn=speed;speed=500;}if(fn){var fnId=makeId();listeners[fnId]=fn;}this.display=player._api().fp_fadeTo(name,o,speed,fnId);this.opacity=o;return self;},fadeIn:function(speed,fn){return self.fadeTo(1,speed,fn);},fadeOut:function(speed,fn){return self.fadeTo(0,speed,fn);},getName:function(){return name;},_fireEvent:function(evt,arg){if(evt=='onUpdate'){var json=player._api().fp_getPlugin(name);if(!json){return;}extend(self,json);delete self.methods;if(!hasMethods){each(json.methods,function(){var method=""+this;self[method]=function(){var a=[].slice.call(arguments);var ret=player._api().fp_invoke(name,method,a);return ret=='undefined'?self:ret;};});hasMethods=true;}}var fn=listeners[evt];if(fn){fn.call(self,arg);if(evt.substring(0,1)=="_"){delete listeners[evt];}}}});};function Player(wrapper,params,conf){var -self=this,api=null,html,commonClip,playlist=[],plugins={},listeners={},playerId,apiId,playerIndex,activeIndex,swfHeight,wrapperHeight;extend(self,{id:function(){return playerId;},isLoaded:function(){return(api!==null);},getParent:function(){return wrapper;},hide:function(all){if(all){wrapper.style.height="0px";}if(api){api.style.height="0px";}return self;},show:function(){wrapper.style.height=wrapperHeight+"px";if(api){api.style.height=swfHeight+"px";}return self;},isHidden:function(){return api&&parseInt(api.style.height,10)===0;},load:function(fn){if(!api&&self._fireEvent("onBeforeLoad")!==false){each(players,function(){this.unload();});html=wrapper.innerHTML;flashembed(wrapper,params,{config:conf});if(fn){fn.cached=true;bind(listeners,"onLoad",fn);}}return self;},unload:function(){try{if(api&&api.fp_isFullscreen()){}}catch(error){return;}if(api&&html.replace(/\s/g,'')!==''&&!api.fp_isFullscreen()&&self._fireEvent("onBeforeUnload")!==false){api.fp_close();wrapper.innerHTML=html;self._fireEvent("onUnload");api=null;}return self;},getClip:function(index){if(index===undefined){index=activeIndex;}return playlist[index];},getCommonClip:function(){return commonClip;},getPlaylist:function(){return playlist;},getPlugin:function(name){var plugin=plugins[name];if(!plugin&&self.isLoaded()){var json=self._api().fp_getPlugin(name);if(json){plugin=new Plugin(name,json,self);plugins[name]=plugin;}}return plugin;},getScreen:function(){return self.getPlugin("screen");},getControls:function(){return self.getPlugin("controls");},getConfig:function(copy){return copy?clone(conf):conf;},getFlashParams:function(){return params;},loadPlugin:function(name,url,props,fn){if(typeof props=='function'){fn=props;props={};}var fnId=fn?makeId():"_";self._api().fp_loadPlugin(name,url,props,fnId);var arg={};arg[fnId]=fn;var p=new Plugin(name,null,self,arg);plugins[name]=p;return p;},getState:function(){return api?api.fp_getState():-1;},play:function(clip){function play(){if(clip!==undefined){self._api().fp_play(clip);}else{self._api().fp_play();}}if(api){play();}else{self.load(function(){play();});}return self;},getVersion:function(){var js="flowplayer.js 3.0.5";if(api){var ver=api.fp_getVersion();ver.push(js);return ver;}return js;},_api:function(){if(!api){throw"Flowplayer "+self.id()+" not loaded. Try moving your call to player's onLoad event";}return api;},_dump:function(){console.log(listeners);},setClip:function(clip){self.setPlaylist([clip]);},getIndex:function(){return playerIndex;}});each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,Fullscreen*,FullscreenExit,Error").split(","),function(){var name="on"+this;if(name.indexOf("*")!=-1){name=name.substring(0,name.length-1);var name2="onBefore"+name.substring(2);self[name2]=function(fn){bind(listeners,name2,fn);return self;};}self[name]=function(fn){bind(listeners,name,fn);return self;};});each(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,reset,close,setPlaylist").split(","),function(){var name=this;self[name]=function(arg){if(!api){return self;}var ret=(arg===undefined)?api["fp_"+name]():api["fp_"+name](arg);return ret=='undefined'?self:ret;};});self._fireEvent=function(evt,arg0,arg1,arg2){if(conf.debug){log(arguments);}if(!api&&evt=='onLoad'&&arg0=='player'){api=api||el(apiId);swfHeight=api.clientHeight;each(playlist,function(){this._fireEvent("onLoad");});each(plugins,function(name,p){p._fireEvent("onUpdate");});commonClip._fireEvent("onLoad");}if(evt=='onLoad'&&arg0!='player'){return;}if(evt=='onError'){if(typeof arg0=='string'||(typeof arg0=='number'&&typeof arg1=='number')){arg0=arg1;arg1=arg2;}}if(evt=='onContextMenu'){each(conf.contextMenu[arg0],function(key,fn){fn.call(self);});return;}if(evt=='onPluginEvent'){var name=arg0.name||arg0;var p=plugins[name];if(p){p._fireEvent("onUpdate",arg0);p._fireEvent(arg1);}return;}if(evt=='onPlaylistReplace'){playlist=[];var index=0;each(arg0,function(){playlist.push(new Clip(this,index++,self));});}var ret=true;if(typeof arg0=='number'&&arg0<playlist.length){activeIndex=arg0;var clip=playlist[arg0];if(clip){ret=clip._fireEvent(evt,arg1,arg2);}if(!clip||ret!==false){ret=commonClip._fireEvent(evt,arg1,arg2,clip);}}var i=0;each(listeners[evt],function(){ret=this.call(self,arg0,arg1);if(this.cached){listeners[evt].splice(i,1);}if(ret===false){return false;}i++;});return ret;};function init(){if($f(wrapper)){$f(wrapper).getParent().innerHTML="";playerIndex=$f(wrapper).getIndex();players[playerIndex]=self;}else{players.push(self);playerIndex=players.length-1;}wrapperHeight=parseInt(wrapper.style.height,10)||wrapper.clientHeight;if(typeof params=='string'){params={src:params};}playerId=wrapper.id||"fp"+makeId();apiId=params.id||playerId+"_api";params.id=apiId;conf.playerId=playerId;if(typeof conf=='string'){conf={clip:{url:conf}};}if(typeof conf.clip=='string'){conf.clip={url:conf.clip};}conf.clip=conf.clip||{};if(wrapper.getAttribute("href",2)&&!conf.clip.url){conf.clip.url=wrapper.getAttribute("href",2);}commonClip=new Clip(conf.clip,-1,self);conf.playlist=conf.playlist||[conf.clip];var index=0;each(conf.playlist,function(){var clip=this;if(typeof clip=='object'&&clip.length){clip=""+clip;}if(typeof clip=='string'){clip={url:clip};}each(conf.clip,function(key,val){if(conf.clip[key]!==undefined&&typeof val!='function'){clip[key]=val;}});conf.playlist[index]=clip;clip=new Clip(clip,index,self);playlist.push(clip);index++;});each(conf,function(key,val){if(typeof val=='function'){bind(listeners,key,val);delete conf[key];}});each(conf.plugins,function(name,val){if(val){plugins[name]=new Plugin(name,val,self);}});if(!conf.plugins||conf.plugins.controls===undefined){plugins.controls=new Plugin("controls",null,self);}params.bgcolor=params.bgcolor||"#000000";params.version=params.version||[9,0];params.expressInstall='http://www.flowplayer.org/swf/expressinstall.swf';function doClick(e){if(!self.isLoaded()&&self._fireEvent("onBeforeClick")!==false){self.load();}return stopEvent(e);}html=wrapper.innerHTML;if(html.replace(/\s/g,'')!==''){if(wrapper.addEventListener){wrapper.addEventListener("click",doClick,false);}else if(wrapper.attachEvent){wrapper.attachEvent("onclick",doClick);}}else{if(wrapper.addEventListener){wrapper.addEventListener("click",stopEvent,false);}self.load();}}if(typeof wrapper=='string'){flashembed.domReady(function(){var node=el(wrapper);if(!node){throw"Flowplayer cannot access element: "+wrapper;}else{wrapper=node;init();}});}else{init();}}var players=[];function Iterator(arr){this.length=arr.length;this.each=function(fn){each(arr,fn);};this.size=function(){return arr.length;};}window.flowplayer=window.$f=function(){var instance=null;var arg=arguments[0];if(!arguments.length){each(players,function(){if(this.isLoaded()){instance=this;return false;}});return instance||players[0];}if(arguments.length==1){if(typeof arg=='number'){return players[arg];}else{if(arg=='*'){return new Iterator(players);}each(players,function(){if(this.id()==arg.id||this.id()==arg||this.getParent()==arg){instance=this;return false;}});return instance;}}if(arguments.length>1){var swf=arguments[1];var conf=(arguments.length==3)?arguments[2]:{};if(typeof arg=='string'){if(arg.indexOf(".")!=-1){var instances=[];each(select(arg),function(){instances.push(new Player(this,clone(swf),clone(conf)));});return new Iterator(instances);}else{var node=el(arg);return new Player(node!==null?node:arg,swf,conf);}}else if(arg){return new Player(arg,swf,conf);}}return null;};extend(window.$f,{fireEvent:function(id,evt,a0,a1,a2){var p=$f(id);return p?p._fireEvent(evt,a0,a1,a2):null;},addPlugin:function(name,fn){Player.prototype[name]=fn;return $f;},each:each,extend:extend});if(document.all){window.onbeforeunload=function(){$f("*").each(function(){if(this.isLoaded()){this.close();}});};}if(typeof jQuery=='function'){jQuery.prototype.flowplayer=function(params,conf){if(!arguments.length||typeof arguments[0]=='number'){var arr=[];this.each(function(){var p=$f(this);if(p){arr.push(p);}});return arguments.length?arr[arguments[0]]:new Iterator(arr);}return this.each(function(){$f(this,clone(params),conf?clone(conf):{});});};}})();(function(){var jQ=typeof jQuery=='function';function isDomReady(){if(domReady.done){return false;}var d=document;if(d&&d.getElementsByTagName&&d.getElementById&&d.body){clearInterval(domReady.timer);domReady.timer=null;for(var i=0;i<domReady.ready.length;i++){domReady.ready[i].call();}domReady.ready=null;domReady.done=true;}}var domReady=jQ?jQuery:function(f){if(domReady.done){return f();}if(domReady.timer){domReady.ready.push(f);}else{domReady.ready=[f];domReady.timer=setInterval(isDomReady,13);}};function extend(to,from){if(from){for(key in from){if(from.hasOwnProperty(key)){to[key]=from[key];}}}return to;}function asString(obj){switch(typeOf(obj)){case'string':obj=obj.replace(new RegExp('(["\\\\])','g'),'\\$1');obj=obj.replace(/^\s?(\d+)%/,"$1pct");return'"'+obj+'"';case'array':return'['+map(obj,function(el){return asString(el);}).join(',')+']';case'function':return'"function()"';case'object':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push('"'+prop+'":'+asString(obj[prop]));}}return'{'+str.join(',')+'}';}return String(obj).replace(/\s/g," ").replace(/\'/g,"\"");}function typeOf(obj){if(obj===null||obj===undefined){return false;}var type=typeof obj;return(type=='object'&&obj.push)?'array':type;}if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});}function map(arr,func){var newArr=[];for(var i in arr){if(arr.hasOwnProperty(i)){newArr[i]=func(arr[i]);}}return newArr;}function getHTML(p,c){var ie=document.all;var html='<object width="'+p.width+'" height="'+p.height+'"';if(ie&&!p.id){p.id="_"+(""+Math.random()).substring(9);}if(p.id){html+=' id="'+p.id+'"';}if(p.w3c||!ie){html+=' data="'+p.src+'" type="application/x-shockwave-flash"';}else{html+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';}html+='>';if(p.w3c||ie){html+='<param name="movie" value="'+p.src+'" />';}var e=extend({},p);e.width=e.height=e.id=e.w3c=e.src=null;for(var k in e){if(e[k]!==null){html+='<param name="'+k+'" value="'+e[k]+'" />';}}var vars="";if(c){for(var key in c){if(c[key]!==null){vars+=key+'='+(typeof c[key]=='object'?asString(c[key]):c[key])+'&';}}vars=vars.substring(0,vars.length-1);html+='<param name="flashvars" value=\''+vars+'\' />';}html+="</object>";return html;}function Flash(root,opts,flashvars){var version=flashembed.getVersion();extend(this,{getContainer:function(){return root;},getConf:function(){return conf;},getVersion:function(){return version;},getFlashvars:function(){return flashvars;},getApi:function(){return root.firstChild;},getHTML:function(){return getHTML(opts,flashvars);}});var required=opts.version;var express=opts.expressInstall;var ok=!required||flashembed.isSupported(required);if(ok){opts.onFail=opts.version=opts.expressInstall=null;root.innerHTML=getHTML(opts,flashvars);}else if(required&&express&&flashembed.isSupported([6,65])){extend(opts,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title};root.innerHTML=getHTML(opts,flashvars);}else{if(root.innerHTML.replace(/\s/g,'')!==''){}else{root.innerHTML="<h2>Flash version "+required+" or greater is required</h2>"+"<h3>"+(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"</h3>"+"<p>Download latest version from <a href='http://www.adobe.com/go/getflashplayer'>here</a></p>";}}if(!ok&&opts.onFail){var ret=opts.onFail.call(this);if(typeof ret=='string'){root.innerHTML=ret;}}}window.flashembed=function(root,conf,flashvars){if(typeof root=='string'){var el=document.getElementById(root);if(el){root=el;}else{domReady(function(){flashembed(root,conf,flashvars);});return;}}if(!root){return;}var opts={width:'100%',height:'100%',allowfullscreen:true,allowscriptaccess:'always',quality:'high',version:null,onFail:null,expressInstall:null,w3c:false};if(typeof conf=='string'){conf={src:conf};}extend(opts,conf);return new Flash(root,opts,flashvars);};extend(window.flashembed,{getVersion:function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return;}}try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}}if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\S+\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}}return version;},isSupported:function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;},domReady:domReady,asString:asString,getHTML:getHTML});if(jQ){jQuery.prototype.flashembed=function(conf,flashvars){return this.each(function(){flashembed(this,conf,flashvars);});};}})();
\ No newline at end of file + +(function(){function log(args){console.log("$f.fireEvent",[].slice.call(args));} +function clone(obj){if(!obj||typeof obj!='object'){return obj;} +var temp=new obj.constructor();for(var key in obj){if(obj.hasOwnProperty(key)){temp[key]=clone(obj[key]);}} +return temp;} +function each(obj,fn){if(!obj){return;} +var name,i=0,length=obj.length;if(length===undefined){for(name in obj){if(fn.call(obj[name],name,obj[name])===false){break;}}}else{for(var value=obj[0];i<length&&fn.call(value,i,value)!==false;value=obj[++i]){}} +return obj;} +function el(id){return document.getElementById(id);} +function extend(to,from,skipFuncs){if(typeof from!='object'){return to;} +if(to&&from){each(from,function(name,value){if(!skipFuncs||typeof value!='function'){to[name]=value;}});} +return to;} +function select(query){var index=query.indexOf(".");if(index!=-1){var tag=query.substring(0,index)||"*";var klass=query.substring(index+1,query.length);var els=[];each(document.getElementsByTagName(tag),function(){if(this.className&&this.className.indexOf(klass)!=-1){els.push(this);}});return els;}} +function stopEvent(e){e=e||window.event;if(e.preventDefault){e.stopPropagation();e.preventDefault();}else{e.returnValue=false;e.cancelBubble=true;} +return false;} +function bind(to,evt,fn){to[evt]=to[evt]||[];to[evt].push(fn);} +function makeId(){return"_"+(""+Math.random()).substring(2,10);} +var Clip=function(json,index,player){var self=this;var cuepoints={};var listeners={};self.index=index;if(typeof json=='string'){json={url:json};} +extend(this,json,true);each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),function(){var evt="on"+this;if(evt.indexOf("*")!=-1){evt=evt.substring(0,evt.length-1);var before="onBefore"+evt.substring(2);self[before]=function(fn){bind(listeners,before,fn);return self;};} +self[evt]=function(fn){bind(listeners,evt,fn);return self;};if(index==-1){if(self[before]){player[before]=self[before];} +if(self[evt]){player[evt]=self[evt];}}});extend(this,{onCuepoint:function(points,fn){if(arguments.length==1){cuepoints.embedded=[null,points];return self;} +if(typeof points=='number'){points=[points];} +var fnId=makeId();cuepoints[fnId]=[points,fn];if(player.isLoaded()){player._api().fp_addCuepoints(points,index,fnId);} +return self;},update:function(json){extend(self,json);if(player.isLoaded()){player._api().fp_updateClip(json,index);} +var conf=player.getConfig();var clip=(index==-1)?conf.clip:conf.playlist[index];extend(clip,json,true);},_fireEvent:function(evt,arg1,arg2,target){if(evt=='onLoad'){each(cuepoints,function(key,val){if(val[0]){player._api().fp_addCuepoints(val[0],index,key);}});return false;} +target=target||self;if(evt=='onCuepoint'){var fn=cuepoints[arg1];if(fn){return fn[1].call(player,target,arg2);}} +if(arg1&&"onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(evt)!=-1){extend(target,arg1);if(arg1.metaData){if(!target.duration){target.duration=arg1.metaData.duration;}else{target.fullDuration=arg1.metaData.duration;}}} +var ret=true;each(listeners[evt],function(){ret=this.call(player,target,arg1,arg2);});return ret;}});if(json.onCuepoint){var arg=json.onCuepoint;self.onCuepoint.apply(self,typeof arg=='function'?[arg]:arg);delete json.onCuepoint;} +each(json,function(key,val){if(typeof val=='function'){bind(listeners,key,val);delete json[key];}});if(index==-1){player.onCuepoint=this.onCuepoint;}};var Plugin=function(name,json,player,fn){var listeners={};var self=this;var hasMethods=false;if(fn){extend(listeners,fn);} +each(json,function(key,val){if(typeof val=='function'){listeners[key]=val;delete json[key];}});extend(this,{animate:function(props,speed,fn){if(!props){return self;} +if(typeof speed=='function'){fn=speed;speed=500;} +if(typeof props=='string'){var key=props;props={};props[key]=speed;speed=500;} +if(fn){var fnId=makeId();listeners[fnId]=fn;} +if(speed===undefined){speed=500;} +json=player._api().fp_animate(name,props,speed,fnId);return self;},css:function(props,val){if(val!==undefined){var css={};css[props]=val;props=css;} +json=player._api().fp_css(name,props);extend(self,json);return self;},show:function(){this.display='block';player._api().fp_showPlugin(name);return self;},hide:function(){this.display='none';player._api().fp_hidePlugin(name);return self;},toggle:function(){this.display=player._api().fp_togglePlugin(name);return self;},fadeTo:function(o,speed,fn){if(typeof speed=='function'){fn=speed;speed=500;} +if(fn){var fnId=makeId();listeners[fnId]=fn;} +this.display=player._api().fp_fadeTo(name,o,speed,fnId);this.opacity=o;return self;},fadeIn:function(speed,fn){return self.fadeTo(1,speed,fn);},fadeOut:function(speed,fn){return self.fadeTo(0,speed,fn);},getName:function(){return name;},getPlayer:function(){return player;},_fireEvent:function(evt,arg,arg2){if(evt=='onUpdate'){var json=player._api().fp_getPlugin(name);if(!json){return;} +extend(self,json);delete self.methods;if(!hasMethods){each(json.methods,function(){var method=""+this;self[method]=function(){var a=[].slice.call(arguments);var ret=player._api().fp_invoke(name,method,a);return ret==='undefined'||ret===undefined?self:ret;};});hasMethods=true;}} +var fn=listeners[evt];if(fn){fn.apply(self,arg);if(evt.substring(0,1)=="_"){delete listeners[evt];}}}});};function Player(wrapper,params,conf){var +self=this,api=null,html,commonClip,playlist=[],plugins={},listeners={},playerId,apiId,playerIndex,activeIndex,swfHeight,wrapperHeight;extend(self,{id:function(){return playerId;},isLoaded:function(){return(api!==null);},getParent:function(){return wrapper;},hide:function(all){if(all){wrapper.style.height="0px";} +if(api){api.style.height="0px";} +return self;},show:function(){wrapper.style.height=wrapperHeight+"px";if(api){api.style.height=swfHeight+"px";} +return self;},isHidden:function(){return api&&parseInt(api.style.height,10)===0;},load:function(fn){if(!api&&self._fireEvent("onBeforeLoad")!==false){each(players,function(){this.unload();});html=wrapper.innerHTML;if(html&&!flashembed.isSupported(params.version)){wrapper.innerHTML="";} +flashembed(wrapper,params,{config:conf});if(fn){fn.cached=true;bind(listeners,"onLoad",fn);}} +return self;},unload:function(){if(html.replace(/\s/g,'')!==''){if(self._fireEvent("onBeforeUnload")===false){return self;} +try{if(api){api.fp_close();self._fireEvent("onUnload");}}catch(error){} +api=null;wrapper.innerHTML=html;} +return self;},getClip:function(index){if(index===undefined){index=activeIndex;} +return playlist[index];},getCommonClip:function(){return commonClip;},getPlaylist:function(){return playlist;},getPlugin:function(name){var plugin=plugins[name];if(!plugin&&self.isLoaded()){var json=self._api().fp_getPlugin(name);if(json){plugin=new Plugin(name,json,self);plugins[name]=plugin;}} +return plugin;},getScreen:function(){return self.getPlugin("screen");},getControls:function(){return self.getPlugin("controls");},getConfig:function(copy){return copy?clone(conf):conf;},getFlashParams:function(){return params;},loadPlugin:function(name,url,props,fn){if(typeof props=='function'){fn=props;props={};} +var fnId=fn?makeId():"_";self._api().fp_loadPlugin(name,url,props,fnId);var arg={};arg[fnId]=fn;var p=new Plugin(name,null,self,arg);plugins[name]=p;return p;},getState:function(){return api?api.fp_getState():-1;},play:function(clip,instream){function play(){if(clip!==undefined){self._api().fp_play(clip,instream);}else{self._api().fp_play();}} +if(api){play();}else{self.load(function(){play();});} +return self;},getVersion:function(){var js="flowplayer.js @VERSION";if(api){var ver=api.fp_getVersion();ver.push(js);return ver;} +return js;},_api:function(){if(!api){throw"Flowplayer "+self.id()+" not loaded when calling an API method";} +return api;},setClip:function(clip){self.setPlaylist([clip]);return self;},getIndex:function(){return playerIndex;}});each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),function(){var name="on"+this;if(name.indexOf("*")!=-1){name=name.substring(0,name.length-1);var name2="onBefore"+name.substring(2);self[name2]=function(fn){bind(listeners,name2,fn);return self;};} +self[name]=function(fn){bind(listeners,name,fn);return self;};});each(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,toggleFullscreen,reset,close,setPlaylist,addClip,playFeed").split(","),function(){var name=this;self[name]=function(a1,a2){if(!api){return self;} +var ret=null;if(a1!==undefined&&a2!==undefined){ret=api["fp_"+name](a1,a2);}else{ret=(a1===undefined)?api["fp_"+name]():api["fp_"+name](a1);} +return ret==='undefined'||ret===undefined?self:ret;};});self._fireEvent=function(a){if(typeof a=='string'){a=[a];} +var evt=a[0],arg0=a[1],arg1=a[2],arg2=a[3],i=0;if(conf.debug){log(a);} +if(!api&&evt=='onLoad'&&arg0=='player'){api=api||el(apiId);swfHeight=api.clientHeight;each(playlist,function(){this._fireEvent("onLoad");});each(plugins,function(name,p){p._fireEvent("onUpdate");});commonClip._fireEvent("onLoad");} +if(evt=='onLoad'&&arg0!='player'){return;} +if(evt=='onError'){if(typeof arg0=='string'||(typeof arg0=='number'&&typeof arg1=='number')){arg0=arg1;arg1=arg2;}} +if(evt=='onContextMenu'){each(conf.contextMenu[arg0],function(key,fn){fn.call(self);});return;} +if(evt=='onPluginEvent'){var name=arg0.name||arg0;var p=plugins[name];if(p){p._fireEvent("onUpdate",arg0);p._fireEvent(arg1,a.slice(3));} +return;} +if(evt=='onPlaylistReplace'){playlist=[];var index=0;each(arg0,function(){playlist.push(new Clip(this,index++,self));});} +if(evt=='onClipAdd'){if(arg0.isInStream){return;} +arg0=new Clip(arg0,arg1,self);playlist.splice(arg1,0,arg0);for(i=arg1+1;i<playlist.length;i++){playlist[i].index++;}} +var ret=true;if(typeof arg0=='number'&&arg0<playlist.length){activeIndex=arg0;var clip=playlist[arg0];if(clip){ret=clip._fireEvent(evt,arg1,arg2);} +if(!clip||ret!==false){ret=commonClip._fireEvent(evt,arg1,arg2,clip);}} +each(listeners[evt],function(){ret=this.call(self,arg0,arg1);if(this.cached){listeners[evt].splice(i,1);} +if(ret===false){return false;} +i++;});return ret;};function init(){if($f(wrapper)){$f(wrapper).getParent().innerHTML="";playerIndex=$f(wrapper).getIndex();players[playerIndex]=self;}else{players.push(self);playerIndex=players.length-1;} +wrapperHeight=parseInt(wrapper.style.height,10)||wrapper.clientHeight;if(typeof params=='string'){params={src:params};} +playerId=wrapper.id||"fp"+makeId();apiId=params.id||playerId+"_api";params.id=apiId;conf.playerId=playerId;if(typeof conf=='string'){conf={clip:{url:conf}};} +if(typeof conf.clip=='string'){conf.clip={url:conf.clip};} +conf.clip=conf.clip||{};if(wrapper.getAttribute("href",2)&&!conf.clip.url){conf.clip.url=wrapper.getAttribute("href",2);} +commonClip=new Clip(conf.clip,-1,self);conf.playlist=conf.playlist||[conf.clip];var index=0;each(conf.playlist,function(){var clip=this;if(typeof clip=='object'&&clip.length){clip={url:""+clip};} +each(conf.clip,function(key,val){if(val!==undefined&&clip[key]===undefined&&typeof val!='function'){clip[key]=val;}});conf.playlist[index]=clip;clip=new Clip(clip,index,self);playlist.push(clip);index++;});each(conf,function(key,val){if(typeof val=='function'){if(commonClip[key]){commonClip[key](val);}else{bind(listeners,key,val);} +delete conf[key];}});each(conf.plugins,function(name,val){if(val){plugins[name]=new Plugin(name,val,self);}});if(!conf.plugins||conf.plugins.controls===undefined){plugins.controls=new Plugin("controls",null,self);} +plugins.canvas=new Plugin("canvas",null,self);params.bgcolor=params.bgcolor||"#000000";params.version=params.version||[9,0];params.expressInstall='http://www.flowplayer.org/swf/expressinstall.swf';function doClick(e){if(!self.isLoaded()&&self._fireEvent("onBeforeClick")!==false){self.load();} +return stopEvent(e);} +html=wrapper.innerHTML;if(html.replace(/\s/g,'')!==''){if(wrapper.addEventListener){wrapper.addEventListener("click",doClick,false);}else if(wrapper.attachEvent){wrapper.attachEvent("onclick",doClick);}}else{if(wrapper.addEventListener){wrapper.addEventListener("click",stopEvent,false);} +self.load();}} +if(typeof wrapper=='string'){flashembed.domReady(function(){var node=el(wrapper);if(!node){throw"Flowplayer cannot access element: "+wrapper;}else{wrapper=node;init();}});}else{init();}} +var players=[];function Iterator(arr){this.length=arr.length;this.each=function(fn){each(arr,fn);};this.size=function(){return arr.length;};} +window.flowplayer=window.$f=function(){var instance=null;var arg=arguments[0];if(!arguments.length){each(players,function(){if(this.isLoaded()){instance=this;return false;}});return instance||players[0];} +if(arguments.length==1){if(typeof arg=='number'){return players[arg];}else{if(arg=='*'){return new Iterator(players);} +each(players,function(){if(this.id()==arg.id||this.id()==arg||this.getParent()==arg){instance=this;return false;}});return instance;}} +if(arguments.length>1){var swf=arguments[1];var conf=(arguments.length==3)?arguments[2]:{};if(typeof arg=='string'){if(arg.indexOf(".")!=-1){var instances=[];each(select(arg),function(){instances.push(new Player(this,clone(swf),clone(conf)));});return new Iterator(instances);}else{var node=el(arg);return new Player(node!==null?node:arg,swf,conf);}}else if(arg){return new Player(arg,swf,conf);}} +return null;};extend(window.$f,{fireEvent:function(){var a=[].slice.call(arguments);var p=$f(a[0]);return p?p._fireEvent(a.slice(1)):null;},addPlugin:function(name,fn){Player.prototype[name]=fn;return $f;},each:each,extend:extend});if(typeof jQuery=='function'){jQuery.prototype.flowplayer=function(params,conf){if(!arguments.length||typeof arguments[0]=='number'){var arr=[];this.each(function(){var p=$f(this);if(p){arr.push(p);}});return arguments.length?arr[arguments[0]]:new Iterator(arr);} +return this.each(function(){$f(this,clone(params),conf?clone(conf):{});});};}})();(function(){var jQ=typeof jQuery=='function';var options={width:'100%',height:'100%',allowfullscreen:true,allowscriptaccess:'always',quality:'high',version:null,onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(jQ){jQuery.tools=jQuery.tools||{};jQuery.tools.flashembed={version:'1.0.4',conf:options};} +function isDomReady(){if(domReady.done){return false;} +var d=document;if(d&&d.getElementsByTagName&&d.getElementById&&d.body){clearInterval(domReady.timer);domReady.timer=null;for(var i=0;i<domReady.ready.length;i++){domReady.ready[i].call();} +domReady.ready=null;domReady.done=true;}} +var domReady=jQ?jQuery:function(f){if(domReady.done){return f();} +if(domReady.timer){domReady.ready.push(f);}else{domReady.ready=[f];domReady.timer=setInterval(isDomReady,13);}};function extend(to,from){if(from){for(key in from){if(from.hasOwnProperty(key)){to[key]=from[key];}}} +return to;} +var 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;function string2JsonString(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} +function asString(obj){switch(typeOf(obj)){case'string':return string2JsonString(obj);case'array':return'['+map(obj,function(el){return asString(el);}).join(',')+']';case'function':return'"function()"';case'object':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push('"'+prop+'":'+asString(obj[prop]));}} +return'{'+str.join(',')+'}';} +return String(obj).replace(/\s/g," ").replace(/\'/g,"\"");} +function typeOf(obj){if(obj===null||obj===undefined){return false;} +var type=typeof obj;return(type=='object'&&obj.push)?'array':type;} +if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});} +function map(arr,func){var newArr=[];for(var i in arr){if(arr.hasOwnProperty(i)){newArr[i]=func(arr[i]);}} +return newArr;} +function getHTML(p,c){var e=extend({},p);var ie=document.all;var html='<object width="'+e.width+'" height="'+e.height+'"';if(ie&&!e.id){e.id="_"+(""+Math.random()).substring(9);} +if(e.id){html+=' id="'+e.id+'"';} +if(e.cachebusting){e.src+=((e.src.indexOf("?")!=-1?"&":"?")+Math.random());} +if(e.w3c||!ie){html+=' data="'+e.src+'" type="application/x-shockwave-flash"';}else{html+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';} +html+='>';if(e.w3c||ie){html+='<param name="movie" value="'+e.src+'" />';} +e.width=e.height=e.id=e.w3c=e.src=null;for(var k in e){if(e[k]!==null){html+='<param name="'+k+'" value="'+e[k]+'" />';}} +var vars="";if(c){for(var key in c){if(c[key]!==null){vars+=encodeURIComponent(key)+'=' ++encodeURIComponent(typeof c[key]=='object'?asString(c[key]):c[key]) ++'&';}} +vars=vars.substring(0,vars.length-1);html+='<param name="flashvars" value="'+vars+'" />';} +html+="</object>";return html;} +function Flash(root,opts,flashvars){var version=flashembed.getVersion();extend(this,{getContainer:function(){return root;},getConf:function(){return opts;},getVersion:function(){return version;},getFlashvars:function(){return flashvars;},getApi:function(){return root.firstChild;},getHTML:function(){return getHTML(opts,flashvars);}});var required=opts.version;var express=opts.expressInstall;var ok=!required||flashembed.isSupported(required);if(ok){opts.onFail=opts.version=opts.expressInstall=null;root.innerHTML=getHTML(opts,flashvars);}else if(required&&express&&flashembed.isSupported([6,65])){extend(opts,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title};root.innerHTML=getHTML(opts,flashvars);}else{if(root.innerHTML.replace(/\s/g,'')!==''){}else{root.innerHTML="<h2>Flash version "+required+" or greater is required</h2>"+"<h3>"+ +(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"</h3>"+ +(root.tagName=='A'?"<p>Click here to download latest version</p>":"<p>Download latest version from <a href='http://www.adobe.com/go/getflashplayer'>here</a></p>");if(root.tagName=='A'){root.onclick=function(){location.href='http://www.adobe.com/go/getflashplayer';};}}} +if(!ok&&opts.onFail){var ret=opts.onFail.call(this);if(typeof ret=='string'){root.innerHTML=ret;}} +if(document.all){window[opts.id]=document.getElementById(opts.id);}} +window.flashembed=function(root,conf,flashvars){if(typeof root=='string'){var el=document.getElementById(root);if(el){root=el;}else{domReady(function(){flashembed(root,conf,flashvars);});return;}} +if(!root){return;} +if(typeof conf=='string'){conf={src:conf};} +var opts=extend({},options);extend(opts,conf);return new Flash(root,opts,flashvars);};extend(window.flashembed,{getVersion:function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return version;}} +try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}} +if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\S+\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}} +return version;},isSupported:function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;},domReady:domReady,asString:asString,getHTML:getHTML});if(jQ){jQuery.fn.flashembed=function(conf,flashvars){var el=null;this.each(function(){el=flashembed(this,conf,flashvars);});return conf.api===false?this:el;};}})();
\ No newline at end of file diff --git a/modules/akismet/controllers/admin_akismet.php b/modules/akismet/controllers/admin_akismet.php index ca3a1473..4847db53 100644 --- a/modules/akismet/controllers/admin_akismet.php +++ b/modules/akismet/controllers/admin_akismet.php @@ -25,17 +25,8 @@ class Admin_Akismet_Controller extends Admin_Controller { // @todo move the "post" handler part of this code into a separate function access::verify_csrf(); - $valid = $form->validate(); - - if ($valid) { + if ($form->validate()) { $new_key = $form->configure_akismet->api_key->value; - if ($new_key && !akismet::validate_key($new_key)) { - $form->configure_akismet->api_key->add_error("invalid", 1); - $valid = false; - } - } - - if ($valid) { $old_key = module::get_var("akismet", "api_key"); if ($old_key && !$new_key) { message::success(t("Your Akismet key has been cleared.")); diff --git a/modules/akismet/helpers/akismet.php b/modules/akismet/helpers/akismet.php index 46a305b2..b4405de5 100644 --- a/modules/akismet/helpers/akismet.php +++ b/modules/akismet/helpers/akismet.php @@ -23,8 +23,9 @@ class akismet_Core { static function get_configure_form() { $form = new Forge("admin/akismet", "", "post", array("id" => "g-configure-akismet-form")); $group = $form->group("configure_akismet")->label(t("Configure Akismet")); - $group->input("api_key")->label(t("API Key"))->value(module::get_var("akismet", "api_key")); - $group->api_key->error_messages("invalid", t("The API key you provided is invalid.")); + $group->input("api_key")->label(t("API Key"))->value(module::get_var("akismet", "api_key")) + ->callback("akismet::validate_key") + ->error_messages("invalid", t("The API key you provided is invalid.")); $group->submit("")->value(t("Save")); return $form; } @@ -82,10 +83,14 @@ class akismet_Core { * @param string $api_key the API key * @return boolean */ - static function validate_key($api_key) { - $request = self::_build_verify_request($api_key); - $response = self::_http_post($request, "rest.akismet.com"); - return "valid" == $response->body[0]; + static function validate_key($api_key_input) { + if ($api_key_input->value) { + $request = self::_build_verify_request($api_key_input->value); + $response = self::_http_post($request, "rest.akismet.com"); + if ("valid" != $response->body[0]) { + $api_key_input->add_error("invalid", 1); + } + } } diff --git a/modules/akismet/tests/Akismet_Helper_Test.php b/modules/akismet/tests/Akismet_Helper_Test.php index d8605d5c..e185f280 100644 --- a/modules/akismet/tests/Akismet_Helper_Test.php +++ b/modules/akismet/tests/Akismet_Helper_Test.php @@ -17,25 +17,32 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Akismet_Helper_Test extends Unit_Test_Case { +class Akismet_Helper_Test extends Gallery_Unit_Test_Case { private $_comment; public function setup() { Input::instance()->ip_address = "1.1.1.1"; request::set_user_agent("Akismet_Helper_Test"); + module::set_var("akismet", "api_key", "TEST_KEY"); + } + + private function _make_comment() { + $comment = ORM::factory("comment"); + $comment->item_id = item::root()->id; + $comment->author_id = identity::guest()->id; + $comment->text = "This is a comment"; + $comment->guest_name = "John Doe"; + $comment->guest_email = "john@gallery2.org"; + $comment->guest_url = "http://gallery2.org"; + $comment->save(); - $root = ORM::factory("item", 1); - $this->_comment = comment::create( - $root, identity::guest(), "This is a comment", - "John Doe", "john@gallery2.org", "http://gallery2.org"); - foreach ($this->_comment->list_fields("comments") as $name => $field) { + // Set the server fields to a known placeholder + foreach ($comment->list_fields("comments") as $name => $field) { if (strpos($name, "server_") === 0) { - $this->_comment->$name = substr($name, strlen("server_")); + $comment->$name = substr($name, strlen("server_")); } } - $this->_comment->save(); - - module::set_var("akismet", "api_key", "TEST_KEY"); + return $comment->save(); } public function build_verify_request_test() { @@ -51,8 +58,8 @@ class Akismet_Helper_Test extends Unit_Test_Case { } public function build_comment_check_request_test() { - $request = akismet::_build_request("comment-check", $this->_comment); - $id = $this->_comment->id; + $comment = $this->_make_comment(); + $request = akismet::_build_request("comment-check", $comment); $expected = "POST /1.1/comment-check HTTP/1.0\r\n" . "Host: TEST_KEY.rest.akismet.com\r\n" . "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" . @@ -66,15 +73,15 @@ class Akismet_Helper_Test extends Unit_Test_Case { "SERVER_HTTP_ACCEPT_CHARSET=http_accept_charset&" . "blog=http%3A%2F%2F.%2F&comment_author=John+Doe&comment_author_email=john%40gallery2.org&" . "comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment&" . - "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$id}&" . + "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$comment->id}&" . "referrer=http_referer&user_agent=http_user_agent&user_ip=remote_addr"; $this->assert_equal($expected, $request); } public function build_submit_spam_request_test() { - $request = akismet::_build_request("submit-spam", $this->_comment); - $id = $this->_comment->id; + $comment = $this->_make_comment(); + $request = akismet::_build_request("submit-spam", $comment); $expected = "POST /1.1/submit-spam HTTP/1.0\r\n" . "Host: TEST_KEY.rest.akismet.com\r\n" . @@ -89,15 +96,15 @@ class Akismet_Helper_Test extends Unit_Test_Case { "SERVER_HTTP_ACCEPT_CHARSET=http_accept_charset&" . "blog=http%3A%2F%2F.%2F&comment_author=John+Doe&comment_author_email=john%40gallery2.org&" . "comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment&" . - "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$id}&" . + "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$comment->id}&" . "referrer=http_referer&user_agent=http_user_agent&user_ip=remote_addr"; $this->assert_equal($expected, $request); } public function build_submit_ham_request_test() { - $request = akismet::_build_request("submit-ham", $this->_comment); - $id = $this->_comment->id; + $comment = $this->_make_comment(); + $request = akismet::_build_request("submit-ham", $comment); $expected = "POST /1.1/submit-ham HTTP/1.0\r\n" . "Host: TEST_KEY.rest.akismet.com\r\n" . @@ -112,7 +119,7 @@ class Akismet_Helper_Test extends Unit_Test_Case { "SERVER_HTTP_ACCEPT_CHARSET=http_accept_charset&blog=http%3A%2F%2F.%2F&" . "comment_author=John+Doe&comment_author_email=john%40gallery2.org&" . "comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment&" . - "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$id}&" . + "comment_type=comment&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$comment->id}&" . "referrer=http_referer&user_agent=http_user_agent&user_ip=remote_addr"; $this->assert_equal($expected, $request); diff --git a/modules/comment/controllers/admin_comments.php b/modules/comment/controllers/admin_comments.php index b7dc5fb3..3dd45919 100644 --- a/modules/comment/controllers/admin_comments.php +++ b/modules/comment/controllers/admin_comments.php @@ -92,6 +92,7 @@ class Admin_Comments_Controller extends Admin_Controller { } private function _counts() { + $counts = new stdClass(); $counts->unpublished = 0; $counts->published = 0; $counts->spam = 0; diff --git a/modules/comment/controllers/comments.php b/modules/comment/controllers/comments.php index 068152a2..6c546321 100644 --- a/modules/comment/controllers/comments.php +++ b/modules/comment/controllers/comments.php @@ -26,50 +26,39 @@ class Comments_Controller extends Controller { access::required("view", $item); $form = comment::get_add_form($item); - $valid = $form->validate(); - if ($valid) { - if (identity::active_user()->guest && !$form->add_comment->inputs["name"]->value) { - $form->add_comment->inputs["name"]->add_error("missing", 1); - $valid = false; - } - - if (!$form->add_comment->text->value) { - $form->add_comment->text->add_error("missing", 1); - $valid = false; + try { + $valid = $form->validate(); + $comment = ORM::factory("comment"); + $comment->item_id = $id; + $comment->author_id = identity::active_user()->id; + $comment->text = $form->add_comment->text->value; + $comment->guest_name = $form->add_comment->inputs["name"]->value; + $comment->guest_email = $form->add_comment->email->value; + $comment->guest_url = $form->add_comment->url->value; + $comment->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + switch ($key) { + case "guest_name": $key = "name"; break; + case "guest_email": $key = "email"; break; + } + $form->add_comment->inputs[$key]->add_error($error, 1); } + $valid = false; } if ($valid) { - $comment = comment::create( - $item, identity::active_user(), - $form->add_comment->text->value, - $form->add_comment->inputs["name"]->value, - $form->add_comment->email->value, - $form->add_comment->url->value); - - $active = identity::active_user(); - if ($active->guest) { - $form->add_comment->inputs["name"]->value(""); - $form->add_comment->email->value(""); - $form->add_comment->url->value(""); - } else { - $form->add_comment->inputs["name"]->value($active->full_name); - $form->add_comment->email->value($active->email); - $form->add_comment->url->value($active->url); - } - - $form->add_comment->text->value(""); + $comment->save(); $view = new Theme_View("comment.html", "other", "comment-fragment"); $view->comment = $comment; print json_encode( array("result" => "success", - "view" => $view->__toString(), - "form" => $form->__toString())); + "view" => (string) $view, + "form" => (string) comment::get_add_form($item))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/comment/helpers/comment.php b/modules/comment/helpers/comment.php index 1e1e7d2f..f710ad92 100644 --- a/modules/comment/helpers/comment.php +++ b/modules/comment/helpers/comment.php @@ -24,53 +24,23 @@ * Note: by design, this class does not do any permission checking. */ class comment_Core { - /** - * Create a new comment. - * @param Item_MOdel $item the parent item - * @param User_Model $author the author User_Model - * @param string $text comment body - * @param string $guest_name guest's name (if the author is a guest user, default empty) - * @param string $guest_email guest's email (if the author is a guest user, default empty) - * @param string $guest_url guest's url (if the author is a guest user, default empty) - * @return Comment_Model - */ - static function create($item, $author, $text, $guest_name=null, - $guest_email=null, $guest_url=null) { - $comment = ORM::factory("comment"); - $comment->author_id = $author->id; - $comment->guest_email = $guest_email; - $comment->guest_name = $guest_name; - $comment->guest_url = $guest_url; - $comment->item_id = $item->id; - $comment->text = $text; - $comment->state = "published"; - - // These values are useful for spam fighting, so save them with the comment. - $input = Input::instance(); - $comment->server_http_accept = substr($input->server("HTTP_ACCEPT"), 0, 128); - $comment->server_http_accept_charset = substr($input->server("HTTP_ACCEPT_CHARSET"), 0, 64); - $comment->server_http_accept_encoding = substr($input->server("HTTP_ACCEPT_ENCODING"), 0, 64); - $comment->server_http_accept_language = substr($input->server("HTTP_ACCEPT_LANGUAGE"), 0, 64); - $comment->server_http_connection = substr($input->server("HTTP_CONNECTION"), 0, 64); - $comment->server_http_host = substr($input->server("HTTP_HOST"), 0, 64); - $comment->server_http_referer = substr($input->server("HTTP_REFERER"), 0, 255); - $comment->server_http_user_agent = substr($input->server("HTTP_USER_AGENT"), 0, 128); - $comment->server_query_string = substr($input->server("QUERY_STRING"), 0, 64); - $comment->server_remote_addr = substr($input->server("REMOTE_ADDR"), 0, 32); - $comment->server_remote_host = substr($input->server("REMOTE_HOST"), 0, 64); - $comment->server_remote_port = substr($input->server("REMOTE_PORT"), 0, 16); - $comment->save(); - - return $comment; - } - static function get_add_form($item) { $form = new Forge("comments/create/{$item->id}", "", "post", array("id" => "g-comment-form")); $group = $form->group("add_comment")->label(t("Add comment")); - $group->input("name") ->label(t("Name")) ->id("g-author"); - $group->input("email") ->label(t("Email (hidden)")) ->id("g-email"); - $group->input("url") ->label(t("Website (hidden)"))->id("g-url"); - $group->textarea("text")->label(t("Comment")) ->id("g-text"); + $group->input("name") + ->label(t("Name")) + ->id("g-author") + ->error_messages("required", t("You must enter a name for yourself")); + $group->input("email") + ->label(t("Email (hidden)")) + ->id("g-email"); + $group->input("url") + ->label(t("Website (hidden)")) + ->id("g-url"); + $group->textarea("text") + ->label(t("Comment")) + ->id("g-text") + ->error_messages("required", t("You must enter a comment")); $group->hidden("item_id")->value($item->id); module::event("comment_add_form", $form); $group->submit("")->value(t("Add"))->class("ui-state-default ui-corner-all"); @@ -80,10 +50,7 @@ class comment_Core { $group->inputs["name"]->value($active->full_name)->disabled("disabled"); $group->email->value($active->email)->disabled("disabled"); $group->url->value($active->url)->disabled("disabled"); - } else { - $group->inputs["name"]->error_messages("missing", t("You must provide a name")); } - $group->text->error_messages("missing", t("You must provide a comment")); return $form; } diff --git a/modules/comment/helpers/comment_rss.php b/modules/comment/helpers/comment_rss.php index 77044884..79fa07df 100644 --- a/modules/comment/helpers/comment_rss.php +++ b/modules/comment/helpers/comment_rss.php @@ -42,6 +42,7 @@ class comment_rss_Core { $comments->where("item_id", "=", $id); } + $feed = new stdClass(); $feed->view = "comment.mrss"; $feed->children = array(); foreach ($comments->find_all($limit, $offset) as $comment) { diff --git a/modules/comment/models/comment.php b/modules/comment/models/comment.php index e0b82039..d9d05995 100644 --- a/modules/comment/models/comment.php +++ b/modules/comment/models/comment.php @@ -54,30 +54,70 @@ class Comment_Model extends ORM { } /** + * Add some custom per-instance rules. + */ + public function validate(Validation $array=null) { + // validate() is recursive, only modify the rules on the outermost call. + if (!$array) { + $this->rules = array( + "guest_name" => array("callbacks" => array(array($this, "valid_author"))), + "guest_email" => array("rules" => array("email")), + "guest_url" => array("rules" => array("url")), + "item_id" => array("callbacks" => array(array($this, "valid_item"))), + "state" => array("rules" => array("Comment_Model::valid_state")), + "text" => array("rules" => array("required")), + ); + } + + parent::validate($array); + } + + /** * @see ORM::save() */ public function save() { - if (!empty($this->changed)) { - $this->updated = time(); - if (!$this->loaded() && empty($this->created)) { - $this->created = $this->updated; - $created = true; + $this->updated = time(); + if (!$this->loaded()) { + // New comment + $this->created = $this->updated; + if (empty($this->state)) { + $this->state = "published"; } - } - $visible_change = $this->original()->state == "published" || $this->state == "published"; - $original = clone $this->original(); - parent::save(); + // These values are useful for spam fighting, so save them with the comment. It's painful to + // check each one to see if it already exists before setting it, so just use server_http_host + // as a semaphore for now (we use that in g2_import.php) + if (empty($this->server_http_host)) { + $input = Input::instance(); + $this->server_http_accept = substr($input->server("HTTP_ACCEPT"), 0, 128); + $this->server_http_accept_charset = substr($input->server("HTTP_ACCEPT_CHARSET"), 0, 64); + $this->server_http_accept_encoding = substr($input->server("HTTP_ACCEPT_ENCODING"), 0, 64); + $this->server_http_accept_language = substr($input->server("HTTP_ACCEPT_LANGUAGE"), 0, 64); + $this->server_http_connection = substr($input->server("HTTP_CONNECTION"), 0, 64); + $this->server_http_host = substr($input->server("HTTP_HOST"), 0, 64); + $this->server_http_referer = substr($input->server("HTTP_REFERER"), 0, 255); + $this->server_http_user_agent = substr($input->server("HTTP_USER_AGENT"), 0, 128); + $this->server_query_string = substr($input->server("QUERY_STRING"), 0, 64); + $this->server_remote_addr = substr($input->server("REMOTE_ADDR"), 0, 32); + $this->server_remote_host = substr($input->server("REMOTE_HOST"), 0, 64); + $this->server_remote_port = substr($input->server("REMOTE_PORT"), 0, 16); + } - if (isset($created)) { + $visible_change = $this->state == "published"; + parent::save(); module::event("comment_created", $this); } else { + // Updated comment + $original = ORM::factory("comment", $this->id); + $visible_change = $original->state == "published" || $this->state == "published"; + parent::save(); module::event("comment_updated", $original, $this); } // We only notify on the related items if we're making a visible change. if ($visible_change) { - module::event("item_related_update", $this->item()); + $item = $this->item(); + module::event("item_related_update", $item); } return $this; @@ -92,4 +132,34 @@ class Comment_Model extends ORM { $this->join("items", "items.id", "comments.item_id"); return item::viewable($this); } + + /** + * Make sure we have an appropriate author id set, or a guest name. + */ + public function valid_author(Validation $v, $field) { + if (empty($this->author_id)) { + $v->add_error("author_id", "required"); + } else if ($this->author_id == identity::guest()->id && empty($this->guest_name)) { + $v->add_error("guest_name", "required"); + } + } + + /** + * Make sure we have a valid associated item id. + */ + public function valid_item(Validation $v, $field) { + if (db::build() + ->from("items") + ->where("id", "=", $this->item_id) + ->count_records() != 1) { + $v->add_error("item_id", "invalid"); + } + } + + /** + * Make sure that the state is legal. + */ + static function valid_state($value) { + return in_array($value, array("published", "unpublished", "spam", "deleted")); + } } diff --git a/modules/comment/tests/Comment_Event_Test.php b/modules/comment/tests/Comment_Event_Test.php index ff7f1c26..08f55b3f 100644 --- a/modules/comment/tests/Comment_Event_Test.php +++ b/modules/comment/tests/Comment_Event_Test.php @@ -17,16 +17,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Comment_Event_Test extends Unit_Test_Case { +class Comment_Event_Test extends Gallery_Unit_Test_Case { public function deleting_an_item_deletes_its_comments_too_test() { - $rand = rand(); - $album = album::create(ORM::factory("item", 1), "test_$rand", "test_$rand"); - $comment = comment::create( - $album, identity::guest(), "text_$rand", "name_$rand", "email_$rand", "url_$rand"); + $album = test::random_album(); + + $comment = ORM::factory("comment"); + $comment->item_id = $album->id; + $comment->author_id = identity::guest()->id; + $comment->guest_name = "test"; + $comment->text = "text"; + $comment->save(); $album->delete(); - $deleted_comment = ORM::factory("comment", $comment->id); - $this->assert_false($deleted_comment->loaded()); + $this->assert_false(ORM::factory("comment", $comment->id)->loaded()); } } diff --git a/modules/comment/tests/Comment_Helper_Test.php b/modules/comment/tests/Comment_Helper_Test.php index 8e726869..7ba024c7 100644 --- a/modules/comment/tests/Comment_Helper_Test.php +++ b/modules/comment/tests/Comment_Helper_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Comment_Helper_Test extends Unit_Test_Case { +class Comment_Helper_Test extends Gallery_Unit_Test_Case { private $_ip_address; private $_user_agent; @@ -48,15 +48,19 @@ class Comment_Helper_Test extends Unit_Test_Case { } public function create_comment_for_guest_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $comment = comment::create( - $root, identity::guest(), "text_$rand", "name_$rand", "email_$rand", "url_$rand"); + $comment = ORM::factory("comment"); + $comment->item_id = item::root()->id; + $comment->text = "text"; + $comment->author_id = identity::guest()->id; + $comment->guest_name = "name"; + $comment->guest_email = "email@email.com"; + $comment->guest_url = "http://url.com"; + $comment->save(); - $this->assert_equal("name_$rand", $comment->author_name()); - $this->assert_equal("email_$rand", $comment->author_email()); - $this->assert_equal("url_$rand", $comment->author_url()); - $this->assert_equal("text_$rand", $comment->text); + $this->assert_equal("name", $comment->author_name()); + $this->assert_equal("email@email.com", $comment->author_email()); + $this->assert_equal("http://url.com", $comment->author_url()); + $this->assert_equal("text", $comment->text); $this->assert_equal(1, $comment->item_id); $this->assert_equal("REMOTE_ADDR", $comment->server_remote_addr); @@ -78,16 +82,18 @@ class Comment_Helper_Test extends Unit_Test_Case { } public function create_comment_for_user_test() { - $rand = rand(); - $root = ORM::factory("item", 1); $admin = identity::admin_user(); - $comment = comment::create( - $root, $admin, "text_$rand", "name_$rand", "email_$rand", "url_$rand"); + + $comment = ORM::factory("comment"); + $comment->item_id = item::root()->id; + $comment->text = "text"; + $comment->author_id = $admin->id; + $comment->save(); $this->assert_equal($admin->full_name, $comment->author_name()); $this->assert_equal($admin->email, $comment->author_email()); $this->assert_equal($admin->url, $comment->author_url()); - $this->assert_equal("text_$rand", $comment->text); + $this->assert_equal("text", $comment->text); $this->assert_equal(1, $comment->item_id); $this->assert_equal("REMOTE_ADDR", $comment->server_remote_addr); diff --git a/modules/comment/tests/Comment_Model_Test.php b/modules/comment/tests/Comment_Model_Test.php index aa91d6f2..f0449c05 100644 --- a/modules/comment/tests/Comment_Model_Test.php +++ b/modules/comment/tests/Comment_Model_Test.php @@ -17,24 +17,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Comment_Model_Test extends Unit_Test_Case { +class Comment_Model_Test extends Gallery_Unit_Test_Case { public function cant_view_comments_for_unviewable_items_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), rand(), rand()); - $comment = comment::create($album, identity::guest(), "text", "name", "email", "url"); + $album = test::random_album(); + + $comment = ORM::factory("comment"); + $comment->item_id = $album->id; + $comment->author_id = identity::admin_user()->id; + $comment->text = "text"; + $comment->save(); + identity::set_active_user(identity::guest()); // We can see the comment when permissions are granted on the album access::allow(identity::everybody(), "view", $album); - $this->assert_equal( - 1, + $this->assert_true( ORM::factory("comment")->viewable()->where("comments.id", "=", $comment->id)->count_all()); // We can't see the comment when permissions are denied on the album access::deny(identity::everybody(), "view", $album); - $this->assert_equal( - 0, + $this->assert_false( ORM::factory("comment")->viewable()->where("comments.id", "=", $comment->id)->count_all()); } } diff --git a/modules/comment/views/admin_block_recent_comments.html.php b/modules/comment/views/admin_block_recent_comments.html.php index 99f72a30..4017e4f9 100644 --- a/modules/comment/views/admin_block_recent_comments.html.php +++ b/modules/comment/views/admin_block_recent_comments.html.php @@ -8,10 +8,16 @@ width="32" height="32" /> <?= gallery::date_time($comment->created) ?> + <? if ($comment->author()->guest): ?> + <?= t('%author_name said <em>%comment_text</em>', + array("author_name" => html::clean($comment->author_name()), + "comment_text" => text::limit_words(nl2br(html::purify($comment->text)), 50))); ?> + <? else: ?> <?= t('<a href="%url">%author_name</a> said <em>%comment_text</em>', array("author_name" => html::clean($comment->author_name()), "url" => user_profile::url($comment->author_id), "comment_text" => text::limit_words(nl2br(html::purify($comment->text)), 50))); ?> + <? endif ?> </li> <? endforeach ?> </ul> diff --git a/modules/comment/views/comment.html.php b/modules/comment/views/comment.html.php index c4cf1ce0..263e5f97 100644 --- a/modules/comment/views/comment.html.php +++ b/modules/comment/views/comment.html.php @@ -8,10 +8,16 @@ width="40" height="40" /> </a> + <? if ($comment->author()->guest): ?> + <?= t("on %date_time, %name said", + array("date_time" => gallery::date_time($comment->created), + "name" => html::clean($comment->author_name()))) ?> + <? else: ?> <?= t("on %date_time, <a href=\"%url\">%name</a> said", array("date_time" => gallery::date_time($comment->created), "url" => user_profile::url($comment->author_id), "name" => html::clean($comment->author_name()))) ?> + <? endif ?> </p> <div> <?= nl2br(html::purify($comment->text)) ?> diff --git a/modules/comment/views/comments.html.php b/modules/comment/views/comments.html.php index c8236997..0ed07c22 100644 --- a/modules/comment/views/comments.html.php +++ b/modules/comment/views/comments.html.php @@ -22,10 +22,16 @@ width="40" height="40" /> </a> + <? if ($comment->author()->guest): ?> + <?= t('on %date %name said', + array("date" => date("Y-M-d H:i:s", $comment->created), + "name" => html::clean($comment->author_name()))); ?> + <? else: ?> <?= t('on %date <a href="%url">%name</a> said', array("date" => date("Y-M-d H:i:s", $comment->created), "url" => user_profile::url($comment->author_id), "name" => html::clean($comment->author_name()))); ?> + <? endif ?> </p> <div> <?= nl2br(html::purify($comment->text)) ?> diff --git a/modules/digibug/controllers/digibug.php b/modules/digibug/controllers/digibug.php index 6e6009db..c98ae20c 100644 --- a/modules/digibug/controllers/digibug.php +++ b/modules/digibug/controllers/digibug.php @@ -36,7 +36,7 @@ class Digibug_Controller extends Controller { } $v = new View("digibug_form.html"); - $v->order_parms = array( + $v->order_params = array( "digibug_api_version" => "100", "company_id" => module::get_var("digibug", "company_id"), "event_id" => module::get_var("digibug", "event_id"), @@ -91,7 +91,7 @@ class Digibug_Controller extends Controller { } // We don't need to save the session for this request - Session::abort_save(); + Session::instance()->abort_save(); if (!TEST_MODE) { // Dump out the image diff --git a/modules/digibug/tests/Digibug_Controller_Test.php b/modules/digibug/tests/Digibug_Controller_Test.php index 015a270a..561dd3c9 100644 --- a/modules/digibug/tests/Digibug_Controller_Test.php +++ b/modules/digibug/tests/Digibug_Controller_Test.php @@ -17,42 +17,42 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Digibug_Controller_Test extends Unit_Test_Case { - private $_proxy; - private $_item; +class Digibug_Controller_Test extends Gallery_Unit_Test_Case { private $_server; + public function setup() { + $this->_server = $_SERVER; + } + public function teardown() { $_SERVER = $this->_server; } - public function setup() { - $this->_server = $_SERVER; + private function _get_proxy() { + $album = test::random_album(); + $photo = test::random_photo($album); - $root = ORM::factory("item", 1); - $this->_album = album::create($root, rand(), "test album"); - access::deny(identity::everybody(), "view_full", $this->_album); - access::deny(identity::registered_users(), "view_full", $this->_album); + access::deny(identity::everybody(), "view_full", $album); + access::deny(identity::registered_users(), "view_full", $album); - $rand = rand(); - $this->_item = photo::create($this->_album, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", - $rand, $rand); - $this->_proxy = ORM::factory("digibug_proxy"); - $this->_proxy->uuid = md5(rand()); - $this->_proxy->item_id = $this->_item->id; - $this->_proxy->save(); + $proxy = ORM::factory("digibug_proxy"); + $proxy->uuid = md5(rand()); + $proxy->item_id = $photo->id; + return $proxy->save(); } public function digibug_request_thumb_test() { + $proxy = $this->_get_proxy(); + $controller = new Digibug_Controller(); - $controller->print_proxy("thumb", $this->_proxy->uuid); + $controller->print_proxy("thumb", $proxy->uuid); } public function digibug_request_full_malicious_ip_test() { $_SERVER["REMOTE_ADDR"] = "123.123.123.123"; try { $controller = new Digibug_Controller(); - $controller->print_proxy("full", $this->_proxy->uuid); + $controller->print_proxy("full", $this->_get_proxy()->uuid); $this->assert_true(false, "Should have failed with an 404 exception"); } catch (Kohana_404_Exception $e) { // expected behavior @@ -69,6 +69,6 @@ class Digibug_Controller_Test extends Unit_Test_Case { $_SERVER["REMOTE_ADDR"] = long2ip(rand($low, $high)); $controller = new Digibug_Controller(); - $controller->print_proxy("full", $this->_proxy->uuid); + $controller->print_proxy("full", $this->_get_proxy()->uuid); } } diff --git a/modules/digibug/views/digibug_form.html.php b/modules/digibug/views/digibug_form.html.php index c6994cbe..af5a88b4 100644 --- a/modules/digibug/views/digibug_form.html.php +++ b/modules/digibug/views/digibug_form.html.php @@ -2,10 +2,12 @@ <html> <body> <?= form::open("http://www.digibug.com/dapi/order.php") ?> - <?= form::hidden($order_parms) ?> - <?= form::close() ?> + <? foreach ($order_params as $key => $value): ?> + <?= form::hidden($key, $value) ?> + <? endforeach ?> + </form> <script type="text/javascript"> - document.forms[0].submit(); + document.forms[0].submit(); </script> </body> </html> diff --git a/modules/exif/helpers/exif_task.php b/modules/exif/helpers/exif_task.php index 27352643..90869630 100644 --- a/modules/exif/helpers/exif_task.php +++ b/modules/exif/helpers/exif_task.php @@ -82,7 +82,7 @@ class exif_task_Core { $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $task->log($e->__toString()); + $task->log((string)$e); } } } diff --git a/modules/exif/tests/Exif_Test.php b/modules/exif/tests/Exif_Test.php index 5c5a5cb1..e4835b7f 100644 --- a/modules/exif/tests/Exif_Test.php +++ b/modules/exif/tests/Exif_Test.php @@ -17,12 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Exif_Test extends Unit_Test_Case { +class Exif_Test extends Gallery_Unit_Test_Case { public function exif_extract_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $photo = photo::create( - $root, MODPATH . "exif/tests/data/image.jpg", "$rand.jpg", $rand, $rand); + $photo = test::random_photo_unsaved() + ->set_data_file(MODPATH . "exif/tests/data/image.jpg") + ->save(); $expected = array( array("caption" => "Camera Maker", "value" => "Pentax Corporation"), diff --git a/modules/forge/libraries/Form_Group.php b/modules/forge/libraries/Form_Group.php index e0601321..0a04912b 100644 --- a/modules/forge/libraries/Form_Group.php +++ b/modules/forge/libraries/Form_Group.php @@ -80,7 +80,7 @@ class Form_Group_Core extends Forge { } } - public function render() + public function render($template = 'forge_template', $custom = FALSE) { // No Sir, we don't want any html today thank you return; diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php index 1c65f482..6dd155b9 100644 --- a/modules/g2_import/controllers/admin_g2_import.php +++ b/modules/g2_import/controllers/admin_g2_import.php @@ -19,6 +19,7 @@ */ class Admin_g2_import_Controller extends Admin_Controller { public function index() { + g2_import::lower_error_reporting(); if (g2_import::is_configured()) { g2_import::init(); } @@ -31,6 +32,7 @@ class Admin_g2_import_Controller extends Admin_Controller { $view = new Admin_View("admin.html"); $view->content = new View("admin_g2_import.html"); $view->content->form = $this->_get_import_form(); + $view->content->version = g2_import::version(); if (g2_import::is_initialized()) { $view->content->g2_stats = $g2_stats; @@ -38,11 +40,13 @@ class Admin_g2_import_Controller extends Admin_Controller { $view->content->thumb_size = module::get_var("gallery", "thumb_size"); $view->content->resize_size = module::get_var("gallery", "resize_size"); } + g2_import::restore_error_reporting(); print $view; } public function save() { access::verify_csrf(); + g2_import::lower_error_reporting(); $form = $this->_get_import_form(); if ($form->validate()) { @@ -63,6 +67,7 @@ class Admin_g2_import_Controller extends Admin_Controller { $view = new Admin_View("admin.html"); $view->content = new View("admin_g2_import.html"); $view->content->form = $form; + g2_import::restore_error_reporting(); print $view; } diff --git a/modules/g2_import/controllers/g2.php b/modules/g2_import/controllers/g2.php index 3e002758..5fd4400c 100644 --- a/modules/g2_import/controllers/g2.php +++ b/modules/g2_import/controllers/g2.php @@ -50,7 +50,7 @@ class G2_Controller extends Admin_Controller { throw new Kohana_404_Exception(); } - $item = ORM::factory("item")->where("id", "=", $g2_map->g3_id)->find(); + $item = ORM::factory("item", $g2_map->g3_id); if (!$item->loaded() || !access::can("view", $item)) { throw new Kohana_404_Exception(); } diff --git a/modules/g2_import/helpers/g2_import.php b/modules/g2_import/helpers/g2_import.php index 8724ae50..0fcc0539 100644 --- a/modules/g2_import/helpers/g2_import.php +++ b/modules/g2_import/helpers/g2_import.php @@ -24,6 +24,7 @@ class g2_import_Core { public static $g2_base_url = null; private static $current_g2_item = null; + private static $error_reporting = null; static function is_configured() { return module::get_var("g2_import", "embed_path"); @@ -239,7 +240,7 @@ class g2_import_Core { $g2_group = g2(GalleryCoreApi::loadEntitiesById($g2_group_id)); } catch (Exception $e) { return t("Failed to import Gallery 2 group with id: %id\n%exception", - array("id" => $g2_group_id, "exception" => $e->__toString())); + array("id" => $g2_group_id, "exception" => (string)$e)); } switch ($g2_group->getGroupType()) { @@ -295,21 +296,20 @@ class g2_import_Core { $g2_user = g2(GalleryCoreApi::loadEntitiesById($g2_user_id)); } catch (Exception $e) { return t("Failed to import Gallery 2 user with id: %id\n%exception", - array("id" => $g2_user_id, "exception" => $e->__toString())); + array("id" => $g2_user_id, "exception" => (string)$e)); } $g2_groups = g2(GalleryCoreApi::fetchGroupsForUser($g2_user->getId())); - try { + $user = identity::lookup_user_by_name($g2_user->getUsername()); + if ($user) { + $message = t("Loaded existing user: '%name'.", array("name" => $user->name)); + } else { $user = identity::create_user($g2_user->getUsername(), $g2_user->getfullname(), ""); $message = t("Created user: '%name'.", array("name" => $user->name)); - } catch (Exception $e) { - // @todo For now we assume this is a "duplicate user" exception - $user = identity::lookup_user_by_name($g2_user->getUsername()); - $message = t("Loaded existing user: '%name'.", array("name" => $user->name)); } $user->hashed_password = $g2_user->getHashedPassword(); - $user->email = $g2_user->getEmail(); + $user->email = $g2_user->getEmail() ? $g2_user->getEmail() : "unknown@unknown.com"; $user->locale = $g2_user->getLanguage(); foreach ($g2_groups as $g2_group_id => $g2_group_name) { if ($g2_group_id == $g2_admin_group_id) { @@ -353,7 +353,7 @@ class g2_import_Core { $g2_album = g2(GalleryCoreApi::loadEntitiesById($g2_album_id)); } catch (Exception $e) { return t("Failed to import Gallery 2 album with id: %id\n%exception", - array("id" => $g2_album_id, "exception" => $e->__toString())); + array("id" => $g2_album_id, "exception" => (string)$e)); } if ($g2_album->getParentId() == null) { @@ -361,13 +361,13 @@ class g2_import_Core { } $parent_album = ORM::factory("item", self::map($g2_album->getParentId())); - $album = album::create( - $parent_album, - $g2_album->getPathComponent(), - self::_decode_html_special_chars($g2_album->getTitle()), - self::_decode_html_special_chars(self::extract_description($g2_album)), - self::map($g2_album->getOwnerId())); - + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = self::map($g2_album->getParentId()); + $album->name = $g2_album->getPathComponent(); + $album->title = self::_decode_html_special_chars($g2_album->getTitle()); + $album->description = self::_decode_html_special_chars(self::extract_description($g2_album)); + $album->owner_id = self::map($g2_album->getOwnerId()); $album->view_count = g2(GalleryCoreApi::fetchItemViewCount($g2_album_id)); $album->created = $g2_album->getCreationTimestamp(); @@ -449,7 +449,7 @@ class g2_import_Core { $g2_path = g2($g2_item->fetchPath()); } catch (Exception $e) { return t("Failed to import Gallery 2 item with id: %id\n%exception", - array("id" => $g2_item_id, "exception" => $e->__toString())); + array("id" => $g2_item_id, "exception" => (string)$e)); } $parent = ORM::factory("item", self::map($g2_item->getParentId())); @@ -482,19 +482,21 @@ class g2_import_Core { $corrupt = 1; } try { - $item = photo::create( - $parent, - $g2_path, - $g2_item->getPathComponent(), - self::_decode_html_special_chars($g2_item->getTitle()), - self::_decode_html_special_chars(self::extract_description($g2_item)), - self::map($g2_item->getOwnerId())); + $item = ORM::factory("item"); + $item->type = "photo"; + $item->parent_id = $parent->id; + $item->set_data_file($g2_path); + $item->name = $g2_item->getPathComponent(); + $item->title = self::_decode_html_special_chars($g2_item->getTitle()); + $item->description = self::_decode_html_special_chars(self::extract_description($g2_item)); + $item->owner_id = self::map($g2_item->getOwnerId()); + $item->save(); } catch (Exception $e) { - Kohana_Log::add( - "alert", "Corrupt image $g2_path\n" . $e->__toString()); + Kohana_Log::add("alert", "Corrupt image $g2_path\n" . (string)$e); $message[] = t("Corrupt image '%path'", array("path" => $g2_path)); - $message[] = $e->__toString(); + $message[] = (string)$e; $corrupt = 1; + $item = null; } break; @@ -502,18 +504,19 @@ class g2_import_Core { // @todo we should transcode other types into FLV if (in_array($g2_item->getMimeType(), array("video/mp4", "video/x-flv"))) { try { - $item = movie::create( - $parent, - $g2_path, - $g2_item->getPathComponent(), - self::_decode_html_special_chars($g2_item->getTitle()), - self::_decode_html_special_chars(self::extract_description($g2_item)), - self::map($g2_item->getOwnerId())); + $item = ORM::factory("item"); + $item->parent_id = $parent->id; + $item->set_data_file($g2_path); + $item->name = $g2_item->getPathComponent(); + $item->title = self::_decode_html_special_chars($g2_item->getTitle()); + $item->description = self::_decode_html_special_chars(self::extract_description($g2_item)); + $item->owner_id = self::map($g2_item->getOwnerId()); } catch (Exception $e) { - Kohana_Log::add("alert", "Corrupt movie $g2_path\n" . $e->__toString()); + Kohana_Log::add("alert", "Corrupt movie $g2_path\n" . (string)$e); $message[] = t("Corrupt movie '%path'", array("path" => $g2_path)); - $message[] = $e->__toString(); + $message[] = (string)$e; $corrupt = 1; + $item = null; } } else { Kohana_Log::add("alert", "$g2_path is an unsupported movie type"); @@ -593,7 +596,7 @@ class g2_import_Core { $g2_comment = g2(GalleryCoreApi::loadEntitiesById($g2_comment_id)); } catch (Exception $e) { return t("Failed to import Gallery 2 comment with id: %id\%exception", - array("id" => $g2_comment_id, "exception" => $e->__toString())); + array("id" => $g2_comment_id, "exception" => (string)$e)); } $text = $g2_comment->getSubject(); @@ -631,15 +634,17 @@ class g2_import_Core { GalleryCoreApi::requireOnce("modules/tags/classes/TagsHelper.class"); $g2_item_id = array_shift($queue); $g3_item = ORM::factory("item", self::map($g2_item_id)); + if (!$g3_item->loaded()) { + return; + } try { $tag_names = array_values(g2(TagsHelper::getTagsByItemId($g2_item_id))); } catch (Exception $e) { return t("Failed to import Gallery 2 tags for item with id: %id\n%exception", - array("id" => $g2_item_id, "exception" => $e->__toString())); + array("id" => $g2_item_id, "exception" => (string)$e)); } - // Multiword tags have the space changed to dots.s foreach ($tag_names as $tag_name) { tag::add($g3_item, $tag_name); } @@ -927,6 +932,16 @@ class g2_import_Core { "useAuthToken" => false)); return str_replace(self::$g2_base_url, "", $url); } + + static function lower_error_reporting() { + // Gallery 2 was not designed to run in E_STRICT mode and will barf out errors. So dial down + // the error reporting when we make G2 calls. + self::$error_reporting = error_reporting(error_reporting() & ~E_STRICT); + } + + static function restore_error_reporting() { + error_reporting(self::$error_reporting); + } } /** diff --git a/modules/g2_import/helpers/g2_import_installer.php b/modules/g2_import/helpers/g2_import_installer.php index 77b61d3e..1ec8afa9 100644 --- a/modules/g2_import/helpers/g2_import_installer.php +++ b/modules/g2_import/helpers/g2_import_installer.php @@ -31,7 +31,7 @@ class g2_import_installer { KEY `g2_id` (`g2_id`)) DEFAULT CHARSET=utf8;"); - module::set_version("g2_import", 1); + module::set_version("g2_import", 2); mkdir(VARPATH . "modules/g2_import"); } diff --git a/modules/g2_import/helpers/g2_import_task.php b/modules/g2_import/helpers/g2_import_task.php index e80b88b9..21ba4c3a 100644 --- a/modules/g2_import/helpers/g2_import_task.php +++ b/modules/g2_import/helpers/g2_import_task.php @@ -19,17 +19,19 @@ */ class g2_import_task_Core { static function available_tasks() { + g2_import::lower_error_reporting(); if (g2_import::is_configured()) { g2_import::init(); } - + $version = g2_import::version(); + g2_import::restore_error_reporting(); if (class_exists("GalleryCoreApi")) { return array(Task_Definition::factory() ->callback("g2_import_task::import") ->name(t("Import from Gallery 2")) ->description( - t("Gallery %version detected", array("version" => g2_import::version()))) + t("Gallery %version detected", array("version" => $version))) ->severity(log::SUCCESS)); } @@ -37,6 +39,8 @@ class g2_import_task_Core { } static function import($task) { + g2_import::lower_error_reporting(); + $start = microtime(true); g2_import::init(); @@ -207,5 +211,7 @@ class g2_import_task_Core { $task->set("mode", $mode); $task->set("queue", $queue); $task->set("done", $done); + + g2_import::restore_error_reporting(); } } diff --git a/modules/g2_import/views/admin_g2_import.html.php b/modules/g2_import/views/admin_g2_import.html.php index 0875e7f7..6a5214a3 100644 --- a/modules/g2_import/views/admin_g2_import.html.php +++ b/modules/g2_import/views/admin_g2_import.html.php @@ -34,7 +34,7 @@ <h2> <?= t("Import") ?> </h2> <ul id="g-action-status" class="g-message-block"> <li class="g-success"> - <?= t("Gallery version %version detected", array("version" => g2_import::version())) ?> + <?= t("Gallery version %version detected", array("version" => $version)) ?> </li> <? if ($g2_sizes["thumb"]["size"] && $thumb_size != $g2_sizes["thumb"]["size"]): ?> <li class="g-warning"> diff --git a/modules/gallery/config/locale.php b/modules/gallery/config/locale.php index 4789dc2b..f8241444 100644 --- a/modules/gallery/config/locale.php +++ b/modules/gallery/config/locale.php @@ -32,7 +32,11 @@ $config['language'] = array('en_US', 'English_United States'); * Locale timezone. Defaults to use the server timezone. * @see http://php.net/timezones */ -$config['timezone'] = ''; +$config['timezone'] = ini_get('date.timezone'); +if (empty($config['timezone'])) { + // This is a required field. Pick something as a default. + $config['timezone'] = "America/Los_Angeles"; +} // i18n settings diff --git a/modules/gallery/config/routes.php b/modules/gallery/config/routes.php index 63cc6150..8ccd5a01 100644 --- a/modules/gallery/config/routes.php +++ b/modules/gallery/config/routes.php @@ -21,6 +21,10 @@ // Admin controllers are not available, except via /admin $config["^admin_.*"] = null; +// Redirect /form/add/admin/controller and /form/edit/admin/controller to +// admin/controller/form_(add|edit)/parms. provides the same as below for admin pages +$config["^form/(edit|add)/admin/(\w+)/(.*)$"] = "admin/$2/form_$1/$3"; + // Redirect /form/add and /form/edit to the module/form_(add|edit)/parms. $config["^form/(edit|add)/(\w+)/(.*)$"] = "$2/form_$1/$3"; diff --git a/modules/gallery/controllers/admin_dashboard.php b/modules/gallery/controllers/admin_dashboard.php index 5f2cb41d..42a3c081 100644 --- a/modules/gallery/controllers/admin_dashboard.php +++ b/modules/gallery/controllers/admin_dashboard.php @@ -91,7 +91,5 @@ class Admin_Dashboard_Controller extends Admin_Controller { } block_manager::set_active($location, $new_blocks); } - - $this->_force_block_adder(); } } diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index aa4fb29f..3062ea09 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -215,7 +215,7 @@ class Admin_Maintenance_Controller extends Admin_Controller { print json_encode(array("result" => "success", "task" => array( "percent_complete" => $task->percent_complete, - "status" => $task->status, + "status" => (string) $task->status, "done" => (bool) $task->done), "location" => url::site("admin/maintenance"))); @@ -223,7 +223,7 @@ class Admin_Maintenance_Controller extends Admin_Controller { print json_encode(array("result" => "in_progress", "task" => array( "percent_complete" => $task->percent_complete, - "status" => $task->status, + "status" => (string) $task->status, "done" => (bool) $task->done))); } } diff --git a/modules/gallery/controllers/admin_modules.php b/modules/gallery/controllers/admin_modules.php index 84fee25d..081b3f12 100644 --- a/modules/gallery/controllers/admin_modules.php +++ b/modules/gallery/controllers/admin_modules.php @@ -67,6 +67,7 @@ class Admin_Modules_Controller extends Admin_Controller { } private function _do_save() { + $changes = new stdClass(); $changes->activate = array(); $changes->deactivate = array(); $activated_names = array(); diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index 2eeefdf1..a378f3ee 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -95,30 +95,36 @@ class Albums_Controller extends Items_Controller { access::required("view", $album); access::required("add", $album); - $input = Input::instance(); $form = album::get_add_form($album); - if ($form->validate()) { - $new_album = album::create( - $album, - $input->post("name"), - $input->post("title", $input->post("name")), - $input->post("description"), - identity::active_user()->id, - $input->post("slug")); + try { + $valid = $form->validate(); + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent_id; + $album->name = $form->add_album->inputs["name"]->value; + $album->title = $form->add_album->title->value ? + $form->add_album->title->value : $form->add_album->inputs["name"]->value; + $album->description = $form->add_album->description->value; + $album->slug = $form->add_album->slug->value; + $album->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->add_album->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + if ($valid) { + $album->save(); log::success("content", "Created an album", - html::anchor("albums/$new_album->id", "view album")); + html::anchor("albums/$album->id", "view album")); message::success(t("Created album %album_title", - array("album_title" => html::purify($new_album->title)))); + array("album_title" => html::purify($album->title)))); - print json_encode( - array("result" => "success", - "location" => $new_album->url())); + print json_encode(array("result" => "success", "location" => $album->url())); } else { - print json_encode( - array( - "result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -129,42 +135,24 @@ class Albums_Controller extends Items_Controller { access::required("edit", $album); $form = album::get_edit_form($album); - if ($valid = $form->validate()) { - if ($album->id != 1 && - $form->edit_item->dirname->value != $album->name || - $form->edit_item->slug->value != $album->slug) { - // Make sure that there's not a conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $album->parent_id) - ->where("id", "<>", $album->id) - ->and_open() - ->where("name", "=", $form->edit_item->dirname->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->dirname->value) { - $form->edit_item->dirname->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; - } - } - } - - if ($valid) { + try { + $valid = $form->validate(); $album->title = $form->edit_item->title->value; $album->description = $form->edit_item->description->value; $album->sort_column = $form->edit_item->sort_order->column->value; $album->sort_order = $form->edit_item->sort_order->direction->value; - if ($album->id != 1) { - $album->rename($form->edit_item->dirname->value); - } + $album->name = $form->edit_item->inputs["name"]->value; $album->slug = $form->edit_item->slug->value; + $album->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_item->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + + if ($valid) { $album->save(); module::event("item_edit_form_completed", $album, $form); @@ -180,9 +168,7 @@ class Albums_Controller extends Items_Controller { print json_encode(array("result" => "success")); } } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/gallery/controllers/combined.php b/modules/gallery/controllers/combined.php index e90a2f1a..7f3a3c7d 100644 --- a/modules/gallery/controllers/combined.php +++ b/modules/gallery/controllers/combined.php @@ -41,7 +41,7 @@ class Combined_Controller extends Controller { $input = Input::instance(); // We don't need to save the session for this request - Session::abort_save(); + Session::instance()->abort_save(); // Our data is immutable, so if they already have a copy then it needs no updating. if ($input->server("HTTP_IF_MODIFIED_SINCE")) { diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index 646edf17..33952366 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -121,7 +121,7 @@ class File_Proxy_Controller extends Controller { expires::check(2592000, $item->updated); // We don't need to save the session for this request - Session::abort_save(); + Session::instance()->abort_save(); expires::set(2592000, $item->updated); // 30 days diff --git a/modules/gallery/controllers/login.php b/modules/gallery/controllers/login.php index cfccaf17..1426f0d8 100644 --- a/modules/gallery/controllers/login.php +++ b/modules/gallery/controllers/login.php @@ -33,9 +33,7 @@ class Login_Controller extends Controller { print json_encode( array("result" => "success")); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -64,11 +62,10 @@ class Login_Controller extends Controller { if ($valid) { $user = identity::lookup_user_by_name($form->login->inputs["name"]->value); if (empty($user) || !identity::is_correct_password($user, $form->login->password->value)) { - log::warning( - "user", - t("Failed login for %name", - array("name" => $form->login->inputs["name"]->value))); $form->login->inputs["name"]->add_error("invalid_login", 1); + $name = $form->login->inputs["name"]->value; + log::warning("user", t("Failed login for %name", array("name" => $name))); + module::event("user_login_failed", $name); $valid = false; } } diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php index 7a8e4d2a..b51282b3 100644 --- a/modules/gallery/controllers/movies.php +++ b/modules/gallery/controllers/movies.php @@ -61,48 +61,22 @@ class Movies_Controller extends Items_Controller { access::required("edit", $movie); $form = movie::get_edit_form($movie); - $valid = $form->validate(); - - if ($valid) { - $new_ext = pathinfo($form->edit_item->filename->value, PATHINFO_EXTENSION); - $old_ext = pathinfo($movie->name, PATHINFO_EXTENSION); - if (strcasecmp($new_ext, $old_ext)) { - $form->edit_item->filename->add_error("illegal_extension", 1); - $valid = false; - } - } - - if ($valid) { - if ($form->edit_item->filename->value != $movie->name || - $form->edit_item->slug->value != $movie->slug) { - // Make sure that there's not a name or slug conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $movie->parent_id) - ->where("id", "<>", $movie->id) - ->and_open() - ->where("name", "=", $form->edit_item->filename->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->filename->value) { - $form->edit_item->filename->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; - } + try { + $valid = $form->validate(); + $movie->title = $form->edit_item->title->value; + $movie->description = $form->edit_item->description->value; + $movie->slug = $form->edit_item->slug->value; + $movie->name = $form->edit_item->inputs["name"]->value; + $movie->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_item->inputs[$key]->add_error($error, 1); } + $valid = false; } if ($valid) { - $movie->title = $form->edit_item->title->value; - $movie->description = $form->edit_item->description->value; - $movie->slug = $form->edit_item->slug->value; - $movie->rename($form->edit_item->filename->value); $movie->save(); module::event("item_edit_form_completed", $movie, $form); @@ -118,9 +92,7 @@ class Movies_Controller extends Items_Controller { print json_encode(array("result" => "success")); } } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/gallery/controllers/packager.php b/modules/gallery/controllers/packager.php index cb64f1bf..66626483 100644 --- a/modules/gallery/controllers/packager.php +++ b/modules/gallery/controllers/packager.php @@ -164,7 +164,7 @@ class Packager_Controller extends Controller { foreach($objects as $name => $file){ if ($file->getBasename() == "database.php") { continue; - } else if (basename($file->getPath()) == "logs") { + } else if (basename($file->getPath()) == "logs" && $file->getBasename() != ".htaccess") { continue; } @@ -172,8 +172,8 @@ class Packager_Controller extends Controller { $paths[] = "VARPATH . \"" . substr($name, strlen(VARPATH)) . "\""; } else { // @todo: serialize non-directories - print "IGNORING FILE: $name\n"; - return; + $files["VARPATH . \"" . substr($name, strlen(VARPATH)) . "\""] = + base64_encode(file_get_contents($name)); } } // Sort the paths so that the var file is stable @@ -185,6 +185,9 @@ class Packager_Controller extends Controller { foreach ($paths as $path) { fwrite($fd, "!file_exists($path) && mkdir($path);\n"); } + foreach ($files as $file => $contents) { + fwrite($fd, "file_put_contents($file, base64_decode(\"$contents\"));\n"); + } fclose($fd); } }
\ No newline at end of file diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php index 56b454ce..b5da3884 100644 --- a/modules/gallery/controllers/photos.php +++ b/modules/gallery/controllers/photos.php @@ -61,48 +61,22 @@ class Photos_Controller extends Items_Controller { access::required("edit", $photo); $form = photo::get_edit_form($photo); - $valid = $form->validate(); - - if ($valid) { - $new_ext = pathinfo($form->edit_item->filename->value, PATHINFO_EXTENSION); - $old_ext = pathinfo($photo->name, PATHINFO_EXTENSION); - if (strcasecmp($new_ext, $old_ext)) { - $form->edit_item->filename->add_error("illegal_extension", 1); - $valid = false; - } - } - - if ($valid) { - if ($form->edit_item->filename->value != $photo->name || - $form->edit_item->slug->value != $photo->slug) { - // Make sure that there's not a name or slug conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $photo->parent_id) - ->where("id", "<>", $photo->id) - ->and_open() - ->where("name", "=", $form->edit_item->filename->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->filename->value) { - $form->edit_item->filename->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; - } + try { + $valid = $form->validate(); + $photo->title = $form->edit_item->title->value; + $photo->description = $form->edit_item->description->value; + $photo->slug = $form->edit_item->slug->value; + $photo->name = $form->edit_item->inputs["name"]->value; + $photo->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_item->inputs[$key]->add_error($error, 1); } + $valid = false; } if ($valid) { - $photo->title = $form->edit_item->title->value; - $photo->description = $form->edit_item->description->value; - $photo->slug = $form->edit_item->slug->value; - $photo->rename($form->edit_item->filename->value); $photo->save(); module::event("item_edit_form_completed", $photo, $form); @@ -118,9 +92,7 @@ class Photos_Controller extends Items_Controller { print json_encode(array("result" => "success")); } } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php index 7f9a9826..04aab8dc 100644 --- a/modules/gallery/controllers/quick.php +++ b/modules/gallery/controllers/quick.php @@ -47,6 +47,7 @@ class Quick_Controller extends Controller { graphics::generate($item); $parent = $item->parent(); + // @todo: this is an inadequate way to regenerate the parent's thumbnail after rotation. if ($parent->album_cover_item_id == $item->id) { copy($item->thumb_path(), $parent->thumb_path()); $parent->thumb_width = $item->thumb_width; diff --git a/modules/gallery/controllers/simple_uploader.php b/modules/gallery/controllers/simple_uploader.php index 5d32e35f..16d1d241 100644 --- a/modules/gallery/controllers/simple_uploader.php +++ b/modules/gallery/controllers/simple_uploader.php @@ -40,39 +40,52 @@ class Simple_Uploader_Controller extends Controller { access::required("add", $album); access::verify_csrf(); + // The Flash uploader not call /start directly, so simulate it here for now. + if (!batch::in_progress()) { + batch::start(); + } + + $form = $this->_get_add_form($album); + + // Uploadify adds its own field to the form, so validate that separately. $file_validation = new Validation($_FILES); $file_validation->add_rules( "Filedata", "upload::valid", "upload::required", "upload::type[gif,jpg,jpeg,png,flv,mp4]"); - if ($file_validation->validate()) { - // SimpleUploader.swf does not yet call /start directly, so simulate it here for now. - if (!batch::in_progress()) { - batch::start(); - } + if ($form->validate() && $file_validation->validate()) { $temp_filename = upload::save("Filedata"); try { - $name = substr(basename($temp_filename), 10); // Skip unique identifier Kohana adds - $title = item::convert_filename_to_title($name); + $item = ORM::factory("item"); + $item->name = substr(basename($temp_filename), 10); // Skip unique identifier Kohana adds + $item->title = item::convert_filename_to_title($item->name); + $item->parent_id = $album->id; + $item->set_data_file($temp_filename); + $path_info = @pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) { - $item = movie::create($album, $temp_filename, $name, $title); + $item->type = "movie"; + $item->save(); log::success("content", t("Added a movie"), html::anchor("movies/$item->id", t("view movie"))); } else { - $item = photo::create($album, $temp_filename, $name, $title); + $item->type = "photo"; + $item->save(); log::success("content", t("Added a photo"), html::anchor("photos/$item->id", t("view photo"))); } - // We currently have no way of showing errors if validation fails, so only call our event - // handlers if validation passes. - $form = $this->_get_add_form($album); - if ($form->validate()) { - module::event("add_photos_form_completed", $item, $form); - } + module::event("add_photos_form_completed", $item, $form); } catch (Exception $e) { - Kohana_Log::add("alert", $e->__toString()); + // The Flash uploader has no good way of reporting complex errors, so just keep it simple. + Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); + + // Ugh. I hate to use instanceof, But this beats catching the exception separately since + // we mostly want to treat it the same way as all other exceptions + if ($e instanceof ORM_Validation_Exception) { + Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1)); + } + if (file_exists($temp_filename)) { unlink($temp_filename); } @@ -84,7 +97,7 @@ class Simple_Uploader_Controller extends Controller { print "FILEID: $item->id"; } else { header("HTTP/1.1 400 Bad Request"); - print "ERROR: " . t("Invalid Upload"); + print "ERROR: " . t("Invalid upload"); } } diff --git a/modules/gallery/controllers/user_profile.php b/modules/gallery/controllers/user_profile.php index a0e6619e..327d2ff1 100644 --- a/modules/gallery/controllers/user_profile.php +++ b/modules/gallery/controllers/user_profile.php @@ -53,11 +53,11 @@ class User_Profile_Controller extends Controller { if ($form->validate()) { Sendmail::factory() ->to($user->email) - ->subject($form->message->subject->value) + ->subject(html::clean($form->message->subject->value)) ->header("Mime-Version", "1.0") ->header("Content-type", "text/html; charset=iso-8859-1") ->reply_to($form->message->reply_to->value) - ->message($form->message->message->value) + ->message(html::purify($form->message->message->value)) ->send(); message::success(t("Sent message to %user_name", array("user_name" => $user->display_name()))); print json_encode(array("result" => "success")); diff --git a/modules/gallery/helpers/MY_url.php b/modules/gallery/helpers/MY_url.php index 8a7909b6..323cae37 100644 --- a/modules/gallery/helpers/MY_url.php +++ b/modules/gallery/helpers/MY_url.php @@ -93,8 +93,8 @@ class url extends url_Core { /** * Just like url::merge except that it escapes any XSS in the path. */ - static function merge($params) { - return htmlspecialchars(parent::merge($params)); + static function merge(array $arguments) { + return htmlspecialchars(parent::merge($arguments)); } /** diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php index e0a0e979..29b981e8 100644 --- a/modules/gallery/helpers/access.php +++ b/modules/gallery/helpers/access.php @@ -183,10 +183,10 @@ class access_Core { } /** - * Terminate immediately with an HTTP 503 Forbidden response. + * Terminate immediately with an HTTP 403 Forbidden response. */ static function forbidden() { - throw new Exception("@todo FORBIDDEN", 503); + throw new Kohana_Exception("@todo FORBIDDEN", null, 403); } /** @@ -663,23 +663,28 @@ class access_Core { * working and our permission system works. */ static function htaccess_works() { - $success_url = url::file("var/tmp/security_test/success"); + $success_url = url::file("var/security_test/success"); - @mkdir(VARPATH . "tmp/security_test"); - if ($fp = @fopen(VARPATH . "tmp/security_test/.htaccess", "w+")) { - fwrite($fp, "RewriteEngine On\n"); - fwrite($fp, "RewriteRule verify $success_url [L]\n"); - fclose($fp); - } + @mkdir(VARPATH . "security_test"); + try { + if ($fp = @fopen(VARPATH . "security_test/.htaccess", "w+")) { + fwrite($fp, "RewriteEngine On\n"); + fwrite($fp, "RewriteRule verify $success_url [L]\n"); + fclose($fp); + } - if ($fp = @fopen(VARPATH . "tmp/security_test/success", "w+")) { - fwrite($fp, "success"); - fclose($fp); - } + if ($fp = @fopen(VARPATH . "security_test/success", "w+")) { + fwrite($fp, "success"); + fclose($fp); + } - list ($response) = remote::do_request(url::abs_file("var/tmp/security_test/verify")); - $works = $response == "HTTP/1.1 200 OK"; - @dir::unlink(VARPATH . "tmp/security_test"); + list ($response) = remote::do_request(url::abs_file("var/security_test/verify")); + $works = $response == "HTTP/1.1 200 OK"; + } catch (Exception $e) { + @dir::unlink(VARPATH . "security_test"); + throw $e; + } + @dir::unlink(VARPATH . "security_test"); return $works; } diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index feaf74cc..389f6e48 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -24,90 +24,27 @@ * Note: by design, this class does not do any permission checking. */ class album_Core { - /** - * Create a new album. - * @param integer $parent_id id of parent album - * @param string $name the name of this new album (it will become the directory name on disk) - * @param integer $title the title of the new album - * @param string $description (optional) the longer description of this album - * @param string $slug (optional) the url component for this photo - * @return Item_Model - */ - static function create($parent, $name, $title, $description=null, $owner_id=null, $slug=null) { - if (!$parent->loaded() || !$parent->is_album()) { - throw new Exception("@todo INVALID_PARENT"); - } - - if (strpos($name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - // We don't allow trailing periods as a security measure - // ref: http://dev.kohanaphp.com/issues/684 - if (rtrim($name, ".") != $name) { - throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); - } - - if (empty($slug)) { - $slug = item::convert_filename_to_slug($name); - } - - $album = ORM::factory("item"); - $album->type = "album"; - $album->title = $title; - $album->description = $description; - $album->name = $name; - $album->owner_id = $owner_id; - $album->thumb_dirty = 1; - $album->resize_dirty = 1; - $album->slug = $slug; - $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album->sort_column = "created"; - $album->sort_order = "ASC"; - - // Randomize the name or slug if there's a conflict - // @todo Improve this. Random numbers are not user friendly - while (ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->and_open() - ->where("name", "=", $album->name) - ->or_where("slug", "=", $album->slug) - ->close() - ->find()->id) { - $rand = rand(); - $album->name = "{$name}-$rand"; - $album->slug = "{$slug}-$rand"; - } - - $album = $album->add_to_parent($parent); - mkdir($album->file_path()); - mkdir(dirname($album->thumb_path())); - mkdir(dirname($album->resize_path())); - - // @todo: publish this from inside Item_Model::save() when we refactor to the point where - // there's only one save() happening here. - module::event("item_created", $album); - - return $album; - } static function get_add_form($parent) { $form = new Forge("albums/create/{$parent->id}", "", "post", array("id" => "g-add-album-form")); $group = $form->group("add_album") ->label(t("Add an album to %album_title", array("album_title" => $parent->title))); - $group->input("title")->label(t("Title")); + $group->input("title")->label(t("Title")) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); $group->textarea("description")->label(t("Description")); $group->input("name")->label(t("Directory name")) - ->callback("item::validate_no_slashes") - ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character")); + ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character")) + ->error_messages("required", t("You must provide a directory name")) + ->error_messages("length", t("Your directory name is too long")); $group->input("slug")->label(t("Internet Address")) - ->callback("item::validate_url_safe") ->error_messages( "not_url_safe", - t("The internet address should contain only letters, numbers, hyphens and underscores")); + t("The internet address should contain only letters, numbers, hyphens and underscores")) + ->error_messages("required", t("You must provide an internet address")) + ->error_messages("length", t("Your internet address is too long")); $group->hidden("type")->value("album"); $group->submit("")->value(t("Create")); - $form->add_rules_from(ORM::factory("item")); $form->script("") ->url(url::abs_file("modules/gallery/js/albums_form_add.js")); return $form; @@ -118,26 +55,28 @@ class album_Core { $form->hidden("from_id"); $group = $form->group("edit_item")->label(t("Edit Album")); - $group->input("title")->label(t("Title"))->value($parent->title); + $group->input("title")->label(t("Title"))->value($parent->title) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); $group->textarea("description")->label(t("Description"))->value($parent->description); if ($parent->id != 1) { - $group->input("dirname")->label(t("Directory Name"))->value($parent->name) - ->rules("required") + $group->input("name")->label(t("Directory Name"))->value($parent->name) ->error_messages( - "name_conflict", t("There is already a movie, photo or album with this name")) - ->callback("item::validate_no_slashes") + "conflict", t("There is already a movie, photo or album with this name")) ->error_messages("no_slashes", t("The directory name can't contain a \"/\"")) - ->callback("item::validate_no_trailing_period") - ->error_messages("no_trailing_period", t("The directory name can't end in \".\"")); + ->error_messages("no_trailing_period", t("The directory name can't end in \".\"")) + ->error_messages("required", t("You must provide a directory name")) + ->error_messages("length", t("Your directory name is too long")); $group->input("slug")->label(t("Internet Address"))->value($parent->slug) ->error_messages( - "slug_conflict", t("There is already a movie, photo or album with this internet address")) - ->callback("item::validate_url_safe") + "conflict", t("There is already a movie, photo or album with this internet address")) ->error_messages( "not_url_safe", - t("The internet address should contain only letters, numbers, hyphens and underscores")); + t("The internet address should contain only letters, numbers, hyphens and underscores")) + ->error_messages("required", t("You must provide an internet address")) + ->error_messages("length", t("Your internet address is too long")); } else { - $group->hidden("dirname")->value($parent->name); + $group->hidden("name")->value($parent->name); $group->hidden("slug")->value($parent->slug); } @@ -159,7 +98,6 @@ class album_Core { $group = $form->group("buttons")->label(""); $group->hidden("type")->value("album"); $group->submit("")->value(t("Modify")); - $form->add_rules_from(ORM::factory("item")); return $form; } diff --git a/modules/gallery/helpers/auth.php b/modules/gallery/helpers/auth.php index 21a39bfb..16f8915a 100644 --- a/modules/gallery/helpers/auth.php +++ b/modules/gallery/helpers/auth.php @@ -22,7 +22,10 @@ class auth_Core { $form = new Forge($url, "", "post", array("id" => "g-login-form")); $form->set_attr('class', "g-narrow"); $group = $form->group("login")->label(t("Login")); - $group->input("name")->label(t("Username"))->id("g-username")->class(null); + $group->input("name")->label(t("Username"))->id("g-username")->class(null) + ->callback("auth::validate_too_many_failed_logins") + ->error_messages( + "too_many_failed_logins", t("Too many failed login attempts. Try again later")); $group->password("password")->label(t("Password"))->id("g-password")->class(null); $group->inputs["name"]->error_messages("invalid_login", t("Invalid name or password")); $group->submit("")->value(t("Login")); @@ -30,12 +33,12 @@ class auth_Core { } static function login($user) { + identity::set_active_user($user); if (identity::is_writable()) { $user->login_count += 1; $user->last_login = time(); $user->save(); } - identity::set_active_user($user); log::info("user", t("User %name logged in", array("name" => $user->name))); module::event("user_login", $user); } @@ -51,6 +54,52 @@ class auth_Core { module::event("user_logout", $user); } log::info("user", t("User %name logged out", array("name" => $user->name)), - html::anchor("user/$user->id", html::clean($user->name))); + t('<a href="%url">%user_name</a>', + array("url" => user_profile::url($user->id), + "user_name" => html::clean($user->name)))); + } + + /** + * After there have been 5 failed login attempts, any failure leads to getting locked out for a + * minute. + */ + static function too_many_failed_logins($name) { + $failed_login = ORM::factory("failed_login") + ->where("name", "=", $name) + ->find(); + return ($failed_login->loaded() && + $failed_login->count > 5 && + (time() - $failed_login->time < 60)); + } + + static function validate_too_many_failed_logins($name_input) { + if (self::too_many_failed_logins($name_input->value)) { + $name_input->add_error("too_many_failed_logins", 1); + } + } + + /** + * Record a failed login for this user + */ + static function record_failed_login($name) { + $failed_login = ORM::factory("failed_login") + ->where("name", "=", $name) + ->find(); + if (!$failed_login->loaded()) { + $failed_login->name = $name; + } + $failed_login->time = time(); + $failed_login->count++; + $failed_login->save(); + } + + /** + * Clear any failed logins for this user + */ + static function record_successful_login($user) { + db::build() + ->delete("failed_logins") + ->where("name", "=", $user->name) + ->execute(); } }
\ No newline at end of file diff --git a/modules/gallery/helpers/gallery_block.php b/modules/gallery/helpers/gallery_block.php index 9d4e81b6..be0f11b8 100644 --- a/modules/gallery/helpers/gallery_block.php +++ b/modules/gallery/helpers/gallery_block.php @@ -72,7 +72,7 @@ class gallery_block_Core { $block->content = new View("admin_block_platform.html"); if (is_readable("/proc/loadavg")) { $block->content->load_average = - join(" ", array_slice(explode(" ", array_shift(file("/proc/loadavg"))), 0, 3)); + join(" ", array_slice(explode(" ", current(file("/proc/loadavg"))), 0, 3)); } else { $block->content->load_average = t("Unavailable"); } diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index b35ae3c4..6479e2c3 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -75,6 +75,24 @@ class gallery_event_Core { static function item_created($item) { access::add_item($item); + + if ($item->is_photo() || $item->is_movie()) { + // Build our thumbnail/resizes. + try { + graphics::generate($item); + } catch (Exception $e) { + log::error("graphics", t("Couldn't create a thumbnail or resize for %item_title", + array("item_title" => $item->title)), + html::anchor($item->abs_url(), t("details"))); + Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); + } + + // If the parent has no cover item, make this it. + $parent = $item->parent(); + if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { + item::make_album_cover($item); + } + } } static function item_deleted($item) { @@ -92,6 +110,11 @@ class gallery_event_Core { graphics::choose_default_toolkit(); module::clear_var("gallery", "choose_default_tookit"); } + auth::record_successful_login($user); + } + + static function user_login_failed($name) { + auth::record_failed_login($name); } static function item_index_data($item, $data) { @@ -268,6 +291,7 @@ class gallery_event_Core { ->label(t("Options")) ->css_class("ui-icon-carat-1-n")); + $page_type = $theme->page_type(); if (access::can("edit", $item)) { switch ($item->type) { case "movie": @@ -306,7 +330,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-ccw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$thumb_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id"))) + ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))) ->append( Menu::factory("ajax_link") ->id("rotate_cw") @@ -314,7 +338,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-cw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$thumb_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id"))); + ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); } // @todo Don't move photos from the photo page; we don't yet have a good way of redirecting @@ -354,7 +378,7 @@ class gallery_event_Core { ->label($delete_title) ->css_class("ui-icon-trash") ->css_id("g-quick-delete") - ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id"))); + ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); } if ($item->is_album()) { @@ -386,14 +410,14 @@ class gallery_event_Core { if (!$data->display_all) { $fields = array("name" => t("Name"), "full_name" => t("Full name"), "url" => "Web site"); } - $v->fields = array(); + $v->user_profile_data = array(); foreach ($fields as $field => $label) { if (!empty($data->user->$field)) { $value = $data->user->$field; if ($field == "locale") { $value = locales::display_name($value); } - $v->fields[(string) $label] = html::clean($value); + $v->user_profile_data[(string) $label] = $value; } } $data->content[] = (object) array("title" => t("User information"), "view" => $v); diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index 1e0ad28c..bffef8e6 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -42,6 +42,14 @@ class gallery_installer { KEY (`tags`)) DEFAULT CHARSET=utf8;"); + $db->query("CREATE TABLE {failed_logins} ( + `id` int(9) NOT NULL auto_increment, + `count` int(9) NOT NULL, + `name` varchar(255) NOT NULL, + `time` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + $db->query("CREATE TABLE {graphics_rules} ( `id` int(9) NOT NULL auto_increment, `active` BOOLEAN default 0, @@ -196,6 +204,9 @@ class gallery_installer { foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) { @mkdir(VARPATH . $dir); + if (in_array($dir, array("logs", "tmp", "uploads"))) { + self::_protect_directory(VARPATH . $dir); + } } access::register_permission("view", "View"); @@ -209,19 +220,26 @@ class gallery_installer { t("Edit"); t("Add"); - $root = ORM::factory("item"); - $root->type = "album"; - $root->title = "Gallery"; - $root->description = ""; - $root->left_ptr = 1; - $root->right_ptr = 2; - $root->parent_id = 0; - $root->level = 1; - $root->thumb_dirty = 1; - $root->resize_dirty = 1; - $root->sort_column = "weight"; - $root->sort_order = "ASC"; - $root->save(); + // Hardcode the first item to sidestep ORM validation rules + $now = time(); + db::build()->insert( + "items", + array("created" => $now, + "description" => "", + "left_ptr" => 1, + "level" => 1, + "parent_id" => 0, + "resize_dirty" => 1, + "right_ptr" => 2, + "sort_column" => "weight", + "sort_order" => "ASC", + "thumb_dirty" => 1, + "title" => "Gallery", + "type" => "album", + "updated" => $now, + "weight" => 1)) + ->execute(); + $root = ORM::factory("item", 1); access::add_item($root); module::set_var("gallery", "active_site_theme", "wind"); @@ -269,7 +287,7 @@ class gallery_installer { // @todo this string needs to be picked up by l10n_scanner module::set_var("gallery", "credits", "Powered by <a href=\"%url\">Gallery %version</a>"); module::set_var("gallery", "simultaneous_upload_limit", 5); - module::set_version("gallery", 21); + module::set_version("gallery", 25); } static function upgrade($version) { @@ -443,7 +461,7 @@ class gallery_installer { // Update the graphics rules table so that the maximum height for resizes is 640 not 480. // Fixes ticket #671 - if ( $version == 21) { + if ($version == 21) { $resize_rule = ORM::factory("graphics_rule") ->where("id", "=", "2") ->find(); @@ -456,6 +474,46 @@ class gallery_installer { } module::set_version("gallery", $version = 22); } + + // Update slug values to be legal. We should have done this in the 11->12 upgrader, but I was + // lazy. Mea culpa! + if ($version == 22) { + foreach (db::build() + ->from("items") + ->select("id", "slug") + ->where(new Database_Expression("`slug` REGEXP '[^_A-Za-z0-9-]'"), "=", 1) + ->execute() as $row) { + $new_slug = item::convert_filename_to_slug($row->slug); + if (empty($new_slug)) { + $new_slug = rand(); + } + db::build() + ->update("items") + ->set("slug", $new_slug) + ->set("relative_url_cache", null) + ->where("id", "=", $row->id) + ->execute(); + } + module::set_version("gallery", $version = 23); + } + + if ($version == 23) { + $db->query("CREATE TABLE {failed_logins} ( + `id` int(9) NOT NULL auto_increment, + `count` int(9) NOT NULL, + `name` varchar(255) NOT NULL, + `time` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("gallery", $version = 24); + } + + if ($version == 24) { + foreach (array("logs", "tmp", "uploads") as $dir) { + self::_protect_directory(VARPATH . $dir); + } + module::set_version("gallery", $version = 25); + } } static function uninstall() { @@ -464,6 +522,7 @@ class gallery_installer { $db->query("DROP TABLE IF EXISTS {access_intents}"); $db->query("DROP TABLE IF EXISTS {graphics_rules}"); $db->query("DROP TABLE IF EXISTS {incoming_translations}"); + $db->query("DROP TABLE IF EXISTS {failed_logins}"); $db->query("DROP TABLE IF EXISTS {items}"); $db->query("DROP TABLE IF EXISTS {logs}"); $db->query("DROP TABLE IF EXISTS {modules}"); @@ -479,4 +538,12 @@ class gallery_installer { system("/bin/rm -rf " . VARPATH . $entry); } } + + static function _protect_directory($dir) { + $fp = @fopen("$dir/.htaccess", "w+"); + fwrite($fp, "DirectoryIndex .htaccess\nSetHandler Gallery_Security_Do_Not_Remove\n" . + "Options None\n<IfModule mod_rewrite.c>\nRewriteEngine off\n</IfModule>\n" . + "Order allow,deny\nDeny from all\n"); + fclose($fp); + } } diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php deleted file mode 100644 index a87ebb4e..00000000 --- a/modules/gallery/helpers/gallery_rest.php +++ /dev/null @@ -1,248 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class gallery_rest_Core { - static function get($request) { - $path = implode("/", $request->arguments); - - $item = gallery_rest::_get_item($path); - - $parent = $item->parent(); - $response_data = array("type" => $item->type, - "name" => $item->name, - "path" => $item->relative_url(), - "parent_path" => empty($parent) ? null : $parent->relative_url(), - "title" => $item->title, - "thumb_url" => $item->thumb_url(true), - "thumb_size" => array("height" => $item->thumb_height, - "width" => $item->thumb_width), - "resize_url" => $item->resize_url(true), - "resize_size" => array("height" => (int)$item->resize_height, - "width" => (int)$item->resize_width), - "url" => $item->file_url(true), - "size" => array("height" => $item->height, - "width" => $item->width), - "description" => $item->description, - "slug" => $item->slug); - - $children = self::_get_children($item, $request); - if (!empty($children) || $item->is_album()) { - $response_data["children"] = $children; - } - return rest::success(array("resource" => $response_data)); - } - - static function put($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } - $path = implode("/", $request->arguments); - $item = gallery_rest::_get_item($path, "edit"); - - // Validate the request data - $new_values = gallery_rest::_validate($request, $item->parent_id, $item->id); - $errors = $new_values->errors(); - if (empty($errors)) { - $item->title = $new_values->title; - $item->description = $new_values->description; - if ($item->id != 1) { - $item->rename($new_values->name); - } - $item->slug = $new_values->slug; - $item->save(); - - log::success("content", "Updated $item->type", - "<a href=\"{$item->type}s/$item->id\">view</a>"); - - return rest::success(); - } else { - return rest::validation_error($errors); - } - } - - static function post($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } - - $components = $request->arguments; - $name = urldecode(array_pop($components)); - - $parent = gallery_rest::_get_item(implode("/", $components), "edit"); - - // Validate the request data - $request->name = $name; - $new_values = gallery_rest::_validate($request, $parent->id); - $errors = $new_values->errors(); - if (!empty($errors)) { - return rest::validation_error($errors); - } - - if (empty($new_values["image"])) { - $new_item = album::create( - $parent, - $name, - empty($new_values["title"]) ? $name : $new_values["title"], - empty($new_values["description"]) ? null : $new_values["description"], - identity::active_user()->id, - empty($new_values["slug"]) ? $name : $new_values["slug"]); - $log_message = t("Added an album"); - } else { - $temp_filename = upload::save("image"); - $path_info = @pathinfo($temp_filename); - if (array_key_exists("extension", $path_info) && - in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) { - $new_item = - movie::create($parent, $temp_filename, $new_values["name"], $new_values["title"]); - $log_message = t("Added a movie"); - } else { - $new_item = - photo::create($parent, $temp_filename, $new_values["name"], $new_values["title"]); - $log_message = t("Added a photo"); - } - } - - log::success("content", $log_message, "<a href=\"{$new_item->type}s/$new_item->id\">view</a>"); - - return rest::success(array("path" => $new_item->relative_url())); - } - - static function delete($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } - $path = implode("/", $request->arguments); - - $item = gallery_rest::_get_item($path, "edit"); - - if ($item->id == 1) { - throw new Rest_Exception(400, "Bad request"); - } - - $parent = $item->parent(); - $item->delete(); - - if ($item->is_album()) { - $msg = t("Deleted album <b>%title</b>", array("title" => html::purify($item->title))); - } else { - $msg = t("Deleted photo <b>%title</b>", array("title" => html::purify($item->title))); - } - log::success("content", $msg); - - return rest::success(array("resource" => array("parent_path" => $parent->relative_url()))); - } - - private static function _get_item($path, $permission="view") { - $item = url::get_item_from_uri($path); - - if (!$item->loaded()) { - throw new Kohana_404_Exception(); - } - - if (!access::can($permission, $item)) { - throw new Kohana_404_Exception(); - } - - return $item; - } - - private static function _get_children($item, $request) { - $children = array(); - $limit = empty($request->limit) ? null : $request->limit; - $offset = empty($request->offset) ? null : $request->offset; - $where = empty($request->filter) ? array() : array("type" => $request->filter); - foreach ($item->viewable()->children($limit, $offset, $where) as $child) { - $children[] = array("type" => $child->type, - "has_children" => $child->children_count() > 0, - "path" => $child->relative_url(), - "thumb_url" => $child->thumb_url(true), - "thumb_dimensions" => array("width" => $child->thumb_width, - "height" => $child->thumb_height), - "has_thumb" => $child->has_thumb(), - "title" => $child->title); - } - - return $children; - } - - private static function _validate($request, $parent_id, $item_id=0) { - $item = ORM::factory("item", $item_id); - - // Normalize the inputs so all fields have a value - $new_values = Validation::factory(array()); - foreach ($item->form_rules as $field => $rule_set) { - if (isset($request->$field)) { - $new_values[$field] = $request->$field; - } else if (isset($item->$field)) { - $new_values[$field] = $item->$field; - } - foreach (explode("|", $rule_set) as $rule) { - $new_values->add_rules($field, $rule); - } - } - $name = $new_values["name"]; - $new_values["title"] = empty($new_values["title"]) ? $name : $new_values["title"]; - $new_values["description"] = - empty($new_values["description"]) ? null : $new_values["description"]; - $new_values["slug"] = empty($new_values["slug"]) ? $name : $new_values["slug"]; - - if (!empty($request->image)) { - $new_values["image"] = $request->image; - $new_values->add_rules( - "image", "upload::valid", "upload::required", "upload::type[gif,jpg,jpeg,png,flv,mp4]"); - } - - if ($new_values->validate() && $item_id != 1) { - $errors = gallery_rest::_check_for_conflicts($parent_id, $item_id, - $new_values["name"], $new_values["slug"]); - if (!empty($errors)) { - !empty($errors["name_conflict"]) OR $new_values->add_error("name", "Duplicate name"); - !empty($errors["slug_conflict"]) OR - $new_values->add_error("slug", "Duplicate Internet address"); - } - } - - return $new_values; - } - - private static function _check_for_conflicts($parent_id, $item_id, $new_name, $new_slug) { - $errors = array(); - - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $parent_id) - ->where("id", "<>", $item_id) - ->and_open() - ->where("name", "=", $new_name) - ->or_where("slug", "=", $new_slug) - ->close() - ->execute() - ->current()) { - if ($row->name == $new_name) { - $errors["name_conflict"] = 1; - } - if ($row->slug == $new_slug) { - $errors["slug_conflict"] = 1; - } - } - - return $errors; - } -} diff --git a/modules/gallery/helpers/gallery_rss.php b/modules/gallery/helpers/gallery_rss.php index d422636f..c1790d28 100644 --- a/modules/gallery/helpers/gallery_rss.php +++ b/modules/gallery/helpers/gallery_rss.php @@ -25,6 +25,7 @@ class gallery_rss_Core { } static function feed($feed_id, $offset, $limit, $id) { + $feed = new stdClass(); switch ($feed_id) { case "latest": $feed->children = ORM::factory("item") diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 5402b5d1..3e6278e5 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -40,7 +40,7 @@ class gallery_task_Core { $tasks[] = Task_Definition::factory() ->callback("gallery_task::file_cleanup") ->name(t("Remove old files")) - ->description(t("Remove files from the logs and tmp directory")) + ->description(t("Remove expired files from the logs and tmp directory")) ->severity(log::SUCCESS); return $tasks; } @@ -81,7 +81,7 @@ class gallery_task_Core { } catch (Exception $e) { $errors[] = t("Unable to rebuild images for '%title'", array("title" => html::purify($item->title))); - $errors[] = $e->__toString(); + $errors[] = (string)$e; $ignored[$item->id] = 1; } } @@ -111,10 +111,11 @@ class gallery_task_Core { site_status::clear("graphics_dirty"); } } catch (Exception $e) { + Kohana_Log::add("error",(string)$e); $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $errors[] = $e->__toString(); + $errors[] = (string)$e; } if ($errors) { $task->log($errors); @@ -214,10 +215,11 @@ class gallery_task_Core { Cache::instance()->delete("update_l10n_cache:{$task->id}"); } } catch (Exception $e) { + Kohana_Log::add("error",(string)$e); $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $errors[] = $e->__toString(); + $errors[] = (string)$e; } if ($errors) { $task->log($errors); @@ -233,13 +235,13 @@ class gallery_task_Core { try { $start = microtime(true); $data = Cache::instance()->get("file_cleanup_cache:{$task->id}"); - if ($data) { - $files = unserialize($data); - } + $files = $data ? unserialize($data) : array(); $i = 0; + $current = 0; + $total = 0; switch ($task->get("mode", "init")) { - case "init": // 0% + case "init": $threshold = time() - 1209600; // older than 2 weeks foreach(array("logs", "tmp") as $dir) { $dir = VARPATH . $dir; @@ -262,6 +264,7 @@ class gallery_task_Core { if (count($files) == 0) { break; } + case "delete_files": $current = $task->get("current"); $total = $task->get("total"); @@ -273,18 +276,21 @@ class gallery_task_Core { $task->set("current", $current); } - $task->status = t("Removed: %count files. Total: %total_count.", - array("count" => $current, "total_count" => $total)); + $task->status = t2("Removed: 1 file. Total: %total_count.", + "Removed: %count files. Total: %total_count.", + $current, array("total_count" => $total)); if ($total == $current) { $task->done = true; $task->state = "success"; + $task->percent_complete = 100; } } catch (Exception $e) { + Kohana_Log::add("error",(string)$e); $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $errors[] = $e->__toString(); + $errors[] = (string)$e; } if ($errors) { $task->log($errors); diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 5a290905..c85c7750 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -262,6 +262,9 @@ class graphics_Core { */ static function detect_toolkits() { $toolkits = new stdClass(); + $toolkits->gd = new stdClass(); + $toolkits->imagemagick = new stdClass(); + $toolkits->graphicsmagick = new stdClass(); // GD is special, it doesn't use exec() $gd = function_exists("gd_info") ? gd_info() : array(); diff --git a/modules/gallery/helpers/identity.php b/modules/gallery/helpers/identity.php index eae0ea3e..6df860d6 100644 --- a/modules/gallery/helpers/identity.php +++ b/modules/gallery/helpers/identity.php @@ -68,9 +68,7 @@ class identity_Core { // upconvert into a user. // @todo set the user name into the session instead of 2 and then use it to get the user object if ($user === 2) { - $user = IdentityProvider::instance()->admin_user(); - self::set_active_user($user); - $session->set("user", $user); + auth::login(IdentityProvider::instance()->admin_user()); } if (!$session->get("group_ids")) { @@ -82,7 +80,7 @@ class identity_Core { } } catch (Exception $e) { // Log it, so we at least have so notification that we swallowed the exception. - Kohana_Log::add("error", "Load_user Exception: " . + Kohana_Log::add("error", "load_user Exception: " . $e->getMessage() . "\n" . $e->getTraceAsString()); try { Session::instance()->destroy(); @@ -155,8 +153,8 @@ class identity_Core { /** * @see IdentityProvider_Driver::create_user. */ - static function create_user($name, $full_name, $password) { - return IdentityProvider::instance()->create_user($name, $full_name, $password); + static function create_user($name, $full_name, $password, $email) { + return IdentityProvider::instance()->create_user($name, $full_name, $password, $email); } /** diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index f6181f8a..41d49ce9 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -39,7 +39,8 @@ class item_Core { } } - $source->move_to($target); + $source->parent_id = $target->id; + $source->save(); // If the target has no cover item, make this it. if ($target->album_cover_item_id == null) { @@ -59,7 +60,8 @@ class item_Core { $parent->save(); graphics::generate($parent); $grand_parent = $parent->parent(); - if ($grand_parent && $grand_parent->album_cover_item_id == null) { + if ($grand_parent && access::can("edit", $grand_parent) && + $grand_parent->album_cover_item_id == null) { item::make_album_cover($parent); } } @@ -78,24 +80,6 @@ class item_Core { graphics::generate($album); } - static function validate_no_slashes($input) { - if (strpos($input->value, "/") !== false) { - $input->add_error("no_slashes", 1); - } - } - - static function validate_no_trailing_period($input) { - if (rtrim($input->value, ".") !== $input->value) { - $input->add_error("no_trailing_period", 1); - } - } - - static function validate_url_safe($input) { - if (preg_match("/[^A-Za-z0-9-_]/", $input->value)) { - $input->add_error("not_url_safe", 1); - } - } - /** * Sanitize a filename into something presentable as an item title * @param string $filename @@ -173,4 +157,20 @@ class item_Core { static function root() { return model_cache::get("item", 1); } + + /** + * Return a query to get a random Item_Model, with optional filters + * + * @param array (optional) where tuple + */ + static function random_query($where=null) { + // Pick a random number and find the item that's got nearest smaller number. + // This approach works best when the random numbers in the system are roughly evenly + // distributed so this is going to be more efficient with larger data sets. + return ORM::factory("item") + ->viewable() + ->where("rand_key", "<", ((float)mt_rand()) / (float)mt_getrandmax()) + ->merge_where($where) + ->order_by("rand_key", "DESC"); + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/item_rest.php b/modules/gallery/helpers/item_rest.php new file mode 100644 index 00000000..c0fc422a --- /dev/null +++ b/modules/gallery/helpers/item_rest.php @@ -0,0 +1,176 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class item_rest_Core { + /** + * For items that are collections, you can specify the following additional query parameters to + * query the collection. You can specify them in any combination. + * + * scope=direct + * only return items that are immediately under this one + * scope=all + * return items anywhere under this one + * + * name=<substring> + * only return items where the name contains this substring + * + * random=true + * return a single random item + * + * type=<comma separate list of photo, movie or album> + * limit the type to types in this list. eg, "type=photo,movie" + */ + static function get($request) { + $item = rest::resolve($request->url); + access::required("view", $item); + + $p = $request->params; + if (isset($p->random)) { + $orm = item::random_query()->offset(0)->limit(1); + } else { + $orm = ORM::factory("item")->viewable(); + } + + if (empty($p->scope)) { + $p->scope = "direct"; + } + + if (!in_array($p->scope, array("direct", "all"))) { + throw new Rest_Exception("Bad Request", 400); + } + + if ($p->scope == "direct") { + $orm->where("parent_id", "=", $item->id); + } else { + $orm->where("left_ptr", ">", $item->left_ptr); + $orm->where("right_ptr", "<", $item->right_ptr); + } + + if (isset($p->name)) { + $orm->where("name", "LIKE", "%{$p->name}%"); + } + + if (isset($p->type)) { + $orm->where("type", "IN", explode(",", $p->type)); + } + + $members = array(); + foreach ($orm->find_all() as $child) { + $members[] = rest::url("item", $child); + } + + return array( + "url" => $request->url, + "entity" => $item->as_restful_array(), + "members" => $members, + "relationships" => rest::relationships("item", $item)); + } + + static function put($request) { + $item = rest::resolve($request->url); + access::required("edit", $item); + + $params = $request->params; + + // Only change fields from a whitelist. + foreach (array("album_cover", "captured", "description", + "height", "mime_type", "name", "parent", "rand_key", "resize_dirty", + "resize_height", "resize_width", "slug", "sort_column", "sort_order", + "thumb_dirty", "thumb_height", "thumb_width", "title", "view_count", + "weight", "width") as $key) { + switch ($key) { + case "album_cover": + if (property_exists($request->params, "album_cover")) { + $album_cover_item = rest::resolve($request->params->album_cover); + access::required("view", $album_cover_item); + $item->album_cover_item_id = $album_cover_item->id; + } + break; + + case "parent": + if (property_exists($request->params, "parent")) { + $parent = rest::resolve($request->params->parent); + access::required("edit", $parent); + $item->parent_id = $parent->id; + } + break; + default: + if (property_exists($request->params, $key)) { + $item->$key = $request->params->$key; + } + } + } + $item->save(); + } + + static function post($request) { + $parent = rest::resolve($request->url); + access::required("edit", $parent); + + $params = $request->params; + $item = ORM::factory("item"); + switch ($params->type) { + case "album": + $item->type = "album"; + $item->parent_id = $parent->id; + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $name; + $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; + $item->save(); + break; + + case "photo": + case "movie": + $item->type = $params->type; + $item->parent_id = $parent->id; + $item->set_data_file($request->file); + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $params->name; + $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; + $item->save(); + break; + + default: + throw new Rest_Exception("Invalid type: $params->type", 400); + } + + return array("url" => rest::url("item", $item)); + } + + static function delete($request) { + $item = rest::resolve($request->url); + access::required("edit", $item); + + $item->delete(); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/item/{$item->id}"); + } +} diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index 086245e8..c27e4e5b 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -77,6 +77,7 @@ class l10n_client_Core { * translations for. */ static function fetch_updates(&$num_fetched) { + $request = new stdClass(); $request->locales = array(); $request->messages = new stdClass(); diff --git a/modules/gallery/helpers/locales.php b/modules/gallery/helpers/locales.php index 5c8c227a..dc32b12f 100644 --- a/modules/gallery/helpers/locales.php +++ b/modules/gallery/helpers/locales.php @@ -238,7 +238,9 @@ class locales_Core { } static function cookie_locale() { - $cookie_data = Input::instance()->cookie("g_locale"); + // Can't use Input framework for client side cookies since + // they're not signed. + $cookie_data = isset($_COOKIE["g_locale"]) ? $_COOKIE["g_locale"] : null; $locale = null; if ($cookie_data) { if (preg_match("/^([a-z]{2,3}(?:_[A-Z]{2})?)$/", trim($cookie_data), $matches)) { diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php index 95e426c4..9523d1d2 100644 --- a/modules/gallery/helpers/module.php +++ b/modules/gallery/helpers/module.php @@ -430,7 +430,8 @@ class module_Core { // This could happen if there's a race condition continue; } - self::$var_cache->{$row->module_name}->{$row->name} = $row->value; + // Mute the "Creating default object from empty value" warning below + @self::$var_cache->{$row->module_name}->{$row->name} = $row->value; } $cache = ORM::factory("var"); $cache->module_name = "gallery"; diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 01859924..7033b7da 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -24,138 +24,36 @@ * Note: by design, this class does not do any permission checking. */ class movie_Core { - /** - * Create a new movie. - * @param integer $parent_id id of parent album - * @param string $filename path to the photo file on disk - * @param string $name the filename to use for this photo in the album - * @param integer $title the title of the new photo - * @param string $description (optional) the longer description of this photo - * @param string $slug (optional) the url component for this photo - * @return Item_Model - */ - static function create($parent, $filename, $name, $title, - $description=null, $owner_id=null, $slug=null) { - if (!$parent->loaded() || !$parent->is_album()) { - throw new Exception("@todo INVALID_PARENT"); - } - - if (!is_file($filename)) { - throw new Exception("@todo MISSING_MOVIE_FILE"); - } - - if (strpos($name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - // We don't allow trailing periods as a security measure - // ref: http://dev.kohanaphp.com/issues/684 - if (rtrim($name, ".") != $name) { - throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); - } - - try { - $movie_info = movie::getmoviesize($filename); - } catch (Exception $e) { - // Assuming this is MISSING_FFMPEG for now - $movie_info = getimagesize(MODPATH . "gallery/images/missing_movie.png"); - } - - // Force an extension onto the name - $pi = pathinfo($filename); - if (empty($pi["extension"])) { - $pi["extension"] = image_type_to_extension($movie_info[2], false); - $name .= "." . $pi["extension"]; - } - - if (empty($slug)) { - $slug = item::convert_filename_to_slug($name); - } - - $movie = ORM::factory("item"); - $movie->type = "movie"; - $movie->title = $title; - $movie->description = $description; - $movie->name = $name; - $movie->owner_id = $owner_id ? $owner_id : identity::active_user()->id; - $movie->width = $movie_info[0]; - $movie->height = $movie_info[1]; - $movie->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; - $movie->thumb_dirty = 1; - $movie->resize_dirty = 1; - $movie->sort_column = "weight"; - $movie->slug = $slug; - $movie->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - - // Randomize the name if there's a conflict - // @todo Improve this. Random numbers are not user friendly - while (ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->and_open() - ->where("name", "=", $movie->name) - ->or_where("slug", "=", $movie->slug) - ->close() - ->find()->id) { - $rand = rand(); - $movie->name = "{$name}.$rand.{$pi['extension']}"; - $movie->slug = "{$slug}-$rand"; - } - - // This saves the photo - $movie->add_to_parent($parent); - - // If the thumb or resize already exists then rename it - if (file_exists($movie->resize_path()) || - file_exists($movie->thumb_path())) { - $movie->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; - $movie->save(); - } - - copy($filename, $movie->file_path()); - - // @todo: publish this from inside Item_Model::save() when we refactor to the point where - // there's only one save() happening here. - module::event("item_created", $movie); - - // Build our thumbnail - graphics::generate($movie); - - // If the parent has no cover item, make this it. - if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { - item::make_album_cover($movie); - } - - return $movie; - } - static function get_edit_form($movie) { $form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form")); $form->hidden("from_id"); $group = $form->group("edit_item")->label(t("Edit Movie")); - $group->input("title")->label(t("Title"))->value($movie->title); + $group->input("title")->label(t("Title"))->value($movie->title) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); $group->textarea("description")->label(t("Description"))->value($movie->description); - $group->input("filename")->label(t("Filename"))->value($movie->name) - ->rules("required") + $group->input("name")->label(t("Filename"))->value($movie->name) ->error_messages( - "name_conflict", t("There is already a movie, photo or album with this name")) - ->callback("item::validate_no_slashes") + "conflict", t("There is already a movie, photo or album with this name")) ->error_messages("no_slashes", t("The movie name can't contain a \"/\"")) - ->callback("item::validate_no_trailing_period") ->error_messages("no_trailing_period", t("The movie name can't end in \".\"")) - ->error_messages("illegal_extension", t("You cannot change the filename extension")); + ->error_messages("illegal_data_file_extension", t("You cannot change the movie file extension")) + ->error_messages("required", t("You must provide a movie file name")) + ->error_messages("length", t("Your movie file name is too long")); $group->input("slug")->label(t("Internet Address"))->value($movie->slug) - ->callback("item::validate_url_safe") ->error_messages( - "slug_conflict", t("There is already a movie, photo or album with this internet address")) + "conflict", t("There is already a movie, photo or album with this internet address")) ->error_messages( "not_url_safe", - t("The internet address should contain only letters, numbers, hyphens and underscores")); + t("The internet address should contain only letters, numbers, hyphens and underscores")) + ->error_messages("required", t("You must provide an internet address")) + ->error_messages("length", t("Your internet address is too long")); module::event("item_edit_form", $movie, $form); $group = $form->group("buttons")->label(""); $group->submit("")->value(t("Modify")); - $form->add_rules_from(ORM::factory("item")); + return $form; } diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php index 4e20e610..bbdf2e3b 100644 --- a/modules/gallery/helpers/photo.php +++ b/modules/gallery/helpers/photo.php @@ -24,147 +24,34 @@ * Note: by design, this class does not do any permission checking. */ class photo_Core { - /** - * Create a new photo. - * @param integer $parent parent album - * @param string $filename path to the photo file on disk - * @param string $name the filename to use for this photo in the album - * @param integer $title the title of the new photo - * @param string $description (optional) the longer description of this photo - * @param string $slug (optional) the url component for this photo - * @return Item_Model - */ - static function create($parent, $filename, $name, $title, - $description=null, $owner_id=null, $slug=null) { - if (!$parent->loaded() || !$parent->is_album()) { - throw new Exception("@todo INVALID_PARENT"); - } - - if (!is_file($filename)) { - throw new Exception("@todo MISSING_IMAGE_FILE"); - } - - if (strpos($name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - // We don't allow trailing periods as a security measure - // ref: http://dev.kohanaphp.com/issues/684 - if (rtrim($name, ".") != $name) { - throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); - } - - if (filesize($filename) == 0) { - throw new Exception("@todo EMPTY_INPUT_FILE"); - } - - $image_info = getimagesize($filename); - - // Force an extension onto the name - $pi = pathinfo($filename); - if (empty($pi["extension"])) { - $pi["extension"] = image_type_to_extension($image_info[2], false); - $name .= "." . $pi["extension"]; - } - - if (empty($slug)) { - $slug = item::convert_filename_to_slug($name); - } - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->title = $title; - $photo->description = $description; - $photo->name = $name; - $photo->owner_id = $owner_id ? $owner_id : identity::active_user()->id; - $photo->width = $image_info[0]; - $photo->height = $image_info[1]; - $photo->mime_type = empty($image_info['mime']) ? "application/unknown" : $image_info['mime']; - $photo->thumb_dirty = 1; - $photo->resize_dirty = 1; - $photo->sort_column = "weight"; - $photo->slug = $slug; - $photo->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - - // Randomize the name or slug if there's a conflict - // @todo Improve this. Random numbers are not user friendly - while (ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->and_open() - ->where("name", "=", $photo->name) - ->or_where("slug", "=", $photo->slug) - ->close() - ->find()->id) { - $rand = rand(); - $photo->name = "{$name}.$rand.{$pi['extension']}"; - $photo->slug = "{$slug}-$rand"; - } - - // This saves the photo - $photo->add_to_parent($parent); - - /* - * If the thumb or resize already exists then rename it. We need to do this after the save - * because the resize_path and thumb_path both call relative_path which caches the - * path. Before add_to_parent the relative path will be incorrect. - */ - if (file_exists($photo->resize_path()) || - file_exists($photo->thumb_path())) { - $photo->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; - $photo->save(); - } - - copy($filename, $photo->file_path()); - - // @todo: publish this from inside Item_Model::save() when we refactor to the point where - // there's only one save() happening here. - module::event("item_created", $photo); - - // Build our thumbnail/resizes. If we fail to build thumbnail/resize we assume that the image - // is bad in some way and discard it. - try { - graphics::generate($photo); - } catch (Exception $e) { - $photo->delete(); - throw $e; - } - - // If the parent has no cover item, make this it. - if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { - item::make_album_cover($photo); - } - - return $photo; - } - static function get_edit_form($photo) { $form = new Forge("photos/update/$photo->id", "", "post", array("id" => "g-edit-photo-form")); $form->hidden("from_id"); $group = $form->group("edit_item")->label(t("Edit Photo")); - $group->input("title")->label(t("Title"))->value($photo->title); + $group->input("title")->label(t("Title"))->value($photo->title) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); $group->textarea("description")->label(t("Description"))->value($photo->description); - $group->input("filename")->label(t("Filename"))->value($photo->name) - ->rules("required") - ->error_messages( - "name_conflict", t("There is already a movie, photo or album with this name")) - ->callback("item::validate_no_slashes") + $group->input("name")->label(t("Filename"))->value($photo->name) + ->error_messages("conflict", t("There is already a movie, photo or album with this name")) ->error_messages("no_slashes", t("The photo name can't contain a \"/\"")) - ->callback("item::validate_no_trailing_period") ->error_messages("no_trailing_period", t("The photo name can't end in \".\"")) - ->error_messages("illegal_extension", t("You cannot change the filename extension")); + ->error_messages("illegal_data_file_extension", t("You cannot change the photo file extension")) + ->error_messages("required", t("You must provide a photo file name")) + ->error_messages("length", t("Your photo file name is too long")); $group->input("slug")->label(t("Internet Address"))->value($photo->slug) - ->callback("item::validate_url_safe") ->error_messages( - "slug_conflict", t("There is already a movie, photo or album with this internet address")) + "conflict", t("There is already a movie, photo or album with this internet address")) ->error_messages( "not_url_safe", - t("The internet address should contain only letters, numbers, hyphens and underscores")); + t("The internet address should contain only letters, numbers, hyphens and underscores")) + ->error_messages("required", t("You must provide an internet address")) + ->error_messages("length", t("Your internet address is too long")); module::event("item_edit_form", $photo, $form); $group = $form->group("buttons")->label(""); $group->submit("")->value(t("Modify")); - $form->add_rules_from(ORM::factory("item")); return $form; } diff --git a/modules/gallery/helpers/task.php b/modules/gallery/helpers/task.php index 4aa95f33..645850d1 100644 --- a/modules/gallery/helpers/task.php +++ b/modules/gallery/helpers/task.php @@ -84,8 +84,15 @@ class task_Core { } $task->save(); } catch (Exception $e) { - Kohana_Log::add("error", $e->__toString()); - $task->log($e->__toString()); + Kohana_Log::add("error", (string)$e); + + // Ugh. I hate to use instanceof, But this beats catching the exception separately since + // we mostly want to treat it the same way as all other exceptions + if ($e instanceof ORM_Validation_Exception) { + Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1)); + } + + $task->log((string)$e); $task->state = "error"; $task->done = true; $task->status = substr($e->getMessage(), 0, 255); diff --git a/modules/gallery/libraries/Form_Script.php b/modules/gallery/libraries/Form_Script.php index e841408d..1f965767 100644 --- a/modules/gallery/libraries/Form_Script.php +++ b/modules/gallery/libraries/Form_Script.php @@ -50,7 +50,7 @@ class Form_Script_Core extends Forge { return $this; } - public function render() { + public function render($template="forge_template", $custom=false) { $script = array(); if (!empty($this->data["url"])) { $script[] = html::script($this->data["url"]); @@ -63,4 +63,4 @@ class Form_Script_Core extends Forge { return implode("\n", $script); } -} // End Form Script
\ No newline at end of file +}
\ No newline at end of file diff --git a/modules/gallery/libraries/IdentityProvider.php b/modules/gallery/libraries/IdentityProvider.php index 2ed85bd1..3f1666eb 100644 --- a/modules/gallery/libraries/IdentityProvider.php +++ b/modules/gallery/libraries/IdentityProvider.php @@ -83,7 +83,7 @@ class IdentityProvider_Core { module::event("identity_provider_changed", $current_provider, $new_provider); - auth::login($provider->admin_user()); + identity::set_active_user($provider->admin_user()); Session::instance()->regenerate(); } catch (Exception $e) { static $restore_already_running; @@ -174,8 +174,8 @@ class IdentityProvider_Core { /** * @see IdentityProvider_Driver::create_user. */ - public function create_user($name, $full_name, $password) { - return $this->driver->create_user($name, $full_name, $password); + public function create_user($name, $full_name, $password, $email) { + return $this->driver->create_user($name, $full_name, $password, $email); } /** diff --git a/modules/gallery/libraries/InPlaceEdit.php b/modules/gallery/libraries/InPlaceEdit.php index 67ab3805..04a2e9a5 100644 --- a/modules/gallery/libraries/InPlaceEdit.php +++ b/modules/gallery/libraries/InPlaceEdit.php @@ -70,7 +70,6 @@ class InPlaceEdit_Core { public function render() { $v = new View("in_place_edit.html"); - $v->hidden = array("csrf" => access::csrf_token()); $v->action = url::site($this->action); $v->form = $this->form; $v->errors = $this->errors; diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php index 61f23fb0..e2ef68cd 100644 --- a/modules/gallery/libraries/MY_Database.php +++ b/modules/gallery/libraries/MY_Database.php @@ -38,7 +38,7 @@ abstract class Database extends Database_Core { * Parse the query string and convert any strings of the form `\([a-zA-Z0-9_]*?)\] * table prefix . $1 */ - public function query($sql = '') { + public function query($sql) { if (!empty($sql)) { $sql = $this->add_table_prefixes($sql); } diff --git a/modules/gallery/libraries/MY_Forge.php b/modules/gallery/libraries/MY_Forge.php index 9564f941..ee2a0bef 100644 --- a/modules/gallery/libraries/MY_Forge.php +++ b/modules/gallery/libraries/MY_Forge.php @@ -35,20 +35,6 @@ class Forge extends Forge_Core { } /** - * Associate validation rules defined in the model with this form. - */ - public function add_rules_from($model) { - foreach ($this->inputs as $name => $input) { - if (isset($input->inputs)) { - $input->add_rules_from($model); - } - if (isset($model->form_rules[$name])) { - $input->rules($model->form_rules[$name]); - } - } - } - - /** * Validate our CSRF value as a mandatory part of all form validation. */ public function validate() { diff --git a/modules/gallery/libraries/MY_Kohana_Exception.php b/modules/gallery/libraries/MY_Kohana_Exception.php index dd5998a1..1c40091a 100644 --- a/modules/gallery/libraries/MY_Kohana_Exception.php +++ b/modules/gallery/libraries/MY_Kohana_Exception.php @@ -28,4 +28,11 @@ class Kohana_Exception extends Kohana_Exception_Core { $e->getFile(), $e->getLine(), $e->getTraceAsString()); } + + public static function handle(Exception $e) { + if ($e instanceof ORM_Validation_Exception) { + Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1)); + } + return parent::handle($e); + } }
\ No newline at end of file diff --git a/modules/gallery/libraries/MY_ORM.php b/modules/gallery/libraries/MY_ORM.php index 198a430b..a158d853 100644 --- a/modules/gallery/libraries/MY_ORM.php +++ b/modules/gallery/libraries/MY_ORM.php @@ -18,41 +18,9 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class ORM extends ORM_Core { - // Track the original value of this ORM so that we can look it up in ORM::original() - protected $original = null; - public function save() { model_cache::clear(); - $result = parent::save(); - $this->original = clone $this; - return $result; - } - - public function __set($column, $value) { - if (!isset($this->original)) { - $this->original = clone $this; - } - - if ($value instanceof SafeString) { - $value = $value->unescaped(); - } - - return parent::__set($column, $value); - } - - public function __unset($column) { - if (!isset($this->original)) { - $this->original = clone $this; - } - - return parent::__unset($column); - } - - public function original() { - if (!isset($this->original)) { - $this->original = clone $this; - } - return $this->original; + return parent::save(); } } diff --git a/modules/gallery/libraries/MY_View.php b/modules/gallery/libraries/MY_View.php index cec59ec1..83e0d0be 100644 --- a/modules/gallery/libraries/MY_View.php +++ b/modules/gallery/libraries/MY_View.php @@ -27,7 +27,7 @@ class View extends View_Core { View::$global_data[$key] = $value; } - public function is_set($key) { + public function is_set($key=null) { return parent::is_set($key) ? true : array_key_exists($key, View::$global_data); } diff --git a/modules/gallery/libraries/ORM_MPTT.php b/modules/gallery/libraries/ORM_MPTT.php index 0ea519c9..3668d42d 100644 --- a/modules/gallery/libraries/ORM_MPTT.php +++ b/modules/gallery/libraries/ORM_MPTT.php @@ -40,50 +40,52 @@ class ORM_MPTT_Core extends ORM { } /** - * Add this node as a child of the parent provided. + * Overload ORM::save() to update the MPTT tree when we add new items to the hierarchy. * * @chainable - * @param integer $parent_id the id of the parent node - * @return ORM + * @return ORM */ - function add_to_parent($parent) { - $this->lock(); - $parent->reload(); // Assume that the prior lock holder may have changed the parent - - try { - // Make a hole in the parent for this new item - $this->db_builder - ->update($this->table_name) - ->set("left_ptr", new Database_Expression("`left_ptr` + 2")) - ->where("left_ptr", ">=", $parent->right_ptr) - ->execute(); - $this->db_builder - ->update($this->table_name) - ->set("right_ptr", new Database_Expression("`right_ptr` + 2")) - ->where("right_ptr", ">=", $parent->right_ptr) - ->execute(); - $parent->right_ptr += 2; - - // Insert this item into the hole - $this->left_ptr = $parent->right_ptr - 2; - $this->right_ptr = $parent->right_ptr - 1; - $this->parent_id = $parent->id; - $this->level = $parent->level + 1; - $this->save(); - $parent->reload(); - } catch (Exception $e) { + function save() { + if (!$this->loaded()) { + $this->lock(); + $parent = ORM::factory("item", $this->parent_id); + + try { + // Make a hole in the parent for this new item + db::build() + ->update($this->table_name) + ->set("left_ptr", new Database_Expression("`left_ptr` + 2")) + ->where("left_ptr", ">=", $parent->right_ptr) + ->execute(); + db::build() + ->update($this->table_name) + ->set("right_ptr", new Database_Expression("`right_ptr` + 2")) + ->where("right_ptr", ">=", $parent->right_ptr) + ->execute(); + $parent->right_ptr += 2; + + // Insert this item into the hole + $this->left_ptr = $parent->right_ptr - 2; + $this->right_ptr = $parent->right_ptr - 1; + $this->parent_id = $parent->id; + $this->level = $parent->level + 1; + } catch (Exception $e) { + $this->unlock(); + throw $e; + } + parent::save(); $this->unlock(); - throw $e; + } else { + parent::save(); } - $this->unlock(); return $this; } /** * Delete this node and all of its children. */ - public function delete() { + public function delete($ignored_id=null) { $children = $this->children(); if ($children) { foreach ($this->children() as $item) { @@ -100,12 +102,12 @@ class ORM_MPTT_Core extends ORM { $this->lock(); $this->reload(); // Assume that the prior lock holder may have changed this entry try { - $this->db_builder + db::build() ->update($this->table_name) ->set("left_ptr", new Database_Expression("`left_ptr` - 2")) ->where("left_ptr", ">", $this->right_ptr) ->execute(); - $this->db_builder + db::build() ->update($this->table_name) ->set("right_ptr", new Database_Expression("`right_ptr` - 2")) ->where("right_ptr", ">", $this->right_ptr) @@ -165,11 +167,8 @@ class ORM_MPTT_Core extends ORM { * @return array ORM */ function children($limit=null, $offset=null, $where=null, $order_by=array("id" => "ASC")) { - if ($where) { - $this->merge_where($where); - } - return $this + ->merge_where($where) ->where("parent_id", "=", $this->id) ->order_by($order_by) ->find_all($limit, $offset); @@ -183,11 +182,8 @@ class ORM_MPTT_Core extends ORM { * @return array ORM */ function children_count($where=null) { - if ($where) { - $this->merge_where($where); - } - return $this + ->merge_where($where) ->where("parent_id", "=", $this->id) ->count_all(); } @@ -202,11 +198,8 @@ class ORM_MPTT_Core extends ORM { * @return object ORM_Iterator */ function descendants($limit=null, $offset=null, $where=null, $order_by=array("id" => "ASC")) { - if ($where) { - $this->merge_where($where); - } - return $this + ->merge_where($where) ->where("left_ptr", ">", $this->left_ptr) ->where("right_ptr", "<=", $this->right_ptr) ->order_by($order_by) @@ -220,11 +213,8 @@ class ORM_MPTT_Core extends ORM { * @return integer child count */ function descendants_count($where=null) { - if ($where) { - $this->merge_where($where); - } - return $this + ->merge_where($where) ->where("left_ptr", ">", $this->left_ptr) ->where("right_ptr", "<=", $this->right_ptr) ->count_all(); @@ -237,11 +227,15 @@ class ORM_MPTT_Core extends ORM { * @param Item_Model $target Target node * @return ORM_MTPP */ - function move_to($target) { + protected function move_to($target) { if ($this->contains($target)) { throw new Exception("@todo INVALID_TARGET can't move item inside itself"); } + $this->lock(); + $this->reload(); // Assume that the prior lock holder may have changed this entry + $target->reload(); + $number_to_move = (int)(($this->right_ptr - $this->left_ptr) / 2 + 1); $size_of_hole = $number_to_move * 2; $original_left_ptr = $this->left_ptr; @@ -249,13 +243,10 @@ class ORM_MPTT_Core extends ORM { $target_right_ptr = $target->right_ptr; $level_delta = ($target->level + 1) - $this->level; - $this->lock(); - $this->reload(); // Assume that the prior lock holder may have changed this entry - $target->reload(); try { if ($level_delta) { // Update the levels for the to-be-moved items - $this->db_builder + db::build() ->update($this->table_name) ->set("level", new Database_Expression("`level` + $level_delta")) ->where("left_ptr", ">=", $original_left_ptr) @@ -264,25 +255,26 @@ class ORM_MPTT_Core extends ORM { } // Make a hole in the target for the move - $target->db_builder + db::build() ->update($this->table_name) ->set("left_ptr", new Database_Expression("`left_ptr` + $size_of_hole")) ->where("left_ptr", ">=", $target_right_ptr) ->execute(); - $target->db_builder + db::build() ->update($this->table_name) ->set("right_ptr", new Database_Expression("`right_ptr` + $size_of_hole")) ->where("right_ptr", ">=", $target_right_ptr) ->execute(); // Change the parent. - $this->db_builder + db::build() ->update($this->table_name) ->set("parent_id", $target->id) ->where("id", "=", $this->id) ->execute(); - // If the source is to the right of the target then we just adjusted its left_ptr and right_ptr above. + // If the source is to the right of the target then we just adjusted its left_ptr and + // right_ptr above. $left_ptr = $original_left_ptr; $right_ptr = $original_right_ptr; if ($original_left_ptr > $target_right_ptr) { @@ -291,7 +283,7 @@ class ORM_MPTT_Core extends ORM { } $new_offset = $target->right_ptr - $left_ptr; - $this->db_builder + db::build() ->update($this->table_name) ->set("left_ptr", new Database_Expression("`left_ptr` + $new_offset")) ->set("right_ptr", new Database_Expression("`right_ptr` + $new_offset")) @@ -300,12 +292,12 @@ class ORM_MPTT_Core extends ORM { ->execute(); // Close the hole in the source's parent after the move - $this->db_builder + db::build() ->update($this->table_name) ->set("left_ptr", new Database_Expression("`left_ptr` - $size_of_hole")) ->where("left_ptr", ">", $right_ptr) ->execute(); - $this->db_builder + db::build() ->update($this->table_name) ->set("right_ptr", new Database_Expression("`right_ptr` - $size_of_hole")) ->where("right_ptr", ">", $right_ptr) diff --git a/modules/gallery/libraries/drivers/IdentityProvider.php b/modules/gallery/libraries/drivers/IdentityProvider.php index a808c7e8..b7b1fbe8 100644 --- a/modules/gallery/libraries/drivers/IdentityProvider.php +++ b/modules/gallery/libraries/drivers/IdentityProvider.php @@ -38,9 +38,10 @@ interface IdentityProvider_Driver { * @param string $name * @param string $full_name * @param string $password + * @param string $email * @return User_Definition the user object */ - public function create_user($name, $full_name, $password); + public function create_user($name, $full_name, $password, $email); /** * Is the password provided correct? diff --git a/modules/gallery/models/failed_login.php b/modules/gallery/models/failed_login.php new file mode 100644 index 00000000..0b84c295 --- /dev/null +++ b/modules/gallery/models/failed_login.php @@ -0,0 +1,20 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Failed_Login_Model extends ORM {} diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 6851e1a3..dbd56fa2 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -18,15 +18,24 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Item_Model extends ORM_MPTT { - protected $children = 'items'; + protected $children = "items"; protected $sorting = array(); + protected $data_file = null; - var $form_rules = array( - "name" => "required|length[0,255]", - "title" => "required|length[0,255]", - "description" => "length[0,65535]", - "slug" => "required|length[0,255]" - ); + public function __construct($id=null) { + parent::__construct($id); + + if (!$this->loaded()) { + // Set reasonable defaults + $this->created = time(); + $this->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + $this->thumb_dirty = 1; + $this->resize_dirty = 1; + $this->sort_column = "created"; + $this->sort_order = "ASC"; + $this->owner_id = identity::active_user()->id; + } + } /** * Add a set of restrictions to any following queries to restrict access only to items @@ -61,7 +70,13 @@ class Item_Model extends ORM_MPTT { return $this->type == 'movie'; } - public function delete() { + public function delete($ignored_id=null) { + if ($this->id == 1) { + $v = new Validation(array("id")); + $v->add_error("id", "cant_delete_root_album"); + ORM_Validation_Exception::handle_validation($this->table_name, $v); + } + $old = clone $this; module::event("item_before_delete", $this); @@ -98,98 +113,12 @@ class Item_Model extends ORM_MPTT { } /** - * Move this item to the specified target. + * Specify the path to the data file associated with this item. To actually associate it, + * you still have to call save(). * @chainable - * @param Item_Model $target Target item (must be an album) - * @return ORM_MPTT */ - function move_to($target) { - if (!$target->is_album()) { - throw new Exception("@todo INVALID_MOVE_TYPE $target->type"); - } - - if (file_exists($target_file = "{$target->file_path()}/$this->name")) { - throw new Exception("@todo INVALID_MOVE_TARGET_EXISTS: $target_file"); - } - - if ($this->id == 1) { - throw new Exception("@todo INVALID_SOURCE root album"); - } - - $original_path = $this->file_path(); - $original_resize_path = $this->resize_path(); - $original_thumb_path = $this->thumb_path(); - $original_parent = $this->parent(); - - parent::move_to($target, true); - model_cache::clear(); - $this->relative_path_cache = null; - - rename($original_path, $this->file_path()); - if ($this->is_album()) { - @rename(dirname($original_resize_path), dirname($this->resize_path())); - @rename(dirname($original_thumb_path), dirname($this->thumb_path())); - db::build() - ->update("items") - ->set("relative_path_cache", null) - ->set("relative_url_cache", null) - ->where("left_ptr", ">", $this->left_ptr) - ->where("right_ptr", "<", $this->right_ptr) - ->execute(); - } else { - @rename($original_resize_path, $this->resize_path()); - @rename($original_thumb_path, $this->thumb_path()); - } - - module::event("item_moved", $this, $original_parent); - return $this; - } - - /** - * Rename the underlying file for this item to a new name. Move all the files. This requires a - * save. - * - * @chainable - */ - public function rename($new_name) { - if ($new_name == $this->name) { - return; - } - - if (strpos($new_name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - $old_relative_path = urldecode($this->relative_path()); - $new_relative_path = dirname($old_relative_path) . "/" . $new_name; - if (file_exists(VARPATH . "albums/$new_relative_path")) { - throw new Exception("@todo INVALID_RENAME_FILE_EXISTS: $new_relative_path"); - } - - @rename(VARPATH . "albums/$old_relative_path", VARPATH . "albums/$new_relative_path"); - @rename(VARPATH . "resizes/$old_relative_path", VARPATH . "resizes/$new_relative_path"); - if ($this->is_movie()) { - // Movie thumbnails have a .jpg extension - $old_relative_thumb_path = preg_replace("/...$/", "jpg", $old_relative_path); - $new_relative_thumb_path = preg_replace("/...$/", "jpg", $new_relative_path); - @rename(VARPATH . "thumbs/$old_relative_thumb_path", - VARPATH . "thumbs/$new_relative_thumb_path"); - } else { - @rename(VARPATH . "thumbs/$old_relative_path", VARPATH . "thumbs/$new_relative_path"); - } - - $this->name = $new_name; - - if ($this->is_album()) { - db::build() - ->update("items") - ->set("relative_url_cache", null) - ->set("relative_path_cache", null) - ->where("left_ptr", ">", $this->left_ptr) - ->where("right_ptr", "<", $this->right_ptr) - ->execute(); - } - + public function set_data_file($data_file) { + $this->data_file = $data_file; return $this; } @@ -323,7 +252,7 @@ class Item_Model extends ORM_MPTT { } $this->relative_path_cache = implode($names, "/"); $this->relative_url_cache = implode($slugs, "/"); - $this->save(); + return $this; } /** @@ -338,7 +267,7 @@ class Item_Model extends ORM_MPTT { } if (!isset($this->relative_path_cache)) { - $this->_build_relative_caches(); + $this->_build_relative_caches()->save(); } return $this->relative_path_cache; } @@ -353,7 +282,7 @@ class Item_Model extends ORM_MPTT { } if (!isset($this->relative_url_cache)) { - $this->_build_relative_caches(); + $this->_build_relative_caches()->save(); } return $this->relative_url_cache; } @@ -376,30 +305,10 @@ class Item_Model extends ORM_MPTT { } /** - * @see ORM::__set() - */ - public function __set($column, $value) { - if ($column == "name") { - $this->relative_path_cache = null; - } else if ($column == "slug") { - if ($this->slug != $value) { - // Clear the relative url cache for this item and all children - $this->relative_url_cache = null; - if ($this->is_album()) { - db::build() - ->update("items") - ->set("relative_url_cache", null) - ->where("left_ptr", ">", $this->left_ptr) - ->where("right_ptr", "<", $this->right_ptr) - ->execute(); - } - } - } - parent::__set($column, $value); - } - - /** + * Handle any business logic necessary to create or modify an item. * @see ORM::save() + * + * @return ORM Item_Model */ public function save() { $significant_changes = $this->changed; @@ -410,18 +319,168 @@ class Item_Model extends ORM_MPTT { if (!empty($this->changed) && $significant_changes) { $this->updated = time(); if (!$this->loaded()) { - $this->created = $this->updated; - $this->weight = item::get_max_weight(); + // Create a new item. + + // Set a weight if it's missing. We don't do this in the constructor because it's not a + // simple assignment. + if (empty($this->weight)) { + $this->weight = item::get_max_weight(); + } + + // Make an url friendly slug from the name, if necessary + if (empty($this->slug)) { + $tmp = pathinfo($this->name, PATHINFO_FILENAME); + $tmp = preg_replace("/[^A-Za-z0-9-_]+/", "-", $tmp); + $this->slug = trim($tmp, "-"); + } + + // Get the width, height and mime type from our data file for photos and movies. + if ($this->is_movie() || $this->is_photo()) { + $pi = pathinfo($this->data_file); + + if ($this->is_photo()) { + $image_info = getimagesize($this->data_file); + $this->width = $image_info[0]; + $this->height = $image_info[1]; + $this->mime_type = $image_info["mime"]; + + // Force an extension onto the name if necessary + if (empty($pi["extension"])) { + $pi["extension"] = image_type_to_extension($image_info[2], false); + $this->name .= "." . $pi["extension"]; + } + } else { + list ($this->width, $this->height) = movie::getmoviesize($this->data_file); + + // No extension? Assume FLV. + if (empty($pi["extension"])) { + $pi["extension"] = "flv"; + $this->name .= "." . $pi["extension"]; + } + + $this->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; + } + } + + // Randomize the name or slug if there's a conflict. Preserve the extension. + // @todo Improve this. Random numbers are not user friendly + $base_name = pathinfo($this->name, PATHINFO_FILENAME); + $base_ext = pathinfo($this->name, PATHINFO_EXTENSION); + $base_slug = $this->slug; + while (ORM::factory("item") + ->where("parent_id", "=", $this->parent_id) + ->and_open() + ->where("name", "=", $this->name) + ->or_where("slug", "=", $this->slug) + ->close() + ->find()->id) { + $rand = rand(); + if ($base_ext) { + $this->name = "$base_name-$rand.$base_ext"; + } else { + $this->name = "$base_name-$rand"; + } + $this->slug = "$base_slug-$rand"; + } + + parent::save(); + + // Build our url caches, then save again. We have to do this after it's already been + // saved once because we use only information from the database to build the paths. If we + // could depend on a save happening later we could defer this 2nd save. + $this->_build_relative_caches(); + parent::save(); + + // Take any actions that we can only do once all our paths are set correctly after saving. + switch ($this->type) { + case "album": + mkdir($this->file_path()); + mkdir(dirname($this->thumb_path())); + mkdir(dirname($this->resize_path())); + break; + + case "photo": + case "movie": + // The thumb or resize may already exist in the case where a movie and a photo generate + // a thumbnail of the same name (eg, foo.flv movie and foo.jpg photo will generate + // foo.jpg thumbnail). If that happens, randomize and save again. + if (file_exists($this->resize_path()) || + file_exists($this->thumb_path())) { + $pi = pathinfo($this->name); + $this->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; + parent::save(); + } + + copy($this->data_file, $this->file_path()); + break; + } + + // This will almost definitely trigger another save, so put it at the end so that we're + // tail recursive. + module::event("item_created", $this); } else { - $send_event = 1; + // Update an existing item + + // If any significant fields have changed, load up a copy of the original item and + // keep it around. + $original = ORM::factory("item", $this->id); + if (array_intersect($this->changed, array("parent_id", "name", "slug"))) { + $original->_build_relative_caches(); + $this->relative_path_cache = null; + $this->relative_url_cache = null; + } + + parent::save(); + + // Now update the filesystem and any database caches if there were significant value + // changes. If anything past this point fails, then we'll have an inconsistent database + // so this code should be as robust as we can make it. + + // Update the MPTT pointers, if necessary. We have to do this before we generate any + // cached paths! + if ($original->parent_id != $this->parent_id) { + parent::move_to($this->parent()); + } + + if ($original->parent_id != $this->parent_id || $original->name != $this->name) { + // Move all of the items associated data files + @rename($original->file_path(), $this->file_path()); + if ($this->is_album()) { + @rename(dirname($original->resize_path()), dirname($this->resize_path())); + @rename(dirname($original->thumb_path()), dirname($this->thumb_path())); + } else { + @rename($original->resize_path(), $this->resize_path()); + @rename($original->thumb_path(), $this->thumb_path()); + } + + if ($original->parent_id != $this->parent_id) { + // This will result in 2 events since we'll still fire the item_updated event below + module::event("item_moved", $this, $original->parent()); + } + } + + // Changing the name, slug or parent ripples downwards + if ($this->is_album() && + ($original->name != $this->name || + $original->slug != $this->slug || + $original->parent_id != $this->parent_id)) { + db::build() + ->update("items") + ->set("relative_url_cache", null) + ->set("relative_path_cache", null) + ->where("left_ptr", ">", $this->left_ptr) + ->where("right_ptr", "<", $this->right_ptr) + ->execute(); + } + + module::event("item_updated", $original, $this); } + } else if (!empty($this->changed)) { + // Insignificant changes only. Don't fire events or do any special checking to try to keep + // this lightweight. + parent::save(); } - $original = clone $this->original(); - parent::save(); - if (isset($send_event)) { - module::event("item_updated", $original, $this); - } return $this; } @@ -657,4 +716,224 @@ class Item_Model extends ORM_MPTT { } return parent::descendants($limit, $offset, $where, $order_by); } + + /** + * Specify our rules here so that we have access to the instance of this model. + */ + public function validate(Validation $array=null) { + if (!$array) { + $this->rules = array( + "album_cover_item_id" => array("callbacks" => array(array($this, "valid_album_cover"))), + "description" => array("rules" => array("length[0,65535]")), + "mime_type" => array("callbacks" => array(array($this, "valid_field"))), + "name" => array("rules" => array("length[0,255]", "required"), + "callbacks" => array(array($this, "valid_name"))), + "parent_id" => array("callbacks" => array(array($this, "valid_parent"))), + "rand_key" => array("rule" => array("decimal")), + "slug" => array("rules" => array("length[0,255]", "required"), + "callbacks" => array(array($this, "valid_slug"))), + "sort_column" => array("callbacks" => array(array($this, "valid_field"))), + "sort_order" => array("callbacks" => array(array($this, "valid_field"))), + "title" => array("rules" => array("length[0,255]", "required")), + "type" => array("callbacks" => array(array($this, "read_only"), + array($this, "valid_field"))), + ); + + // Conditional rules + if ($this->id == 1) { + // Root album can't have a name or slug so replace the rules + $this->rules["name"] = array("rules" => array("length[0]")); + $this->rules["slug"] = array("rules" => array("length[0]")); + } + + // Movies and photos must have data files + if (($this->is_photo() || $this->is_movie()) && !$this->loaded()) { + $this->rules["name"]["callbacks"][] = array($this, "valid_data_file"); + } + } + + parent::validate($array); + } + + /** + * Validate that the desired slug does not conflict. + */ + public function valid_slug(Validation $v, $field) { + if (preg_match("/[^A-Za-z0-9-_]/", $this->slug)) { + $v->add_error("slug", "not_url_safe"); + } else if (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("id", "<>", $this->id) + ->where("slug", "=", $this->slug) + ->count_records()) { + $v->add_error("slug", "conflict"); + } + } + + /** + * Validate the item name. It can't conflict with other names, can't contain slashes or + * trailing periods. + */ + public function valid_name(Validation $v, $field) { + if (strpos($this->name, "/") !== false) { + $v->add_error("name", "no_slashes"); + return; + } else if (rtrim($this->name, ".") !== $this->name) { + $v->add_error("name", "no_trailing_period"); + return; + } + + if ($this->is_movie() || $this->is_photo()) { + if ($this->loaded()) { + // Existing items can't change their extension + $original = ORM::factory("item", $this->id); + $new_ext = pathinfo($this->name, PATHINFO_EXTENSION); + $old_ext = pathinfo($original->name, PATHINFO_EXTENSION); + if (strcasecmp($new_ext, $old_ext)) { + $v->add_error("name", "illegal_data_file_extension"); + return; + } + } else { + // New items must have an extension + if (!pathinfo($this->name, PATHINFO_EXTENSION)) { + $v->add_error("name", "illegal_data_file_extension"); + return; + } + } + } + + if (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("name", "=", $this->name) + ->merge_where($this->id ? array(array("id", "<>", $this->id)) : null) + ->count_records()) { + $v->add_error("name", "conflict"); + return; + } + } + + /** + * Make sure that the data file is well formed (it exists and isn't empty). + */ + public function valid_data_file(Validation $v, $field) { + if (!is_file($this->data_file)) { + $v->add_error("name", "bad_data_file_path"); + } else if (filesize($this->data_file) == 0) { + $v->add_error("name", "empty_data_file"); + } + } + + /** + * Make sure that the parent id refers to an album. + */ + public function valid_parent(Validation $v, $field) { + if ($this->id == 1) { + if ($this->parent_id != 0) { + $v->add_error("parent_id", "invalid"); + } + } else { + $query = db::build() + ->from("items") + ->where("id", "=", $this->parent_id) + ->where("type", "=", "album"); + + // If this is an existing item, make sure the new parent is not part of our hierarchy + if ($this->loaded()) { + $query->and_open() + ->where("left_ptr", "<", $this->left_ptr) + ->or_where("right_ptr", ">", $this->right_ptr) + ->close(); + } + + if ($query->count_records() != 1) { + $v->add_error("parent_id", "invalid"); + } + } + } + + /** + * Make sure the album cover item id refers to a valid item, or is null. + */ + public function valid_album_cover(Validation $v, $field) { + if ($this->id == 1) { + return; + } + + if ($this->album_cover_item_id && db::build() + ->from("items") + ->where("id", "=", $this->album_cover_item_id) + ->count_records() != 1) { + $v->add_error("album_cover_item_id", "invalid_item"); + } + } + + /** + * Make sure that the type is valid. + */ + public function valid_field(Validation $v, $field) { + switch($field) { + case "mime_type": + if ($this->is_movie()) { + $legal_values = array("video/flv", "video/x-flv", "video/mp4"); + } if ($this->is_photo()) { + $legal_values = array("image/jpeg", "image/gif", "image/png"); + } + break; + + case "sort_column": + if (!array_key_exists($this->sort_column, $this->object)) { + $v->add_error($field, "invalid"); + } + break; + + case "sort_order": + $legal_values = array("ASC", "DESC", "asc", "desc"); + break; + + case "type": + $legal_values = array("album", "photo", "movie"); + break; + + default: + $v->add_error($field, "unvalidated_field"); + break; + } + + if (isset($legal_values) && !in_array($this->$field, $legal_values)) { + $v->add_error($field, "invalid"); + } + } + + /** + * This field cannot be changed after it's been set. + */ + public function read_only(Validation $v, $field) { + if ($this->loaded() && isset($this->changed[$field])) { + $v->add_error($field, "read_only"); + } + } + + /** + * Same as ORM::as_array() but convert id fields into their RESTful form. + */ + public function as_restful_array() { + // Convert item ids to rest URLs for consistency + $data = $this->as_array(); + if ($tmp = $this->parent()) { + $data["parent"] = rest::url("item", $tmp); + } + unset($data["parent_id"]); + if ($tmp = $this->album_cover()) { + $data["album_cover"] = rest::url("item", $tmp); + } + unset($data["album_cover_item_id"]); + + // Elide some internal-only data that is going to cause confusion in the client. + foreach (array("relative_path_cache", "relative_url_cache", "left_ptr", "right_ptr") as $key) { + unset($data[$key]); + } + return $data; + } } diff --git a/modules/gallery/models/task.php b/modules/gallery/models/task.php index f40be492..24d909cb 100644 --- a/modules/gallery/models/task.php +++ b/modules/gallery/models/task.php @@ -27,7 +27,7 @@ class Task_Model extends ORM { } } - public function set($key, $value) { + public function set($key, $value=null) { $context = unserialize($this->context); $context[$key] = $value; $this->context = serialize($context); @@ -40,7 +40,7 @@ class Task_Model extends ORM { return parent::save(); } - public function delete() { + public function delete($ignored_id=null) { Cache::instance()->delete($this->_cache_key()); return parent::delete(); } diff --git a/modules/gallery/module.info b/modules/gallery/module.info index 107d9a12..50a1505f 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,4 +1,3 @@ name = "Gallery 3" description = "Gallery core application" -version = 22 - +version = 25 diff --git a/modules/gallery/tests/Access_Helper_Test.php b/modules/gallery/tests/Access_Helper_Test.php index b2244766..5331117d 100644 --- a/modules/gallery/tests/Access_Helper_Test.php +++ b/modules/gallery/tests/Access_Helper_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Access_Helper_Test extends Unit_Test_Case { +class Access_Helper_Test extends Gallery_Unit_Test_Case { private $_group; public function teardown() { @@ -40,8 +40,7 @@ class Access_Helper_Test extends Unit_Test_Case { } catch (Exception $e) { } // Reset some permissions that we mangle below - $root = ORM::factory("item", 1); - access::allow(identity::everybody(), "view", $root); + access::allow(identity::everybody(), "view", item::root()); } public function setup() { @@ -67,16 +66,15 @@ class Access_Helper_Test extends Unit_Test_Case { public function user_can_access_test() { $access_test = identity::create_group("access_test"); - $root = ORM::factory("item", 1); - access::allow($access_test, "view", $root); + access::allow($access_test, "view", item::root()); - $item = album::create($root, rand(), "test album"); + $item = test::random_album(); access::deny(identity::everybody(), "view", $item); access::deny(identity::registered_users(), "view", $item); $item->reload(); - $user = identity::create_user("access_test", "Access Test", ""); + $user = identity::create_user("access_test", "Access Test", "*****", "user@user.com"); foreach ($user->groups() as $group) { $user->remove($group); } @@ -87,13 +85,12 @@ class Access_Helper_Test extends Unit_Test_Case { } public function user_can_no_access_test() { - $root = ORM::factory("item", 1); - $item = album::create($root, rand(), "test album"); + $item = test::random_album(); access::deny(identity::everybody(), "view", $item); access::deny(identity::registered_users(), "view", $item); - $user = identity::create_user("access_test", "Access Test", ""); + $user = identity::create_user("access_test", "Access Test", "*****", "user@user.com"); foreach ($user->groups() as $group) { $user->remove($group); } @@ -103,8 +100,7 @@ class Access_Helper_Test extends Unit_Test_Case { } public function adding_and_removing_items_adds_ands_removes_rows_test() { - $root = ORM::factory("item", 1); - $item = album::create($root, rand(), "test album"); + $item = test::random_album(); // New rows exist $this->assert_true(ORM::factory("access_cache")->where("item_id", "=", $item->id)->find()->loaded()); @@ -119,19 +115,16 @@ class Access_Helper_Test extends Unit_Test_Case { } public function new_photos_inherit_parent_permissions_test() { - $root = ORM::factory("item", 1); - - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); access::allow(identity::everybody(), "view", $album); - $photo = photo::create($album, MODPATH . "gallery/images/gallery.png", "", ""); + $photo = test::random_photo($album); $this->assert_true($photo->__get("view_" . identity::everybody()->id)); } public function can_allow_deny_and_reset_intent_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); $intent = ORM::factory("access_intent")->where("item_id", "=", $album->id)->find(); // Allow @@ -167,23 +160,21 @@ class Access_Helper_Test extends Unit_Test_Case { } public function can_view_item_test() { - $root = ORM::factory("item", 1); - access::allow(identity::everybody(), "view", $root); - $this->assert_true(access::group_can(identity::everybody(), "view", $root)); + access::allow(identity::everybody(), "view", item::root()); + $this->assert_true(access::group_can(identity::everybody(), "view", item::root())); } public function can_always_fails_on_unloaded_items_test() { - $root = ORM::factory("item", 1); - access::allow(identity::everybody(), "view", $root); - $this->assert_true(access::group_can(identity::everybody(), "view", $root)); + access::allow(identity::everybody(), "view", item::root()); + $this->assert_true(access::group_can(identity::everybody(), "view", item::root())); $bogus = ORM::factory("item", -1); $this->assert_false(access::group_can(identity::everybody(), "view", $bogus)); } public function cant_view_child_of_hidden_parent_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $root = item::root(); + $album = test::random_album(); $root->reload(); access::deny(identity::everybody(), "view", $root); @@ -194,28 +185,39 @@ class Access_Helper_Test extends Unit_Test_Case { } public function view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); - access::allow(identity::everybody(), "view", $root); + access::allow(identity::everybody(), "view", item::root()); access::reset(identity::everybody(), "view", $album); $album->reload(); $this->assert_true(access::group_can(identity::everybody(), "view", $album)); } + public function view_permissions_propagate_down_to_photos_test() { + $album = test::random_album(); + $photo = test::random_photo($album); + identity::set_active_user(identity::guest()); + + $this->assert_true(access::can("view", $photo)); + $album->reload(); // MPTT pointers have changed, so reload before calling access::deny + access::deny(identity::everybody(), "view", $album); + + $photo->reload(); // view permissions are cached in the photo, so reload before checking + $this->assert_false(access::can("view", $photo)); + } + public function can_toggle_view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, rand(), "test album"); - $album2 = album::create($album1, rand(), "test album"); - $album3 = album::create($album2, rand(), "test album"); - $album4 = album::create($album3, rand(), "test album"); + $album1 = test::random_album(item::root()); + $album2 = test::random_album($album1); + $album3 = test::random_album($album2); + $album4 = test::random_album($album3); $album1->reload(); $album2->reload(); $album3->reload(); $album4->reload(); - access::allow(identity::everybody(), "view", $root); + access::allow(identity::everybody(), "view", item::root()); access::deny(identity::everybody(), "view", $album1); access::reset(identity::everybody(), "view", $album2); access::reset(identity::everybody(), "view", $album3); @@ -230,9 +232,9 @@ class Access_Helper_Test extends Unit_Test_Case { } public function revoked_view_permissions_cant_be_allowed_lower_down_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, rand(), "test album"); - $album2 = album::create($album1, rand(), "test album"); + $root = item::root(); + $album1 = test::random_album($root); + $album2 = test::random_album($album1); $root->reload(); access::deny(identity::everybody(), "view", $root); @@ -246,38 +248,30 @@ class Access_Helper_Test extends Unit_Test_Case { } public function can_edit_item_test() { - $root = ORM::factory("item", 1); + $root = item::root(); access::allow(identity::everybody(), "edit", $root); $this->assert_true(access::group_can(identity::everybody(), "edit", $root)); } public function non_view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); - access::allow(identity::everybody(), "edit", $root); + access::allow(identity::everybody(), "edit", item::root()); access::reset(identity::everybody(), "edit", $album); $this->assert_true(access::group_can(identity::everybody(), "edit", $album)); } public function non_view_permissions_can_be_revoked_lower_down_test() { - $root = ORM::factory("item", 1); - $outer = album::create($root, rand(), "test album"); - $outer_photo = ORM::factory("item"); - $outer_photo->type = "photo"; - $outer_photo->add_to_parent($outer); - access::add_item($outer_photo); - - $inner = album::create($outer, rand(), "test album"); - $inner_photo = ORM::factory("item"); - $inner_photo->type = "photo"; - $inner_photo->add_to_parent($inner); - access::add_item($inner_photo); + $outer = test::random_album(); + $outer_photo = test::random_photo($outer); + + $inner = test::random_album($outer); + $inner_photo = test::random_photo($inner); $outer->reload(); $inner->reload(); - access::allow(identity::everybody(), "edit", $root); + access::allow(identity::everybody(), "edit", item::root()); access::deny(identity::everybody(), "edit", $outer); access::allow(identity::everybody(), "edit", $inner); @@ -288,7 +282,7 @@ class Access_Helper_Test extends Unit_Test_Case { public function i_can_edit_test() { // Create a new user that belongs to no groups - $user = identity::create_user("access_test", "Access Test", ""); + $user = identity::create_user("access_test", "Access Test", "*****", "user@user.com"); foreach ($user->groups() as $group) { $user->remove($group); } @@ -296,7 +290,7 @@ class Access_Helper_Test extends Unit_Test_Case { identity::set_active_user($user); // This user can't edit anything - $root = ORM::factory("item", 1); + $root = item::root(); $this->assert_false(access::can("edit", $root)); // Now add them to a group that has edit permission @@ -313,8 +307,7 @@ class Access_Helper_Test extends Unit_Test_Case { } public function everybody_view_permission_maintains_htaccess_files_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); $this->assert_false(file_exists($album->file_path() . "/.htaccess")); @@ -332,8 +325,7 @@ class Access_Helper_Test extends Unit_Test_Case { } public function everybody_view_full_permission_maintains_htaccess_files_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); + $album = test::random_album(); $this->assert_false(file_exists($album->file_path() . "/.htaccess")); $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); @@ -363,16 +355,15 @@ class Access_Helper_Test extends Unit_Test_Case { public function moved_items_inherit_new_permissions_test() { identity::set_active_user(identity::lookup_user_by_name("admin")); - $root = ORM::factory("item", 1); - $public_album = album::create($root, rand(), "public album"); - $public_photo = photo::create($public_album, MODPATH . "gallery/images/gallery.png", "", ""); + $public_album = test::random_album(); + $public_photo = test::random_photo($public_album); access::allow(identity::everybody(), "view", $public_album); - $root->reload(); // Account for MPTT changes + item::root()->reload(); // Account for MPTT changes - $private_album = album::create($root, rand(), "private album"); + $private_album = test::random_album(); access::deny(identity::everybody(), "view", $private_album); - $private_photo = photo::create($private_album, MODPATH . "gallery/images/gallery.png", "", ""); + $private_photo = test::random_photo($private_album); // Make sure that we now have a public photo and private photo. $this->assert_true(access::group_can(identity::everybody(), "view", $public_photo)); diff --git a/modules/gallery/tests/Album_Helper_Test.php b/modules/gallery/tests/Album_Helper_Test.php deleted file mode 100644 index ef0905da..00000000 --- a/modules/gallery/tests/Album_Helper_Test.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class Album_Helper_Test extends Unit_Test_Case { - public function create_album_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - - $this->assert_equal(VARPATH . "albums/$rand", $album->file_path()); - $this->assert_equal(VARPATH . "thumbs/$rand/.album.jpg", $album->thumb_path()); - $this->assert_true(is_dir(VARPATH . "thumbs/$rand"), "missing thumb dir"); - - // It's unclear that a resize makes sense for an album. But we have one. - $this->assert_equal(VARPATH . "resizes/$rand/.album.jpg", $album->resize_path()); - $this->assert_true(is_dir(VARPATH . "resizes/$rand"), "missing resizes dir"); - - $this->assert_equal(1, $album->parent_id); // MPTT tests will cover other hierarchy checks - $this->assert_equal($rand, $album->name); - $this->assert_equal($rand, $album->title); - $this->assert_equal($rand, $album->description); - } - - public function create_conflicting_album_test() { - $rand = "name_" . rand(); - $root = ORM::factory("item", 1); - $album1 = album::create($root, $rand, $rand, $rand); - $album2 = album::create($root, $rand, $rand, $rand); - $this->assert_true($album1->name != $album2->name); - } - - public function thumb_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $this->assert_equal( - "http://./var/thumbs/$rand/.album.jpg?m={$album->updated}", $album->thumb_url()); - } - - public function resize_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $this->assert_equal("http://./var/resizes/$rand/.album.jpg?m={$album->updated}", $album->resize_url()); - } - - public function create_album_shouldnt_allow_names_with_slash_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $album = album::create($root, $rand . "/", $rand, $rand); - } catch (Exception $e) { - // pass - return; - } - - $this->assert_true(false, "Shouldn't create an album with / in the name"); - } - - public function create_album_silently_trims_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $album = album::create($root, $rand . "..", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create an album with trailing . in the name"); - } -} diff --git a/modules/gallery/tests/Albums_Controller_Test.php b/modules/gallery/tests/Albums_Controller_Test.php index 4d8935cd..76c9a628 100644 --- a/modules/gallery/tests/Albums_Controller_Test.php +++ b/modules/gallery/tests/Albums_Controller_Test.php @@ -17,64 +17,58 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Albums_Controller_Test extends Unit_Test_Case { +class Albums_Controller_Test extends Gallery_Unit_Test_Case { public function setup() { $this->_save = array($_POST, $_SERVER); } public function teardown() { list($_POST, $_SERVER) = $this->_save; - if (isset($this->_album)) { - $this->_album->delete(); - } } public function change_album_test() { $controller = new Albums_Controller(); - $root = ORM::factory("item", 1); - $this->_album = album::create($root, "test", "test", "test"); - $orig_name = $this->_album->name; + $album = test::random_album(); // Randomize to avoid conflicts. - $new_dirname = "new_name_" . rand(); + $new_name = "new_name_" . rand(); - $_POST["dirname"] = $new_dirname; + $_POST["name"] = $new_name; $_POST["title"] = "new title"; $_POST["description"] = "new description"; $_POST["column"] = "weight"; $_POST["direction"] = "ASC"; $_POST["csrf"] = access::csrf_token(); $_POST["slug"] = "new-name"; - access::allow(identity::everybody(), "edit", $root); + access::allow(identity::everybody(), "edit", item::root()); ob_start(); - $controller->update($this->_album->id); - $this->_album->reload(); + $controller->update($album->id); + $album->reload(); $results = ob_get_contents(); ob_end_clean(); - $this->assert_equal( - json_encode(array("result" => "success")), - $results); - $this->assert_equal($new_dirname, $this->_album->name); - $this->assert_equal("new title", $this->_album->title); - $this->assert_equal("new description", $this->_album->description); + $this->assert_equal(json_encode(array("result" => "success")), $results); + $this->assert_equal($new_name, $album->name); + $this->assert_equal("new title", $album->title); + $this->assert_equal("new description", $album->description); } public function change_album_no_csrf_fails_test() { $controller = new Albums_Controller(); - $root = ORM::factory("item", 1); - $this->_album = album::create($root, "test", "test", "test"); + $album = test::random_album(); + $_POST["name"] = "new name"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; - access::allow(identity::everybody(), "edit", $root); + access::allow(identity::everybody(), "edit", item::root()); try { - $controller->_update($this->_album); + $controller->update($album->id); $this->assert_true(false, "This should fail"); } catch (Exception $e) { // pass + $this->assert_same("@todo FORBIDDEN", $e->getMessage()); } } } diff --git a/modules/gallery/tests/Cache_Test.php b/modules/gallery/tests/Cache_Test.php index d5bf37cc..1023568b 100644 --- a/modules/gallery/tests/Cache_Test.php +++ b/modules/gallery/tests/Cache_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Cache_Test extends Unit_Test_Case { +class Cache_Test extends Gallery_Unit_Test_Case { private $_driver; public function setup() { db::build()->delete("caches")->execute(); diff --git a/modules/gallery/tests/Controller_Auth_Test.php b/modules/gallery/tests/Controller_Auth_Test.php index 124d8b4c..c27196da 100644 --- a/modules/gallery/tests/Controller_Auth_Test.php +++ b/modules/gallery/tests/Controller_Auth_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Controller_Auth_Test extends Unit_Test_Case { +class Controller_Auth_Test extends Gallery_Unit_Test_Case { public function find_missing_auth_test() { $found = array(); $controllers = explode("\n", `git ls-files '*/*/controllers/*.php'`); diff --git a/modules/gallery/tests/Database_Test.php b/modules/gallery/tests/Database_Test.php index 6aa186e5..861f7bba 100644 --- a/modules/gallery/tests/Database_Test.php +++ b/modules/gallery/tests/Database_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Database_Test extends Unit_Test_Case { +class Database_Test extends Gallery_Unit_Test_Case { function setup() { $config = Kohana_Config::instance(); $config->set("database.mock.connection.type", "mock"); @@ -168,12 +168,12 @@ class Database_Mock extends Database { return array("test"); } - public function quote_column($val) { - return "[$val]"; + public function quote_column($val, $alias=null) { + return $alias ? "[$val,$alias]" : "[$val]"; } - public function quote_table($val) { - return "[$val]"; + public function quote_table($val, $alias=null) { + return $alias ? "[$val,$alias]" : "[$val]"; } public function quote($val) { diff --git a/modules/gallery/tests/Dir_Helper_Test.php b/modules/gallery/tests/Dir_Helper_Test.php index 46bb871c..69241447 100644 --- a/modules/gallery/tests/Dir_Helper_Test.php +++ b/modules/gallery/tests/Dir_Helper_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Dir_Helper_Test extends Unit_Test_Case { +class Dir_Helper_Test extends Gallery_Unit_Test_Case { public function remove_album_test() { $dirname = (VARPATH . "albums/testdir"); mkdir($dirname, 0777, true); diff --git a/modules/gallery/tests/DrawForm_Test.php b/modules/gallery/tests/DrawForm_Test.php index da8a6b04..f7b727c0 100644 --- a/modules/gallery/tests/DrawForm_Test.php +++ b/modules/gallery/tests/DrawForm_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class DrawForm_Test extends Unit_Test_Case { +class DrawForm_Test extends Gallery_Unit_Test_Case { function no_group_test() { $form = new Forge("test/controller", "", "post", array("id" => "g-test-group-form")); $form->input("title")->label(t("Title")); diff --git a/modules/gallery/tests/File_Structure_Test.php b/modules/gallery/tests/File_Structure_Test.php index b5026188..4590e95d 100644 --- a/modules/gallery/tests/File_Structure_Test.php +++ b/modules/gallery/tests/File_Structure_Test.php @@ -19,7 +19,7 @@ */ require_once(MODPATH . "gallery/tests/Gallery_Filters.php"); -class File_Structure_Test extends Unit_Test_Case { +class File_Structure_Test extends Gallery_Unit_Test_Case { public function no_trailing_closing_php_tag_test() { $dir = new GalleryCodeFilterIterator( new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); @@ -36,7 +36,7 @@ class File_Structure_Test extends Unit_Test_Case { $dir = new GalleryCodeFilterIterator( new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); foreach ($dir as $file) { - if (strpos($file, "modules/gallery/views/kohana/error.php")) { + if (strpos($file, "views/kohana/error.php")) { continue; } diff --git a/modules/gallery/tests/Gallery_I18n_Test.php b/modules/gallery/tests/Gallery_I18n_Test.php index 5d2fd994..f6e50d71 100644 --- a/modules/gallery/tests/Gallery_I18n_Test.php +++ b/modules/gallery/tests/Gallery_I18n_Test.php @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Gallery_I18n_Test extends Unit_Test_Case { +class Gallery_I18n_Test extends Gallery_Unit_Test_Case { private $i18n; public function setup() { diff --git a/modules/gallery/tests/Gallery_Installer_Test.php b/modules/gallery/tests/Gallery_Installer_Test.php index 74a07b1a..3db434bc 100644 --- a/modules/gallery/tests/Gallery_Installer_Test.php +++ b/modules/gallery/tests/Gallery_Installer_Test.php @@ -22,7 +22,7 @@ * This test case operates under the assumption that gallery_installer::install() is called by the * test controller before it starts. */ -class Gallery_Installer_Test extends Unit_Test_Case { +class Gallery_Installer_Test extends Gallery_Unit_Test_Case { public function install_creates_dirs_test() { $this->assert_true(file_exists(VARPATH . "albums")); $this->assert_true(file_exists(VARPATH . "resizes")); diff --git a/modules/gallery/tests/Gallery_Rest_Helper_Test.php b/modules/gallery/tests/Gallery_Rest_Helper_Test.php deleted file mode 100644 index f8cf6190..00000000 --- a/modules/gallery/tests/Gallery_Rest_Helper_Test.php +++ /dev/null @@ -1,277 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class Gallery_Rest_Helper_Test extends Unit_Test_Case { - public function setup() { - $this->_save = array($_GET, $_POST, $_SERVER, $_FILES); - $this->_saved_active_user = identity::active_user(); - } - - public function teardown() { - list($_GET, $_POST, $_SERVER, $_FILES) = $this->_save; - identity::set_active_user($this->_saved_active_user); - if (!empty($this->_user)) { - try { - $this->_user->delete(); - } catch (Exception $e) { } - } - } - - private function _create_user() { - if (empty($this->_user)) { - $this->_user = identity::create_user("access_test" . rand(), "Access Test", "password"); - $key = ORM::factory("user_access_token"); - $key->access_key = md5($this->_user->name . rand()); - $key->user_id = $this->_user->id; - $key->save(); - identity::set_active_user($this->_user); - } - return $this->_user; - } - - private function _create_album($parent=null) { - $album_name = "rest_album_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - return album::create($parent, $album_name, $album_name, $album_name); - } - - private function _create_image($parent=null) { - $filename = MODPATH . "gallery/tests/test.jpg"; - $image_name = "rest_image_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - return photo::create($parent, $filename, "$image_name.jpg", $image_name); - } - - public function gallery_rest_get_album_test() { - $album = $this->_create_album(); - $child = $this->_create_album($album); - $photo = $this->_create_image($child); - $child->reload(); - $request = (object)array("arguments" => explode("/", $child->relative_url())); - - $this->assert_equal( - json_encode(array("status" => "OK", - "resource" => - array("type" => $child->type, - "name" => $child->name, - "path" => $child->relative_url(), - "parent_path" => $album->relative_url(), - "title" => $child->title, - "thumb_url" => $child->thumb_url(), - "thumb_size" => array("height" => $child->thumb_height, - "width" => $child->thumb_width), - "resize_url" => $child->resize_url(), - "resize_size" => array("height" => 0, - "width" => 0), - "url" => $child->file_url(), - "size" => array("height" => $child->height, - "width" => $child->width), - "description" => $child->description, - "slug" => $child->slug, - "children" => array(array( - "type" => "photo", - "has_children" => false, - "path" => $photo->relative_url(), - "thumb_url" => $photo->thumb_url(), - "thumb_dimensions" => array( - "width" => (string)$photo->thumb_width, - "height" => (string)$photo->thumb_height), - "has_thumb" => true, - "title" => $photo->title))))), - gallery_rest::get($request)); - } - - public function gallery_rest_get_photo_test() { - $child = $this->_create_album(); - $photo = $this->_create_image($child); - $request = (object)array("arguments" => explode("/", $photo->relative_url())); - - $this->assert_equal( - json_encode(array("status" => "OK", - "resource" => - array("type" => $photo->type, - "name" => $photo->name, - "path" => $photo->relative_url(), - "parent_path" => $child->relative_url(), - "title" => $photo->title, - "thumb_url" => $photo->thumb_url(), - "thumb_size" => array("height" => (string)$photo->thumb_height, - "width" => (string)$photo->thumb_width), - "resize_url" => $photo->resize_url(), - "resize_size" => array("height" => $photo->resize_height, - "width" => $photo->resize_width), - "url" => $photo->file_url(), - "size" => array("height" => (string)$photo->height, - "width" => (string)$photo->width), - "description" => $photo->description, - "slug" => $photo->slug))), - gallery_rest::get($request)); - } - - public function gallery_rest_put_album_no_path_test() { - $request = (object)array("description" => "Updated description", - "title" => "Updated Title", - "name" => "new name"); - - try { - gallery_rest::put($request); - } catch (Rest_Exception $e) { - $this->assert_equal("400 Bad request", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function gallery_rest_put_album_not_found_test() { - $photo = $this->_create_image(); - $request = (object)array("arguments" => explode("/", $photo->relative_url() . rand()), - "description" => "Updated description", - "title" => "Updated Title", - "name" => "new name"); - - try { - gallery_rest::put($request); - } catch (Kohana_404_Exception $k404) { - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function gallery_rest_put_album_no_edit_permission_test() { - $child = $this->_create_album(); - $this->_create_user(); - $request = (object)array("arguments" => explode("/", $child->relative_url()), - "description" => "Updated description", - "title" => "Updated Title", - "name" => "new name"); - - try { - gallery_rest::put($request); - } catch (Kohana_404_Exception $k404) { - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function gallery_rest_put_album_rename_conflict_test() { - $child = $this->_create_album(); - $sibling = $this->_create_image(); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $child); - $request = (object)array("arguments" => explode("/", $child->relative_url()), - "description" => "Updated description", - "title" => "Updated Title", - "name" => $sibling->name); - - $this->assert_equal( - json_encode(array("status" => "VALIDATE_ERROR", - "fields" => array("slug" => "Duplicate Internet address"))), - gallery_rest::put($request)); - } - - public function gallery_rest_put_album_test() { - $child = $this->_create_album(); - $sibling = $this->_create_image(); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $child); - - $new_name = "new_album_name" . rand(); - $request = (object)array("arguments" => explode("/", $child->relative_url()), - "description" => "Updated description", - "title" => "Updated Title", - "name" => $new_name); - - $this->assert_equal(json_encode(array("status" => "OK")), gallery_rest::put($request)); - $child->reload(); - $this->assert_equal("Updated description", $child->description); - $this->assert_equal("Updated Title", $child->title); - $this->assert_equal($new_name, $child->name); - } - - public function gallery_rest_put_photo_test() { - $child = $this->_create_album(); - $photo = $this->_create_image($child); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $child); - - $request = (object)array("arguments" => explode("/", $photo->relative_url()), - "description" => "Updated description", - "title" => "Updated Title", - "name" => "new name"); - - $this->assert_equal(json_encode(array("status" => "OK")), gallery_rest::put($request)); - $photo->reload(); - $this->assert_equal("Updated description", $photo->description); - $this->assert_equal("Updated Title", $photo->title); - $this->assert_equal("new name", $photo->name); - } - - public function gallery_rest_delete_album_test() { - $album = $this->_create_album(); - $child = $this->_create_album($album); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $album); - - $request = (object)array("arguments" => explode("/", $child->relative_url())); - - $this->assert_equal(json_encode(array("status" => "OK", - "resource" => array( - "parent_path" => $album->relative_url()))), - gallery_rest::delete($request)); - $child->reload(); - $this->assert_false($child->loaded()); - } - - public function gallery_rest_delete_photo_test() { - $album = $this->_create_album(); - $photo = $this->_create_image($album); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $album); - - $request = (object)array("arguments" => explode("/", $photo->relative_url())); - - $this->assert_equal(json_encode(array("status" => "OK", - "resource" => array( - "parent_path" => $album->relative_url()))), - gallery_rest::delete($request)); - $photo->reload(); - $this->assert_false($photo->loaded()); - } - - public function gallery_rest_post_album_test() { - $album = $this->_create_album(); - $this->_create_user(); - access::allow(identity::registered_users(), "edit", $album); - - $new_path = $album->relative_url() . "/new%20child"; - $request = (object)array("arguments" => explode("/", $new_path)); - - $this->assert_equal(json_encode(array("status" => "OK", "path" => $new_path)), - gallery_rest::post($request)); - $album = ORM::factory("item") - ->where("relative_url_cache", "=", $new_path) - ->find(); - $this->assert_true($album->loaded()); - $this->assert_equal("new child", $album->slug); - } -} diff --git a/modules/gallery/tests/Html_Helper_Test.php b/modules/gallery/tests/Html_Helper_Test.php index 1662b866..be318632 100644 --- a/modules/gallery/tests/Html_Helper_Test.php +++ b/modules/gallery/tests/Html_Helper_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Html_Helper_Test extends Unit_Test_Case { +class Html_Helper_Test extends Gallery_Unit_Test_Case { public function clean_test() { $safe_string = html::clean("hello <p >world</p>"); $this->assert_equal("hello <p >world</p>", diff --git a/modules/gallery/tests/Item_Helper_Test.php b/modules/gallery/tests/Item_Helper_Test.php index f0c653c0..cdbdd324 100644 --- a/modules/gallery/tests/Item_Helper_Test.php +++ b/modules/gallery/tests/Item_Helper_Test.php @@ -17,12 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Item_Helper_Test extends Unit_Test_Case { +class Item_Helper_Test extends Gallery_Unit_Test_Case { public function viewable_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), rand(), rand()); - $item = self::_create_random_item($album); + $album = test::random_album(); + $item = test::random_photo($album); + $album->reload(); identity::set_active_user(identity::guest()); // We can see the item when permissions are granted @@ -38,33 +38,8 @@ class Item_Helper_Test extends Unit_Test_Case { ORM::factory("item")->viewable()->where("id", "=", $item->id)->count_all()); } - public function validate_url_safe_test() { - $input = new MockInput(); - $input->value = "Ab_cd-ef-d9"; - item::validate_url_safe($input); - $this->assert_true(!isset($input->not_url_safe)); - - $input->value = "ab&cd"; - item::validate_url_safe($input); - $this->assert_equal(1, $input->not_url_safe); - } - public function convert_filename_to_slug_test() { $this->assert_equal("foo", item::convert_filename_to_slug("{[foo]}")); $this->assert_equal("foo-bar", item::convert_filename_to_slug("{[foo!@#!$@#^$@($!(@bar]}")); } - - private static function _create_random_item($album) { - // Set all required fields (values are irrelevant) - $item = ORM::factory("item"); - $item->name = rand(); - $item->type = "photo"; - return $item->add_to_parent($album); - } } - -class MockInput { - function add_error($error, $value) { - $this->$error = $value; - } -}
\ No newline at end of file diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index d03a03f4..9f632fb5 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -17,22 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Item_Model_Test extends Unit_Test_Case { +class Item_Model_Test extends Gallery_Unit_Test_Case { public function saving_sets_created_and_updated_dates_test() { - $item = self::_create_random_item(); + $item = test::random_photo(); $this->assert_true(!empty($item->created)); $this->assert_true(!empty($item->updated)); } - private static function _create_random_item($root=null, $rand=null) { - $root = $root ? $root : ORM::factory("item", 1); - $rand = $rand ? $rand : rand(); - $item = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - return $item; - } - public function updating_doesnt_change_created_date_test() { - $item = self::_create_random_item(); + $item = test::random_photo(); // Force the creation date to something well known db::build() @@ -50,7 +43,7 @@ class Item_Model_Test extends Unit_Test_Case { } public function updating_view_count_only_doesnt_change_updated_date_test() { - $item = self::_create_random_item(); + $item = test::random_photo(); $item->reload(); $this->assert_equal(0, $item->view_count); @@ -69,18 +62,16 @@ class Item_Model_Test extends Unit_Test_Case { } public function rename_photo_test() { - // Create a test photo - $item = self::_create_random_item(); + $item = test::random_photo(); + $original_name = $item->name; file_put_contents($item->thumb_path(), "thumb"); file_put_contents($item->resize_path(), "resize"); file_put_contents($item->file_path(), "file"); - $original_name = $item->name; - $new_name = rand(); - // Now rename it - $item->rename($new_name)->save(); + $item->name = ($new_name = test::random_name($item)); + $item->save(); // Expected: the name changed, the name is now baked into all paths, and all files were moved. $this->assert_equal($new_name, $item->name); @@ -93,10 +84,9 @@ class Item_Model_Test extends Unit_Test_Case { } public function rename_album_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), rand(), rand()); - $photo = self::_create_random_item($album); + $album = test::random_album(); + $photo = test::random_photo($album); + $album->reload(); file_put_contents($photo->thumb_path(), "thumb"); file_put_contents($photo->resize_path(), "resize"); @@ -104,10 +94,11 @@ class Item_Model_Test extends Unit_Test_Case { $original_album_name = $album->name; $original_photo_name = $photo->name; - $new_album_name = rand(); + $new_album_name = test::random_name(); // Now rename the album - $album->rename($new_album_name)->save(); + $album->name = $new_album_name; + $album->save(); $photo->reload(); // Expected: @@ -120,9 +111,9 @@ class Item_Model_Test extends Unit_Test_Case { $this->assert_equal($new_album_name, basename(dirname($album->thumb_path()))); $this->assert_equal($new_album_name, basename(dirname($album->resize_path()))); - $this->assert_same(0, strpos($photo->file_path(), $album->file_path())); - $this->assert_same(0, strpos($photo->thumb_path(), dirname($album->thumb_path()))); - $this->assert_same(0, strpos($photo->resize_path(), dirname($album->resize_path()))); + $this->assert_true(test::starts_with($photo->file_path(), $album->file_path())); + $this->assert_true(test::starts_with($photo->thumb_path(), dirname($album->thumb_path()))); + $this->assert_true(test::starts_with($photo->resize_path(), dirname($album->resize_path()))); $this->assert_equal("thumb", file_get_contents($photo->thumb_path())); $this->assert_equal("resize", file_get_contents($photo->resize_path())); @@ -130,81 +121,56 @@ class Item_Model_Test extends Unit_Test_Case { } public function item_rename_wont_accept_slash_test() { - // Create a test photo - $item = self::_create_random_item(); - - $new_name = rand() . "/"; - + $item = test::random_photo(); try { - $item->rename($new_name)->save(); - } catch (Exception $e) { - // pass + $item->name = test::random_name() . "/"; + $item->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal(array("name" => "no_slashes"), $e->validation->errors()); return; } - $this->assert_false(true, "Item_Model::rename should not accept / characters"); + $this->assert_true(false, "Shouldn't get here"); } public function item_rename_fails_with_existing_name_test() { // Create a test photo - $item = self::_create_random_item(); - $item2 = self::_create_random_item(); - - $new_name = $item2->name; + $item = test::random_photo(); + $item2 = test::random_photo(); try { - $item->rename($new_name)->save(); - } catch (Exception $e) { - // pass - $this->assert_true(strpos($e->getMessage(), "INVALID_RENAME_FILE_EXISTS") !== false, - "incorrect exception."); + $item->name = $item2->name; + $item->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_true(in_array("conflict", $e->validation->errors())); return; } - $this->assert_false(true, "Item_Model::rename should fail."); - } - public function save_original_values_test() { - $item = self::_create_random_item(); - $item->title = "ORIGINAL_VALUE"; - $item->save(); - $item->title = "NEW_VALUE"; - - $this->assert_same("ORIGINAL_VALUE", $item->original()->title); - $this->assert_same("NEW_VALUE", $item->title); - } - - public function urls_are_rawurlencoded_test() { - $item = self::_create_random_item(); - $item->slug = "foo bar"; - $item->name = "foo bar.jpg"; - $item->save(); - - $this->assert_equal("foo%20bar", $item->relative_url()); - $this->assert_equal("foo%20bar.jpg", $item->relative_path()); + $this->assert_false(true, "rename should conflict"); } public function move_album_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $album2 = album::create($root, rand(), rand(), rand()); - $album = album::create($album2, rand(), rand(), rand()); - $photo = self::_create_random_item($album); + $album2 = test::random_album(); + $album1 = test::random_album($album2); + $photo = test::random_photo($album1); file_put_contents($photo->thumb_path(), "thumb"); file_put_contents($photo->resize_path(), "resize"); file_put_contents($photo->file_path(), "file"); // Now move the album - $album->move_to($root); + $album1->parent_id = item::root()->id; + $album1->save(); $photo->reload(); // Expected: - // * the album dirs are all moved + // * album is not inside album2 anymore // * the photo's paths are all inside the albums paths // * the photo files are all still intact and accessible - $this->assert_same(0, strpos($photo->file_path(), $album->file_path())); - $this->assert_same(0, strpos($photo->thumb_path(), dirname($album->thumb_path()))); - $this->assert_same(0, strpos($photo->resize_path(), dirname($album->resize_path()))); + $this->assert_false(test::starts_with($album2->file_path(), $album1->file_path())); + $this->assert_true(test::starts_with($photo->file_path(), $album1->file_path())); + $this->assert_true(test::starts_with($photo->thumb_path(), dirname($album1->thumb_path()))); + $this->assert_true(test::starts_with($photo->resize_path(), dirname($album1->resize_path()))); $this->assert_equal("thumb", file_get_contents($photo->thumb_path())); $this->assert_equal("resize", file_get_contents($photo->resize_path())); @@ -212,71 +178,184 @@ class Item_Model_Test extends Unit_Test_Case { } public function move_photo_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $album2 = album::create($root, rand(), rand(), rand()); - $album = album::create($album2, rand(), rand(), rand()); - $photo = self::_create_random_item($album); + $album1 = test::random_album(); + $photo = test::random_photo($album1); + + $album2 = test::random_album(); file_put_contents($photo->thumb_path(), "thumb"); file_put_contents($photo->resize_path(), "resize"); file_put_contents($photo->file_path(), "file"); - // Now move the album - $photo->move_to($album2); - $photo->reload(); + // Now move the photo + $photo->parent_id = $album2->id; + $photo->save(); // Expected: - // * the album dirs are all moved - // * the photo's paths are all inside the albums paths + // * the photo's paths are inside the album2 not album1 // * the photo files are all still intact and accessible - $this->assert_same(0, strpos($photo->file_path(), $album->file_path())); - $this->assert_same(0, strpos($photo->thumb_path(), dirname($album->thumb_path()))); - $this->assert_same(0, strpos($photo->resize_path(), dirname($album->resize_path()))); + $this->assert_true(test::starts_with($photo->file_path(), $album2->file_path())); + $this->assert_true(test::starts_with($photo->thumb_path(), dirname($album2->thumb_path()))); + $this->assert_true(test::starts_with($photo->resize_path(), dirname($album2->resize_path()))); $this->assert_equal("thumb", file_get_contents($photo->thumb_path())); $this->assert_equal("resize", file_get_contents($photo->resize_path())); $this->assert_equal("file", file_get_contents($photo->file_path())); } - public function move_album_fails_invalid_target_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $name = rand(); - $album = album::create($root, $name, $name, $name); - $source = album::create($album, $name, $name, $name); + public function move_album_fails_conflicting_target_test() { + $album = test::random_album(); + $source = test::random_album_unsaved($album); + $source->name = $album->name; + $source->save(); + + // $source and $album have the same name, so if we move $source into the root they should + // conflict. try { - $source->move_to($root); - } catch (Exception $e) { - // pass - $this->assert_true(strpos($e->getMessage(), "INVALID_MOVE_TARGET_EXISTS") !== false, - "incorrect exception."); + $source->parent_id = item::root()->id; + $source->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal( + array("name" => "conflict", "slug" => "conflict"), $e->validation->errors()); return; } + $this->assert_true(false, "Shouldn't get here"); + } + + public function move_album_fails_wrong_target_type_test() { + $album = test::random_album(); + $photo = test::random_photo(); - $this->assert_false(true, "Item_Model::rename should not accept / characters"); + // $source and $album have the same name, so if we move $source into the root they should + // conflict. + + try { + $album->parent_id = $photo->id; + $album->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal(array("parent_id" => "invalid"), $e->validation->errors()); + return; + } + $this->assert_true(false, "Shouldn't get here"); } - public function move_photo_fails_invalid_target_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $photo_name = rand(); - $photo1 = self::_create_random_item($root, $photo_name); - $name = rand(); - $album = album::create($root, $name, $name, $name); - $photo2 = self::_create_random_item($album, $photo_name); + public function move_photo_fails_conflicting_target_test() { + $photo1 = test::random_photo(); + $album = test::random_album(); + $photo2 = test::random_photo_unsaved($album); + $photo2->name = $photo1->name; + $photo2->save(); + + // $photo1 and $photo2 have the same name, so if we move $photo1 into the root they should + // conflict. try { - $photo2->move_to($root); + $photo2->parent_id = item::root()->id; + $photo2->save(); } catch (Exception $e) { // pass - $this->assert_true(strpos($e->getMessage(), "INVALID_MOVE_TARGET_EXISTS") !== false, - "incorrect exception."); + $this->assert_equal( + array("name" => "conflict", "slug" => "conflict"), $e->validation->errors()); + return; + } + $this->assert_true(false, "Shouldn't get here"); + } + + public function move_album_inside_descendent_fails_test() { + $album1 = test::random_album(); + $album2 = test::random_album($album1); + $album3 = test::random_album($album2); + + try { + $album1->parent_id = $album3->id; + $album1->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal(array("parent_id" => "invalid"), $e->validation->errors()); + return; + } + $this->assert_true(false, "Shouldn't get here"); + } + + + public function basic_validation_test() { + $item = ORM::factory("item"); + $item->album_cover_item_id = rand(); // invalid + $item->description = str_repeat("x", 70000); // invalid + $item->name = null; + $item->parent_id = rand(); + $item->slug = null; + $item->sort_column = "bogus"; + $item->sort_order = "bogus"; + $item->title = null; + $item->type = "bogus"; + try { + $item->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_same(array("description" => "length", + "name" => "required", + "slug" => "required", + "title" => "required", + "album_cover_item_id" => "invalid_item", + "parent_id" => "invalid", + "sort_column" => "invalid", + "sort_order" => "invalid", + "type" => "invalid"), + $e->validation->errors()); return; } - $this->assert_false(true, "Item_Model::rename should not accept / characters"); + $this->assert_false(true, "Shouldn't get here"); + } + + public function slug_is_url_safe_test() { + try { + $album = test::random_album_unsaved(); + $album->slug = "illegal chars! !@#@#$!@~"; + $album->save(); + $this->assert_true(false, "Shouldn't be able to save"); + } catch (ORM_Validation_Exception $e) { + $this->assert_same(array("slug" => "not_url_safe"), $e->validation->errors()); + } + + // This should work + $album->slug = "the_quick_brown_fox"; + $album->save(); + } + + public function cant_change_item_type_test() { + $photo = test::random_photo(); + try { + $photo->type = "movie"; + $photo->mime_type = "video/x-flv"; + $photo->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_same(array("type" => "read_only"), $e->validation->errors()); + return; // pass + } + $this->assert_true(false, "Shouldn't get here"); + } + + public function cant_delete_root_album_test() { + try { + item::root()->delete(); + } catch (ORM_Validation_Exception $e) { + $this->assert_same(array("id" => "cant_delete_root_album"), $e->validation->errors()); + return; // pass + } + $this->assert_true(false, "Shouldn't get here"); + } + + public function as_restful_array_test() { + $album = test::random_album(); + $photo = test::random_photo($album); + $album->reload(); + + $result = $album->as_restful_array(); + $this->assert_same(rest::url("item", item::root()), $result["parent"]); + $this->assert_same(rest::url("item", $photo), $result["album_cover"]); + $this->assert_true(!array_key_exists("parent_id", $result)); + $this->assert_true(!array_key_exists("album_cover_item_id", $result)); } } diff --git a/modules/gallery/tests/Item_Rest_Helper_Test.php b/modules/gallery/tests/Item_Rest_Helper_Test.php new file mode 100644 index 00000000..6d1dd864 --- /dev/null +++ b/modules/gallery/tests/Item_Rest_Helper_Test.php @@ -0,0 +1,241 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Item_Rest_Helper_Test extends Gallery_Unit_Test_Case { + public function resolve_test() { + $album = test::random_album(); + $resolved = rest::resolve(rest::url("item", $album)); + $this->assert_equal($album->id, $resolved->id); + } + + public function get_scope_test() { + $album1 = test::random_album(); + $photo1 = test::random_photo($album1); + $album2 = test::random_album($album1); + $photo2 = test::random_photo($album2); + $album1->reload(); + + // No scope is the same as "direct" + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $this->assert_equal_array( + array("url" => rest::url("item", $album1), + "entity" => $album1->as_restful_array(), + "members" => array( + rest::url("item", $photo1), + rest::url("item", $album2)), + "relationships" => array( + "tags" => array( + "url" => rest::url("item_tags", $album1), + "members" => array()))), + item_rest::get($request)); + + $request->url = rest::url("item", $album1); + $request->params->scope = "direct"; + $this->assert_equal_array( + array("url" => rest::url("item", $album1), + "entity" => $album1->as_restful_array(), + "members" => array( + rest::url("item", $photo1), + rest::url("item", $album2)), + "relationships" => array( + "tags" => array( + "url" => rest::url("item_tags", $album1), + "members" => array()))), + item_rest::get($request)); + + $request->url = rest::url("item", $album1); + $request->params->scope = "all"; + $this->assert_equal_array( + array("url" => rest::url("item", $album1), + "entity" => $album1->as_restful_array(), + "members" => array( + rest::url("item", $photo1), + rest::url("item", $album2), + rest::url("item", $photo2)), + "relationships" => array( + "tags" => array( + "url" => rest::url("item_tags", $album1), + "members" => array()))), + item_rest::get($request)); + } + + public function get_children_like_test() { + $album1 = test::random_album(); + $photo1 = test::random_photo($album1); + $photo2 = test::random_photo_unsaved($album1); + $photo2->name = "foo.jpg"; + $photo2->save(); + $album1->reload(); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->name = "foo"; + $this->assert_equal_array( + array("url" => rest::url("item", $album1), + "entity" => $album1->as_restful_array(), + "members" => array( + rest::url("item", $photo2)), + "relationships" => array( + "tags" => array( + "url" => rest::url("item_tags", $album1), + "members" => array()))), + item_rest::get($request)); + } + + public function get_children_type_test() { + $album1 = test::random_album(); + $photo1 = test::random_photo($album1); + $album2 = test::random_album($album1); + $album1->reload(); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->type = "album"; + $this->assert_equal_array( + array("url" => rest::url("item", $album1), + "entity" => $album1->as_restful_array(), + "members" => array( + rest::url("item", $album2)), + "relationships" => array( + "tags" => array( + "url" => rest::url("item_tags", $album1), + "members" => array() ))), + item_rest::get($request)); + } + + public function update_album_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->title = "my new title"; + + item_rest::put($request); + $this->assert_equal("my new title", $album1->reload()->title); + } + + public function update_album_illegal_value_fails_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->title = "my new title"; + $request->params->slug = "not url safe"; + + try { + item_rest::put($request); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal(array("slug" => "not_url_safe"), $e->validation->errors()); + return; + } + $this->assert_true(false, "Shouldn't get here"); + } + + public function add_album_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->type = "album"; + $request->params->name = "my album"; + $request->params->title = "my album"; + $response = item_rest::post($request); + $new_album = rest::resolve($response["url"]); + + $this->assert_true($new_album->is_album()); + $this->assert_equal($album1->id, $new_album->parent_id); + } + + public function add_album_illegal_value_fails_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->type = "album"; + $request->params->name = "my album"; + $request->params->title = "my album"; + $request->params->slug = "not url safe"; + + try { + item_rest::post($request); + } catch (ORM_Validation_Exception $e) { + $this->assert_equal(array("slug" => "not_url_safe"), $e->validation->errors()); + return; + } + $this->assert_true(false, "Shouldn't get here"); + } + + + public function add_photo_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + $request->params = new stdClass(); + $request->params->type = "photo"; + $request->params->name = "my photo.jpg"; + $request->file = MODPATH . "gallery/tests/test.jpg"; + $response = item_rest::post($request); + $new_photo = rest::resolve($response["url"]); + + $this->assert_true($new_photo->is_photo()); + $this->assert_equal($album1->id, $new_photo->parent_id); + } + + public function delete_album_test() { + $album1 = test::random_album(); + access::allow(identity::everybody(), "edit", $album1); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + item_rest::delete($request); + + $album1->reload(); + $this->assert_false($album1->loaded()); + } + + public function delete_album_fails_without_permission_test() { + $album1 = test::random_album(); + access::deny(identity::everybody(), "edit", $album1); + identity::set_active_user(identity::guest()); + + $request = new stdClass(); + $request->url = rest::url("item", $album1); + try { + item_rest::delete($request); + } catch (Exception $e) { + $this->assert_equal("@todo FORBIDDEN", $e->getMessage()); + return; + } + $this->assert_true(false, "Shouldn't get here"); + } +} diff --git a/modules/gallery/tests/Locales_Helper_Test.php b/modules/gallery/tests/Locales_Helper_Test.php index 4c03d8d4..a2680928 100644 --- a/modules/gallery/tests/Locales_Helper_Test.php +++ b/modules/gallery/tests/Locales_Helper_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Locales_Helper_Test extends Unit_Test_Case { +class Locales_Helper_Test extends Gallery_Unit_Test_Case { static $installed_locales; static $default_locale; diff --git a/modules/gallery/tests/Menu_Test.php b/modules/gallery/tests/Menu_Test.php index c91aee0b..643aa727 100644 --- a/modules/gallery/tests/Menu_Test.php +++ b/modules/gallery/tests/Menu_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Menu_Test extends Unit_Test_Case { +class Menu_Test extends Gallery_Unit_Test_Case { public function find_menu_item_test() { $menu = new Menu(true); $menu diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php deleted file mode 100644 index 23544934..00000000 --- a/modules/gallery/tests/Movie_Helper_Test.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class Movie_Helper_Test extends Unit_Test_Case { - public function create_movie_shouldnt_allow_names_with_slash_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $movie = movie::create($root, MODPATH . "gallery/tests/test.flv", "$rand/.flv", $rand, $rand); - } catch (Exception $e) { - // pass - return; - } - - $this->assert_true(false, "Shouldn't create a movie with / in the name"); - } - - public function create_movie_shouldnt_allow_names_with_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $movie = movie::create($root, MODPATH . "gallery/tests/test.flv", "$rand.flv.", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create a movie with trailing . in the name"); - } - - public function create_movie_creates_reasonable_slug_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $movie = movie::create( - $album, MODPATH . "gallery/tests/test.flv", "This (is) my file%name.flv", $rand, $rand); - - $this->assert_equal("This-is-my-file-name", $movie->slug); - } -} diff --git a/modules/gallery/tests/ORM_MPTT_Test.php b/modules/gallery/tests/ORM_MPTT_Test.php index 69b6bea9..5e741537 100644 --- a/modules/gallery/tests/ORM_MPTT_Test.php +++ b/modules/gallery/tests/ORM_MPTT_Test.php @@ -17,21 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class ORM_MPTT_Test extends Unit_Test_Case { - - private function create_item_and_add_to_parent($parent) { - $album = album::create($parent, rand(), "test album"); - return $album; - } +class ORM_MPTT_Test extends Gallery_Unit_Test_Case { public function add_to_parent_test() { - $root = ORM::factory("item", 1); - $album = ORM::factory("item"); - $album->type = "album"; - $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album->sort_column = "weight"; - $album->sort_order = "ASC"; - $album->add_to_parent($root); + $album = test::random_album(); $this->assert_equal($album->parent()->right_ptr - 2, $album->left_ptr); $this->assert_equal($album->parent()->right_ptr - 1, $album->right_ptr); @@ -40,12 +29,11 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function add_hierarchy_test() { - $root = ORM::factory("item", 1); - $album1 = self::create_item_and_add_to_parent($root); - $album1_1 = self::create_item_and_add_to_parent($album1); - $album1_2 = self::create_item_and_add_to_parent($album1); - $album1_1_1 = self::create_item_and_add_to_parent($album1_1); - $album1_1_2 = self::create_item_and_add_to_parent($album1_1); + $album1 = test::random_album(); + $album1_1 = test::random_album($album1); + $album1_2 = test::random_album($album1); + $album1_1_1 = test::random_album($album1_1); + $album1_1_2 = test::random_album($album1_1); $album1->reload(); $this->assert_equal(9, $album1->right_ptr - $album1->left_ptr); @@ -55,12 +43,11 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function delete_hierarchy_test() { - $root = ORM::factory("item", 1); - $album1 = self::create_item_and_add_to_parent($root); - $album1_1 = self::create_item_and_add_to_parent($album1); - $album1_2 = self::create_item_and_add_to_parent($album1); - $album1_1_1 = self::create_item_and_add_to_parent($album1_1); - $album1_1_2 = self::create_item_and_add_to_parent($album1_1); + $album1 = test::random_album(); + $album1_1 = test::random_album($album1); + $album1_2 = test::random_album($album1); + $album1_1_1 = test::random_album($album1_1); + $album1_1_2 = test::random_album($album1_1); $album1_1->delete(); $album1->reload(); @@ -70,17 +57,17 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function move_to_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, "move_to_test_1", "move_to_test_1"); - $album1_1 = album::create($album1, "move_to_test_1_1", "move_to_test_1_1"); - $album1_2 = album::create($album1, "move_to_test_1_2", "move_to_test_1_2"); - $album1_1_1 = album::create($album1_1, "move_to_test_1_1_1", "move_to_test_1_1_1"); - $album1_1_2 = album::create($album1_1, "move_to_test_1_1_2", "move_to_test_1_1_2"); + $album1 = test::random_album(); + $album1_1 = test::random_album($album1); + $album1_2 = test::random_album($album1); + $album1_1_1 = test::random_album($album1_1); + $album1_1_2 = test::random_album($album1_1); $album1_2->reload(); $album1_1_1->reload(); - $album1_1_1->move_to($album1_2); + $album1_1_1->parent_id = $album1_2->id; + $album1_1_1->save(); $album1_1->reload(); $album1_2->reload(); @@ -89,39 +76,38 @@ class ORM_MPTT_Test extends Unit_Test_Case { $this->assert_equal(3, $album1_2->right_ptr - $album1_2->left_ptr); $this->assert_equal( - array($album1_1_2->id => "move_to_test_1_1_2"), + array($album1_1_2->id => $album1_1_2->name), $album1_1->children()->select_list()); $this->assert_equal( - array($album1_1_1->id => "move_to_test_1_1_1"), + array($album1_1_1->id => $album1_1_1->name), $album1_2->children()->select_list()); } public function cant_move_parent_into_own_subtree_test() { - $album1 = album::create(item::root(), "move_to_test", "move_to_test"); - $album2 = album::create($album1, "move_to_test", "move_to_test"); - $album3 = album::create($album2, "move_to_test", "move_to_test"); + $album1 = test::random_album(item::root()); + $album2 = test::random_album($album1); + $album3 = test::random_album($album2); try { - $album1->move_to($album3); - $self->assert_true(false, "We should be unable to move an item inside its own hierarchy"); + $album1->parent_id = $album3->id; + $album1->save(); + $this->assert_true(false, "We should be unable to move an item inside its own hierarchy"); } catch (Exception $e) { // pass } } public function parent_test() { - $root = ORM::factory("item", 1); - $album = self::create_item_and_add_to_parent($root); + $album = test::random_album(); $parent = ORM::factory("item", 1); $this->assert_equal($parent->id, $album->parent()->id); } public function parents_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner = self::create_item_and_add_to_parent($outer); + $outer = test::random_album(); + $inner = test::random_album($outer); $parent_ids = array(); foreach ($inner->parents() as $parent) { @@ -131,10 +117,9 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function children_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); + $outer = test::random_album(); + $inner1 = test::random_album($outer); + $inner2 = test::random_album($outer); $child_ids = array(); foreach ($outer->children() as $child) { @@ -144,48 +129,27 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function children_limit_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); + $outer = test::random_album(); + $inner1 = test::random_album($outer); + $inner2 = test::random_album($outer); $this->assert_equal(array($inner2->id => $inner2->name), $outer->children(1, 1)->select_list('id')); } public function children_count_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); + $outer = test::random_album(); + $inner1 = test::random_album($outer); + $inner2 = test::random_album($outer); $this->assert_equal(2, $outer->children_count()); } public function descendant_test() { - $root = ORM::factory("item", 1); - - $parent = ORM::factory("item"); - $parent->type = "album"; - $parent->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $parent->sort_column = "weight"; - $parent->sort_order = "ASC"; - $parent->add_to_parent($root); - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->add_to_parent($parent); - - $album1 = ORM::factory("item"); - $album1->type = "album"; - $album1->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album1->sort_column = "weight"; - $album1->sort_order = "ASC"; - $album1->add_to_parent($parent); - - $photo1 = ORM::factory("item"); - $photo1->type = "photo"; - $photo1->add_to_parent($album1); + $parent = test::random_album(); + $photo = test::random_photo($parent); + $album1 = test::random_album($parent); + $photo1 = test::random_photo($album1); $parent->reload(); @@ -195,36 +159,20 @@ class ORM_MPTT_Test extends Unit_Test_Case { } public function descendant_limit_test() { - $root = ORM::factory("item", 1); - - $parent = self::create_item_and_add_to_parent($root); - $album1 = self::create_item_and_add_to_parent($parent); - $album2 = self::create_item_and_add_to_parent($parent); - $album3 = self::create_item_and_add_to_parent($parent); - + $parent = test::random_album(); + $album1 = test::random_album($parent); + $album2 = test::random_album($parent); + $album3 = test::random_album($parent); $parent->reload(); + $this->assert_equal(2, $parent->descendants(2)->count()); } public function descendant_count_test() { - $root = ORM::factory("item", 1); - - $parent = ORM::factory("item"); - $parent->type = "album"; - $parent->add_to_parent($root); - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->add_to_parent($parent); - - $album1 = ORM::factory("item"); - $album1->type = "album"; - $album1->add_to_parent($parent); - - $photo1 = ORM::factory("item"); - $photo1->type = "photo"; - $photo1->add_to_parent($album1); - + $parent = test::random_album(); + $photo = test::random_photo($parent); + $album1 = test::random_album($parent); + $photo1 = test::random_photo($album1); $parent->reload(); $this->assert_equal(3, $parent->descendants_count()); diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php deleted file mode 100644 index 97923f90..00000000 --- a/modules/gallery/tests/Photo_Helper_Test.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class Photo_Helper_Test extends Unit_Test_Case { - public function create_photo_test() { - $rand = rand(); - - $filename = MODPATH . "gallery/tests/test.jpg"; - $image_info = getimagesize($filename); - - $root = ORM::factory("item", 1); - $photo = photo::create($root, $filename, "$rand.jpg", $rand, $rand); - - $this->assert_equal(VARPATH . "albums/$rand.jpg", $photo->file_path()); - $this->assert_equal(VARPATH . "thumbs/{$rand}.jpg", $photo->thumb_path()); - $this->assert_equal(VARPATH . "resizes/{$rand}.jpg", $photo->resize_path()); - - $this->assert_true(is_file($photo->file_path()), "missing: {$photo->file_path()}"); - $this->assert_true(is_file($photo->resize_path()), "missing: {$photo->resize_path()}"); - $this->assert_true(is_file($photo->thumb_path()), "missing: {$photo->thumb_path()}"); - - $this->assert_equal($root->id, $photo->parent_id); // MPTT tests cover other hierarchy checks - $this->assert_equal("$rand.jpg", $photo->name); - $this->assert_equal($rand, $photo->title); - $this->assert_equal($rand, $photo->description); - $this->assert_equal("image/jpeg", $photo->mime_type); - $this->assert_equal($image_info[0], $photo->width); - $this->assert_equal($image_info[1], $photo->height); - - $this->assert_equal($photo->parent()->right_ptr - 2, $photo->left_ptr); - $this->assert_equal($photo->parent()->right_ptr - 1, $photo->right_ptr); - } - - public function create_conflicting_photo_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $photo1 = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - $photo2 = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - $this->assert_true($photo1->name != $photo2->name); - } - - public function create_photo_with_no_extension_test() { - $root = ORM::factory("item", 1); - try { - photo::create($root, "/tmp", "name", "title", "description"); - $this->assert_false("should fail with an exception"); - } catch (Exception $e) { - // pass - } - } - - public function thumb_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - $this->assert_equal("http://./var/thumbs/{$rand}.jpg?m={$photo->updated}", $photo->thumb_url()); - } - - public function resize_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $photo = photo::create($album, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - - $this->assert_equal( - "http://./var/resizes/{$rand}/{$rand}.jpg?m={$photo->updated}", $photo->resize_url()); - } - - public function file_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $photo = photo::create($album, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); - - $this->assert_equal( - "http://./var/albums/{$rand}/{$rand}.jpg?m={$photo->updated}", $photo->file_url()); - } - - public function create_photo_creates_reasonable_slug_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $photo = photo::create( - $album, MODPATH . "gallery/tests/test.jpg", "This (is) my file%name.jpg", $rand, $rand); - - $this->assert_equal("This-is-my-file-name", $photo->slug); - } - - public function create_photo_shouldnt_allow_names_with_slash_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand/.jpg", $rand, $rand); - } catch (Exception $e) { - // pass - return; - } - - $this->assert_true(false, "Shouldn't create a photo with / in the name"); - } - - public function create_photo_silently_trims_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg.", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create a photo with trailing . in the name"); - } -} diff --git a/modules/gallery/tests/Photos_Controller_Test.php b/modules/gallery/tests/Photos_Controller_Test.php index b6c6df47..50d49fcc 100644 --- a/modules/gallery/tests/Photos_Controller_Test.php +++ b/modules/gallery/tests/Photos_Controller_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Photos_Controller_Test extends Unit_Test_Case { +class Photos_Controller_Test extends Gallery_Unit_Test_Case { public function setup() { $this->_save = array($_POST, $_SERVER); $_SERVER["HTTP_REFERER"] = "HTTP_REFERER"; @@ -29,19 +29,14 @@ class Photos_Controller_Test extends Unit_Test_Case { public function change_photo_test() { $controller = new Photos_Controller(); - $root = ORM::factory("item", 1); - $photo = photo::create( - $root, MODPATH . "gallery/tests/test.jpg", "test.jpeg", - "test", "test", identity::active_user()->id, "slug"); - $orig_name = $photo->name; + $photo = test::random_photo(); - $_POST["filename"] = "test.jpeg"; - $_POST["name"] = "new name"; + $_POST["name"] = "new name.jpg"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; $_POST["slug"] = "new-slug"; $_POST["csrf"] = access::csrf_token(); - access::allow(identity::everybody(), "edit", $root); + access::allow(identity::everybody(), "edit", item::root()); ob_start(); $controller->update($photo->id); @@ -53,26 +48,25 @@ class Photos_Controller_Test extends Unit_Test_Case { $this->assert_equal("new-slug", $photo->slug); $this->assert_equal("new title", $photo->title); $this->assert_equal("new description", $photo->description); - - // We don't change the name, yet. - $this->assert_equal($orig_name, $photo->name); + $this->assert_equal("new name.jpg", $photo->name); } public function change_photo_no_csrf_fails_test() { $controller = new Photos_Controller(); - $root = ORM::factory("item", 1); - $photo = photo::create( - $root, MODPATH . "gallery/tests/test.jpg", "test.jpg", "test", "test"); - $_POST["name"] = "new name"; + $photo = test::random_photo(); + + $_POST["name"] = "new name.jpg"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; - access::allow(identity::everybody(), "edit", $root); + $_POST["slug"] = "new slug"; + access::allow(identity::everybody(), "edit", item::root()); try { - $controller->_update($photo); + $controller->update($photo); $this->assert_true(false, "This should fail"); } catch (Exception $e) { // pass + $this->assert_same("@todo FORBIDDEN", $e->getMessage()); } } } diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php index 2c07d934..7002a874 100644 --- a/modules/gallery/tests/SafeString_Test.php +++ b/modules/gallery/tests/SafeString_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class SafeString_Test extends Unit_Test_Case { +class SafeString_Test extends Gallery_Unit_Test_Case { public function toString_escapes_for_html_test() { $safe_string = new SafeString("hello <p>world</p>"); $this->assert_equal("hello <p>world</p>", diff --git a/modules/gallery/tests/Sendmail_Test.php b/modules/gallery/tests/Sendmail_Test.php index f3a8d897..bc57e434 100644 --- a/modules/gallery/tests/Sendmail_Test.php +++ b/modules/gallery/tests/Sendmail_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Sendmail_Test extends Unit_Test_Case { +class Sendmail_Test extends Gallery_Unit_Test_Case { public function setup() { Kohana_Config::instance()->set("sendmail.from", "from@gallery3.com"); } diff --git a/modules/gallery/tests/Url_Security_Test.php b/modules/gallery/tests/Url_Security_Test.php index de25880f..255b3909 100644 --- a/modules/gallery/tests/Url_Security_Test.php +++ b/modules/gallery/tests/Url_Security_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Url_Security_Test extends Unit_Test_Case { +class Url_Security_Test extends Gallery_Unit_Test_Case { public function setup() { $this->save = array(Router::$current_uri, Router::$complete_uri, $_GET); } diff --git a/modules/gallery/tests/Var_Test.php b/modules/gallery/tests/Var_Test.php index 355d94a7..fb19da7a 100644 --- a/modules/gallery/tests/Var_Test.php +++ b/modules/gallery/tests/Var_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Var_Test extends Unit_Test_Case { +class Var_Test extends Gallery_Unit_Test_Case { public function add_parameter_test() { module::set_var("gallery", "Parameter", "original value"); $this->assert_equal("original value", module::get_var("gallery", "Parameter")); diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php index b296d97c..a39a069d 100644 --- a/modules/gallery/tests/Xss_Security_Test.php +++ b/modules/gallery/tests/Xss_Security_Test.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Xss_Security_Test extends Unit_Test_Case { +class Xss_Security_Test extends Gallery_Unit_Test_Case { public function find_unescaped_variables_in_views_test() { $found = array(); foreach (glob("*/*/views/*.php") as $view) { diff --git a/modules/gallery/tests/controller_auth_data.txt b/modules/gallery/tests/controller_auth_data.txt index da7108d8..0aa26057 100644 --- a/modules/gallery/tests/controller_auth_data.txt +++ b/modules/gallery/tests/controller_auth_data.txt @@ -19,12 +19,12 @@ modules/gallery/controllers/quick.php form_edit modules/gallery/controllers/simple_uploader.php start DIRTY_AUTH modules/gallery/controllers/simple_uploader.php finish DIRTY_AUTH modules/gallery/controllers/upgrader.php index DIRTY_AUTH -modules/gallery/controllers/user_profile.php show DIRTY_CSRF|DIRTY_AUTH +modules/gallery/controllers/user_profile.php show DIRTY_AUTH modules/gallery/controllers/user_profile.php contact DIRTY_AUTH modules/gallery/controllers/user_profile.php send DIRTY_AUTH modules/gallery/controllers/welcome_message.php index DIRTY_AUTH -modules/rest/controllers/rest.php access_key DIRTY_CSRF|DIRTY_AUTH -modules/rest/controllers/rest.php __call DIRTY_AUTH +modules/rest/controllers/rest.php index DIRTY_CSRF|DIRTY_AUTH +modules/rest/controllers/rest.php __call DIRTY_CSRF|DIRTY_AUTH modules/rss/controllers/rss.php feed DIRTY_CSRF|DIRTY_AUTH modules/search/controllers/search.php index DIRTY_CSRF|DIRTY_AUTH modules/server_add/controllers/admin_server_add.php autocomplete DIRTY_CSRF diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index a89725c0..65b45a08 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -34,19 +34,21 @@ modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $chi modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $child->thumb_width modules/comment/views/comments.html.php 16 DIRTY_ATTR $comment->id modules/comment/views/comments.html.php 19 DIRTY_ATTR $comment->author()->avatar_url(40,$theme->url(,true)) +modules/comment/views/user_profile_comments.html.php 5 DIRTY_ATTR $comment->id +modules/comment/views/user_profile_comments.html.php 10 DIRTY_JS $comment->item()->url() +modules/comment/views/user_profile_comments.html.php 11 DIRTY $comment->item()->thumb_img(array(),50) modules/digibug/views/digibug_form.html.php 4 DIRTY form::open("http://www.digibug.com/dapi/order.php") -modules/digibug/views/digibug_form.html.php 5 DIRTY form::hidden($order_parms) -modules/digibug/views/digibug_form.html.php 6 DIRTY form::close() +modules/digibug/views/digibug_form.html.php 6 DIRTY form::hidden($key,$value) modules/exif/views/exif_dialog.html.php 14 DIRTY $details[$i]["caption"] modules/exif/views/exif_dialog.html.php 21 DIRTY $details[$i]["caption"] modules/g2_import/views/admin_g2_import.html.php 30 DIRTY $form modules/gallery/views/admin_advanced_settings.html.php 21 DIRTY_ATTR text::alternate("g-odd","g-even") modules/gallery/views/admin_advanced_settings.html.php 22 DIRTY $var->module_name modules/gallery/views/admin_block_log_entries.html.php 4 DIRTY_ATTR log::severity_class($entry->severity) -modules/gallery/views/admin_block_log_entries.html.php 5 DIRTY_JS user_profile::url($entryr->id) -modules/gallery/views/admin_block_log_entries.html.php 6 DIRTY gallery::date_time($entry->timestamp) -modules/gallery/views/admin_block_log_entries.html.php 7 DIRTY $entry->message -modules/gallery/views/admin_block_log_entries.html.php 8 DIRTY $entry->html +modules/gallery/views/admin_block_log_entries.html.php 8 DIRTY_JS user_profile::url($entry->user->id) +modules/gallery/views/admin_block_log_entries.html.php 10 DIRTY gallery::date_time($entry->timestamp) +modules/gallery/views/admin_block_log_entries.html.php 11 DIRTY $entry->message +modules/gallery/views/admin_block_log_entries.html.php 12 DIRTY $entry->html modules/gallery/views/admin_block_news.html.php 5 DIRTY_JS $entry["link"] modules/gallery/views/admin_block_news.html.php 5 DIRTY $entry["title"] modules/gallery/views/admin_block_news.html.php 7 DIRTY text::limit_words(strip_tags($entry["description"]),25); @@ -94,13 +96,11 @@ modules/gallery/views/admin_maintenance.html.php 158 DIRTY $task- modules/gallery/views/admin_maintenance_show_log.html.php 8 DIRTY_JS url::site("admin/maintenance/save_log/$task->id?csrf=$csrf") modules/gallery/views/admin_maintenance_show_log.html.php 13 DIRTY $task->name modules/gallery/views/admin_maintenance_task.html.php 55 DIRTY $task->name -modules/gallery/views/admin_modules.html.php 25 DIRTY_JS t("Continue") -modules/gallery/views/admin_modules.html.php 35 DIRTY_JS t("Continue") modules/gallery/views/admin_modules.html.php 51 DIRTY access::csrf_form_field() modules/gallery/views/admin_modules.html.php 60 DIRTY_ATTR text::alternate("g-odd","g-even") modules/gallery/views/admin_modules.html.php 63 DIRTY form::checkbox($data,'1',module::is_active($module_name)) modules/gallery/views/admin_modules.html.php 65 DIRTY $module_info->version -modules/gallery/views/admin_modules_confirm.html.php 11 DIRTY_ATTR $class +modules/gallery/views/admin_modules_confirm.html.php 11 DIRTY_ATTR $css_class modules/gallery/views/admin_modules_confirm.html.php 11 DIRTY $message modules/gallery/views/admin_modules_confirm.html.php 16 DIRTY access::csrf_form_field() modules/gallery/views/admin_modules_confirm.html.php 18 DIRTY form::hidden($module,1) @@ -124,11 +124,9 @@ modules/gallery/views/form_uploadify.html.php 30 DIRTY_JS url::f modules/gallery/views/form_uploadify.html.php 31 DIRTY_JS url::site("simple_uploader/add_photo/{$album->id}") modules/gallery/views/form_uploadify.html.php 35 DIRTY_JS url::file("lib/uploadify/cancel.png") modules/gallery/views/form_uploadify.html.php 36 DIRTY_JS $simultaneous_upload_limit -modules/gallery/views/form_uploadify.html.php 61 DIRTY_JS t("Completed") -modules/gallery/views/in_place_edit.html.php 2 DIRTY form::open($action,array("method"=>"post","id"=>"g-in-place-edit-form","class"=>"g-short-form"),$hidden) -modules/gallery/views/in_place_edit.html.php 5 DIRTY form::input("input",$form["input"]," class=\"textbox\"") -modules/gallery/views/in_place_edit.html.php 12 DIRTY form::close() -modules/gallery/views/in_place_edit.html.php 14 DIRTY $errors["input"] +modules/gallery/views/in_place_edit.html.php 2 DIRTY form::open($action,array("method"=>"post","id"=>"g-in-place-edit-form","class"=>"g-short-form")) +modules/gallery/views/in_place_edit.html.php 3 DIRTY access::csrf_form_field() +modules/gallery/views/in_place_edit.html.php 6 DIRTY form::input("input",$form["input"]," class=\"textbox\"") modules/gallery/views/kohana_error_page.php 102 DIRTY $message modules/gallery/views/kohana_error_page.php 116 DIRTY $trace modules/gallery/views/kohana_profiler.php 32 DIRTY $profile->render(); @@ -219,10 +217,8 @@ modules/gallery/views/upgrader.html.php 77 DIRTY $modul modules/gallery/views/upgrader.html.php 99 DIRTY_ATTR $done?"muted":"" modules/gallery/views/upgrader.html.php 102 DIRTY_ATTR $done?"muted":"" modules/gallery/views/user_languages_block.html.php 2 DIRTY form::dropdown("g-select-session-locale",$installed_locales,$selected) -modules/gallery/views/user_profile.html.php 35 DIRTY_ATTR $height -modules/gallery/views/user_profile.html.php 44 DIRTY $field -modules/gallery/views/user_profile.html.php 45 DIRTY $value -modules/gallery/views/user_profile.html.php 65 DIRTY_JS $return->for_html_attr() +modules/gallery/views/user_profile.html.php 36 DIRTY_ATTR $user->avatar_url(40,$theme->url(,true)) +modules/gallery/views/user_profile.html.php 47 DIRTY $info->view modules/image_block/views/image_block_block.html.php 3 DIRTY_JS $item->url() modules/image_block/views/image_block_block.html.php 4 DIRTY $item->thumb_img(array("class"=>"g-thumbnail")) modules/info/views/info_block.html.php 22 DIRTY date("M j, Y H:i:s",$item->captured) @@ -235,6 +231,8 @@ modules/notification/views/item_deleted.html.php 18 DIRTY_JS $item- modules/notification/views/item_deleted.html.php 19 DIRTY $item->parent()->abs_url() modules/notification/views/item_updated.html.php 20 DIRTY_JS $item->abs_url() modules/notification/views/item_updated.html.php 20 DIRTY $item->abs_url() +modules/notification/views/user_profile_notification.html.php 5 DIRTY_ATTR $subscription->id +modules/notification/views/user_profile_notification.html.php 6 DIRTY_JS $subscription->url modules/organize/views/organize_dialog.html.php 3 DIRTY_JS url::site("organize/move_to/__ALBUM_ID__?csrf=$csrf") modules/organize/views/organize_dialog.html.php 4 DIRTY_JS url::site("organize/rearrange/__TARGET_ID__/__BEFORE__?csrf=$csrf") modules/organize/views/organize_dialog.html.php 5 DIRTY_JS url::site("organize/sort_order/__ALBUM_ID__/__COL__/__DIR__?csrf=$csrf") diff --git a/modules/gallery/views/admin_block_log_entries.html.php b/modules/gallery/views/admin_block_log_entries.html.php index 453724cb..5a8ed23c 100644 --- a/modules/gallery/views/admin_block_log_entries.html.php +++ b/modules/gallery/views/admin_block_log_entries.html.php @@ -2,7 +2,11 @@ <ul> <? foreach ($entries as $entry): ?> <li class="<?= log::severity_class($entry->severity) ?>" style="direction: ltr"> + <? if ($entry->user->guest): ?> + </span><?= html::clean($entry->user->name) ?></span> + <? else: ?> <a href="<?= user_profile::url($entry->user->id) ?>"><?= html::clean($entry->user->name) ?></a> + <? endif ?> <?= gallery::date_time($entry->timestamp) ?> <?= $entry->message ?> <?= $entry->html ?> diff --git a/modules/gallery/views/admin_modules.html.php b/modules/gallery/views/admin_modules.html.php index 26b2c87c..c5015e68 100644 --- a/modules/gallery/views/admin_modules.html.php +++ b/modules/gallery/views/admin_modules.html.php @@ -22,7 +22,7 @@ buttons: { <?= t("Continue")->for_js() ?>: function() { $("form", this).submit(); - $(".ui-dialog-buttonpane button:contains(<?= t("Continue") ?>)") + $(".ui-dialog-buttonpane button:contains(" + <?= t("Continue")->for_js() ?> + ")") .attr("disabled", "disabled") .addClass("ui-state-disabled"); }, @@ -32,7 +32,7 @@ } }); if (!data.allow_continue) { - $(".ui-dialog-buttonpane button:contains(<?= t("Continue") ?>)") + $(".ui-dialog-buttonpane button:contains(" + <?= t("Continue")->for_js() ?> + ")") .attr("disabled", "disabled") .addClass("ui-state-disabled"); } diff --git a/modules/gallery/views/admin_modules_confirm.html.php b/modules/gallery/views/admin_modules_confirm.html.php index 59592505..8c4cb2bd 100644 --- a/modules/gallery/views/admin_modules_confirm.html.php +++ b/modules/gallery/views/admin_modules_confirm.html.php @@ -6,9 +6,9 @@ <div id="g-admin-modules-messages" class="g-block-content"> <ul> - <? foreach (array("error" => "g-error", "warn" => "g-warning") as $type => $class): ?> + <? foreach (array("error" => "g-error", "warn" => "g-warning") as $type => $css_class): ?> <? foreach ($messages[$type] as $message): ?> - <li class="<?= $class ?>" style="padding-bottom: 0"><?= $message ?></li> + <li class="<?= $css_class ?>" style="padding-bottom: 0"><?= $message ?></li> <? endforeach ?> <? endforeach ?> </ul> diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php index b3b81ecb..137cb353 100644 --- a/modules/gallery/views/form_uploadify.html.php +++ b/modules/gallery/views/form_uploadify.html.php @@ -58,7 +58,7 @@ "<li class=\"g-error\">" + fileObj.name + " - " + msg[1] + "</li>"); } else { $("#g-add-photos-status ul").append( - "<li class=\"g-success\">" + fileObj.name + " - <?= t("Completed") ?></li>"); + "<li class=\"g-success\">" + fileObj.name + " - " + <?= t("Completed")->for_js() ?> + "</li>"); } return true; }, diff --git a/modules/gallery/views/in_place_edit.html.php b/modules/gallery/views/in_place_edit.html.php index 45cf1d8c..05a16ad4 100644 --- a/modules/gallery/views/in_place_edit.html.php +++ b/modules/gallery/views/in_place_edit.html.php @@ -1,5 +1,6 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<?= form::open($action, array("method" => "post", "id" => "g-in-place-edit-form", "class" => "g-short-form"), $hidden) ?> +<?= form::open($action, array("method" => "post", "id" => "g-in-place-edit-form", "class" => "g-short-form")) ?> + <?= access::csrf_form_field() ?> <ul> <li<? if (!empty($errors["input"])): ?> class="g-error"<? endif ?>> <?= form::input("input", $form["input"], " class=\"textbox\"") ?> @@ -9,8 +10,6 @@ </li> <li><a href="#" class="g-cancel"><?= t("Cancel") ?></a></li> </ul> -<?= form::close() ?> -<? if (!empty($errors["input"])): ?> -<div id="g-in-place-edit-message" class="g-error"><?= $errors["input"] ?></div> -<? endif ?> +</form> + diff --git a/modules/gallery/views/kohana/error.php b/modules/gallery/views/kohana/error.php index 7271db14..26628cf2 100644 --- a/modules/gallery/views/kohana/error.php +++ b/modules/gallery/views/kohana/error.php @@ -1,5 +1,6 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <? $error_id = uniqid("error") ?> +<? if (!function_exists("t")) { function t($msg) { return $msg; } } ?> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <style type="text/css"> @@ -131,7 +132,7 @@ </head> <body> <? try { $user = identity::active_user(); } catch (Exception $e) { } ?> - <? $admin = php_sapi_name() == "cli" || isset($user) && $user->admin ?> + <? $admin = php_sapi_name() == "cli" || (class_exists("User_Model") && isset($user) && $user->admin) ?> <div class="big_box" id="framework_error"> <h1> <?= t("Dang... Something went wrong!") ?> diff --git a/modules/gallery/views/user_languages_block.html.php b/modules/gallery/views/user_languages_block.html.php index 89185967..3776ca13 100644 --- a/modules/gallery/views/user_languages_block.html.php +++ b/modules/gallery/views/user_languages_block.html.php @@ -1,7 +1,7 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <?= form::dropdown("g-select-session-locale", $installed_locales, $selected) ?> <script type="text/javascript"> - $("#g-select-session-locale").change(function() { + $("select[name=g-select-session-locale]").change(function() { var old_locale_preference = <?= html::js_string($selected) ?>; var locale = $(this).val(); if (old_locale_preference == locale) { diff --git a/modules/gallery/views/user_profile.html.php b/modules/gallery/views/user_profile.html.php index 708b1613..f35f8c3f 100644 --- a/modules/gallery/views/user_profile.html.php +++ b/modules/gallery/views/user_profile.html.php @@ -26,6 +26,7 @@ $(document).ready(function() { $("#g-profile-return").click(function(event) { history.go(-1); + return false; }) }); </script> @@ -41,7 +42,7 @@ <? foreach ($info_parts as $info): ?> <div> <fieldset> - <label><?= $info->title ?></label> + <label><?= html::purify($info->title) ?></label> <div> <?= $info->view ?> </div> diff --git a/modules/gallery/views/user_profile_info.html.php b/modules/gallery/views/user_profile_info.html.php index 2a2549c8..58e134bb 100644 --- a/modules/gallery/views/user_profile_info.html.php +++ b/modules/gallery/views/user_profile_info.html.php @@ -1,9 +1,9 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <table> - <? foreach ($fields as $field => $value): ?> + <? foreach ($user_profile_data as $label => $value): ?> <tr> - <td><?= $field ?></td> - <td><?= $value ?></td> + <td><?= html::clean($label) ?></td> + <td><?= html::purify($value) ?></td> </tr> <? endforeach ?> </table> diff --git a/modules/gallery_unit_test/controllers/gallery_unit_test.php b/modules/gallery_unit_test/controllers/gallery_unit_test.php index 498dd756..2690ad24 100644 --- a/modules/gallery_unit_test/controllers/gallery_unit_test.php +++ b/modules/gallery_unit_test/controllers/gallery_unit_test.php @@ -18,11 +18,15 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Gallery_Unit_Test_Controller extends Controller { - function Index() { + function index() { if (!TEST_MODE) { throw new Kohana_404_Exception(); } + // Force strict behavior to flush out bugs early + ini_set("display_errors", true); + error_reporting(-1); + // Jump through some hoops to satisfy the way that we check for the site_domain in // config.php. We structure this such that the code in config will leave us with a // site_domain of "." (for historical reasons) @@ -127,6 +131,12 @@ class Gallery_Unit_Test_Controller extends Controller { $filter = count($_SERVER["argv"]) > 2 ? $_SERVER["argv"][2] : null; print new Unit_Test($modules, $filter); + } catch (ORM_Validation_Exception $e) { + print "Validation Exception: {$e->getMessage()}\n"; + print $e->getTraceAsString() . "\n"; + foreach ($e->validation->errors() as $field => $msg) { + print "$field: $msg\n"; + } } catch (Exception $e) { print "Exception: {$e->getMessage()}\n"; print $e->getTraceAsString() . "\n"; diff --git a/modules/gallery_unit_test/helpers/test.php b/modules/gallery_unit_test/helpers/test.php new file mode 100644 index 00000000..3e116808 --- /dev/null +++ b/modules/gallery_unit_test/helpers/test.php @@ -0,0 +1,85 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class test_Core { + static function random_album_unsaved($parent=null) { + $rand = rand(); + + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent ? $parent->id : 1; + $album->name = "name_$rand"; + $album->title = "title_$rand"; + return $album; + } + + static function random_album($parent=null) { + return test::random_album_unsaved($parent)->save()->reload(); + } + + static function random_photo_unsaved($parent=null) { + $rand = rand(); + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->parent_id = $parent ? $parent->id : 1; + $photo->set_data_file(MODPATH . "gallery/tests/test.jpg"); + $photo->name = "name_$rand.jpg"; + $photo->title = "title_$rand"; + return $photo; + } + + static function random_photo($parent=null) { + return test::random_photo_unsaved($parent)->save()->reload(); + } + + static function random_user($password="password") { + $rand = "name_" . rand(); + return identity::create_user($rand, $rand, $password, "$rand@rand.com"); + } + + static function random_group() { + return identity::create_group((string)rand()); + } + + static function random_name($item=null) { + $rand = "name_" . rand(); + if ($item && $item->is_photo()) { + $rand .= ".jpg"; + } + return $rand; + } + + static function starts_with($outer, $inner) { + return strpos($outer, $inner) === 0; + } + + static function call_and_capture($callback) { + ob_start(); + call_user_func($callback); + return ob_get_clean(); + } + + static function random_tag() { + $tag = ORM::factory("tag"); + $tag->name = (string)rand(); + + // Reload so that ORM coerces all fields into strings. + return $tag->save()->reload(); + } +} diff --git a/modules/gallery_unit_test/libraries/Gallery_Unit_Test_Case.php b/modules/gallery_unit_test/libraries/Gallery_Unit_Test_Case.php new file mode 100644 index 00000000..509b4125 --- /dev/null +++ b/modules/gallery_unit_test/libraries/Gallery_Unit_Test_Case.php @@ -0,0 +1,36 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Gallery_Unit_Test_Case extends Unit_Test_Case { + public function assert_equal_array($expected, $actual, $debug=null) { + if ($expected !== $actual) { + throw new Kohana_Unit_Test_Exception( + sprintf("Expected (%s) %s but received (%s) %s\n Diff: %s", + gettype($expected), var_export($expected, true), + gettype($actual), var_export($actual, true), + diff::compare(var_export($expected, true), var_export($actual, true))), + $debug); + } + return $this; + } + + public function assert_array_equal_to_json($expected_array, $actual_json, $debug=null) { + return $this->assert_equal_array($expected_array, json_decode($actual_json, true), $debug); + } +} diff --git a/modules/gallery_unit_test/views/kohana/error.php b/modules/gallery_unit_test/views/kohana/error.php new file mode 100644 index 00000000..079a279b --- /dev/null +++ b/modules/gallery_unit_test/views/kohana/error.php @@ -0,0 +1,6 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<? +echo $e->getMessage(), "\n"; +echo $e->getFile(), ":", $e->getLine(), "\n"; +echo $e->getTraceAsString(), "\n"; + diff --git a/modules/gallery_unit_test/views/kohana_error_page.php b/modules/gallery_unit_test/views/kohana_error_page.php deleted file mode 100644 index c0ae7173..00000000 --- a/modules/gallery_unit_test/views/kohana_error_page.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access.") ?> -<? -echo $error, "\n\n"; -echo wordwrap($description, 80), "\n\n"; -if (!empty($line) && !empty($file)) { - echo $file, "[", $line, "]:\n"; -} -echo $message, "\n\n"; - -if (!empty($trace)) { - $trace = preg_replace( - array('/<li>/', '/<(.*?)>/', '/>/'), - array(" ", '', '>'), - $trace); - echo "Stack Trace:\n"; - echo $trace, "\n"; -} - diff --git a/modules/gallery_unit_test/views/kohana_unit_test_cli.php b/modules/gallery_unit_test/views/kohana_unit_test_cli.php index 3203ee44..a0de0f52 100644 --- a/modules/gallery_unit_test/views/kohana_unit_test_cli.php +++ b/modules/gallery_unit_test/views/kohana_unit_test_cli.php @@ -57,6 +57,12 @@ foreach ($results as $class => $methods) { if ($result->getMessage()) { echo " ", $result->getMessage(), "\n"; } + if ($result instanceof ORM_Validation_Exception) { + echo " Validation errors:\n"; + foreach ($result->validation->errors() as $key => $value) { + echo " $key: $value\n"; + } + } echo " ", $result->getFile(), " (Line ", $result->getLine(), ")\n"; echo "\n"; echo $result->getTraceAsString(), "\n"; diff --git a/modules/image_block/helpers/image_block_block.php b/modules/image_block/helpers/image_block_block.php index f591e8d1..185df850 100644 --- a/modules/image_block/helpers/image_block_block.php +++ b/modules/image_block/helpers/image_block_block.php @@ -26,34 +26,13 @@ class image_block_block_Core { $block = ""; switch ($block_id) { case "random_image": - $block = new Block(); - $block->css_id = "g-image-block"; - $block->title = t("Random image"); - $block->content = new View("image_block_block.html"); - - $random = ((float)mt_rand()) / (float)mt_getrandmax(); - - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", "<", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - - if ($items->count() == 0) { - // Try once more. If this fails, just ditch the block altogether - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", ">=", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - } - - if ($items->count() > 0) { - $block->content->item = $items->current(); - } else { - $block = ""; + $item = item::random_query(array(array("type", "!=", "album")))->find_all(1)->current(); + if ($item && $item->loaded()) { + $block = new Block(); + $block->css_id = "g-image-block"; + $block->title = t("Random image"); + $block->content = new View("image_block_block.html"); + $block->content->item = $item; } break; } diff --git a/modules/image_block/helpers/image_block_rest.php b/modules/image_block/helpers/image_block_rest.php deleted file mode 100644 index 7afd974c..00000000 --- a/modules/image_block/helpers/image_block_rest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 Bharat Mediratta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. - */ -class image_block_rest_Core { - static function get($request) { - $type = empty($request->type) ? "random" : $request->type; - switch ($type) { - case "random": - $random = ((float)mt_rand()) / (float)mt_getrandmax(); - - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", "<", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - - if ($items->count() == 0) { - // Try once more. If this fails, just ditch the block altogether - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", ">= ", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - } - break; - default: - return rest::fail("Unsupported image block type: '{$type}'"); - } - - if ($items->count() > 0) { - $item = $items->current(); - $response_data = array("name" => $item->name, - "path" => $item->relative_url(), - "title" => $item->title, - "thumb_url" => $item->thumb_url(true), - "thumb_size" => array("height" => $item->thumb_height, - "width" => $item->thumb_width)); - - return rest::success(array("resource" => $response_data)); - } else { - return rest::fail("No Image found"); - } - } -} diff --git a/modules/kohana23_compat/libraries/MY_Database_Builder.php b/modules/kohana23_compat/libraries/MY_Database_Builder.php index c82b6ac4..b2a5c92a 100644 --- a/modules/kohana23_compat/libraries/MY_Database_Builder.php +++ b/modules/kohana23_compat/libraries/MY_Database_Builder.php @@ -23,8 +23,10 @@ class Database_Builder extends Database_Builder_Core { * @chainable */ public function merge_where($tuples) { - foreach ($tuples as $tuple) { - $this->where($tuple[0], $tuple[1], $tuple[2]); + if ($tuples) { + foreach ($tuples as $tuple) { + $this->where($tuple[0], $tuple[1], $tuple[2]); + } } return $this; } @@ -34,8 +36,10 @@ class Database_Builder extends Database_Builder_Core { * @chainable */ public function merge_or_where($tuples) { - foreach ($tuples as $tuple) { - $this->or_where($tuple[0], $tuple[1], $tuple[2]); + if ($tuples) { + foreach ($tuples as $tuple) { + $this->or_where($tuple[0], $tuple[1], $tuple[2]); + } } return $this; } diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 201ced30..4a4b9f13 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -36,7 +36,7 @@ class Organize_Controller extends Controller { access::required("edit", $album); print json_encode( - array("grid" => self::_get_micro_thumb_grid($album, $offset)->__toString(), + array("grid" => (string)self::_get_micro_thumb_grid($album, $offset), "sort_column" => $album->sort_column, "sort_order" => $album->sort_order)); } @@ -57,8 +57,8 @@ class Organize_Controller extends Controller { } print json_encode( - array("tree" => self::_expanded_tree(ORM::factory("item", 1), $target_album)->__toString(), - "grid" => self::_get_micro_thumb_grid($target_album, 0)->__toString())); + array("tree" => (string)self::_expanded_tree(ORM::factory("item", 1), $target_album), + "grid" => (string)self::_get_micro_thumb_grid($target_album, 0))); } function rearrange($target_id, $before_or_after) { @@ -114,7 +114,7 @@ class Organize_Controller extends Controller { module::event("album_rearrange", $album); print json_encode( - array("grid" => self::_get_micro_thumb_grid($album, 0)->__toString(), + array("grid" => (string)self::_get_micro_thumb_grid($album, 0), "sort_column" => $album->sort_column, "sort_order" => $album->sort_order)); } @@ -136,7 +136,7 @@ class Organize_Controller extends Controller { $album->save(); print json_encode( - array("grid" => self::_get_micro_thumb_grid($album, 0)->__toString(), + array("grid" => (string)self::_get_micro_thumb_grid($album, 0), "sort_column" => $album->sort_column, "sort_order" => $album->sort_order)); } diff --git a/modules/recaptcha/controllers/admin_recaptcha.php b/modules/recaptcha/controllers/admin_recaptcha.php index 6874fce9..0ddb98c1 100644 --- a/modules/recaptcha/controllers/admin_recaptcha.php +++ b/modules/recaptcha/controllers/admin_recaptcha.php @@ -42,7 +42,7 @@ class Admin_Recaptcha_Controller extends Admin_Controller { } else { module::set_var("recaptcha", "public_key", ""); module::set_var("recaptcha", "private_key", ""); - message::success(t("reCAPTCHA disabled!")); + message::success(t("No keys provided. reCAPTCHA is disabled!")); log::success("recaptcha", t("reCAPTCHA public and private keys cleared")); url::redirect("admin/recaptcha"); } diff --git a/modules/recaptcha/helpers/recaptcha.php b/modules/recaptcha/helpers/recaptcha.php index 789bae85..5df22cbc 100644 --- a/modules/recaptcha/helpers/recaptcha.php +++ b/modules/recaptcha/helpers/recaptcha.php @@ -24,12 +24,16 @@ class recaptcha_Core { ->label(t("Configure reCAPTCHA")); $group->input("public_key") ->label(t("Public Key")) - ->value(module::get_var("recaptcha", "public_key")); - $group->public_key->error_messages("invalid", t("The public key you provided is invalid.")); + ->value(module::get_var("recaptcha", "public_key")) + ->rules("required") + ->error_messages("required", t("You must enter a public key")) + ->error_messages("invalid", t("This public key is invalid")); $group->input("private_key") ->label(t("Private Key")) - ->value(module::get_var("recaptcha", "private_key")); - $group->private_key->error_messages("invalid", t("The private key you provided is invalid.")); + ->value(module::get_var("recaptcha", "private_key")) + ->callback("recaptcha::verify_key") + ->error_messages("required", t("You must enter a private key")) + ->error_messages("invalid", t("This private key is invalid")); $group->submit("")->value(t("Save")); $site_domain = urlencode(stripslashes($_SERVER["HTTP_HOST"])); @@ -55,19 +59,23 @@ class recaptcha_Core { * @param string $private_key * @return boolean */ - static function verify_key($private_key) { + static function verify_key($private_key_input) { + if (!$private_key_input->value) { + $private_key_input->add_error("required", 1); + return; + } + $remote_ip = Input::instance()->server("REMOTE_ADDR"); $response = self::_http_post("api-verify.recaptcha.net", "/verify", - array("privatekey" => $private_key, + array("privatekey" => $private_key_input->value, "remoteip" => $remote_ip, "challenge" => "right", "response" => "wrong")); - $answers = explode("\n", $response[1]); - if (trim($answers[0]) == "true") { - return null; - } else { - return $answers[1]; + if ($response[1] == "false\ninvalid-site-private-key") { + // This is the only thing I can figure out how to verify. + // See http://recaptcha.net/apidocs/captcha for possible return values + $private_key_input->add_error("invalid", 1); } } @@ -80,16 +88,16 @@ class recaptcha_Core { $input = Input::instance(); $remote_ip = $input->server("REMOTE_ADDR"); - //discard spam submissions + // discard spam submissions if (empty($challenge) || empty($response)) { return "incorrect-captcha-sol"; } $response = self::_http_post("api-verify.recaptcha.net", "/verify", - array ("privatekey" => $private_key, - "remoteip" => $remote_ip, - "challenge" => $challenge, - "response" => $response)); + array("privatekey" => $private_key, + "remoteip" => $remote_ip, + "challenge" => $challenge, + "response" => $response)); $answers = explode ("\n", $response [1]); if (trim ($answers [0]) == "true") { diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php index 26e5b31a..374ae0d2 100644 --- a/modules/rest/controllers/rest.php +++ b/modules/rest/controllers/rest.php @@ -18,51 +18,62 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Rest_Controller extends Controller { - public function access_key() { - try { - $request = (object)Input::instance()->get(); - if (empty($request->user) || empty($request->password)) { - throw new Rest_Exception(403, "Forbidden"); - } + public function index() { + $username = Input::instance()->post("user"); + $password = Input::instance()->post("password"); - $user = identity::lookup_user_by_name($request->user); - if (empty($user)) { - throw new Rest_Exception(403, "Forbidden"); - } - - if (!identity::is_correct_password($user, $request->password)) { - throw new Rest_Exception(403, "Forbidden"); - } + if (empty($username) || auth::too_many_failed_logins($username)) { + throw new Rest_Exception("Forbidden", 403); + } - $key = ORM::factory("user_access_token") - ->where("user_id", "=", $user->id) - ->find(); - if (!$key->loaded()) { - $key->user_id = $user->id; - $key->access_key = md5($user->name . rand()); - $key->save(); - } - print rest::success(array("token" => $key->access_key)); - } catch (Rest_Exception $e) { - $e->sendHeaders(); + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + module::event("user_login_failed", $username); + throw new Rest_Exception("Forbidden", 403); } + + auth::login($user); + + $key = rest::get_access_token($user->id); + rest::reply($key->access_key); } public function __call($function, $args) { - $request = rest::normalize_request($args); - try { - if (rest::set_active_user($request->access_token)) { - $handler_class = "{$function}_rest"; - $handler_method = $request->method; + $input = Input::instance(); + $request = new stdClass(); + switch ($method = strtolower($input->server("REQUEST_METHOD"))) { + case "get": + $request->params = (object) $input->get(); + break; + + case "post": + $request->params = (object) $input->post(); + if (isset($_FILES["file"])) { + $request->file = upload::save("file"); + } + break; + } + + $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); + $request->access_token = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); + $request->url = url::abs_current(true); - if (!method_exists($handler_class, $handler_method)) { - throw new Rest_Exception(403, "Forbidden"); - } + rest::set_active_user($request->access_token); - print call_user_func(array($handler_class, $handler_method), $request); + $handler_class = "{$function}_rest"; + $handler_method = $request->method; + + if (!method_exists($handler_class, $handler_method)) { + throw new Rest_Exception("Bad Request", 400); + } + + try { + print rest::reply(call_user_func(array($handler_class, $handler_method), $request)); + } catch (ORM_Validation_Exception $e) { + foreach ($e->validation->errors() as $key => $value) { + $msgs[] = "$key: $value"; } - } catch (Rest_Exception $e) { - $e->sendHeaders(); + throw new Rest_Exception("Bad Request: " . join(", ", $msgs), 400); } } }
\ No newline at end of file diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index be0644f2..a61aba2f 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -18,87 +18,123 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class rest_Core { - /** - * Request failed - */ - static function fail($log_message=null) { - if (!empty($log_message)) { - Kohana_Log::add("info", $log_message); + static function reply($data=array()) { + Session::instance()->abort_save(); + + if ($data) { + if (Input::instance()->get("output") == "html") { + header("Content-type: text/html"); + $html = preg_replace( + "#([\w]+?://[\w]+[^ \'\"\n\r\t<]*)#ise", "'<a href=\"\\1\" >\\1</a>'", + var_export($data, 1)); + print "<pre>$html</pre>"; + } else { + header("Content-type: application/json"); + print json_encode($data); + } } - // We don't need to save the session for this request - Session::abort_save(); - return json_encode(array("status" => "ERROR", "message" => (string)$message)); } - /** - * Success - */ - static function success($response_data=array(), $message=null) { - $response = array("status" => "OK"); - if (!empty($message)) { - $response["message"] = (string)$message; + static function set_active_user($access_token) { + if (empty($access_token)) { + identity::set_active_user(identity::guest()); + return; } - $response = array_merge($response, $response_data); - // We don't need to save the session for this request - Session::abort_save(); - return json_encode($response); + $key = ORM::factory("user_access_token") + ->where("access_key", "=", $access_token) + ->find(); + + if (!$key->loaded()) { + throw new Rest_Exception("Forbidden", 403); + } + + $user = identity::lookup_user($key->user_id); + if (empty($user)) { + throw new Rest_Exception("Forbidden", 403); + } + + identity::set_active_user($user); + } + + static function get_access_token($user_id) { + $key = ORM::factory("user_access_token") + ->where("user_id", "=", $user_id) + ->find(); + + if (!$key->loaded()) { + $key->user_id = $user_id; + $key->access_key = md5(md5(uniqid(mt_rand(), true) . access::private_key())); + $key->save(); + } + return $key; } /** - * Validation Error + * Convert a REST url into an object. + * Eg: + * http://example.com/gallery3/index.php/rest/item/35 -> Item_Model + * http://example.com/gallery3/index.php/rest/tag/16 -> Tag_Model + * http://example.com/gallery3/index.php/rest/tagged_item/1,16 -> [Tag_Model, Item_Model] + * + * @param string the fully qualified REST url + * @return mixed the corresponding object (usually a model of some kind) */ - static function validation_error($error_data) { - $response = array("status" => "VALIDATE_ERROR"); - $response = array_merge($response, array("fields" => $error_data)); + static function resolve($url) { + $relative_url = substr($url, strlen(url::abs_site("rest"))); + $path = parse_url($relative_url, PHP_URL_PATH); + $components = explode("/", $path, 3); + + if (count($components) != 3) { + throw new Kohana_404_Exception($url); + } + + $class = "$components[1]_rest"; + if (!method_exists($class, "resolve")) { + throw new Kohana_404_Exception($url); + } - // We don't need to save the session for this request - Session::abort_save(); - return json_encode($response); + return call_user_func(array($class, "resolve"), !empty($components[2]) ? $components[2] : null); } + /** + * Return an absolute url used for REST resource location. + * @param string resource type (eg, "item", "tag") + * @param object resource + */ + static function url() { + $args = func_get_args(); + $resource_type = array_shift($args); - static function normalize_request($args=array()) { - $input = Input::instance(); - $method = strtolower($input->server("REQUEST_METHOD")); - $request = new stdClass(); - foreach (array_keys($input->get()) as $key) { - $request->$key = $input->get($key); + $class = "{$resource_type}_rest"; + if (!method_exists($class, "url")) { + throw new Rest_Exception("Bad Request", 400); } - if ($method != "get") { - foreach (array_keys($input->post()) as $key) { - $request->$key = $input->post($key); - } - foreach (array_keys($_FILES) as $key) { - $request->$key = $_FILES[$key]; + + $url = call_user_func_array(array($class, "url"), $args); + if (Input::instance()->get("output") == "html") { + if (strpos($url, "?") === false) { + $url .= "?output=html"; + } else { + $url .= "&output=html"; } } - - $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); - $request->access_token = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); - $request->arguments = $args; // Let the rest handler figure out what the arguments mean - - return $request; + return $url; } - static function set_active_user($access_token) { - if (empty($access_token)) { - $user = identity::guest(); - } else { - $key = ORM::factory("user_access_token") - ->where("access_key", "=", $access_token) - ->find(); - - if ($key->loaded()) { - $user = identity::lookup_user($key->user_id); - if (empty($user)) { - throw new Rest_Exception(403, "Forbidden"); + static function relationships($resource_type, $resource) { + $results = array(); + foreach (module::active() as $module) { + foreach (glob(MODPATH . "{$module->name}/helpers/*_rest.php") as $filename) { + $class = str_replace(".php", "", basename($filename)); + if (method_exists($class, "relationships")) { + $results = array_merge( + $results, + call_user_func(array($class, "relationships"), $resource_type, $resource)); } - } else { - throw new Rest_Exception(403, "Forbidden"); } } - identity::set_active_user($user); - return true; + + return $results; } } diff --git a/modules/rest/libraries/Rest_Exception.php b/modules/rest/libraries/Rest_Exception.php index 905b94a0..c3548b7e 100644 --- a/modules/rest/libraries/Rest_Exception.php +++ b/modules/rest/libraries/Rest_Exception.php @@ -17,20 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Rest_Exception_Core extends Exception { - /** - * Set internal properties. - */ - public function __construct($code, $text) { - parent::__construct("$code $text"); +class Rest_Exception_Core extends Kohana_Exception { + public function __construct($message, $code) { + parent::__construct($message, null, $code); } - /** - * Sends the headers, to emulate server behavior. - * - * @return void - */ public function sendHeaders() { - header('HTTP/1.1 {$this->getMessage()}'); + if (!headers_sent()) { + header("HTTP/1.1 " . $this->getCode() . " " . $this->getMessage()); + } } -} // End Rest Exception
\ No newline at end of file +}
\ No newline at end of file diff --git a/modules/rest/tests/Rest_Controller_Test.php b/modules/rest/tests/Rest_Controller_Test.php index 83bd9db6..9f73bed9 100644 --- a/modules/rest/tests/Rest_Controller_Test.php +++ b/modules/rest/tests/Rest_Controller_Test.php @@ -17,208 +17,129 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Rest_Controller_Test extends Unit_Test_Case { +class Rest_Controller_Test extends Gallery_Unit_Test_Case { public function setup() { $this->_save = array($_GET, $_POST, $_SERVER); } - private function _create_user() { - if (empty($this->_user)) { - $this->_user = identity::create_user("access_test" . rand(), "Access Test", "password"); - $this->_key = ORM::factory("user_access_token"); - $this->_key->access_key = md5($this->_user->name . rand()); - $this->_key->user_id = $this->_user->id; - $this->_key->save(); - identity::set_active_user($this->_user); - } - return array($this->_key->access_key, $this->_user); - } - public function teardown() { list($_GET, $_POST, $_SERVER) = $this->_save; - if (!empty($this->_user)) { - try { - $this->_user->delete(); - } catch (Exception $e) { } - } - } - - private function _create_image($parent=null) { - $filename = MODPATH . "gallery/tests/test.jpg"; - $image_name = "image_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - return photo::create($parent, $filename, "$image_name.jpg", $image_name); } - public function rest_access_key_exists_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_GET["user"] = $user->name;; - $_GET["password"] = "password"; + public function login_test() { + $user = test::random_user("password"); - $this->assert_equal( - json_encode(array("status" => "OK", "token" => $access_key)), - $this->_call_controller()); - } + // There's no access key at first + $this->assert_false( + ORM::factory("user_access_token")->where("user_id", "=", $user->id)->find()->loaded()); - public function rest_access_key_generated_test() { - list ($access_key, $user) = $this->_create_user(); - ORM::factory("user_access_token") - ->where("access_key", $access_key) - ->delete(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_GET["user"] = $user->name; - $_GET["password"] = "password"; + $_POST["user"] = $user->name; + $_POST["password"] = "password"; - $results = json_decode($this->_call_controller()); + $response = test::call_and_capture(array(new Rest_Controller(), "index")); + $expected = + ORM::factory("user_access_token")->where("user_id", "=", $user->id)->find()->access_key; - $this->assert_equal("OK", $results->status); - $this->assert_false(empty($results->token)); + // Now there is an access key, and it was returned + $this->assert_equal(json_encode($expected), $response); } - public function rest_access_key_no_parameters_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; + public function login_failed_test() { + $user = test::random_user("password"); try { - $this->_call_controller(); + $_POST["user"] = $user->name; + $_POST["password"] = "WRONG PASSWORD"; + test::call_and_capture(array(new Rest_Controller(), "index")); } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); + $this->assert_equal(403, $e->getCode()); + return; } - } - - public function rest_access_key_user_not_found_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["request"] = json_encode(array("user" => "access_test2", "password" => "password")); - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_true(false, "Shouldn't get here"); } - public function rest_access_key_invalid_password_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; + public function get_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_GET["key"] = "value"; - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "get", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_request_key_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; - $photo = $this->_create_image(); - - $this->assert_equal( - json_encode(array("status" => "OK", "message" => (string)t("Processed"), - "photo" => array("path" => $photo->relative_url(), - "title" => $photo->title, - "thumb_url" => $photo->thumb_url(), - "description" => $photo->description, - "internet_address" => $photo->slug))), - $this->_call_controller("rest", explode("/", $photo->relative_url()))); - } + public function get_with_access_key_test() { + $key = rest::get_access_token(1); // admin user - public function rest_get_resource_invalid_key_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = md5($access_key); // screw up the access key; $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $key->access_key; + $_GET["key"] = "value"; - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "get", + "access_token" => $key->access_key, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_user_for_key_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; + public function post_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["key"] = "value"; - $user->delete(); + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "post", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); + } - $photo = $this->_create_image(); + public function put_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "put"; + $_POST["key"] = "value"; - try { - $this->_call_controller("rest", explode("/", $photo->relative_url())); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "put", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_handler_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; - $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "PUT"; - $photo = $this->_create_image(); + public function delete_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "delete"; + $_POST["key"] = "value"; + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "delete", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); + } + + public function bogus_method_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "BOGUS"; try { - $this->_call_controller("rest", explode("/", $photo->relative_url())); - } catch (Rest_Exception $e) { - $this->assert_equal("501 Not Implemented", $e->getMessage()); + test::call_and_capture(array(new Rest_Controller(), "mock")); } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); + $this->assert_equal(400, $e->getCode()); + return; } - } - - public function rest_get_resource_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; - - $photo = $this->_create_image(); - $this->assert_equal( - json_encode(array("status" => "OK", "message" => (string)t("Processed"), - "photo" => array("path" => $photo->relative_url(), - "title" => $photo->title, - "thumb_url" => $photo->thumb_url(), - "description" => $photo->description, - "internet_address" => $photo->slug))), - $this->_call_controller("rest", explode("/", $photo->relative_url()))); - } - - private function _call_controller($method="access_key", $arg=null) { - $controller = new Rest_Controller(); - - ob_start(); - call_user_func_array(array($controller, $method), $arg); - $results = ob_get_contents(); - ob_end_clean(); - - return $results; + $this->assert_true(false, "Shouldn't get here"); } } -class rest_rest { - static $request = null; - - static function get($request) { - self::$request = $request; - $item = ORM::factory("item") - ->where("relative_url_cache", "=", implode("/", $request->arguments)) - ->find(); - $response["path"] = $item->relative_url(); - $response["title"] = $item->title; - $response["thumb_url"] = $item->thumb_url(); - $response["description"] = $item->description; - $response["internet_address"] = $item->slug; - return rest::success(array($item->type => $response), t("Processed")); - } - -} +class mock_rest { + static function get($request) { return $request; } + static function post($request) { return $request; } + static function put($request) { return $request; } + static function delete($request) { return $request; } +}
\ No newline at end of file diff --git a/modules/rest/views/user_profile_rest.html.php b/modules/rest/views/user_profile_rest.html.php index 3807817e..397afa89 100644 --- a/modules/rest/views/user_profile_rest.html.php +++ b/modules/rest/views/user_profile_rest.html.php @@ -2,7 +2,7 @@ <div id="g-rest-detail"> <ul> <li id="g-rest-key"> - <p><b><?= t("Key") ?></b>:<?= $rest_key ?></p> + <p><b><?= t("Key") ?></b>:<?= html::clean($rest_key) ?></p> </li> </ul> </div> diff --git a/modules/search/helpers/search.php b/modules/search/helpers/search.php index b2497eae..9018ffa2 100644 --- a/modules/search/helpers/search.php +++ b/modules/search/helpers/search.php @@ -65,7 +65,8 @@ class search_Core { $record->item_id = $item->id; } - module::event("item_index_data", $record->item(), $data); + $item = $record->item(); + module::event("item_index_data", $item, $data); $record->data = join(" ", (array)$data); $record->dirty = 0; $record->save(); diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index d7572b52..f68335df 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -42,7 +42,6 @@ class Server_Add_Controller extends Admin_Controller { // Make a tree with the parents back up to the authorized path, and all the children under the // current path. - Kohana_Log::add("error", $path); if (server_add::is_valid_path($path)) { $tree->parents[] = $path; while (server_add::is_valid_path(dirname($tree->parents[0]))) { @@ -110,7 +109,7 @@ class Server_Add_Controller extends Admin_Controller { } $task = task::run($task_id); - print json_encode(array("done" => $task->done, + print json_encode(array("done" => (bool)$task->done, "status" => $task->status, "percent_complete" => $task->percent_complete)); } @@ -226,16 +225,36 @@ class Server_Add_Controller extends Admin_Controller { $name = basename($entry->file); $title = item::convert_filename_to_title($name); if (is_dir($entry->file)) { - $album = album::create($parent, $name, $title, null, $owner_id); + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent->id; + $album->name = $name; + $album->title = $title; + $album->owner_id = $owner_id; + $album->save(); $entry->item_id = $album->id; } else { try { $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (in_array($extension, array("gif", "png", "jpg", "jpeg"))) { - $photo = photo::create($parent, $entry->file, $name, $title, null, $owner_id); + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->parent_id = $parent->id; + $photo->set_data_file($entry->file); + $photo->name = $name; + $photo->title = $title; + $photo->owner_id = $owner_id; + $photo->save(); $entry->item_id = $photo->id; } else if (in_array($extension, array("flv", "mp4"))) { - $movie = movie::create($parent, $entry->file, $name, $title, null, $owner_id); + $movie = ORM::factory("item"); + $movie->type = "movie"; + $movie->parent_id = $parent->id; + $movie->set_data_file($entry->file); + $movie->name = $name; + $movie->title = $title; + $movie->owner_id = $owner_id; + $movie->save(); $entry->item_id = $movie->id; } else { // This should never happen, because we don't add stuff to the list that we can't diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php index e20b8ac8..9092ec68 100644 --- a/modules/tag/controllers/admin_tags.php +++ b/modules/tag/controllers/admin_tags.php @@ -60,9 +60,7 @@ class Admin_Tags_Controller extends Admin_Controller { array("result" => "success", "location" => url::site("admin/tags"))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -99,7 +97,8 @@ class Admin_Tags_Controller extends Admin_Controller { message::success($message); log::success("tags", $message); - print json_encode(array("result" => "success")); + print json_encode(array("result" => "success", + "location" => url::site("admin/tags"))); } else { print json_encode(array("result" => "error", "form" => $in_place_edit->render())); } diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index 992c7411..1eede907 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -69,11 +69,9 @@ class Tags_Controller extends Controller { print json_encode( array("result" => "success", - "cloud" => tag::cloud(30)->__toString())); + "cloud" => (string)tag::cloud(30))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/tag/helpers/item_tags_rest.php b/modules/tag/helpers/item_tags_rest.php new file mode 100644 index 00000000..43e2cef0 --- /dev/null +++ b/modules/tag/helpers/item_tags_rest.php @@ -0,0 +1,64 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class item_tags_rest_Core { + static function get($request) { + $item = rest::resolve($request->url); + $tags = array(); + foreach (tag::item_tags($item) as $tag) { + $tags[] = rest::url("tag_item", $tag, $item); + } + + return array( + "url" => $request->url, + "members" => $tags); + } + + static function post($request) { + $tag = rest::resolve($request->params->tag); + $item = rest::resolve($request->params->item); + access::required("view", $item); + + tag::add($item, $tag->name); + return array( + "url" => rest::url("tag_item", $tag, $item), + "members" => array( + rest::url("tag", $tag), + rest::url("item", $item))); + } + + static function delete($request) { + list ($tag, $item) = rest::resolve($request->url); + $tag->remove($item); + $tag->save(); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + + return $item; + } + + static function url($item) { + return url::abs_site("rest/item_tags/{$item->id}"); + } +} diff --git a/modules/tag/helpers/tag.php b/modules/tag/helpers/tag.php index 8075afe4..a500be58 100644 --- a/modules/tag/helpers/tag.php +++ b/modules/tag/helpers/tag.php @@ -37,17 +37,11 @@ class tag_Core { if (!$tag->loaded()) { $tag->name = $tag_name; $tag->count = 0; - $tag->save(); } - if (!$tag->has($item)) { - if (!$tag->add($item, $tag)) { - throw new Exception("@todo {$tag->name} WAS_NOT_ADDED_TO {$item->id}"); - } - $tag->count++; - $tag->save(); - } - return $tag; + $tag->add($item); + $tag->count++; + return $tag->save(); } /** @@ -73,6 +67,9 @@ class tag_Core { if ($tags) { $cloud = new View("tag_cloud.html"); $cloud->max_count = $tags[0]->count; + if (!$cloud->max_count) { + return; + } usort($tags, array("tag", "sort_by_name")); $cloud->tags = $tags; return $cloud; @@ -88,16 +85,10 @@ class tag_Core { * @return array */ static function item_tags($item) { - $tags = array(); - foreach (db::build() - ->select("name") - ->from("tags") - ->join("items_tags", "tags.id", "items_tags.tag_id", "left") - ->where("items_tags.item_id", "=", $item->id) - ->execute() as $row) { - $tags[] = $row->name; - } - return $tags; + return ORM::factory("tag") + ->join("items_tags", "tags.id", "items_tags.tag_id", "left") + ->where("items_tags.item_id", "=", $item->id) + ->find_all(); } static function get_add_form($item) { diff --git a/modules/tag/helpers/tag_event.php b/modules/tag/helpers/tag_event.php index 6ee8e708..403ccd52 100644 --- a/modules/tag/helpers/tag_event.php +++ b/modules/tag/helpers/tag_event.php @@ -71,9 +71,13 @@ class tag_event_Core { $('form input[id=tags]').autocomplete( '$url', {max: 30, multiple: true, multipleSeparator: ',', cacheLength: 1}); });"); - $tag_value = implode(", ", tag::item_tags($item)); + + $tag_names = array(); + foreach (tag::item_tags($item) as $tag) { + $tag_names[] = $tag->name; + } $form->edit_item->input("tags")->label(t("Tags (comma separated)")) - ->value($tag_value); + ->value(implode(", ", $tag_names)); } static function item_edit_form_completed($item, $form) { @@ -95,7 +99,9 @@ class tag_event_Core { } static function item_index_data($item, $data) { - $data[] = join(" ", tag::item_tags($item)); + foreach (tag::item_tags($item) as $tag) { + $data[] = $tag->name; + } } static function add_photos_form($album, $form) { diff --git a/modules/tag/helpers/tag_item_rest.php b/modules/tag/helpers/tag_item_rest.php new file mode 100644 index 00000000..672cec53 --- /dev/null +++ b/modules/tag/helpers/tag_item_rest.php @@ -0,0 +1,50 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class tag_item_rest_Core { + static function get($request) { + list ($tag, $item) = rest::resolve($request->url); + return array( + "url" => $request->url, + "members" => array( + "tag" => rest::url("tag", $tag), + "item" => rest::url("item", $item))); + } + + static function delete($request) { + list ($tag, $item) = rest::resolve($request->url); + $tag->remove($item); + $tag->save(); + } + + static function resolve($tuple) { + list ($tag_id, $item_id) = split(",", $tuple); + $tag = ORM::factory("tag", $tag_id); + $item = ORM::factory("item", $item_id); + if (!$tag->loaded() || !$item->loaded() || !$tag->has($item) || !access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + + return array($tag, $item); + } + + static function url($tag, $item) { + return url::abs_site("rest/tag_item/{$tag->id},{$item->id}"); + } +} diff --git a/modules/tag/helpers/tag_items_rest.php b/modules/tag/helpers/tag_items_rest.php new file mode 100644 index 00000000..18973ebb --- /dev/null +++ b/modules/tag/helpers/tag_items_rest.php @@ -0,0 +1,65 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class tag_items_rest_Core { + static function get($request) { + $tag = rest::resolve($request->url); + $items = array(); + foreach ($tag->items() as $item) { + if (access::can("view", $item)) { + $items[] = rest::url("tag_item", $tag, $item); + } + } + + return array( + "url" => $request->url, + "members" => $items); + } + + static function post($request) { + $tag = rest::resolve($request->params->tag); + $item = rest::resolve($request->params->item); + access::required("view", $item); + + if (!$tag->loaded()) { + throw new Kohana_404_Exception(); + } + + tag::add($item, $tag->name); + return array( + "url" => rest::url("tag_item", $tag, $item), + "members" => array( + "tag" => rest::url("tag", $tag), + "item" => rest::url("item", $item))); + } + + static function delete($request) { + list ($tag, $item) = rest::resolve($request->url); + $tag->remove($item); + $tag->save(); + } + + static function resolve($id) { + return ORM::factory("tag", $id); + } + + static function url($tag) { + return url::abs_site("rest/tag_items/{$tag->id}"); + } +} diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php index cd1ca6c6..4879cf63 100644 --- a/modules/tag/helpers/tag_rest.php +++ b/modules/tag/helpers/tag_rest.php @@ -18,143 +18,74 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class tag_rest_Core { - // If no arguments just return all the tags. If 2 or more then it is a path then - // return the tags for that item. But if its only 1, then is it a path or a tag? - // Assume a tag first, if nothing is found then try finding the item. static function get($request) { - $resources = array(); - switch (count($request->arguments)) { - case 0: - $tags = ORM::factory("tag") - ->select("name", "count") - ->order_by("count", "DESC"); - if (!empty($request->limit)) { - $tags->limit($request->limit); - } - if (!empty($request->offset)) { - $tags->offset($request->offset); - } - $resources = array("tags" => array()); - foreach ($tags->find_all() as $row) { - $resources["tags"][] = array("name" => $row->name, "count" => $row->count); - } - break; - case 1: - $resources = tag_rest::_get_items($request); - if (!empty($resources)) { - $resources = array("resources" => $resources); - break; - } - default: - $item = ORM::factory("item") - ->where("relative_url_cache", "=", implode("/", $request->arguments)) - ->viewable() - ->find(); - if ($item->loaded()) { - $resources = array("tags" => tag::item_tags($item)); + $tag = rest::resolve($request->url); + $tag_items = array(); + foreach ($tag->items() as $item) { + if (access::can("view", $item)) { + $tag_items[] = rest::url("tag_item", $tag, $item); } } - return rest::success($resources); + return array( + "url" => $request->url, + "entity" => $tag->as_array(), + "relationships" => array( + "items" => array( + "url" => rest::url("tag_items", $tag), + "members" => $tag_items))); } static function post($request) { - if (empty($request->arguments) || count($request->arguments) != 1 || empty($request->path)) { - throw new Rest_Exception(400, "Bad request"); - } - $path = $request->path; - $tags = explode(",", $request->arguments[0]); - - $item = ORM::factory("item") - ->where("relative_url_cache", "=", $path) - ->viewable() - ->find(); - if (!$item->loaded()) { - throw new Kohana_404_Exception(); + if (empty($request->params->url)) { + throw new Rest_Exception("Bad request", 400); } - if (!access::can("edit", $item)) { - throw new Kohana_404_Exception(); - } + $tag = rest::resolve($request->url); + $item = rest::resolve($request->params->url); + access::required("edit", $item); - foreach ($tags as $tag) { - tag::add($item, $tag); - } - return rest::success(); + tag::add($item, $tag->name); + return array("url" => rest::url("tag_item", $tag, $item)); } static function put($request) { - if (empty($request->arguments[0]) || empty($request->new_name)) { - throw new Rest_Exception(400, "Bad request"); + $tag = rest::resolve($request->url); + if (isset($request->params->name)) { + $tag->name = $request->params->name; + $tag->save(); } + } - $name = $request->arguments[0]; + static function delete($request) { + $tag = rest::resolve($request->url); + $tag->delete(); + } - $tag = ORM::factory("tag") - ->where("name", "=", $name) - ->find(); - if (!$tag->loaded()) { - throw new Kohana_404_Exception(); + static function relationships($resource_type, $resource) { + switch ($resource_type) { + case "item": + $tags = array(); + foreach (tag::item_tags($resource) as $tag) { + $tags[] = rest::url("tag_item", $tag, $resource); + } + return array( + "tags" => array( + "url" => rest::url("item_tags", $resource), + "members" => $tags)); } - - $tag->name = $request->new_name; - $tag->save(); - - return rest::success(); } - static function delete($request) { - if (empty($request->arguments[0])) { - throw new Rest_Exception(400, "Bad request"); - } - $tags = explode(",", $request->arguments[0]); - if (!empty($request->path)) { - $tag_list = ORM::factory("tag") - ->join("items_tags", "tags.id", "items_tags.tag_id") - ->join("items", "items.id", "items_tags.item_id") - ->where("tags.name", "IN", $tags) - ->where("relative_url_cache", "=", $request->path) - ->viewable() - ->find_all(); - } else { - $tag_list = ORM::factory("tag") - ->where("name", "IN", $tags) - ->find_all(); + static function resolve($id) { + $tag = ORM::factory("tag", $id); + if (!$tag->loaded()) { + throw new Kohana_404_Exception(); } - foreach ($tag_list as $row) { - $row->delete(); - }; - - tag::compact(); - return rest::success(); + return $tag; } - private static function _get_items($request) { - $tags = explode(",", $request->arguments[0]); - $items = ORM::factory("item") - ->select_distinct("*") - ->join("items_tags", "items.id", "items_tags.item_id") - ->join("tags", "tags.id", "items_tags.tag_id") - ->where("tags.name", "IN", $tags); - if (!empty($request->limit)) { - $items->limit($request->limit); - } - if (!empty($request->offset)) { - $items->offset($request->offset); - } - $resources = array(); - foreach ($items->find_all() as $item) { - $resources[] = array("type" => $item->type, - "has_children" => $item->children_count() > 0, - "path" => $item->relative_url(), - "thumb_url" => $item->thumb_url(true), - "thumb_dimensions" => array("width" => $item->thumb_width, - "height" => $item->thumb_height), - "has_thumb" => $item->has_thumb(), - "title" => $item->title); - } - - return $resources; + static function url($tag) { + return url::abs_site("rest/tag/{$tag->id}"); } } diff --git a/modules/tag/helpers/tag_rss.php b/modules/tag/helpers/tag_rss.php index f09a4530..5d42caab 100644 --- a/modules/tag/helpers/tag_rss.php +++ b/modules/tag/helpers/tag_rss.php @@ -34,6 +34,8 @@ class tag_rss_Core { if (!$tag->loaded()) { throw new Kohana_404_Exception(); } + + $feed = new stdClass(); $feed->children = $tag->items($limit, $offset, "photo"); $feed->max_pages = ceil($tag->count / $limit); $feed->title = $tag->name; diff --git a/modules/tag/helpers/tags_rest.php b/modules/tag/helpers/tags_rest.php new file mode 100644 index 00000000..f28be7b5 --- /dev/null +++ b/modules/tag/helpers/tags_rest.php @@ -0,0 +1,60 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class tags_rest_Core { + static function get($request) { + $tags = array(); + foreach (ORM::factory("tag")->find_all() as $tag) { + $tags[] = rest::url("tag", $tag); + } + return array("url" => rest::url("tags"), + "members" => $tags); + } + + static function post($request) { + // The user must have some edit permission somewhere to create a tag. + if (!identity::active_user()->admin) { + $query = db::build()->from("access_caches")->and_open(); + foreach (identity::active_user()->groups() as $group) { + $query->or_where("edit_{$group->id}", "=", access::ALLOW); + } + $has_any_edit_perm = $query->close()->count_records(); + if (!$has_any_edit_perm) { + access::forbidden(); + } + } + + if (empty($request->params->name)) { + throw new Rest_Exception("Bad Request", 400); + } + + $tag = ORM::factory("tag")->where("name", "=", $request->params->name)->find(); + if (!$tag->loaded()) { + $tag->name = $request->params->name; + $tag->count = 0; + $tag->save(); + } + + return array("url" => rest::url("tag", $tag)); + } + + static function url() { + return url::abs_site("rest/tags"); + } +} diff --git a/modules/tag/models/tag.php b/modules/tag/models/tag.php index 2b33c30d..38a8ed69 100644 --- a/modules/tag/models/tag.php +++ b/modules/tag/models/tag.php @@ -95,7 +95,7 @@ class Tag_Model extends ORM { * Overload ORM::delete() to trigger an item_related_update event for all items that are * related to this tag. */ - public function delete() { + public function delete($ignored_id=null) { $related_item_ids = array(); foreach (db::build() ->select("item_id") diff --git a/modules/tag/tests/Tag_Item_Rest_Helper_Test.php b/modules/tag/tests/Tag_Item_Rest_Helper_Test.php new file mode 100644 index 00000000..cb368790 --- /dev/null +++ b/modules/tag/tests/Tag_Item_Rest_Helper_Test.php @@ -0,0 +1,70 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Tag_Item_Rest_Helper_Test extends Gallery_Unit_Test_Case { + public function setup() { + try { + Database::instance()->query("TRUNCATE {tags}"); + Database::instance()->query("TRUNCATE {items_tags}"); + } catch (Exception $e) { } + } + + public function get_test() { + $tag = tag::add(item::root(), "tag1")->reload(); + + $request = new stdClass(); + $request->url = rest::url("tag_item", $tag, item::root()); + $this->assert_equal_array( + array("url" => rest::url("tag_item", $tag, item::root()), + "members" => array( + "tag" => rest::url("tag", $tag), + "item" => rest::url("item", item::root()))), + tag_item_rest::get($request)); + } + + public function get_with_invalid_url_test() { + $request = new stdClass(); + $request->url = "bogus"; + try { + tag_item_rest::get($request); + } catch (Kohana_404_Exception $e) { + return; // pass + } + $this->assert_true(false, "Shouldn't get here"); + } + + public function delete_test() { + $tag = tag::add(item::root(), "tag1")->reload(); + + $request = new stdClass(); + $request->url = rest::url("tag_item", $tag, item::root()); + tag_item_rest::delete($request); + + $this->assert_false($tag->reload()->has(item::root())); + } + + public function resolve_test() { + $album = test::random_album(); + $tag = tag::add($album, "tag1")->reload(); + + $tuple = rest::resolve(rest::url("tag_item", $tag, $album)); + $this->assert_equal_array($tag->as_array(), $tuple[0]->as_array()); + $this->assert_equal_array($album->as_array(), $tuple[1]->as_array()); + } +} diff --git a/modules/tag/tests/Tag_Rest_Helper_Test.php b/modules/tag/tests/Tag_Rest_Helper_Test.php index 514538d4..838de975 100644 --- a/modules/tag/tests/Tag_Rest_Helper_Test.php +++ b/modules/tag/tests/Tag_Rest_Helper_Test.php @@ -17,272 +17,110 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Tag_Rest_Helper_Test extends Unit_Test_Case { +class Tag_Rest_Helper_Test extends Gallery_Unit_Test_Case { public function setup() { try { Database::instance()->query("TRUNCATE {tags}"); Database::instance()->query("TRUNCATE {items_tags}"); } catch (Exception $e) { } - $this->_save = array($_GET, $_POST, $_SERVER, $_FILES); - $this->_saved_active_user = identity::active_user(); } - public function teardown() { - list($_GET, $_POST, $_SERVER, $_FILES) = $this->_save; - identity::set_active_user($this->_saved_active_user); + public function get_test() { + $tag = tag::add(item::root(), "tag1")->reload(); - try { - if (!empty($this->_user)) { - $this->_user->delete(); - } - } catch (Exception $e) { } - } - - private function _create_user() { - if (empty($this->_user)) { - $this->_user = identity::create_user("access_test" . rand(), "Access Test", "password"); - $key = ORM::factory("user_access_token"); - $key->access_key = md5($this->_user->name . rand()); - $key->user_id = $this->_user->id; - $key->save(); - identity::set_active_user($this->_user); - } - return $this->_user; - } - - private function _create_album($tags=array(), $parent=null) { - $album_name = "tag_album_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - $album = album::create($parent, $album_name, $album_name, $album_name); - foreach ($tags as $tag) { - tag::add($album, $tag); - } - return $album; + $request = new stdClass(); + $request->url = rest::url("tag", $tag); + $this->assert_equal_array( + array("url" => rest::url("tag", $tag), + "entity" => $tag->as_array(), + "relationships" => array( + "items" => array( + "url" => rest::url("tag_items", $tag), + "members" => array( + rest::url("tag_item", $tag, item::root()))))), + tag_rest::get($request)); } - private function _create_image($tags=array(), $parent=null) { - $filename = MODPATH . "gallery/tests/test.jpg"; - $image_name = "tag_image_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - $photo = photo::create($parent, $filename, "$image_name.jpg", $image_name); - foreach ($tags as $tag) { - tag::add($photo, $tag); + public function get_with_invalid_url_test() { + $request = new stdClass(); + $request->url = "bogus"; + try { + tag_rest::get($request); + } catch (Kohana_404_Exception $e) { + return; // pass } - return $photo; - } - - public function tag_rest_get_all_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "C1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - $sibling = $this->_create_image(array("photos", "P3"), $album); - - $request = (object)array("arguments" => array(), "limit" => 2, "offset" => 1); - - $this->assert_equal( - json_encode(array("status" => "OK", - "tags" => array(array("name" => "albums", "count" => "2"), - array("name" => "photos", "count" => "2")))), - tag_rest::get($request)); + $this->assert_true(false, "Shouldn't get here"); } - public function tag_rest_get_tags_for_item_test() { - $photo = $this->_create_image(array("photos", "P1", "T1")); - - $request = (object)array("arguments" => explode("/", $photo->relative_url())); + public function get_with_no_relationships_test() { + $tag = test::random_tag(); - $this->assert_equal( - json_encode(array("status" => "OK", - "tags" => array("photos", "P1", "T1"))), + $request = new stdClass(); + $request->url = rest::url("tag", $tag); + $this->assert_equal_array( + array("url" => rest::url("tag", $tag), + "entity" => $tag->as_array(), + "relationships" => array( + "items" => array( + "url" => rest::url("tag_items", $tag), + "members" => array()))), tag_rest::get($request)); } - public function tag_rest_get_items_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "A1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - $sibling = $this->_create_image(array("photos", "P3"), $album); - $child->reload(); - $album->reload(); + public function post_test() { + $tag = test::random_tag(); - $request = (object)array("arguments" => array("albums")); + // Create an editable item to be tagged + $album = test::random_album(); + access::allow(identity::everybody(), "edit", $album); - $resources = array(); - foreach (array($album, $child) as $resource) { - $resources[] = array("type" => $resource->type, - "has_children" => $resource->children_count() > 0, - "path" => $resource->relative_url(), - "thumb_url" => $resource->thumb_url(), - "thumb_dimensions" => array("width" => $resource->thumb_width, - "height" => $resource->thumb_height), - "has_thumb" => $resource->has_thumb(), - "title" => $resource->title); - - } - $this->assert_equal(json_encode(array("status" => "OK", "resources" => $resources)), - tag_rest::get($request)); + // Add the album to the tag + $request = new stdClass(); + $request->url = rest::url("tag", $tag); + $request->params = new stdClass(); + $request->params->url = rest::url("item", $album); + $this->assert_equal_array( + array("url" => rest::url("tag_item", $tag, $album)), + tag_rest::post($request)); } - public function tag_rest_add_tags_for_item_no_path_test() { - $request = (object)array("arguments" => array("new,one")); - + public function post_with_no_item_url_test() { + $request = new stdClass(); try { tag_rest::post($request); } catch (Rest_Exception $e) { - $this->assert_equal("400 Bad request", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function tag_rest_add_tags_for_item_not_found_test() { - $photo = $this->_create_image(array("photos", "P1", "T1")); - $request = (object)array("path" => $photo->relative_url() . "b", - "arguments" => array("new,one")); - try { - tag_rest::post($request); - } catch (Kohana_404_Exception $k404) { - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function tag_rest_add_tags_for_item_no_access_test() { - $photo = $this->_create_image(array("photos", "P1", "T1")); - $this->_create_user(); - $request = (object)array("path" => $photo->relative_url(), - "arguments" => array("new,one")); - - try { - tag_rest::post($request); - } catch (Kohana_404_Exception $k404) { - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); + $this->assert_equal(400, $e->getCode()); + return; } - } - - public function tag_rest_add_tags_for_item_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "A1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - $sibling = $this->_create_image(array("photos", "P3"), $album); - access::allow(identity::registered_users(), "edit", $child); - $this->_create_user(); - $request = (object)array("path" => $photo->relative_url(), - "arguments" => array("new,one")); - $this->assert_equal( - json_encode(array("status" => "OK")), - tag_rest::post($request)); - $request = (object)array("arguments" => explode("/", $photo->relative_url())); - $this->assert_equal( - json_encode(array("status" => "OK", - "tags" => array("photos", "P1", "T1", "new", "one"))), - tag_rest::get($request)); + $this->assert_true(false, "Shouldn't get here"); } - public function tag_rest_update_tag_no_arguments_test() { - $request = (object)array("arguments" => array()); + public function put_test() { + $tag = test::random_tag(); + $request = new stdClass(); + $request->url = rest::url("tag", $tag); + $request->params = new stdClass(); + $request->params->name = "new name"; - try { - tag_rest::put($request); - } catch (Rest_Exception $e) { - $this->assert_equal("400 Bad request", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - } - - public function tag_rest_update_tag_one_arguments_test() { - $request = (object)array("arguments" => array("photos")); - try { - tag_rest::put($request); - } catch (Rest_Exception $e) { - $this->assert_equal("400 Bad request", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } - - $request = (object)array("arguments" => array(), "new_name" => "valid"); - try { - tag_rest::put($request); - } catch (Rest_Exception $e) { - $this->assert_equal("400 Bad request", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + tag_rest::put($request); + $this->assert_equal("new name", $tag->reload()->name); } - public function tag_rest_update_tags_not_found_test() { - $request = (object)array("arguments" => array("not"), "new_name" => "found"); + public function delete_tag_test() { + $tag = test::random_tag(); + $request = new stdClass(); + $request->url = rest::url("tag", $tag); + tag_rest::delete($request); - try { - tag_rest::put($request); - } catch (Kohana_404_Exception $k404) { - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_false($tag->reload()->loaded()); } - public function tag_rest_update_tags_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "A1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - $child->reload(); - $sibling = $this->_create_image(array("photos", "P3"), $album); - $child->reload(); - $album->reload(); - - $request = (object)array("arguments" => array("albums"), "new_name" => "new name"); + public function resolve_test() { + $tag = test::random_tag(); - $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::put($request)); - - $request = (object)array("arguments" => array("new name")); - $resources = array(); - foreach (array($album, $child) as $resource) { - $resources[] = array("type" => $resource->type, - "has_children" => $resource->children_count() > 0, - "path" => $resource->relative_url(), - "thumb_url" => $resource->thumb_url(), - "thumb_dimensions" => array("width" => $resource->thumb_width, - "height" => $resource->thumb_height), - "has_thumb" => $resource->has_thumb(), - "title" => $resource->title); - - } $this->assert_equal( - json_encode(array("status" => "OK", "resources" => $resources)), - tag_rest::get($request)); - } - - public function tag_rest_delete_tag_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "A1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - - $request = (object)array("arguments" => array("T1,P1")); - $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::delete($request)); - - $request = (object)array("arguments" => array("T1,P1")); - $this->assert_equal(json_encode(array("status" => "OK")), - tag_rest::get($request)); - } - - public function tag_rest_delete_tagc_from_item_test() { - $album = $this->_create_album(array("albums", "A1", "T1")); - $child = $this->_create_album(array("albums", "A1", "T1"), $album); - $photo = $this->_create_image(array("photos", "P1", "T1"), $child); - $request = (object)array("arguments" => array("T1,P1"), - $photo->relative_url()); - - $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::delete($request)); - - $request = (object)array("arguments" => explode("/", $photo->relative_url())); - $this->assert_equal(json_encode(array("status" => "OK", "tags" => array("photos"))), - tag_rest::get($request)); + $tag->as_array(), + rest::resolve(rest::url("tag", $tag))->as_array()); } } diff --git a/modules/tag/tests/Tag_Test.php b/modules/tag/tests/Tag_Test.php index c96e7f2b..defc4e89 100644 --- a/modules/tag/tests/Tag_Test.php +++ b/modules/tag/tests/Tag_Test.php @@ -17,26 +17,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Tag_Test extends Unit_Test_Case { +class Tag_Test extends Gallery_Unit_Test_Case { public function create_tag_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $tag1 = "tag1"; + $album = test::random_album(); - tag::add($album, $tag1); - $tag = ORM::factory("tag")->where("name", "=", $tag1)->find(); + tag::add($album, "tag1"); + $tag = ORM::factory("tag")->where("name", "=", "tag1")->find(); $this->assert_true(1, $tag->count); // Make sure adding the tag again doesn't increase the count - tag::add($album, $tag1); - $tag = ORM::factory("tag")->where("name", "=", $tag1)->find(); - $this->assert_true(1, $tag->count); + tag::add($album, "tag1"); + $this->assert_true(1, $tag->reload()->count); - $rand = rand(); - $album = album::create($root, $rand, $rand, $rand); - tag::add($album, $tag1); - $tag = ORM::factory("tag")->where("name", "=", $tag1)->find(); - $this->assert_true(2, $tag->count); + tag::add(test::random_album(), "tag1"); + $this->assert_true(2, $tag->reload()->count); } }
\ No newline at end of file diff --git a/modules/tag/tests/Tags_Rest_Helper_Test.php b/modules/tag/tests/Tags_Rest_Helper_Test.php new file mode 100644 index 00000000..cdf7bfdf --- /dev/null +++ b/modules/tag/tests/Tags_Rest_Helper_Test.php @@ -0,0 +1,71 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Tags_Rest_Helper_Test extends Gallery_Unit_Test_Case { + public function setup() { + try { + Database::instance()->query("TRUNCATE {tags}"); + Database::instance()->query("TRUNCATE {items_tags}"); + } catch (Exception $e) { + } + } + + public function get_test() { + $t1 = tag::add(item::root(), "t1"); + $t2 = tag::add(item::root(), "t2"); + + $request = new stdClass(); + $this->assert_equal_array( + array( + "url" => rest::url("tags"), + "members" => array( + rest::url("tag", $t1), + rest::url("tag", $t2))), + tags_rest::get($request)); + } + + public function post_test() { + access::allow(identity::everybody(), "edit", item::root()); + + $request = new stdClass(); + $request->params = new stdClass(); + $request->params->name = "test tag"; + $this->assert_equal( + array("url" => url::site("rest/tag/1")), + tags_rest::post($request)); + } + + public function post_fails_without_permissions_test() { + // We have to remove edit permissions from everywhere + Database::instance()->query("UPDATE {access_caches} SET edit_1=0"); + identity::set_active_user(identity::guest()); + + try { + $request = new stdClass(); + $request->params = new stdClass(); + $request->params->name = "test tag"; + tags_rest::post($request); + } catch (Exception $e) { + $this->assert_equal(403, $e->getCode()); + return; + } + $this->assert_true(false, "Shouldnt get here"); + } + +} diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index 96b86fff..03d9858b 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -21,12 +21,8 @@ class Admin_Users_Controller extends Admin_Controller { public function index() { $view = new Admin_View("admin.html"); $view->content = new View("admin_users.html"); - $view->content->users = ORM::factory("user") - ->order_by("name", "ASC") - ->find_all(); - $view->content->groups = ORM::factory("group") - ->order_by("name", "ASC") - ->find_all(); + $view->content->users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + $view->content->groups = ORM::factory("group")->order_by("name", "ASC")->find_all(); print $view; } @@ -34,31 +30,32 @@ class Admin_Users_Controller extends Admin_Controller { access::verify_csrf(); $form = $this->_get_user_add_form_admin(); - $valid = $form->validate(); - $name = $form->add_user->inputs["name"]->value; - if ($user = user::lookup_by_name($name)) { - $form->add_user->inputs["name"]->add_error("in_use", 1); + try { + $user = ORM::factory("user"); + $valid = $form->validate(); + $user->name = $form->add_user->inputs["name"]->value; + $user->full_name = $form->add_user->full_name->value; + $user->password = $form->add_user->password->value; + $user->email = $form->add_user->email->value; + $user->url = $form->add_user->url->value; + $user->locale = $form->add_user->locale->value; + $user->admin = $form->add_user->admin->checked; + $user->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->add_user->inputs[$key]->add_error($error, 1); + } $valid = false; } if ($valid) { - $user = user::create( - $name, $form->add_user->full_name->value, $form->add_user->password->value); - $user->email = $form->add_user->email->value; - $user->admin = $form->add_user->admin->checked; - - if ($form->add_user->locale) { - $desired_locale = $form->add_user->locale->value; - $user->locale = $desired_locale == "none" ? null : $desired_locale; - } $user->save(); module::event("user_add_form_admin_completed", $user, $form); - message::success(t("Created user %user_name", array("user_name" => $user->name))); print json_encode(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -85,8 +82,7 @@ class Admin_Users_Controller extends Admin_Controller { $name = $user->name; $user->delete(); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } $message = t("Deleted user %user_name", array("user_name" => $name)); @@ -112,43 +108,36 @@ class Admin_Users_Controller extends Admin_Controller { } $form = $this->_get_user_edit_form_admin($user); - $valid = $form->validate(); - if ($valid) { - $new_name = $form->edit_user->inputs["name"]->value; - $temp_user = user::lookup_by_name($new_name); - if ($new_name != $user->name && - ($temp_user && $temp_user->id != $user->id)) { - $form->edit_user->inputs["name"]->add_error("in_use", 1); - $valid = false; - } else { - $user->name = $new_name; - } - } - - if ($valid) { + try { + $valid = $form->validate(); + $user->name = $form->edit_user->inputs["name"]->value; $user->full_name = $form->edit_user->full_name->value; if ($form->edit_user->password->value) { $user->password = $form->edit_user->password->value; } $user->email = $form->edit_user->email->value; $user->url = $form->edit_user->url->value; - if ($form->edit_user->locale) { - $desired_locale = $form->edit_user->locale->value; - $user->locale = $desired_locale == "none" ? null : $desired_locale; - } - - // An admin can change the admin status for any user but themselves + $user->locale = $form->edit_user->locale->value; if ($user->id != identity::active_user()->id) { $user->admin = $form->edit_user->admin->checked; } + + $user->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_user->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + + if ($valid) { $user->save(); module::event("user_edit_form_admin_completed", $user, $form); - message::success(t("Changed user %user_name", array("user_name" => $user->name))); print json_encode(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -193,25 +182,26 @@ class Admin_Users_Controller extends Admin_Controller { access::verify_csrf(); $form = $this->_get_group_add_form_admin(); - $valid = $form->validate(); - if ($valid) { - $new_name = $form->add_group->inputs["name"]->value; - $group = group::lookup_by_name($new_name); - if (!empty($group)) { - $form->add_group->inputs["name"]->add_error("in_use", 1); - $valid = false; + try { + $valid = $form->validate(); + $group = ORM::factory("group"); + $group->name = $form->add_group->inputs["name"]->value; + $group->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->add_group->inputs[$key]->add_error($error, 1); } + $valid = false; } if ($valid) { - $group = group::create($new_name); $group->save(); message::success( t("Created group %group_name", array("group_name" => $group->name))); print json_encode(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -232,8 +222,7 @@ class Admin_Users_Controller extends Admin_Controller { $name = $group->name; $group->delete(); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } $message = t("Deleted group %group_name", array("group_name" => $name)); @@ -260,19 +249,19 @@ class Admin_Users_Controller extends Admin_Controller { } $form = $this->_get_group_edit_form_admin($group); - $valid = $form->validate(); - - if ($valid) { - $new_name = $form->edit_group->inputs["name"]->value; - $group = group::lookup_by_name($name); - if ($group->loaded()) { - $form->edit_group->inputs["name"]->add_error("in_use", 1); - $valid = false; + try { + $valid = $form->validate(); + $group->name = $form->edit_group->inputs["name"]->value; + $group->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_group->inputs[$key]->add_error($error, 1); } + $valid = false; } if ($valid) { - $group->name = $form->edit_group->inputs["name"]->value; $group->save(); message::success( t("Changed group %group_name", array("group_name" => $group->name))); @@ -280,8 +269,7 @@ class Admin_Users_Controller extends Admin_Controller { } else { message::error( t("Failed to change group %group_name", array("group_name" => $group->name))); - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -301,7 +289,7 @@ class Admin_Users_Controller extends Admin_Controller { $group = $form->group("edit_user")->label(t("Edit user")); $group->input("name")->label(t("Username"))->id("g-username")->value($user->name); $group->inputs["name"]->error_messages( - "in_use", t("There is already a user with that username")); + "conflict", t("There is already a user with that username")); $group->input("full_name")->label(t("Full name"))->id("g-fullname")->value($user->full_name); self::_add_locale_dropdown($group, $user); $group->password("password")->label(t("Password"))->id("g-password"); @@ -310,10 +298,6 @@ class Admin_Users_Controller extends Admin_Controller { $group->input("email")->label(t("Email"))->id("g-email")->value($user->email); $group->input("url")->label(t("URL"))->id("g-url")->value($user->url); $group->checkbox("admin")->label(t("Admin"))->id("g-admin")->checked($user->admin); - $form->add_rules_from($user); - $minimum_length = module::get_var("user", "mininum_password_length", 5); - $form->edit_user->password - ->rules($minimum_length ? "length[$minimum_length, 40]" : "length[40]"); module::event("user_edit_form_admin", $user, $form); $group->submit("")->value(t("Modify User")); @@ -324,7 +308,7 @@ class Admin_Users_Controller extends Admin_Controller { $form = new Forge("admin/users/add_user", "", "post", array("id" => "g-add-user-form")); $group = $form->group("add_user")->label(t("Add user")); $group->input("name")->label(t("Username"))->id("g-username") - ->error_messages("in_use", t("There is already a user with that username")); + ->error_messages("conflict", t("There is already a user with that username")); $group->input("full_name")->label(t("Full name"))->id("g-fullname"); $group->password("password")->label(t("Password"))->id("g-password"); $group->password("password2")->label(t("Confirm password"))->id("g-password2") @@ -333,31 +317,25 @@ class Admin_Users_Controller extends Admin_Controller { $group->input("url")->label(t("URL"))->id("g-url"); self::_add_locale_dropdown($group); $group->checkbox("admin")->label(t("Admin"))->id("g-admin"); - $form->add_rules_from(ORM::factory("user")); - - $minimum_length = module::get_var("user", "mininum_password_length", 5); - $form->add_user->password - ->rules($minimum_length ? "required|length[$minimum_length, 40]" : "length[40]"); module::event("user_add_form_admin", $user, $form); $group->submit("")->value(t("Add user")); return $form; } - private function _add_locale_dropdown(&$form, $user=null) { + private static function _add_locale_dropdown(&$form, $user=null) { $locales = locales::installed(); foreach ($locales as $locale => $display_name) { $locales[$locale] = SafeString::of_safe_html($display_name); } - if (count($locales) > 1) { - // Put "none" at the first position in the array - $locales = array_merge(array("" => t("« none »")), $locales); - $selected_locale = ($user && $user->locale) ? $user->locale : ""; - $form->dropdown("locale") - ->label(t("Language Preference")) - ->options($locales) - ->selected($selected_locale); - } + + // Put "none" at the first position in the array + $locales = array_merge(array("" => t("« none »")), $locales); + $selected_locale = ($user && $user->locale) ? $user->locale : ""; + $form->dropdown("locale") + ->label(t("Language Preference")) + ->options($locales) + ->selected($selected_locale); } private function _get_user_delete_form_admin($user) { @@ -375,9 +353,8 @@ class Admin_Users_Controller extends Admin_Controller { $form_group = $form->group("edit_group")->label(t("Edit group")); $form_group->input("name")->label(t("Name"))->id("g-name")->value($group->name); $form_group->inputs["name"]->error_messages( - "in_use", t("There is already a group with that name")); + "conflict", t("There is already a group with that name")); $form_group->submit("")->value(t("Save")); - $form->add_rules_from($group); return $form; } @@ -386,9 +363,8 @@ class Admin_Users_Controller extends Admin_Controller { $form_group = $form->group("add_group")->label(t("Add group")); $form_group->input("name")->label(t("Name"))->id("g-name"); $form_group->inputs["name"]->error_messages( - "in_use", t("There is already a group with that name")); + "conflict", t("There is already a group with that name")); $form_group->submit("")->value(t("Add group")); - $form->add_rules_from(ORM::factory("group")); return $form; } diff --git a/modules/user/controllers/users.php b/modules/user/controllers/users.php index ca218393..d0c67dd1 100644 --- a/modules/user/controllers/users.php +++ b/modules/user/controllers/users.php @@ -26,34 +26,43 @@ class Users_Controller extends Controller { } $form = $this->_get_edit_form($user); - $valid = $form->validate(); - if ($valid) { + try { + $valid = $form->validate(); $user->full_name = $form->edit_user->full_name->value; if ($form->edit_user->password->value) { $user->password = $form->edit_user->password->value; } $user->email = $form->edit_user->email->value; $user->url = $form->edit_user->url->value; - if ($form->edit_user->locale) { - $desired_locale = $form->edit_user->locale->value; - $new_locale = $desired_locale == "none" ? null : $desired_locale; - if ($new_locale != $user->locale) { - // Delete the session based locale preference - setcookie("g_locale", "", time() - 24 * 3600, "/"); - } - $user->locale = $new_locale; + + if ($user->locale != $form->edit_user->locale->value) { + $user->locale = $form->edit_user->locale->value; + $flush_locale_cookie = true; + } + + $user->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->edit_user->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + + if ($valid) { + if (isset($flush_locale_cookie)) { + // Delete the session based locale preference + setcookie("g_locale", "", time() - 24 * 3600, "/"); } + $user->save(); module::event("user_edit_form_completed", $user, $form); - message::success(t("User information updated.")); print json_encode( array("result" => "success", "resource" => url::site("users/{$user->id}"))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -71,18 +80,18 @@ class Users_Controller extends Controller { private function _get_edit_form($user) { $form = new Forge("users/update/$user->id", "", "post", array("id" => "g-edit-user-form")); $group = $form->group("edit_user")->label(t("Edit User: %name", array("name" => $user->name))); - $group->input("full_name")->label(t("Full Name"))->id("g-fullname")->value($user->full_name); + $group->input("full_name")->label(t("Full Name"))->id("g-fullname")->value($user->full_name) + ->error_messages("length", t("Your name is too long")); self::_add_locale_dropdown($group, $user); - $group->password("password")->label(t("Password"))->id("g-password"); + $group->password("password")->label(t("Password"))->id("g-password") + ->error_messages("min_length", t("Your password is too short")); $group->password("password2")->label(t("Confirm Password"))->id("g-password2") - ->matches($group->password); - $group->input("email")->label(t("Email"))->id("g-email")->value($user->email); + ->matches($group->password) + ->error_messages("matches", t("The passwords you entered do not match")); + $group->input("email")->label(t("Email"))->id("g-email")->value($user->email) + ->error_messages("email", t("You must enter a valid email address")) + ->error_messages("required", t("You must enter a valid email address")); $group->input("url")->label(t("URL"))->id("g-url")->value($user->url); - $form->add_rules_from($user); - - $minimum_length = module::get_var("user", "mininum_password_length", 5); - $form->edit_user->password - ->rules($minimum_length ? "length[$minimum_length, 40]" : "length[40]"); module::event("user_edit_form", $user, $form); $group->submit("")->value(t("Save")); @@ -95,14 +104,13 @@ class Users_Controller extends Controller { foreach ($locales as $locale => $display_name) { $locales[$locale] = SafeString::of_safe_html($display_name); } - if (count($locales) > 1) { - // Put "none" at the first position in the array - $locales = array_merge(array("" => t("« none »")), $locales); - $selected_locale = ($user && $user->locale) ? $user->locale : ""; - $form->dropdown("locale") - ->label(t("Language Preference")) - ->options($locales) - ->selected($selected_locale); - } + + // Put "none" at the first position in the array + $locales = array_merge(array("" => t("« none »")), $locales); + $selected_locale = ($user && $user->locale) ? $user->locale : ""; + $form->dropdown("locale") + ->label(t("Language Preference")) + ->options($locales) + ->selected($selected_locale); } } diff --git a/modules/user/helpers/group.php b/modules/user/helpers/group.php index 2ada0ac1..38124b0d 100644 --- a/modules/user/helpers/group.php +++ b/modules/user/helpers/group.php @@ -25,23 +25,6 @@ */ class group_Core { /** - * Create a new group. - * - * @param string $name - * @return Group_Definition the group object - */ - static function create($name) { - $group = ORM::factory("group")->where("name", "=", $name)->find(); - if ($group->loaded()) { - throw new Exception("@todo GROUP_ALREADY_EXISTS $name"); - } - - $group->name = $name; - $group->save(); - return $group; - } - - /** * The group of all possible visitors. This includes the guest user. * * @return Group_Definition the group object diff --git a/modules/user/helpers/user.php b/modules/user/helpers/user.php index e092aecc..3561021f 100644 --- a/modules/user/helpers/user.php +++ b/modules/user/helpers/user.php @@ -36,32 +36,6 @@ class user_Core { } /** - * Create a new user. - * - * @param string $name - * @param string $full_name - * @param string $password - * @return User_Model - */ - static function create($name, $full_name, $password) { - $user = ORM::factory("user")->where("name", "=", $name)->find(); - if ($user->loaded()) { - throw new Exception("@todo USER_ALREADY_EXISTS $name"); - } - - $user->name = $name; - $user->full_name = $full_name; - $user->password = $password; - - // Required groups - $user->add(group::everybody()); - $user->add(group::registered_users()); - - $user->save(); - return $user; - } - - /** * Is the password provided correct? * * @param user User Model diff --git a/modules/user/helpers/user_installer.php b/modules/user/helpers/user_installer.php index 38f8020b..729f087a 100644 --- a/modules/user/helpers/user_installer.php +++ b/modules/user/helpers/user_installer.php @@ -29,9 +29,21 @@ class user_installer { static function upgrade($version) { if ($version == 1) { module::set_var("user", "mininum_password_length", 5); - module::set_version("user", $version = 2); } + + if ($version == 2) { + db::build() + ->update("users") + ->set("email", "unknown@unknown.com") + ->where("guest", "=", 0) + ->and_open() + ->where("email", "IS", null) + ->or_where("email", "=", "") + ->close() + ->execute(); + module::set_version("user", $version = 3); + } } static function uninstall() { @@ -85,20 +97,28 @@ class user_installer { UNIQUE KEY(`user_id`, `group_id`)) DEFAULT CHARSET=utf8;"); - $everybody = group::create("Everybody"); + $everybody = ORM::factory("group"); + $everybody->name = "Everybody"; $everybody->special = true; $everybody->save(); - $registered = group::create("Registered Users"); + $registered = ORM::factory("group"); + $registered->name = "Registered Users"; $registered->special = true; $registered->save(); - $guest = user::create("guest", "Guest User", ""); + $guest = ORM::factory("user"); + $guest->name = "guest"; + $guest->full_name = "Guest User"; + $guest->password = ""; $guest->guest = true; - $guest->remove($registered); $guest->save(); - $admin = user::create("admin", "Gallery Administrator", "admin"); + $admin = ORM::factory("user"); + $admin->name = "admin"; + $admin->full_name = "Gallery Administrator"; + $admin->password = "admin"; + $admin->email = "unknown@unknown.com"; $admin->admin = true; $admin->save(); @@ -109,7 +129,7 @@ class user_installer { access::allow($registered, "view", $root); access::allow($registered, "view_full", $root); - module::set_version("user", 2); module::set_var("user", "mininum_password_length", 5); + module::set_version("user", 3); } }
\ No newline at end of file diff --git a/modules/user/libraries/drivers/IdentityProvider/Gallery.php b/modules/user/libraries/drivers/IdentityProvider/Gallery.php index d874512c..666f185f 100644 --- a/modules/user/libraries/drivers/IdentityProvider/Gallery.php +++ b/modules/user/libraries/drivers/IdentityProvider/Gallery.php @@ -38,8 +38,13 @@ class IdentityProvider_Gallery_Driver implements IdentityProvider_Driver { /** * @see IdentityProvider_Driver::create_user. */ - public function create_user($name, $full_name, $password) { - return user::create($name, $full_name, $password); + public function create_user($name, $full_name, $password, $email) { + $user = ORM::factory("user"); + $user->name = $name; + $user->full_name = $full_name; + $user->password = $password; + $user->email = $email; + return $user->save(); } /** @@ -91,7 +96,9 @@ class IdentityProvider_Gallery_Driver implements IdentityProvider_Driver { * @see IdentityProvider_Driver::create_group. */ public function create_group($name) { - return group::create($name); + $group = ORM::factory("group"); + $group->name = $name; + return $group->save(); } /** diff --git a/modules/user/models/group.php b/modules/user/models/group.php index 10f6f4b3..82843ad1 100644 --- a/modules/user/models/group.php +++ b/modules/user/models/group.php @@ -20,9 +20,6 @@ class Group_Model extends ORM implements Group_Definition { protected $has_and_belongs_to_many = array("users"); - var $form_rules = array( - "name" => "required|length[4,255]"); - /** * @see ORM::delete() */ @@ -37,18 +34,44 @@ class Group_Model extends ORM implements Group_Definition { return $this->users->find_all(); } - public function save() { - if (!$this->loaded()) { - $created = 1; + /** + * Specify our rules here so that we have access to the instance of this model. + */ + public function validate(Validation $array=null) { + // validate() is recursive, only modify the rules on the outermost call. + if (!$array) { + $this->rules = array( + "name" => array("rules" => array("required", "length[4,255]"), + "callbacks" => array(array($this, "valid_name")))); } - $original = clone $this->original(); - parent::save(); - if (isset($created)) { + parent::validate($array); + } + + public function save() { + if (!$this->loaded()) { + // New group + parent::save(); module::event("group_created", $this); } else { + // Updated group + $original = ORM::factory("group", $this->id); + parent::save(); module::event("group_updated", $original, $this); } + return $this; } + + /** + * Validate the user name. Make sure there are no conflicts. + */ + public function valid_name(Validation $v, $field) { + if (db::build()->from("groups") + ->where("name", "=", $this->name) + ->where("id", "<>", $this->id) + ->count_records() == 1) { + $v->add_error("name", "conflict"); + } + } }
\ No newline at end of file diff --git a/modules/user/models/user.php b/modules/user/models/user.php index edba2a2c..baac9315 100644 --- a/modules/user/models/user.php +++ b/modules/user/models/user.php @@ -19,14 +19,7 @@ */ class User_Model extends ORM implements User_Definition { protected $has_and_belongs_to_many = array("groups"); - - var $form_rules = array( - "name" => "required|length[1,32]", - "full_name" => "length[0,255]", - "email" => "required|valid_email|length[1,255]", - "password" => "length[1,40]", - "url" => "valid_url", - "locale" => "length[2,10]"); + protected $password_length = null; public function __set($column, $value) { switch ($column) { @@ -35,6 +28,7 @@ class User_Model extends ORM implements User_Definition { break; case "password": + $this->password_length = strlen($value); $value = user::hash_password($value); break; } @@ -65,18 +59,51 @@ class User_Model extends ORM implements User_Definition { return $this->groups->find_all(); } + /** + * Specify our rules here so that we have access to the instance of this model. + */ + public function validate(Validation $array=null) { + // validate() is recursive, only modify the rules on the outermost call. + if (!$array) { + $this->rules = array( + "admin" => array("callbacks" => array(array($this, "valid_admin"))), + "email" => array("rules" => array("length[1,255]", "valid::email"), + "callbacks" => array(array($this, "valid_email"))), + "full_name" => array("rules" => array("length[0,255]")), + "locale" => array("rules" => array("length[2,10]")), + "name" => array("rules" => array("length[1,32]", "required"), + "callbacks" => array(array($this, "valid_name"))), + "password" => array("callbacks" => array(array($this, "valid_password"))), + "url" => array("rules" => array("valid::url")), + ); + } + + parent::validate($array); + } + + /** + * Handle any business logic necessary to create or update a user. + * @see ORM::save() + * + * @return ORM User_Model + */ public function save() { if (!$this->loaded()) { - $created = 1; - } + // New user + $this->add(group::everybody()); + if (!$this->guest) { + $this->add(group::registered_users()); + } - $original = clone $this->original(); - parent::save(); - if (isset($created)) { + parent::save(); module::event("user_created", $this); } else { + // Updated user + $original = ORM::factory("user", $this->id); + parent::save(); module::event("user_updated", $original, $this); } + return $this; } @@ -88,4 +115,55 @@ class User_Model extends ORM implements User_Definition { public function display_name() { return empty($this->full_name) ? $this->name : $this->full_name; } + + /** + * Validate the user name. Make sure there are no conflicts. + */ + public function valid_name(Validation $v, $field) { + if (db::build()->from("users") + ->where("name", "=", $this->name) + ->where("id", "<>", $this->id) + ->count_records() == 1) { + $v->add_error("name", "conflict"); + } + } + + /** + * Validate the password. + */ + public function valid_password(Validation $v, $field) { + if ($this->guest) { + return; + } + + if (!$this->loaded() || $this->password_length) { + $minimum_length = module::get_var("user", "mininum_password_length", 5); + if ($this->password_length < $minimum_length) { + $v->add_error("password", "min_length"); + } + } + } + + /** + * Validate the admin bit. + */ + public function valid_admin(Validation $v, $field) { + $active = identity::active_user(); + if ($this->id == $active->id && $active->admin && !$this->admin) { + $v->add_error("admin", "locked"); + } + } + + /** + * Validate the email field. + */ + public function valid_email(Validation $v, $field) { + if ($this->guest) { // guests don't require an email address + return; + } + + if (empty($this->email)) { + $v->add_error("email", "required"); + } + } } diff --git a/modules/user/module.info b/modules/user/module.info index d1e02382..185a3e3a 100644 --- a/modules/user/module.info +++ b/modules/user/module.info @@ -1,4 +1,4 @@ name = "Users and Groups" description = "Gallery 3 user and group management" -version = 2 +version = 3 diff --git a/modules/user/tests/No_Direct_ORM_Access_Test.php b/modules/user/tests/No_Direct_ORM_Access_Test.php index 440321fa..c372258e 100644 --- a/modules/user/tests/No_Direct_ORM_Access_Test.php +++ b/modules/user/tests/No_Direct_ORM_Access_Test.php @@ -19,7 +19,7 @@ */ require_once(MODPATH . "gallery/tests/Gallery_Filters.php"); -class No_Direct_ORM_Access_Test extends Unit_Test_Case { +class No_Direct_ORM_Access_Test extends Gallery_Unit_Test_Case { public function no_access_to_users_table_test() { $dir = new UserModuleFilterIterator( new PhpCodeFilterIterator( diff --git a/modules/user/tests/User_Groups_Test.php b/modules/user/tests/User_Groups_Test.php index 163b7d79..d0026d89 100644 --- a/modules/user/tests/User_Groups_Test.php +++ b/modules/user/tests/User_Groups_Test.php @@ -18,33 +18,10 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class User_Groups_Test extends Unit_Test_Case { - public function teardown() { - try { - $group = ORM::factory("group")->where("name", "=", "user_groups_test")->find(); - if ($group->loaded()) { - $group->delete(); - } - } catch (Exception $e) { } - - try { - $user = ORM::factory("user")->where("name", "=", "user_groups_test")->find(); - if ($user->loaded()) { - $user->delete(); - } - } catch (Exception $e) { } - } - +class User_Groups_Test extends Gallery_Unit_Test_Case { public function add_user_to_group_test() { - $user = ORM::factory("user"); - $user->name = "user_groups_test"; - $user->full_name = "user groups test"; - $user->password = "test password"; - $user->save(); - - $group = ORM::factory("group"); - $group->name = "user_groups_test"; - $group->save(); + $user = test::random_user(); + $group = test::random_group(); $group->add($user); $group->save(); diff --git a/modules/user/tests/User_Installer_Test.php b/modules/user/tests/User_Installer_Test.php index 12a10eda..b3c5960a 100644 --- a/modules/user/tests/User_Installer_Test.php +++ b/modules/user/tests/User_Installer_Test.php @@ -22,7 +22,7 @@ * This test case operates under the assumption that user_installer::install() is called by the * test controller before it starts. */ -class User_Installer_Test extends Unit_Test_Case { +class User_Installer_Test extends Gallery_Unit_Test_Case { public function install_creates_admin_user_test() { $user = ORM::factory("user", 1); $this->assert_equal("guest", $user->name); diff --git a/modules/user/views/user_form.html.php b/modules/user/views/user_form.html.php index 039ae8a5..4ce2b532 100644 --- a/modules/user/views/user_form.html.php +++ b/modules/user/views/user_form.html.php @@ -1,5 +1,5 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<script type="text/javascript"> +<script type="text/javascript"> $("form").ready(function(){ $('input[name="password"]').user_password_strength(); }); diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 2a1d5f60..f535ad08 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -52,9 +52,7 @@ class Admin_Watermarks_Controller extends Admin_Controller { array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -84,9 +82,7 @@ class Admin_Watermarks_Controller extends Admin_Controller { array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -127,9 +123,7 @@ class Admin_Watermarks_Controller extends Admin_Controller { array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); + print json_encode(array("result" => "error", "form" => (string) $form)); } } diff --git a/modules/watermark/helpers/watermark_installer.php b/modules/watermark/helpers/watermark_installer.php index b3e91044..e45ef746 100644 --- a/modules/watermark/helpers/watermark_installer.php +++ b/modules/watermark/helpers/watermark_installer.php @@ -33,7 +33,7 @@ class watermark_installer { DEFAULT CHARSET=utf8;"); @mkdir(VARPATH . "modules/watermark"); - module::set_version("watermark", 1); + module::set_version("watermark", 2); } static function uninstall() { |