Hackers or Gremlins

Happy New Year! Well, 2012 has begun with a bang. Shortly after noon on January 2 all my Gallery 2 data was trashed unexpectedly. Now, this is either hackers, or someone doing something on my hosted web service site, or some unseen glitch in the Gallery 2 software.

This must be a sign that I need to spend some time to upgrade to Gallery 3. I has been planning to rebuild my Gallery implementation anyway, so this is not necessarily all bad. I want to centralize on a standard maximum image size and reload the albumns.

It’s time to do this. Please be patient as this work begins. Page caching is off for the moment so performance may suffer.

 

January 3, 2012: Gallery 3 is not installing. It fails with a 500 server error code attempting to run the install script. The Apache version on my hosting service is version 2.0.52 and Gallery 3 requires Apache 2.2. This may be the issue.

Gallery 2 is being reinstalled. Let’s just think of this as a major engine overhall.

 

January 4, 2012: I found this in the Gallery2 security section while doing some research yesterday.

A shared webserver that runs all PHP scripts under the same generic user and not under the specific account’s user can only be secured in a limited way.

Reality check: Most shared webhosting plans fall into the above category and we hear from incidents like a Gallery 2 that was somehow deleted over night only very rarely, maybe once a year. So it isn’t as bad as it sounds. The chance being on a webhost with a malicious customer should be really small.

 

January 6, 2012: All the China photos, Utah photos, and PKking resin model construction photos are restored. One benefit of this work is that all images now contain EXIF information and will have consistent display sizes. One liability of this restoration is that all descriptions and keyword tags for the images have been lost. I will try to recover descriptive inforormation for the gallery folders.

 

January 8, 2012: Resin model photographs are being restored. Please be patient.

 

Gallery2 MiniSlideShow Performance Cache

MiniSlideShow is an interesting Flash plugin to display Gallery2 images on your website. It uses an XML RSS feed to access Gallery2 albums. It also uses Gallery2 to return the correct size image resolution for embedded slideshows. MiniSlideShow is being used in the sidebars on Kisekae World.

One problem with this solution is that generating an RSS feed takes time. The generation delay impacts the start of the slideshow, particularly if the slideshow is included as a sidebar block on every Gallery page or embedded application page. This can be particularly noticable if the feed contains many images. Each display of a new page results is a new invokation of the slideshow and this results in a new initialization of the RSS feed.

One way to mitigate the feed generation performance loss is to cache the generated RSS feed XML text. For example, if the album feed has been previously generated and saved then it can be referenced on a new page load without the overhead of regenerating the RSS feed. This can provide some performance relief.

Gallery2 provides disk caching services as part of its core module. I have modified the xml() function in mediaRSS.php distributed with MiniSlideShow to use the Gallery2 cache functions. The code assumes that a new ‘minislideshow’ module has been installed as a Gallery2 module as described further below. For caching, the Gallery2 itemId is used as the cache key, and each cached RSS text entity will expire after 3600 seconds (1 hour). All new lines inserted into the function are shown in a different color.

 

mediaRss.php

<?php
// +---------------------------------------------------------------------------+
// |  E2  XML Audio/Video Player/Minislideshow for Gallery2                    |
// +---------------------------------------------------------------------------+
// | mediaRss.php     [v.3.0.0]                                                |
// +---------------------------------------------------------------------------+
// | Copyright (C) 2009 Wayne Patterson [suprsidr@flashyourweb.com]            |
// +---------------------------------------------------------------------------+
// |                                                                           |
// | 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           |
// |                                                                           |
// +---------------------------------------------------------------------------+
//

.
. [snipped code]
.

    function xml() {
	init();
	global $gallery, $userId, $count;


	// Determine cache item	
	if (isset ($_REQUEST['g2_itemId'])) {
	    $cacheId = $_REQUEST['g2_itemId'];
	} else if (isset ($_REQUEST['g2_albumId'])) {
	    $cacheId = $_REQUEST['g2_albumId'];
	} else {
		$cacheId = getRoot() ;
	}
	
	// Retrieve cache item if not expired	
	if (isset($cacheId)) {
		$expire = 3600;		   
		$cacheMsg = "n";
		$cachePathInfo = array('type' => 'module-data',
			   'module' => 'minislideshow',
			   'itemId' => $cacheId);
		list ($xmltime, $xmldata) =
			GalleryDataCache::getFromDisk($cachePathInfo);
		if (isset($xmltime) && isset($xmldata)) {
			$vm = $gallery->getPhpVm();
			$cacbeTime = date('U', $vm->time());
			$diff = (int)$cacbeTime - (int)$xmltime;
			$expiretime = (int)$xmltime + $expire;
			if ($diff < $expire) {
				$xmldata .= "nRetrieved itemId " . $cacheId . " at " . $cacbeTime . " that was cached at " . $xmltime 
					. " and will expire at " . $expiretime;
				echo $xmldata;
				return;
			} else {
				$cacheMsg .= "Cache expired, item cached at " . $xmltime . ", current time " . $cacbeTime 
					. ", item expired at " . $expiretime . "n" ;
			}
		} else {
			$cacheMsg .= "Cache not found, itemId " . $cacheId . "n";
		}
	}

	
	$title = '';
	$recursive = '';
	if (!$userId) {
	    $userId = $gallery->getActiveUserId();
	}
	if (!$userId) {
	    list($ret,$userId) = GalleryCoreApi::getAnonymousUserId();
	}
	if (isset ($_REQUEST['mode'])) {
	    $mode = $_REQUEST['mode'];
	}else{
		$mode = '';
	}
	if (isset ($_REQUEST['g2_itemId'])) {
	    $g2_itemId = $_REQUEST['g2_itemId'];
	    list ($ret, $item) = GalleryCoreApi::loadEntitiesById($g2_itemId, 'GalleryAlbumItem');
	    if ($ret) {
	        print "Error loading initial item:" . $ret->getAsHtml();
	    }
	    $title = getTitle($item);
	}else{
	    $title = "Gallery2 MediaRss";
	}
	if (isset ($_REQUEST['g2_view'])) {
	    $g2_view = $_REQUEST['g2_view'];
	}
	if (isset ($_REQUEST['mime'])) {
	    $mime = $_REQUEST['mime'];
	}
	if (isset ($_REQUEST['recursive'])) {
	    $recursive = $_REQUEST['recursive'];
	}
	if(isset ($_REQUEST['limit'])){
		global $limit;
		$limit = $_REQUEST['limit'];
	}
	$xml = '';
	$count = 0;
	$urlGenerator =& $gallery->getUrlGenerator();
	$link = $urlGenerator->generateUrl(array(), array('forceFullUrl' => true));
	$vm = $gallery->getPhpVm();
	list ($ret, $language) = GalleryTranslator::getDefaultLanguageCode( );
	if ($ret) {
	    $language = "en-us";
	}
	if (!$vm->headers_sent()) {
	    $vm->header('Content-Type: application/rss+xml; charset=UTF-8');
	}
	echo "<?xml version="1.0" encoding="UTF-8" ?>n";	
	$xml .= "<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">n";
	$xml .= "    <channel>n";
	$xml .= "        <title>" . cdata($title) . "</title>n";
	$xml .= "        <link>" . $link . "</link>n";
	$xml .= "        <description>" . cdata($title) . "</description>n";
	$xml .= "        <language>" .$language. "</language>n";
	$xml .= "        <generator>FlashYourWeb MediaRSS Generator v3.0.0</generator>n";
	$xml .= "        <lastBuildDate>" . date('r', $vm->time()) . "</lastBuildDate>n";
	$xml .= "        <ttl>120</ttl>n";
	if(isset($g2_itemId)){
		$xml .= getAlbumList ($g2_itemId);
	}else{
		$xml .= getAlbumList (getRoot());
	}
	switch ($mode) {
	    case 'dynamic':
	        switch ($g2_view) {
	            case 'dynamicalbum.UpdatesAlbum':
	                $xml .= getDynamicChildIds($userId);
	            break;
	            case 'dynamicalbum.PopularAlbum':
	                $xml .= getDynamicChildIds($userId, 'views', 'viewCount', ORDER_DESCENDING, 'GalleryItemAttributesMap', 'itemId');
	            break;
	            case 'dynamicalbum.RandomAlbum':
	                $xml .= getDynamicChildIds($userId, 'random', 'random', ORDER_ASCENDING, null, 'id');
	            break;
	            case 'keyalbum.KeywordAlbum':
	            	$xml .= getKeywordChildIds($userId, $g2_keyword=null);
	            break;
	            case 'tags.VirtualAlbum':
	                $xml .= getTagChildIds($userId, $g2_tagName=null);
	            break;
	            default:
	            $xml .= getDynamicChildIds($userId);
	        }
	    break;
	    case 'search':
	        $xml .= getSearchItems($g2_itemId, $mime);
	    break;
	    case 'simple':
	        $xml .= itemListDisplay();
	    break;
	    default:
	        if(isset($g2_itemId) && $recursive){
	            $xml .= getItemsRecursive ($g2_itemId);
	        }else if(isset($g2_itemId)){
	            $xml .= getItems($g2_itemId);
	        }else{
	            $xml .= getItems(getRoot());
	        }
	}
	
	$xml .= "    </channel>n";
	$xml .= "</rss>";


	// Cache item for future access	
	if (isset($cacheId)) {
		$cachePath = GalleryDataCache::getCachePath($cachePathInfo);
		if (isset($cachePath)) {
			$xmltime = date('U', $vm->time());
			GalleryDataCache::putToDisk($cachePathInfo, $dataToCache = array($xmltime, $xml));
			$xml .= $cacheMsg;	
			$xml .= "Cache itemId " . $cacheId . " at " . $xmltime . " to " . $cachePath;
		}
	}


	echo $xml;
    }
xml();
?>

 

For the above solution to work there must be a ‘/g2data/cache/module/minislideshow’ directory on your web server. This is where Gallery2 stores the cached files. If this directory does not exist or disk caching is not possible then the cache modifications will fail silently. To determine if caching is functioning you need to examine the source code for the XML feed. Cache message text is inserted in the document following the RSS directives. The following three feeds below will show cache information if you use your browser to display the source text.

Example:

Cache expired, item cached at 1325184690, current time 1325189988, item expired at 1325188290
Cache itemId 3576 at 1325189988 to /home/g2data/cache/module/minislideshow/3/5/3576.dat

 

Installation

The MiniSlideShow has been packaged as a rudimentary Gallery2 module so that it can be included in the Gallery2 sidebar block. Download the MiniSlideShow Gallery2 Module and copy the ‘minislideshow’ directory to your Gallery2 modules folder.

The packaged module doesn’t install the MiniSlideShow function for you. It does include the modified ‘mediaRSS.php’ file shown above and the required ‘minislideshow.swf’ and ‘swfobject.js’ files released with MiniSlideShow. Copy these files to your Gallery2 home directory on your web server.

You need to edit the ‘MiniSlideShow.tpl’ block template file found in the ‘minislideshow/templates/blocks’ directory. See below. Change the reference URLs to refer to your Gallery2 URL of your ‘mediaRSS.php’ and the ‘minislideshow.swf’ files. You should also ensure that the ‘swfobject.js’ file is included in the <head> section of your pages with <script type=”text/javascript” src=”http://www.yoursite.com/swfobject.js”></script>

 

MiniSlideShowBlock.tpl

{*
 * $Revision: 1.5 $
 * If you want to customize this file, do not edit it directly since future upgrades
 * may overwrite it.  Instead, copy it into a new directory called "local" and edit that
 * version.  Gallery will look for that file first and use it if it exists.
 *}

<div id="slideshow" style="margin: 10px 10px 15px 0px; padding: 5px; border-style:solid; border-width:1px; background: #ffffff; text-align: center;">

{if $theme.pageType == 'album'}
	{if !empty($theme.item.title)} <span style="color: #800000; font-weight: bold;">{$theme.item.title|markup}</span><br /> {/if} Slide Show <br />
{/if}
{if $theme.pageType == 'photo'}
	{if !empty($theme.parent.title)} <span style="color: #800000; font-weight: bold;">{$theme.parent.title|markup}</span><br /> {/if} Slide Show <br />
{/if}

<div id="g2slidecontainer" style="height: 160px;">
 <div id="g2slideplayer">
    You need Flash Player and JavaScript enabled to view this item.
 </div>
</div>

<script type="text/javascript">
    var attributes = {ldelim}
      id: 'mini',
      name: 'movie'
    {rdelim};
    var params = {ldelim}
      menu: 'false',
      wmode: 'transparent',
      allowscriptaccess: 'always',
      allowfullscreen: 'true'
    {rdelim};
    var flashvars = {ldelim}
{if $theme.pageType == 'album'}
      xmlUrl: 'http://www.yoursite.com/gallery2/mediaRss.php?mode=dynamic%26g2_view=dynamicalbum.RandomAlbum%26g2_albumId={$theme.item.id}',
{/if}
{if $theme.pageType == 'photo'}
      xmlUrl: 'http://www.yoursite.com/gallery2/mediaRss.php?mode=dynamic%26g2_view=dynamicalbum.RandomAlbum%26g2_albumId={$theme.parent.id}',
{/if}
      shuffle: 'true',
      showDropShadow: 'true',
      delay: 3,
      useFull: 'true',
      showControls: 'always',
      roundedMask: 'true'
    {rdelim};
    swfobject.embedSWF("http://www.yoursite.com/gallery2/minislideshow.swf", "g2slideplayer", "160", "160", "9.0.115.0", null, flashvars, params, attributes);
</script>
</div>

 

As a Gallery2 administrator you can install the ‘minislideshow’ plugin. The installation process should create the Gallery2 module cache directory for you. If you want to include the slideshow in your Gallery2 theme add the ‘minislideshow’ as a sidebar block. If you want to view the slideshow from an external application such as WordPress, simply create a text widget with code similar to the template file above.

 

WordPress Text Widget

<div id="slideshow" style="margin: 10px 10px 15px 0px; padding: 5px; border-style:none; background: #ffffff; text-align: center;">
<span style="color: #800000; font-weight: bold;">Resin Figure Models</span><br /><span style="font-size: 0.8em; font-family: Verdana,Arial,Helvetica,sans-serif;">Slide Show </span><br />

<div id="g2slidecontainer" style="height: 160px;">
 <div id="g2slideplayer">
    You need Flash Player and JavaScript enabled to view this item.
 </div>
</div>

<script type="text/javascript">
    var attributes = {
      id: 'mini',
      name: 'movie'
    };
    var params = {
      menu: 'false',
      wmode: 'transparent',
      allowscriptaccess: 'always',
      allowfullscreen: 'true'
    };
    var flashvars = {
      xmlUrl: 'http://www.yoursite.com/gallery2/mediaRss.php?mode=dynamic%26g2_view=dynamicalbum.RandomAlbum%26g2_albumId=21930',
      shuffle: 'true',
      showDropShadow: 'true',
      delay: 3,
      useFull: 'true',
      showControls: 'always',
      roundedMask: 'true'
   };
    swfobject.embedSWF("http://www.yoursite.com/gallery2/minislideshow.swf", "g2slideplayer", "160", "160", "9.0.115.0", null, flashvars, params, attributes);
</script>
</div>

Gallery2 Website Option Sidebar Block

My website is Lightbox enabled for image viewing and slideshow viewing of Gallery2 contents. Although the default case is to enable these Lightbox services, many website viewers may not want this. Website visitors should have the option to configure their viewing experience to suit their preference. This introduces the idea of user specific customization or configuration of their profile on my website.

To enable customization I implemented a user options section as a new Gallery2 sidebar block. The first challenge was to create a new Gallery2 block. Fortunately there is a very simple HTML page custom plugin for Gallery2 available on the Internet, from Bharat Mediratta, that can be used to do this. I didn’t care about the intended capability to link to HTML pages but I did want the ability to establish a sidebar block. I wanted to display checkbox options in the sidebar that the user could modify. This sidebar block would be shown on all Gallery2 pages so I also needed some way to retain the settings of these checkboxes as the user traversed the gallery pages.

To do this, I had to maintain the user option state in session cookies. The cookies provide persistance to the option values across individual page changes within one session and also across different sessions. I had two Lightbox options custom configured in my Matrix theme that could be used for initial settings. One was a global Lightbox option that identified if Lightbox services were available, and the second identified if Lightbox slideshow capabilities were available. If these options were set in the theme and if cookie values did not exist then I could assume that the checkbox options should be set, otherwise the checkbox option value would be the cookie value. For persistance the cookies would be retained within the browser for five days.

The Gallery2 sidebar block contents, found in the HtmlBlock.tpl template file in the plugin blocks directory, were replaced with the following Javascript:

templates/blocks/HtmlBlock.tpl

{*
 * $Revision: 1.5 $
 * If you want to customize this file, do not edit it directly since future upgrades
 * may overwrite it.  Instead, copy it into a new directory called "local" and edit that
 * version.  Gallery will look for that file first and use it if it exists.
 *}
{if ($theme.params.lightbox == 1)} 
    <script type="text/javascript">
      // <![CDATA[
      function set(s) {ldelim}
        var x = readCookie(s) ;
        if (!x) return ;
        eraseCookie(s) ;
        (x.charAt(0)=='1') ? x = "0" : x = "1" ;
        createCookie(s,x,5) ;
      {rdelim}

	function createCookie(name,value,days) {ldelim}
	  if (days) {ldelim}
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	  {rdelim}
	  else var expires = "";
	  document.cookie = name+"="+value+expires+"; path=/";
	{rdelim}

	function readCookie(name) {ldelim}
	  var nameEQ = name + "=";
	  var ca = document.cookie.split(';');
	  for(var i=0;i < ca.length;i++) {ldelim}
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	  {rdelim}
        if (name == "cb1") return {if ($theme.params.lightbox == 1)} "1" ; {else} "0" ; {/if}
        if (name == "cb2") return {if ($theme.params.autoLightbox == 1)} "1" ; {else} "0" ; {/if}
	  return null;
	{rdelim}

	function eraseCookie(name) {ldelim}
	  createCookie(name,"",-1);
	{rdelim}


    document.write('<div class="{$class}">') ;
    if (readCookie('cb1') == "1")
      document.write('<input type="checkbox" id="cb1" checked="checked" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
    else 
      document.write('<input type="checkbox" id="cb1" onClick="set('+"'cb1'"+');"/><b>Photo Lightbox on</b><br />') ;
  {if ($theme.params.autoLightbox == 1)} 
    if (readCookie('cb2') == "1")
      document.write('<input type="checkbox" id="cb2" checked="checked" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
    else 
      document.write('<input type="checkbox" id="cb2" onClick="set('+"'cb2'"+');"/><b>Gallery Slideshow on</b>') ;
  {/if}
    document.write('</div>') ;
      // ]]>
    </script>
{/if} 

Meta Tags for Gallery2 and WordPress

When Gallery2 is embedded in WordPress there is no standard capability to generate page meta descriptions and keyword tags for individual Gallery2 albums or photos.  The WPG2 plugin creates one WordPress page, the WPG2 page, in which all Gallery2 content is rendered.  Each time a Gallery2 page is rendered by WordPress we need to be able to generate unique meta tags so that search engines can index and identify our site Gallery2 pages. Without the capability to uniquely tag individual Gallery2 pages our photo galleries may not be indexed.

To generate meta tags for each WordPress Gallery2 page, the first step is to generate meta tags in the Gallery2 header.  In many cases the meta description tag is based upon the Gallery2 page description or title and the meta keyword tag is based upon the Gallery2 page keywords.  In my case I follow similar technique for the page description, but for keywords I want to automatically generate these values from the album contents if keywords were not specified.

Some postings on the Internet show how to do this. Meta tags are inserted into your Gallery2 theme.tpl file near the beginning, after the generation of the <title> directive. I have modified the code to default the keyword values from the Gallery2 summary text and album title. I do not assign Gallery2 keywords to album and photo pages. Instead, my pages use the summary text as a keyword to identify the album contents.

theme.tpl

    
    {* If Gallery doesn't provide a header, we use the album/photo title (or filename) *}
    {if empty($head.title)}
      <title>{$theme.item.title|markup:strip|default:$theme.item.pathComponent}</title>
    {/if}

    {* For meta tags, if no keywords for the page we default to use the title and summary text from all children in the page*}
    {assign var="keywordlist" value=$theme.item.title|markup:strip|default:$theme.item.pathComponent}
    {if count($theme.children) > 0}
       {foreach from=$theme.children item=child}
          {if !empty($child.summary)}
            {if !empty($child.title)}
 	       {assign var="keywordlist" value=$keywordlist|cat:", "}
 	       {assign var="keywordlist" value=$keywordlist|cat:$child.title|markup:strip|entitytruncate:1000}
            {/if}
            {assign var="keywordlist" value=$keywordlist|cat:", "}
	    {assign var="keywordlist" value=$keywordlist|cat:$child.summary|markup:strip|entitytruncate:1000}
         {/if}
       {/foreach}
    {/if}

    <meta name="description" content="{$theme.item.description|markup:strip|truncate:180:'...'|default:$theme.parent.title|markup:strip|default:$theme.item.pathComponent}" />
    <meta name="keywords" content="{$theme.item.keywords|markup:strip|default:$keywordlist}" />


The next step, once the <meta> tags are being generated in the Gallery2 HTML <head> section, is to write a new function in the WPG2 plugin to extract these tags from the Gallery2 page and insert them into the WordPress header. We will use this function in the WordPress header template when we want to write the Gallery2 meta tags. Note, this code isolates only the meta tags which have the ‘name=’ directive. This selects only the meta tags identified above and does not bring forward any other Gallery2 meta tags of different types that may duplicate existing WordPress tags. Edit the wpg2functions.inc file and add the following code:

wpg2functions.inc

/**
* Function Hook on WordPress Header
* WP action to add the Gallery2 meta tags to the WP header
*
* @param NULL
* @return NULL
*/
function g2_addmeta() {

	if (!defined('G2INIT')) {
		$ret = g2_login();
		if ($ret) {
			echo '<h2>' . __('Fatal G2 error', 'wpg2') . '</h2> ' . __("Here's the error from G2: ", 'wpg2') . $ret->getAsHtml();
			exit;
		}
	}

	global $g2data;

	if (defined('WPG2PAGE')) {
		// Merge Gallery2 HEAD Outputs Into WP Header 
		if (isset($g2data['headHtml'])) {
			list($g2_title, $g2_css, $g2_javascript, $g2_meta) = GalleryEmbed::parseHead($g2data['headHtml']);
			foreach ($g2_meta as $wpg2_meta) { 
				if (strpos("$wpg2_meta","name=")) { echo "$wpg2_meta rn"; }
			}
		}
	}
		
	// Add BTEV Event Message
	if (function_exists('btev_trigger_error')) {
		btev_trigger_error('WPG2 ADDED G2 HEADER TO WP HEADER', E_USER_NOTICE, __FILE__);
	}
}


Lastly, you need to find the section in your WordPress header template where meta tags are written. In my case I am using the Atahualpa theme and this theme has put some hooks into the header generation to call special functions. I updated bfa_meta_tags.php and inserted the call to our new function g2_addmeta() in the section where meta tags were generated for static and single post pages. Note, for this solution to work correctly you do not want to define meta tags or descriptions within your WordPress theme for the WPG2 page, otherwise you will generate both the theme meta tags and the Gallery2 meta tags.

functions/bfa_meta_tags.php


// META DESCRIPTION & KEYWORDS Tag for single post pages and static pages:
if ( is_single() OR is_page() ) {
	$bfa_meta_description = get_post_meta($post->ID, 'bfa_ata_meta_description', true);
	$bfa_meta_keywords = get_post_meta($post->ID, 'bfa_ata_meta_keywords', true);
	if ( $bfa_meta_description != '' ) {
		echo "<meta name="description" content="" .
    	htmlentities($bfa_meta_description,ENT_QUOTES,'UTF-8') . "" />n";
	}
	if ( $bfa_meta_keywords != '' ) {
		echo "<meta name="keywords" content="" .
    	htmlentities($bfa_meta_keywords,ENT_QUOTES,'UTF-8') . "" />n";
	}  
	// META DESCRIPTION Tag for WPG2 pages:
	g2_addmeta() ;
}

Gallery2 Image Highlight

To modify your Gallery2 theme to effect a mouseover indicator to all images that have anchors or hyperlinks, you need to insert a few lines into your theme CSS file.  This is for the Matrix theme and may apply to others.

This code sets all images with anchors to a 90% opacity. When the mouse hovers over an image the image filter is removed and it shows fully opague. This effect has been disabled for Gallery2 photo frames which uses class gsImageView.

theme.css

#gallery a img {filter:alpha(opacity=90); -moz-opacity:0.9; opacity:0.9; -khtml-opacity:0.9;}
#gallery a:hover img {filter:none; -moz-opacity:1.0; opacity:1.0; -khtml-opacity:1.0;}
#gsImageView a img {filter:none; -moz-opacity:1.0; opacity:1.0; -khtml-opacity:1.0;}


For a similar effect when using Gallery2 images in WordPress with the WPG2 plugin, the wpg2.css file will require the following lines:

wpg2.css

.wpg2sidebarblock-image a img {filter:alpha(opacity=90); -moz-opacity:0.9; opacity:0.9; -khtml-opacity:0.9;}
.wpg2sidebarblock-image a:hover img {filter:none; -moz-opacity:1.0; opacity:1.0; -khtml-opacity:1.0;}
.wpg2tag-image a img {filter:alpha(opacity=90); -moz-opacity:0.9; opacity:0.9; -khtml-opacity:0.9;}
.wpg2tag-image a:hover img {filter:none; -moz-opacity:1.0; opacity:1.0; -khtml-opacity:1.0;}

WPG2 Tag Custom URL

The WordPress to Gallery plugin inserts <wpg2>...</wpg2> tags into the post when WPG2 tags are used for gallery thumbnail image  insertions.  The default hyperlink associated with the thumbnail is a link to the photo of the image.

The G2Image module of WPG2 was modified to allow for entry of custom URL hyperlinks on WPG2 tags.  Custom URLs can be used to link to gallery albums or other external locations.  These URLs apply to WPG2 normal or resized thumbnail images.

The WPG2 tag syntax has optional parameters delimited by ‘|’:

<wpg2>n</wpg2> where ‘n’ is the gallery image number
<wpg2>n|size</wpg2> where ‘size’ is the thumbnail maximum pixel width or height
<wpg2>n|link</wpg2> where ‘link’ is the external URL in the form ‘http://…’
<wpg2>n|size|link</wpg2> where ‘size’ and ‘link’ are specified together


wpg2embed.inc

The ImageBlock processing is updated to process the modified syntax for the new ‘link’ parameter.  A ‘link’ specifier is provided to the image block options.  This is output in the generated html. The complete code for the g2_tagimageblock() function is shown:

/*
*********************************************************************************
Gallery2 ImageBlock Handling
*********************************************************************************
*/

/**
* Include image from Gallery2 from WPG2 Tag
*
* @param string $g2inputid Gallery2 Item path relative to root Gallery2 Data directory
* @param integer $g2itemsize Item Size in pixels.  Defaults to null if not included in GET parameters.
* @return string HTML for img tag
*/
function g2_tagimageblock( $g2inputstr ) {

  // Get WPG2 Option Settings
  $wpg2_option = get_option('wpg2_options');

  global $post;

  if ($wpg2_option['g2_validated'] == "Yes") {

    // Initialize Gallery
    if (!defined('G2INIT')) {
      $ret = g2_login();
      if ($ret) {
        echo '<h2>' . __('Fatal G2 error', 'wpg2') . '</h2>' . __("Here's the error from G2: ", 'wpg2') . $ret->getAsHtml();
        exit;
      }
    }

    // Check for the Item Size | parameter & Clean up Strings..
    $g2itempos = strpos ($g2inputstr, '|');
    $g2linkref = "";
    if ($g2itempos) {
      $g2itemsize = substr ($g2inputstr, $g2itempos+1);
      $g2itemid = substr ($g2inputstr, 0, $g2itempos);

      // Check for the link | parameter
      $g2itempos2 = strpos ($g2itemsize, '|');
      if ($g2itempos2) {
        $g2linkref = substr ($g2itemsize, $g2itempos2+1);
        $g2itemsize = substr ($g2itemsize, 0, $g2itempos2);
      } else {
        if ( !is_numeric($g2itemsize) ) {
          $g2linkref = $g2itemsize ;
          $g2itemsize = $wpg2_option['g2_postimgsize'];
      }
    }
  } else {
    $g2itemsize = $wpg2_option['g2_postimgsize'];
      $g2itemid = $g2inputstr;
    }

    if ( !is_numeric($g2itemid) ) {
      // Make Sure Item Path does not contain a + as it should instead be a space
      $g2itemid = str_replace ("+", " ", $g2itemid);

      // Get the Image ID
      list ($ret, $g2itemid) = GalleryCoreAPI::fetchItemIdByPath($g2itemid);
      if ($ret) {
        $img = '* ' . __('WPG2 CANNOT LOCATE GALLERY2 ITEM BY '.$g2itemid, 'wpg2') . ' *';
        return $img;
      }
    } else {
      list ($ret, $g2item) = GalleryCoreApi::loadEntitiesById($g2itemid);
      if ($ret) {
        $img = '* ' . __('WPG2 CANNOT LOCATE GALLERY2 ITEM ID '.$g2itemid, 'wpg2') . ' *';
        return $img;
      }
    }

    // Build the Image Block
    $blockoptions['blocks'] = 'specificItem';
    $blockoptions['show'] = 'none';
    $blockoptions['itemId'] = $g2itemid;
    if ($g2linkref != "")
      $blockoptions['link'] = $g2linkref;

    // Assign Show Details
    if ( $wpg2_option['g2_tagblockshow'] ) {
      if ( count($wpg2_option['g2_tagblockshow']) > 1 )
        $blockoptions['show'] = $wpg2_option['g2_tagblockshow'][1].'|'.$wpg2_option['g2_tagblockshow'][2] ;
      else
        $blockoptions['show'] = $wpg2_option['g2_tagblockshow'][1];
      } else
        $blockoptions['show'] = 'none';

    // Assign maxSize
    if ($g2itemsize)
      $blockoptions['exactSize'] = $g2itemsize;
    else
      if ( $wpg2_option['g2_tagimgsize'] )
        $blockoptions['exactSize'] = $wpg2_option['g2_tagimgsize'];

    // Assign Item Frame Style
    if ($wpg2_option['g2_tagimageframe'])
      $blockoptions['itemFrame']  = $wpg2_option['g2_tagimageframe'];

    // Assign Album Frame Style
    if ($wpg2_option['g2_tagalbumframe'])
      $blockoptions['albumFrame']  = $wpg2_option['g2_tagalbumframe'];

    if ($wpg2_option['wpg2_enabletagslightbox'])
      list ($ret, $img, $headimg, $g2_isalbum ) = g2_getLightImageBlock($blockoptions,$wpg2_option['g2_lightboximgsize'],'wpg2tag-image');
    else
      list ($ret, $img, $headimg, $g2_isalbum ) = g2_getLightImageBlock($blockoptions,null,'wpg2tag-image');

    if ($ret)
      $img = $ret->getAsHtml().print_r($blockoptions);

    $img = preg_replace("/(s+)?(<.+>)(s+)?/", "$2", $img);
    $img = str_replace("n", "", $img); // strip out CRs

    // Requires raw-html plugin because WP filters mess up quotes
    if ($wpg2_option['wpg2_enabletagslightbox'] && !$g2_isalbum) {
      if ($g2linkref == "") {
        $img = str_replace('><.img', ' rel="lightbox['.$post->ID.']"><.img', $img);
      }
      $img = '<.!--start_raw-->' . $img . '<.!--end_raw-->' ;
    }

  } else
    $img = '* ' . __('WPG2 Plugin Not Validated', 'wpg2') . ' *';

  // Add BTEV Event Message
  if (function_exists('btev_trigger_error')) {
    btev_trigger_error('WPG2 TAG G2 IMAGEBLOCK CALL ID ('.$g2inputstr.')', E_USER_NOTICE, __FILE__);
  }

  return $img;
}


wpg2imageblock.tpl

The template file in the WPG2 plugin is used to display the WPG2 tags.  This file required modification to access the link parameter from the ImageBlock and not from the block.  This template file also includes lightbox specific code. The full template file is shown:

{*
 * Author: WPG2 Team, Gallery2 Development Team
 * Author URI: http://wpg2.galleryembedded.com/
 * Updated: 23/06/2007

 * 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.

 *}

{foreach from=$ImageBlockData.blocks item=block}
 <table align="center" cellspacing="0" cellpadding="0"><tr><td>
  {if !empty($ImageBlockData.divClass)}
     <div class="{$ImageBlockData.divClass}">
  {else}
    <div class="one-image">
  {/if}
  {if !empty($block.title)}
    <h3> {g->text text=$block.title} </h3>
  {/if}

  {capture assign="linkHref"}{strip}
  {if empty($ImageBlockData.link)}
    {if empty($block.lightboxid)}
      {g->url arg1="view=core.ShowItem" arg2="itemId=`$block.id`" forceFullUrl=true}
    {else}
      {g->url arg1="view=core.DownloadItem" arg2="itemId=`$block.lightboxid`" forceFullUrl=true}
    {/if}
  {elseif $ImageBlockData.link != 'none'}
    {$ImageBlockData.link}
  {/if}
  {/strip}{/capture}

  {capture assign="linkView"}{strip}
  {if empty($ImageBlockData.link)}
    {g->url arg1="view=core.ShowItem" arg2="itemId=`$block.id`" forceFullUrl=true}
  {elseif $ImageBlockData.link != 'none'}
    {$ImageBlockData.link}
  {/if}
  {/strip}{/capture}

  {capture assign="titleText"}{strip}
  {if empty($block.lightboxid) || !empty($ImageBlockData.link)}
    {$block.item.title|markup} {else} {$block.item.title|markup}<br /><a href='{$linkView}'>View Photo Page</a><br />{/if}
  {/strip}{/capture}

  {capture assign="link"}{if !empty($linkHref)}
    <a href="{$linkHref}" {if !empty($ImageBlockData.linkTarget)} target="{$ImageBlockData.linkTarget}" {/if} title="{$titleText}">
  {/if}{/capture}

  {if $block.item.canContainChildren}
    {assign var=frameType value="albumFrame"}
  {else}
    {assign var=frameType value="itemFrame"}
  {/if}
  {if array_key_exists('maxSize', $ImageBlockData)}
    {assign var=maxSize value=$ImageBlockData.maxSize}
  {/if}
  {assign var=imageItem value=$block.item}
  {if isset($block.forceItem)}{assign var=imageItem value=$block.thumb}{/if}
  {if isset($ImageBlockData.$frameType)}
    {g->container type="imageframe.ImageFrame" frame=$ImageBlockData.$frameType
		  width=$block.thumb.width height=$block.thumb.height maxSize=$maxSize}
      {$link}
	{g->image item=$imageItem image=$block.thumb id="%ID%" class="%CLASS%" maxSize=$maxSize forceFullUrl=true}
      {if !empty($linkHref)} </a> {/if}
    {/g->container}
  {else}
    {$link}
      {g->image item=$imageItem image=$block.thumb class="giThumbnail" maxSize=$maxSize forceFullUrl=true}
    {if !empty($linkHref)} </a> {/if}
  {/if}

  {if isset($ImageBlockData.show.title) && isset($block.item.title)}
    <h4 class="giDescription">
      {$block.item.title|markup}
    </h4>
  {/if}

  {if isset($ImageBlockData.show.date) ||
      isset($ImageBlockData.show.views) ||
      isset($ImageBlockData.show.owner)}
    <p class="giInfo">
      {if isset($ImageBlockData.show.date)}
      <span class="summary">
	{g->text text="Date:"} {g->date timestamp=$block.item.originationTimestamp}
      </span>
      {/if}

      {if isset($ImageBlockData.show.views)}
      <span class="summary">
	{g->text text="Views: %d" arg1=$block.viewCount}
      </span>
      {/if}

      {if isset($ImageBlockData.show.owner)}
      <span class="summary">
	{g->text text="Owner: %s" arg1=$block.owner.fullName|default:$block.owner.userName}
      </span>
      {/if}
    </p>
  {/if}
 </div>
</td></tr></table>
{/foreach}


functions.js

In the WPG2 g2image module the insertItems() function requires modification to output the new ‘link’ specifier in the WPG2 tag. The case statement to process the WPG2 tag in this function is modified as shown:

  case 'wpg2_image':
    if (obj.alignment.value != 'none'){
      htmlCode += '<div class="' + obj.alignment.value + '">';
    }
    if(window.tinyMCE) {
      htmlCode += '<img src="' + thumbnail_src[i]
        + '" alt="' + image_id[i];
      if (obj.wpg2_tag_size.value)
        htmlCode += '|' + obj.wpg2_tag_size.value;
      if (obj.custom_url.value)
        htmlCode += '|' + obj.custom_url.value;
      htmlCode += '" title="' + image_id[i];
      if (obj.wpg2_tag_size.value)
        htmlCode += '|' + obj.wpg2_tag_size.value;
      if (obj.custom_url.value)
        htmlCode += '|' + obj.custom_url.value;
      htmlCode += '" ' + thumbw[i] + thumbh[i]
        + 'class="mceItem" id="mce_plugin_g2image_wpg2" />';
    }
    else {
      htmlCode += '<wpg2>' + image_id[i];
      if (obj.wpg2_tag_size.value)
        htmlCode += '|' + obj.wpg2_tag_size.value;
      if (obj.custom_url.value)
        htmlCode += '|' + obj.custom_url.value;
      htmlCode += '</wpg2>';
    }
    if (obj.alignment.value != 'none'){
      htmlCode += '</div>';
    }
  break;


g2image.php

In the WPG2 g2image module the function g2ic_make_html_controls() in g2image.php requires modification to enable the new ‘link’ entry when selecting a WPG2 tag. The initial part of this function is shown:

/**
 * Create the HTML for the image controls
 *
 * @return string $html The HTML for the image controls
 */
function g2ic_make_html_controls(){
	global $gallery, $g2ic_imginsert_options, $g2ic_options;

	// "How to insert:" radio buttons
	$html = "        <fieldset>n"
	. '            <legend>' . T_('Insertion Options') . '</legend>' . "n"
	. '            <label for="alignment">' . T_('How to Insert Image') . '</label>' . "n"
	. g2ic_make_html_select('imginsert', $g2ic_imginsert_options, 'toggleTextboxes();')
	. '            <br />' . "n"
	. '            <br />' . "n"

	// "Custom URL" textbox
	. '            <div name="custom_url_textbox"';
	if ($g2ic_options['default_action'] == 'thumbnail_custom_url' || $g2ic_options['wpg2_valid']) {
		$html .= ' class="displayed_textbox"';
	}
	else {
		$html .= 'class="hidden_textbox"';
	}
	$html .= '>' . "n"
	. '            <label for="custom_url">' . T_('Custom URL') . '<br /></label>' . "n"
	. '            <input type="text" name="custom_url" size="84" maxlength="150" value="' . $g2ic_options['custom_url'] . '" />' . "n"
	. '            <br />' . "n"
	. '            <br />' . "n"
	. '            </div>' . "n"

Gallery2 Image Types

This shows the various types of Gallery2 thumbnail images that can be inserted into a post entry, including WPG2 tags and other thumbnails available from the G2Image module. The images are centered in a left justified table to show standard margin and border characteristics.

WPG2 tag to image with link to photo
 
Dsc03140
 

Zion National Park

Thumbnail with no link
Dsc03143
Thumbnail with link to photo
Dsc03144
Thumbnail with link to album
Dsc03160

 

This is another Gallery2 WPG2 tumbnail inserted in the text flow.  The image has been expanded to a 300 pixel size and inserted with ‘normal’ positioning.

 
Dsc_0035
 

Yowane

 

All WPG2 tag images are assigned the same Lightbox group for the post or page.  Thumbnails that are inserted specifically and Lightbox enabled are in a different group.

The following 300 pixel expanded thumbnails are also WPG2 tags but these tags have specified links to the gallery.  Link entry for WPG2 tags is a local code modification to the WPG2 WordPress plugin. These images have been positioned to float right and float left within a block container.

 
Dsc_0050
 

Yowane

 
Dsc_0079
 

Yowane