<?php
/*
 * $RCSfile: CustomFieldHelper.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.
 */
/**
 * @version $Revision: 1.14 $ $Date: 2005/08/23 03:49:37 $
 * @package CustomField
 * @author Alan Harder <alan.harder@sun.com>
 */

/**
 * Session key for storing item admin mode
 */
define('CUSTOM_FIELD_SESSION_KEY', 'customfield.adminMode.itemId');

GalleryCoreApi::relativeRequireOnce('modules/customfield/classes/CustomFieldInterface_1_0.class');

/**
 * A helper class for the Custom Field module.
 *
 * @package CustomField
 * @subpackage Classes
 */
class CustomFieldHelper extends CustomFieldInterface_1_0 /* and GalleryEventListener */ {

    /**
     * Load and decode module parameters
     *
     * @param int id of container; check for album-specific settings
     * @param boolean fallback to site-defaults if no album-specific settings found
     * @param array sets to load; all by default
     * @return array GalleryStatus a status code
     *               mixed containing custom field data
     *               boolean true if album-specific settings are returned
     * @static
     */
    function loadParameters($containerId=0, $fallback=true,
			    $sets = array('common', 'album', 'photo')) {
	$isContainer = ($containerId > 0);
	list ($ret, $param) =
	    GalleryCoreApi::fetchAllPluginParameters('module', 'customfield', $containerId);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null, null);
	}
	if (empty($param) && $containerId > 0 && $fallback) {
	    list ($ret, $param) = GalleryCoreApi::fetchAllPluginParameters('module', 'customfield');
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null, null);
	    }
	    $isContainer = false;
	}
	foreach ($sets as $set) {
	    $result[$set] = array();
	    foreach ((!empty($param[$set]) ? explode('|', $param[$set]) : array()) as $tmp) {
		$list = explode('`', $tmp);
		$result[$set][] = array('field' => $list[0],
					'summary' => ($list[1]=='1'), 'detail' => ($list[2]=='1'),
					'choices' => array_splice($list, 3));
	    }
	}
	return array(GalleryStatus::success(), $result, $isContainer);
    }

    /**
     * Encode and save module parameters
     *
     * @param array data to save
     * @param int id of container; to save album-specific settings
     * @return GalleryStatus a status code
     * @static
     */
    function saveParameters($param, $containerId=0) {
	foreach (array('common', 'album', 'photo') as $set) {
	    if (!isset($param[$set])) {
		continue;
	    }
	    $list = array();
	    foreach ($param[$set] as $item) {
		$list[] = $item['field'] . '`' . ($item['summary'] ? 1 : 0) . '`'
			. ($item['detail'] ? 1 : 0)
			. (!empty($item['choices']) ? ('`' . implode('`', $item['choices'])) : '');
	    }
	    $ret = GalleryCoreApi::setPluginParameter('module', 'customfield', $set,
						      implode('|', $list), $containerId);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}
	return GalleryStatus::success();
    }

    /**
     * Add new custom field in given set/container.
     *
     * @param string field name
     * @param string set (common, album, photo)
     * @param int id of container; to save album-specific settings
     * @return array object GalleryStatus a status code
     *               boolean true on success, false on duplicate field name
     * @static
     */
    function addField($newField, $set, $containerId=0) {
	list ($ret, $param) = CustomFieldHelper::loadParameters($containerId, false);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	foreach ($param as $list) {
	    $idx = CustomFieldHelper::findParameter($list, $newField);
	    if ($idx >= 0) {
		return array(GalleryStatus::success(), false);
	    }
	}
	$param[$set][] = array('field' => $newField,
			       'summary' => false, 'detail' => true, 'choices' => array());
	$ret = CustomFieldHelper::saveParameters(array($set => $param[$set]), $containerId);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	return array(GalleryStatus::success(), true);
    }

    /**
     * Find a field in a parameter list
     *
     * @param array parameter list
     * @param string field to find
     * @return int index or -1 if not found
     * @static
     */
    function findParameter(&$list, $field) {
	foreach ($list as $i => $item) {
	    if ($item['field'] == $field) {
		return $i;
	    }
	}
	return -1;
    }

    /**
     * Delete custom field values for given field
     *
     * @param string field to delete
     * @param int id of container, to delete only for specific album
     * @param string 'album' or 'photo' -- don't delete values for items of this type
     * @return GalleryStatus a status code
     * @static
     */
    function deleteField($field, $containerId=0, $exceptType='') {
	GalleryCoreApi::relativeRequireOnce('modules/customfield/classes/CustomFieldMap.class');
	switch ($exceptType) {

	case 'album':
	    $setTypes = array(0,2);
	case 'photo':
	    if (!isset($setTypes)) {
		$setTypes = array(0,1);
	    }
	    $ret = CustomFieldMap::removeMapEntry( array('field' => $field,
							 'setId' => $containerId,
							 'setType' => $setTypes) );
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    break;

	default:
	    $ret = CustomFieldMap::removeMapEntry( array('field' => $field,
							 'setId' => $containerId) );
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}
	return GalleryStatus::success();
    }

    /**
     * Load custom field values for specified items
     *
     * @param array  object GalleryEntity item, ...
     * @param string if specified then only include fields for that view (summary or detail)
     * @param string if specified then include in results all valid fields for
     *               this type, even if item has no value
     * @return array GalleryStatus a status code
     *               array(itemId => array(field => value))
     *               array(itemId => array) loadParameters results
     *		     array(itemId => boolean) loadParameters results
     * @static
     */
    function fetchFieldValues($items, $viewType=null, $fillSet=null) {
	global $gallery;
	$data = $result = array();

	if (empty($items) || !is_array($items)) {
	    return array(GalleryStatus::error(ERROR_BAD_PARAMETER, __FILE__, __LINE__),
		    null, null, null);
	}

	foreach ($items as $item) {
	    $itemId = $item->getId();
	    $itemIds[] = $itemId;
	    $result[$itemId] = array();
	    $containerId =
		GalleryUtilities::isA($item, 'GalleryAlbumItem') ? $itemId : $item->getParentId();

	    list ($ret, $param[$itemId], $isContainer[$itemId]) =
		CustomFieldHelper::loadParameters($containerId);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null, null, null);
	    }
	}

	$query = '
	SELECT [CustomFieldMap::itemId], [CustomFieldMap::field], [CustomFieldMap::value]
	FROM [CustomFieldMap]
	WHERE [CustomFieldMap::itemId] IN (' . GalleryUtilities::makeMarkers($itemIds) . ')';

	list ($ret, $searchResults) = $gallery->search($query, $itemIds);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null, null, null);
	}

	while ($rec = $searchResults->nextResult()) {
	    $data[$rec[0]][$rec[1]] = $rec[2];
	}

	foreach ($itemIds as $itemId) {
	    foreach (array('common', 'album', 'photo') as $set) {
		foreach ($param[$itemId][$set] as $it) {
		    $field = $it['field'];
		    if (isset($viewType) && !$it[$viewType]) {
			continue;
		    }
		    if (isset($data[$itemId][$field])) {
			$result[$itemId][$field] = $data[$itemId][$field];
		    }
		    else if (isset($fillSet) && ($set == 'common' || $set == $fillSet)) {
			$result[$itemId][$field] = '';
		    }
		}
	    }
	}

	return array(GalleryStatus::success(), $result, $param, $isContainer);
    }

    /**
     * Save custom field values for specified item, overwriting any existing values
     *
     * @param object GalleryEntity item
     * @param array (field => value)
     * @return GalleryStatus a status code
     * @static
     */
    function saveFieldValues(&$item, $fields) {
	$set = GalleryUtilities::isA($item, 'GalleryAlbumItem') ? 1
	     : (GalleryUtilities::isA($item, 'GalleryPhotoItem') ? 2 : 0);
	$containerId = ($set == 1) ? $item->getId() : $item->getParentId();
	list ($ret, $param) =
	    GalleryCoreApi::fetchAllPluginParameters('module', 'customfield', $containerId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	$isContainer = !empty($param);
	GalleryCoreApi::relativeRequireOnce('modules/customfield/classes/CustomFieldMap.class');
	$ret = CustomFieldMap::removeMapEntry(array('itemId' => $item->getId()));
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	foreach ($fields as $field => $value) {
	    if (!empty($value)) {
		$ret = CustomFieldMap::addMapEntry(
		    array('itemId' => $item->getId(), 'field' => $field, 'value' => $value,
			  'setId' => $isContainer ? $containerId : 0, 'setType' => $set));
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}
	    }
	}
	return GalleryStatus::success();
    }

    /**
     * Event handler for GalleryEntity::delete event
     * Remove any custom field values for entity being deleted.
     *
     * @see GalleryEventListener::handleEvent
     */
    function handleEvent($event) {
	GalleryCoreApi::relativeRequireOnce('modules/customfield/classes/CustomFieldMap.class');
	if ($event->getEventName() == 'GalleryEntity::delete') {
	    $item = $event->getEntity();
	    $ret = CustomFieldMap::removeMapEntry(array('itemId' => $item->getId()));
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	}
	return array(GalleryStatus::success(), null);
    }

    /**
     * Handler for Custom Field Admin actions
     *
     * @param mixed form data
     * @param int id of container, for album-specific settings
     * @return array GalleryStatus a status code
     *               mixed status to return from controller
     * @static
     */
    function handleAdminAction($form, $containerId=0) {
	$status = array();

	if (isset($form['action']['save'])) {
	    list ($ret, $param) = CustomFieldHelper::loadParameters($containerId, false);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    foreach (array('common', 'album', 'photo') as $set) {
		$newParam[$set] = array();
		if (isset($form[$set]['index'])) {
		    foreach ($form[$set]['index'] as $newidx => $oldidx) {
			$newParam[$set][] =
				array('field' => $param[$set][$oldidx]['field'],
				      'summary' => isset($form[$set]['summary'][$newidx]),
				      'detail' => isset($form[$set]['detail'][$newidx]),
				      'choices' => $param[$set][$oldidx]['choices']);
		    }
		}
	    }
	    $ret = CustomFieldHelper::saveParameters($newParam, $containerId);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    $status['saved'] = 1;
	} else if (isset($form['action']['add'])) {
	    foreach ($form['action']['add'] as $set => $tmp) {}
	    $newField = str_replace('|', ':', str_replace('`', "&#039;", $form[$set]['newField']));
            if (empty($newField)) {
		$status['error']['empty'] = 1;
                return array(GalleryStatus::success(), $status);
            }
	    list ($ret, $added) = CustomFieldHelper::addField($newField, $set, $containerId);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    if ($added) {
		$status['added'] = 1;
	    } else {
		$status['error']['duplicate'] = 1;
	    }
	} else if (isset($form['action']['go'])) {
	    foreach ($form['action']['go'] as $set => $tmp) {}
	    list ($ret, $param, $isContainer) =
		    CustomFieldHelper::loadParameters($containerId, false);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    $idx = CustomFieldHelper::findParameter($param[$set], $form[$set]['goField']);
	    if ($idx < 0) {
		return array(GalleryStatus::error(ERROR_MISSING_OBJECT, __FILE__, __LINE__), null);
	    }
	    switch ($form[$set]['goAction']) {

	    case 'common':
		// Remove from $set, add to common; no db change
		$param['common'][] = $param[$set][$idx];
		unset($param[$set][$idx]);
		$ret = CustomFieldHelper::saveParameters(
		    array('common' => $param['common'], $set => $param[$set]), $containerId);
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
		break;

	    case 'remove':
		// Remove from $set; delete from all items
		unset($param[$set][$idx]);
		$ret = CustomFieldHelper::saveParameters(array($set => $param[$set]), $containerId);
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
		$ret = CustomFieldHelper::deleteField($form[$set]['goField'],
						      $isContainer ? $containerId : 0);
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
		$status['removed'] = 1;
		break;

	    case 'album':
		// Remove from common, add to album; delete from non-album items
	    case 'photo':
		// Remove from common, add to photo; delete from non-photo items
		$newSet = $form[$set]['goAction'];
		$param[$newSet][] = $param[$set][$idx];
		unset($param[$set][$idx]);
		$ret = CustomFieldHelper::saveParameters(
		    array('common' => $param['common'], $newSet => $param[$newSet]), $containerId);
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
		$ret = CustomFieldHelper::deleteField($form[$set]['goField'],
						      $isContainer ? $containerId : 0, $newSet);
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
		break;

	    default:
		return array(GalleryStatus::error(ERROR_UNIMPLEMENTED, __FILE__, __LINE__), null);
	    }
	    if (empty($status)) {
		$status['moved'] = 1;
	    }
	} else if (isset($form['action']['picklist'])) {
	    $choices = array();
	    foreach (array_map('trim', explode("\n", $form['picklist'])) as $tmp) {
		if (!empty($tmp)) {
		    $choices[] = $tmp;
		}
	    }
	    list ($ret, $param) = CustomFieldHelper::loadParameters($containerId, false);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    foreach ($param as $set => $list) {
		$idx = CustomFieldHelper::findParameter($list, $form['pickField']);
		if ($idx >= 0) {
		    $param[$set][$idx]['choices'] = $choices;
		    $ret = CustomFieldHelper::saveParameters(
					      array($set => $param[$set]), $containerId);
		    if ($ret->isError()) {
			return array($ret->wrap(__FILE__, __LINE__), null);
		    }
		    $status['picklist'] = 1;
		    break;
		}
	    }
	} /* else $form['action']['reset'] */

	return array(GalleryStatus::success(), $status);
    }

    /**
     * Load form data for Admin template
     *
     * @param mixed form
     * @param int id of container, for album-specific settings
     * @return object GalleryStatus a status code
     * @static
     */
    function loadAdminForm(&$form, $containerId=0) {
	list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'customfield');
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	$form['set'] = array();
	$form['set'][] = array('key' => 'common', 'name' => $module->translate('Common Fields'));
	$form['set'][] = array('key' => 'album', 'name' => $module->translate('Album Fields'));
	$form['set'][] = array('key' => 'photo', 'name' => $module->translate('Photo Fields'));

	list ($ret, $form['fields']) = CustomFieldHelper::loadParameters($containerId, false);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	return GalleryStatus::success();
    }

    /**
     * @see CustomFieldInterface_1_0::createCustomFields()
     */
    function createCustomFields($fieldNames, $containerId=0) {
	$duplicates = array();
	foreach ($fieldNames as $fieldName) {
	    list ($ret, $added) = CustomFieldHelper::addField($fieldName, 'common', $containerId);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    if (!$added) {
		$duplicates[] = $fieldName;
	    }
	}
	return array(GalleryStatus::success(), $duplicates);
    }

    /**
     * @see CustomFieldInterface_1_0::setCustomFieldValues()
     */
    function setCustomFieldValues($itemId, $data) {
	list ($ret, $item) = GalleryCoreApi::loadEntitiesById($itemId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	$ret = CustomFieldHelper::saveFieldValues($item, $data);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	return GalleryStatus::success();
    }
}
?>
