summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.build_number2
-rw-r--r--installer/install.sql19
-rw-r--r--installer/installer.php10
-rw-r--r--lib/flowplayer.controls.swfbin27007 -> 36843 bytes
-rw-r--r--lib/flowplayer.js216
-rw-r--r--lib/flowplayer.pseudostreaming.swfbin3763 -> 4477 bytes
-rw-r--r--lib/flowplayer.swfbin110762 -> 120221 bytes
-rw-r--r--lib/images/apple-touch-icon.pngbin0 -> 4441 bytes
-rw-r--r--lib/jquery-ui.js2
-rw-r--r--modules/akismet/module.info4
-rw-r--r--modules/comment/controllers/admin_manage_comments.php39
-rw-r--r--modules/comment/helpers/comment_installer.php19
-rw-r--r--modules/comment/models/comment.php4
-rw-r--r--modules/comment/module.info6
-rw-r--r--modules/comment/views/admin_manage_comments.html.php211
-rw-r--r--modules/comment/views/admin_manage_comments_queue.html.php157
-rw-r--r--modules/digibug/controllers/digibug.php6
-rw-r--r--modules/digibug/module.info4
-rw-r--r--modules/exif/helpers/exif.php10
-rw-r--r--modules/exif/helpers/exif_event.php6
-rw-r--r--modules/exif/module.info4
-rw-r--r--modules/g2_import/controllers/admin_g2_import.php74
-rw-r--r--modules/g2_import/controllers/g2.php6
-rw-r--r--modules/g2_import/helpers/g2_import.php424
-rw-r--r--modules/g2_import/helpers/g2_import_event.php2
-rw-r--r--modules/g2_import/helpers/g2_import_task.php8
-rw-r--r--modules/g2_import/module.info4
-rw-r--r--modules/g2_import/views/admin_g2_import.html.php177
-rw-r--r--modules/gallery/config/locale.php11
-rw-r--r--modules/gallery/controllers/admin_graphics.php2
-rw-r--r--modules/gallery/controllers/admin_modules.php6
-rw-r--r--modules/gallery/controllers/admin_theme_options.php14
-rw-r--r--modules/gallery/controllers/admin_themes.php3
-rw-r--r--modules/gallery/controllers/albums.php22
-rw-r--r--modules/gallery/controllers/login.php5
-rw-r--r--modules/gallery/controllers/movies.php24
-rw-r--r--modules/gallery/controllers/photos.php24
-rw-r--r--modules/gallery/controllers/quick.php4
-rw-r--r--modules/gallery/controllers/uploader.php2
-rw-r--r--modules/gallery/css/gallery.css6
-rw-r--r--modules/gallery/helpers/MY_num.php14
-rw-r--r--modules/gallery/helpers/encoding.php35
-rw-r--r--modules/gallery/helpers/gallery.php16
-rw-r--r--modules/gallery/helpers/gallery_block.php26
-rw-r--r--modules/gallery/helpers/gallery_event.php6
-rw-r--r--modules/gallery/helpers/gallery_installer.php17
-rw-r--r--modules/gallery/helpers/gallery_rss.php8
-rw-r--r--modules/gallery/helpers/gallery_theme.php2
-rw-r--r--modules/gallery/helpers/graphics.php38
-rw-r--r--modules/gallery/helpers/item.php29
-rw-r--r--modules/gallery/helpers/legal_file.php83
-rw-r--r--modules/gallery/helpers/module.php2
-rw-r--r--modules/gallery/helpers/random.php10
-rw-r--r--modules/gallery/helpers/system.php21
-rw-r--r--modules/gallery/helpers/theme.php23
-rw-r--r--modules/gallery/images/missing_photo.pngbin0 -> 1570 bytes
-rw-r--r--modules/gallery/libraries/Breadcrumb.php70
-rw-r--r--modules/gallery/libraries/Form_Uploadify.php15
-rw-r--r--modules/gallery/libraries/Gallery_View.php55
-rw-r--r--modules/gallery/libraries/IdentityProvider.php12
-rw-r--r--modules/gallery/libraries/InPlaceEdit.php8
-rw-r--r--modules/gallery/libraries/Theme_View.php47
-rw-r--r--modules/gallery/models/item.php80
-rw-r--r--modules/gallery/module.info6
-rw-r--r--modules/gallery/tests/Albums_Controller_Test.php2
-rw-r--r--modules/gallery/tests/Breadcrumb_Test.php36
-rw-r--r--modules/gallery/tests/File_Structure_Test.php1
-rw-r--r--modules/gallery/tests/Gallery_Filters.php1
-rw-r--r--modules/gallery/tests/Item_Helper_Test.php4
-rw-r--r--modules/gallery/tests/Item_Model_Test.php23
-rw-r--r--modules/gallery/tests/Num_Helper_Test.php32
-rw-r--r--modules/gallery/tests/System_Helper_Test.php (renamed from modules/gallery/libraries/MY_Pagination.php)20
-rw-r--r--modules/gallery/tests/controller_auth_data.txt1
-rw-r--r--modules/gallery/tests/xss_data.txt174
-rw-r--r--modules/gallery/views/admin_block_welcome.html.php2
-rw-r--r--modules/gallery/views/admin_languages.html.php2
-rw-r--r--modules/gallery/views/admin_maintenance.html.php2
-rw-r--r--modules/gallery/views/admin_modules.html.php54
-rw-r--r--modules/gallery/views/admin_themes.html.php12
-rw-r--r--modules/gallery/views/admin_themes_buttonset.html.php48
-rw-r--r--modules/gallery/views/form_uploadify.html.php38
-rw-r--r--modules/gallery/views/movieplayer.html.php2
-rw-r--r--modules/gallery/views/upgrade_checker_block.html.php4
-rw-r--r--modules/gallery/views/upgrader.html.php2
-rw-r--r--modules/gallery_unit_test/helpers/test.php30
-rw-r--r--modules/gallery_unit_test/vendor/LoremIpsum.class.php461
-rw-r--r--modules/image_block/module.info4
-rw-r--r--modules/info/module.info4
-rw-r--r--modules/notification/module.info4
-rw-r--r--modules/organize/module.info4
-rw-r--r--modules/recaptcha/module.info4
-rw-r--r--modules/rest/module.info4
-rw-r--r--modules/rss/module.info4
-rw-r--r--modules/search/controllers/search.php61
-rw-r--r--modules/search/helpers/search.php61
-rw-r--r--modules/search/module.info4
-rw-r--r--modules/server_add/controllers/server_add.php2
-rw-r--r--modules/server_add/helpers/server_add_theme.php16
-rw-r--r--modules/server_add/js/admin.js8
-rw-r--r--modules/server_add/module.info4
-rw-r--r--modules/server_add/views/admin_server_add.html.php23
-rw-r--r--modules/slideshow/module.info4
-rw-r--r--modules/tag/controllers/admin_tags.php31
-rw-r--r--modules/tag/controllers/tag.php62
-rw-r--r--modules/tag/controllers/tags.php3
-rw-r--r--modules/tag/helpers/tag.php20
-rw-r--r--modules/tag/helpers/tag_block.php2
-rw-r--r--modules/tag/helpers/tag_event.php14
-rw-r--r--modules/tag/helpers/tag_installer.php7
-rw-r--r--modules/tag/helpers/tag_item_rest.php1
-rw-r--r--modules/tag/helpers/tag_items_rest.php1
-rw-r--r--modules/tag/models/tag.php64
-rw-r--r--modules/tag/module.info6
-rw-r--r--modules/tag/tests/Tag_Test.php50
-rw-r--r--modules/user/controllers/admin_users.php8
-rw-r--r--modules/user/controllers/password.php5
-rw-r--r--modules/user/helpers/user_installer.php12
-rw-r--r--modules/user/libraries/drivers/IdentityProvider/Gallery.php2
-rw-r--r--modules/user/models/group.php6
-rw-r--r--modules/user/models/user.php8
-rw-r--r--modules/user/module.info6
-rw-r--r--modules/user/views/admin_users.html.php2
-rw-r--r--modules/user/views/admin_users_group.html.php2
-rw-r--r--modules/user/views/reset_password.html.php5
-rw-r--r--modules/watermark/module.info4
-rw-r--r--themes/admin_wind/css/screen-rtl.css400
-rw-r--r--themes/admin_wind/css/screen.css357
-rw-r--r--themes/admin_wind/theme.info4
-rw-r--r--themes/admin_wind/views/admin.html.php8
-rw-r--r--themes/admin_wind/views/pager.html.php44
-rw-r--r--themes/admin_wind/views/paginator.html.php88
-rw-r--r--themes/wind/css/screen-rtl.css333
-rw-r--r--themes/wind/css/screen.css361
-rw-r--r--themes/wind/js/ui.init.js2
-rw-r--r--themes/wind/theme.info4
-rw-r--r--themes/wind/views/page.html.php33
136 files changed, 3591 insertions, 1686 deletions
diff --git a/.build_number b/.build_number
index 4521d1ec..14584f9b 100644
--- a/.build_number
+++ b/.build_number
@@ -3,4 +3,4 @@
; process. You don't need to edit it. In fact..
;
; DO NOT EDIT THIS FILE BY HAND!
-build_number=71
+build_number=171
diff --git a/installer/install.sql b/installer/install.sql
index 865cb2a4..2b8ec11e 100644
--- a/installer/install.sql
+++ b/installer/install.sql
@@ -67,8 +67,8 @@ CREATE TABLE {comments} (
`server_http_referer` varchar(255) DEFAULT NULL,
`server_http_user_agent` varchar(128) DEFAULT NULL,
`server_query_string` varchar(64) DEFAULT NULL,
- `server_remote_addr` varchar(32) DEFAULT NULL,
- `server_remote_host` varchar(64) DEFAULT NULL,
+ `server_remote_addr` varchar(40) DEFAULT NULL,
+ `server_remote_host` varchar(255) DEFAULT NULL,
`server_remote_port` varchar(16) DEFAULT NULL,
`state` varchar(15) DEFAULT 'unpublished',
`text` text,
@@ -244,15 +244,15 @@ CREATE TABLE {modules} (
KEY `weight` (`weight`)
) AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
-INSERT INTO {modules} VALUES (1,1,'gallery',46,1);
-INSERT INTO {modules} VALUES (2,1,'user',3,2);
-INSERT INTO {modules} VALUES (3,1,'comment',3,3);
+INSERT INTO {modules} VALUES (1,1,'gallery',49,1);
+INSERT INTO {modules} VALUES (2,1,'user',4,2);
+INSERT INTO {modules} VALUES (3,1,'comment',4,3);
INSERT INTO {modules} VALUES (4,1,'organize',4,4);
INSERT INTO {modules} VALUES (5,1,'info',2,5);
INSERT INTO {modules} VALUES (6,1,'rss',1,6);
INSERT INTO {modules} VALUES (7,1,'search',1,7);
INSERT INTO {modules} VALUES (8,1,'slideshow',2,8);
-INSERT INTO {modules} VALUES (9,1,'tag',2,9);
+INSERT INTO {modules} VALUES (9,1,'tag',3,9);
DROP TABLE IF EXISTS {outgoing_translations};
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -382,7 +382,7 @@ CREATE TABLE {vars} (
`value` text,
PRIMARY KEY (`id`),
UNIQUE KEY `module_name` (`module_name`,`name`)
-) AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;
+) AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;
/*!40101 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');
@@ -406,15 +406,17 @@ INSERT INTO {vars} VALUES (NULL,'gallery','admin_area_timeout','5400');
INSERT INTO {vars} VALUES (NULL,'gallery','maintenance_mode','0');
INSERT INTO {vars} VALUES (NULL,'gallery','visible_title_length','15');
INSERT INTO {vars} VALUES (NULL,'gallery','favicon_url','lib/images/favicon.ico');
+INSERT INTO {vars} VALUES (NULL,'gallery','apple_touch_icon_url','lib/images/apple-touch-icon.png');
INSERT INTO {vars} VALUES (NULL,'gallery','email_from','unknown@unknown.com');
INSERT INTO {vars} VALUES (NULL,'gallery','email_reply_to','unknown@unknown.com');
INSERT INTO {vars} VALUES (NULL,'gallery','email_line_length','70');
INSERT INTO {vars} VALUES (NULL,'gallery','email_header_separator','s:1:\"\n\";');
INSERT INTO {vars} VALUES (NULL,'gallery','show_user_profiles_to','registered_users');
INSERT INTO {vars} VALUES (NULL,'gallery','extra_binary_paths','/usr/local/bin:/opt/local/bin:/opt/bin');
+INSERT INTO {vars} VALUES (NULL,'gallery','timezone',NULL);
INSERT INTO {vars} VALUES (NULL,'gallery','blocks_site_sidebar','a:4:{i:10;a:2:{i:0;s:7:\"gallery\";i:1;s:8:\"language\";}i:11;a:2:{i:0;s:4:\"info\";i:1;s:8:\"metadata\";}i:12;a:2:{i:0;s:3:\"rss\";i:1;s:9:\"rss_feeds\";}i:13;a:2:{i:0;s:3:\"tag\";i:1;s:3:\"tag\";}}');
INSERT INTO {vars} VALUES (NULL,'gallery','identity_provider','user');
-INSERT INTO {vars} VALUES (NULL,'user','mininum_password_length','5');
+INSERT INTO {vars} VALUES (NULL,'user','minimum_password_length','5');
INSERT INTO {vars} VALUES (NULL,'comment','spam_caught','0');
INSERT INTO {vars} VALUES (NULL,'comment','access_permissions','everybody');
INSERT INTO {vars} VALUES (NULL,'info','show_title','1');
@@ -423,3 +425,4 @@ INSERT INTO {vars} VALUES (NULL,'info','show_owner','1');
INSERT INTO {vars} VALUES (NULL,'info','show_name','1');
INSERT INTO {vars} VALUES (NULL,'info','show_captured','1');
INSERT INTO {vars} VALUES (NULL,'slideshow','max_scale','0');
+INSERT INTO {vars} VALUES (NULL,'tag','tag_cloud_size','30');
diff --git a/installer/installer.php b/installer/installer.php
index c23d918f..0bef57ae 100644
--- a/installer/installer.php
+++ b/installer/installer.php
@@ -191,8 +191,10 @@ class installer {
$errors[] = "Gallery 3 requires a MySQL database, but PHP doesn't have either the <a href=\"http://php.net/mysql\">MySQL</a> or the <a href=\"http://php.net/mysqli\">MySQLi</a> extension.";
}
- if (!@preg_match("/^.$/u", utf8_encode("\xF1"))) {
- $errors[] = "PHP is missing <a href=\"http://php.net/pcre\">Perl-Compatible Regular Expression</a> support.";
+ if (!preg_match("/^.$/u", "ñ")) {
+ $errors[] = "PHP is missing <a href=\"http://php.net/pcre\">Perl-Compatible Regular Expression</a> with UTF-8 support.";
+ } else if (!preg_match("/^\pL$/u", "ñ")) {
+ $errors[] = "PHP is missing <a href=\"http://php.net/pcre\">Perl-Compatible Regular Expression</a> with Unicode support.";
}
if (!(function_exists("spl_autoload_register"))) {
@@ -211,6 +213,10 @@ class installer {
$errors[] = "PHP is missing the <a href=\"http://php.net/iconv\">iconv extension</a>";
}
+ if (!(extension_loaded("xml"))) {
+ $errors[] = "PHP is missing the <a href=\"http://php.net/xml\">XML Parser extension</a>";
+ }
+
if (!(extension_loaded("simplexml"))) {
$errors[] = "PHP is missing the <a href=\"http://php.net/simplexml\">SimpleXML extension</a>";
}
diff --git a/lib/flowplayer.controls.swf b/lib/flowplayer.controls.swf
index aacdcd30..5507a531 100644
--- a/lib/flowplayer.controls.swf
+++ b/lib/flowplayer.controls.swf
Binary files differ
diff --git a/lib/flowplayer.js b/lib/flowplayer.js
index 57f00e65..3bbdc1d3 100644
--- a/lib/flowplayer.js
+++ b/lib/flowplayer.js
@@ -10,13 +10,13 @@ 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 select(query){var index=query.indexOf(".");if(index!=-1){var tag=query.slice(0,index)||"*";var klass=query.slice(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;};}
+function makeId(){return"_"+(""+Math.random()).slice(2,10);}
+var Clip=function(json,index,player){var self=this,cuepoints={},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.slice(0,evt.length-1);var before="onBefore"+evt.slice(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];}
@@ -26,7 +26,7 @@ var conf=player.getConfig();var clip=(index==-1)?conf.clip:conf.playlist[index];
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'){bind(listeners,key,val);delete json[key];}});if(index==-1){player.onCuepoint=this.onCuepoint;}};var Plugin=function(name,json,player,fn){var self=this,listeners={},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;}
@@ -37,32 +37,37 @@ json=player._api().fp_css(name,props);extend(self,json);return self;},show:funct
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;}
+var fn=listeners[evt];if(fn){var ret=fn.apply(self,arg);if(evt.slice(0,1)=="_"){delete listeners[evt];}
+return ret;}
+return self;}});};function Player(wrapper,params,conf){var self=this,api=null,isUnloading=false,html,commonClip,playlist=[],plugins={},listeners={},playerId,apiId,playerIndex,activeIndex,swfHeight,wrapperHeight;extend(self,{id:function(){return playerId;},isLoaded:function(){return(api!==null&&api.fp_play!==undefined&&!isUnloading);},getParent:function(){return wrapper;},hide:function(all){if(all){wrapper.style.height="0px";}
+if(self.isLoaded()){api.style.height="0px";}
+return self;},show:function(){wrapper.style.height=wrapperHeight+"px";if(self.isLoaded()){api.style.height=swfHeight+"px";}
+return self;},isHidden:function(){return self.isLoaded()&&parseInt(api.style.height,10)===0;},load:function(fn){if(!self.isLoaded()&&self._fireEvent("onBeforeLoad")!==false){var onPlayersUnloaded=function(){html=wrapper.innerHTML;if(html&&!flashembed.isSupported(params.version)){wrapper.innerHTML="";}
+if(fn){fn.cached=true;bind(listeners,"onLoad",fn);}
+flashembed(wrapper,params,{config:conf});};var unloadedPlayersNb=0;each(players,function(){this.unload(function(wasUnloaded){if(++unloadedPlayersNb==players.length){onPlayersUnloaded();}});});}
+return self;},unload:function(fn){if(this.isFullscreen()&&/WebKit/i.test(navigator.userAgent)){if(fn){fn(false);}
+return self;}
+if(html.replace(/\s/g,'')!==''){if(self._fireEvent("onBeforeUnload")===false){if(fn){fn(false);}
+return self;}
+isUnloading=true;try{if(api){api.fp_close();self._fireEvent("onUnload");}}catch(error){}
+var clean=function(){api=null;wrapper.innerHTML=html;isUnloading=false;if(fn){fn(true);}};setTimeout(clean,50);}
+else if(fn){fn(false);}
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 3.1.4";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;}
+return plugin;},getScreen:function(){return self.getPlugin("screen");},getControls:function(){return self.getPlugin("controls")._fireEvent("onUpdate");},getLogo:function(){try{return self.getPlugin("logo")._fireEvent("onUpdate");}catch(ignored){}},getPlay:function(){return self.getPlugin("play")._fireEvent("onUpdate");},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 self.isLoaded()?api.fp_getState():-1;},play:function(clip,instream){var p=function(){if(clip!==undefined){self._api().fp_play(clip,instream);}else{self._api().fp_play();}};if(self.isLoaded()){p();}else if(isUnloading){setTimeout(function(){self.play(clip,instream);},50);}else{self.load(function(){p();});}
+return self;},getVersion:function(){var js="flowplayer.js 3.2.6";if(self.isLoaded()){var ver=api.fp_getVersion();ver.push(js);return ver;}
+return js;},_api:function(){if(!self.isLoaded()){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;},_swfHeight:function(){return api.clientHeight;}});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.slice(0,name.length-1);var name2="onBefore"+name.slice(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,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled").split(","),function(){var name=this;self[name]=function(a1,a2){if(!self.isLoaded()){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(!self.isLoaded()&&evt=='onLoad'&&arg0=='player'){api=api||el(apiId);swfHeight=self._swfHeight();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));}
+if(evt=='onPluginEvent'||evt=='onBeforePluginEvent'){var name=arg0.name||arg0;var p=plugins[name];if(p){p._fireEvent("onUpdate",arg0);return 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;}
@@ -72,60 +77,143 @@ 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}};}
+wrapperHeight=parseInt(wrapper.style.height,10)||wrapper.clientHeight;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();}
+plugins.canvas=new Plugin("canvas",null,self);html=wrapper.innerHTML;function doClick(e){var hasiPadSupport=self.hasiPadSupport&&self.hasiPadSupport();if(/iPad|iPhone|iPod/i.test(navigator.userAgent)&&!/.flv$/i.test(playlist[0].url)&&!hasiPadSupport){return true;}
+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);}
+function installPlayer(){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();}}
+setTimeout(installPlayer,0);}
+if(typeof wrapper=='string'){var node=el(wrapper);if(!node){throw"Flowplayer cannot access element: "+wrapper;}
+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];}}}
+if(arguments.length>1){var params=arguments[1],conf=(arguments.length==3)?arguments[2]:{};if(typeof params=='string'){params={src:params};}
+params=extend({bgcolor:"#000000",version:[9,0],expressInstall:"http://static.flowplayer.org/swf/expressinstall.swf",cachebusting:false},params);if(typeof arg=='string'){if(arg.indexOf(".")!=-1){var instances=[];each(select(arg),function(){instances.push(new Player(this,clone(params),clone(conf)));});return new Iterator(instances);}else{var node=el(arg);return new Player(node!==null?node:arg,params,conf);}}else if(arg){return new Player(arg,params,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.fn.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 IE=document.all,URL='http://www.adobe.com/go/getflashplayer',JQUERY=typeof jQuery=='function',RE=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,GLOBAL_OPTS={width:'100%',height:'100%',id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:'always',quality:'high',version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});}
+function extend(to,from){if(from){for(var 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])
+window.flashembed=function(root,opts,conf){if(typeof root=='string'){root=document.getElementById(root.replace("#",""));}
+if(!root){return;}
+if(typeof opts=='string'){opts={src:opts};}
+return new Flash(root,extend(extend({},GLOBAL_OPTS),opts),conf);};var f=extend(window.flashembed,{conf:GLOBAL_OPTS,getVersion:function(){var fo,ver;try{ver=navigator.plugins["Shockwave Flash"].description.slice(16);}catch(e){try{fo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");ver=fo&&fo.GetVariable("$version");}catch(err){try{fo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");ver=fo&&fo.GetVariable("$version");}catch(err2){}}}
+ver=RE.exec(ver);return ver?[ver[1],ver[3]]:[0,0];},asString:function(obj){if(obj===null||obj===undefined){return null;}
+var type=typeof obj;if(type=='object'&&obj.push){type='array';}
+switch(type){case'string':return string2JsonString(obj);case'array':return'['+map(obj,function(el){return f.asString(el);}).join(',')+']';case'function':return'"function()"';case'object':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push('"'+prop+'":'+f.asString(obj[prop]));}}
+return'{'+str.join(',')+'}';}
+return String(obj).replace(/\s/g," ").replace(/\'/g,"\"");},getHTML:function(opts,conf){opts=extend({},opts);var html='<object width="'+opts.width+'" height="'+opts.height+'" id="'+opts.id+'" name="'+opts.id+'"';if(opts.cachebusting){opts.src+=((opts.src.indexOf("?")!=-1?"&":"?")+Math.random());}
+if(opts.w3c||!IE){html+=' data="'+opts.src+'" type="application/x-shockwave-flash"';}else{html+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';}
+html+='>';if(opts.w3c||IE){html+='<param name="movie" value="'+opts.src+'" />';}
+opts.width=opts.height=opts.id=opts.w3c=opts.src=null;opts.onFail=opts.version=opts.expressInstall=null;for(var key in opts){if(opts[key]){html+='<param name="'+key+'" value="'+opts[key]+'" />';}}
+var vars="";if(conf){for(var k in conf){if(conf[k]){var val=conf[k];vars+=encodeURIComponent(k)+'='
++encodeURIComponent(/function|object/.test(typeof val)?f.asString(val):val)
+'&';}}
-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;}}
+vars=vars.slice(0,-1);html+='<param name="flashvars" value="'+vars+'" />';}
+html+="</object>";return html;},isSupported:function(ver){return VERSION[0]>ver[0]||VERSION[0]==ver[0]&&VERSION[1]>=ver[1];}});var VERSION=f.getVersion();function Flash(root,opts,conf){if(f.isSupported(opts.version)){root.innerHTML=f.getHTML(opts,conf);}else if(opts.expressInstall&&f.isSupported([6,65])){root.innerHTML=f.getHTML(extend(opts,{src:opts.expressInstall}),{MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title});}else{if(!root.innerHTML.replace(/\s/g,'')){root.innerHTML="<h2>Flash version "+opts.version+" 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='"+URL+"'>here</a></p>");if(root.tagName=='A'){root.onclick=function(){location.href=URL;};}}
+if(opts.onFail){var ret=opts.onFail.call(this);if(typeof ret=='string'){root.innerHTML=ret;}}}
+if(IE){window[opts.id]=document.getElementById(opts.id);}
+extend(this,{getRoot:function(){return root;},getOptions:function(){return opts;},getConf:function(){return conf;},getApi:function(){return root.firstChild;}});}
+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+'"';}
+if(JQUERY){jQuery.tools=jQuery.tools||{version:'3.2.6'};jQuery.tools.flashembed={conf:GLOBAL_OPTS};jQuery.fn.flashembed=function(opts,conf){return this.each(function(){jQuery(this).data("flashembed",flashembed(this,opts,conf));});};}})();$f.addPlugin("ipad",function(options){var STATE_UNLOADED=-1;var STATE_LOADED=0;var STATE_UNSTARTED=1;var STATE_BUFFERING=2;var STATE_PLAYING=3;var STATE_PAUSED=4;var STATE_ENDED=5;var self=this;var currentVolume=1;var onStartFired=false;var stopping=false;var playAfterSeek=false;var activeIndex=0;var activePlaylist=[];var clipDefaults={accelerated:false,autoBuffering:false,autoPlay:true,baseUrl:null,bufferLength:3,connectionProvider:null,cuepointMultiplier:1000,cuepoints:[],controls:{},duration:0,extension:'',fadeInSpeed:1000,fadeOutSpeed:1000,image:false,linkUrl:null,linkWindow:'_self',live:false,metaData:{},originalUrl:null,position:0,playlist:[],provider:'http',scaling:'scale',seekableOnBegin:false,start:0,url:null,urlResolvers:[]};var currentState=STATE_UNLOADED;var previousState=STATE_UNLOADED;var isiDevice=/iPad|iPhone|iPod/i.test(navigator.userAgent);var video=null;function extend(to,from,includeFuncs){if(from){for(key in from){if(key){if(from[key]&&typeof from[key]=="function"&&!includeFuncs)
+continue;if(from[key]&&typeof from[key]=="object"&&from[key].length==undefined){var cp={};extend(cp,from[key]);to[key]=cp;}else{to[key]=from[key];}}}}
+return to;}
+var opts={simulateiDevice:false,controlsSizeRatio:1.5,controls:true,debug:false,validExtensions:/mov|m4v|mp4|avi/gi};extend(opts,options);function log(){if(opts.debug){if(isiDevice){var str=[].splice.call(arguments,0).join(', ');console.log.apply(console,[str]);}else{console.log.apply(console,arguments);}}}
+function stateDescription(state){switch(state){case-1:return"UNLOADED";case 0:return"LOADED";case 1:return"UNSTARTED";case 2:return"BUFFERING";case 3:return"PLAYING";case 4:return"PAUSED";case 5:return"ENDED";}
+return"UNKOWN";}
+function actionAllowed(eventName){var ret=$f.fireEvent(self.id(),"onBefore"+eventName,activeIndex);return ret!==false;}
+function stopEvent(e){e.stopPropagation();e.preventDefault();return false;}
+function setState(state,force){if(currentState==STATE_UNLOADED&&!force)
+return;previousState=currentState;currentState=state;stopPlayTimeTracker();if(state==STATE_PLAYING)
+startPlayTimeTracker();log(stateDescription(state));}
+function resetState(){video.fp_stop();onStartFired=false;stopping=false;playAfterSeek=false;setState(STATE_UNSTARTED);setState(STATE_UNSTARTED);}
+var _playTimeTracker=null;function startPlayTimeTracker(){if(_playTimeTracker)
+return;console.log("starting tracker");_playTimeTracker=setInterval(onTimeTracked,100);onTimeTracked();}
+function stopPlayTimeTracker(){clearInterval(_playTimeTracker);_playTimeTracker=null;}
+function onTimeTracked(){var currentTime=Math.floor(video.fp_getTime()*10)*100;var duration=Math.floor(video.duration*10)*100;var fireTime=(new Date()).time;function fireCuePointsIfNeeded(time,cues){time=time>=0?time:duration-Math.abs(time);for(var i=0;i<cues.length;i++){if(cues[i].lastTimeFired>fireTime){cues[i].lastTimeFired=-1;}else if(cues[i].lastTimeFired+500>fireTime){continue;}else{if(time==currentTime||(currentTime-500<time&&currentTime>time)){cues[i].lastTimeFired=fireTime;$f.fireEvent(self.id(),'onCuepoint',activeIndex,cues[i].fnId,cues[i].parameters);}}}}
+$f.each(self.getCommonClip()._cuepoints,fireCuePointsIfNeeded);$f.each(activePlaylist[activeIndex]._cuepoints,fireCuePointsIfNeeded);}
+function replay(){resetState();playAfterSeek=true;video.fp_seek(0);}
+function scaleVideo(clip){}
+function addAPI(){function fixClip(clip){var extendedClip={};extend(extendedClip,clipDefaults);extend(extendedClip,self.getCommonClip());extend(extendedClip,clip);if(extendedClip.ipadUrl)
+url=decodeURIComponent(extendedClip.ipadUrl);else if(extendedClip.url)
+url=extendedClip.url;if(url&&url.indexOf('://')==-1&&extendedClip.baseUrl)
+url=extendedClip.baseUrl+'/'+url;extendedClip.originalUrl=extendedClip.url;extendedClip.completeUrl=url;extendedClip.extension=extendedClip.completeUrl.substr(extendedClip.completeUrl.lastIndexOf('.'));extendedClip.type='video';delete extendedClip.index;log("fixed clip",extendedClip);return extendedClip;}
+video.fp_play=function(clip,inStream,forcePlay){var url=null;var autoBuffering=true;var autoPlay=true;log("Calling play() "+clip,clip);if(inStream){log("ERROR: inStream clips not yet supported");return;}
+if(clip!==undefined){if(typeof clip=="number"){if(activeIndex>=activePlaylist.length)
+return;activeIndex=clip;clip=activePlaylist[activeIndex];}else{if(typeof clip=="string"){clip={url:clip};}
+video.fp_setPlaylist(clip.length!==undefined?clip:[clip]);}
+if(!opts.validExtensions.test(activePlaylist[activeIndex].extension)){if(activePlaylist.length>1&&activeIndex<(activePlaylist.length-1)){log("Not last clip in the playlist, moving to next one");video.fp_play(++activeIndex,false,true);}
+return;}
+clip=activePlaylist[activeIndex];url=clip.completeUrl;if(clip.autoBuffering!==undefined&&clip.autoBuffering===false)
+autoBuffering=false;if(clip.autoPlay===undefined||clip.autoPlay===true||forcePlay===true){autoBuffering=true;autoPlay=true;}else{autoPlay=false;}}else{log("clip was not given, simply calling video.play, if not already buffering");if(currentState!=STATE_BUFFERING)
+video.play();return;}
+log("about to play "+url,autoBuffering,autoPlay);resetState();if(url){log("Changing SRC attribute"+url);video.setAttribute('src',url);}
+if(autoBuffering){if(!actionAllowed('Begin'))
+return false;$f.fireEvent(self.id(),'onBegin',activeIndex);log("calling video.load()");video.load();}
+if(autoPlay){log("calling video.play()");video.play();}}
+video.fp_pause=function(){log("pause called");if(!actionAllowed('Pause'))
+return false;video.pause();};video.fp_resume=function(){log("resume called");if(!actionAllowed('Resume'))
+return false;video.play();};video.fp_stop=function(){log("stop called");if(!actionAllowed('Stop'))
+return false;stopping=true;video.pause();try{video.currentTime=0;}catch(ignored){}};video.fp_seek=function(position){log("seek called "+position);if(!actionAllowed('Seek'))
+return false;var seconds=0;var position=position+"";if(position.charAt(position.length-1)=='%'){var percentage=parseInt(position.substr(0,position.length-1))/100;var duration=video.duration;seconds=duration*percentage;}else{seconds=position;}
+try{video.currentTime=seconds;}catch(e){log("Wrong seek time");}};video.fp_getTime=function(){return video.currentTime;};video.fp_mute=function(){log("mute called");if(!actionAllowed('Mute'))
+return false;currentVolume=video.volume;video.volume=0;};video.fp_unmute=function(){if(!actionAllowed('Unmute'))
+return false;video.volume=currentVolume;};video.fp_getVolume=function(){return video.volume*100;};video.fp_setVolume=function(volume){if(!actionAllowed('Volume'))
+return false;video.volume=volume/100;};video.fp_toggle=function(){log('toggle called');if(self.getState()==STATE_ENDED){replay();return;}
+if(video.paused)
+video.fp_play();else
+video.fp_pause();};video.fp_isPaused=function(){return video.paused;};video.fp_isPlaying=function(){return!video.paused;};video.fp_getPlugin=function(name){if(name=='canvas'||name=='controls'){var config=self.getConfig();return config['plugins']&&config['plugins'][name]?config['plugins'][name]:null;}
+log("ERROR: no support for "+name+" plugin on iDevices");return null;};video.fp_close=function(){setState(STATE_UNLOADED);video.parentNode.removeChild(video);video=null;};video.fp_getStatus=function(){var bufferStart=0;var bufferEnd=0;try{bufferStart=video.buffered.start();bufferEnd=video.buffered.end();}catch(ignored){}
+return{bufferStart:bufferStart,bufferEnd:bufferEnd,state:currentState,time:video.fp_getTime(),muted:video.muted,volume:video.fp_getVolume()};};video.fp_getState=function(){return currentState;};video.fp_startBuffering=function(){if(currentState==STATE_UNSTARTED)
+video.load();};video.fp_setPlaylist=function(playlist){log("Setting playlist");activeIndex=0;for(var i=0;i<playlist.length;i++)
+playlist[i]=fixClip(playlist[i]);activePlaylist=playlist;$f.fireEvent(self.id(),'onPlaylistReplace',playlist);};video.fp_addClip=function(clip,index){clip=fixClip(clip);activePlaylist.splice(index,0,clip);$f.fireEvent(self.id(),'onClipAdd',clip,index);};video.fp_updateClip=function(clip,index){extend(activePlaylist[index],clip);return activePlaylist[index];};video.fp_getVersion=function(){return'3.2.3';}
+video.fp_isFullscreen=function(){return false;}
+video.fp_toggleFullscreen=function(){if(video.fp_isFullscreen())
+video.webkitExitFullscreen();else
+video.webkitEnterFullscreen();}
+video.fp_addCuepoints=function(points,index,fnId){var clip=index==-1?self.getCommonClip():activePlaylist[index];clip._cuepoints=clip._cuepoints||{};points=points instanceof Array?points:[points];for(var i=0;i<points.length;i++){var time=typeof points[i]=="object"?(points[i]['time']||null):points[i];if(time==null)continue;time=Math.floor(time/100)*100;var parameters=time;if(typeof points[i]=="object"){parameters=extend({},points[i],false);if(parameters['time']!=undefined)delete parameters['time'];if(parameters['parameters']!=undefined){extend(parameters,parameters['parameters'],false);delete parameters['parameters'];}}
+clip._cuepoints[time]=clip._cuepoints[time]||[];clip._cuepoints[time].push({fnId:fnId,lastTimeFired:-1,parameters:parameters});}}
+$f.each(("toggleFullscreen,stopBuffering,reset,playFeed,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled,css,animate,showPlugin,hidePlugin,togglePlugin,fadeTo,invoke,loadPlugin").split(","),function(){var name=this;video["fp_"+name]=function(){log("ERROR: unsupported API on iDevices "+name);return false;};});}
+function addListeners(){var events=['abort','canplay','canplaythrough','durationchange','emptied','ended','error','loadeddata','loadedmetadata','loadstart','pause','play','playing','progress','ratechange','seeked','seeking','stalled','suspend','volumechange','waiting'];var eventsLogger=function(e){log("Got event "+e.type,e);}
+for(var i=0;i<events.length;i++)
+video.addEventListener(events[i],eventsLogger,false);var onBufferEmpty=function(e){log("got onBufferEmpty event "+e.type)
+setState(STATE_BUFFERING);$f.fireEvent(self.id(),'onBufferEmpty',activeIndex);};video.addEventListener('emptied',onBufferEmpty,false);video.addEventListener('waiting',onBufferEmpty,false);var onBufferFull=function(e){if(previousState==STATE_UNSTARTED||previousState==STATE_BUFFERING){}else{log("Restoring old state "+stateDescription(previousState));setState(previousState);}
+$f.fireEvent(self.id(),'onBufferFull',activeIndex);};video.addEventListener('canplay',onBufferFull,false);video.addEventListener('canplaythrough',onBufferFull,false);var onMetaData=function(e){video.fp_updateClip({duration:video.duration,metaData:{duration:video.duration}},activeIndex);activePlaylist[activeIndex].duration=video.duration;$f.fireEvent(self.id(),'onMetaData',activeIndex,activePlaylist[activeIndex]);};video.addEventListener('loadedmetadata',onMetaData,false);video.addEventListener('durationchange',onMetaData,false);var onStart=function(e){if(currentState==STATE_PAUSED){if(!actionAllowed('Resume')){log("Resume disallowed, pausing");video.fp_pause();return stopEvent(e);}
+$f.fireEvent(self.id(),'onResume',activeIndex);}
+setState(STATE_PLAYING);if(!onStartFired){onStartFired=true;$f.fireEvent(self.id(),'onStart',activeIndex);}};video.addEventListener('playing',onStart,false);var onFinish=function(e){if(!actionAllowed('Finish')){if(activePlaylist.length==1){log("Active playlist only has one clip, onBeforeFinish returned false. Replaying");replay();}else if(activeIndex!=(activePlaylist.length-1)){log("Not the last clip in the playlist, but onBeforeFinish returned false. Returning to the beginning of current clip");video.fp_seek(0);}else{log("Last clip in playlist, but onBeforeFinish returned false, start again from the beginning");video.fp_play(0);}
+return stopEvent(e);}
+setState(STATE_ENDED);$f.fireEvent(self.id(),'onFinish',activeIndex);if(activePlaylist.length>1&&activeIndex<(activePlaylist.length-1)){log("Not last clip in the playlist, moving to next one");video.fp_play(++activeIndex,false,true);}};video.addEventListener('ended',onFinish,false);var onError=function(e){setState(STATE_LOADED,true);$f.fireEvent(self.id(),'onError',activeIndex,201);if(opts.onFail&&opts.onFail instanceof Function)
+opts.onFail.apply(self,[]);};video.addEventListener('error',onError,false);var onPause=function(e){log("got pause event from player"+self.id());if(stopping)
+return;if(currentState==STATE_BUFFERING&&previousState==STATE_UNSTARTED){log("forcing play");setTimeout(function(){video.play();},0);return;}
+if(!actionAllowed('Pause')){video.fp_resume();return stopEvent(e);}
+setState(STATE_PAUSED);$f.fireEvent(self.id(),'onPause',activeIndex);}
+video.addEventListener('pause',onPause,false);var onSeek=function(e){$f.fireEvent(self.id(),'onBeforeSeek',activeIndex);};video.addEventListener('seeking',onSeek,false);var onSeekDone=function(e){if(stopping){stopping=false;$f.fireEvent(self.id(),'onStop',activeIndex);}
+else
+$f.fireEvent(self.id(),'onSeek',activeIndex);log("seek done, currentState",stateDescription(currentState));if(playAfterSeek){playAfterSeek=false;video.fp_play();}else if(currentState!=STATE_PLAYING)
+video.fp_pause();};video.addEventListener('seeked',onSeekDone,false);var onVolumeChange=function(e){$f.fireEvent(self.id(),'onVolume',video.fp_getVolume());};video.addEventListener('volumechange',onVolumeChange,false);}
+function onPlayerLoaded(){video.fp_play(0);}
+function installControlbar(){}
+if(isiDevice||opts.simulateiDevice){if(!window.flashembed.__replaced){var realFlashembed=window.flashembed;window.flashembed=function(root,opts,conf){if(typeof root=='string'){root=document.getElementById(root.replace("#",""));}
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
+var style=window.getComputedStyle(root,null);var width=parseInt(style.width);var height=parseInt(style.height);while(root.firstChild)
+root.removeChild(root.firstChild);var container=document.createElement('div');var api=document.createElement('video');container.appendChild(api);root.appendChild(container);container.style.height=height+'px';container.style.width=width+'px';container.style.display='block';container.style.position='relative';container.style.background='-webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.7)))';container.style.cursor='default';container.style.webkitUserDrag='none';api.style.height='100%';api.style.width='100%';api.style.display='block';api.id=opts.id;api.name=opts.id;api.style.cursor='pointer';api.style.webkitUserDrag='none';api.type="video/mp4";api.playerConfig=conf.config;$f.fireEvent(conf.config.playerId,'onLoad','player');};flashembed.getVersion=realFlashembed.getVersion;flashembed.asString=realFlashembed.asString;flashembed.isSupported=function(){return true;}
+flashembed.__replaced=true;}
+var __fireEvent=self._fireEvent;self._fireEvent=function(a){if(a[0]=='onLoad'&&a[1]=='player'){video=self.getParent().querySelector('video');if(opts.controls)
+video.controls="controls";addAPI();addListeners();setState(STATE_LOADED,true);video.fp_setPlaylist(video.playerConfig.playlist);onPlayerLoaded();__fireEvent.apply(self,[a]);}
+var shouldFireEvent=currentState!=STATE_UNLOADED;if(currentState==STATE_UNLOADED&&typeof a=='string')
+shouldFireEvent=true;if(shouldFireEvent)
+return __fireEvent.apply(self,[a]);}
+self._swfHeight=function(){return parseInt(video.style.height);}
+self.hasiPadSupport=function(){return true;}}
+return self;}); \ No newline at end of file
diff --git a/lib/flowplayer.pseudostreaming.swf b/lib/flowplayer.pseudostreaming.swf
index 91b3fdd6..28f5d72a 100644
--- a/lib/flowplayer.pseudostreaming.swf
+++ b/lib/flowplayer.pseudostreaming.swf
Binary files differ
diff --git a/lib/flowplayer.swf b/lib/flowplayer.swf
index 63f3934a..20a41193 100644
--- a/lib/flowplayer.swf
+++ b/lib/flowplayer.swf
Binary files differ
diff --git a/lib/images/apple-touch-icon.png b/lib/images/apple-touch-icon.png
new file mode 100644
index 00000000..d15ce1a6
--- /dev/null
+++ b/lib/images/apple-touch-icon.png
Binary files differ
diff --git a/lib/jquery-ui.js b/lib/jquery-ui.js
index 3398b9e4..74515a5b 100644
--- a/lib/jquery-ui.js
+++ b/lib/jquery-ui.js
@@ -18,7 +18,7 @@ this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widge
(this._mouseStarted&&this._mouseUp(event));this._mouseDownEvent=event;var self=this,btnIsLeft=(event.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(event.target).parents().add(event.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(event)){return true;}
this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
if(this._mouseDistanceMet(event)&&this._mouseDelayMet(event)){this._mouseStarted=(this._mouseStart(event)!==false);if(!this._mouseStarted){event.preventDefault();return true;}}
-this._mouseMoveDelegate=function(event){return self._mouseMove(event);};this._mouseUpDelegate=function(event){return self._mouseUp(event);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);($.browser.safari||event.preventDefault());event.originalEvent.mouseHandled=true;return true;},_mouseMove:function(event){if($.browser.msie&&!event.button){return this._mouseUp(event);}
+this._mouseMoveDelegate=function(event){return self._mouseMove(event);};this._mouseUpDelegate=function(event){return self._mouseUp(event);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);($.browser.safari||event.preventDefault());event.originalEvent.mouseHandled=true;return true;},_mouseMove:function(event){if($.browser.msie&&!(document.documentMode>=9)&&!event.button){return this._mouseUp(event);}
if(this._mouseStarted){this._mouseDrag(event);return event.preventDefault();}
if(this._mouseDistanceMet(event)&&this._mouseDelayMet(event)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,event)!==false);(this._mouseStarted?this._mouseDrag(event):this._mouseUp(event));}
return!this._mouseStarted;},_mouseUp:function(event){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(event.target==this._mouseDownEvent.target);this._mouseStop(event);}
diff --git a/modules/akismet/module.info b/modules/akismet/module.info
index b61ed107..63473468 100644
--- a/modules/akismet/module.info
+++ b/modules/akismet/module.info
@@ -1,3 +1,7 @@
name = "Akismet"
description = "Filter comments through the Akismet web service to detect and eliminate spam (http://akismet.com). You'll need a WordPress.com API key to use it."
version = 1
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:akismet"
+discuss_url = "http://gallery.menalto.com/forum_module_akismet"
diff --git a/modules/comment/controllers/admin_manage_comments.php b/modules/comment/controllers/admin_manage_comments.php
index 9bd1d7f6..72684e70 100644
--- a/modules/comment/controllers/admin_manage_comments.php
+++ b/modules/comment/controllers/admin_manage_comments.php
@@ -28,8 +28,10 @@ class Admin_Manage_Comments_Controller extends Admin_Controller {
->where("updated", "<", db::expr("UNIX_TIMESTAMP() - 86400 * 7"))
->execute();
- // Redirect to the appropriate queue
- url::redirect("admin/manage_comments/queue/unpublished");
+ $view = new Admin_View("admin.html");
+ $view->content = new View("admin_manage_comments.html");
+ $view->content->menu = $this->_menu($this->_counts());
+ print $view;
}
public function menu_labels() {
@@ -43,25 +45,32 @@ class Admin_Manage_Comments_Controller extends Admin_Controller {
public function queue($state) {
$page = max(Input::instance()->get("page"), 1);
- $view = new Admin_View("admin.html");
- $view->page_title = t("Manage comments");
- $view->content = new View("admin_manage_comments.html");
- $view->content->counts = $this->_counts();
- $view->content->menu = $this->_menu($view->content->counts);
- $view->content->state = $state;
- $view->content->comments = ORM::factory("comment")
+ $view = new Gallery_View("admin_manage_comments_queue.html");
+ $view->counts = $this->_counts();
+ $view->menu = $this->_menu($view->counts);
+ $view->state = $state;
+ $view->comments = ORM::factory("comment")
->order_by("created", "DESC")
->order_by("id", "DESC")
->where("state", "=", $state)
->limit(self::$items_per_page)
->offset(($page - 1) * self::$items_per_page)
->find_all();
- $view->content->pager = new Pagination();
- $view->content->pager->initialize(
- array("query_string" => "page",
- "total_items" => $view->content->counts->$state,
- "items_per_page" => self::$items_per_page,
- "style" => "classic"));
+
+ // This view is not themed so we can't use $theme->url() in the view and have to
+ // reproduce Gallery_View::url() logic here.
+ $atn = theme::$admin_theme_name;
+ $view->fallback_avatar_url = url::abs_file("themes/$atn/images/avatar.jpg");
+
+ $view->page = $page;
+ $view->page_type = "collection";
+ $view->page_subtype = "admin_comments";
+ $view->page_size = self::$items_per_page;
+ $view->children_count = $this->_counts()->$state;
+ $view->max_pages = ceil($view->children_count / $view->page_size);
+
+ // Also we want to use $theme->paginator() so we need a dummy theme
+ $view->theme = $view;
print $view;
}
diff --git a/modules/comment/helpers/comment_installer.php b/modules/comment/helpers/comment_installer.php
index 48b6ee21..5c6bd586 100644
--- a/modules/comment/helpers/comment_installer.php
+++ b/modules/comment/helpers/comment_installer.php
@@ -37,8 +37,8 @@ class comment_installer {
`server_http_referer` varchar(255) default NULL,
`server_http_user_agent` varchar(128) default NULL,
`server_query_string` varchar(64) default NULL,
- `server_remote_addr` varchar(32) default NULL,
- `server_remote_host` varchar(64) default NULL,
+ `server_remote_addr` varchar(40) default NULL,
+ `server_remote_host` varchar(255) default NULL,
`server_remote_port` varchar(16) default NULL,
`state` varchar(15) default 'unpublished',
`text` text,
@@ -48,7 +48,7 @@ class comment_installer {
module::set_var("comment", "spam_caught", 0);
module::set_var("comment", "access_permissions", "everybody");
- module::set_version("comment", 3);
+ module::set_version("comment", 4);
}
static function upgrade($version) {
@@ -62,6 +62,19 @@ class comment_installer {
module::set_var("comment", "access_permissions", "everybody");
module::set_version("comment", $version = 3);
}
+
+ if ($version == 3) {
+ // 40 bytes for server_remote_addr is enough to swallow the longest
+ // representation of an IPv6 addy.
+ //
+ // 255 bytes for server_remote_host is enough to swallow the longest
+ // legit DNS entry, with a few bytes to spare.
+ $db->query(
+ "ALTER TABLE {comments} CHANGE `server_remote_addr` `server_remote_addr` varchar(40)");
+ $db->query(
+ "ALTER TABLE {comments} CHANGE `server_remote_host` `server_remote_host` varchar(255)");
+ module::set_version("comment", $version = 4);
+ }
}
static function uninstall() {
diff --git a/modules/comment/models/comment.php b/modules/comment/models/comment.php
index d5e952eb..7c189a0e 100644
--- a/modules/comment/models/comment.php
+++ b/modules/comment/models/comment.php
@@ -98,8 +98,8 @@ class Comment_Model_Core extends ORM {
$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_addr = substr($input->server("REMOTE_ADDR"), 0, 40);
+ $this->server_remote_host = substr($input->server("REMOTE_HOST"), 0, 255);
$this->server_remote_port = substr($input->server("REMOTE_PORT"), 0, 16);
}
diff --git a/modules/comment/module.info b/modules/comment/module.info
index cd34f140..4e7df6f1 100644
--- a/modules/comment/module.info
+++ b/modules/comment/module.info
@@ -1,3 +1,7 @@
name = "Comments"
description = "Allows users and guests to leave comments on photos and albums."
-version = 3
+version = 4
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:comment"
+discuss_url = "http://gallery.menalto.com/forum_module_comment"
diff --git a/modules/comment/views/admin_manage_comments.html.php b/modules/comment/views/admin_manage_comments.html.php
index 34a28986..e3c8546c 100644
--- a/modules/comment/views/admin_manage_comments.html.php
+++ b/modules/comment/views/admin_manage_comments.html.php
@@ -2,200 +2,45 @@
<script type="text/javascript">
var set_state_url =
<?= html::js_string(url::site("admin/manage_comments/set_state/__ID__/__STATE__?csrf=$csrf")) ?>;
- function set_state(state, id) {
- $.get(set_state_url.replace("__STATE__", state).replace("__ID__", id),
+ var set_state = function(state, id) {
+ $("#g-comment-" + id).fadeOut("fast", function() {
+ $.get(set_state_url.replace("__STATE__", state).replace("__ID__", id),
{},
- function() {
- $("#g-comment-" + id).slideUp();
- update_menu();
- });
+ update_menu);
+ });
}
- var delete_url =
- <?= html::js_string(url::site("admin/manage_comments/delete/__ID__?csrf=$csrf")) ?>;
-
- function del(id) {
- $.get(delete_url.replace("__ID__", id),
- {},
- function() {
- $("#g-comment-" + id).slideUp();
- update_menu();
- });
- }
-
- function update_menu() {
+ var update_menu = function() {
$.get(<?= html::js_string(url::site("admin/manage_comments/menu_labels")) ?>, {},
function(data) {
for (var i = 0; i < data.length; i++) {
- $("#g-admin-comments-menu li:eq(" + i + ") a").html(data[i]);
+ $("#g-admin-comments ul li:eq(" + i + ") a").html(data[i]);
}
},
"json");
}
-</script>
-
-<div id="g-admin-comments" class="g-block">
- <h1> <?= t("Manage comments") ?> </h1>
- <div class="g-block-content">
- <!-- @todo: Highlight active menu option -->
- <div id="g-admin-comments-menu" class="ui-helper-clearfix">
- <?= $menu->render() ?>
- </div>
-
- <!-- @todo: Remove after setting active option? -->
- <h2>
- <? if ($state == "published"): ?>
- <?= t("Approved comments") ?>
- <? elseif ($state == "unpublished"): ?>
- <?= t("Comments awaiting moderation") ?>
- <? elseif ($state == "spam"): ?>
- <?= t("Spam comments") ?>
- <? elseif ($state == "deleted"): ?>
- <?= t("Recently deleted comments") ?>
- <? endif ?>
- </h2>
-
- <? if ($state == "spam"): ?>
- <div>
- <? $spam_caught = module::get_var("comment", "spam_caught") ?>
- <? if ($spam_caught > 0): ?>
- <p>
- <?= t2("Gallery has caught %count spam for you since you installed spam filtering.",
- "Gallery has caught %count spam for you since you installed spam filtering.",
- $spam_caught) ?>
- </p>
- <? endif ?>
- <p>
- <? if ($counts->spam): ?>
- <?= t2("There is currently one comment in your spam queue. You can delete it with a single click, but there is no undo operation so you may want to check the message first to make sure that it really is spam.",
- "There are currently %count comments in your spam queue. You can delete them all with a single click, but there is no undo operation so you may want to check the messages first to make sure that they really are spam. All spam messages will be deleted after 7 days automatically.",
- $counts->spam) ?>
- </p>
- <p>
- <a href="<?= url::site("admin/manage_comments/delete_all_spam?csrf=$csrf") ?>">
- <?= t("Delete all spam") ?>
- </a>
- <? else: ?>
- <?= t("Your spam queue is empty!") ?>
- <? endif ?>
- </p>
- </div>
- <? endif ?>
-
- <? if ($state == "deleted"): ?>
- <div>
- <p>
- <?= t("These are messages that have been recently deleted. They will be permanently erased automatically after 7 days.") ?>
- </p>
- </div>
- <? endif ?>
-
- <table id="g-admin-comments-list">
- <tr>
- <th>
- <?= t("Author") ?>
- </th>
- <th>
- <?= t("Comment") ?>
- </th>
- <th>
- <?= t("Actions") ?>
- </th>
- </tr>
- <? foreach ($comments as $comment): ?>
- <tr id="g-comment-<?= $comment->id ?>" class="<?= text::alternate("g-odd", "g-even") ?>">
- <td>
- <a href="#">
- <img src="<?= $comment->author()->avatar_url(40, $theme->url("images/avatar.jpg", true)) ?>"
- class="g-avatar"
- alt="<?= html::clean_attribute($comment->author_name()) ?>"
- width="40"
- height="40" />
- </a>
- <p><a href="mailto:<?= html::clean_attribute($comment->author_email()) ?>"
- title="<?= html::clean_attribute($comment->author_email()) ?>"> <?= html::clean($comment->author_name()) ?> </a></p>
- </td>
- <td>
- <div class="g-right">
- <? $item = $comment->item() ?>
- <div class="g-item g-photo">
- <a href="<?= $item->url() ?>">
- <? if ($item->has_thumb()): ?>
- <img src="<?= $item->thumb_url() ?>"
- alt="<?= html::purify($item->title)->for_html_attr() ?>"
- <?= photo::img_dimensions($item->thumb_width, $item->thumb_height, 75) ?>
- />
- <? else: ?>
- <?= t("No thumbnail") ?>
- <? endif ?>
- </a>
- </div>
- </div>
- <p><?= gallery::date($comment->created) ?></p>
- <?= nl2br(html::purify($comment->text)) ?>
- </td>
- <td>
- <ul class="g-buttonset-vertical">
- <? if ($comment->state != "unpublished"): ?>
- <li>
- <a href="javascript:set_state('unpublished',<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-check"></span>
- <?= t("Unapprove") ?>
- </a>
- </li>
- <? endif ?>
- <? if ($comment->state != "published"): ?>
- <li>
- <a href="javascript:set_state('published',<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-check"></span>
- <?= t("Approve") ?>
- </a>
- </li>
- <? endif ?>
- <? if ($comment->state != "spam"): ?>
- <li>
- <a href="javascript:set_state('spam',<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-cancel"></span>
- <?= t("Spam") ?>
- </a>
- </li>
- <? endif ?>
- <!--
- <li>
- <a href="javascript:reply(<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-arrowreturnthick-1-w"></span>
- <?= t("Reply") ?>
- </a>
- </li>
- <li>
- <a href="javascript:Edit(<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-pencil"></span>
- <?= t("Edit") ?>
- </a>
- </li>
- -->
- <li>
- <a href="javascript:set_state('deleted',<?=$comment->id?>)"
- class="g-button ui-state-default ui-icon-left">
- <span class="ui-icon ui-icon-trash"></span>
- <?= t("Delete") ?>
- </a>
- </li>
- </ul>
- </td>
- </tr>
- <? endforeach ?>
- </table>
+ // Paginator clicks load their href in the active tab panel
+ var fix_links = function() {
+ $(".g-paginator a, a#g-delete-all-spam").click(function(event) {
+ event.stopPropagation();
+ $.scrollTo(0, 800, { easing: "swing" });
+ $(this).parents(".ui-tabs-panel").load(
+ $(this).attr("href"),
+ function() {
+ fix_links();
+ });
+ return false;
+ });
+ }
- <div class="g-paginator">
- <?= $pager ?>
- </div>
+ $(document).ready(function() {
+ $("#g-admin-comments").tabs({
+ show: fix_links,
+ });
+ });
+</script>
- </div>
+<div id="g-admin-comments" class="g-block">
+ <?= $menu->render() ?>
</div>
diff --git a/modules/comment/views/admin_manage_comments_queue.html.php b/modules/comment/views/admin_manage_comments_queue.html.php
new file mode 100644
index 00000000..d847d729
--- /dev/null
+++ b/modules/comment/views/admin_manage_comments_queue.html.php
@@ -0,0 +1,157 @@
+<?php defined("SYSPATH") or die("No direct script access.") ?>
+<div class="g-block-content">
+ <? if ($state == "spam"): ?>
+ <div>
+ <? $spam_caught = module::get_var("comment", "spam_caught") ?>
+ <? if ($spam_caught > 0): ?>
+ <p>
+ <?= t2("Gallery has caught %count spam for you since you installed spam filtering.",
+ "Gallery has caught %count spam for you since you installed spam filtering.",
+ $spam_caught) ?>
+ </p>
+ <? endif ?>
+ <p>
+ <? if ($counts->spam): ?>
+ <?= t2("There is currently one comment in your spam queue. You can delete it with a single click, but there is no undo operation so you may want to check the message first to make sure that it really is spam.",
+ "There are currently %count comments in your spam queue. You can delete them all with a single click, but there is no undo operation so you may want to check the messages first to make sure that they really are spam. All spam messages will be deleted after 7 days automatically.",
+ $counts->spam) ?>
+ </p>
+ <p>
+ <a id="g-delete-all-spam"
+ href="<?= url::site("admin/manage_comments/delete_all_spam?csrf=$csrf") ?>">
+ <?= t("Delete all spam") ?>
+ </a>
+ <? else: ?>
+ <?= t("Your spam queue is empty!") ?>
+ <? endif ?>
+ </p>
+ </div>
+ <? endif ?>
+
+ <? if ($state == "deleted"): ?>
+ <div>
+ <p>
+ <?= t("These are messages that have been recently deleted. They will be permanently erased automatically after 7 days.") ?>
+ </p>
+ </div>
+ <? endif ?>
+
+ <div class="g-paginator">
+ <?= $theme->paginator() ?>
+ </div>
+ <table id="g-admin-comments-list">
+ <tr>
+ <th>
+ <?= t("Author") ?>
+ </th>
+ <th>
+ <?= t("Comment") ?>
+ </th>
+ <th>
+ <?= t("Actions") ?>
+ </th>
+ </tr>
+ <? foreach ($comments as $comment): ?>
+ <tr id="g-comment-<?= $comment->id ?>" class="<?= text::alternate("g-odd", "g-even") ?>">
+ <td>
+ <a href="#">
+ <img src="<?= $comment->author()->avatar_url(40, $fallback_avatar_url) ?>"
+ class="g-avatar"
+ alt="<?= html::clean_attribute($comment->author_name()) ?>"
+ width="40"
+ height="40" />
+ </a>
+ <p>
+ <a href="mailto:<?= html::clean_attribute($comment->author_email()) ?>"
+ title="<?= html::clean_attribute($comment->author_email()) ?>">
+ <?= html::clean($comment->author_name()) ?>
+ </a>
+ </p>
+ </td>
+ <td>
+ <div class="g-right">
+ <? $item = $comment->item() ?>
+ <div class="g-item g-photo">
+ <a href="<?= $item->url() ?>">
+ <? if ($item->has_thumb()): ?>
+ <img src="<?= $item->thumb_url() ?>"
+ alt="<?= html::purify($item->title)->for_html_attr() ?>"
+ <?= photo::img_dimensions($item->thumb_width, $item->thumb_height, 75) ?>
+ />
+ <? else: ?>
+ <?= t("No thumbnail") ?>
+ <? endif ?>
+ </a>
+ </div>
+ </div>
+ <p><?= gallery::date($comment->created) ?></p>
+ <?= nl2br(html::purify($comment->text)) ?>
+ </td>
+ <td>
+ <ul class="g-buttonset-vertical">
+ <? if ($comment->state != "unpublished" && $comment->state != "deleted"): ?>
+ <li>
+ <a href="javascript:set_state('unpublished',<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-check"></span>
+ <?= t("Unapprove") ?>
+ </a>
+ </li>
+ <? endif ?>
+ <? if ($comment->state != "published"): ?>
+ <li>
+ <a href="javascript:set_state('published',<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-check"></span>
+ <? if ($state == "deleted"): ?>
+ <?= t("Undelete") ?>
+ <? else: ?>
+ <?= t("Approve") ?>
+ <? endif ?>
+ </a>
+ </li>
+ <? endif ?>
+ <? if ($comment->state != "spam"): ?>
+ <li>
+ <a href="javascript:set_state('spam',<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-cancel"></span>
+ <?= t("Spam") ?>
+ </a>
+ </li>
+ <? endif ?>
+ <!--
+ <li>
+ <a href="javascript:reply(<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-arrowreturnthick-1-w"></span>
+ <?= t("Reply") ?>
+ </a>
+ </li>
+ <li>
+ <a href="javascript:Edit(<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-pencil"></span>
+ <?= t("Edit") ?>
+ </a>
+ </li>
+ -->
+ <? if ($comment->state != "deleted"): ?>
+ <li>
+ <a href="javascript:set_state('deleted',<?=$comment->id?>)"
+ class="g-button ui-state-default ui-icon-left">
+ <span class="ui-icon ui-icon-trash"></span>
+ <?= t("Delete") ?>
+ </a>
+ </li>
+ <? endif ?>
+ </ul>
+ </td>
+ </tr>
+ <? endforeach ?>
+ </table>
+
+ <div class="g-paginator">
+ <?= $theme->paginator() ?>
+ </div>
+</div>
diff --git a/modules/digibug/controllers/digibug.php b/modules/digibug/controllers/digibug.php
index 4acb6513..672afe57 100644
--- a/modules/digibug/controllers/digibug.php
+++ b/modules/digibug/controllers/digibug.php
@@ -33,8 +33,8 @@ class Digibug_Controller extends Controller {
$proxy->uuid = random::hash();
$proxy->item_id = $item->id;
$proxy->save();
- $full_url = url::abs_site("digibug/print_proxy/full/$proxy->uuid");
- $thumb_url = url::abs_site("digibug/print_proxy/thumb/$proxy->uuid");
+ $full_url = url::abs_site("digibug/print_proxy/full/$proxy->uuid/$item->id");
+ $thumb_url = url::abs_site("digibug/print_proxy/thumb/$proxy->uuid/$item->id");
}
$v = new View("digibug_form.html");
@@ -114,7 +114,7 @@ class Digibug_Controller extends Controller {
private function _clean_expired() {
db::build()
->delete("digibug_proxies")
- ->where("request_date", "<=", db::expr("(CURDATE() - INTERVAL 10 DAY)"))
+ ->where("request_date", "<=", db::expr("(CURDATE() - INTERVAL 90 DAY)"))
->limit(20)
->execute();
}
diff --git a/modules/digibug/module.info b/modules/digibug/module.info
index be4e880a..781d5f01 100644
--- a/modules/digibug/module.info
+++ b/modules/digibug/module.info
@@ -1,3 +1,7 @@
name = "Digibug"
description = "Digibug Photo Printing Module"
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:digibug"
+discuss_url = "http://gallery.menalto.com/forum_module_digibug"
diff --git a/modules/exif/helpers/exif.php b/modules/exif/helpers/exif.php
index 1cdf7d34..a35a2141 100644
--- a/modules/exif/helpers/exif.php
+++ b/modules/exif/helpers/exif.php
@@ -36,10 +36,7 @@ class exif_Core {
foreach(self::_keys() as $field => $exifvar) {
if (isset($exif_raw[$exifvar[0]][$exifvar[1]])) {
$value = $exif_raw[$exifvar[0]][$exifvar[1]];
- if (function_exists("mb_detect_encoding") &&
- mb_detect_encoding($value, "ISO-8859-1, UTF-8") != "UTF-8") {
- $value = utf8_encode($value);
- }
+ $value = encoding::convert_to_utf8($value);
$keys[$field] = Input::clean($value);
if ($field == "DateTime") {
@@ -60,10 +57,7 @@ class exif_Core {
foreach (array("Keywords" => "2#025", "Caption" => "2#120") as $keyword => $iptc_key) {
if (!empty($iptc[$iptc_key])) {
$value = implode(" ", $iptc[$iptc_key]);
- if (function_exists("mb_detect_encoding") &&
- mb_detect_encoding($value, "ISO-8859-1, UTF-8") != "UTF-8") {
- $value = utf8_encode($value);
- }
+ $value = encoding::convert_to_utf8($value);
$keys[$keyword] = Input::clean($value);
if ($keyword == "Caption" && !$item->description) {
diff --git a/modules/exif/helpers/exif_event.php b/modules/exif/helpers/exif_event.php
index e594c765..72e88041 100644
--- a/modules/exif/helpers/exif_event.php
+++ b/modules/exif/helpers/exif_event.php
@@ -24,6 +24,12 @@ class exif_event_Core {
}
}
+ static function item_updated_data_file($item) {
+ if (!$item->is_album()) {
+ exif::extract($item);
+ }
+ }
+
static function item_deleted($item) {
db::build()
->delete("exif_records")
diff --git a/modules/exif/module.info b/modules/exif/module.info
index c8ae688e..e266e20e 100644
--- a/modules/exif/module.info
+++ b/modules/exif/module.info
@@ -1,3 +1,7 @@
name = "Exif Data"
description = "Extract Exif data and display it on photo pages."
version = 1
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:exif"
+discuss_url = "http://gallery.menalto.com/forum_module_exif"
diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php
index 33186fb5..4c8af852 100644
--- a/modules/g2_import/controllers/admin_g2_import.php
+++ b/modules/g2_import/controllers/admin_g2_import.php
@@ -24,23 +24,47 @@ class Admin_g2_import_Controller extends Admin_Controller {
g2_import::init();
}
- if (class_exists("GalleryCoreApi")) {
- $g2_stats = g2_import::stats();
- $g2_sizes = g2_import::common_sizes();
- }
-
$view = new Admin_View("admin.html");
$view->page_title = t("Gallery 2 import");
$view->content = new View("admin_g2_import.html");
+
+ if (class_exists("GalleryCoreApi")) {
+ $view->content->g2_stats = $g2_stats = g2_import::g2_stats();
+ $view->content->g3_stats = $g3_stats = g2_import::g3_stats();
+ $view->content->g2_sizes = g2_import::common_sizes();
+ $view->content->g2_version = g2_import::version();
+
+ // Don't count tags because we don't track them in g2_map
+ $view->content->g2_resource_count =
+ $g2_stats["users"] + $g2_stats["groups"] + $g2_stats["albums"] +
+ $g2_stats["photos"] + $g2_stats["movies"] + $g2_stats["comments"];
+ $view->content->g3_resource_count =
+ $g3_stats["user"] + $g3_stats["group"] + $g3_stats["album"] +
+ $g3_stats["item"] + $g3_stats["comment"] + $g3_stats["tag"];
+ }
+
$view->content->form = $this->_get_import_form();
$view->content->version = "";
+ $view->content->thumb_size = module::get_var("gallery", "thumb_size");
+ $view->content->resize_size = module::get_var("gallery", "resize_size");
if (g2_import::is_initialized()) {
- $view->content->g2_stats = $g2_stats;
- $view->content->g2_sizes = $g2_sizes;
- $view->content->thumb_size = module::get_var("gallery", "thumb_size");
- $view->content->resize_size = module::get_var("gallery", "resize_size");
- $view->content->version = g2_import::version();
+ if ((bool)ini_get("eaccelerator.enable") || (bool)ini_get("xcache.cacher")) {
+ message::warning(t("The eAccelerator and XCache PHP performance extensions are known to cause issues. If you're using either of those and are having problems, please disable them while you do your import. Add the following lines: <pre>%lines</pre> to gallery3/.htaccess and remove them when the import is done.", array("lines" => "\n\n php_value eaccelerator.enable 0\n php_value xcache.cacher off\n php_value xcache.optimizer off\n\n")));
+ }
+
+ foreach (array("notification", "search", "exif") as $module_id) {
+ if (module::is_active($module_id)) {
+ message::warning(
+ t("<a href=\"%url\">Deactivating</a> the <b>%module_id</b> module during your import will make it faster",
+ array("url" => url::site("admin/modules"), "module_id" => $module_id)));
+ }
+ }
+ if (module::is_active("akismet")) {
+ message::warning(
+ t("The Akismet module may mark some or all of your imported comments as spam. <a href=\"%url\">Deactivate</a> it to avoid that outcome.",
+ array("url" => url::site("admin/modules"))));
+ }
} else if (g2_import::is_configured()) {
$view->content->form->configure_g2_import->embed_path->add_error("invalid", 1);
}
@@ -59,12 +83,12 @@ class Admin_g2_import_Controller extends Admin_Controller {
$embed_path = "$embed_path/embed.php";
}
- if (g2_import::is_valid_embed_path($embed_path)) {
+ if (($g2_init_error = g2_import::is_valid_embed_path($embed_path)) == "ok") {
message::success(t("Gallery 2 path saved"));
module::set_var("g2_import", "embed_path", $embed_path);
url::redirect("admin/g2_import");
} else {
- $form->configure_g2_import->embed_path->add_error("invalid", 1);
+ $form->configure_g2_import->embed_path->add_error($g2_init_error, 1);
}
}
@@ -75,15 +99,37 @@ class Admin_g2_import_Controller extends Admin_Controller {
print $view;
}
+ public function autocomplete() {
+ $directories = array();
+ $path_prefix = Input::instance()->get("q");
+ foreach (glob("{$path_prefix}*") as $file) {
+ if (is_dir($file) && !is_link($file)) {
+ $directories[] = $file;
+
+ // If we find an embed.php, include it as well
+ if (file_exists("$file/embed.php")) {
+ $directories[] = "$file/embed.php";
+ }
+ }
+ }
+
+ print implode("\n", $directories);
+ }
+
private function _get_import_form() {
+ $embed_path = module::get_var("g2_import", "embed_path", "");
$form = new Forge(
"admin/g2_import/save", "", "post", array("id" => "g-admin-configure-g2-import-form"));
$group = $form->group("configure_g2_import")->label(t("Configure Gallery 2 Import"));
$group->input("embed_path")->label(t("Filesystem path to your Gallery 2 embed.php file"))
- ->value(module::get_var("g2_import", "embed_path", ""));
+ ->value($embed_path);
$group->embed_path->error_messages(
"invalid", t("The path you entered is not a Gallery 2 installation."));
- $group->submit("")->value(t("Save"));
+ $group->embed_path->error_messages(
+ "broken", t("Your Gallery 2 install isn't working properly. Please verify it!"));
+ $group->embed_path->error_messages(
+ "missing", t("The path you entered does not exist."));
+ $group->submit("")->value($embed_path ? t("Change") : t("Continue"));
return $form;
}
} \ No newline at end of file
diff --git a/modules/g2_import/controllers/g2.php b/modules/g2_import/controllers/g2.php
index 6e60bed0..1252014f 100644
--- a/modules/g2_import/controllers/g2.php
+++ b/modules/g2_import/controllers/g2.php
@@ -34,14 +34,16 @@ class G2_Controller extends Controller {
$path = $input->get("path");
$id = $input->get("g2_itemId");
- if ($path || $id) {
+ if (($path && $path != 'index.php' && $path != 'main.php') || $id) {
if ($id) {
// Requests by id are either core.DownloadItem or core.ShowItem requests. Later versions of
// Gallery 2 don't specify g2_view if it's the default (core.ShowItem). And in some cases
// (bbcode, embedding) people are using the id style URLs although URL rewriting is enabled.
$where = array(array("g2_id", "=", $id));
$view = $input->get("g2_view");
- if ($view) {
+ if ($view == "core.DownloadItem") {
+ $where[] = array("resource_type", "IN", array("file", "resize", "thumbnail", "full"));
+ } else if ($view) {
$where[] = array("g2_url", "like", "%g2_view=$view%");
} // else: Assuming that the first search hit is sufficiently good.
} else if ($path) {
diff --git a/modules/g2_import/helpers/g2_import.php b/modules/g2_import/helpers/g2_import.php
index 22fb68c6..8a5d2c5f 100644
--- a/modules/g2_import/helpers/g2_import.php
+++ b/modules/g2_import/helpers/g2_import.php
@@ -31,7 +31,7 @@ class g2_import_Core {
}
static function is_initialized() {
- return g2_import::$init;
+ return g2_import::$init == "ok";
}
static function init() {
@@ -52,146 +52,163 @@ class g2_import_Core {
if (file_exists($mod_path)) {
dir::unlink($mod_path);
}
- return file_exists($embed_path) && g2_import::init_embed($embed_path);
+ return g2_import::init_embed($embed_path);
}
/**
* Initialize the embedded Gallery 2 instance. Call this before any other Gallery 2 calls.
+ *
+ * Return values:
+ * "ok" - the Gallery 2 install is fine
+ * "missing" - the embed path does not exist
+ * "invalid" - the install path is not a valid Gallery 2 code base
+ * "broken" - the embed path is correct, but the Gallery 2 install is broken
*/
static function init_embed($embed_path) {
if (!is_file($embed_path)) {
- return false;
+ return "missing";
}
- // Gallery 2 defines a class called Gallery. So does Gallery 3. They don't get along. So do
- // a total hack here and copy over a few critical files (embed.php, main.php, bootstrap.inc
- // and Gallery.class) and munge them so that we can rename the Gallery class to be
- // G2_Gallery. Is this retarded? Why yes it is.
- //
- // Store the munged files in a directory that's the md5 hash of the embed path so that
- // multiple import sources don't interfere with each other.
-
- $mod_path = VARPATH . "modules/g2_import/" . md5($embed_path);
- if (!file_exists($mod_path) || !file_exists("$mod_path/embed.php")) {
- @dir::unlink($mod_path);
- mkdir($mod_path);
-
- $config_dir = dirname($embed_path);
- if (filesize($embed_path) > 200) {
- // Regular install
- $base_dir = $config_dir;
- } else {
- // Multisite install. Line 2 of embed.php will be something like:
- // require('/usr/home/bharat/public_html/gallery2/embed.php');
- $lines = file($embed_path);
- preg_match("#require\('(.*)/embed.php'\);#", $lines[2], $matches);
- $base_dir = $matches[1];
+ try {
+ // Gallery 2 defines a class called Gallery. So does Gallery 3. They don't get along. So do
+ // a total hack here and copy over a few critical files (embed.php, main.php, bootstrap.inc
+ // and Gallery.class) and munge them so that we can rename the Gallery class to be
+ // G2_Gallery. Is this retarded? Why yes it is.
+ //
+ // Store the munged files in a directory that's the md5 hash of the embed path so that
+ // multiple import sources don't interfere with each other.
+
+ $mod_path = VARPATH . "modules/g2_import/" . md5($embed_path);
+ if (!file_exists($mod_path) || !file_exists("$mod_path/embed.php")) {
+ @dir::unlink($mod_path);
+ mkdir($mod_path);
+
+ $config_dir = dirname($embed_path);
+ if (filesize($embed_path) > 200) {
+ // Regular install
+ $base_dir = $config_dir;
+ } else {
+ // Multisite install. Line 2 of embed.php will be something like:
+ // require('/usr/home/bharat/public_html/gallery2/embed.php');
+ $lines = file($embed_path);
+ preg_match("#require\('(.*)/embed.php'\);#", $lines[2], $matches);
+ $base_dir = $matches[1];
+ }
+
+ file_put_contents(
+ "$mod_path/embed.php",
+ str_replace(
+ array(
+ "require_once(dirname(__FILE__) . '/modules/core/classes/GalleryDataCache.class');",
+ "require(dirname(__FILE__) . '/modules/core/classes/GalleryEmbed.class');"),
+ array(
+ "require_once('$base_dir/modules/core/classes/GalleryDataCache.class');",
+ "require('$base_dir/modules/core/classes/GalleryEmbed.class');"),
+ array_merge(
+ array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
+ file("$base_dir/embed.php"))));
+
+ file_put_contents(
+ "$mod_path/main.php",
+ str_replace(
+ array(
+ "include(dirname(__FILE__) . '/bootstrap.inc');",
+ "require_once(dirname(__FILE__) . '/init.inc');"),
+ array(
+ "include(dirname(__FILE__) . '/bootstrap.inc');",
+ "require_once('$base_dir/init.inc');"),
+ array_merge(
+ array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
+ file("$base_dir/main.php"))));
+
+ file_put_contents(
+ "$mod_path/bootstrap.inc",
+ str_replace(
+ array(
+ "require_once(dirname(__FILE__) . '/modules/core/classes/Gallery.class');",
+ "require_once(dirname(__FILE__) . '/modules/core/classes/GalleryDataCache.class');",
+ "define('GALLERY_CONFIG_DIR', dirname(__FILE__));",
+ "\$gallery =& new Gallery();",
+ "\$GLOBALS['gallery'] =& new Gallery();",
+ "\$gallery = new Gallery();"),
+ array(
+ "require_once(dirname(__FILE__) . '/Gallery.class');",
+ "require_once('$base_dir/modules/core/classes/GalleryDataCache.class');",
+ "define('GALLERY_CONFIG_DIR', '$config_dir');",
+ "\$gallery =& new G2_Gallery();",
+ "\$GLOBALS['gallery'] =& new G2_Gallery();",
+ "\$gallery = new G2_Gallery();"),
+ array_merge(
+ array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
+ file("$base_dir/bootstrap.inc"))));
+
+ file_put_contents(
+ "$mod_path/Gallery.class",
+ str_replace(
+ array("class Gallery",
+ "function Gallery"),
+ array("class G2_Gallery",
+ "function G2_Gallery"),
+ array_merge(
+ array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
+ file("$base_dir/modules/core/classes/Gallery.class"))));
+ } else {
+ // Ok, this is a good one. If you're running a bytecode accelerator and you move your
+ // Gallery install, these files sometimes get cached with the wrong path and then fail to
+ // load properly.
+ // Documented in https://sourceforge.net/apps/trac/gallery/ticket/1253
+ touch("$mod_path/embed.php");
+ touch("$mod_path/main.php");
+ touch("$mod_path/bootstrap.inc");
+ touch("$mod_path/Gallery.class.inc");
+ }
+
+ require("$mod_path/embed.php");
+ if (!class_exists("GalleryEmbed")) {
+ return "invalid";
}
- file_put_contents(
- "$mod_path/embed.php",
- str_replace(
- array(
- "require_once(dirname(__FILE__) . '/modules/core/classes/GalleryDataCache.class');",
- "require(dirname(__FILE__) . '/modules/core/classes/GalleryEmbed.class');"),
- array(
- "require_once('$base_dir/modules/core/classes/GalleryDataCache.class');",
- "require('$base_dir/modules/core/classes/GalleryEmbed.class');"),
- array_merge(array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
- file("$base_dir/embed.php"))));
-
- file_put_contents(
- "$mod_path/main.php",
- str_replace(
- array(
- "include(dirname(__FILE__) . '/bootstrap.inc');",
- "require_once(dirname(__FILE__) . '/init.inc');"),
- array(
- "include(dirname(__FILE__) . '/bootstrap.inc');",
- "require_once('$base_dir/init.inc');"),
- array_merge(array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
- file("$base_dir/main.php"))));
-
- file_put_contents(
- "$mod_path/bootstrap.inc",
- str_replace(
- array("require_once(dirname(__FILE__) . '/modules/core/classes/Gallery.class');",
- "require_once(dirname(__FILE__) . '/modules/core/classes/GalleryDataCache.class');",
- "define('GALLERY_CONFIG_DIR', dirname(__FILE__));",
- "\$gallery =& new Gallery();",
- "\$GLOBALS['gallery'] =& new Gallery();",
- "\$gallery = new Gallery();"),
- array("require_once(dirname(__FILE__) . '/Gallery.class');",
- "require_once('$base_dir/modules/core/classes/GalleryDataCache.class');",
- "define('GALLERY_CONFIG_DIR', '$config_dir');",
- "\$gallery =& new G2_Gallery();",
- "\$GLOBALS['gallery'] =& new G2_Gallery();",
- "\$gallery = new G2_Gallery();"),
- array_merge(array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
- file("$base_dir/bootstrap.inc"))));
-
- file_put_contents(
- "$mod_path/Gallery.class",
- str_replace(
- array("class Gallery",
- "function Gallery"),
- array("class G2_Gallery",
- "function G2_Gallery"),
- array_merge(array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"),
- file("$base_dir/modules/core/classes/Gallery.class"))));
- } else {
- // Ok, this is a good one. If you're running a bytecode accelerator and you move your
- // Gallery install, these files sometimes get cached with the wrong path and then fail to
- // load properly.
- // Documented in https://sourceforge.net/apps/trac/gallery/ticket/1253
- touch("$mod_path/embed.php");
- touch("$mod_path/main.php");
- touch("$mod_path/bootstrap.inc");
- touch("$mod_path/Gallery.class.inc");
- }
-
- require("$mod_path/embed.php");
- if (!class_exists("GalleryEmbed")) {
- return false;
- }
-
- $ret = GalleryEmbed::init();
- if ($ret) {
- Kohana_Log::add("error", "Gallery 2 call failed with: " . $ret->getAsText());
- return false;
- }
-
- $admin_group_id = g2(GalleryCoreApi::getPluginParameter("module", "core", "id.adminGroup"));
- $admins = g2(GalleryCoreApi::fetchUsersForGroup($admin_group_id, 1));
- $admin_id = current(array_flip($admins));
- $admin = g2(GalleryCoreApi::loadEntitiesById($admin_id));
- $GLOBALS["gallery"]->setActiveUser($admin);
+ $ret = GalleryEmbed::init();
+ if ($ret) {
+ Kohana_Log::add("error", "Gallery 2 call failed with: " . $ret->getAsText());
+ return "broken";
+ }
- // Make sure we have an embed location so that embedded url generation comes out ok. Without
- // this, the Gallery2 ModRewrite code won't try to do url generation.
- $g2_embed_location =
- g2(GalleryCoreApi::getPluginParameter("module", "rewrite", "modrewrite.embeddedLocation"));
+ $admin_group_id = g2(GalleryCoreApi::getPluginParameter("module", "core", "id.adminGroup"));
+ $admins = g2(GalleryCoreApi::fetchUsersForGroup($admin_group_id, 1));
+ $admin_id = current(array_flip($admins));
+ $admin = g2(GalleryCoreApi::loadEntitiesById($admin_id));
+ $GLOBALS["gallery"]->setActiveUser($admin);
- if (empty($g2_embed_location)) {
+ // Make sure we have an embed location so that embedded url generation comes out ok. Without
+ // this, the Gallery2 ModRewrite code won't try to do url generation.
$g2_embed_location =
- g2(GalleryCoreApi::getPluginParameter("module", "rewrite", "modrewrite.galleryLocation"));
- g2(GalleryCoreApi::setPluginParameter(
- "module", "rewrite", "modrewrite.embeddedLocation", $g2_embed_location));
- g2($gallery->getStorage()->checkPoint());
- }
+ g2(GalleryCoreApi::getPluginParameter("module", "rewrite", "modrewrite.embeddedLocation"));
+
+ if (empty($g2_embed_location)) {
+ $g2_embed_location =
+ g2(GalleryCoreApi::getPluginParameter("module", "rewrite", "modrewrite.galleryLocation"));
+ g2(GalleryCoreApi::setPluginParameter("module", "rewrite", "modrewrite.embeddedLocation",
+ $g2_embed_location));
+ g2($gallery->getStorage()->checkPoint());
+ }
- if ($g2_embed_location) {
- self::$g2_base_url = $g2_embed_location;
- } else {
- self::$g2_base_url = $GLOBALS["gallery"]->getUrlGenerator()->generateUrl(
- array(),
- array("forceSessionId" => false,
- "htmlEntities" => false,
- "urlEncode" => false,
- "useAuthToken" => false));
+ if ($g2_embed_location) {
+ self::$g2_base_url = $g2_embed_location;
+ } else {
+ self::$g2_base_url = $GLOBALS["gallery"]->getUrlGenerator()->generateUrl(
+ array(),
+ array("forceSessionId" => false,
+ "htmlEntities" => false,
+ "urlEncode" => false,
+ "useAuthToken" => false));
+ }
+ } catch (ErrorException $e) {
+ Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString());
+ return "broken";
}
- return true;
+ return "ok";
}
/**
@@ -219,7 +236,7 @@ class g2_import_Core {
* Return a set of statistics about the number of users, groups, albums, photos, movies and
* comments available for import from the Gallery 2 instance.
*/
- static function stats() {
+ static function g2_stats() {
global $gallery;
$root_album_id = g2(GalleryCoreApi::getDefaultAlbumId());
$stats["users"] = g2(GalleryCoreApi::fetchUserCount());
@@ -248,6 +265,25 @@ class g2_import_Core {
}
/**
+ * Return a set of statistics about the number of users, groups, albums, photos, movies and
+ * comments already imported into the Gallery 3 instance.
+ */
+ static function g3_stats() {
+ $g3_stats = array(
+ "album" => 0, "comment" => 0, "item" => 0, "user" => 0, "group" => 0, "tag" => 0);
+ foreach (db::build()
+ ->select("resource_type")
+ ->select(array("C" => 'COUNT("*")'))
+ ->from("g2_maps")
+ ->where("resource_type", "IN", array("album", "comment", "item", "user", "group"))
+ ->group_by("resource_type")
+ ->execute() as $row) {
+ $g3_stats[$row->resource_type] = $row->C;
+ }
+ return $g3_stats;
+ }
+
+ /**
* Import a single group.
*/
static function import_group(&$queue) {
@@ -429,49 +465,8 @@ class g2_import_Core {
$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->title or $album->title = $album->name;
- $album->description = self::_decode_html_special_chars(self::extract_description($g2_album));
- $album->owner_id = self::map($g2_album->getOwnerId());
- try {
- $album->view_count = (int) g2(GalleryCoreApi::fetchItemViewCount($g2_album_id));
- } catch (Exception $e) {
- // @todo log
- $album->view_count = 0;
- }
- $album->created = $g2_album->getCreationTimestamp();
-
- $order_map = array(
- "originationTimestamp" => "captured",
- "creationTimestamp" => "created",
- "description" => "description",
- "modificationTimestamp" => "updated",
- "orderWeight" => "weight",
- "pathComponent" => "name",
- "summary" => "description",
- "title" => "title",
- "viewCount" => "view_count");
- $direction_map = array(
- 1 => "ASC",
- ORDER_ASCENDING => "ASC",
- ORDER_DESCENDING => "DESC");
- // Only consider G2's first sort order
- $g2_order = explode("|", $g2_album->getOrderBy() . "");
- $g2_order = $g2_order[0];
- if (empty($g2_order)) {
- $g2_order = g2(GalleryCoreApi::getPluginParameter('module', 'core', 'default.orderBy'));
- }
- $g2_order_direction = explode("|", $g2_album->getOrderDirection() . "");
- $g2_order_direction = $g2_order_direction[0];
- if (empty($g2_order_direction)) {
- $g2_order_direction =
- g2(GalleryCoreApi::getPluginParameter('module', 'core', 'default.orderDirection'));
- }
- if (array_key_exists($g2_order, $order_map)) {
- $album->sort_column = $order_map[$g2_order];
- $album->sort_order = $direction_map[$g2_order_direction];
- }
+
+ g2_import::set_album_values($album, $g2_album);
try {
$album->save();
@@ -494,17 +489,66 @@ class g2_import_Core {
}
/**
+ * Transfer over all the values from a G2 album to a G3 album.
+ */
+ static function set_album_values($album, $g2_album) {
+ $album->name = $g2_album->getPathComponent();
+ $album->title = self::_decode_html_special_chars($g2_album->getTitle());
+ $album->title or $album->title = $album->name;
+ $album->description = self::_decode_html_special_chars(self::extract_description($g2_album));
+ $album->owner_id = self::map($g2_album->getOwnerId());
+ try {
+ $album->view_count = (int) g2(GalleryCoreApi::fetchItemViewCount($g2_album_id));
+ } catch (Exception $e) {
+ // @todo log
+ $album->view_count = 0;
+ }
+ $album->created = $g2_album->getCreationTimestamp();
+
+ $order_map = array(
+ "originationTimestamp" => "captured",
+ "creationTimestamp" => "created",
+ "description" => "description",
+ "modificationTimestamp" => "updated",
+ "orderWeight" => "weight",
+ "pathComponent" => "name",
+ "summary" => "description",
+ "title" => "title",
+ "viewCount" => "view_count");
+ $direction_map = array(
+ 1 => "ASC",
+ ORDER_ASCENDING => "ASC",
+ ORDER_DESCENDING => "DESC");
+ // G2 sorts can either be <sort> or <presort>|<sort>. Right now we can't
+ // map presorts so ignore them.
+ $g2_order = explode("|", $g2_album->getOrderBy() . "");
+ $g2_order = end($g2_order);
+ if (empty($g2_order)) {
+ $g2_order = g2(GalleryCoreApi::getPluginParameter('module', 'core', 'default.orderBy'));
+ }
+ $g2_order_direction = explode("|", $g2_album->getOrderDirection() . "");
+ $g2_order_direction = $g2_order_direction[0];
+ if (empty($g2_order_direction)) {
+ $g2_order_direction =
+ g2(GalleryCoreApi::getPluginParameter('module', 'core', 'default.orderDirection'));
+ }
+ if (array_key_exists($g2_order, $order_map)) {
+ $album->sort_column = $order_map[$g2_order];
+ $album->sort_order = $direction_map[$g2_order_direction];
+ }
+ }
+
+ /**
* Set the highlight properly for a single album
*/
static function set_album_highlight(&$queue) {
// Dequeue the current album and enqueue its children
list($g2_album_id, $children) = each($queue);
- if (empty($children)) {
- return;
- }
unset($queue[$g2_album_id]);
- foreach ($children as $key => $value) {
- $queue[$key] = $value;
+ if (!empty($children)) {
+ foreach ($children as $key => $value) {
+ $queue[$key] = $value;
+ }
}
$messages = array();
@@ -516,7 +560,7 @@ class g2_import_Core {
$table = g2(GalleryCoreApi::fetchThumbnailsByItemIds(array($g2_album_id)));
if (isset($table[$g2_album_id])) {
// Backtrack the source id to an item
- $g2_source = $table[$g2_album_id];
+ $orig_g2_source = $g2_source = $table[$g2_album_id];
while (GalleryUtilities::isA($g2_source, "GalleryDerivative")) {
$g2_source = g2(GalleryCoreApi::loadEntitiesById($g2_source->getDerivativeSourceId()));
}
@@ -540,6 +584,11 @@ class g2_import_Core {
array("name" => $g3_album->name)),
$e);
}
+
+ self::set_map(
+ $orig_g2_source->getId(), $g3_album->id,
+ "thumbnail",
+ self::g2_url(array("view" => "core.DownloadItem", "itemId" => $orig_g2_source->getId())));
}
}
}
@@ -859,9 +908,13 @@ class g2_import_Core {
array("id" => $g2_comment_id, "exception" => (string)$e));
}
- if (self::map($g2_comment->getId())) {
- // Already imported
- return;
+ if ($id = self::map($g2_comment->getId())) {
+ if (ORM::factory("comment", $id)->loaded()) {
+ // Already imported and still exists
+ return;
+ }
+ // This comment was already imported, but now it no longer exists. Import it again, per
+ // ticket #1736.
}
$item_id = self::map($g2_comment->getParentId());
@@ -899,10 +952,11 @@ class g2_import_Core {
self::set_map($g2_comment->getId(), $comment->id, "comment");
// Backdate the creation date. We can't do this at creation time because
- // Comment_Model::save() will override it.
+ // Comment_Model::save() will override it. Leave the updated date alone
+ // so that if the comments get marked as spam, they don't immediately get
+ // flushed (see ticket #1736)
db::update("comments")
->set("created", $g2_comment->getDate())
- ->set("updated", $g2_comment->getDate())
->where("id", "=", $comment->id)
->execute();
}
@@ -1243,6 +1297,7 @@ class g2_import_Core {
* Associate a Gallery 2 id with a Gallery 3 item id.
*/
static function set_map($g2_id, $g3_id, $resource_type, $g2_url=null) {
+ self::clear_map($g2_id, $resource_type);
$g2_map = ORM::factory("g2_map");
$g2_map->g3_id = $g3_id;
$g2_map->g2_id = $g2_id;
@@ -1257,6 +1312,17 @@ class g2_import_Core {
self::$map[$g2_id] = $g3_id;
}
+ /**
+ * Remove all map entries associated with the given Gallery 2 id.
+ */
+ static function clear_map($g2_id, $resource_type) {
+ db::build()
+ ->delete("g2_maps")
+ ->where("g2_id", "=", $g2_id)
+ ->where("resource_type", "=", $resource_type)
+ ->execute();
+ }
+
static function log($msg) {
message::warning($msg);
Kohana_Log::add("alert", $msg);
diff --git a/modules/g2_import/helpers/g2_import_event.php b/modules/g2_import/helpers/g2_import_event.php
index 0e078d08..0afa62d8 100644
--- a/modules/g2_import/helpers/g2_import_event.php
+++ b/modules/g2_import/helpers/g2_import_event.php
@@ -34,7 +34,7 @@ class g2_import_event_Core {
->get("settings_menu")
->append(Menu::factory("link")
->id("g2_import")
- ->label(t("Gallery 2 Import"))
+ ->label(t("Gallery 2 import"))
->url(url::site("admin/g2_import")));
}
}
diff --git a/modules/g2_import/helpers/g2_import_task.php b/modules/g2_import/helpers/g2_import_task.php
index 6bda8f17..31615a55 100644
--- a/modules/g2_import/helpers/g2_import_task.php
+++ b/modules/g2_import/helpers/g2_import_task.php
@@ -56,7 +56,7 @@ class g2_import_task_Core {
$mode = $task->get("mode");
$queue = $task->get("queue");
if (!isset($mode)) {
- $stats = g2_import::stats();
+ $stats = g2_import::g2_stats();
$stats["items"] = $stats["photos"] + $stats["movies"];
unset($stats["photos"]);
unset($stats["movies"]);
@@ -127,6 +127,12 @@ class g2_import_task_Core {
$g2_root_id = g2(GalleryCoreApi::getDefaultAlbumId());
$tree = g2(GalleryCoreApi::fetchAlbumTree());
$task->set("queue", $queue = array($g2_root_id => $tree));
+
+ // Update the root album to reflect the Gallery2 root album.
+ $root_album = item::root();
+ g2_import::set_album_values(
+ $root_album, g2(GalleryCoreApi::loadEntitiesById($g2_root_id)));
+ $root_album->save();
}
$log_message = g2_import::import_album($queue);
if ($log_message) {
diff --git a/modules/g2_import/module.info b/modules/g2_import/module.info
index 977af251..30fb46d4 100644
--- a/modules/g2_import/module.info
+++ b/modules/g2_import/module.info
@@ -1,3 +1,7 @@
name = "Gallery2 Import"
description = "Import your Gallery 2 content into Gallery 3"
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:g2_import"
+discuss_url = "http://gallery.menalto.com/forum_module_g2_import"
diff --git a/modules/g2_import/views/admin_g2_import.html.php b/modules/g2_import/views/admin_g2_import.html.php
index cb13363a..9c4eb840 100644
--- a/modules/g2_import/views/admin_g2_import.html.php
+++ b/modules/g2_import/views/admin_g2_import.html.php
@@ -1,24 +1,64 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
+<?= $theme->css("jquery.autocomplete.css") ?>
+<?= $theme->script("jquery.autocomplete.js") ?>
+<script type="text/javascript">
+$("document").ready(function() {
+ $("form input[name=embed_path]").autocomplete(
+ "<?= url::site("__ARGS__") ?>".replace("__ARGS__", "admin/g2_import/autocomplete"),
+ {
+ max: 256,
+ loadingClass: "g-loading-small",
+ });
+});
+</script>
+
<div id="g-admin-g2-import" class="g-block">
<h1> <?= t("Gallery 2 import") ?> </h1>
<p>
<?= t("Import your Gallery 2 users, photos, movies, comments and tags into your new Gallery 3 installation.") ?>
</p>
- <div class="g-block-content">
- <?= $form ?>
- </div>
+ <script type="text/javascript">
+ $(document).ready(function() {
+ $("#g-admin-g2-import-tabs").tabs()
+ <? if (!isset($g2_version)): ?>
+ .tabs("disable", 1)
+ .tabs("disable", 2)
+ <? elseif ($g3_resource_count > .9 * $g2_resource_count): ?>
+ .tabs("select", 2)
+ <? else: ?>
+ .tabs("select", 1)
+ <? endif ?>
+ ;
- <div class="g-block-content">
- <? if (g2_import::is_initialized()): ?>
- <div id="g-admin-g2-import-details">
- <h2> <?= t("Import") ?> </h2>
- <ul id="g-action-status" class="g-message-block">
- <li class="g-success">
- <?= t("Gallery version %version detected", array("version" => $version)) ?>
+ // Show the tabs after the page has loaded to prevent Firefox from rendering the
+ // unstyled page and then flashing.
+ $("#g-admin-g2-import-tabs").show();
+ });
+ </script>
+ <div id="g-admin-g2-import-tabs" class="g-block-content" style="display: none">
+ <ul>
+ <li>
+ <a href="#g-admin-g2-import-configure"><?= t("1. Configure Gallery2 path") ?></a>
+ </li>
+ <li>
+ <a href="#g-admin-g2-import-import"><?= t("2. Import!") ?></a>
+ </li>
+ <li>
+ <a href="#g-admin-g2-import-notes"><?= t("3. After your import") ?></a>
+ </li>
+ </ul>
+ <div id="g-admin-g2-import-configure" class="g-block-content">
+ <?= $form ?>
+ </div>
+ <div id="g-admin-g2-import-import">
+ <? if (isset($g2_version)): ?>
+ <ul>
+ <li>
+ <?= t("Gallery version %version detected", array("version" => $g2_version)) ?>
</li>
<? if ($g2_sizes["thumb"]["size"] && $thumb_size != $g2_sizes["thumb"]["size"]): ?>
- <li class="g-warning">
+ <li>
<?= t("Your most common thumbnail size in Gallery 2 is %g2_pixels pixels, but your Gallery 3 thumbnail size is set to %g3_pixels pixels. <a href=\"%url\">Using the same value</a> will speed up your import.",
array("g2_pixels" => $g2_sizes["thumb"]["size"],
"g3_pixels" => $thumb_size,
@@ -27,73 +67,84 @@
<? endif ?>
<? if ($g2_sizes["resize"]["size"] && $resize_size != $g2_sizes["resize"]["size"]): ?>
- <li class="g-warning">
+ <li>
<?= t("Your most common intermediate size in Gallery 2 is %g2_pixels pixels, but your Gallery 3 intermediate size is set to %g3_pixels pixels. <a href=\"%url\">Using the same value</a> will speed up your import.",
- array("g2_pixels" => $g2_sizes["resize"]["size"],
- "g3_pixels" => $resize_size,
- "url" => html::mark_clean(url::site("admin/theme_options")))) ?>
+ array("g2_pixels" => $g2_sizes["resize"]["size"],
+ "g3_pixels" => $resize_size,
+ "url" => html::mark_clean(url::site("admin/theme_options")))) ?>
</li>
<? endif ?>
- <li class="g-info">
- <?= t("Your Gallery 2 has the following importable data in it:") ?>
- <p>
- <?= t2("1 user", "%count users", $g2_stats["users"]) ?>,
- <?= t2("1 group", "%count groups", $g2_stats["groups"]) ?>,
- <?= t2("1 album", "%count albums", $g2_stats["albums"]) ?>,
- <?= t2("1 photo", "%count photos", $g2_stats["photos"]) ?>,
- <?= t2("1 movie", "%count movies", $g2_stats["movies"]) ?>,
- <?= t2("1 comment", "%count comments", $g2_stats["comments"]) ?>,
- <?= t2("1 tagged photo/movie/album",
- "%count tagged photos/movies/albums", $g2_stats["tags"]) ?>
- </p>
+ <li>
+ <?
+ $t = array();
+ $t[] = t2("1 user", "%count users", $g2_stats["users"]);
+ $t[] = t2("1 group", "%count groups", $g2_stats["groups"]);
+ $t[] = t2("1 album", "%count albums", $g2_stats["albums"]);
+ $t[] = t2("1 photo", "%count photos/movies", $g2_stats["photos"] + $g2_stats["movies"]);
+ $t[] = t2("1 comment", "%count comments", $g2_stats["comments"]);
+ $t[] = t2("1 tagged photo/movie/album", "%count tagged photos/movies/albums",
+ $g2_stats["tags"]);
+ ?>
+ <?= t("Your Gallery 2 has the following importable data in it: %t0, %t1, %t2, %t3, %t4, %t5",
+ array("t0" => $t[0], "t1" => $t[1], "t2" => $t[2],
+ "t3" => $t[3], "t4" => $t[4], "t5" => $t[5])) ?>
</li>
- </ul>
+ <? if ($g3_resource_count): ?>
+ <li>
+ <?
+ $t = array();
+ $t[] = t2("1 user", "%count users", $g3_stats["user"]);
+ $t[] = t2("1 group", "%count groups", $g3_stats["group"]);
+ $t[] = t2("1 album", "%count albums", $g3_stats["album"]);
+ $t[] = t2("1 photo/movie", "%count photos/movies", $g3_stats["item"]);
+ $t[] = t2("1 comment", "%count comments", $g3_stats["comment"]);
+ $t[] = t2("1 tagged photo/movie/album", "%count tagged photos/movies/albums", $g3_stats["tag"]);
+ ?>
+ <?= t("It looks like you've imported the following Gallery 2 data already: %t0, %t1, %t2, %t3, %t4, %t5",
+ array("t0" => $t[0], "t1" => $t[1], "t2" => $t[2],
+ "t3" => $t[3], "t4" => $t[4], "t5" => $t[5])) ?>
+ </li>
+ <? endif ?>
+ </ul>
<p>
<a class="g-button g-dialog-link ui-state-default ui-corner-all"
href="<?= url::site("admin/maintenance/start/g2_import_task::import?csrf=$csrf") ?>">
<?= t("Begin import!") ?>
</a>
</p>
+ <? endif ?>
</div>
+ <div id="g-admin-g2-import-notes" class="g-text">
+ <ul>
+ <li>
+ <?= t("Gallery 3 does not support per-user / per-item permissions. <b>Review permissions!</b>") ?>
+ </li>
+ <li>
+ <?= t("The only supported file formats are JPG, PNG and GIF, FLV and MP4. Other formats will be skipped.") ?>
+ </li>
+ <li>
+ <p>
+ <?= t("Redirecting Gallery 2 URLs once your migration is complete. Put this block at the top of your gallery2/.htaccess file and all Gallery 2 urls will be redirected to Gallery 3") ?>
+ </p>
- <div class="g-block-content">
- <div id="g-admin-g2-import-notes">
- <h2> <?= t("Notes") ?> </h2>
- <ul class="enumeration">
- <li>
- <?= t("Gallery 3 does not support per-user / per-item permissions. <b>Review permissions after your import is done.</b>") ?>
- </li>
- <li>
- <?= t("The only supported file formats are JPG, PNG and GIF, FLV and MP4. Other formats will be skipped.") ?>
- </li>
- <li>
- <?= t("Deactivating the <b>notification</b>, <b>search</b> and <b>exif</b> modules during your import will make it go faster.") ?>
- </li>
- <li>
- <?= t("The eAccelerator and XCache PHP performance extensions are known to cause issues. If you're using either of those and are having problems, please disable them while you do your import. Add the following lines: <pre>%lines</pre> to gallery3/.htaccess and remove them when the import is done.", array("lines" => "\n\n php_value eaccelerator.enable 0\n php_value xcache.cacher off\n php_value xcache.optimizer off\n\n")) ?>
- </li>
- </ul>
- </div>
- </div>
-
- <div class="g-block-content">
- <div>
- <h2> <?= t("Migrating from Gallery 2") ?> </h2>
- <p>
- <?= t("Once your migration is complete, put this block at the top of your gallery2/.htaccess file and all Gallery 2 urls will be redirected to Gallery 3") ?>
- </p>
-
- <textarea rows="4" cols="60">&lt;IfModule mod_rewrite.c&gt;
- Options +FollowSymLinks
- RewriteEngine On
- RewriteBase <?= html::clean(g2_import::$g2_base_url) ?>
+ <textarea id="g-g2-redirect-rules" rows="4" cols="60">&lt;IfModule mod_rewrite.c&gt;
+ Options +FollowSymLinks
+ RewriteEngine On
+ RewriteBase <?= html::clean(g2_import::$g2_base_url) ?>
- RewriteRule ^(.*)$ <?= url::site("g2/map?path=\$1") ?> [QSA,L,R=301]
-&lt;/IfModule&gt;</textarea>
- </div>
- <? endif ?>
+ RewriteRule ^(.*)$ <?= url::site("g2/map?path=\$1") ?> [QSA,L,R=301]
+ &lt;/IfModule&gt;</textarea>
+ <script type="text/javascript">
+ $(document).ready(function() {
+ $("#g-g2-redirect-rules").click(function(event) {
+ this.select();
+ });
+ });
+ </script>
+ </li>
+ </ul>
</div>
</div>
</div>
diff --git a/modules/gallery/config/locale.php b/modules/gallery/config/locale.php
index 0509e45f..bce7fb49 100644
--- a/modules/gallery/config/locale.php
+++ b/modules/gallery/config/locale.php
@@ -29,13 +29,14 @@
$config['language'] = array('en_US', 'English_United States');
/**
- * Locale timezone. Defaults to use the server timezone.
+ * Locale timezone. Set in 'Advanced' settings, falling back to the server's zone.
* @see http://php.net/timezones
*/
-$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";
+if (file_exists(VARPATH . "database.php")) {
+ $config['timezone'] = module::get_var("gallery", "timezone", date_default_timezone_get());
+} else {
+ // Gallery3 is not installed yet -- don't make module::get_var() calls.
+ $config['timezone'] = date_default_timezone_get();
}
// i18n settings
diff --git a/modules/gallery/controllers/admin_graphics.php b/modules/gallery/controllers/admin_graphics.php
index a2d19d4a..a8a7cdc0 100644
--- a/modules/gallery/controllers/admin_graphics.php
+++ b/modules/gallery/controllers/admin_graphics.php
@@ -40,6 +40,8 @@ class Admin_Graphics_Controller extends Admin_Controller {
$msg = t("Changed graphics toolkit to: %toolkit", array("toolkit" => $tk->$toolkit_id->name));
message::success($msg);
log::success("graphics", $msg);
+
+ module::event("graphics_toolkit_change", $toolkit_id);
}
url::redirect("admin/graphics");
diff --git a/modules/gallery/controllers/admin_modules.php b/modules/gallery/controllers/admin_modules.php
index 787785ea..b712d14f 100644
--- a/modules/gallery/controllers/admin_modules.php
+++ b/modules/gallery/controllers/admin_modules.php
@@ -19,6 +19,9 @@
*/
class Admin_Modules_Controller extends Admin_Controller {
public function index() {
+ // If modules need upgrading, this will get recreated in module::available()
+ site_status::clear("upgrade_now");
+
$view = new Admin_View("admin.html");
$view->page_title = t("Modules");
$view->content = new View("admin_modules.html");
@@ -103,9 +106,6 @@ class Admin_Modules_Controller extends Admin_Controller {
module::event("module_change", $changes);
- // If modules need upgrading, this will get recreated
- site_status::clear("upgrade_now");
-
// @todo this type of collation is questionable from an i18n perspective
if ($activated_names) {
message::success(t("Activated: %names", array("names" => join(", ", $activated_names))));
diff --git a/modules/gallery/controllers/admin_theme_options.php b/modules/gallery/controllers/admin_theme_options.php
index 055e063c..d9323ea0 100644
--- a/modules/gallery/controllers/admin_theme_options.php
+++ b/modules/gallery/controllers/admin_theme_options.php
@@ -59,6 +59,7 @@ class Admin_Theme_Options_Controller extends Admin_Controller {
module::set_var("gallery", "footer_text", $form->edit_theme->footer_text->value);
module::set_var("gallery", "show_credits", $form->edit_theme->show_credits->value);
module::set_var("gallery", "favicon_url", $form->edit_theme->favicon_url->value);
+ module::set_var("gallery", "apple_touch_icon_url", $form->edit_theme->apple_touch_icon_url->value);
module::event("theme_edit_form_completed", $form);
@@ -77,8 +78,10 @@ class Admin_Theme_Options_Controller extends Admin_Controller {
$group = $form->group("edit_theme")->label(t("Theme layout"));
$group->input("page_size")->label(t("Items per page"))->id("g-page-size")
->rules("required|valid_digit")
+ ->callback(array($this, "_valididate_page_size"))
->error_messages("required", t("You must enter a number"))
->error_messages("valid_digit", t("You must enter a number"))
+ ->error_messages("valid_min_value", t("The value must be greater than zero"))
->value(module::get_var("gallery", "page_size"));
$group->input("thumb_size")->label(t("Thumbnail size (in pixels)"))->id("g-thumb-size")
->rules("required|valid_digit")
@@ -93,6 +96,9 @@ class Admin_Theme_Options_Controller extends Admin_Controller {
$group->input("favicon_url")->label(t("URL (or relative path) to your favicon.ico"))
->id("g-favicon")
->value(module::get_var("gallery", "favicon_url"));
+ $group->input("apple_touch_icon_url")->label(t("URL (or relative path) to your Apple Touch icon"))
+ ->id("g-apple-touch")
+ ->value(module::get_var("gallery", "apple_touch_icon_url"));
$group->textarea("header_text")->label(t("Header text"))->id("g-header-text")
->value(module::get_var("gallery", "header_text"));
$group->textarea("footer_text")->label(t("Footer text"))->id("g-footer-text")
@@ -102,9 +108,15 @@ class Admin_Theme_Options_Controller extends Admin_Controller {
module::event("theme_edit_form", $form);
- $group = $form->group("buttons");
$group->submit("")->value(t("Save"));
return $form;
}
+
+ function _valididate_page_size($input) {
+ if ($input->value < 1) {
+ $input->add_error("valid_min_value", true);
+ }
+
+ }
}
diff --git a/modules/gallery/controllers/admin_themes.php b/modules/gallery/controllers/admin_themes.php
index cd8a5530..9cdc3db5 100644
--- a/modules/gallery/controllers/admin_themes.php
+++ b/modules/gallery/controllers/admin_themes.php
@@ -25,6 +25,9 @@ class Admin_Themes_Controller extends Admin_Controller {
$view->content->admin = module::get_var("gallery", "active_admin_theme");
$view->content->site = module::get_var("gallery", "active_site_theme");
$view->content->themes = $this->_get_themes();
+
+ site_status::clear("missing_site_theme");
+ site_status::clear("missing_admin_theme");
print $view;
}
diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php
index ccf6c1cb..8aa3bb35 100644
--- a/modules/gallery/controllers/albums.php
+++ b/modules/gallery/controllers/albums.php
@@ -69,12 +69,32 @@ class Albums_Controller extends Items_Controller {
"item" => $album,
"children" => $album->viewable()->children($page_size, $offset),
"parents" => $album->parents()->as_array(), // view calls empty() on this
+ "breadcrumbs" => Breadcrumb::array_from_item_parents($album),
"children_count" => $children_count));
$template->content = new View("album.html");
-
$album->increment_view_count();
print $template;
+ item::set_display_context_callback("Albums_Controller::get_display_context");
+ }
+
+ static function get_display_context($item) {
+ $where = array(array("type", "!=", "album"));
+ $position = item::get_position($item, $where);
+ if ($position > 1) {
+ list ($previous_item, $ignore, $next_item) =
+ $item->parent()->viewable()->children(3, $position - 2, $where);
+ } else {
+ $previous_item = null;
+ list ($next_item) = $item->parent()->viewable()->children(1, $position, $where);
+ }
+
+ return array("position" => $position,
+ "previous_item" => $previous_item,
+ "next_item" => $next_item,
+ "sibling_count" => $item->parent()->viewable()->children_count($where),
+ "parents" => $item->parents()->as_array(),
+ "breadcrumbs" => Breadcrumb::array_from_item_parents($item));
}
public function create($parent_id) {
diff --git a/modules/gallery/controllers/login.php b/modules/gallery/controllers/login.php
index b203b7d3..fdf5d7b7 100644
--- a/modules/gallery/controllers/login.php
+++ b/modules/gallery/controllers/login.php
@@ -42,8 +42,9 @@ class Login_Controller extends Controller {
public function html() {
$view = new Theme_View("page.html", "other", "login");
- $view->page_title = t("Login");
- $view->content = auth::get_login_form("login/auth_html");
+ $view->page_title = t("Log in to Gallery");
+ $view->content = new View("login_ajax.html");
+ $view->content->form = auth::get_login_form("login/auth_html");
print $view;
}
diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php
index 8e81c594..76263dc0 100644
--- a/modules/gallery/controllers/movies.php
+++ b/modules/gallery/controllers/movies.php
@@ -27,27 +27,11 @@ class Movies_Controller extends Items_Controller {
access::required("view", $movie);
- $where = array(array("type", "!=", "album"));
- $position = item::get_position($movie, $where);
- if ($position > 1) {
- list ($previous_item, $ignore, $next_item) =
- $movie->parent()->viewable()->children(3, $position - 2, $where);
- } else {
- $previous_item = null;
- list ($next_item) = $movie->parent()->viewable()->children(1, $position, $where);
- }
-
$template = new Theme_View("page.html", "item", "movie");
- $template->set_global(
- array("item" => $movie,
- "children" => array(),
- "children_count" => 0,
- "parents" => $movie->parents()->as_array(),
- "next_item" => $next_item,
- "previous_item" => $previous_item,
- "sibling_count" => $movie->parent()->viewable()->children_count($where),
- "position" => $position));
-
+ $template->set_global(array("item" => $movie,
+ "children" => array(),
+ "children_count" => 0));
+ $template->set_global(item::get_display_context($movie));
$template->content = new View("movie.html");
$movie->increment_view_count();
diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php
index 054300a1..7e78b205 100644
--- a/modules/gallery/controllers/photos.php
+++ b/modules/gallery/controllers/photos.php
@@ -27,27 +27,11 @@ class Photos_Controller extends Items_Controller {
access::required("view", $photo);
- $where = array(array("type", "!=", "album"));
- $position = item::get_position($photo, $where);
- if ($position > 1) {
- list ($previous_item, $ignore, $next_item) =
- $photo->parent()->viewable()->children(3, $position - 2, $where);
- } else {
- $previous_item = null;
- list ($next_item) = $photo->parent()->viewable()->children(1, $position, $where);
- }
-
$template = new Theme_View("page.html", "item", "photo");
- $template->set_global(
- array("item" => $photo,
- "children" => array(),
- "children_count" => 0,
- "parents" => $photo->parents()->as_array(),
- "next_item" => $next_item,
- "previous_item" => $previous_item,
- "sibling_count" => $photo->parent()->viewable()->children_count($where),
- "position" => $position));
-
+ $template->set_global(array("item" => $photo,
+ "children" => array(),
+ "children_count" => 0));
+ $template->set_global(item::get_display_context($photo));
$template->content = new View("photo.html");
$photo->increment_view_count();
diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php
index da4768fd..b6576ec0 100644
--- a/modules/gallery/controllers/quick.php
+++ b/modules/gallery/controllers/quick.php
@@ -36,8 +36,8 @@ class Quick_Controller extends Controller {
}
if ($degrees) {
- $tmpfile = tempnam(TMPPATH, "rotate") . "." .
- pathinfo($item->file_path(), PATHINFO_EXTENSION);
+ $tmpfile = system::temp_filename("rotate",
+ pathinfo($item->file_path(), PATHINFO_EXTENSION));
gallery_graphics::rotate($item->file_path(), $tmpfile, array("degrees" => $degrees), $item);
$item->set_data_file($tmpfile);
$item->save();
diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php
index 6b1455e4..9c2bf7d7 100644
--- a/modules/gallery/controllers/uploader.php
+++ b/modules/gallery/controllers/uploader.php
@@ -51,7 +51,7 @@ class Uploader_Controller extends Controller {
$file_validation = new Validation($_FILES);
$file_validation->add_rules(
"Filedata", "upload::valid", "upload::required",
- "upload::type[gif,jpg,jpeg,png,flv,mp4,m4v]");
+ "upload::type[" . implode(",", legal_file::get_extensions()) . "]");
if ($form->validate() && $file_validation->validate()) {
$temp_filename = upload::save("Filedata");
diff --git a/modules/gallery/css/gallery.css b/modules/gallery/css/gallery.css
index 275a3d7d..ecf89565 100644
--- a/modules/gallery/css/gallery.css
+++ b/modules/gallery/css/gallery.css
@@ -29,12 +29,12 @@
#g-add-photos-canvas object,
#g-add-photos-button {
- left: 137px;
- margin: .5em 0;
+ left: 90px;
+ margin: .5em 0;
padding: .4em 1em;
position: absolute;
top: 0;
- width: 175px;
+ width: 300px;
}
#g-add-photos-canvas object {
diff --git a/modules/gallery/helpers/MY_num.php b/modules/gallery/helpers/MY_num.php
index 9787044c..842a2ee3 100644
--- a/modules/gallery/helpers/MY_num.php
+++ b/modules/gallery/helpers/MY_num.php
@@ -37,4 +37,18 @@ class num extends num_Core {
return $val;
}
+
+ /**
+ * Convert a size value as accepted by PHP's shorthand to bytes.
+ * ref: http://us2.php.net/manual/en/function.ini-get.php
+ * ref: http://us2.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
+ */
+ static function convert_to_human_readable($num) {
+ foreach (array("G" => 1e9, "M" => 1e6, "K" => 1e3) as $k => $v) {
+ if ($num > $v) {
+ $num = round($num / $v) . $k;
+ }
+ }
+ return $num;
+ }
}
diff --git a/modules/gallery/helpers/encoding.php b/modules/gallery/helpers/encoding.php
new file mode 100644
index 00000000..7d5add34
--- /dev/null
+++ b/modules/gallery/helpers/encoding.php
@@ -0,0 +1,35 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2011 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 encoding_Core {
+ static function convert_to_utf8($value) {
+ if (function_exists("mb_detect_encoding")) {
+ // Rely on mb_detect_encoding()'s strict mode
+ $src_encoding = mb_detect_encoding($value, mb_detect_order(), true);
+ if ($src_encoding != "UTF-8") {
+ if (function_exists("mb_convert_encoding") && $src_encoding) {
+ $value = mb_convert_encoding($value, "UTF-8", $src_encoding);
+ } else {
+ $value = utf8_encode($value);
+ }
+ }
+ }
+ return $value;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php
index 1fafdef7..cbd9b33c 100644
--- a/modules/gallery/helpers/gallery.php
+++ b/modules/gallery/helpers/gallery.php
@@ -193,19 +193,25 @@ class gallery_Core {
*/
static function version_string() {
if (gallery::RELEASE_CHANNEL == "git") {
+ $build_number = gallery::build_number();
return sprintf(
- "%s (branch %s build %s)", gallery::VERSION, gallery::RELEASE_BRANCH,
- gallery::build_number());
+ "%s (branch %s, %s)", gallery::VERSION, gallery::RELEASE_BRANCH,
+ $build_number ? " build $build_number" : "unknown build number");
} else {
return sprintf("%s (%s)", gallery::VERSION, gallery::CODE_NAME);
}
}
/**
- * Return the contents of the .build_number file, which should be a single integer.
+ * Return the contents of the .build_number file, which should be a single integer
+ * or return null if the .build_number file is missing.
*/
static function build_number() {
- $result = parse_ini_file(DOCROOT . ".build_number");
- return $result["build_number"];
+ $build_file = DOCROOT . ".build_number";
+ if (file_exists($build_file)) {
+ $result = parse_ini_file(DOCROOT . ".build_number");
+ return $result["build_number"];
+ }
+ return null;
}
} \ No newline at end of file
diff --git a/modules/gallery/helpers/gallery_block.php b/modules/gallery/helpers/gallery_block.php
index b9ccf25b..49d16246 100644
--- a/modules/gallery/helpers/gallery_block.php
+++ b/modules/gallery/helpers/gallery_block.php
@@ -82,9 +82,13 @@ class gallery_block_Core {
break;
case "block_adder":
- $block->css_id = "g-block-adder";
- $block->title = t("Dashboard content");
- $block->content = gallery_block::get_add_block_form();
+ if ($form = gallery_block::get_add_block_form()) {
+ $block->css_id = "g-block-adder";
+ $block->title = t("Dashboard content");
+ $block->content = $form;
+ } else {
+ $block = "";
+ }
break;
case "language":
@@ -112,16 +116,28 @@ class gallery_block_Core {
$block->content->version_info = upgrade_checker::version_info();
$block->content->auto_check_enabled = upgrade_checker::auto_check_enabled();
$block->content->new_version = upgrade_checker::get_upgrade_message();
+ $block->content->build_number = gallery::build_number();
}
return $block;
}
static function get_add_block_form() {
+ $available_blocks = block_manager::get_available_admin_blocks();
+
+ $active = array();
+ foreach (array_merge(block_manager::get_active("dashboard_sidebar"),
+ block_manager::get_active("dashboard_center")) as $b) {
+ unset($available_blocks[implode(":", $b)]);
+ }
+
+ if (!$available_blocks) {
+ return;
+ }
+
$form = new Forge("admin/dashboard/add_block", "", "post",
array("id" => "g-add-dashboard-block-form"));
$group = $form->group("add_block")->label(t("Add Block"));
- $group->dropdown("id")->label(t("Available Blocks"))
- ->options(block_manager::get_available_admin_blocks());
+ $group->dropdown("id")->label(t("Available blocks"))->options($available_blocks);
$group->submit("center")->value(t("Add to center"));
$group->submit("sidebar")->value(t("Add to sidebar"));
return $form;
diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php
index 07817187..fbdb4ad5 100644
--- a/modules/gallery/helpers/gallery_event.php
+++ b/modules/gallery/helpers/gallery_event.php
@@ -539,9 +539,9 @@ class gallery_event_Core {
$v = new View("user_profile_info.html");
$fields = array("name" => t("Name"), "locale" => t("Language Preference"),
- "email" => t("Email"), "full_name" => t("Full name"), "url" => "Web site");
+ "email" => t("Email"), "full_name" => t("Full name"), "url" => t("Web site"));
if (!$data->user->guest) {
- $fields = array("name" => t("Name"), "full_name" => t("Full name"), "url" => "Web site");
+ $fields = array("name" => t("Name"), "full_name" => t("Full name"), "url" => t("Web site"));
}
$v->user_profile_data = array();
foreach ($fields as $field => $label) {
@@ -549,6 +549,8 @@ class gallery_event_Core {
$value = $data->user->$field;
if ($field == "locale") {
$value = locales::display_name($value);
+ } elseif ($field == "url") {
+ $value = html::mark_clean(html::anchor($data->user->$field));
}
$v->user_profile_data[(string) $label] = $value;
}
diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php
index 01c59eaa..7a9af402 100644
--- a/modules/gallery/helpers/gallery_installer.php
+++ b/modules/gallery/helpers/gallery_installer.php
@@ -304,14 +304,16 @@ class gallery_installer {
module::set_var("gallery", "maintenance_mode", 0);
module::set_var("gallery", "visible_title_length", 15);
module::set_var("gallery", "favicon_url", "lib/images/favicon.ico");
+ module::set_var("gallery", "apple_touch_icon_url", "lib/images/apple-touch-icon.png");
module::set_var("gallery", "email_from", "");
module::set_var("gallery", "email_reply_to", "");
module::set_var("gallery", "email_line_length", 70);
module::set_var("gallery", "email_header_separator", serialize("\n"));
module::set_var("gallery", "show_user_profiles_to", "registered_users");
module::set_var("gallery", "extra_binary_paths", "/usr/local/bin:/opt/local/bin:/opt/bin");
+ module::set_var("gallery", "timezone", null);
- module::set_version("gallery", 46);
+ module::set_version("gallery", 49);
}
static function upgrade($version) {
@@ -677,6 +679,19 @@ class gallery_installer {
module::set_var("gallery", "upgrade_checker_auto_enabled", true);
module::set_version("gallery", $version = 46);
}
+
+ if ($version == 46) {
+ module::set_var("gallery", "apple_touch_icon_url", "lib/images/apple-touch-icon.png");
+ module::set_version("gallery", $version = 47);
+ }
+
+ if ($version == 47 || $version == 48) {
+ // Add configuration variable to set timezone. Defaults to the currently
+ // used timezone (from PHP configuration). Note that in v48 we werew
+ // setting this value incorrectly, so we're going to stomp this value for v49.
+ module::set_var("gallery", "timezone", null);
+ module::set_version("gallery", $version = 49);
+ }
}
static function uninstall() {
diff --git a/modules/gallery/helpers/gallery_rss.php b/modules/gallery/helpers/gallery_rss.php
index 612872e6..9af67118 100644
--- a/modules/gallery/helpers/gallery_rss.php
+++ b/modules/gallery/helpers/gallery_rss.php
@@ -21,6 +21,14 @@
class gallery_rss_Core {
static function available_feeds($item, $tag) {
$feeds["gallery/latest"] = t("Latest photos and movies");
+
+ if ($item) {
+ $feed_item = $item -> is_album() ? $item : $item->parent();
+
+ $feeds["gallery/album/{$feed_item->id}"] =
+ t("%title photos and movies", array("title" => $feed_item->title));
+ }
+
return $feeds;
}
diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php
index c4a82390..e07839d9 100644
--- a/modules/gallery/helpers/gallery_theme.php
+++ b/modules/gallery/helpers/gallery_theme.php
@@ -64,7 +64,7 @@ class gallery_theme_Core {
if ($session->get("l10n_mode", false)) {
$buf .= $theme->css("l10n_client.css");
$buf .= $theme->script("jquery.cookie.js");
- $buf .=$theme->script("l10n_client.js");
+ $buf .= $theme->script("l10n_client.js");
}
return $buf;
}
diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php
index 04501132..3548faa1 100644
--- a/modules/gallery/helpers/graphics.php
+++ b/modules/gallery/helpers/graphics.php
@@ -176,17 +176,25 @@ class graphics_Core {
}
if (!empty($ops["thumb"])) {
+ if (file_exists($item->thumb_path())) {
+ $item->thumb_dirty = 0;
+ } else {
+ copy(MODPATH . "gallery/images/missing_photo.png", $item->thumb_path());
+ }
$dims = getimagesize($item->thumb_path());
$item->thumb_width = $dims[0];
$item->thumb_height = $dims[1];
- $item->thumb_dirty = 0;
}
if (!empty($ops["resize"])) {
+ if (file_exists($item->resize_path())) {
+ $item->resize_dirty = 0;
+ } else {
+ copy(MODPATH . "gallery/images/missing_photo.png", $item->resize_path());
+ }
$dims = getimagesize($item->resize_path());
$item->resize_width = $dims[0];
$item->resize_height = $dims[1];
- $item->resize_dirty = 0;
}
$item->save();
} catch (Exception $e) {
@@ -316,10 +324,10 @@ class graphics_Core {
// ImageMagick & GraphicsMagick
$magick_kits = array(
"imagemagick" => array(
- "name" => "ImageMagick", "binary" => "convert", "version" => "convert -v",
+ "name" => "ImageMagick", "binary" => "convert", "version_arg" => "-v",
"version_regex" => "/Version: \S+ (\S+)/"),
"graphicsmagick" => array(
- "name" => "GraphicsMagick", "binary" => "gm", "version" => "gm version",
+ "name" => "GraphicsMagick", "binary" => "gm", "version_arg" => "version",
"version_regex" => "/\S+ (\S+)/"));
// Loop through the kits
foreach ($magick_kits as $index => $settings) {
@@ -328,7 +336,8 @@ class graphics_Core {
$toolkits->$index->name = $settings["name"];
if ($path) {
if (@is_file($path) &&
- preg_match($settings["version_regex"], shell_exec($settings["version"]), $matches)) {
+ preg_match(
+ $settings["version_regex"], shell_exec($path . " " . $settings["version_arg"]), $matches)) {
$version = $matches[1];
$toolkits->$index->installed = true;
@@ -423,4 +432,23 @@ class graphics_Core {
return true;
}
+
+ /**
+ * Return the max file size that this graphics toolkit can handle.
+ */
+ static function max_filesize() {
+ if (module::get_var("gallery", "graphics_toolkit") == "gd") {
+ $memory_limit = trim(ini_get("memory_limit"));
+ $memory_limit_bytes = num::convert_to_bytes($memory_limit);
+
+ // GD expands images in memory and uses 4 bytes of RAM for every byte
+ // in the file.
+ $max_filesize = $memory_limit_bytes / 4;
+ $max_filesize_human_readable = num::convert_to_human_readable($max_filesize);
+ return array($max_filesize, $max_filesize_human_readable);
+ }
+
+ // Some arbitrarily large size
+ return array(1000000000, "1G");
+ }
}
diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php
index 1a5c631e..494c6db4 100644
--- a/modules/gallery/helpers/item.php
+++ b/modules/gallery/helpers/item.php
@@ -152,8 +152,18 @@ class item_Core {
* @param string $filename
*/
static function convert_filename_to_slug($filename) {
- $result = pathinfo($filename, PATHINFO_FILENAME);
+ $result = str_replace("&", "-and-", $filename);
+ $result = str_replace(" ", "-", $result);
+
+ // It's not easy to extend the text helper since it's called by the Input class which is
+ // referenced in hooks/init_gallery, so it's
+ if (class_exists("transliterate")) {
+ $result = transliterate::utf8_to_ascii($result);
+ } else {
+ $result = text::transliterate_to_ascii($result);
+ }
$result = preg_replace("/[^A-Za-z0-9-_]+/", "-", $result);
+ $result = preg_replace("/-+/", "-", $result);
return trim($result, "-");
}
@@ -392,4 +402,21 @@ class item_Core {
return $position;
}
+
+ /**
+ * Set the display context callback for any future item renders.
+ */
+ static function set_display_context_callback() {
+ Cache::instance()->set("display_context_" . $sid = Session::instance()->id(), func_get_args());
+ }
+
+ /**
+ * Call the display context callback for the given item
+ */
+ static function get_display_context($item) {
+ $args = Cache::instance()->get("display_context_" . $sid = Session::instance()->id());
+ $callback = $args[0];
+ $args[0] = $item;
+ return call_user_func_array($callback, $args);
+ }
} \ No newline at end of file
diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php
new file mode 100644
index 00000000..6f66c85c
--- /dev/null
+++ b/modules/gallery/helpers/legal_file.php
@@ -0,0 +1,83 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2011 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 legal_file_Core {
+ /**
+ * Create a default list of allowed photo extensions and then let modules modify it.
+ */
+ static function get_photo_extensions() {
+ $extensions_wrapper = new stdClass();
+ $extensions_wrapper->extensions = array("gif", "jpg", "jpeg", "png");
+ module::event("legal_photo_extensions", $extensions_wrapper);
+ return $extensions_wrapper->extensions;
+ }
+
+ /**
+ * Create a default list of allowed movie extensions and then let modules modify it.
+ */
+ static function get_movie_extensions() {
+ $extensions_wrapper = new stdClass();
+ $extensions_wrapper->extensions = array("flv", "mp4", "m4v");
+ module::event("legal_movie_extensions", $extensions_wrapper);
+ return $extensions_wrapper->extensions;
+ }
+
+ /**
+ * Create a merged list of all allowed photo and movie extensions.
+ */
+ static function get_extensions() {
+ $extensions = legal_file::get_photo_extensions();
+ if (movie::find_ffmpeg()) {
+ $extensions = array_merge($extensions, legal_file::get_movie_extensions());
+ }
+ return $extensions;
+ }
+
+ /**
+ * Create a merged list of all photo and movie filename filters,
+ * (e.g. "*.gif"), based on allowed extensions.
+ */
+ static function get_filters() {
+ $filters = array();
+ foreach (legal_file::get_extensions() as $extension) {
+ array_push($filters, "*." . $extension, "*." . strtoupper($extension));
+ }
+ return $filters;
+ }
+
+ /**
+ * Create a default list of allowed photo MIME types and then let modules modify it.
+ */
+ static function get_photo_types() {
+ $types_wrapper = new stdClass();
+ $types_wrapper->types = array("image/jpeg", "image/gif", "image/png");
+ module::event("legal_photo_types", $types_wrapper);
+ return $types_wrapper->types;
+ }
+
+ /**
+ * Create a default list of allowed movie MIME types and then let modules modify it.
+ */
+ static function get_movie_types() {
+ $types_wrapper = new stdClass();
+ $types_wrapper->types = array("video/flv", "video/x-flv", "video/mp4");
+ module::event("legal_movie_types", $types_wrapper);
+ return $types_wrapper->types;
+ }
+}
diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php
index 37f7f68a..4b7d4a5f 100644
--- a/modules/gallery/helpers/module.php
+++ b/modules/gallery/helpers/module.php
@@ -101,7 +101,7 @@ class module_Core {
$m->locked = false;
if ($m->active && $m->version != $m->code_version) {
- site_status::warning(t("Some of your modules are out of date. <a href=\"%upgrader_url\">Upgrade now!</a>", array("upgrader_url" => url::site("upgrader"))), "upgrade_now");
+ site_status::warning(t("Some of your modules are out of date. <a href=\"%upgrader_url\">Upgrade now!</a>", array("upgrader_url" => url::abs_site("upgrader"))), "upgrade_now");
}
}
diff --git a/modules/gallery/helpers/random.php b/modules/gallery/helpers/random.php
index 6016df7b..06542e8c 100644
--- a/modules/gallery/helpers/random.php
+++ b/modules/gallery/helpers/random.php
@@ -19,7 +19,7 @@
*/
class random_Core {
/**
- * Return a random 32 bit hash value.
+ * Return a random 32 byte hash value.
* @param string extra entropy data
*/
static function hash($entropy="") {
@@ -27,14 +27,6 @@ class random_Core {
}
/**
- * Return a random hexadecimal string of the given length.
- * @param int the desired length of the string
- */
- static function string($length) {
- return substr(random::hash(), 0, $length);
- }
-
- /**
* Return a random floating point number between 0 and 1
*/
static function percent() {
diff --git a/modules/gallery/helpers/system.php b/modules/gallery/helpers/system.php
index c39c7227..4110b4ed 100644
--- a/modules/gallery/helpers/system.php
+++ b/modules/gallery/helpers/system.php
@@ -40,4 +40,25 @@ class system_Core {
}
return null;
}
+
+ /**
+ * Create a file with a unique file name.
+ * This helper is similar to the built-in tempnam.
+ * It allows the caller to specify a prefix and an extension.
+ * It always places the file in TMPPATH.
+ */
+ static function temp_filename($prefix="", $extension="") {
+ do {
+ $basename = tempnam(TMPPATH, $prefix);
+ if (!$basename) {
+ return false;
+ }
+ $filename = "$basename.$extension";
+ $success = !file_exists($filename) && @rename($basename, $filename);
+ if (!$success) {
+ @unlink($basename);
+ }
+ } while (!$success);
+ return $filename;
+ }
} \ No newline at end of file
diff --git a/modules/gallery/helpers/theme.php b/modules/gallery/helpers/theme.php
index f285834c..37707f28 100644
--- a/modules/gallery/helpers/theme.php
+++ b/modules/gallery/helpers/theme.php
@@ -41,11 +41,34 @@ class theme_Core {
$config = Kohana_Config::instance();
$modules = $config->get("core.modules");
+
+ // Normally Router::find_uri() strips off the url suffix for us, but we're working off of the
+ // PATH_INFO here so we need to strip it off manually
+ if ($suffix = Kohana::config("core.url_suffix")) {
+ $path = preg_replace("#" . preg_quote($suffix) . "$#u", "", $path);
+ }
+
self::$is_admin = $path == "/admin" || !strncmp($path, "/admin/", 7);
self::$site_theme_name = module::get_var("gallery", "active_site_theme");
+
+ // If the site theme doesn't exist, fall back to wind.
+ if (!file_exists(THEMEPATH . self::$site_theme_name . "/theme.info")) {
+ site_status::error(t("Theme '%name' is missing. Falling back to the Wind theme.",
+ array("name" => self::$site_theme_name)), "missing_site_theme");
+ module::set_var("gallery", "active_site_theme", self::$site_theme_name = "wind");
+ }
+
if (self::$is_admin) {
// Load the admin theme
self::$admin_theme_name = module::get_var("gallery", "active_admin_theme");
+
+ // If the admin theme doesn't exist, fall back to admin_wind.
+ if (!file_exists(THEMEPATH . self::$admin_theme_name . "/theme.info")) {
+ site_status::error(t("Admin theme '%name' is missing! Falling back to the Wind theme.",
+ array("name" => self::$admin_theme_name)), "missing_admin_theme");
+ module::set_var("gallery", "active_admin_theme", self::$admin_theme_name = "admin_wind");
+ }
+
array_unshift($modules, THEMEPATH . self::$admin_theme_name);
// If the site theme has an admin subdir, load that as a module so that
diff --git a/modules/gallery/images/missing_photo.png b/modules/gallery/images/missing_photo.png
new file mode 100644
index 00000000..67786275
--- /dev/null
+++ b/modules/gallery/images/missing_photo.png
Binary files differ
diff --git a/modules/gallery/libraries/Breadcrumb.php b/modules/gallery/libraries/Breadcrumb.php
new file mode 100644
index 00000000..e151890d
--- /dev/null
+++ b/modules/gallery/libraries/Breadcrumb.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-2011 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 Breadcrumb_Core {
+ public $title;
+ public $url;
+ public $first;
+ public $last;
+
+ static function instance($title, $url) {
+ return new Breadcrumb($title, $url);
+ }
+
+ public function __construct($title, $url) {
+ $this->title = $title;
+ $this->url = $url;
+ $this->first = false;
+ $this->last = false;
+ }
+
+ /**
+ * Return an array of Breadcrumb instances build from the parents of a given item.
+ * The first and last Breadcrumb instances will be marked first/last as appropriate.
+ * Each breadcrumb will have a ?show= query parameter that refers to the id of the next
+ * item in line.
+ *
+ * @return array Breadcrumb instances
+ */
+ static function array_from_item_parents($item) {
+ if ($item->id == item::root()->id) {
+ return array();
+ }
+
+ $bc = array_merge($item->parents()->as_array(), array($item));
+ for ($i = 0; $i < count($bc) - 1; $i++) {
+ $bc[$i] = new Breadcrumb($bc[$i]->title, $bc[$i]->url("show={$bc[$i+1]->id}"));
+ }
+ $bc[$i] = new Breadcrumb($item->title, $item->url());
+
+ $bc[0]->set_first();
+ end($bc)->set_last();
+ return $bc;
+ }
+
+ public function set_first() {
+ $this->first = true;
+ return $this;
+ }
+
+ public function set_last() {
+ $this->last = true;
+ return $this;
+ }
+}
diff --git a/modules/gallery/libraries/Form_Uploadify.php b/modules/gallery/libraries/Form_Uploadify.php
index 27ab9684..450320b3 100644
--- a/modules/gallery/libraries/Form_Uploadify.php
+++ b/modules/gallery/libraries/Form_Uploadify.php
@@ -47,7 +47,22 @@ class Form_Uploadify_Core extends Form_Input {
$v->script_data = $this->data["script_data"];
$v->simultaneous_upload_limit = module::get_var("gallery", "simultaneous_upload_limit");
$v->movies_allowed = (bool) movie::find_ffmpeg();
+ $v->extensions = legal_file::get_filters();
$v->suhosin_session_encrypt = (bool) ini_get("suhosin.session.encrypt");
+
+ list ($toolkit_max_filesize_bytes, $toolkit_max_filesize) = graphics::max_filesize();
+
+ $upload_max_filesize = trim(ini_get("upload_max_filesize"));
+ $upload_max_filesize_bytes = num::convert_to_bytes($upload_max_filesize);
+
+ if ($upload_max_filesize_bytes < $toolkit_max_filesize_bytes) {
+ $v->size_limit_bytes = $upload_max_filesize_bytes;
+ $v->size_limit = $upload_max_filesize;
+ } else {
+ $v->size_limit_bytes = $toolkit_max_filesize_bytes;
+ $v->size_limit = $toolkit_max_filesize;
+ }
+
return $v;
}
diff --git a/modules/gallery/libraries/Gallery_View.php b/modules/gallery/libraries/Gallery_View.php
index 562f7929..e04b9169 100644
--- a/modules/gallery/libraries/Gallery_View.php
+++ b/modules/gallery/libraries/Gallery_View.php
@@ -31,6 +31,52 @@ class Gallery_View_Core extends View {
}
/**
+ * Set up the data and render a pager.
+ *
+ * See themes/wind/views/pager.html for documentation on the variables generated here.
+ */
+ public function paginator() {
+ $v = new View("paginator.html");
+ $v->page_type = $this->page_type;
+ $v->page_subtype = $this->page_subtype;
+ $v->first_page_url = null;
+ $v->previous_page_url = null;
+ $v->next_page_url = null;
+ $v->last_page_url = null;
+
+ if ($this->page_type == "collection") {
+ $v->page = $this->page;
+ $v->max_pages = $this->max_pages;
+ $v->total = $this->children_count;
+
+ if ($this->page != 1) {
+ $v->first_page_url = url::site(url::merge(array("page" => 1)));
+ $v->previous_page_url = url::site(url::merge(array("page" => $this->page - 1)));
+ }
+
+ if ($this->page != $this->max_pages) {
+ $v->next_page_url = url::site(url::merge(array("page" => $this->page + 1)));
+ $v->last_page_url = url::site(url::merge(array("page" => $this->max_pages)));
+ }
+
+ $v->first_visible_position = ($this->page - 1) * $this->page_size + 1;
+ $v->last_visible_position = min($this->page * $this->page_size, $v->total);
+ } else if ($this->page_type == "item") {
+ $v->position = $this->position;
+ $v->total = $this->sibling_count;
+ if ($this->previous_item) {
+ $v->previous_page_url = $this->previous_item->url();
+ }
+
+ if ($this->next_item) {
+ $v->next_page_url = $this->next_item->url();
+ }
+ }
+
+ return $v;
+ }
+
+ /**
* Begin gather up scripts or css files so that they can be combined into a single request.
*
* @param $types a comma separated list of types to combine, eg "script,css"
@@ -111,6 +157,8 @@ class Gallery_View_Core extends View {
$contents = $cache->get($key);
if (empty($contents)) {
+ module::event("before_combine", $type, $this->combine_queue[$type][$group]);
+
$contents = "";
foreach (array_keys($this->combine_queue[$type][$group]) as $path) {
if ($type == "css") {
@@ -120,6 +168,8 @@ class Gallery_View_Core extends View {
}
}
+ module::event("after_combine", $type, $contents);
+
$cache->set($key, $contents, array($type), 30 * 84600);
$use_gzip = function_exists("gzencode") &&
@@ -128,9 +178,13 @@ class Gallery_View_Core extends View {
$cache->set("{$key}_gz", gzencode($contents, 9, FORCE_GZIP),
array($type, "gzip"), 30 * 84600);
}
+
}
unset($this->combine_queue[$type][$group]);
+ if (empty($this->combine_queue[$type])) {
+ unset($this->combine_queue[$type]);
+ }
if ($type == "css") {
return html::stylesheet("combined/css/$key", "screen,print,projection", true);
@@ -158,6 +212,7 @@ class Gallery_View_Core extends View {
$replace[] = "url('" . url::abs_file($relative) . "')";
} else {
Kohana_Log::add("error", "Missing URL reference '{$match[1]}' in CSS file '$css_file'");
+
}
}
$replace = str_replace(DIRECTORY_SEPARATOR, "/", $replace);
diff --git a/modules/gallery/libraries/IdentityProvider.php b/modules/gallery/libraries/IdentityProvider.php
index 153446e6..3f995923 100644
--- a/modules/gallery/libraries/IdentityProvider.php
+++ b/modules/gallery/libraries/IdentityProvider.php
@@ -110,11 +110,11 @@ class IdentityProvider_Core {
Kohana_Log::add("error", "Error restoring original identity provider\n" .
$e2->getMessage() . "\n" . $e2->getTraceAsString());
}
-
+
message::error(
t("Error attempting to enable \"%new_provider\" identity provider, reverted to \"%old_provider\" identity provider",
array("new_provider" => $new_provider, "old_provider" => $current_provider)));
-
+
$restore_already_running = false;
}
throw $e;
@@ -260,14 +260,14 @@ class IdentityProvider_Core {
/**
* @see IdentityProvider_Driver::add_user_to_group.
*/
- public function add_user_to_group($user, $group_id) {
- return $this->driver->add_user_to_group($user, $group_id);
+ public function add_user_to_group($user, $group) {
+ return $this->driver->add_user_to_group($user, $group);
}
/**
* @see IdentityProvider_Driver::remove_user_to_group.
*/
- public function remove_user_from_group($user, $group_id) {
- return $this->driver->remove_user_from_group($user, $group_id);
+ public function remove_user_from_group($user, $group) {
+ return $this->driver->remove_user_from_group($user, $group);
}
} // End Identity
diff --git a/modules/gallery/libraries/InPlaceEdit.php b/modules/gallery/libraries/InPlaceEdit.php
index 88c30494..739cbb61 100644
--- a/modules/gallery/libraries/InPlaceEdit.php
+++ b/modules/gallery/libraries/InPlaceEdit.php
@@ -56,8 +56,12 @@ class InPlaceEdit_Core {
}
public function validate() {
- $post = Validation::factory($_POST)
- ->add_callbacks("input", $this->callback);
+ $post = Validation::factory($_POST);
+
+ if (!empty($this->callback)) {
+ $post->add_callbacks("input", $this->callback);
+ }
+
foreach ($this->rules as $rule) {
$post->add_rules("input", $rule);
}
diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php
index d0b35d3e..13e58004 100644
--- a/modules/gallery/libraries/Theme_View.php
+++ b/modules/gallery/libraries/Theme_View.php
@@ -38,6 +38,7 @@ class Theme_View_Core extends Gallery_View {
$this->item = null;
$this->tag = null;
$this->set_global(array("theme" => $this,
+ "theme_info" => theme::get_info($this->theme_name),
"user" => identity::active_user(),
"page_type" => $page_type,
"page_subtype" => $page_subtype,
@@ -142,52 +143,6 @@ class Theme_View_Core extends Gallery_View {
}
/**
- * Set up the data and render a pager.
- *
- * See themes/wind/views/pager.html for documentation on the variables generated here.
- */
- public function paginator() {
- $v = new View("paginator.html");
- $v->page_type = $this->page_type;
- $v->page_subtype = $this->page_subtype;
- $v->first_page_url = null;
- $v->previous_page_url = null;
- $v->next_page_url = null;
- $v->last_page_url = null;
-
- if ($this->page_type == "collection") {
- $v->page = $this->page;
- $v->max_pages = $this->max_pages;
- $v->total = $this->children_count;
-
- if ($this->page != 1) {
- $v->first_page_url = url::site(url::merge(array("page" => 1)));
- $v->previous_page_url = url::site(url::merge(array("page" => $this->page - 1)));
- }
-
- if ($this->page != $this->max_pages) {
- $v->next_page_url = url::site(url::merge(array("page" => $this->page + 1)));
- $v->last_page_url = url::site(url::merge(array("page" => $this->max_pages)));
- }
-
- $v->first_visible_position = ($this->page - 1) * $this->page_size + 1;
- $v->last_visible_position = min($this->page * $this->page_size, $v->total);
- } else if ($this->page_type == "item") {
- $v->position = $this->position;
- $v->total = $this->sibling_count;
- if ($this->previous_item) {
- $v->previous_page_url = $this->previous_item->url();
- }
-
- if ($this->next_item) {
- $v->next_page_url = $this->next_item->url();
- }
- }
-
- return $v;
- }
-
- /**
* Print out any site wide status information.
*/
public function site_status() {
diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php
index 8f4bc5e4..93e97af6 100644
--- a/modules/gallery/models/item.php
+++ b/modules/gallery/models/item.php
@@ -336,9 +336,7 @@ class Item_Model_Core extends ORM_MPTT {
// 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, "-");
+ $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME));
// If the filename is all invalid characters, then the slug may be empty here. Pick a
// random value.
@@ -410,6 +408,27 @@ class Item_Model_Core extends ORM_MPTT {
// If any significant fields have changed, load up a copy of the original item and
// keep it around.
$original = ORM::factory("item", $this->id);
+
+ // Preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume
+ // the MIME type from the extension. So when we adopt the new data file, it's important
+ // to adopt the new extension. That ensures that the item's extension is always
+ // appropriate for its data. We don't try to preserve the name of the data file, though,
+ // because the name is typically a temporary randomly-generated name.
+ if (isset($this->data_file)) {
+ $extension = pathinfo($this->data_file, PATHINFO_EXTENSION);
+ $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".$extension";
+ if (!empty($extension) && strcmp($this->name, $new_name)) {
+ $this->name = $new_name;
+ }
+ if ($this->is_photo()) {
+ list ($this->width, $this->height, $this->mime_type, $extension) =
+ photo::get_file_metadata($this->data_file);
+ } else if ($this->is_movie()) {
+ list ($this->width, $this->height, $this->mime_type, $extension) =
+ movie::get_file_metadata($this->data_file);
+ }
+ }
+
if (array_intersect($this->changed, array("parent_id", "name", "slug"))) {
$original->_build_relative_caches();
$this->relative_path_cache = null;
@@ -431,8 +450,19 @@ class Item_Model_Core extends ORM_MPTT {
}
if ($original->parent_id != $this->parent_id || $original->name != $this->name) {
+ $this->_build_relative_caches();
+ // If there is a data file, then we want to preserve both the old data and the new data.
+ // (Third-party event handlers would like access to both). The old data file will be
+ // accessible via the $original item, and the new one via $this item. But in that case,
+ // we don't want to rename the original as below, because the old data would end up being
+ // clobbered by the new data file. Also, the rename isn't necessary, because the new item
+ // data is coming from the data file anyway. So we only perform the rename if there isn't
+ // a data file. Another way to solve this would be to copy the original file rather than
+ // conditionally rename it, but a copy would cost far more than the rename.
+ if (!isset($this->data_file)) {
+ @rename($original->file_path(), $this->file_path());
+ }
// 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()));
@@ -462,8 +492,6 @@ class Item_Model_Core extends ORM_MPTT {
}
// Replace the data file, if requested.
- // @todo: we don't handle the case where you swap in a file of a different mime type
- // should we prevent that in validation? or in set_data_file()
if ($this->data_file && ($this->is_photo() || $this->is_movie())) {
copy($this->data_file, $this->file_path());
@@ -483,6 +511,9 @@ class Item_Model_Core extends ORM_MPTT {
// Null out the data file variable here, otherwise this event will trigger another
// save() which will think that we're doing another file move.
$this->data_file = null;
+ if ($original->file_path() != $this->file_path()) {
+ @unlink($original->file_path());
+ }
module::event("item_updated_data_file", $this);
}
}
@@ -519,6 +550,8 @@ class Item_Model_Core extends ORM_MPTT {
$this->name = "$base_name-$rand";
}
$this->slug = "$base_slug-$rand";
+ $this->relative_path_cache = null;
+ $this->relative_url_cache = null;
}
}
@@ -770,16 +803,7 @@ class Item_Model_Core extends ORM_MPTT {
}
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 {
+ if (!$this->loaded()) {
// New items must have an extension
$ext = pathinfo($this->name, PATHINFO_EXTENSION);
if (!$ext) {
@@ -787,9 +811,10 @@ class Item_Model_Core extends ORM_MPTT {
return;
}
- if ($this->is_movie() && !preg_match("/^(flv|mp4|m4v)$/i", $ext)) {
- $v->add_error("name", "illegal_data_file_extension");
- } else if ($this->is_photo() && !preg_match("/^(gif|jpg|jpeg|png)$/i", $ext)) {
+ if ($this->is_photo() &&
+ !in_array(strtolower($ext), array_map("strtolower", legal_file::get_photo_extensions())) ||
+ $this->is_movie() &&
+ !in_array(strtolower($ext), array_map("strtolower", legal_file::get_movie_extensions()))) {
$v->add_error("name", "illegal_data_file_extension");
}
}
@@ -815,17 +840,6 @@ class Item_Model_Core extends ORM_MPTT {
} else if (filesize($this->data_file) == 0) {
$v->add_error("name", "empty_data_file");
}
-
- if ($this->loaded()) {
- if ($this->is_photo()) {
- list ($a, $b, $mime_type) = photo::get_file_metadata($this->data_file);
- } else if ($this->is_movie()) {
- list ($a, $b, $mime_type) = movie::get_file_metadata($this->data_file);
- }
- if ($mime_type != $this->mime_type) {
- $v->add_error("name", "cant_change_mime_type");
- }
- }
}
/**
@@ -879,9 +893,9 @@ class Item_Model_Core extends ORM_MPTT {
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");
+ $legal_values = legal_file::get_movie_types();
+ } else if ($this->is_photo()) {
+ $legal_values = legal_file::get_photo_types();
}
break;
diff --git a/modules/gallery/module.info b/modules/gallery/module.info
index 4c0c8866..42345531 100644
--- a/modules/gallery/module.info
+++ b/modules/gallery/module.info
@@ -1,3 +1,7 @@
name = "Gallery 3"
description = "Gallery core application"
-version = 46
+version = 49
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:gallery"
+discuss_url = "http://gallery.menalto.com/forum_module_gallery"
diff --git a/modules/gallery/tests/Albums_Controller_Test.php b/modules/gallery/tests/Albums_Controller_Test.php
index d9983cc2..2ff017d7 100644
--- a/modules/gallery/tests/Albums_Controller_Test.php
+++ b/modules/gallery/tests/Albums_Controller_Test.php
@@ -31,7 +31,7 @@ class Albums_Controller_Test extends Gallery_Unit_Test_Case {
$album = test::random_album();
// Randomize to avoid conflicts.
- $new_name = "new_name_" . random::string(6);
+ $new_name = "new_name_" . test::random_string(6);
$_POST["name"] = $new_name;
$_POST["title"] = "new title";
diff --git a/modules/gallery/tests/Breadcrumb_Test.php b/modules/gallery/tests/Breadcrumb_Test.php
new file mode 100644
index 00000000..ed2e5608
--- /dev/null
+++ b/modules/gallery/tests/Breadcrumb_Test.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-2011 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 Breadcrumb_Test extends Gallery_Unit_Test_Case {
+ private $album;
+ private $item;
+
+ public function build_breadcrumbs_for_item_test() {
+ $album = test::random_album();
+ $item = test::random_photo($album);
+
+ $expected = array();
+ $expected[] = Breadcrumb::instance(
+ item::root()->title, item::root()->url("show={$album->id}"))->set_first();
+ $expected[] =
+ Breadcrumb::instance($album->title, $album->url("show={$item->id}"));
+ $expected[] = Breadcrumb::instance($item->title, $item->url())->set_last();
+ $this->assert_equal($expected, Breadcrumb::array_from_item_parents($item));
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/tests/File_Structure_Test.php b/modules/gallery/tests/File_Structure_Test.php
index 69c4bbf9..1d1ff5ce 100644
--- a/modules/gallery/tests/File_Structure_Test.php
+++ b/modules/gallery/tests/File_Structure_Test.php
@@ -101,6 +101,7 @@ class File_Structure_Test extends Gallery_Unit_Test_Case {
$expected_4 = array("<?php defined('SYSPATH') or die('No direct script access.');\n");
} else if (strpos($path, MODPATH . "forge") === 0 ||
strpos($path, MODPATH . "exif/lib") === 0 ||
+ strpos($path, MODPATH . "gallery_unit_test/vendor") === 0 ||
strpos($path, MODPATH . "gallery/lib/HTMLPurifier") === 0 ||
$path == MODPATH . "user/lib/PasswordHash.php" ||
$path == DOCROOT . "var/database.php") {
diff --git a/modules/gallery/tests/Gallery_Filters.php b/modules/gallery/tests/Gallery_Filters.php
index e0208ed3..b008b593 100644
--- a/modules/gallery/tests/Gallery_Filters.php
+++ b/modules/gallery/tests/Gallery_Filters.php
@@ -38,6 +38,7 @@ class GalleryCodeFilterIterator extends FilterIterator {
strpos($path_name, MODPATH . "forge") !== false ||
strpos($path_name, MODPATH . "gallery/views/kohana_error_page.php") !== false ||
strpos($path_name, MODPATH . "gallery/views/kohana_profiler.php") !== false ||
+ strpos($path_name, MODPATH . "gallery_unit_test/vendor") !== false ||
strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_error_page.php") !== false ||
strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_unit_test_cli.php") !== false ||
strpos($path_name, MODPATH . "unit_test") !== false ||
diff --git a/modules/gallery/tests/Item_Helper_Test.php b/modules/gallery/tests/Item_Helper_Test.php
index 4d5aed41..2fde7cc0 100644
--- a/modules/gallery/tests/Item_Helper_Test.php
+++ b/modules/gallery/tests/Item_Helper_Test.php
@@ -49,6 +49,10 @@ class Item_Helper_Test extends Gallery_Unit_Test_Case {
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]}"));
+ $this->assert_equal("english-text", item::convert_filename_to_slug("english text"));
+ $this->assert_equal("new-line", item::convert_filename_to_slug("new \n line"));
+ $this->assert_equal("foo-and-bar", item::convert_filename_to_slug("foo&bar"));
+ $this->assert_equal("special", item::convert_filename_to_slug("šṗëçîąļ"));
}
public function move_test() {
diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php
index 968d7510..19ab8ec4 100644
--- a/modules/gallery/tests/Item_Model_Test.php
+++ b/modules/gallery/tests/Item_Model_Test.php
@@ -394,15 +394,34 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {
$this->assert_equal(20337, filesize($photo->file_path()));
}
- public function replacement_data_file_must_be_same_mime_type_test() {
+ public function replace_data_file_type_test() {
// Random photo is modules/gallery/tests/test.jpg
$photo = test::random_photo();
+ $this->assert_equal(1024, $photo->width);
+ $this->assert_equal(768, $photo->height);
+ $this->assert_equal(6232, filesize($photo->file_path()));
+ $this->assert_equal("image/jpeg", $photo->mime_type);
+ $orig_name = $photo->name;
+
+ // Random photo is gallery/images/graphicsmagick.png is 104x76 and 1486 bytes
$photo->set_data_file(MODPATH . "gallery/images/graphicsmagick.png");
+ $photo->save();
+
+ $this->assert_equal(104, $photo->width);
+ $this->assert_equal(76, $photo->height);
+ $this->assert_equal(1486, filesize($photo->file_path()));
+ $this->assert_equal("image/png", $photo->mime_type);
+ $this->assert_equal("png", pathinfo($photo->name, PATHINFO_EXTENSION));
+ $this->assert_equal(pathinfo($orig_name, PATHINFO_FILENAME), pathinfo($photo->name, PATHINFO_FILENAME));
+ }
+ public function unsafe_data_file_replacement_test() {
try {
+ $photo = test::random_photo();
+ $photo->set_data_file(MODPATH . "gallery/tests/Item_Model_Test.php");
$photo->save();
} catch (ORM_Validation_Exception $e) {
- $this->assert_same(array("name" => "cant_change_mime_type"), $e->validation->errors());
+ $this->assert_same(array("mime_type" => "invalid"), $e->validation->errors());
return; // pass
}
$this->assert_true(false, "Shouldn't get here");
diff --git a/modules/gallery/tests/Num_Helper_Test.php b/modules/gallery/tests/Num_Helper_Test.php
new file mode 100644
index 00000000..a22f9359
--- /dev/null
+++ b/modules/gallery/tests/Num_Helper_Test.php
@@ -0,0 +1,32 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2011 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 Num_Helper_Test extends Gallery_Unit_Test_Case {
+ public function convert_to_bytes_test() {
+ $this->assert_equal(5 * 1024, num::convert_to_bytes("5K"));
+ $this->assert_equal(3 * 1024*1024, num::convert_to_bytes("3M"));
+ $this->assert_equal(4 * 1024*1024*1024, num::convert_to_bytes("4G"));
+ }
+
+ public function convert_to_human_readable_test() {
+ $this->assert_equal("6K", num::convert_to_human_readable(5615));
+ $this->assert_equal("1M", num::convert_to_human_readable(1205615));
+ $this->assert_equal("3G", num::convert_to_human_readable(3091205615));
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_Pagination.php b/modules/gallery/tests/System_Helper_Test.php
index e697c0bd..b6c00f4c 100644
--- a/modules/gallery/libraries/MY_Pagination.php
+++ b/modules/gallery/tests/System_Helper_Test.php
@@ -17,19 +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 Pagination extends Pagination_Core {
- public function render($style=NULL) {
- // Hide single page pagination
- if ($this->auto_hide === TRUE AND $this->total_pages <= 1) {
- return "";
- }
-
- if ($style === NULL) {
- // Use default style
- $style = $this->style;
- }
-
- // Return rendered pagination view
- return View::factory("pager.html", get_object_vars($this))->render();
+class System_Helper_Test extends Gallery_Unit_Test_Case {
+ public function temp_filename_test() {
+ $filename = system::temp_filename("file", "ext");
+ $this->assert_true(file_exists($filename), "File not created");
+ unlink($filename);
+ $this->assert_pattern($filename, "|/file.*\\.ext$|");
}
}
diff --git a/modules/gallery/tests/controller_auth_data.txt b/modules/gallery/tests/controller_auth_data.txt
index f1192071..e35708c0 100644
--- a/modules/gallery/tests/controller_auth_data.txt
+++ b/modules/gallery/tests/controller_auth_data.txt
@@ -1,6 +1,7 @@
modules/comment/controllers/admin_manage_comments.php queue DIRTY_CSRF
modules/comment/helpers/comment_rss.php feed DIRTY_AUTH
modules/digibug/controllers/digibug.php print_proxy DIRTY_CSRF|DIRTY_AUTH
+modules/g2_import/controllers/admin_g2_import.php autocomplete DIRTY_CSRF
modules/g2_import/controllers/g2.php map DIRTY_CSRF
modules/gallery/controllers/admin.php __call DIRTY_AUTH
modules/gallery/controllers/albums.php index DIRTY_AUTH
diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt
index 0c812fb4..df087669 100644
--- a/modules/gallery/tests/xss_data.txt
+++ b/modules/gallery/tests/xss_data.txt
@@ -5,21 +5,22 @@ modules/comment/views/admin_block_recent_comments.html.php 4 DIRTY_ATTR text
modules/comment/views/admin_block_recent_comments.html.php 5 DIRTY_ATTR $comment->author()->avatar_url(32,$theme->url(,true))
modules/comment/views/admin_block_recent_comments.html.php 10 DIRTY gallery::date_time($comment->created)
modules/comment/views/admin_comments.html.php 5 DIRTY $form
-modules/comment/views/admin_manage_comments.html.php 43 DIRTY $menu->render()
-modules/comment/views/admin_manage_comments.html.php 107 DIRTY_ATTR $comment->id
-modules/comment/views/admin_manage_comments.html.php 107 DIRTY_ATTR text::alternate("g-odd","g-even")
-modules/comment/views/admin_manage_comments.html.php 110 DIRTY_ATTR $comment->author()->avatar_url(40,$theme->url(,true))
-modules/comment/views/admin_manage_comments.html.php 123 DIRTY_JS $item->url()
-modules/comment/views/admin_manage_comments.html.php 125 DIRTY_ATTR $item->thumb_url()
-modules/comment/views/admin_manage_comments.html.php 127 DIRTY photo::img_dimensions($item->thumb_width,$item->thumb_height,75)
-modules/comment/views/admin_manage_comments.html.php 135 DIRTY gallery::date($comment->created)
-modules/comment/views/admin_manage_comments.html.php 142 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 151 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 160 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 169 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 176 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 184 DIRTY_JS $comment->id
-modules/comment/views/admin_manage_comments.html.php 197 DIRTY $pager
+modules/comment/views/admin_manage_comments.html.php 45 DIRTY $menu->render()
+modules/comment/views/admin_manage_comments_queue.html.php 40 DIRTY $theme->paginator()
+modules/comment/views/admin_manage_comments_queue.html.php 55 DIRTY_ATTR $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 55 DIRTY_ATTR text::alternate("g-odd","g-even")
+modules/comment/views/admin_manage_comments_queue.html.php 58 DIRTY_ATTR $comment->author()->avatar_url(40,$fallback_avatar_url)
+modules/comment/views/admin_manage_comments_queue.html.php 75 DIRTY_JS $item->url()
+modules/comment/views/admin_manage_comments_queue.html.php 77 DIRTY_ATTR $item->thumb_url()
+modules/comment/views/admin_manage_comments_queue.html.php 79 DIRTY photo::img_dimensions($item->thumb_width,$item->thumb_height,75)
+modules/comment/views/admin_manage_comments_queue.html.php 87 DIRTY gallery::date($comment->created)
+modules/comment/views/admin_manage_comments_queue.html.php 94 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 103 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 116 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 125 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 132 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 141 DIRTY_JS $comment->id
+modules/comment/views/admin_manage_comments_queue.html.php 155 DIRTY $theme->paginator()
modules/comment/views/comment.html.php 2 DIRTY_ATTR $comment->id;
modules/comment/views/comment.html.php 5 DIRTY_ATTR $comment->author()->avatar_url(40,$theme->url(,true))
modules/comment/views/comment.mrss.php 10 DIRTY $feed->uri
@@ -42,7 +43,8 @@ modules/digibug/views/digibug_form.html.php 4 DIRTY form::
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 9 DIRTY $form
+modules/g2_import/views/admin_g2_import.html.php 7 DIRTY_JS url::site("__ARGS__")
+modules/g2_import/views/admin_g2_import.html.php 52 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)
@@ -77,30 +79,35 @@ modules/gallery/views/admin_languages.html.php 62 DIRTY form::
modules/gallery/views/admin_languages.html.php 63 DIRTY $display_name
modules/gallery/views/admin_languages.html.php 65 DIRTY form::radio("default_locale",$code,($default_locale==$code),((isset($installed_locales[$code]))?'':'disabled="disabled"'))
modules/gallery/views/admin_languages.html.php 113 DIRTY $share_translations_form
-modules/gallery/views/admin_maintenance.html.php 40 DIRTY_ATTR text::alternate("g-odd","g-even")
-modules/gallery/views/admin_maintenance.html.php 40 DIRTY_ATTR log::severity_class($task->severity)
-modules/gallery/views/admin_maintenance.html.php 41 DIRTY_ATTR log::severity_class($task->severity)
-modules/gallery/views/admin_maintenance.html.php 42 DIRTY $task->name
-modules/gallery/views/admin_maintenance.html.php 45 DIRTY $task->description
-modules/gallery/views/admin_maintenance.html.php 86 DIRTY_ATTR text::alternate("g-odd","g-even")
-modules/gallery/views/admin_maintenance.html.php 86 DIRTY_ATTR $task->state=="stalled"?"g-warning":""
-modules/gallery/views/admin_maintenance.html.php 87 DIRTY_ATTR $task->state=="stalled"?"g-warning":""
-modules/gallery/views/admin_maintenance.html.php 88 DIRTY gallery::date_time($task->updated)
-modules/gallery/views/admin_maintenance.html.php 91 DIRTY $task->name
-modules/gallery/views/admin_maintenance.html.php 106 DIRTY $task->status
-modules/gallery/views/admin_maintenance.html.php 162 DIRTY_ATTR text::alternate("g-odd","g-even")
-modules/gallery/views/admin_maintenance.html.php 162 DIRTY_ATTR $task->state=="success"?"g-success":"g-error"
-modules/gallery/views/admin_maintenance.html.php 163 DIRTY_ATTR $task->state=="success"?"g-success":"g-error"
-modules/gallery/views/admin_maintenance.html.php 164 DIRTY gallery::date_time($task->updated)
-modules/gallery/views/admin_maintenance.html.php 167 DIRTY $task->name
-modules/gallery/views/admin_maintenance.html.php 179 DIRTY $task->status
+modules/gallery/views/admin_maintenance.html.php 42 DIRTY_ATTR text::alternate("g-odd","g-even")
+modules/gallery/views/admin_maintenance.html.php 42 DIRTY_ATTR log::severity_class($task->severity)
+modules/gallery/views/admin_maintenance.html.php 43 DIRTY_ATTR log::severity_class($task->severity)
+modules/gallery/views/admin_maintenance.html.php 44 DIRTY $task->name
+modules/gallery/views/admin_maintenance.html.php 47 DIRTY $task->description
+modules/gallery/views/admin_maintenance.html.php 88 DIRTY_ATTR text::alternate("g-odd","g-even")
+modules/gallery/views/admin_maintenance.html.php 88 DIRTY_ATTR $task->state=="stalled"?"g-warning":""
+modules/gallery/views/admin_maintenance.html.php 89 DIRTY_ATTR $task->state=="stalled"?"g-warning":""
+modules/gallery/views/admin_maintenance.html.php 90 DIRTY gallery::date_time($task->updated)
+modules/gallery/views/admin_maintenance.html.php 93 DIRTY $task->name
+modules/gallery/views/admin_maintenance.html.php 108 DIRTY $task->status
+modules/gallery/views/admin_maintenance.html.php 164 DIRTY_ATTR text::alternate("g-odd","g-even")
+modules/gallery/views/admin_maintenance.html.php 164 DIRTY_ATTR $task->state=="success"?"g-success":"g-error"
+modules/gallery/views/admin_maintenance.html.php 165 DIRTY_ATTR $task->state=="success"?"g-success":"g-error"
+modules/gallery/views/admin_maintenance.html.php 166 DIRTY gallery::date_time($task->updated)
+modules/gallery/views/admin_maintenance.html.php 169 DIRTY $task->name
+modules/gallery/views/admin_maintenance.html.php 181 DIRTY $task->status
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 75 DIRTY $task->name
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.html.php 61 DIRTY_ATTR text::alternate("g-odd","g-even")
+modules/gallery/views/admin_modules.html.php 64 DIRTY form::checkbox($data,'1',module::is_active($module_name))
+modules/gallery/views/admin_modules.html.php 66 DIRTY $module_info->version
+modules/gallery/views/admin_modules.html.php 74 DIRTY_JS $module_info->author_url
+modules/gallery/views/admin_modules.html.php 81 DIRTY_ATTR $module_info->author_name
+modules/gallery/views/admin_modules.html.php 85 DIRTY $module_info->author_name
+modules/gallery/views/admin_modules.html.php 93 DIRTY_JS $module_info->info_url
+modules/gallery/views/admin_modules.html.php 106 DIRTY_JS $module_info->discuss_url
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()
@@ -114,12 +121,17 @@ modules/gallery/views/admin_themes.html.php 3 DIRTY_JS url::s
modules/gallery/views/admin_themes.html.php 5 DIRTY_JS $csrf
modules/gallery/views/admin_themes.html.php 22 DIRTY $themes[$site]->name
modules/gallery/views/admin_themes.html.php 24 DIRTY $themes[$site]->description
-modules/gallery/views/admin_themes.html.php 38 DIRTY $info->name
-modules/gallery/views/admin_themes.html.php 40 DIRTY $info->description
-modules/gallery/views/admin_themes.html.php 60 DIRTY $themes[$admin]->name
-modules/gallery/views/admin_themes.html.php 62 DIRTY $themes[$admin]->description
-modules/gallery/views/admin_themes.html.php 76 DIRTY $info->name
-modules/gallery/views/admin_themes.html.php 78 DIRTY $info->description
+modules/gallery/views/admin_themes.html.php 39 DIRTY $info->name
+modules/gallery/views/admin_themes.html.php 41 DIRTY $info->description
+modules/gallery/views/admin_themes.html.php 62 DIRTY $themes[$admin]->name
+modules/gallery/views/admin_themes.html.php 64 DIRTY $themes[$admin]->description
+modules/gallery/views/admin_themes.html.php 79 DIRTY $info->name
+modules/gallery/views/admin_themes.html.php 81 DIRTY $info->description
+modules/gallery/views/admin_themes_buttonset.html.php 7 DIRTY_JS $info['author_url']
+modules/gallery/views/admin_themes_buttonset.html.php 14 DIRTY_ATTR $info['author_name']
+modules/gallery/views/admin_themes_buttonset.html.php 18 DIRTY $info['author_name']
+modules/gallery/views/admin_themes_buttonset.html.php 26 DIRTY_JS $info['info_url']
+modules/gallery/views/admin_themes_buttonset.html.php 39 DIRTY_JS $info['discuss_url']
modules/gallery/views/admin_themes_preview.html.php 8 DIRTY_ATTR $url
modules/gallery/views/error_404.html.php 14 DIRTY $login_form
modules/gallery/views/error_admin.html.php 178 DIRTY @gallery_block::get("platform_info")
@@ -172,9 +184,11 @@ modules/gallery/views/form_uploadify.html.php 16 DIRTY_JS url::s
modules/gallery/views/form_uploadify.html.php 24 DIRTY_JS $flash_minimum_version
modules/gallery/views/form_uploadify.html.php 28 DIRTY_JS url::file("lib/uploadify/uploadify.swf")
modules/gallery/views/form_uploadify.html.php 29 DIRTY_JS url::site("uploader/add_photo/{$album->id}")
+modules/gallery/views/form_uploadify.html.php 31 DIRTY_JS implode(";",$extensions)
modules/gallery/views/form_uploadify.html.php 33 DIRTY_JS url::file("lib/uploadify/cancel.png")
modules/gallery/views/form_uploadify.html.php 34 DIRTY_JS $simultaneous_upload_limit
-modules/gallery/views/form_uploadify.html.php 160 DIRTY_ATTR request::protocol()
+modules/gallery/views/form_uploadify.html.php 35 DIRTY_JS $size_limit_bytes
+modules/gallery/views/form_uploadify.html.php 162 DIRTY_ATTR request::protocol()
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\"")
@@ -248,7 +262,7 @@ modules/gallery/views/permissions_form.html.php 80 DIRTY_JS $permi
modules/gallery/views/permissions_form.html.php 80 DIRTY_JS $item->id
modules/gallery/views/quick_delete_confirm.html.php 11 DIRTY $form
modules/gallery/views/reauthenticate.html.php 9 DIRTY $form
-modules/gallery/views/upgrade_checker_block.html.php 17 DIRTY $new_version
+modules/gallery/views/upgrade_checker_block.html.php 19 DIRTY $new_version
modules/gallery/views/upgrader.html.php 76 DIRTY_ATTR $done?"muted":""
modules/gallery/views/upgrader.html.php 94 DIRTY_ATTR $done?"muted":""
modules/gallery/views/upgrader.html.php 102 DIRTY_ATTR $module->version==$module->code_version?"current":"upgradeable"
@@ -330,8 +344,9 @@ modules/search/views/search.html.php 27 DIRTY_ATTR $ite
modules/search/views/search.html.php 28 DIRTY_JS $item->url()
modules/search/views/search.html.php 29 DIRTY $item->thumb_img()
modules/search/views/search.html.php 40 DIRTY $theme->paginator()
-modules/server_add/views/admin_server_add.html.php 5 DIRTY $form
-modules/server_add/views/admin_server_add.html.php 15 DIRTY_ATTR $id
+modules/server_add/views/admin_server_add.html.php 8 DIRTY_JS url::site("__ARGS__")
+modules/server_add/views/admin_server_add.html.php 19 DIRTY $form
+modules/server_add/views/admin_server_add.html.php 30 DIRTY_ATTR $id
modules/server_add/views/server_add_tree.html.php 20 DIRTY_ATTR is_dir($file)?"ui-icon-folder-collapsed":"ui-icon-document"
modules/server_add/views/server_add_tree.html.php 21 DIRTY_ATTR is_dir($file)?"g-directory":"g-file"
modules/server_add/views/server_add_tree_dialog.html.php 3 DIRTY_JS url::site("server_add/children?path=__PATH__")
@@ -354,7 +369,7 @@ modules/user/views/admin_users.html.php 73 DIRTY_ATTR $use
modules/user/views/admin_users.html.php 74 DIRTY_ATTR $user->avatar_url(20,$theme->url(,true))
modules/user/views/admin_users.html.php 88 DIRTY ($user->last_login==0)?"":gallery::date($user->last_login)
modules/user/views/admin_users.html.php 91 DIRTY db::build()->from("items")->where("owner_id","=",$user->id)->count_records()
-modules/user/views/admin_users.html.php 113 DIRTY $pager
+modules/user/views/admin_users.html.php 113 DIRTY $theme->paginator()
modules/user/views/admin_users.html.php 132 DIRTY_ATTR $group->id
modules/user/views/admin_users.html.php 132 DIRTY_ATTR ($group->special?"g-default-group":"")
modules/user/views/admin_users.html.php 134 DIRTY $v
@@ -365,31 +380,30 @@ modules/watermark/views/admin_watermarks.html.php 20 DIRTY_ATTR $wid
modules/watermark/views/admin_watermarks.html.php 20 DIRTY_ATTR $height
modules/watermark/views/admin_watermarks.html.php 20 DIRTY_ATTR $url
themes/admin_wind/views/admin.html.php 4 DIRTY $theme->html_attributes()
-themes/admin_wind/views/admin.html.php 31 DIRTY $theme->admin_head()
-themes/admin_wind/views/admin.html.php 40 DIRTY_JS $theme->url()
-themes/admin_wind/views/admin.html.php 45 DIRTY $theme->get_combined("script")
-themes/admin_wind/views/admin.html.php 48 DIRTY $theme->get_combined("css")
-themes/admin_wind/views/admin.html.php 52 DIRTY $theme->admin_page_top()
-themes/admin_wind/views/admin.html.php 60 DIRTY $theme->admin_header_top()
-themes/admin_wind/views/admin.html.php 61 DIRTY_JS item::root()->url()
-themes/admin_wind/views/admin.html.php 64 DIRTY $theme->user_menu()
-themes/admin_wind/views/admin.html.php 67 DIRTY $theme->admin_menu()
-themes/admin_wind/views/admin.html.php 70 DIRTY $theme->admin_header_bottom()
-themes/admin_wind/views/admin.html.php 77 DIRTY $content
-themes/admin_wind/views/admin.html.php 83 DIRTY $sidebar
-themes/admin_wind/views/admin.html.php 88 DIRTY $theme->admin_footer()
-themes/admin_wind/views/admin.html.php 91 DIRTY $theme->admin_credits()
-themes/admin_wind/views/admin.html.php 96 DIRTY $theme->admin_page_bottom()
+themes/admin_wind/views/admin.html.php 34 DIRTY $theme->admin_head()
+themes/admin_wind/views/admin.html.php 46 DIRTY_JS $theme->url()
+themes/admin_wind/views/admin.html.php 51 DIRTY $theme->get_combined("css")
+themes/admin_wind/views/admin.html.php 54 DIRTY $theme->get_combined("script")
+themes/admin_wind/views/admin.html.php 58 DIRTY $theme->admin_page_top()
+themes/admin_wind/views/admin.html.php 66 DIRTY $theme->admin_header_top()
+themes/admin_wind/views/admin.html.php 67 DIRTY_JS item::root()->url()
+themes/admin_wind/views/admin.html.php 70 DIRTY $theme->user_menu()
+themes/admin_wind/views/admin.html.php 73 DIRTY $theme->admin_menu()
+themes/admin_wind/views/admin.html.php 76 DIRTY $theme->admin_header_bottom()
+themes/admin_wind/views/admin.html.php 83 DIRTY $content
+themes/admin_wind/views/admin.html.php 89 DIRTY $sidebar
+themes/admin_wind/views/admin.html.php 94 DIRTY $theme->admin_footer()
+themes/admin_wind/views/admin.html.php 97 DIRTY $theme->admin_credits()
+themes/admin_wind/views/admin.html.php 102 DIRTY $theme->admin_page_bottom()
themes/admin_wind/views/block.html.php 3 DIRTY_ATTR $anchor
themes/admin_wind/views/block.html.php 5 DIRTY $id
themes/admin_wind/views/block.html.php 5 DIRTY_ATTR $css_id
themes/admin_wind/views/block.html.php 13 DIRTY $title
themes/admin_wind/views/block.html.php 16 DIRTY $content
-themes/admin_wind/views/pager.html.php 13 DIRTY_JS str_replace('{page}',1,$url)
-themes/admin_wind/views/pager.html.php 20 DIRTY_JS str_replace('{page}',$previous_page,$url)
-themes/admin_wind/views/pager.html.php 27 DIRTY $from_to_msg
-themes/admin_wind/views/pager.html.php 30 DIRTY_JS str_replace('{page}',$next_page,$url)
-themes/admin_wind/views/pager.html.php 37 DIRTY_JS str_replace('{page}',$last_page,$url)
+themes/admin_wind/views/paginator.html.php 35 DIRTY_JS $first_page_url
+themes/admin_wind/views/paginator.html.php 44 DIRTY_JS $previous_page_url
+themes/admin_wind/views/paginator.html.php 70 DIRTY_JS $next_page_url
+themes/admin_wind/views/paginator.html.php 79 DIRTY_JS $last_page_url
themes/wind/views/album.html.php 16 DIRTY_ATTR $child->id
themes/wind/views/album.html.php 16 DIRTY_ATTR $item_class
themes/wind/views/album.html.php 18 DIRTY_JS $child->url()
@@ -414,19 +428,19 @@ themes/wind/views/page.html.php 4 DIRTY $theme
themes/wind/views/page.html.php 10 DIRTY $page_title
themes/wind/views/page.html.php 13 DIRTY $theme->item()->title
themes/wind/views/page.html.php 17 DIRTY item::root()->title
-themes/wind/views/page.html.php 31 DIRTY $new_width
-themes/wind/views/page.html.php 32 DIRTY $new_height
-themes/wind/views/page.html.php 33 DIRTY $thumb_proportion
-themes/wind/views/page.html.php 70 DIRTY_JS $theme->url()
-themes/wind/views/page.html.php 75 DIRTY $theme->get_combined("script")
-themes/wind/views/page.html.php 78 DIRTY $theme->get_combined("css")
-themes/wind/views/page.html.php 88 DIRTY $header_text
-themes/wind/views/page.html.php 90 DIRTY_JS item::root()->url()
-themes/wind/views/page.html.php 94 DIRTY $theme->user_menu()
-themes/wind/views/page.html.php 115 DIRTY_JS $parent->url($parent->id==$theme->item()->parent_id?"show={$theme->item()->id}":null)
-themes/wind/views/page.html.php 136 DIRTY $content
-themes/wind/views/page.html.php 142 DIRTY newView("sidebar.html")
-themes/wind/views/page.html.php 149 DIRTY $footer_text
+themes/wind/views/page.html.php 32 DIRTY $new_width
+themes/wind/views/page.html.php 33 DIRTY $new_height
+themes/wind/views/page.html.php 34 DIRTY $thumb_proportion
+themes/wind/views/page.html.php 74 DIRTY_JS $theme->url()
+themes/wind/views/page.html.php 79 DIRTY $theme->get_combined("css")
+themes/wind/views/page.html.php 82 DIRTY $theme->get_combined("script")
+themes/wind/views/page.html.php 92 DIRTY $header_text
+themes/wind/views/page.html.php 94 DIRTY_JS item::root()->url()
+themes/wind/views/page.html.php 98 DIRTY $theme->user_menu()
+themes/wind/views/page.html.php 119 DIRTY_JS $parent->url($parent->id==$theme->item()->parent_id?"show={$theme->item()->id}":null)
+themes/wind/views/page.html.php 140 DIRTY $content
+themes/wind/views/page.html.php 146 DIRTY newView("sidebar.html")
+themes/wind/views/page.html.php 153 DIRTY $footer_text
themes/wind/views/paginator.html.php 33 DIRTY_JS $first_page_url
themes/wind/views/paginator.html.php 42 DIRTY_JS $previous_page_url
themes/wind/views/paginator.html.php 70 DIRTY_JS $next_page_url
diff --git a/modules/gallery/views/admin_block_welcome.html.php b/modules/gallery/views/admin_block_welcome.html.php
index d8c96187..d3765d19 100644
--- a/modules/gallery/views/admin_block_welcome.html.php
+++ b/modules/gallery/views/admin_block_welcome.html.php
@@ -2,7 +2,7 @@
<p>
<?= t("This is your administration dashboard and it provides a quick overview of status messages, recent updates, and frequently used options. Add or remove blocks and rearrange them to tailor to your needs. The admin menu provides quick access to all of Gallery 3's options and settings. Here are a few of the most used options to get you started.") ?>
</p>
-<ul>
+<ul class="g-text">
<li>
<?= t("General Settings - choose your <a href=\"%graphics_url\">graphics</a> and <a href=\"%language_url\">language</a> settings.",
array("graphics_url" => html::mark_clean(url::site("admin/graphics")),
diff --git a/modules/gallery/views/admin_languages.html.php b/modules/gallery/views/admin_languages.html.php
index 01d1ce3f..eef087e1 100644
--- a/modules/gallery/views/admin_languages.html.php
+++ b/modules/gallery/views/admin_languages.html.php
@@ -51,7 +51,7 @@
<? foreach ($available_locales as $code => $display_name): ?>
<? if ($i == (int) (count($available_locales)/2)): ?>
</table>
- <table>
+ <table class="g-left">
<tr>
<th> <?= t("Installed") ?> </th>
<th> <?= t("Language") ?> </th>
diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php
index c28def1d..230e9353 100644
--- a/modules/gallery/views/admin_maintenance.html.php
+++ b/modules/gallery/views/admin_maintenance.html.php
@@ -3,7 +3,9 @@
<h1> <?= t("Maintenance") ?> </h1>
<div class="g-block-content">
<div id="g-maintenance-mode">
+ <p>
<?= t("When you're performing maintenance on your Gallery, you can enable <b>maintenance mode</b> which prevents any non-admin from accessing your Gallery. Some of the tasks below will automatically put your Gallery in maintenance mode for you.") ?>
+ </p>
<ul id="g-action-status" class="g-message-block">
<? if (module::get_var("gallery", "maintenance_mode")): ?>
<li class="g-warning">
diff --git a/modules/gallery/views/admin_modules.html.php b/modules/gallery/views/admin_modules.html.php
index f4ae965c..03993bb2 100644
--- a/modules/gallery/views/admin_modules.html.php
+++ b/modules/gallery/views/admin_modules.html.php
@@ -6,7 +6,7 @@
dataType: "json",
success: function(data) {
if (data.reload) {
- window.location.reload();
+ window.location = "<? url::site("/admin/modules") ?>";
} else {
$("body").append('<div id="g-dialog">' + data.dialog + '</div>');
$("#g-dialog").dialog({
@@ -43,7 +43,7 @@
</script>
<h1> <?= t("Gallery Modules") ?> </h1>
<p>
- <?= t("Power up your Gallery by adding more modules! Each module provides new cool features.") ?>
+ <?= t("Power up your Gallery by <a href=\"%url\">adding more modules</a>! Each module provides new cool features.", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Modules")) ?>
</p>
<div class="g-block-content">
@@ -55,6 +55,7 @@
<th style="width: 8em"> <?= t("Name") ?> </th>
<th> <?= t("Version") ?> </th>
<th> <?= t("Description") ?> </th>
+ <th style="width: 60px"> <?= t("Details") ?> </th>
</tr>
<? foreach ($available as $module_name => $module_info): ?>
<tr class="<?= text::alternate("g-odd", "g-even") ?>">
@@ -64,6 +65,55 @@
<td> <?= t($module_info->name) ?> </td>
<td> <?= $module_info->version ?> </td>
<td> <?= t($module_info->description) ?> </td>
+ <td style="white-space: nowrap">
+ <ul class="g-buttonset">
+ <li>
+ <a target="_blank"
+ <? if (isset($module_info->author_url)): ?>
+ class="ui-state-default ui-icon ui-icon-person ui-corner-left"
+ href="<?= $module_info->author_url ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-person ui-corner-left"
+ href="#"
+ <? endif ?>
+
+ <? if (isset($module_info->author_name)): ?>
+ title="<?= $module_info->author_name ?>"
+ <? endif ?>
+ >
+ <? if (isset($module_info->author_name)): ?>
+ <?= $module_info->author_name ?>
+ <? endif ?>
+ </a>
+ </li>
+ <li>
+ <a target="_blank"
+ <? if (isset($module_info->info_url)): ?>
+ class="ui-state-default ui-icon ui-icon-info"
+ href="<?= $module_info->info_url ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-info"
+ href="#"
+ <? endif ?>
+ >
+ <?= t("info") ?>
+ </a>
+ </li>
+ <li>
+ <a target="_blank"
+ <? if (isset($module_info->discuss_url)): ?>
+ class="ui-state-default ui-icon ui-icon-comment ui-corner-right"
+ href="<?= $module_info->discuss_url ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-comment ui-corner-right"
+ href="#"
+ <? endif ?>
+ >
+ <?= t("discuss") ?>
+ </a>
+ </li>
+ </ul>
+ </td>
</tr>
<? endforeach ?>
</table>
diff --git a/modules/gallery/views/admin_themes.html.php b/modules/gallery/views/admin_themes.html.php
index d14e8bd4..9d53779f 100644
--- a/modules/gallery/views/admin_themes.html.php
+++ b/modules/gallery/views/admin_themes.html.php
@@ -10,7 +10,7 @@
<div class="g-block ui-helper-clearfix">
<h1> <?= t("Theme choice") ?> </h1>
<p>
- <?= t("Gallery allows you to choose a theme for browsing your Gallery, as well as a special theme for the administration interface. Click a theme to preview and activate it.") ?>
+ <?= t("Make your Gallery beautiful <a href=\"%url\">with a new theme</a>! There are separate themes for the regular site and for the administration interface. Click a theme below to preview and activate it.", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?>
</p>
<div class="g-block-content">
@@ -23,6 +23,7 @@
<p>
<?= $themes[$site]->description ?>
</p>
+ <? $v = new View("admin_themes_buttonset.html"); $v->info = $themes[$site]; print $v; ?>
</div>
<h2> <?= t("Available Gallery themes") ?> </h2>
@@ -40,13 +41,14 @@
<?= $info->description ?>
</p>
</a>
+ <? $v = new View("admin_themes_buttonset.html"); $v->info = $info; print $v; ?>
</div>
<? $count++ ?>
<? endforeach ?>
<? if (!$count): ?>
<p>
- <?= t("There are no other site themes available.") ?>
+ <?= t("There are no other site themes available. <a href=\"%url\">Download one now!</a>", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?>
</p>
<? endif ?>
</div>
@@ -61,6 +63,7 @@
<p>
<?= $themes[$admin]->description ?>
</p>
+ <? $v = new View("admin_themes_buttonset.html"); $v->info = $themes[$admin]; print $v; ?>
</div>
<h2> <?= t("Available admin themes") ?> </h2>
@@ -78,17 +81,18 @@
<?= $info->description ?>
</p>
</a>
+ <? $v = new View("admin_themes_buttonset.html"); $v->info = $info; print $v; ?>
</div>
<? $count++ ?>
<? endforeach ?>
<? if (!$count): ?>
<p>
- <?= t("There are no other admin themes available.") ?>
+ <?= t("There are no other admin themes available. <a href=\"%url\">Download one now!</a>", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?>
</p>
<? endif ?>
</div>
</div>
</div>
-</div> \ No newline at end of file
+</div>
diff --git a/modules/gallery/views/admin_themes_buttonset.html.php b/modules/gallery/views/admin_themes_buttonset.html.php
new file mode 100644
index 00000000..bf474a26
--- /dev/null
+++ b/modules/gallery/views/admin_themes_buttonset.html.php
@@ -0,0 +1,48 @@
+<?php defined("SYSPATH") or die("No direct script access.") ?>
+<ul class="g-buttonset">
+ <li>
+ <a target="_blank"
+ <? if (isset($info['author_url'])): ?>
+ class="ui-state-default ui-icon ui-icon-person ui-corner-left"
+ href="<?= $info['author_url'] ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-person ui-corner-left"
+ href="#"
+ <? endif ?>
+
+ <? if (isset($info['author_name'])): ?>
+ title="<?= $info['author_name'] ?>"
+ <? endif ?>
+ >
+ <? if (isset($info['author_name'])): ?>
+ <?= $info['author_name'] ?>
+ <? endif ?>
+ </a>
+ </li>
+ <li>
+ <a target="_blank"
+ <? if (isset($info['info_url'])): ?>
+ class="ui-state-default ui-icon ui-icon-info"
+ href="<?= $info['info_url'] ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-info"
+ href="#"
+ <? endif ?>
+ >
+ <?= t("info") ?>
+ </a>
+ </li>
+ <li>
+ <a target="_blank"
+ <? if (isset($info['discuss_url'])): ?>
+ class="ui-state-default ui-icon ui-icon-comment ui-corner-right"
+ href="<?= $info['discuss_url'] ?>"
+ <? else: ?>
+ class="ui-state-disabled ui-icon ui-icon-comment ui-corner-right"
+ href="#"
+ <? endif ?>
+ >
+ <?= t("discuss") ?>
+ </a>
+ </li>
+</ul>
diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php
index 77b6d493..ba4a3621 100644
--- a/modules/gallery/views/form_uploadify.html.php
+++ b/modules/gallery/views/form_uploadify.html.php
@@ -28,10 +28,11 @@
uploader: "<?= url::file("lib/uploadify/uploadify.swf") ?>",
script: "<?= url::site("uploader/add_photo/{$album->id}") ?>",
scriptData: <?= json_encode($script_data) ?>,
- fileExt: "*.gif;*.jpg;*.jpeg;*.png;*.GIF;*.JPG;*.JPEG;*.PNG<? if ($movies_allowed): ?>;*.flv;*.mp4;*.m4v;*.FLV;*.MP4;*.M4V<? endif ?>",
+ fileExt: "<?= implode(";", $extensions) ?>",
fileDesc: <?= t("Photos and movies")->for_js() ?>,
cancelImg: "<?= url::file("lib/uploadify/cancel.png") ?>",
simUploadLimit: <?= $simultaneous_upload_limit ?>,
+ sizeLimit: <?= $size_limit_bytes ?>,
wmode: "transparent",
hideButton: true, /* should be true */
auto: true,
@@ -66,26 +67,30 @@
return true;
},
onError: function(event, queueID, fileObj, errorObj) {
- var msg = " - ";
if (errorObj.type == "HTTP") {
if (errorObj.info == "500") {
- msg += <?= t("Unable to process this file")->for_js() ?>;
- // Server error - check server logs
+ error_msg = <?= t("Unable to process this photo")->for_js() ?>;
} else if (errorObj.info == "404") {
- msg += <?= t("The upload script was not found.")->for_js() ?>;
- // Server script not found
+ error_msg = <?= t("The upload script was not found")->for_js() ?>;
+ } else if (errorObj.info == "400") {
+ error_msg = <?= t("This photo is too large (max is %size bytes)",
+ array("size" => $size_limit))->for_js() ?>;
} else {
- // Server Error: status: errorObj.info
- msg += (<?= t("Server error: __INFO__")->for_js() ?>.replace("__INFO__", errorObj.info));
+ msg += (<?= t("Server error: __INFO__ (__TYPE__)")->for_js() ?>
+ .replace("__INFO__", errorObj.info)
+ .replace("__TYPE__", errorObj.type));
}
} else if (errorObj.type == "File Size") {
- var sizelimit = $("#g-uploadify").uploadifySettings(sizeLimit);
- msg += fileObj.name+' '+errorObj.type+' Limit: '+Math.round(d.sizeLimit/1024)+'KB';
+ error_msg = <?= t("This photo is too large (max is %size bytes)",
+ array("size" => $size_limit))->for_js() ?>;
} else {
- msg += (<?= t("Server error: __INFO__ (__TYPE__)")->for_js() ?>
- .replace("__INFO__", errorObj.info)
- .replace("__TYPE__", errorObj.type));
+ error_msg = <?= t("Server error: __INFO__ (__TYPE__)")->for_js() ?>
+ .replace("__INFO__", errorObj.info)
+ .replace("__TYPE__", errorObj.type);
}
+ msg = " - <a target=\"_blank\" href=\"http://codex.gallery2.org/Gallery3:Troubleshooting:Uploading\">" +
+ error_msg + "</a>";
+
$("#g-add-photos-status ul").append(
"<li id=\"q" + queueID + "\" class=\"g-error\">" + fileObj.name + msg + "</li>");
$("#g-uploadify").uploadifyCancel(queueID);
@@ -131,10 +136,7 @@
<? endif ?>
<div>
- <p>
- <?= t("Photos will be uploaded to album: ") ?>
- </p>
- <ul class="g-breadcrumbs ui-helper-clearfix">
+ <ul class="g-breadcrumbs">
<? foreach ($album->parents() as $i => $parent): ?>
<li<? if ($i == 0) print " class=\"g-first\"" ?>> <?= html::clean($parent->title) ?> </li>
<? endforeach ?>
@@ -143,7 +145,7 @@
</div>
<div id="g-add-photos-canvas">
- <button id="g-add-photos-button" class="g-button ui-state-default ui-corner-all" href="#"><?= t("Select photos...") ?></button>
+ <button id="g-add-photos-button" class="g-button ui-state-default ui-corner-all" href="#"><?= t("Select photos (%size max per file)...", array("size" => $size_limit)) ?></button>
<span id="g-uploadify"></span>
</div>
<div id="g-add-photos-status">
diff --git a/modules/gallery/views/movieplayer.html.php b/modules/gallery/views/movieplayer.html.php
index 5c280a36..96d6532c 100644
--- a/modules/gallery/views/movieplayer.html.php
+++ b/modules/gallery/views/movieplayer.html.php
@@ -22,5 +22,5 @@
}
}
}
- )
+ ).ipad();
</script>
diff --git a/modules/gallery/views/upgrade_checker_block.html.php b/modules/gallery/views/upgrade_checker_block.html.php
index b04887b2..c984d99f 100644
--- a/modules/gallery/views/upgrade_checker_block.html.php
+++ b/modules/gallery/views/upgrade_checker_block.html.php
@@ -6,8 +6,10 @@
<p>
<? if (gallery::RELEASE_CHANNEL == "release"): ?>
<?= t("You are using the official Gallery %version release, code named <i>%code_name</i>.", array("version" => gallery::VERSION, "code_name" => gallery::CODE_NAME)) ?>
+ <? elseif (isset($build_number)): ?>
+ <?= t("You are using an experimental snapshot of Gallery %version (build %build_number on branch %branch).", array("version" => gallery::VERSION, "branch" => gallery::RELEASE_BRANCH, "build_number" => $build_number)) ?>
<? else: ?>
- <?= t("You are using an experimental snapshot of Gallery %version (build %build_number on branch %branch).", array("version" => gallery::VERSION, "branch" => gallery::RELEASE_BRANCH, "build_number" => gallery::build_number())) ?>
+ <?= t("You are using an experimental snapshot of Gallery %version (branch %branch) but your gallery3/.build_number file is missing so we don't know what build you have. You should probably upgrade so that you have that file.", array("version" => gallery::VERSION, "branch" => gallery::RELEASE_BRANCH, "build_number" => $build_number)) ?>
<? endif ?>
</p>
diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php
index ad2e3421..70d37dd1 100644
--- a/modules/gallery/views/upgrader.html.php
+++ b/modules/gallery/views/upgrader.html.php
@@ -27,7 +27,7 @@
<div id="done" style="display: none">
<h1> <?= t("That's it!") ?> </h1>
<p>
- <?= t("Your <a href=\"%url\">Gallery</a> is up to date.",
+ <?= t("Your Gallery is up to date.<br/><a href=\"%url\">Return to your Gallery</a>",
array("url" => html::mark_clean(url::base()))) ?>
</p>
</div>
diff --git a/modules/gallery_unit_test/helpers/test.php b/modules/gallery_unit_test/helpers/test.php
index b6b0055a..633f023e 100644
--- a/modules/gallery_unit_test/helpers/test.php
+++ b/modules/gallery_unit_test/helpers/test.php
@@ -19,7 +19,7 @@
*/
class test_Core {
static function random_album_unsaved($parent=null) {
- $rand = random::string(6);
+ $rand = test::random_string(6);
$album = ORM::factory("item");
$album->type = "album";
@@ -34,7 +34,7 @@ class test_Core {
}
static function random_photo_unsaved($parent=null) {
- $rand = random::string(6);
+ $rand = test::random_string(6);
$photo = ORM::factory("item");
$photo->type = "photo";
$photo->parent_id = $parent ? $parent->id : 1;
@@ -49,16 +49,16 @@ class test_Core {
}
static function random_user($password="password") {
- $rand = "name_" . random::string(6);
+ $rand = "name_" . test::random_string(6);
return identity::create_user($rand, $rand, $password, "$rand@rand.com");
}
static function random_group() {
- return identity::create_group(random::string(6));
+ return identity::create_group(test::random_string(6));
}
static function random_name($item=null) {
- $rand = "name_" . random::string(6);
+ $rand = "name_" . test::random_string(6);
if ($item && $item->is_photo()) {
$rand .= ".jpg";
}
@@ -77,7 +77,7 @@ class test_Core {
static function random_tag() {
$tag = ORM::factory("tag");
- $tag->name = random::string(6);
+ $tag->name = test::lorem_ipsum(rand(2, 4));
// Reload so that ORM coerces all fields into strings.
return $tag->save()->reload();
@@ -88,4 +88,22 @@ class test_Core {
fwrite(fopen($b_name = tempnam("/tmp", "test"), "w"), $b);
return `diff $a_name $b_name`;
}
+
+ static function random_string($length) {
+ $buf = "";
+ do {
+ $buf .= random::hash();
+ } while (strlen($buf) < $length);
+ return substr($buf, 0, $length);
+ }
+
+ static function lorem_ipsum($num) {
+ static $lorem_ipsum = null;
+ if (!$lorem_ipsum) {
+ require_once(MODPATH . "gallery_unit_test/vendor/LoremIpsum.class.php");
+ $lorem_ipsum = new LoremIpsumGenerator();
+ }
+ // skip past initial 'lorem ipsum'
+ return substr($lorem_ipsum->getContent($num + 2, "txt"), 13);
+ }
}
diff --git a/modules/gallery_unit_test/vendor/LoremIpsum.class.php b/modules/gallery_unit_test/vendor/LoremIpsum.class.php
new file mode 100644
index 00000000..07050fe5
--- /dev/null
+++ b/modules/gallery_unit_test/vendor/LoremIpsum.class.php
@@ -0,0 +1,461 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+class LoremIpsumGenerator {
+ /**
+ * Copyright (c) 2009, Mathew Tinsley (tinsley@tinsology.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the organization nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATHEW TINSLEY ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ private $words, $wordsPerParagraph, $wordsPerSentence;
+
+ function __construct($wordsPer = 100)
+ {
+ $this->wordsPerParagraph = $wordsPer;
+ $this->wordsPerSentence = 24.460;
+ $this->words = array(
+ 'lorem',
+ 'ipsum',
+ 'dolor',
+ 'sit',
+ 'amet',
+ 'consectetur',
+ 'adipiscing',
+ 'elit',
+ 'curabitur',
+ 'vel',
+ 'hendrerit',
+ 'libero',
+ 'eleifend',
+ 'blandit',
+ 'nunc',
+ 'ornare',
+ 'odio',
+ 'ut',
+ 'orci',
+ 'gravida',
+ 'imperdiet',
+ 'nullam',
+ 'purus',
+ 'lacinia',
+ 'a',
+ 'pretium',
+ 'quis',
+ 'congue',
+ 'praesent',
+ 'sagittis',
+ 'laoreet',
+ 'auctor',
+ 'mauris',
+ 'non',
+ 'velit',
+ 'eros',
+ 'dictum',
+ 'proin',
+ 'accumsan',
+ 'sapien',
+ 'nec',
+ 'massa',
+ 'volutpat',
+ 'venenatis',
+ 'sed',
+ 'eu',
+ 'molestie',
+ 'lacus',
+ 'quisque',
+ 'porttitor',
+ 'ligula',
+ 'dui',
+ 'mollis',
+ 'tempus',
+ 'at',
+ 'magna',
+ 'vestibulum',
+ 'turpis',
+ 'ac',
+ 'diam',
+ 'tincidunt',
+ 'id',
+ 'condimentum',
+ 'enim',
+ 'sodales',
+ 'in',
+ 'hac',
+ 'habitasse',
+ 'platea',
+ 'dictumst',
+ 'aenean',
+ 'neque',
+ 'fusce',
+ 'augue',
+ 'leo',
+ 'eget',
+ 'semper',
+ 'mattis',
+ 'tortor',
+ 'scelerisque',
+ 'nulla',
+ 'interdum',
+ 'tellus',
+ 'malesuada',
+ 'rhoncus',
+ 'porta',
+ 'sem',
+ 'aliquet',
+ 'et',
+ 'nam',
+ 'suspendisse',
+ 'potenti',
+ 'vivamus',
+ 'luctus',
+ 'fringilla',
+ 'erat',
+ 'donec',
+ 'justo',
+ 'vehicula',
+ 'ultricies',
+ 'varius',
+ 'ante',
+ 'primis',
+ 'faucibus',
+ 'ultrices',
+ 'posuere',
+ 'cubilia',
+ 'curae',
+ 'etiam',
+ 'cursus',
+ 'aliquam',
+ 'quam',
+ 'dapibus',
+ 'nisl',
+ 'feugiat',
+ 'egestas',
+ 'class',
+ 'aptent',
+ 'taciti',
+ 'sociosqu',
+ 'ad',
+ 'litora',
+ 'torquent',
+ 'per',
+ 'conubia',
+ 'nostra',
+ 'inceptos',
+ 'himenaeos',
+ 'phasellus',
+ 'nibh',
+ 'pulvinar',
+ 'vitae',
+ 'urna',
+ 'iaculis',
+ 'lobortis',
+ 'nisi',
+ 'viverra',
+ 'arcu',
+ 'morbi',
+ 'pellentesque',
+ 'metus',
+ 'commodo',
+ 'ut',
+ 'facilisis',
+ 'felis',
+ 'tristique',
+ 'ullamcorper',
+ 'placerat',
+ 'aenean',
+ 'convallis',
+ 'sollicitudin',
+ 'integer',
+ 'rutrum',
+ 'duis',
+ 'est',
+ 'etiam',
+ 'bibendum',
+ 'donec',
+ 'pharetra',
+ 'vulputate',
+ 'maecenas',
+ 'mi',
+ 'fermentum',
+ 'consequat',
+ 'suscipit',
+ 'aliquam',
+ 'habitant',
+ 'senectus',
+ 'netus',
+ 'fames',
+ 'quisque',
+ 'euismod',
+ 'curabitur',
+ 'lectus',
+ 'elementum',
+ 'tempor',
+ 'risus',
+ 'cras' );
+ }
+
+ function getContent($count, $format = 'html', $loremipsum = true)
+ {
+ $format = strtolower($format);
+
+ if($count <= 0)
+ return '';
+
+ switch($format)
+ {
+ case 'txt':
+ return $this->getText($count, $loremipsum);
+ case 'plain':
+ return $this->getPlain($count, $loremipsum);
+ default:
+ return $this->getHTML($count, $loremipsum);
+ }
+ }
+
+ private function getWords(&$arr, $count, $loremipsum)
+ {
+ $i = 0;
+ if($loremipsum)
+ {
+ $i = 2;
+ $arr[0] = 'lorem';
+ $arr[1] = 'ipsum';
+ }
+
+ for($i; $i < $count; $i++)
+ {
+ $index = array_rand($this->words);
+ $word = $this->words[$index];
+ //echo $index . '=>' . $word . '<br />';
+
+ if($i > 0 && $arr[$i - 1] == $word)
+ $i--;
+ else
+ $arr[$i] = $word;
+ }
+ }
+
+ private function getPlain($count, $loremipsum, $returnStr = true)
+ {
+ $words = array();
+ $this->getWords($words, $count, $loremipsum);
+ //print_r($words);
+
+ $delta = $count;
+ $curr = 0;
+ $sentences = array();
+ while($delta > 0)
+ {
+ $senSize = $this->gaussianSentence();
+ //echo $curr . '<br />';
+ if(($delta - $senSize) < 4)
+ $senSize = $delta;
+
+ $delta -= $senSize;
+
+ $sentence = array();
+ for($i = $curr; $i < ($curr + $senSize); $i++)
+ $sentence[] = $words[$i];
+
+ $this->punctuate($sentence);
+ $curr = $curr + $senSize;
+ $sentences[] = $sentence;
+ }
+
+ if($returnStr)
+ {
+ $output = '';
+ foreach($sentences as $s)
+ foreach($s as $w)
+ $output .= $w . ' ';
+
+ return $output;
+ }
+ else
+ return $sentences;
+ }
+
+ private function getText($count, $loremipsum)
+ {
+ $sentences = $this->getPlain($count, $loremipsum, false);
+ $paragraphs = $this->getParagraphArr($sentences);
+
+ $paragraphStr = array();
+ foreach($paragraphs as $p)
+ {
+ $paragraphStr[] = $this->paragraphToString($p);
+ }
+
+ $paragraphStr[0] = "\t" . $paragraphStr[0];
+ return implode("\n\n\t", $paragraphStr);
+ }
+
+ private function getParagraphArr($sentences)
+ {
+ $wordsPer = $this->wordsPerParagraph;
+ $sentenceAvg = $this->wordsPerSentence;
+ $total = count($sentences);
+
+ $paragraphs = array();
+ $pCount = 0;
+ $currCount = 0;
+ $curr = array();
+
+ for($i = 0; $i < $total; $i++)
+ {
+ $s = $sentences[$i];
+ $currCount += count($s);
+ $curr[] = $s;
+ if($currCount >= ($wordsPer - round($sentenceAvg / 2.00)) || $i == $total - 1)
+ {
+ $currCount = 0;
+ $paragraphs[] = $curr;
+ $curr = array();
+ //print_r($paragraphs);
+ }
+ //print_r($paragraphs);
+ }
+
+ return $paragraphs;
+ }
+
+ private function getHTML($count, $loremipsum)
+ {
+ $sentences = $this->getPlain($count, $loremipsum, false);
+ $paragraphs = $this->getParagraphArr($sentences);
+ //print_r($paragraphs);
+
+ $paragraphStr = array();
+ foreach($paragraphs as $p)
+ {
+ $paragraphStr[] = "<p>\n" . $this->paragraphToString($p, true) . '</p>';
+ }
+
+ //add new lines for the sake of clean code
+ return implode("\n", $paragraphStr);
+ }
+
+ private function paragraphToString($paragraph, $htmlCleanCode = false)
+ {
+ $paragraphStr = '';
+ foreach($paragraph as $sentence)
+ {
+ foreach($sentence as $word)
+ $paragraphStr .= $word . ' ';
+
+ if($htmlCleanCode)
+ $paragraphStr .= "\n";
+ }
+ return $paragraphStr;
+ }
+
+ /*
+ * Inserts commas and periods in the given
+ * word array.
+ */
+ private function punctuate(& $sentence)
+ {
+ $count = count($sentence);
+ $sentence[$count - 1] = $sentence[$count - 1] . '.';
+
+ if($count < 4)
+ return $sentence;
+
+ $commas = $this->numberOfCommas($count);
+
+ for($i = 1; $i <= $commas; $i++)
+ {
+ $index = (int) round($i * $count / ($commas + 1));
+
+ if($index < ($count - 1) && $index > 0)
+ {
+ $sentence[$index] = $sentence[$index] . ',';
+ }
+ }
+ }
+
+ /*
+ * Determines the number of commas for a
+ * sentence of the given length. Average and
+ * standard deviation are determined superficially
+ */
+ private function numberOfCommas($len)
+ {
+ $avg = (float) log($len, 6);
+ $stdDev = (float) $avg / 6.000;
+
+ return (int) round($this->gauss_ms($avg, $stdDev));
+ }
+
+ /*
+ * Returns a number on a gaussian distribution
+ * based on the average word length of an english
+ * sentence.
+ * Statistics Source:
+ * http://hearle.nahoo.net/Academic/Maths/Sentence.html
+ * Average: 24.46
+ * Standard Deviation: 5.08
+ */
+ private function gaussianSentence()
+ {
+ $avg = (float) 24.460;
+ $stdDev = (float) 5.080;
+
+ return (int) round($this->gauss_ms($avg, $stdDev));
+ }
+
+ /*
+ * The following three functions are used to
+ * compute numbers with a guassian distrobution
+ * Source:
+ * http://us.php.net/manual/en/function.rand.php#53784
+ */
+ private function gauss()
+ { // N(0,1)
+ // returns random number with normal distribution:
+ // mean=0
+ // std dev=1
+
+ // auxilary vars
+ $x=$this->random_0_1();
+ $y=$this->random_0_1();
+
+ // two independent variables with normal distribution N(0,1)
+ $u=sqrt(-2*log($x))*cos(2*pi()*$y);
+ $v=sqrt(-2*log($x))*sin(2*pi()*$y);
+
+ // i will return only one, couse only one needed
+ return $u;
+ }
+
+ private function gauss_ms($m=0.0,$s=1.0)
+ {
+ return $this->gauss()*$s+$m;
+ }
+
+ private function random_0_1()
+ {
+ return (float)rand()/(float)getrandmax();
+ }
+
+} \ No newline at end of file
diff --git a/modules/image_block/module.info b/modules/image_block/module.info
index 6836fabc..6722cc8f 100644
--- a/modules/image_block/module.info
+++ b/modules/image_block/module.info
@@ -1,3 +1,7 @@
name = "Image Block"
description = "Display a random image in the sidebar"
version = 3
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:image_block"
+discuss_url = "http://gallery.menalto.com/forum_module_image_block"
diff --git a/modules/info/module.info b/modules/info/module.info
index 5f84cbb9..f8964a78 100644
--- a/modules/info/module.info
+++ b/modules/info/module.info
@@ -1,3 +1,7 @@
name = "Info"
description = "Display extra information about photos and albums"
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:info"
+discuss_url = "http://gallery.menalto.com/forum_module_info"
diff --git a/modules/notification/module.info b/modules/notification/module.info
index 8c5e1162..84be8f99 100644
--- a/modules/notification/module.info
+++ b/modules/notification/module.info
@@ -1,3 +1,7 @@
name = "Notification"
description = "Send notifications to users when changes are made to watched albums."
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:notification"
+discuss_url = "http://gallery.menalto.com/forum_module_notification"
diff --git a/modules/organize/module.info b/modules/organize/module.info
index 0d16144d..07b9dc38 100644
--- a/modules/organize/module.info
+++ b/modules/organize/module.info
@@ -1,3 +1,7 @@
name = "Organize"
description = "Visually rearrange and move photos in your gallery"
version = 4
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:organize"
+discuss_url = "http://gallery.menalto.com/forum_module_organize"
diff --git a/modules/recaptcha/module.info b/modules/recaptcha/module.info
index cfa1bf7a..ebaff7de 100644
--- a/modules/recaptcha/module.info
+++ b/modules/recaptcha/module.info
@@ -1,3 +1,7 @@
name = "reCAPTCHA"
description = "reCAPTCHA displays a graphical verification that protects the input form from abuse from 'bots,' or automated programs usually written to generate spam (http://recaptcha.net)."
version = 1
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:recaptcha"
+discuss_url = "http://gallery.menalto.com/forum_module_recaptcha"
diff --git a/modules/rest/module.info b/modules/rest/module.info
index 5aaffc28..33c9f1cf 100644
--- a/modules/rest/module.info
+++ b/modules/rest/module.info
@@ -2,3 +2,7 @@ name = "REST API Module"
description = "A REST-based API that allows desktop clients and other apps to interact with Gallery 3"
version = 3
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:rest"
+discuss_url = "http://gallery.menalto.com/forum_module_rest"
diff --git a/modules/rss/module.info b/modules/rss/module.info
index 48375da1..cd13c1b0 100644
--- a/modules/rss/module.info
+++ b/modules/rss/module.info
@@ -1,3 +1,7 @@
name = "RSS"
description = "Provides RSS feeds"
version = 1
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:rss"
+discuss_url = "http://gallery.menalto.com/forum_module_rss"
diff --git a/modules/search/controllers/search.php b/modules/search/controllers/search.php
index eef009a0..e4ac6702 100644
--- a/modules/search/controllers/search.php
+++ b/modules/search/controllers/search.php
@@ -21,28 +21,77 @@ class Search_Controller extends Controller {
public function index() {
$page_size = module::get_var("gallery", "page_size", 9);
$q = Input::instance()->get("q");
+ $q_with_more_terms = search::add_query_terms($q);
+ $show = Input::instance()->get("show");
+
+ if ($show) {
+ $child = ORM::factory("item", $show);
+ $index = search::get_position($child, $q_with_more_terms);
+ if ($index) {
+ $page = ceil($index / $page_size);
+ url::redirect(url::abs_site("search?q=" . urlencode($q) . ($page == 1 ? "" : "&page=$page")));
+ }
+ }
+
$page = Input::instance()->get("page", 1);
- $offset = ($page - 1) * $page_size;
// Make sure that the page references a valid offset
if ($page < 1) {
$page = 1;
}
- list ($count, $result) = search::search($q, $page_size, $offset);
+ $offset = ($page - 1) * $page_size;
+
+ list ($count, $result) = search::search($q_with_more_terms, $page_size, $offset);
+
+ $title = t("Search: %q", array("q" => $q_with_more_terms));
$max_pages = max(ceil($count / $page_size), 1);
$template = new Theme_View("page.html", "collection", "search");
- $template->set_global(array("page" => $page,
- "max_pages" => $max_pages,
- "page_size" => $page_size,
- "children_count" => $count));
+ $root = item::root();
+ $template->set_global(
+ array("page" => $page,
+ "max_pages" => $max_pages,
+ "page_size" => $page_size,
+ "breadcrumbs" => array(
+ Breadcrumb::instance($root->title, $root->url())->set_first(),
+ Breadcrumb::instance($q, url::abs_site("search?q=" . urlencode($q)))->set_last(),
+ ),
+ "children_count" => $count));
$template->content = new View("search.html");
$template->content->items = $result;
$template->content->q = $q;
print $template;
+
+ item::set_display_context_callback(
+ "Search_Controller::get_display_context", $title, $q_with_more_terms, $q);
+ }
+
+ static function get_display_context($item, $title, $query_terms, $q) {
+ $position = search::get_position($item, $query_terms);
+
+ if ($position > 1) {
+ list ($count, $result_data) = search::search($query_terms, 3, $position - 2);
+ list ($previous_item, $ignore, $next_item) = $result_data;
+ } else {
+ $previous_item = null;
+ list ($count, $result_data) = search::search($query_terms, 1, $position);
+ list ($next_item) = $result_data;
+ }
+
+ $search_url = url::abs_site("search?q=" . urlencode($q) . "&show={$item->id}");
+ $root = item::root();
+
+ return array("position" => $position,
+ "previous_item" => $previous_item,
+ "next_item" => $next_item,
+ "sibling_count" => $count,
+ "breadcrumbs" => array(
+ Breadcrumb::instance($root->title, "/", $root->id)->set_first(),
+ Breadcrumb::instance(t("Search: %q", array("q" => $q)), $search_url),
+ Breadcrumb::instance($item->title, $item->url())->set_last()));
}
}
diff --git a/modules/search/helpers/search.php b/modules/search/helpers/search.php
index 09e5e83f..a77a2433 100644
--- a/modules/search/helpers/search.php
+++ b/modules/search/helpers/search.php
@@ -18,10 +18,37 @@
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class search_Core {
+ /**
+ * Add more terms to the query by wildcarding the stem value of the first
+ * few terms in the query.
+ */
+ static function add_query_terms($q) {
+ $MAX_TERMS = 5;
+ $terms = explode(" ", $q, $MAX_TERMS);
+ for ($i = 0; $i < min(count($terms), $MAX_TERMS - 1); $i++) {
+ // Don't wildcard quoted or already wildcarded terms
+ if ((substr($terms[$i], 0, 1) != '"') && (substr($terms[$i], -1, 1) != "*")) {
+ $terms[] = rtrim($terms[$i], "s") . "*";
+ }
+ }
+ return implode(" ", $terms);
+ }
+
static function search($q, $limit, $offset) {
$db = Database::instance();
- $q = $db->escape($q);
+ $query = self::_build_query_base($q) .
+ "ORDER BY `score` DESC " .
+ "LIMIT $limit OFFSET " . (int)$offset;
+
+ $data = $db->query($query);
+ $count = $db->query("SELECT FOUND_ROWS() as c")->current()->c;
+
+ return array($count, new ORM_Iterator(ORM::factory("item"), $data));
+ }
+
+ private static function _build_query_base($q, $where=array()) {
+ $q = Database::instance()->escape($q);
if (!identity::active_user()->admin) {
foreach (identity::group_ids_for_active_user() as $id) {
$fields[] = "`view_$id` = TRUE"; // access::ALLOW
@@ -31,18 +58,13 @@ class search_Core {
$access_sql = "";
}
- $query =
+ return
"SELECT SQL_CALC_FOUND_ROWS {items}.*, " .
" MATCH({search_records}.`data`) AGAINST ('$q') AS `score` " .
"FROM {items} JOIN {search_records} ON ({items}.`id` = {search_records}.`item_id`) " .
"WHERE MATCH({search_records}.`data`) AGAINST ('$q' IN BOOLEAN MODE) " .
- $access_sql .
- "ORDER BY `score` DESC " .
- "LIMIT $limit OFFSET $offset";
- $data = $db->query($query);
- $count = $db->query("SELECT FOUND_ROWS() as c")->current()->c;
-
- return array($count, new ORM_Iterator(ORM::factory("item"), $data));
+ (empty($where) ? "" : " AND " . join(" AND ", $where)) .
+ $access_sql;
}
/**
@@ -87,4 +109,25 @@ class search_Core {
return array($remaining, $total, $percent);
}
+
+ static function get_position($item, $q) {
+ $page_size = module::get_var("gallery", "page_size", 9);
+
+ $query = self::_build_query_base($q, array("{items}.id = " . $item->id));
+
+ $db = Database::instance();
+
+ // Truncate the score by two decimal places as this resolves the issues
+ // that arise due to in exact numeric conversions.
+ $score = $db->query($query)->current()->score;
+ $score = substr($score, 0, strlen($score) - 2);
+
+ $data = $db->query(self::_build_query_base($q) . "having `score` >= " . $score);
+
+ $data->seek($data->count() - 1);
+
+ while ($data->get("id") != $item->id && $data->prev()->valid());
+
+ return $data->key() + 1;
+ }
}
diff --git a/modules/search/module.info b/modules/search/module.info
index f417c4fa..1389798d 100644
--- a/modules/search/module.info
+++ b/modules/search/module.info
@@ -1,3 +1,7 @@
name = "Search"
description = "Allows users to search their Gallery"
version = 1
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:search"
+discuss_url = "http://gallery.menalto.com/forum_module_search"
diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php
index 2afa93a7..ea8907f4 100644
--- a/modules/server_add/controllers/server_add.php
+++ b/modules/server_add/controllers/server_add.php
@@ -286,7 +286,7 @@ class Server_Add_Controller extends Admin_Controller {
} catch (Exception $e) {
// This can happen if a photo file is invalid, like a BMP masquerading as a .jpg
$entry->item_id = 0;
- $task->log("Skipping invalid file: {$entry->file}");
+ $task->log("Skipping invalid file: {$entry->path}");
}
}
diff --git a/modules/server_add/helpers/server_add_theme.php b/modules/server_add/helpers/server_add_theme.php
index 50c050a8..cfca5901 100644
--- a/modules/server_add/helpers/server_add_theme.php
+++ b/modules/server_add/helpers/server_add_theme.php
@@ -24,20 +24,4 @@ class server_add_theme_Core {
. $theme->script("server_add.js");
}
}
-
- static function admin_head($theme) {
- $buf = "";
- if (strpos(Router::$current_uri, "admin/server_add") !== false) {
- $buf .= $theme->css("server_add.css")
- . $theme->css("jquery.autocomplete.css");
- $base = url::site("__ARGS__");
- $csrf = access::csrf_token();
- $buf .= "<script type=\"text/javascript\"> var base_url = \"$base\"; var csrf = \"$csrf\";</script>";
-
- $buf .= $theme->script("jquery.autocomplete.js")
- . $theme->script("admin.js");
- }
-
- return $buf;
- }
} \ No newline at end of file
diff --git a/modules/server_add/js/admin.js b/modules/server_add/js/admin.js
deleted file mode 100644
index 9d7bd181..00000000
--- a/modules/server_add/js/admin.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Set up autocomplete on the server path list
- *
- */
-$("document").ready(function() {
- $("#g-path").autocomplete(
- base_url.replace("__ARGS__", "admin/server_add/autocomplete"), {max: 256});
-});
diff --git a/modules/server_add/module.info b/modules/server_add/module.info
index 87b317b1..4ce0a97d 100644
--- a/modules/server_add/module.info
+++ b/modules/server_add/module.info
@@ -1,3 +1,7 @@
name = "Server Add"
description = "Allows authorized users to load images directly from your web server"
version = 4
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:server_add"
+discuss_url = "http://gallery.menalto.com/forum_module_server_add"
diff --git a/modules/server_add/views/admin_server_add.html.php b/modules/server_add/views/admin_server_add.html.php
index 933ab7f8..474ad428 100644
--- a/modules/server_add/views/admin_server_add.html.php
+++ b/modules/server_add/views/admin_server_add.html.php
@@ -1,4 +1,18 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
+<?= $theme->css("server_add.css") ?>
+<?= $theme->css("jquery.autocomplete.css") ?>
+<?= $theme->script("jquery.autocomplete.js") ?>
+<script type="text/javascript">
+$("document").ready(function() {
+ $("#g-path").autocomplete(
+ "<?= url::site("__ARGS__") ?>".replace("__ARGS__", "admin/server_add/autocomplete"),
+ {
+ max: 256,
+ loadingClass: "g-loading-small",
+ });
+});
+</script>
+
<div class="g-block">
<h1> <?= t("Add from server administration") ?> </h1>
<div class="g-block-content">
@@ -8,12 +22,17 @@
<? if (empty($paths)): ?>
<li class="g-module-status g-info"><?= t("No authorized image source paths defined yet") ?></li>
<? endif ?>
+
<? foreach ($paths as $id => $path): ?>
<li>
<?= html::clean($path) ?>
- <a href="<?= url::site("admin/server_add/remove_path?path=" . urlencode($path) . "&amp;csrf=$csrf") ?>"
+ <a href="<?= url::site("admin/server_add/remove_path?path=" . urlencode($path) . "&amp;csrf=<?= access::csrf_token() ?>") ?>"
id="icon_<?= $id ?>"
- class="g-remove-dir g-button"><span class="ui-icon ui-icon-trash"><?= t("delete") ?></span></a>
+ class="g-remove-dir g-button">
+ <span class="ui-icon ui-icon-trash">
+ <?= t("delete") ?>
+ </span>
+ </a>
</li>
<? endforeach ?>
</ul>
diff --git a/modules/slideshow/module.info b/modules/slideshow/module.info
index b56eac81..8c9a3176 100644
--- a/modules/slideshow/module.info
+++ b/modules/slideshow/module.info
@@ -1,3 +1,7 @@
name = "Slideshow"
description = "Allows users to view a slideshow of photos"
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:slideshow"
+discuss_url = "http://gallery.menalto.com/forum_module_slideshow"
diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php
index 73042a55..77b5f20a 100644
--- a/modules/tag/controllers/admin_tags.php
+++ b/modules/tag/controllers/admin_tags.php
@@ -81,17 +81,25 @@ class Admin_Tags_Controller extends Admin_Controller {
$in_place_edit = InPlaceEdit::factory($tag->name)
->action("admin/tags/rename/$tag->id")
- ->rules(array("required", "length[1,64]"))
- ->messages(array("in_use" => t("There is already a tag with that name")))
- ->callback(array($this, "check_for_duplicate"));
+ ->rules(array("required", "length[1,64]"));
if ($in_place_edit->validate()) {
$old_name = $tag->name;
- $tag->name = $in_place_edit->value();
+ $new_name_or_list = $in_place_edit->value();
+ $tag_list = explode(",", $new_name_or_list);
+
+ $tag->name = array_shift($tag_list);
$tag->save();
- $message = t("Renamed tag <b>%old_name</b> to <b>%new_name</b>",
- array("old_name" => $old_name, "new_name" => $tag->name));
+ if (!empty($tag_list)) {
+ $this->_copy_items_for_tags($tag, $tag_list);
+ $message = t("Split tag <i>%old_name</i> into <i>%tag_list</i>",
+ array("old_name" => $old_name, "tag_list" => $new_name_or_list));
+ } else {
+ $message = t("Renamed tag <i>%old_name</i> to <i>%new_name</i>",
+ array("old_name" => $old_name, "new_name" => $tag->name));
+ }
+
message::success($message);
log::success("tags", $message);
@@ -101,12 +109,11 @@ class Admin_Tags_Controller extends Admin_Controller {
}
}
- public function check_for_duplicate(Validation $post_data, $field) {
- $tag_exists = ORM::factory("tag")->where("name", "=", $post_data[$field])->count_all();
- if ($tag_exists) {
- $post_data->add_error($field, "in_use");
+ private function _copy_items_for_tags($tag, $tag_list) {
+ foreach ($tag->items() as $item) {
+ foreach ($tag_list as $new_tag_name) {
+ tag::add($item, trim($new_tag_name));
+ }
}
}
-
}
-
diff --git a/modules/tag/controllers/tag.php b/modules/tag/controllers/tag.php
index 8f885dea..559e2a5a 100644
--- a/modules/tag/controllers/tag.php
+++ b/modules/tag/controllers/tag.php
@@ -22,7 +22,22 @@ class Tag_Controller extends Controller {
$tag_id = $function;
$tag = ORM::factory("tag")->where("id", "=", $tag_id)->find();
$page_size = module::get_var("gallery", "page_size", 9);
- $page = (int) Input::instance()->get("page", "1");
+
+ $input = Input::instance();
+ $show = $input->get("show");
+
+ if ($show) {
+ $child = ORM::factory("item", $show);
+ $index = tag::get_position($tag, $child);
+ if ($index) {
+ $page = ceil($index / $page_size);
+ $uri = "tag/$tag_id/" . urlencode($tag->name);
+ url::redirect($uri . ($page == 1 ? "" : "?page=$page"));
+ }
+ } else {
+ $page = (int) $input->get("page", "1");
+ }
+
$children_count = $tag->items_count();
$offset = ($page-1) * $page_size;
$max_pages = max(ceil($children_count / $page_size), 1);
@@ -34,16 +49,47 @@ class Tag_Controller extends Controller {
url::redirect(url::merge(array("page" => $max_pages)));
}
+ $root = item::root();
$template = new Theme_View("page.html", "collection", "tag");
- $template->set_global(array("page" => $page,
- "max_pages" => $max_pages,
- "page_size" => $page_size,
- "tag" => $tag,
- "children" => $tag->items($page_size, $offset),
- "children_count" => $children_count));
+ $template->set_global(
+ array("page" => $page,
+ "max_pages" => $max_pages,
+ "page_size" => $page_size,
+ "tag" => $tag,
+ "children" => $tag->items($page_size, $offset),
+ "breadcrumbs" => array(
+ Breadcrumb::instance($root->title, $root->url())->set_first(),
+ Breadcrumb::instance(t("Tag: %tag_name", array("tag_name" => $tag->name)),
+ $tag->url())->set_last()),
+ "children_count" => $children_count));
$template->content = new View("dynamic.html");
$template->content->title = t("Tag: %tag_name", array("tag_name" => $tag->name));
-
print $template;
+
+ item::set_display_context_callback("Tag_Controller::get_display_context", $tag->id);
+ }
+
+ static function get_display_context($item, $tag_id) {
+ $tag = ORM::factory("tag", $tag_id);
+ $where = array(array("type", "!=", "album"));
+
+ $position = tag::get_position($tag, $item, $where);
+ if ($position > 1) {
+ list ($previous_item, $ignore, $next_item) = $tag->items(3, $position - 2, $where);
+ } else {
+ $previous_item = null;
+ list ($next_item) = $tag->items(1, $position, $where);
+ }
+
+ $root = item::root();
+ return array("position" => $position,
+ "previous_item" => $previous_item,
+ "next_item" => $next_item,
+ "sibling_count" => $tag->items_count($where),
+ "breadcrumbs" => array(
+ Breadcrumb::instance($root->title, $root->url())->set_first(),
+ Breadcrumb::instance(t("Tag: %tag_name", array("tag_name" => $tag->name)),
+ $tag->url("show={$item->id}")),
+ Breadcrumb::instance($item->title, $item->url())->set_last()));
}
}
diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php
index fe6d747b..bf41c4df 100644
--- a/modules/tag/controllers/tags.php
+++ b/modules/tag/controllers/tags.php
@@ -22,7 +22,8 @@ class Tags_Controller extends Controller {
// Far from perfection, but at least require view permission for the root album
$album = ORM::factory("item", 1);
access::required("view", $album);
- print tag::cloud(30);
+
+ print tag::cloud(module::get_var("tag", "tag_cloud_size", 30));
}
public function create($item_id) {
diff --git a/modules/tag/helpers/tag.php b/modules/tag/helpers/tag.php
index 733215b3..83a00080 100644
--- a/modules/tag/helpers/tag.php
+++ b/modules/tag/helpers/tag.php
@@ -48,6 +48,7 @@ class tag_Core {
* @return ORM_Iterator of Tag_Model in descending tag count order
*/
static function popular_tags($count) {
+ $count = max($count, 1);
return ORM::factory("tag")
->order_by("count", "DESC")
->limit($count)
@@ -135,4 +136,23 @@ class tag_Core {
// extremely rare case.
db::build()->delete("tags")->where("count", "=", 0)->execute();
}
+
+ /**
+ * Find the position of the given item in the tag collection. The resulting
+ * value is 1-indexed, so the first child in the album is at position 1.
+ *
+ * @param Tag_Model $tag
+ * @param Item_Model $item
+ * @param array $where an array of arrays, each compatible with ORM::where()
+ */
+ static function get_position($tag, $item, $where=array()) {
+ return ORM::factory("item")
+ ->viewable()
+ ->join("items_tags", "items.id", "items_tags.item_id")
+ ->where("items_tags.tag_id", "=", $tag->id)
+ ->where("items.id", "<=", $item->id)
+ ->merge_where($where)
+ ->order_by("items.id")
+ ->count_all();
+ }
} \ No newline at end of file
diff --git a/modules/tag/helpers/tag_block.php b/modules/tag/helpers/tag_block.php
index 8df58a6e..69a9a1c4 100644
--- a/modules/tag/helpers/tag_block.php
+++ b/modules/tag/helpers/tag_block.php
@@ -30,7 +30,7 @@ class tag_block_Core {
$block->css_id = "g-tag";
$block->title = t("Popular tags");
$block->content = new View("tag_block.html");
- $block->content->cloud = tag::cloud(30);
+ $block->content->cloud = tag::cloud(module::get_var("tag", "tag_cloud_size", 30));
if ($theme->item() && $theme->page_subtype() != "tag" && access::can("edit", $theme->item())) {
$controller = new Tags_Controller();
diff --git a/modules/tag/helpers/tag_event.php b/modules/tag/helpers/tag_event.php
index cd79f734..b415b42d 100644
--- a/modules/tag/helpers/tag_event.php
+++ b/modules/tag/helpers/tag_event.php
@@ -36,10 +36,7 @@ class tag_event_Core {
$tag = str_replace("\0", "", $tag);
foreach (explode(",", $tag) as $word) {
$word = trim($word);
- if (function_exists("mb_detect_encoding") &&
- mb_detect_encoding($word, "ISO-8859-1, UTF-8") != "UTF-8") {
- $word = utf8_encode($word);
- }
+ $word = encoding::convert_to_utf8($word);
$tags[$word] = 1;
}
}
@@ -113,11 +110,11 @@ class tag_event_Core {
}
static function add_photos_form($album, $form) {
- if (!isset($group->uploadify)) {
+ $group = $form->add_photos;
+ if (!is_object($group->uploadify)) {
return;
}
- $group = $form->add_photos;
$group->input("tags")
->label(t("Add tags to all uploaded files"))
->value("");
@@ -136,7 +133,8 @@ class tag_event_Core {
}
static function add_photos_form_completed($album, $form) {
- if (!isset($group->uploadify)) {
+ $group = $form->add_photos;
+ if (!is_object($group->uploadify)) {
return;
}
@@ -151,7 +149,7 @@ class tag_event_Core {
static function info_block_get_metadata($block, $item) {
$tags = array();
foreach (tag::item_tags($item) as $tag) {
- $tags[] = "<a href=\"" . url::site("tag/{$tag->name}") . "\">{$tag->name}</a>";
+ $tags[] = "<a href=\"{$tag->url()}\">{$tag->name}</a>";
}
if ($tags) {
$info = $block->content->metadata;
diff --git a/modules/tag/helpers/tag_installer.php b/modules/tag/helpers/tag_installer.php
index 16ad1239..66a78b91 100644
--- a/modules/tag/helpers/tag_installer.php
+++ b/modules/tag/helpers/tag_installer.php
@@ -36,7 +36,8 @@ class tag_installer {
KEY(`tag_id`, `id`),
KEY(`item_id`, `id`))
DEFAULT CHARSET=utf8;");
- module::set_version("tag", 2);
+ module::set_var("tag", "tag_cloud_size", 30);
+ module::set_version("tag", 3);
}
static function upgrade($version) {
@@ -45,6 +46,10 @@ class tag_installer {
$db->query("ALTER TABLE {tags} MODIFY COLUMN `name` VARCHAR(128)");
module::set_version("tag", $version = 2);
}
+ if ($version == 2) {
+ module::set_var("tag", "tag_cloud_size", 30);
+ module::set_version("tag", $version = 3);
+ }
}
static function uninstall() {
diff --git a/modules/tag/helpers/tag_item_rest.php b/modules/tag/helpers/tag_item_rest.php
index a8d3d0bc..be1fa653 100644
--- a/modules/tag/helpers/tag_item_rest.php
+++ b/modules/tag/helpers/tag_item_rest.php
@@ -29,6 +29,7 @@ class tag_item_rest_Core {
static function delete($request) {
list ($tag, $item) = rest::resolve($request->url);
+ access::required("edit", $item);
$tag->remove($item);
$tag->save();
}
diff --git a/modules/tag/helpers/tag_items_rest.php b/modules/tag/helpers/tag_items_rest.php
index 535ab513..8ed07276 100644
--- a/modules/tag/helpers/tag_items_rest.php
+++ b/modules/tag/helpers/tag_items_rest.php
@@ -51,6 +51,7 @@ class tag_items_rest_Core {
static function delete($request) {
list ($tag, $item) = rest::resolve($request->url);
+ access::required("edit", $item);
$tag->remove($item);
$tag->save();
}
diff --git a/modules/tag/models/tag.php b/modules/tag/models/tag.php
index 479a7da0..a7150df8 100644
--- a/modules/tag/models/tag.php
+++ b/modules/tag/models/tag.php
@@ -33,35 +33,39 @@ class Tag_Model_Core extends ORM {
* Return all viewable items associated with this tag.
* @param integer $limit number of rows to limit result to
* @param integer $offset offset in result to start returning rows from
- * @param string $type the type of item (album, photo)
+ * @param string $where an array of arrays, each compatible with ORM::where()
* @return ORM_Iterator
*/
- public function items($limit=null, $offset=null, $type=null) {
- $model = ORM::factory("item")
+ public function items($limit=null, $offset=null, $where=array()) {
+ if (is_scalar($where)) {
+ // backwards compatibility
+ $where = array(array("items.type", "=", $where));
+ }
+ return ORM::factory("item")
->viewable()
->join("items_tags", "items.id", "items_tags.item_id")
- ->where("items_tags.tag_id", "=", $this->id);
- if ($type) {
- $model->where("items.type", "=", $type);
- }
- return $model->find_all($limit, $offset);
+ ->where("items_tags.tag_id", "=", $this->id)
+ ->merge_where($where)
+ ->order_by("items.id")
+ ->find_all($limit, $offset);
}
/**
* Return the count of all viewable items associated with this tag.
- * @param string $type the type of item (album, photo)
+ * @param string $where an array of arrays, each compatible with ORM::where()
* @return integer
*/
- public function items_count($type=null) {
- $model = ORM::factory("item")
+ public function items_count($where=array()) {
+ if (is_scalar($where)) {
+ // backwards compatibility
+ $where = array(array("items.type", "=", $where));
+ }
+ return $model = ORM::factory("item")
->viewable()
->join("items_tags", "items.id", "items_tags.item_id")
- ->where("items_tags.tag_id", "=", $this->id);
-
- if ($type) {
- $model->where("items.type", "=", $type);
- }
- return $model->count_all();
+ ->where("items_tags.tag_id", "=", $this->id)
+ ->merge_where($where)
+ ->count_all();
}
/**
@@ -69,13 +73,23 @@ class Tag_Model_Core extends ORM {
* to this tag.
*/
public function save() {
- $related_item_ids = array();
- foreach (db::build()
- ->select("item_id")
- ->from("items_tags")
- ->where("tag_id", "=", $this->id)
- ->execute() as $row) {
- $related_item_ids[$row->item_id] = 1;
+ // Check to see if another tag exists with the same name
+ $duplicate_tag = ORM::factory("tag")
+ ->where("name", "=", $this->name)
+ ->where("id", "!=", $this->id)
+ ->find();
+ if ($duplicate_tag->loaded()) {
+ // If so, tag its items with this tag so as to merge it
+ $duplicate_tag_items = ORM::factory("item")
+ ->join("items_tags", "items.id", "items_tags.item_id")
+ ->where("items_tags.tag_id", "=", $duplicate_tag->id)
+ ->find_all();
+ foreach ($duplicate_tag_items as $item) {
+ $this->add($item);
+ }
+
+ // ... and remove the duplicate tag
+ $duplicate_tag->delete();
}
if (isset($this->object_relations["items"])) {
@@ -132,7 +146,7 @@ class Tag_Model_Core extends ORM {
* @param string $query the query string (eg "page=3")
*/
public function url($query=null) {
- $url = url::site("tag/{$this->id}/{$this->name}");
+ $url = url::site("tag/{$this->id}/" . urlencode($this->name));
if ($query) {
$url .= "?$query";
}
diff --git a/modules/tag/module.info b/modules/tag/module.info
index 8851d119..75d16bf0 100644
--- a/modules/tag/module.info
+++ b/modules/tag/module.info
@@ -1,3 +1,7 @@
name = "Tags"
description = "Allows users to tag photos and albums"
-version = 2
+version = 3
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:tag"
+discuss_url = "http://gallery.menalto.com/forum_module_tag"
diff --git a/modules/tag/tests/Tag_Test.php b/modules/tag/tests/Tag_Test.php
index f5ccb3a2..52fd4fdd 100644
--- a/modules/tag/tests/Tag_Test.php
+++ b/modules/tag/tests/Tag_Test.php
@@ -18,18 +18,60 @@
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Tag_Test extends Gallery_Unit_Test_Case {
+ public function setup() {
+ ORM::factory("tag")->delete_all();
+ }
+
public function create_tag_test() {
$album = test::random_album();
tag::add($album, "tag1");
$tag = ORM::factory("tag")->where("name", "=", "tag1")->find();
- $this->assert_true(1, $tag->count);
+ $this->assert_equal(1, $tag->count);
// Make sure adding the tag again doesn't increase the count
tag::add($album, "tag1");
- $this->assert_true(1, $tag->reload()->count);
+ $this->assert_equal(1, $tag->reload()->count);
tag::add(test::random_album(), "tag1");
- $this->assert_true(2, $tag->reload()->count);
+ $this->assert_equal(2, $tag->reload()->count);
+ }
+
+ public function rename_merge_tag_test() {
+ $album1 = test::random_album();
+ $album2 = test::random_album();
+
+ tag::add($album1, "tag1");
+ tag::add($album2, "tag2");
+
+ $tag1 = ORM::factory("tag")->where("name", "=", "tag1")->find();
+ $tag1->name = "tag2";
+ $tag1->save();
+
+ // Tags should be merged; $tag2 should be deleted
+ $tag1->reload();
+
+ $this->assert_equal(2, $tag1->count);
+ $this->assert_true($tag1->has($album1));
+ $this->assert_true($tag1->has($album2));
+ $this->assert_equal(1, ORM::factory("tag")->count_all());
+ }
+
+ public function rename_merge_tag_with_same_items_test() {
+ $album = test::random_album();
+
+ tag::add($album, "tag1");
+ tag::add($album, "tag2");
+
+ $tag1 = ORM::factory("tag")->where("name", "=", "tag1")->find();
+ $tag1->name = "tag2";
+ $tag1->save();
+
+ // Tags should be merged
+ $tag1->reload();
+
+ $this->assert_equal(1, $tag1->count);
+ $this->assert_true($tag1->has($album));
+ $this->assert_equal(1, ORM::factory("tag")->count_all());
}
-} \ No newline at end of file
+}
diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php
index a3633b52..41be6c03 100644
--- a/modules/user/controllers/admin_users.php
+++ b/modules/user/controllers/admin_users.php
@@ -21,6 +21,8 @@ class Admin_Users_Controller extends Admin_Controller {
public function index() {
$view = new Admin_View("admin.html");
$view->page_title = t("Users and groups");
+ $view->page_type = "collection";
+ $view->page_subtype = "admin_users";
$view->content = new View("admin_users.html");
// @todo: add this as a config option
@@ -29,6 +31,12 @@ class Admin_Users_Controller extends Admin_Controller {
$builder = db::build();
$user_count = $builder->from("users")->count_records();
+ // Pagination info
+ $view->page = $page;
+ $view->page_size = $page_size;
+ $view->children_count = $user_count;
+ $view->max_pages = ceil($view->children_count / $view->page_size);
+
$view->content->pager = new Pagination();
$view->content->pager->initialize(
array("query_string" => "page",
diff --git a/modules/user/controllers/password.php b/modules/user/controllers/password.php
index 4e93d5ce..ab31c6b4 100644
--- a/modules/user/controllers/password.php
+++ b/modules/user/controllers/password.php
@@ -18,6 +18,9 @@
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Password_Controller extends Controller {
+ const ALLOW_MAINTENANCE_MODE = true;
+ const ALLOW_PRIVATE_GALLERY = true;
+
public function reset() {
$form = self::_reset_form();
if (request::method() == "post") {
@@ -105,7 +108,7 @@ class Password_Controller extends Controller {
if (!empty($hash)) {
$hidden->value($hash);
}
- $minimum_length = module::get_var("user", "mininum_password_length", 5);
+ $minimum_length = module::get_var("user", "minimum_password_length", 5);
$input_password = $group->password("password")->label(t("Password"))->id("g-password")
->rules($minimum_length ? "required|length[$minimum_length, 40]" : "length[40]");
$group->password("password2")->label(t("Confirm Password"))->id("g-password2")
diff --git a/modules/user/helpers/user_installer.php b/modules/user/helpers/user_installer.php
index b889af49..9b582773 100644
--- a/modules/user/helpers/user_installer.php
+++ b/modules/user/helpers/user_installer.php
@@ -24,6 +24,7 @@ class user_installer {
static function install() {
IdentityProvider::change_provider("user");
+ // Set the latest version in initialize() below
}
static function upgrade($version) {
@@ -44,6 +45,13 @@ class user_installer {
->execute();
module::set_version("user", $version = 3);
}
+
+ if ($version == 3) {
+ $password_length = module::get_var("user", "mininum_password_length", 5);
+ module::set_var("user", "minimum_password_length", $password_length);
+ module::clear_var("user", "mininum_password_length");
+ module::set_version("user", $version = 4);
+ }
}
static function uninstall() {
@@ -129,7 +137,7 @@ class user_installer {
access::allow($registered, "view", $root);
access::allow($registered, "view_full", $root);
- module::set_var("user", "mininum_password_length", 5);
- module::set_version("user", 3);
+ module::set_var("user", "minimum_password_length", 5);
+ module::set_version("user", 4);
}
} \ 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 8cba3c82..79d31f7c 100644
--- a/modules/user/libraries/drivers/IdentityProvider/Gallery.php
+++ b/modules/user/libraries/drivers/IdentityProvider/Gallery.php
@@ -156,7 +156,7 @@ class IdentityProvider_Gallery_Driver implements IdentityProvider_Driver {
/**
* @see IdentityProvider_Driver::remove_user_to_group.
*/
- public function remove_user_from_group($user, $group_id) {
+ public function remove_user_from_group($user, $group) {
$group->remove($user);
$group->save();
}
diff --git a/modules/user/models/group.php b/modules/user/models/group.php
index 4409dcb8..46642203 100644
--- a/modules/user/models/group.php
+++ b/modules/user/models/group.php
@@ -28,6 +28,12 @@ class Group_Model_Core extends ORM implements Group_Definition {
$old = clone $this;
module::event("group_before_delete", $this);
parent::delete($id);
+
+ db::build()
+ ->delete("groups_users")
+ ->where("group_id", "=", empty($id) ? $old->id : $id)
+ ->execute();
+
module::event("group_deleted", $old);
$this->users_cache = null;
}
diff --git a/modules/user/models/user.php b/modules/user/models/user.php
index 145738ca..8fe0a87b 100644
--- a/modules/user/models/user.php
+++ b/modules/user/models/user.php
@@ -43,6 +43,12 @@ class User_Model_Core extends ORM implements User_Definition {
$old = clone $this;
module::event("user_before_delete", $this);
parent::delete($id);
+
+ db::build()
+ ->delete("groups_users")
+ ->where("user_id", "=", empty($id) ? $old->id : $id)
+ ->execute();
+
module::event("user_deleted", $old);
$this->groups_cache = null;
}
@@ -147,7 +153,7 @@ class User_Model_Core extends ORM implements User_Definition {
}
if (!$this->loaded() || isset($this->password_length)) {
- $minimum_length = module::get_var("user", "mininum_password_length", 5);
+ $minimum_length = module::get_var("user", "minimum_password_length", 5);
if ($this->password_length < $minimum_length) {
$v->add_error("password", "min_length");
}
diff --git a/modules/user/module.info b/modules/user/module.info
index 185a3e3a..503bcd0d 100644
--- a/modules/user/module.info
+++ b/modules/user/module.info
@@ -1,4 +1,8 @@
name = "Users and Groups"
description = "Gallery 3 user and group management"
-version = 3
+version = 4
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:user"
+discuss_url = "http://gallery.menalto.com/forum_module_user"
diff --git a/modules/user/views/admin_users.html.php b/modules/user/views/admin_users.html.php
index a7bd6b27..033c9dae 100644
--- a/modules/user/views/admin_users.html.php
+++ b/modules/user/views/admin_users.html.php
@@ -110,7 +110,7 @@
</table>
<div class="g-paginator">
- <?= $pager ?>
+ <?= $theme->paginator() ?>
</div>
</div>
diff --git a/modules/user/views/admin_users_group.html.php b/modules/user/views/admin_users_group.html.php
index 2362e42b..31b91351 100644
--- a/modules/user/views/admin_users_group.html.php
+++ b/modules/user/views/admin_users_group.html.php
@@ -17,7 +17,7 @@
<? if ($group->users->count_all() > 0): ?>
<ul class="g-member-list">
- <? foreach ($group->users->find_all() as $i => $user): ?>
+ <? foreach ($group->users->order_by("name", "ASC")->find_all() as $i => $user): ?>
<li class="g-user">
<?= html::clean($user->name) ?>
<? if (!$group->special): ?>
diff --git a/modules/user/views/reset_password.html.php b/modules/user/views/reset_password.html.php
index 3afca881..d939ad42 100644
--- a/modules/user/views/reset_password.html.php
+++ b/modules/user/views/reset_password.html.php
@@ -9,8 +9,9 @@
<?= t("Hello, %name,", array("name" => $user->full_name ? $user->full_name : $user->name)) ?>
</p>
<p>
- <?= t("We received a request to reset your password for <a href=\"%site_url\">%site_url</a>. If you made this request, you can confirm it by <a href=\"%confirm_url\">clicking this link</a>. If you didn't request this password reset, it's ok to ignore this mail.",
- array("site_url" => html::mark_clean(url::base(false, "http")),
+ <?= t("We received a request to reset your password for <a href=\"%site_url\">%base_url</a>. If you made this request, you can confirm it by <a href=\"%confirm_url\">clicking this link</a>. If you didn't request this password reset, it's ok to ignore this mail.",
+ array("site_url" => html::mark_clean(url::abs_site("/")),
+ "base_url" => html::mark_clean(url::base(false)),
"confirm_url" => $confirm_url)) ?>
</p>
</body>
diff --git a/modules/watermark/module.info b/modules/watermark/module.info
index 41a871bd..58efa43f 100644
--- a/modules/watermark/module.info
+++ b/modules/watermark/module.info
@@ -1,3 +1,7 @@
name = "Watermarks"
description = "Allows users to watermark their photos"
version = 2
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Modules:watermark"
+discuss_url = "http://gallery.menalto.com/forum_module_watermark"
diff --git a/themes/admin_wind/css/screen-rtl.css b/themes/admin_wind/css/screen-rtl.css
new file mode 100644
index 00000000..79afabb3
--- /dev/null
+++ b/themes/admin_wind/css/screen-rtl.css
@@ -0,0 +1,400 @@
+/**
+ * Gallery 3 Admin Wind Theme Right-to-Left Screen Styles
+ */
+
+.rtl {
+ direction: rtl;
+}
+
+#g-header,
+#g-content,
+#g-sidebar,
+#g-footer,
+caption,
+th,
+#g-dialog,
+.g-context-menu li a,
+.g-message-box li,
+#g-site-status li {
+ text-align: right;
+}
+
+.g-text-right {
+ text-align: left;
+}
+
+/* Lists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-text li,
+.g-text li {
+ margin-left: 0;
+ margin-right: 1em;
+}
+
+/* Messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-error,
+.g-info,
+.g-success,
+.g-warning,
+#g-add-photos-status .g-success,
+#g-add-photos-status .g-error {
+ background-position: center right;
+ padding-right: 30px !important;
+}
+
+form li.g-error,
+form li.g-info,
+form li.g-success,
+form li.g-warning {
+ padding-right: 0 !important;
+}
+
+.g-left,
+.g-inline li,
+#g-content #g-album-grid .g-item,
+.sf-menu li,
+.g-breadcrumbs li,
+.g-paginator li,
+.g-buttonset li,
+.ui-icon-left .ui-icon,
+.g-short-form li,
+form ul ul li,
+input[type="submit"],
+input[type="reset"],
+input.checkbox,
+input[type=checkbox],
+input.radio,
+input[type=radio] {
+ float: right;
+}
+
+.g-right,
+.ui-icon-right .ui-icon {
+ float: left;
+}
+
+.g-inline li {
+ margin-right: 1em;
+}
+
+.g-inline li.g-first {
+ margin-right: 0;
+}
+
+.g-breadcrumbs li {
+ background: transparent url('../images/ico-separator-rtl.gif') no-repeat scroll right center;
+ padding: 1em 18px 1em 8px;
+}
+
+.g-breadcrumbs .g-first {
+ background: none;
+ padding-right: 0;
+}
+
+input.checkbox,
+input[type="checkbox"],
+input.radio,
+input[type="radio"] {
+ margin-right: 0;
+ margin-left: .4em;
+}
+
+#g-add-comment {
+ right: inherit;
+ left: 0;
+}
+
+.ui-icon-left .ui-icon {
+ margin-left: .2em;
+}
+
+.ui-icon-right .ui-icon {
+ margin-right: .2em;
+}
+
+.g-group h4 {
+ padding: .5em .5em .5em 0;
+}
+
+.g-group .g-user {
+ padding: .2em .5em 0 0;
+}
+
+/* RTL Corner radius ~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-buttonset .ui-corner-tl {
+ -moz-border-radius-topleft: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topright: 5px !important;
+ -webkit-border-top-right-radius: 5px !important;
+ border-top-right-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-tr {
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topleft: 5px !important;
+ -webkit-border-top-left-radius: 5px !important;
+ border-top-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-bl {
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 5px !important;
+ -webkit-border-bottom-right-radius: 5px !important;
+ border-bottom-right-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-br {
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomleft: 5px !important;
+ -webkit-border-bottom-left-radius: 5px !important;
+ border-bottom-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-right,
+.ui-progressbar .ui-corner-right {
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topleft: 5px !important;
+ -webkit-border-top-left-radius: 5px !important;
+ border-top-left-radius: 5px !important;
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomleft: 5px !important;
+ -webkit-border-bottom-left-radius: 5px !important;
+ border-bottom-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-left,
+.ui-progressbar .ui-corner-left {
+ -moz-border-radius-topleft: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topright: 5px !important;
+ -webkit-border-top-right-radius: 5px !important;
+ border-top-right-radius: 5px !important;
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 5px !important;
+ -webkit-border-bottom-right-radius: 5px !important;
+ border-bottom-right-radius: 5px !important;
+}
+
+/* RTL Superfish ~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.sf-menu a {
+ border-left: none;
+ border-right:1px solid #fff;
+}
+
+.sf-menu a.sf-with-ul {
+ padding-left: 2.25em;
+ padding-right: 1em;
+}
+
+.sf-sub-indicator {
+ left: .75em !important;
+ right: auto;
+ background: url('../../../lib/superfish/images/arrows-ffffff-rtl.png') no-repeat -10px -100px;
+}
+
+a > .sf-sub-indicator {
+ top: .8em;
+ background-position: -10px -100px;
+}
+
+a:focus > .sf-sub-indicator,
+a:hover > .sf-sub-indicator,
+a:active > .sf-sub-indicator,
+li:hover > a > .sf-sub-indicator,
+li.sfHover > a > .sf-sub-indicator {
+ background-position: 0 -100px;
+}
+
+.sf-menu ul .sf-sub-indicator {
+ background-position: 0 0;
+}
+
+.sf-menu ul a > .sf-sub-indicator {
+ background-position: -10px 0;
+}
+
+.sf-menu ul a:focus > .sf-sub-indicator,
+.sf-menu ul a:hover > .sf-sub-indicator,
+.sf-menu ul a:active > .sf-sub-indicator,
+.sf-menu ul li:hover > a > .sf-sub-indicator,
+.sf-menu ul li.sfHover > a > .sf-sub-indicator {
+ background-position: 0 0;
+}
+
+.sf-menu li:hover ul,
+.sf-menu li.sfHover ul {
+ right: 0;
+ left: auto;
+}
+
+ul.sf-menu li li:hover ul,
+ul.sf-menu li li.sfHover ul {
+ right: 12em;
+ left: auto;
+}
+ul.sf-menu li li li:hover ul,
+ul.sf-menu li li li.sfHover ul {
+ right: 12em;
+ left: auto;
+}
+
+.sf-shadow ul {
+ background: url('../../../lib/superfish/images/shadow.png') no-repeat bottom left;
+ padding: 0 0 9px 8px;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-top-right-radius: 0;
+ -webkit-border-bottom-left-radius: 0;
+ -moz-border-radius-topleft: 17px;
+ -moz-border-radius-bottomright: 17px;
+ -webkit-border-top-left-radius: 17px;
+ -webkit-border-bottom-right-radius: 17px;
+ border-top-left-radius: 17px;
+ border-bottom-right-radius: 17px;
+}
+
+/* RTL ThemeRoller ~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.ui-dialog .ui-dialog-titlebar {
+ padding: 0.5em 1em 0.3em 0.3em;
+}
+
+.ui-dialog .ui-dialog-title {
+ float: right;
+}
+
+.ui-dialog .ui-dialog-titlebar-close {
+ left: 0.3em;
+ right: auto;
+}
+
+.ui-tabs .ui-tabs-nav li {
+ float: right;
+}
+
+#g-content #g-album-grid .g-item,
+#g-site-theme,
+#g-admin-theme,
+.g-selected img,
+.g-available .g-block img,
+#g-content #g-photo-stream .g-item,
+li.g-group,
+#g-server-add-admin {
+ float: right;
+}
+
+#g-site-theme {
+ margin-right: 0;
+ margin-left: 1em;
+}
+
+#g-admin-graphics .g-available .g-block {
+ float: right;
+ margin-left: 1em;
+ margin-right: 0em;
+}
+
+#g-site-admin-menu {
+ left: auto;
+ right: 150px;
+}
+
+#g-header #g-login-menu {
+ float: left;
+}
+
+#g-header #g-login-menu li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-right: 1.2em;
+}
+
+#g-sidebar .g-block-content {
+ padding-left: 0em;
+ padding-right: 1em;
+}
+
+.g-selected img,
+.g-available .g-block img {
+ margin: 0 0 1em 1em;
+}
+
+.g-button {
+ margin: 0 0 0 4px;
+}
+
+/* RTL paginator ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-paginator .g-info {
+ width: 35%;
+}
+
+.g-paginator .g-text-right {
+ margin-left: 0;
+}
+
+.g-paginator .ui-icon-seek-end {
+ background-position: -80px -160px;
+}
+
+.g-paginator .ui-icon-seek-next {
+ background-position: -48px -160px;
+}
+
+.g-paginator .ui-icon-seek-prev {
+ background-position: -32px -160px;
+}
+
+.g-paginator .ui-icon-seek-first {
+ background-position: -64px -160px;
+}
+
+#g-header #g-login-menu,
+#g-header #g-quick-search-form {
+ clear: left;
+ float: left;
+}
+
+#g-header #g-login-menu li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-right: 1.2em;
+}
+
+#g-site-menu {
+ left: auto;
+ right: 240px;
+}
+
+#g-view-menu #g-slideshow-link {
+ background-image: url('../images/ico-view-slideshow-rtl.png');
+}
+
+#g-sidebar .g-block-content {
+ padding-right: 1em;
+ padding-left: 0;
+}
+
+#g-footer #g-credits li {
+ padding-left: 1.2em !important;
+ padding-right: 0;
+}
diff --git a/themes/admin_wind/css/screen.css b/themes/admin_wind/css/screen.css
index a5376ff6..58942387 100644
--- a/themes/admin_wind/css/screen.css
+++ b/themes/admin_wind/css/screen.css
@@ -12,7 +12,6 @@
* 6) Positioning and order
* 7) Navigation and menus
* 8) jQuery and jQuery UI
- * 9) Right-to-left language styles
*
* @todo Review g-buttonset-vertical
*/
@@ -26,7 +25,10 @@ body, html {
font-family: 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
}
-p {
+p,
+#g-content ul,
+#g-content ol,
+#g-content dl {
margin-bottom: 1em;
}
@@ -87,7 +89,25 @@ a:hover,
text-decoration: none;
}
+/* Lists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+ul.g-text li,
+.g-text ul li {
+ list-style-type: disc;
+}
+
+ol.g-text li,
+.g-text ol li {
+ list-style-type: decimal;
+}
+
+.g-text li,
+.g-text li {
+ margin-left: 1em;
+}
+
/* Forms ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
form {
margin: 0;
}
@@ -296,6 +316,7 @@ th {
}
/* Text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
.g-text-small {
font-size: .8em;
}
@@ -333,10 +354,6 @@ th {
padding: 0;
}
-#g-sidebar .g-block-content {
- padding: 0;
-}
-
#g-content .g-selected,
#g-content .g-available .g-block {
border: 1px solid #ccc;
@@ -399,15 +416,12 @@ th {
background-color: #fff;
}
-ul.enumeration li {
- list-style-type: disc;
- margin-left: 20px;
-}
-
/*** ******************************************************************
* 3) Page layout containers
*********************************************************************/
+
/* Dimension and scale ~~~~~~~~~~~~~~~~~~~ */
+
.g-one-quarter {
width: 25%;
}
@@ -602,11 +616,13 @@ ul.enumeration li {
}
/* In-line editing ~~~~~~~~~~~~~~~~~~~~~~ */
+
#g-in-place-edit-message {
background-color: #FFF;
}
-/* Language options ~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* Language options ~~~~~~~~~~~~~~~~~~~~~ */
+
#g-share-translations-form fieldset {
border: 0px;
margin: 0px;
@@ -703,11 +719,11 @@ tr.g-error td.g-error,
}
tr.g-success {
- background-image: none;
+ background-image: none;
}
tr.g-success td.g-success {
- background-image: url('../images/ico-success.png');
+ background-image: url('../images/ico-success.png');
}
.g-warning,
@@ -731,8 +747,8 @@ form .g-error {
}
.g-default {
- background-color: #c5dbec;
- font-weight: bold;
+ background-color: #c5dbec;
+ font-weight: bold;
}
.g-draggable {
@@ -810,6 +826,7 @@ form .g-error {
/** *******************************************************************
* 8) jQuery and jQuery UI
*********************************************************************/
+
/* Generic block container ~~~~~~~~~~~~~~~ */
.g-block {
@@ -878,7 +895,7 @@ button {
.g-progress-bar {
height: 1em;
width: 100%;
- margin-top: .5em;
+ margin: .5em 0;
display: inline-block;
}
@@ -993,18 +1010,19 @@ div#g-action-status {
}
/* Superfish menu overrides ~~~~~~~~~~~~~~ */
+
.sf-menu ul {
- width: 12em;
+ width: 12em;
}
ul.sf-menu li li:hover ul,
ul.sf-menu li li.sfHover ul {
- left: 12em;
+ left: 12em;
}
ul.sf-menu li li li:hover ul,
ul.sf-menu li li li.sfHover ul {
- left: 12em;
+ left: 12em;
}
@@ -1018,6 +1036,7 @@ ul.sf-menu li li li.sfHover ul {
}
/* jQuery UI Dialog ~~~~~~~~~~~~~~~~~~~~~~ */
+
.ui-widget-overlay {
background: #000;
opacity: .7;
@@ -1037,301 +1056,3 @@ ul.sf-menu li li li.sfHover ul {
#g-admin-dashboard-sidebar .ui-dialog-titlebar {
padding: .2em .4em;
}
-
-/** *******************************************************************
- * 9) Right to left styles
- *********************************************************************/
-.rtl {
- direction: rtl;
-}
-
-.rtl #g-header,
-.rtl #g-content,
-.rtl #g-sidebar,
-.rtl #g-footer,
-.rtl caption,
-.rtl th,
-.rtl #g-dialog,
-.rtl .g-context-menu li a,
-.rtl .g-message-box li,
-.rtl #g-site-status li {
- text-align: right;
-}
-
-.rtl .g-text-right {
- text-align: left;
-}
-
-.rtl .g-error,
-.rtl .g-info,
-.rtl .g-success,
-.rtl .g-warning,
-.rtl #g-add-photos-status .g-success,
-.rtl #g-add-photos-status .g-error {
- background-position: center right;
- padding-right: 30px !important;
-}
-
-.rtl form li.g-error,
-.rtl form li.g-info,
-.rtl form li.g-success,
-.rtl form li.g-warning {
- padding-right: 0 !important;
-}
-
-.rtl .g-left,
-.rtl .g-inline li,
-.rtl #g-content #g-album-grid .g-item,
-.rtl .sf-menu li,
-.rtl .g-breadcrumbs li,
-.rtl .g-paginator li,
-.rtl .g-buttonset li,
-.rtl .ui-icon-left .ui-icon,
-.rtl .g-short-form li,
-.rtl form ul ul li,
-.rtl input[type="submit"],
-.rtl input[type="reset"],
-.rtl input.checkbox,
-.rtl input[type=checkbox],
-.rtl input.radio,
-.rtl input[type=radio] {
- float: right;
-}
-
-.rtl .g-right,
-.rtl .ui-icon-right .ui-icon {
- float: left;
-}
-
-.rtl .g-inline li {
- margin-right: 1em;
-}
-
-.rtl .g-inline li.g-first {
- margin-right: 0;
-}
-
-.rtl .g-breadcrumbs li {
- background: transparent url('..images/ico-separator-rtl.gif') no-repeat scroll right center;
- padding: 1em 18px 1em 8px;
-}
-
-.rtl .g-breadcrumbs .g-first {
- background: none;
- padding-right: 0;
-}
-
-.rtl input.checkbox {
- margin-left: .4em;
-}
-
-.rtl #g-add-comment {
- right: inherit;
- left: 0;
-}
-
-.rtl .ui-icon-left .ui-icon {
- margin-left: .2em;
-}
-
-.rtl .ui-icon-right .ui-icon {
- margin-right: .2em;
-}
-
-/* RTL Corner radius ~~~~~~~~~~~~~~~~~~~~~~ */
-.rtl .g-buttonset .ui-corner-tl {
- -moz-border-radius-topleft: 0;
- -webkit-border-top-left-radius: 0;
- border-top-left-radius: 0;
- -moz-border-radius-topright: 5px !important;
- -webkit-border-top-right-radius: 5px !important;
- border-top-right-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-tr {
- -moz-border-radius-topright: 0;
- -webkit-border-top-right-radius: 0;
- border-top-right-radius: 0;
- -moz-border-radius-topleft: 5px !important;
- -webkit-border-top-left-radius: 5px !important;
- border-top-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-bl {
- -moz-border-radius-bottomleft: 0;
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 5px !important;
- -webkit-border-bottom-right-radius: 5px !important;
- border-bottom-right-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-br {
- -moz-border-radius-bottomright: 0;
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft: 5px !important;
- -webkit-border-bottom-left-radius: 5px !important;
- border-bottom-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-right,
-.rtl .ui-progressbar .ui-corner-right {
- -moz-border-radius-topright: 0;
- -webkit-border-top-right-radius: 0;
- border-top-right-radius: 0;
- -moz-border-radius-topleft: 5px !important;
- -webkit-border-top-left-radius: 5px !important;
- border-top-left-radius: 5px !important;
- -moz-border-radius-bottomright: 0;
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft: 5px !important;
- -webkit-border-bottom-left-radius: 5px !important;
- border-bottom-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-left,
-.rtl .ui-progressbar .ui-corner-left {
- -moz-border-radius-topleft: 0;
- -webkit-border-top-left-radius: 0;
- border-top-left-radius: 0;
- -moz-border-radius-topright: 5px !important;
- -webkit-border-top-right-radius: 5px !important;
- border-top-right-radius: 5px !important;
- -moz-border-radius-bottomleft: 0;
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 5px !important;
- -webkit-border-bottom-right-radius: 5px !important;
- border-bottom-right-radius: 5px !important;
-}
-
-/* RTL Superfish ~~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-.rtl .sf-menu a {
- border-left: none;
- border-right:1px solid #fff;
-}
-
-.rtl .sf-menu a.sf-with-ul {
- padding-left: 2.25em;
- padding-right: 1em;
-}
-
-.rtl .sf-sub-indicator {
- left: .75em !important;
- right: auto;
- background: url('../../../lib/superfish/images/arrows-ffffff-rtl.png') no-repeat -10px -100px; /* 8-bit indexed alpha png. IE6 gets solid image only */
-}
-.rtl a > .sf-sub-indicator { /* give all except IE6 the correct values */
- top: .8em;
- background-position: -10px -100px; /* use translucent arrow for modern browsers*/
-}
-/* apply hovers to modern browsers */
-.rtl a:focus > .sf-sub-indicator,
-.rtl a:hover > .sf-sub-indicator,
-.rtl a:active > .sf-sub-indicator,
-.rtl li:hover > a > .sf-sub-indicator,
-.rtl li.sfHover > a > .sf-sub-indicator {
- background-position: 0 -100px; /* arrow hovers for modern browsers*/
-}
-
-/* point right for anchors in subs */
-.rtl .sf-menu ul .sf-sub-indicator { background-position: 0 0; }
-.rtl .sf-menu ul a > .sf-sub-indicator { background-position: -10px 0; }
-/* apply hovers to modern browsers */
-.rtl .sf-menu ul a:focus > .sf-sub-indicator,
-.rtl .sf-menu ul a:hover > .sf-sub-indicator,
-.rtl .sf-menu ul a:active > .sf-sub-indicator,
-.rtl .sf-menu ul li:hover > a > .sf-sub-indicator,
-.rtl .sf-menu ul li.sfHover > a > .sf-sub-indicator {
- background-position: 0 0; /* arrow hovers for modern browsers*/
-}
-
-.rtl .sf-menu li:hover ul,
-.rtl .sf-menu li.sfHover ul {
- right: 0;
- left: auto;
-}
-
-.rtl ul.sf-menu li li:hover ul,
-.rtl ul.sf-menu li li.sfHover ul {
- right: 12em; /* match ul width */
- left: auto;
-}
-.rtl ul.sf-menu li li li:hover ul,
-.rtl ul.sf-menu li li li.sfHover ul {
- right: 12em; /* match ul width */
- left: auto;
-}
-
-/*** shadows for all but IE6 ***/
-.rtl .sf-shadow ul {
- background: url('../../../lib/superfish/images/shadow.png') no-repeat bottom left;
- padding: 0 0 9px 8px;
- border-top-right-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-topright: 0;
- -moz-border-radius-bottomleft: 0;
- -webkit-border-top-right-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -moz-border-radius-topleft: 17px;
- -moz-border-radius-bottomright: 17px;
- -webkit-border-top-left-radius: 17px;
- -webkit-border-bottom-right-radius: 17px;
- border-top-left-radius: 17px;
- border-bottom-right-radius: 17px;
-}
-
-/* RTL ThemeRoller ~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-.rtl .ui-dialog .ui-dialog-titlebar {
- padding: 0.5em 1em 0.3em 0.3em;
-}
-
-.rtl .ui-dialog .ui-dialog-title {
- float: right;
-}
-
-.rtl .ui-dialog .ui-dialog-titlebar-close {
- left: 0.3em;
- right: auto;
-}
-
-.rtl #g-content #g-album-grid .g-item,
-.rtl #g-site-theme,
-.rtl #g-admin-theme,
-.rtl .g-selected img,
-.rtl .g-available .g-block img,
-.rtl #g-content #g-photo-stream .g-item,
-.rtl li.g-group,
-.rtl #g-server-add-admin {
- float: right;
-}
-
-.rtl #g-admin-graphics .g-available .g-block {
- float: right;
- margin-left: 1em;
- margin-right: 0em;
-}
-
-.rtl #g-site-admin-menu {
- left: auto;
- right: 150px;
-}
-
-.rtl #g-header #g-login-menu {
- float: left;
-}
-
-.rtl #g-header #g-login-menu li {
- margin-left: 0;
- padding-left: 0;
- padding-right: 1.2em;
-}
-
-.rtl .g-selected img,
-.rtl .g-available .g-block img {
- margin: 0 0 1em 1em;
-}
diff --git a/themes/admin_wind/theme.info b/themes/admin_wind/theme.info
index 4034b64a..466d8e43 100644
--- a/themes/admin_wind/theme.info
+++ b/themes/admin_wind/theme.info
@@ -4,3 +4,7 @@ version = 1
author = "Gallery Team"
admin = 1
site = 0
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Themes:admin_wind"
+discuss_url = "http://gallery.menalto.com/forum_theme_admin_wind"
diff --git a/themes/admin_wind/views/admin.html.php b/themes/admin_wind/views/admin.html.php
index a56b6f41..9a149149 100644
--- a/themes/admin_wind/views/admin.html.php
+++ b/themes/admin_wind/views/admin.html.php
@@ -15,6 +15,8 @@
<link rel="shortcut icon"
href="<?= url::file(module::get_var("gallery", "favicon_url")) ?>"
type="image/x-icon" />
+ <link rel="apple-touch-icon-precomposed"
+ href="<?= url::file(module::get_var("gallery", "apple_touch_icon_url")) ?>" />
<?= $theme->script("jquery.js") ?>
<?= $theme->script("jquery.form.js") ?>
@@ -27,6 +29,7 @@
<?= $theme->script("gallery.ajax.js") ?>
<?= $theme->script("gallery.dialog.js") ?>
<?= $theme->script("superfish/js/superfish.js") ?>
+ <?= $theme->script("jquery.scrollTo.js") ?>
<?= $theme->admin_head() ?>
@@ -36,8 +39,11 @@
<?= $theme->css("themeroller/ui.base.css") ?>
<?= $theme->css("superfish/css/superfish.css") ?>
<?= $theme->css("screen.css") ?>
+ <? if (locales::is_rtl()): ?>
+ <?= $theme->css("screen-rtl.css") ?>
+ <? endif; ?>
<!--[if lt IE 8]>
- <link rel="stylesheet" type="text/css" href="<?= $theme->url("fix-ie.css") ?>"
+ <link rel="stylesheet" type="text/css" href="<?= $theme->url("css/fix-ie.css") ?>"
media="screen,print,projection" />
<![endif]-->
diff --git a/themes/admin_wind/views/pager.html.php b/themes/admin_wind/views/pager.html.php
deleted file mode 100644
index 5fff5845..00000000
--- a/themes/admin_wind/views/pager.html.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php defined("SYSPATH") or die("No direct script access.") ?>
-<? // See http://docs.kohanaphp.com/libraries/pagination ?>
-<ul class="g-paginator">
- <? /* @todo This message isn't easily localizable */
- $from_to_msg = t2("Item %from_number of %count",
- "Items %from_number - %to_number of %count",
- $total_items,
- array("from_number" => $current_first_item,
- "to_number" => $current_last_item,
- "count" => $total_items)) ?>
- <li>
- <? if ($first_page): ?>
- <a href="<?= str_replace('{page}', 1, $url) ?>" class="g-button ui-icon-left ui-state-default ui-corner-all">
- <span class="ui-icon ui-icon-seek-first"></span><?= t("First") ?></a>
- <? else: ?>
- <a class="g-button ui-icon-left ui-state-disabled ui-corner-all">
- <span class="ui-icon ui-icon-seek-first"></span><?= t("First") ?></a>
- <? endif ?>
- <? if ($previous_page): ?>
- <a href="<?= str_replace('{page}', $previous_page, $url) ?>" class="g-button ui-icon-left ui-state-default ui-corner-all">
- <span class="ui-icon ui-icon-seek-prev"></span><?= t("Previous") ?></a>
- <? else: ?>
- <a class="g-button ui-icon-left ui-state-disabled ui-corner-all">
- <span class="ui-icon ui-icon-seek-prev"></span><?= t("Previous") ?></a>
- <? endif ?>
- </li>
- <li class="g-info"><?= $from_to_msg ?></li>
- <li class="g-text-right">
- <? if ($next_page): ?>
- <a href="<?= str_replace('{page}', $next_page, $url) ?>" class="g-button ui-icon-right ui-state-default ui-corner-all">
- <span class="ui-icon ui-icon-seek-next"></span><?= t("Next") ?></a>
- <? else: ?>
- <a class="g-button ui-state-disabled ui-icon-right ui-corner-all">
- <span class="ui-icon ui-icon-seek-next"></span><?= t("Next") ?></a>
- <? endif ?>
- <? if ($last_page): ?>
- <a href="<?= str_replace('{page}', $last_page, $url) ?>" class="g-button ui-icon-right ui-state-default ui-corner-all">
- <span class="ui-icon ui-icon-seek-end"></span><?= t("Last") ?></a>
- <? else: ?>
- <a class="g-button ui-state-disabled ui-icon-right ui-corner-all">
- <span class="ui-icon ui-icon-seek-end"></span><?= t("Last") ?></a>
- <? endif ?>
- </li>
-</ul>
diff --git a/themes/admin_wind/views/paginator.html.php b/themes/admin_wind/views/paginator.html.php
new file mode 100644
index 00000000..b46d9741
--- /dev/null
+++ b/themes/admin_wind/views/paginator.html.php
@@ -0,0 +1,88 @@
+<?php defined("SYSPATH") or die("No direct script access.") ?>
+<?
+// This is a generic paginator for admin collections. Depending on the page type, there are
+// different sets of variables available. With this data, you can make a paginator that
+// lets you say "You're viewing photo 5 of 35", or "You're viewing photos 10 - 18 of 37"
+// for album views.
+
+//
+// Available variables for all page types:
+// $page_type - "collection", "item", or "other"
+// $page_subtype - "album", "movie", "photo", "tag", etc.
+// $previous_page_url - the url to the previous page, if there is one
+// $next_page_url - the url to the next page, if there is one
+// $total - the total number of photos in this album
+//
+// Available for the "collection" page types:
+// $page - what page number we're on
+// $max_pages - the maximum page number
+// $page_size - the page size
+// $first_page_url - the url to the first page, or null if we're on the first page
+// $last_page_url - the url to the last page, or null if we're on the last page
+// $first_visible_position - the position number of the first visible photo on this page
+// $last_visible_position - the position number of the last visible photo on this page
+//
+// Available for "item" page types:
+// $position - the position number of this photo
+//
+?>
+
+<? if ($total): ?>
+<ul class="g-paginator ui-helper-clearfix">
+ <li class="g-first">
+ <? if ($page_type == "collection"): ?>
+ <? if (isset($first_page_url)): ?>
+ <a href="<?= $first_page_url ?>" class="g-button ui-icon-left ui-state-default ui-corner-all">
+ <span class="ui-icon ui-icon-seek-first"></span><?= t("First") ?></a>
+ <? else: ?>
+ <a class="g-button ui-icon-left ui-state-disabled ui-corner-all">
+ <span class="ui-icon ui-icon-seek-first"></span><?= t("First") ?></a>
+ <? endif ?>
+ <? endif ?>
+
+ <? if (isset($previous_page_url)): ?>
+ <a href="<?= $previous_page_url ?>" class="g-button ui-icon-left ui-state-default ui-corner-all">
+ <span class="ui-icon ui-icon-seek-prev"></span><?= t("Previous") ?></a>
+ <? else: ?>
+ <a class="g-button ui-icon-left ui-state-disabled ui-corner-all">
+ <span class="ui-icon ui-icon-seek-prev"></span><?= t("Previous") ?></a>
+ <? endif ?>
+ </li>
+
+ <li class="g-info">
+ <? if ($total): ?>
+ <? if ($page_type == "collection"): ?>
+ <?= /* @todo This message isn't easily localizable */
+ t2("Viewing %from_number of %count",
+ "Viewing %from_number - %to_number of %count",
+ $total,
+ array("from_number" => $first_visible_position,
+ "to_number" => $last_visible_position,
+ "count" => $total)) ?>
+ <? else: ?>
+ <?= t("%position of %total", array("position" => $position, "total" => $total)) ?>
+ <? endif ?>
+ <? endif ?>
+ </li>
+
+ <li class="g-text-right">
+ <? if (isset($next_page_url)): ?>
+ <a href="<?= $next_page_url ?>" class="g-button ui-icon-right ui-state-default ui-corner-all">
+ <span class="ui-icon ui-icon-seek-next"></span><?= t("Next") ?></a>
+ <? else: ?>
+ <a class="g-button ui-state-disabled ui-icon-right ui-corner-all">
+ <span class="ui-icon ui-icon-seek-next"></span><?= t("Next") ?></a>
+ <? endif ?>
+
+ <? if ($page_type == "collection"): ?>
+ <? if (isset($last_page_url)): ?>
+ <a href="<?= $last_page_url ?>" class="g-button ui-icon-right ui-state-default ui-corner-all">
+ <span class="ui-icon ui-icon-seek-end"></span><?= t("Last") ?></a>
+ <? else: ?>
+ <a class="g-button ui-state-disabled ui-icon-right ui-corner-all">
+ <span class="ui-icon ui-icon-seek-end"></span><?= t("Last") ?></a>
+ <? endif ?>
+ <? endif ?>
+ </li>
+</ul>
+<? endif ?> \ No newline at end of file
diff --git a/themes/wind/css/screen-rtl.css b/themes/wind/css/screen-rtl.css
new file mode 100644
index 00000000..e79f2c06
--- /dev/null
+++ b/themes/wind/css/screen-rtl.css
@@ -0,0 +1,333 @@
+/**
+ * Gallery 3 Wind Theme Right-to-Left Screen Styles
+ */
+
+.rtl {
+ direction: rtl;
+}
+
+#g-header,
+#g-content,
+#g-sidebar,
+#g-footer,
+caption,
+th,
+#g-dialog,
+.g-context-menu li a,
+.g-message-box li,
+#g-site-status li {
+ text-align: right;
+}
+
+.g-text-right {
+ text-align: left;
+}
+
+/* Lists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-text li,
+.g-text li {
+ margin-left: 0;
+ margin-right: 1em;
+}
+
+/* Messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-error,
+.g-info,
+.g-success,
+.g-warning,
+#g-add-photos-status .g-success,
+#g-add-photos-status .g-error {
+ background-position: center right;
+ padding-right: 30px !important;
+}
+
+form li.g-error,
+form li.g-info,
+form li.g-success,
+form li.g-warning {
+ padding-right: 0 !important;
+}
+
+.g-left,
+.g-inline li,
+#g-content #g-album-grid .g-item,
+.sf-menu li,
+.g-breadcrumbs li,
+.g-paginator li,
+.g-buttonset li,
+.ui-icon-left .ui-icon,
+.g-short-form li,
+form ul ul li,
+input[type="submit"],
+input[type="reset"],
+input.checkbox,
+input[type=checkbox],
+input.radio,
+input[type=radio] {
+ float: right;
+}
+
+.g-right,
+.ui-icon-right .ui-icon {
+ float: left;
+}
+
+.g-inline li {
+ margin-right: 1em;
+}
+
+.g-inline li.g-first {
+ margin-right: 0;
+}
+
+.g-breadcrumbs li {
+ background: transparent url('../images/ico-separator-rtl.gif') no-repeat scroll right center;
+ padding: 1em 18px 1em 8px;
+}
+
+.g-breadcrumbs .g-first {
+ background: none;
+ padding-right: 0;
+}
+
+input.checkbox {
+ margin-left: .4em;
+}
+
+#g-add-comment {
+ right: inherit;
+ left: 0;
+}
+
+.ui-icon-left .ui-icon {
+ margin-left: .2em;
+}
+
+.ui-icon-right .ui-icon {
+ margin-right: .2em;
+}
+
+/* RTL Corner radius ~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-buttonset .ui-corner-tl {
+ -moz-border-radius-topleft: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topright: 5px !important;
+ -webkit-border-top-right-radius: 5px !important;
+ border-top-right-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-tr {
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topleft: 5px !important;
+ -webkit-border-top-left-radius: 5px !important;
+ border-top-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-bl {
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 5px !important;
+ -webkit-border-bottom-right-radius: 5px !important;
+ border-bottom-right-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-br {
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomleft: 5px !important;
+ -webkit-border-bottom-left-radius: 5px !important;
+ border-bottom-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-right,
+.ui-progressbar .ui-corner-right {
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topleft: 5px !important;
+ -webkit-border-top-left-radius: 5px !important;
+ border-top-left-radius: 5px !important;
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomleft: 5px !important;
+ -webkit-border-bottom-left-radius: 5px !important;
+ border-bottom-left-radius: 5px !important;
+}
+
+.g-buttonset .ui-corner-left,
+.ui-progressbar .ui-corner-left {
+ -moz-border-radius-topleft: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topright: 5px !important;
+ -webkit-border-top-right-radius: 5px !important;
+ border-top-right-radius: 5px !important;
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomright: 5px !important;
+ -webkit-border-bottom-right-radius: 5px !important;
+ border-bottom-right-radius: 5px !important;
+}
+
+/* RTL Superfish ~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.sf-menu a {
+ border-left: none;
+ border-right:1px solid #fff;
+}
+
+.sf-menu a.sf-with-ul {
+ padding-left: 2.25em;
+ padding-right: 1em;
+}
+
+.sf-sub-indicator {
+ left: .75em !important;
+ right: auto;
+ background: url('../../../lib/superfish/images/arrows-ffffff-rtl.png') no-repeat -10px -100px;
+}
+
+a > .sf-sub-indicator {
+ top: .8em;
+ background-position: -10px -100px;
+}
+
+a:focus > .sf-sub-indicator,
+a:hover > .sf-sub-indicator,
+a:active > .sf-sub-indicator,
+li:hover > a > .sf-sub-indicator,
+li.sfHover > a > .sf-sub-indicator {
+ background-position: 0 -100px;
+}
+
+.sf-menu ul .sf-sub-indicator {
+ background-position: 0 0;
+}
+
+.sf-menu ul a > .sf-sub-indicator {
+ background-position: -10px 0;
+}
+
+.sf-menu ul a:focus > .sf-sub-indicator,
+.sf-menu ul a:hover > .sf-sub-indicator,
+.sf-menu ul a:active > .sf-sub-indicator,
+.sf-menu ul li:hover > a > .sf-sub-indicator,
+.sf-menu ul li.sfHover > a > .sf-sub-indicator {
+ background-position: 0 0;
+}
+
+.sf-menu li:hover ul,
+.sf-menu li.sfHover ul {
+ right: 0;
+ left: auto;
+}
+
+ul.sf-menu li li:hover ul,
+ul.sf-menu li li.sfHover ul {
+ right: 12em;
+ left: auto;
+}
+ul.sf-menu li li li:hover ul,
+ul.sf-menu li li li.sfHover ul {
+ right: 12em;
+ left: auto;
+}
+
+.sf-shadow ul {
+ background: url('../../../lib/superfish/images/shadow.png') no-repeat bottom left;
+ padding: 0 0 9px 8px;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomleft: 0;
+ -webkit-border-top-right-radius: 0;
+ -webkit-border-bottom-left-radius: 0;
+ -moz-border-radius-topleft: 17px;
+ -moz-border-radius-bottomright: 17px;
+ -webkit-border-top-left-radius: 17px;
+ -webkit-border-bottom-right-radius: 17px;
+ border-top-left-radius: 17px;
+ border-bottom-right-radius: 17px;
+}
+
+/* RTL ThemeRoller ~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.ui-dialog .ui-dialog-titlebar {
+ padding: 0.5em 1em 0.3em 0.3em;
+}
+
+.ui-dialog .ui-dialog-title {
+ float: right;
+}
+
+.ui-dialog .ui-dialog-titlebar-close {
+ left: 0.3em;
+ right: auto;
+}
+
+/* RTL paginator ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+.g-paginator .g-info {
+ width: 35%;
+}
+
+.g-paginator .g-text-right {
+ margin-left: 0;
+}
+
+.g-paginator .ui-icon-seek-end {
+ background-position: -80px -160px;
+}
+
+.g-paginator .ui-icon-seek-next {
+ background-position: -48px -160px;
+}
+
+.g-paginator .ui-icon-seek-prev {
+ background-position: -32px -160px;
+}
+
+.g-paginator .ui-icon-seek-first {
+ background-position: -64px -160px;
+}
+
+#g-header #g-login-menu,
+#g-header #g-quick-search-form {
+ clear: left;
+ float: left;
+}
+
+#g-header #g-login-menu li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-right: 1.2em;
+}
+
+#g-site-menu {
+ left: auto;
+ right: 150px;
+}
+
+#g-view-menu #g-slideshow-link {
+ background-image: url('../images/ico-view-slideshow-rtl.png');
+}
+
+#g-sidebar .g-block-content {
+ padding-right: 1em;
+ padding-left: 0;
+}
+
+#g-footer #g-credits li {
+ padding-left: 1.2em !important;
+ padding-right: 0;
+}
diff --git a/themes/wind/css/screen.css b/themes/wind/css/screen.css
index a49b8c28..d3e0f83c 100644
--- a/themes/wind/css/screen.css
+++ b/themes/wind/css/screen.css
@@ -12,7 +12,6 @@
* 6) Positioning and order
* 7) Navigation and menus
* 8) jQuery and jQuery UI
- * 9) Right-to-left language styles
*/
/** *******************************************************************
@@ -106,12 +105,30 @@ a:hover,
}
#g-dialog #g-action-status li {
- width: 400px;
+ width: 434px;
white-space: normal;
padding-left: 32px;
}
+/* Lists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+ul.g-text li,
+.g-text ul li {
+ list-style-type: disc;
+}
+
+ol.g-text li,
+.g-text ol li {
+ list-style-type: decimal;
+}
+
+.g-text li,
+.g-text li {
+ margin-left: 1em;
+}
+
/* Forms ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
form {
margin: 0;
}
@@ -302,6 +319,7 @@ td {
}
/* Text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
.g-text-small {
font-size: .8em;
}
@@ -332,6 +350,7 @@ td {
*********************************************************************/
/* Dimension and scale ~~~~~~~~~~~~~~~~~~~ */
+
.g-one-quarter {
width: 25%;
}
@@ -523,7 +542,8 @@ td {
background-color: #fff;
}
-/* Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
#g-edit-permissions-form td {
background-image: none;
}
@@ -635,11 +655,11 @@ tr.g-error td.g-error,
}
tr.g-success {
- background-image: none;
+ background-image: none;
}
tr.g-success td.g-success {
- background-image: url('../images/ico-success.png');
+ background-image: url('../images/ico-success.png');
}
.g-warning,
@@ -823,6 +843,7 @@ form .g-error {
/** *******************************************************************
* 8) jQuery and jQuery UI
*********************************************************************/
+
/* Generic block container ~~~~~~~~~~~~~~~ */
.g-block {
@@ -834,18 +855,19 @@ form .g-error {
}
/* Superfish menu overrides ~~~~~~~~~~~~~~ */
+
.sf-menu ul {
- width: 12em;
+ width: 12em;
}
ul.sf-menu li li:hover ul,
ul.sf-menu li li.sfHover ul {
- left: 12em;
+ left: 12em;
}
ul.sf-menu li li li:hover ul,
ul.sf-menu li li li.sfHover ul {
- left: 12em;
+ left: 12em;
}
.sf-menu li li,
@@ -922,7 +944,7 @@ button {
.g-progress-bar {
height: 1em;
width: 100%;
- margin-top: .5em;
+ margin: .5em 0;
display: inline-block;
}
@@ -1037,326 +1059,7 @@ div#g-action-status {
}
/* Autocomplete ~~~~~~~~~~ */
+
.ac_loading {
background: white url('../images/loading-small.gif') right center no-repeat !important;
}
-
-/** *******************************************************************
- * 9) Right to left language styles
- *********************************************************************/
-
-.rtl {
- direction: rtl;
-}
-
-.rtl #g-header,
-.rtl #g-content,
-.rtl #g-sidebar,
-.rtl #g-footer,
-.rtl caption,
-.rtl th,
-.rtl #g-dialog,
-.rtl .g-context-menu li a,
-.rtl .g-message-box li,
-.rtl #g-site-status li {
- text-align: right;
-}
-
-.rtl .g-text-right {
- text-align: left;
-}
-
-.rtl .g-error,
-.rtl .g-info,
-.rtl .g-success,
-.rtl .g-warning,
-.rtl #g-add-photos-status .g-success,
-.rtl #g-add-photos-status .g-error {
- background-position: center right;
- padding-right: 30px !important;
-}
-
-.rtl form li.g-error,
-.rtl form li.g-info,
-.rtl form li.g-success,
-.rtl form li.g-warning {
- padding-right: 0 !important;
-}
-
-.rtl .g-left,
-.rtl .g-inline li,
-.rtl #g-content #g-album-grid .g-item,
-.rtl .sf-menu li,
-.rtl .g-breadcrumbs li,
-.rtl .g-paginator li,
-.rtl .g-buttonset li,
-.rtl .ui-icon-left .ui-icon,
-.rtl .g-short-form li,
-.rtl form ul ul li,
-.rtl input[type="submit"],
-.rtl input[type="reset"],
-.rtl input.checkbox,
-.rtl input[type=checkbox],
-.rtl input.radio,
-.rtl input[type=radio] {
- float: right;
-}
-
-.rtl .g-right,
-.rtl .ui-icon-right .ui-icon {
- float: left;
-}
-
-.rtl .g-inline li {
- margin-right: 1em;
-}
-
-.rtl .g-inline li.g-first {
- margin-right: 0;
-}
-
-.rtl .g-breadcrumbs li {
- background: transparent url('../images/ico-separator-rtl.gif') no-repeat scroll right center;
- padding: 1em 18px 1em 8px;
-}
-
-.rtl .g-breadcrumbs .g-first {
- background: none;
- padding-right: 0;
-}
-
-.rtl input.checkbox {
- margin-left: .4em;
-}
-
-.rtl #g-add-comment {
- right: inherit;
- left: 0;
-}
-
-.rtl .ui-icon-left .ui-icon {
- margin-left: .2em;
-}
-
-.rtl .ui-icon-right .ui-icon {
- margin-right: .2em;
-}
-
-/* RTL Corner radius ~~~~~~~~~~~~~~~~~~~~~~ */
-.rtl .g-buttonset .ui-corner-tl {
- -moz-border-radius-topleft: 0;
- -webkit-border-top-left-radius: 0;
- border-top-left-radius: 0;
- -moz-border-radius-topright: 5px !important;
- -webkit-border-top-right-radius: 5px !important;
- border-top-right-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-tr {
- -moz-border-radius-topright: 0;
- -webkit-border-top-right-radius: 0;
- border-top-right-radius: 0;
- -moz-border-radius-topleft: 5px !important;
- -webkit-border-top-left-radius: 5px !important;
- border-top-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-bl {
- -moz-border-radius-bottomleft: 0;
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 5px !important;
- -webkit-border-bottom-right-radius: 5px !important;
- border-bottom-right-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-br {
- -moz-border-radius-bottomright: 0;
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft: 5px !important;
- -webkit-border-bottom-left-radius: 5px !important;
- border-bottom-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-right,
-.rtl .ui-progressbar .ui-corner-right {
- -moz-border-radius-topright: 0;
- -webkit-border-top-right-radius: 0;
- border-top-right-radius: 0;
- -moz-border-radius-topleft: 5px !important;
- -webkit-border-top-left-radius: 5px !important;
- border-top-left-radius: 5px !important;
- -moz-border-radius-bottomright: 0;
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft: 5px !important;
- -webkit-border-bottom-left-radius: 5px !important;
- border-bottom-left-radius: 5px !important;
-}
-
-.rtl .g-buttonset .ui-corner-left,
-.rtl .ui-progressbar .ui-corner-left {
- -moz-border-radius-topleft: 0;
- -webkit-border-top-left-radius: 0;
- border-top-left-radius: 0;
- -moz-border-radius-topright: 5px !important;
- -webkit-border-top-right-radius: 5px !important;
- border-top-right-radius: 5px !important;
- -moz-border-radius-bottomleft: 0;
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 5px !important;
- -webkit-border-bottom-right-radius: 5px !important;
- border-bottom-right-radius: 5px !important;
-}
-
-/* RTL Superfish ~~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-.rtl .sf-menu a {
- border-left: none;
- border-right:1px solid #fff;
-}
-
-.rtl .sf-menu a.sf-with-ul {
- padding-left: 2.25em;
- padding-right: 1em;
-}
-
-.rtl .sf-sub-indicator {
- left: .75em !important;
- right: auto;
- background: url('../../../lib/superfish/images/arrows-ffffff-rtl.png') no-repeat -10px -100px; /* 8-bit indexed alpha png. IE6 gets solid image only */
-}
-.rtl a > .sf-sub-indicator { /* give all except IE6 the correct values */
- top: .8em;
- background-position: -10px -100px; /* use translucent arrow for modern browsers*/
-}
-/* apply hovers to modern browsers */
-.rtl a:focus > .sf-sub-indicator,
-.rtl a:hover > .sf-sub-indicator,
-.rtl a:active > .sf-sub-indicator,
-.rtl li:hover > a > .sf-sub-indicator,
-.rtl li.sfHover > a > .sf-sub-indicator {
- background-position: 0 -100px; /* arrow hovers for modern browsers*/
-}
-
-/* point right for anchors in subs */
-.rtl .sf-menu ul .sf-sub-indicator { background-position: 0 0; }
-.rtl .sf-menu ul a > .sf-sub-indicator { background-position: -10px 0; }
-/* apply hovers to modern browsers */
-.rtl .sf-menu ul a:focus > .sf-sub-indicator,
-.rtl .sf-menu ul a:hover > .sf-sub-indicator,
-.rtl .sf-menu ul a:active > .sf-sub-indicator,
-.rtl .sf-menu ul li:hover > a > .sf-sub-indicator,
-.rtl .sf-menu ul li.sfHover > a > .sf-sub-indicator {
- background-position: 0 0; /* arrow hovers for modern browsers*/
-}
-
-.rtl .sf-menu li:hover ul,
-.rtl .sf-menu li.sfHover ul {
- right: 0;
- left: auto;
-}
-
-.rtl ul.sf-menu li li:hover ul,
-.rtl ul.sf-menu li li.sfHover ul {
- right: 12em; /* match ul width */
- left: auto;
-}
-.rtl ul.sf-menu li li li:hover ul,
-.rtl ul.sf-menu li li li.sfHover ul {
- right: 12em; /* match ul width */
- left: auto;
-}
-
-/*** shadows for all but IE6 ***/
-.rtl .sf-shadow ul {
- background: url('../../../lib/superfish/images/shadow.png') no-repeat bottom left;
- padding: 0 0 9px 8px;
- border-top-right-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-topright: 0;
- -moz-border-radius-bottomleft: 0;
- -webkit-border-top-right-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -moz-border-radius-topleft: 17px;
- -moz-border-radius-bottomright: 17px;
- -webkit-border-top-left-radius: 17px;
- -webkit-border-bottom-right-radius: 17px;
- border-top-left-radius: 17px;
- border-bottom-right-radius: 17px;
-}
-
-/* RTL ThemeRoller ~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-.rtl .ui-dialog .ui-dialog-titlebar {
- padding: 0.5em 1em 0.3em 0.3em;
-}
-
-.rtl .ui-dialog .ui-dialog-title {
- float: right;
-}
-
-.rtl .ui-dialog .ui-dialog-titlebar-close {
- left: 0.3em;
- right: auto;
-}
-
-
-/* RTL paginator ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-
-.rtl .g-paginator .g-info {
- width: 35%;
-}
-
-.rtl .g-paginator .g-text-right {
- margin-left: 0;
-}
-
-.rtl .g-paginator .ui-icon-seek-end {
- background-position: -80px -160px;
-}
-
-.rtl .g-paginator .ui-icon-seek-next {
- background-position: -48px -160px;
-}
-
-.rtl .g-paginator .ui-icon-seek-prev {
- background-position: -32px -160px;
-}
-
-.rtl .g-paginator .ui-icon-seek-first {
- background-position: -64px -160px;
-}
-
-.rtl #g-header #g-login-menu,
-.rtl #g-header #g-quick-search-form {
- clear: left;
- float: left;
-}
-
-.rtl #g-header #g-login-menu li {
- margin-left: 0;
- padding-left: 0;
- padding-right: 1.2em;
-}
-
-.rtl #g-site-menu {
- left: auto;
- right: 150px;
-}
-
-.rtl #g-view-menu #g-slideshow-link {
- background-image: url('../images/ico-view-slideshow-rtl.png');
-}
-
-.rtl #g-sidebar .g-block-content {
- padding-right: 1em;
- padding-left: 0;
-}
-
-.rtl #g-footer #g-credits li {
- padding-left: 1.2em !important;
- padding-right: 0;
-}
diff --git a/themes/wind/js/ui.init.js b/themes/wind/js/ui.init.js
index 2c67bf3a..3ee3e32e 100644
--- a/themes/wind/js/ui.init.js
+++ b/themes/wind/js/ui.init.js
@@ -82,7 +82,7 @@ $(document).ready(function() {
} else {
var sib_height = $(this).prev().height();
}
- if ($.browser.msie && $.browser.version >= 8) {
+ if ($.browser.msie && $.browser.version <= 8) {
sib_height = sib_height + 1;
}
$(this).css("height", sib_height);
diff --git a/themes/wind/theme.info b/themes/wind/theme.info
index 17ea7c20..e0be78b9 100644
--- a/themes/wind/theme.info
+++ b/themes/wind/theme.info
@@ -4,3 +4,7 @@ version = 1
author = "Gallery Team"
site = 1
admin = 0
+author_name = "Gallery Team"
+author_url = "http://codex.gallery2.org/Gallery:Team"
+info_url = "http://codex.gallery2.org/Gallery3:Themes:wind"
+discuss_url = "http://gallery.menalto.com/forum_theme_wind"
diff --git a/themes/wind/views/page.html.php b/themes/wind/views/page.html.php
index 7462f864..ca6d2bb1 100644
--- a/themes/wind/views/page.html.php
+++ b/themes/wind/views/page.html.php
@@ -21,7 +21,8 @@
<link rel="shortcut icon"
href="<?= url::file(module::get_var("gallery", "favicon_url")) ?>"
type="image/x-icon" />
-
+ <link rel="apple-touch-icon-precomposed"
+ href="<?= url::file(module::get_var("gallery", "apple_touch_icon_url")) ?>" />
<? if ($theme->page_type == "collection"): ?>
<? if (($thumb_proportion = $theme->thumb_proportion($theme->item())) != 1): ?>
<? $new_width = round($thumb_proportion * 213) ?>
@@ -66,6 +67,9 @@
<?= $theme->css("superfish/css/superfish.css") ?>
<?= $theme->css("themeroller/ui.base.css") ?>
<?= $theme->css("screen.css") ?>
+ <? if (locales::is_rtl()): ?>
+ <?= $theme->css("screen-rtl.css") ?>
+ <? endif; ?>
<!--[if lte IE 8]>
<link rel="stylesheet" type="text/css" href="<?= $theme->url("css/fix-ie.css") ?>"
media="screen,print,projection" />
@@ -103,28 +107,15 @@
<?= $theme->header_bottom() ?>
</div>
- <? if ($theme->item() && !empty($parents)): ?>
+ <? if (!empty($breadcrumbs)): ?>
<ul class="g-breadcrumbs">
- <? $i = 0 ?>
- <? foreach ($parents as $parent): ?>
- <li<? if ($i == 0) print " class=\"g-first\"" ?>>
- <? // Adding ?show=<id> causes Gallery3 to display the page
- // containing that photo. For now, we just do it for
- // the immediate parent so that when you go back up a
- // level you're on the right page. ?>
- <a href="<?= $parent->url($parent->id == $theme->item()->parent_id ?
- "show={$theme->item()->id}" : null) ?>">
- <? // limit the title length to something reasonable (defaults to 15) ?>
- <?= html::purify(text::limit_chars($parent->title,
- module::get_var("gallery", "visible_title_length"))) ?>
- </a>
- </li>
- <? $i++ ?>
+ <? foreach ($breadcrumbs as $breadcrumb): ?>
+ <li class="<?= $breadcrumb->last ? "g-active" : "" ?>
+ <?= $breadcrumb->first ? "g-first" : "" ?>">
+ <? if (!$breadcrumb->last): ?> <a href="<?= $breadcrumb->url ?>"><? endif ?>
+ <?= html::purify(text::limit_chars($breadcrumb->title, module::get_var("gallery", "visible_title_length"))) ?>
+ <? if (!$breadcrumb->last): ?></a><? endif ?>
<? endforeach ?>
- <li class="g-active<? if ($i == 0) print " g-first" ?>">
- <?= html::purify(text::limit_chars($theme->item()->title,
- module::get_var("gallery", "visible_title_length"))) ?>
- </li>
</ul>
<? endif ?>
</div>