<?php
/*
 * $RCSfile: GalleryItem.class,v $
 *
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2005 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.
 */
/**
 * @package GalleryCore
 * @subpackage Classes
 * @version $Revision: 1.88 $ $Date: 2005/08/23 03:49:03 $
 * @author Bharat Mediratta <bharat@menalto.com>
 */

/**
 * Load the parent class
 */
GalleryCoreApi::relativeRequireOnce('modules/core/classes/GalleryFileSystemEntity.class');

/**
 * Abstract base class for all objects in the Gallery
 * composite tree
 *
 * Container for all functionality and data common objects that
 * Gallery can deal with.  Each GalleryItem has the capacity
 * to own other GalleryItems in a parent-child relationship.
 * That capacity can be enabled or disabled by subclasses of
 * GalleryItem.
 *
 * @g2 <class-name>GalleryItem</class-name>
 * @g2 <parent-class-name>GalleryFileSystemEntity</parent-class-name>
 * @g2 <schema>
 * @g2   <schema-major>1</schema-major>
 * @g2   <schema-minor>1</schema-minor>
 * @g2 </schema>
 * @g2 <requires-id/>
 *
 * @package GalleryCore
 * @abstract
 */
class GalleryItem_core extends GalleryFileSystemEntity {

    /*
     * ****************************************
     *                 Members
     * ****************************************
     */

    /**
     * Can this item contain children?
     *
     * @g2 <member>
     * @g2   <member-name>canContainChildren</member-name>
     * @g2   <member-type>BOOLEAN</member-type>
     * @g2   <required/>
     * @g2 </member>
     *
     * @var int $_canContainChildren
     * @access private
     */
    var $_canContainChildren;

    /**
     * The (long) description of this item
     *
     * @g2 <member>
     * @g2   <member-name>description</member-name>
     * @g2   <member-type>TEXT</member-type>
     * @g2 </member>
     *
     * @var int $_description
     * @access private
     */
    var $_description;

    /**
     * A set of keywords that describe this item
     *
     * @g2 <member>
     * @g2   <member-name>keywords</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>LARGE</member-size>
     * @g2   <indexed/>
     * @g2 </member>
     *
     * @var string $_keywords
     * @access private
     */
    var $_keywords;

    /**
     * The id of the User who owns this item
     *
     * @g2 <member>
     * @g2   <member-name>ownerId</member-name>
     * @g2   <member-type>INTEGER</member-type>
     * @g2   <indexed/>
     * @g2   <required/>
     * @g2 </member>
     *
     * @var int $_ownerId
     * @access private
     */
    var $_ownerId;

    /**
     * The summary of this item
     *
     * @g2 <member>
     * @g2   <member-name>summary</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>LARGE</member-size>
     * @g2   <indexed/>
     * @g2 </member>
     *
     * @var int $_summary
     * @access private
     */
    var $_summary;

    /**
     * The (short) title of this item
     *
     * @g2 <member>
     * @g2   <member-name>title</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>MEDIUM</member-size>
     * @g2   <indexed/>
     * @g2 </member>
     *
     * @var int $_title
     * @access private
     */
    var $_title;

    /**
     * Date and time marking the beginning of the view count
     *
     * @g2 <member>
     * @g2   <member-name>viewedSinceTimestamp</member-name>
     * @g2   <member-type>INTEGER</member-type>
     * @g2   <required/>
     * @g2 </member>
     *
     * @var int $_viewedSinceTimestamp
     * @access private
     */
    var $_viewedSinceTimestamp;

    /**
     * Date and time when this item was originally captured (i.e. photographed, filmed, etc)
     *
     * @g2 <member>
     * @g2   <member-name>originationTimestamp</member-name>
     * @g2   <member-type>INTEGER</member-type>
     * @g2   <required/>
     * @g2 </member>
     *
     * @var int $_originationTimestamp
     * @access private
     */
    var $_originationTimestamp;

    /*
     * ****************************************
     *                 Methods
     * ****************************************
     */

    /**
     * Create a new instance of this GalleryEntity in the persistent store
     *
     * Let the parent do its work, then add any initialization specific to this
     * class.
     *
     * @param int the id of the GalleryEntity parent of this object
     * @param string the path component of this new object
     * @param int the id of the parent object
     * @return object GalleryStatus a status code
     */
    function create($parentId, $path) {
	global $gallery;

	if (!isset($path) || !isset($parentId)) {
	    return GalleryStatus::error(ERROR_BAD_PARAMETER, __FILE__, __LINE__);
	}

	list($ret, $parent) = GalleryCoreApi::loadEntitiesById($parentId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	if (!$parent->getCanContainChildren()) {
	    return GalleryStatus::error(ERROR_ILLEGAL_CHILD, __FILE__, __LINE__);
	}

	$ret = parent::create($parentId, $path);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* Record the owner */
	$this->setOwnerId($gallery->getActiveUserId());

	/* Initialize the viewedSince timestamp */
	$this->setViewedSinceTimestamp(time());

	/* By default, items can't contain children */
	$this->setCanContainChildren(false);

	/* Origination timestamp defaults to now */
	$this->setOriginationTimestamp(time());

	return GalleryStatus::success();
    }

    /**
     * Create a root level instance of this GalleryEntity in the persistent store
     *
     * Let the parent do its work, then add any initialization specific to this
     * class.
     *
     * @param string the path component of this new object
     * @return object GalleryStatus a status code
     */
    function createRoot() {
	global $gallery;

	$ret = parent::createRoot();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* Record the owner */
	$this->setOwnerId($gallery->getActiveUserId());

	/* Initialize the viewedSince timestamp */
	$this->setViewedSinceTimestamp(time());

	/* Origination timestamp defaults to now */
	$this->setOriginationTimestamp(time());

	return GalleryStatus::success();
    }

    /**
     * @see GalleryEntity::createLink()
     */
    function createLink($entity, $parentId) {
	global $gallery;

	$ret = parent::createLink($entity, $parentId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	list($ret, $parent) = GalleryCoreApi::loadEntitiesById($parentId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	if (!$parent->getCanContainChildren()) {
	    return GalleryStatus::error(ERROR_ILLEGAL_CHILD, __FILE__, __LINE__);
	}

	/* Record the owner */
	$this->setOwnerId($gallery->getActiveUserId());

	/* Initialize the viewedSince timestamp */
	$this->setViewedSinceTimestamp(time());

	/* By default, items can't contain children */
	$this->setCanContainChildren(false);

	/* Copy over anything else from the target entity */
	$this->setDescription($entity->getDescription());
	$this->setKeywords($entity->getKeywords());
	$this->setSummary($entity->getSummary());
	$this->setTitle($entity->getTitle());
	$this->setOriginationTimestamp($entity->getOriginationTimestamp());

	return GalleryStatus::success();
    }

    /**
     * Delete this GalleryEntity
     *
     * Delete all of its children also, if it has any
     *
     * @access public
     * @return object GalleryStatus a status code
     */
    function delete() {
	global $gallery;

	/* Delete any children */
	$query = '
	SELECT
	  [GalleryChildEntity::id]
	FROM
	  [GalleryChildEntity]
	WHERE
	  [GalleryChildEntity::parentId] = ?
	';
	list ($ret, $searchResults) =
	    $gallery->search($query, array($this->getId()));
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	while ($result = $searchResults->nextResult()) {
	    $ret = GalleryCoreApi::deleteEntityById($result[0]);
	    /*
	     * Deletes can cascade in interesting ways.  For example, deleting a derivative will
	     * get rid of any other derivatives that are sourced to it, so it's possible that
	     * deleting children here can lead to a MISSING_OBJECT result unless we re-run the
	     * parent/child query each time.  Easier to just ignore the MISSING_OBJECT error
	     * since we only care that it's gone.
	     */
	    if ($ret->isError() && !($ret->getErrorCode() & ERROR_MISSING_OBJECT)) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}

	/* Delete myself */
	$ret = parent::delete();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* Remove all my attributes */
	$ret = GalleryCoreApi::removeItemAttributes($this->getId());
	if ($ret->isError()) {
	    return $ret2->wrap(__FILE__, __LINE__);
	}

	if ($this->getParentId()) {
	    $event = GalleryCoreApi::newEvent('Gallery::ViewableTreeChange');
	    $event->setData(array('userId' => null, 'itemId' => $this->getParentId()));
	    list ($ret) = GalleryCoreApi::postEvent($event);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }

	    /* Instruct G2 to "touch" modification timestamp of parent album at end of request */
	    $gallery->addShutdownAction(array('GalleryCoreApi', 'updateModificationTimestamp'),
					array($this->getParentId()));
	}

	return GalleryStatus::success();
    }

    /**
     * Move item to a new parent
     *
     * @access public
     * @param int the id of the new parent GalleryItem
     * @return object GalleryStatus a status code
     */
    function move($newParentId) {
	$parentId = $this->getParentId();
	$ret = parent::move($newParentId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	$event = GalleryCoreApi::newEvent('Gallery::ViewableTreeChange');
	$event->setData(array('userId' => null, 'itemId' =>
	    empty($parentId) ? $this->getParentId() : array($parentId, $this->getParentId())));
	list ($ret) = GalleryCoreApi::postEvent($event);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	return GalleryStatus::success();
    }

    /**
     * Save the changes to this GalleryItem
     *
     * Save the changes to this GalleryItem.
     *
     * @access public
     * @return object GalleryStatus a status code
     */
    function save() {
	global $gallery;
	$newItem = $this->testPersistentFlag(STORAGE_FLAG_NEWLY_CREATED);
	$changedParent = $this->getModifiedFlag('parentId') != MEMBER_UNMODIFIED;
	if ($changedParent && !empty($this->_persistentStatus->originalValue['parentId'])) {
	    $oldParentId = $this->_persistentStatus->originalValue['parentId'];
	}

	/* Save myself */
	$ret = parent::save();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	if ($newItem) {
	    if ($this->getParentId()) {
		$ret = GalleryCoreApi::copyPermissions($this->getId(), $this->getParentId());
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}

		list ($ret, $parentSequence) =
		    GalleryCoreApi::fetchParentSequence($this->getParentId());
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}
		$parentSequence[] = $this->getParentId();
	    } else {
		$parentSequence = array();
	    }

	    /* Create an empty attribute entry */
	    $ret = GalleryCoreApi::createItemAttributes($this->getId(), $parentSequence);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }

	    if ($this->getParentId()) {
		$event = GalleryCoreApi::newEvent('Gallery::ViewableTreeChange');
		$event->setData(array('userId' => null, 'itemId' => $this->getParentId()));
		list ($ret) = GalleryCoreApi::postEvent($event);
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}
	    }
	}
	if (($newItem || $changedParent) && $this->getParentId()) {
	    /* Instruct G2 to "touch" modification timestamp of parent album at end of request */
	    $gallery->addShutdownAction(array('GalleryCoreApi', 'updateModificationTimestamp'),
					array($this->getParentId()));
	}
	if (!empty($oldParentId)) {
	    $gallery->addShutdownAction(array('GalleryCoreApi', 'updateModificationTimestamp'),
					array($oldParentId));
	}

	return GalleryStatus::success();
    }
}

include(dirname(__FILE__) . '/interfaces/GalleryItem.inc');
?>
