Logo Search packages:      
Sourcecode: bamboo version File versions  Download package

FolderPageStore.php

<?php 

/***************************************************************************
****************************************************************************

A class for storing and retrieving pages using a file system as
storage. Defines abstract class PageStore. Singleton.

Each page is represented as a directory with this structure:

page_name/  -- the enclosing directory
  b.basic   -- basic page properties which cannot be inherited. 
               ie title, page type, timestamp, nav info.
  b.inherit -- properties which may be inherited
                       ie permissions, visibility.
  b.en.txt  -- static text data in english
  b.es.txt  -- static text data in spanish
  b.en.html -- static html data in english, and so on...
  b.order   -- if this page contains other pages, this file
                       lists their order.
  sub_1/    -- a storage directory for a sub page.
  sub_2/    -- ditto.

  b.history/ -- a directory to store previous version
    b.en.n+.txt -- english text version
    b.en.draft-[user].txt -- special 'draft' version
    b.meta
    
any other files in the page's directory without a .b. in the filename
somewhere are considered to be attachments to the page. The .b. is used
to deny direct access to these files without going through bamboo's frontdoor.
  
To the $page object, we add the variable $storage. $storage points to the
absolute path of the enclosing directory which stores data for this page.

***************************************************************************
**************************************************************************/

$base = dirname(dirname(__FILE__));
require_once("$base/common.php");
require_once("$base/Properties.php");
require_once("$base/PageStore.php");

if ($prop->value('umask')) umask(octdec($prop->value('umask')));

class FolderPageStore extends PageStore {

// initializes the $page to be able
// to talk to this PageStore.
function init(&$page) {
      $page->storage = $this->locateStorage($page);
      $page->fnf = !is_dir($page->storage);
}

/*
 * returns an array of basic page info for page object
 */
 
function &loadbasic($siteroot, $path) {
      $storage = $this->locateStorageFromPath($siteroot,$path);
      $file = "$storage/b.basic";
      $ret = array();
      if (is_file($file)) 
            $ret = $this->loadProperties($file);
      $ret['name'] = deurlize($path); // name is special, can't be set, based on path.
      return $ret;
}

function &loadinherit($siteroot, $path) {
      $storage = $this->locateStorageFromPath($siteroot,$path);
      $file = "$storage/b.inherit";
      if (is_file($file))
            return $this->loadProperties($file);
      else
            return null;
}

function savebasic($siteroot, $path, &$data) {
      $storage = $this->locateStorageFromPath($siteroot,$path);
      $file = "$storage/b.basic";
      $err = $this->saveProperties($file, $data);
      return $err;
}

function saveinherit($siteroot, $path, &$data) {
      $storage = $this->locateStorageFromPath($siteroot,$path);
      $file = "$storage/b.basic";
      $err = $this->saveProperties($file, $data);
      return $err;
}


/*
 * returns the contents of a page
 * (only for static pages, obviously)
 */
function fetch(&$page, $version='') {
      if ($page->fnf) return;

      $data = null;
      $contentfile = find_multilingual_content_file($page->storage, $page->type, $version);
      if (!is_file($contentfile)) return;
      else {
            
            ob_start();
                  readfile($contentfile);
                  $page->content = &ob_get_contents();
            ob_end_clean();
      }
}

/* 
 * saves changes to 'content' or 'name'
 * property saving is handled by $this->saveProperties
 * which is used by the Properties class.
 */
function commit(&$page, $version='') {
      if (!$page->dirty()) return;
      
      # get $lang
      $lang = $_SESSION['lang'];
      $languages = split(' ',$page->get('languages'));
      if (!in_array($lang,$languages)) {
            // the page does not allow saving as $lang, so use the default.
            $lang = $page->get('default-lang');
      }
            
      if ($page->isdirty('content')) {
            if ($version == '')
                  $contentfile = "$page->storage/b.$lang.$page->type";
            else {
                  if (!is_dir("$page->storage/b.history/"))
                        $this->makedir("$page->storage/b.history/");
                  $contentfile = "$page->storage/b.history/b.$lang.$version.$page->type";
            }
            $this->makefile($contentfile);
            $handle = fopen($contentfile,'w');
            if ($handle != FALSE) {
                  fwrite($handle,$page->content);
                  fclose($handle);
                  $page->undirty('content');
            }
      }
      if ($page->isdirty('name')) {
            $oldpath = $page->path;
            $newpath = dirname($page->path) . '/' . $page->name;
            $ok = $this->movePage($page, $newpath);
            $page->undirty('name');
            return $ok;
      }
      return true;
}

function getContentSize(&$page) {
      $contentfile = find_multilingual_content_file($page->storage, $page->type);
      if (is_file($contentfile))
            return filesize($contentfile); 
      else
            return 0;
}

// returns a list of languages which have translated content.
function availableContent(&$page) {
      $files = find_all_content_files($page->storage,$page->type);
      for($i=0;$i<count($files);$i++) {
            $files[$i] = preg_replace("/^b\.(.*)\.$page->type$/",'$1',$files[$i]);
      }
      return $files;
}

function getOrder(&$page) {
      $name = basename($page->path);
      $parent = $page->parent();
      $listing = $this->getDirListing($parent->storage);
      return array_search($name, $listing);
}

function &getNext(&$page,$offset=1) {
      $name = basename($page->path);
      $parent = $page->parent();
      $listing = $this->getDirListing($parent->storage);
      $index = array_search($name, $listing);
      if (isset($listing[$index+$offset])) {
            $p = $this->getPage($page->parentpath . '/' . $listing[$index+$offset]);
            return $p;
      }
      else
            return null;
}

function &getPrev(&$page) {
      return $this->getNext($page, -1);
}

function setOrder(&$page, $order) {
      $parent = $page->parent();
      $name = basename($page->path);
      $listing = $this->getDirListing($parent->storage);
      $files = array();

      $count = count($listing);
      if ($order >= $count || $order < 0)
            return;
                  
      unset($listing[array_search($name,$listing)]);
      reset($listing);
      for($i=0; $i<$count; $i++) {
            if ($i == $order) {
                  $files[$i] = $name;
            }
            else {
                  $files[$i] = current($listing);
                  next($listing);
            }
      }
      $this->writeOrderFile($parent->storage, $files);
}

/*
 * Creates a new page as a child of $page.
 */

function &newChild(&$page, $name) {
      global $prop;
      $name = urlize($name);
      $newpath = "$page->path/$name";
      $newpage = $this->getPage($newpath);
      $ok = $this->makedir($newpage->storage);
      if (!$ok) {
            $page->error("Could not create $newpage->storage, mkdir failed.");
      }
      return $newpage;
}

function destroyPage(&$page) {
      $ok = rmdirr("$page->storage/");
      if (!$ok) {
            $page->error("Could not remove $page->storage.");
      }
}

function movePage(&$page, $newpath) {
      $newpage = $this->getPage($newpath);
      if (is_dir($newpage->storage)) {
            $page->error($newpage->path . _(" already exists. "));
            return false;
      }
      else {
            $ok = rename("$page->storage/","$newpage->storage/");
            if (!$ok) {
                  $page->error("Could not move page $page->path to $newpage->path");
                  return false;
            }
            $page = $this->getPage($newpath);
            return true;
      }
}

function duplicatePage(&$page, $newpath) {
      $newpage = $this->getPage($newpath);
      $ok = $this->cpdir("$page->storage/","$newpage->storage/");
      if (!$ok) {
            $page->error("Could not duplicate page $page->path to $newpage->path.");
      }
}

function getFirstChild($page) {
      $filepath = $this->getFilePath($page->path);
      if (!is_dir($filepath)) 
            return null;
            
      $files = $this->getDirListing($filepath);
      if (isset($files[0]))
            return $this->getPage($page->path . '/' . $files[0]);
      else
            return null;
}

function getChildren(&$page) {            
      $ret = array();   
      $files = $this->getDirListing($page->storage);
      foreach($files as $file) {
            $ret[] = $this->getPage($page->path . '/' . $file);
      }
      return $ret;
}     


/**
 * returns the navigation title from just the path.
 * does not require a page object.
 **/

function getNavTitle($path) {
      // may be optimized in the future have a better way of doing this
      $page = $this->getPage($path);
      if ($page->fnf)
            return deurlize($path);
      else
            return $page->get('title');
}

#################################################
## Attachments

function getAttachments(&$page) {
      $path = $page->storage;
      $handle = opendir($path);
      $files = array();
      while ($filename = readdir($handle)) { 
            if ($filename{0} == '.' || is_dir("$path/$filename") || substr($filename,0,2) == 'b.') {
                  continue;
            }
            $files[$filename] = array(
                  'file' => "$path/$filename",
                  'size' => filesize("$path/$filename"),
                  'type' => mime_type("$path/$filename"),
                  'mtime' => filemtime("$path/$filename"),
                  'name' => $filename
            );
      }
      return $files;
}

function addAttachment(&$page,$filedata) {
      $path = $page->storage;
      $err = '';
      if ($filedata['error']) {
            switch ($filedata['error']) {
                  case UPLOAD_ERR_INI_SIZE: $err = "The uploaded file exceeds the upload_max_filesize directive in php.ini."; break;
                  case UPLOAD_ERR_FORM_SIZE: $err = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form."; break;
                  case UPLOAD_ERR_PARTIAL: $err = "The uploaded file was only partially uploaded."; break;
                  case UPLOAD_ERR_NO_FILE: $err = "No file was uploaded."; break;
            }
      }
      elseif (file_exists("$path/$filedata[name]")) {
            $err = _("That file already exists");
      }
      
      if (!$err && move_uploaded_file($filedata['tmp_name'],"$path/$filedata[name]" )) {
            $this->setperm("$path/$filedata[name]");
            return true;
      } else {
            $err = $err ? $err : 'unknown';
            error_log("file upload failed! $err");
            $page->error("file upload failed! $err");
            return true;
      }
}

function removeAttachment(&$page,$filename) {
      $file = safepath("$page->storage/$filename");
      if (is_dir($file)) {
            $page->error("Something is wrong: that is a directory");
            return;
      }
      $ok = unlink($file);
      if (!$ok) {
            $page->error("Could not remove $file");
      }
}


#################################################
## Private Functions
#################################################

#################################################
## File reading/writing

// returns an array of data in file.
// file                  -->     array
// sample_prop = value   -->     $array['sample_prop'] == 'value'
function &loadProperties($filename) {
      $ret = parse_ini_file($filename);
      if ($ret == null)
            return array();
      else  
            return $ret;
}

function saveProperties($filename, &$array) {
      $this->makefile($filename);
      $out = fopen($filename, 'w');
      if ($out != false) {    
            foreach($array as $key => $value) {
                  if ($this->prop->proptype($key) == 'boolean') {
                        if ($value == false)
                              $value = 'false';
                        fwrite($out, "$key = $value\n");
                  }
                  else {
                        $value = preg_replace('/"/', '&quot;', $value);
                        fwrite($out, "$key = \"$value\"\n");
                  }
            }
            fclose($out);
      }
}

/*
 * returns a lits of the bamboo 'pages' in filesystem directory $dir
 * using b.order to sort. If b.order doesn't exist or is out of date, 
 * we create a new one.
 */
 
function getDirListing($storagedir) {
      $handle = opendir($storagedir);
      $subdirs = array();
      while ($filename = readdir($handle)) { 
            if (!is_dir("$storagedir/$filename") || $filename{0} == '.' || substr($filename,0,2) == "b." )
                  continue;
            $subdirs[] = $filename;
      }
      closedir($handle);
      if (is_file("$storagedir/b.order")) {
            $order = array_map('trim',file("$storagedir/b.order"));
            $rebuildneeded = count(array_diff($subdirs,$order)) || count(array_diff($order,$subdirs));
            if ( $rebuildneeded ) {
                  unlink("$storagedir/b.order");

                  // retain as much of the previous order as we can:
                  $newdirs = array();                 
                  foreach ($order as $dir) {
                        if (in_array($dir,$subdirs))
                              $newdirs[] = $dir;
                  }
                  foreach ($subdirs as $dir) {
                        if (!in_array($dir,$newdirs))
                              $newdirs[] = $dir;
                  }
                  $this->writeOrderFile($storagedir,$newdirs);
                  return $newdirs;
            }
            else {
                  return $order;
            }
      }
      elseif (count($subdirs)) {
            sort($subdirs);
            $this->writeOrderFile($storagedir,$subdirs);
            return $subdirs;
      }
      else {
            return array();
      }
}

function writeOrderFile($dir, $filelist) {
      $this->makefile("$dir/b.order");
      $out = fopen("$dir/b.order", 'w');
      if ($out != false) {
            foreach($filelist as $file) {
                  fwrite($out, "$file\n");
            }
            fclose($out);
      }
}

###################################################
## File paths

/*
 * same as dirname, but if only / is left, return ''.
 */
function dirname($path) {
      $ret = dirname($path);
      if ($ret=='/') return '';
      else return $ret;
}

/*
 returns the absolute path to the file system directory
 where $page is stored. 
*/
function locateStorage(&$page) {
      $ret = $_SERVER['DOCUMENT_ROOT']
            . '/' . $page->prop->getGlobal('siteroot')
            . '/' . $page->path;
      $ret = preg_replace(
            array("'/{2,}'","'/$'"),
            array('/',''),
            $ret
      );
      return $ret;
}

function locateStorageFromPath($siteroot, $path) {
      $ret = $_SERVER['DOCUMENT_ROOT'] . '/' . $siteroot . '/' . $path;
      $ret = preg_replace(
            array("'/{2,}'","'/$'"),
            array('/',''),
            $ret
      );
      return $ret;
}

// this is easy and nice, but a little hackish
function storage($path) {
      global $prop;
      $ret = $_SERVER['DOCUMENT_ROOT'].'/'.$prop->getGlobal('siteroot').'/'.$path;
      $ret = preg_replace(array("'/{2,}'","'/$'"),array('/',''),$ret);
      return $ret;
}

/*
 * makes a directory from a path
 */
function makepath($path) {
      return $this->makedir($this->storage($path));
}

/*
 * makes a directory or file, setting the correct owner and permissions
 */
function makefile($filename,$dir=false) {
      if ( ($dir && is_dir($filename)) || (!$dir && is_file($filename)) )
            return true; // skip if exists

      if (!is_writeable(dirname($filename))) {
            $user = posix_getpwuid(posix_getuid());
            d::error(dirname($filename) . " must be writeable by " . $user['name']);
            return false;
      }

      if ($dir) $ok = mkdir($filename);
      else      $ok = touch($filename);
      
      $ok = $ok & $this->setperm($filename);
      return $ok;
}

function makedir($dirname) {
      return $this->makefile($dirname, true);
}

// returns true if there exists a page with path equal to $path
function exists($path) {
      $filename = $this->storage($path);
      return is_dir($filename);
}

##
## set correct permissions & owner for a file/dir
## returns false on failure
##

function setperm($filename) {
      $ok = true;
      
      if (is_dir($filename)) $perm = $this->prop->value('dir-perm');
      else                   $perm = $this->prop->value('file-perm');

      if ($perm != '')
            $ok = chmod($filename, octdec($perm));
            
      $group = $this->prop->value('file-group');
      if ($group != '') {
            if (!chgrp($filename, $group)) {
                  d::error("Could not set group '$group' on file '$filename'. Make sure that www-data is part of $group.");
                  $ok = false;
            }
      }

      return $ok;
}

##
## recursively copy a directory
##

function cpdir($source, $dest) {
      $ok=true;

      #error_log("$source --> $dest");
      // Simple copy for a file
      if (is_file($source)) {
            $ok = copy($source, $dest);
            $ok = $ok && $this->setperm($dest);
            return $ok;
      }
 
      // Make destination directory
      if (!is_dir($dest)) {
            $ok = $this->makedir($dest);
      }
 
      // Loop through the folder
      if($entries = glob($source."/*")) {
            foreach($entries as $entry) {
                  $entry=basename($entry);
                  if ($dest !== "$source/$entry") {
                       $ok = $ok && $this->cpdir("$source/$entry", "$dest/$entry");
                  }
            }
      }
      return $ok;
}



} // end class

####################################################
## helper functions
####################################################

##
## recursive directory deletion
## 
function rmdirr($dir) {
      if($objs = glob($dir."/*")){
            foreach($objs as $obj) {
                  is_dir($obj)? rmdirr($obj) : unlink($obj);
            }
      }
      return rmdir($dir);
}

 
#
# mime_content_type doesn't seem to work.
# 
function mime_type($file) {
      preg_match('/^.*\.(.*)$/',$file,$matches);
      return isset($matches[1]) ? $matches[1] : ''; 
}

function find_multilingual_content_file($storage, $type, $version='') {
      $languages = Lang::priority();
      $lang_len = count( $languages );
      $i = 0;
      if ($version != '') {
            $storage = "$storage/b.history";
            $version = "$version.";
      }
      $contentfile = "$storage/b.$languages[$i].$version$type";
      while ( !is_file( $contentfile ) && ( $i < $lang_len ) ) {
            $contentfile = "$storage/b.$languages[$i].$version$type";
            $i++;
      }
      return $contentfile;
}

// returns a list of all content files, for the given type.
function find_all_content_files($storage, $type) {
      $handle = opendir($storage);
      $files = array();
      while ($filename = readdir($handle)) { 
            if (preg_match("/^b\..*\.$type$/",$filename))
                  $files[] = $filename;
      }
      closedir($handle);
      return $files;
}

?>

Generated by  Doxygen 1.6.0   Back to index