Writing a CMS/Community with Smarty and the Zend Framework: Part 6

This time we will take a look at the current state of the gallery section.


I’ve opted to use a Flex and Javascript combination for the image uploading which might make this section of the project interesting for other people than ZF/Smarty geeks. Due to that fact most of the markup and actionscript for this piece is a separate article called: Multiple uploads with jQuery and Flex or Flash.

The gallery is currently implemented with a simple file structure that looks like this:

site_url/gallery/user_id
site_url/gallery/user_id/thumbs

We will store the big pictures in the main gallery folder and the thumbnails in the thumbs folder. The database is not involved at all. However I did opt for storing configuration data in a table that looks like this at the moment:

CREATE TABLE `com_gallery` (
  `max_w` bigint(12) NOT NULL,
  `max_h` bigint(12) NOT NULL,
  `max_s` bigint(12) NOT NULL,
  `thumb_w` int(5) NOT NULL,
  `thumb_h` int(5) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `com_gallery` (`max_w`, `max_h`, `max_s`, `thumb_w`, `thumb_h`) VALUES (500, 500, 1048576, 100, 100);

It’s pretty self explanatory, max_s will store the maximum allowed size of an image in bytes. The reason I chose a database solution instead of a config file is that I’ve decided that only the core functionality belongs in the config file and the gallery is not a core component. Yes it’s Joomla style yet again.

Most of the logic is in our new GalleryController which currently is responsible for two main actions. It will generate a page used for uploading images to the gallery and a page to display the user’s gallery. Let’s take a look at the upload logic first:

class GalleryController extends ExtController{
	
	function init(){
		parent::init();
		$this->name = "gallery";
		$this->info = Common::loadModel('gallery')->fetchAll()->current();
		$this->setDirs($this->gs->usr_info['id']);
		parent::finishInit();
	}
	
	function setDirs($id){
		$this->dir = "gallery/$id/";
		$this->thumb_dir = $this->dir."thumbs/";
	}
	
	function upload($params){
		$this->chkSessRdr();
		$this->smarty->assign('max_size', $this->info->max_s);
		$conf = $this->getZendConfSection('paths');
		$base_path = "http://{$conf->site_url}{$this->base_url}/gallery";
		$this->smarty->assign('upload_script', "$base_path/save/PHPSESSID/{$_REQUEST['PHPSESSID']}");
		$this->smarty->assign('get_thumb_url', "$base_path/newThumbs/");
		$this->commonThumbs();
		return $this->fetch('gallery_upload.tpl');
	}
	.
	.
	.
function commonThumbs(){
  $thumbs = $this->getThumbs(); 
  $this->smarty->assign_by_ref("thumbs", $thumbs);
  $this->smarty->assign("thumb_dir", $this->thumb_dir);
  
}

function getThumbs(){
  if(is_dir($this->thumb_dir)){
    return Common::listDir($this->thumb_dir);;
  }else{
    return array();
  }
}

function newThumbsAction(){
  $this->commonThumbs();
  echo $this->fetch('gallery_thumbs.tpl');
  exit;
}

We start by loading the above mentioned configuration information in init(). Then we setup our current directories with the id of the user owning the session in this case. Since the last part I have created a convenience function to reduce code in the form of getZendConfSection():

function getZendConfSection($section){
  $registry 	= Zend_Registry::getInstance();
  $config_file = $registry['config_file'];
  $conf = new Zend_Config_Ini($config_file, $section);
  return $conf;
}

Anyway, the above upload function is responsible for generating the markup that is covered by the Flex/Flash article mentioned in the beginning of this tutorial. As you can see newThumbsAction() is the Ajax function called in that markup.

Let’s move on to the function that the Shockwave is calling in order to upload each image:

function saveAction(){
  
  if(!is_dir($this->dir)){
    mkdir($this->dir);
    mkdir($this->thumb_dir);
    chmod($this->dir, 0777);
    chmod($this->thumb_dir, 0777);
  }
  
  $filename_only = uniqid().".jpg";
  $filename = $this->dir.$filename_only;
  $thumbname = $this->thumb_dir.$filename_only;
  
  $move_result = move_uploaded_file($_FILES["Filedata"]["tmp_name"], $filename);
  $result = Common::reScaleImage($filename, $this->info->max_w, $this->info->max_h);
  
  copy($filename, $thumbname);
  $result2 = Common::reScaleImage($thumbname, $this->info->thumb_w, $this->info->thumb_h);
  
  if(!$result){
    unlink($filename);
    unlink($thumbname);
  }else{
    chmod($filename, 0777);
    chmod($thumbname, 0777);
  }
  
  exit;
}

The chmodding at the top should be unnecessary since mkdir can be called like this: mkdir($this->dir, 0777). However, that would not work with our current Samba configuration we are using to access the Ubuntu server here. Don’t ask me why. After setting up this function I thought I was home free but oh so wrong I was. Note that I make use of session data in the form of the current user’s unique id when using $this->dir above.

No problem you might think since I append the session id to the url used to call saveAction() with. I did too, but I was wrong. It seems the Zend Framework won’t pick up on this without a little help. Why can nothing ever be simple? I was fortunate and quickly found Rob’s Flash upload in Symfony article before I got too panicked. Empowered by the basic aha feeling I got from that piece I was able to quickly hack ZF in a similar way:

class ExtSession extends Zend_Session{
	public static function start($options = false){
    if(strpos($_SERVER['REQUEST_URI'],'PHPSESSID') !== false){
      $tok = strtok($_SERVER['REQUEST_URI'], "/");
      while($tok !== false){
        if($tok == 'PHPSESSID'){
          session_id( strtok("/") );
          break;
        }
        $tok = strtok("/");
      }
    }
    parent::start($options);
  }
}

We simply check our URL for PHPSESSID, and if we find it we start the session with that id instead. Calling my extended session instead of the original Zend Session in the bootstrap file solved the problem.

Let’s move on to the viewing logic:

function view($params){
  $this->chkSessRdr();
  if(!empty($params['id']))
    $this->setDirs($params['id']);
  $this->smarty->assign("gallery_dir", $this->dir);
  $this->commonThumbs();
  return $this->fetch('gallery_view.tpl');
}

If we explicitly pass an id to this function we will use that id instead of currently logged in user’s.

The markup:

{config_load file="$config_file"}
<script src="{$baseUrl}/js/jquery.js" language="JavaScript"></script>
<script src="{$baseUrl}/js/scrollTo.js" language="JavaScript"></script>

<script type="text/javascript">
	  // <![CDATA[
	  var galleryDir 	= "{$baseUrl}"+"/{$gallery_dir}";
	  {literal}
	  function showMainPic(pic){
	  	var inner_html = '<img src="'+galleryDir+pic+'"/>';
                $("#mainPic").html(inner_html);
                $.scrollTo("#mainPic", {speed:500});
	  }
	  {/literal}
	  // ]]>
</script>

<div id="thumbs">
	{if !empty($thumbs)}
		{foreach from=$thumbs item=thumb}
			<a href="javascript:showMainPic('{$thumb}')">
				<img class="gallery_thumb" src="{$baseUrl}/{$thumb_dir}{$thumb}"/>
			</a>
		{/foreach}
	{else}
		{#no_pics#}
	{/if}
</div>
<div id="mainPic"></div>
.gallery_thumb{}

.gallery_thumb:hover{
  filter:alpha(opacity=70);
-moz-opacity: 0.4;
}

I use the jQuery scrollTo plugin yet again. The reason being that if a user loads a vertically big image we will scroll down automatically so that the image is shown completely. Being able to automatically scroll the main window this easily is awesome because it’s one of the most helpful usability features one can implement. No one likes to scroll so when we have content that the user obviously want to access and there might be some scrolling involved it makes no sense to not implement automatic scrolling. And when we have ease in and out like we have with the scrollTo plugin we also avoid the jarring and confusing feeling that it is possible to get with instantaneous jumps.

That was it for this time, next time we will take a look at the blogging component which is the second major requirement we have for the community part of this project.

Related Posts

Tags: , , , , , ,

Posts linking to this article:

links for 2007-12-09
oscmax 2.0 - oscommerce maximized. oscmax v2.0 is a powerful e-commerce/shopping cart web application. there are many advantages to using oscmax as your e-commerce/shopping cart for your web site. it has all the features needed to run a ...

[web] 連結分享
php. the new way to write ajax applications with php. 繼承phpajax 這個類別後,你就可以很方便地產生以ajax 操作的html 元素。 the completely unofficial xdebug.ini. 最完整的xdebug 可在php.ini 設定的項目及說明。 ...

Subscribe with Google Reader