* @license PHP License
* @package WB
* @subpackage content
*/
/**
* Load required class
*/
WBClass::load('WBContent');
/**
* Content component: TableEditor
*
* @version 1.0.0
* @package WB
* @subpackage content
*/
class WBContent_TableEditor extends WBContent
{
/**
* my parameter list
*
* - action either "moveup", "movedown", "list", "add", "edit", "rm", "display"
* - requiredgroup (tableeditor)
* - requiredgroupmanager (tableadmin)
* - showsomeoneelses (1) display other users records, too
* - table (blog)
* - tableenv if table env is set, it will be used to load config and templates
* - limit (20)
* - goto pager's page (0)
* - id (__new)
* - managesortcolumn column name or empty ("")
* - levels number of levels to move, if managesortcolumn applies (1)
* - addcurrentuser either "new", "always" or "never" ("new")
* - searchfields (array()) list of columns that are allowed to search in using LIKE
* - usefilter (0) filter lists, display filter form, use form definition
* in filter.xml and filter.tmpl to render form.
*
* @var array
*/
protected $config = array(
'action' => 'list',
'requiredgroup' => 'tableeditor',
'requiredgroupmanager' => 'tableadmin',
'showsomeoneelses' => 1,
'table' => 'blog',
'tableenv' => '',
'limit' => '20',
'goto' => '0',
'id' => '__new',
'managesortcolumn' => '',
'levels' => 1,
'addcurrentuser' => 'new',
'searchfields' => array(),
'usefilter' => 0
);
/**
* table
* @var WBDatasource_Table
*/
protected $table;
/**
* id of current record of table
* @var string
*/
protected $id = '__new';
/**
* relation table
* @var WBDatasource_RelationTable
*/
private $relTable;
/**
* search and filter clause
* @var array
*/
private $clause = array();
/**
* 2nd constructor
*/
protected function init()
{
$this->table = WBClass::create( 'WBDatasource_Table' );
}
/**
* run
*
* run component
*
* @return array parameter list
*/
public function run()
{
if (!$this->isUserInGroup($this->config['requiredgroup'])
&& !in_array($this->config['action'], array('display', 'list'))) {
$this->loadTemplates('anon');
return $this->config;
}
// by default, table env is the same as table
if (empty($this->config['tableenv'])) {
$this->config['tableenv'] = $this->config['table'];
}
// patch tmplDir with table name
$tmplDir = $this->config['tmplDir'];
$this->config['tmplDir'] = $tmplDir . '/' . $this->config['tableenv'];
if ($this->isUserInGroup($this->config['requiredgroup'])) {
$this->table->switchTranslation(false);
}
$p = null;
$this->config['action'] = strtolower($this->config['action']);
// managesortcolumn is required
if (!empty($this->config['managesortcolumn'])) {
$this->config['level_' . $this->config['levels'] . '_checked'] = 'selected="selected" checked="checked"';
}
else if (in_array($this->config['action'], array('moveup', 'movedown'))) {
$this->config['action'] = 'edit';
}
// administrators may manage other's entries
$foreign = $this->user->getId();
if ($this->isUserInGroup($this->config['requiredgroupmanager']) || WBContent::GROUP_ANON == $this->config['requiredgroupmanager']) {
$foreign = null;
}
if (in_array($this->config['action'], array('display', 'list')) && $this->config['showsomeoneelses']) {
$foreign = null;
}
$search = $this->getSearchQuery();
// reset id
$this->id = $this->config['id'];
$this->config['id'] = '__new';
// load record
$p = array(array());
if($this->id != '__new') {
$p = $this->table->get($this->config['table'], $this->id, $foreign);
}
if (count($p) != 1) {
$p = array(array());
$this->id = '__new';
}
$p = $p[0];
$this->injectFilter();
$this->tmpl->addGlobalVar('id', $this->id);
switch ($this->config['action']) {
case 'moveup':
$this->moveUp($p);
$this->loadTemplates('list');
break;
case 'movedown':
$this->moveUp($p, false);
$this->loadTemplates('list');
break;
case 'add':
$this->id = '__new';
$this->processForm('edit', $p);
break;
case 'edit':
$this->addRelatedData($p);
$tmp = array();
foreach ($p as $k => $v) {
if (is_scalar($v)) {
$tmp[$k] = $v;
}
}
$this->tmpl->addGlobalVars($tmp);
$this->processForm('edit', $p);
break;
case 'rm':
$this->tmpl->addGlobalVars($p);
// delete?
if ($this->config['action'] == 'rm' && $this->id != '__new') {
if ($this->req->get('force', 'no') != 'yes') {
$this->loadTemplates('rm');
$this->config['tmplDir'] = $tmplDir;
return $this->config;
break;
}
$this->table->delete($this->config['table'], $this->id, $foreign);
$this->loadTemplates('rmDeleted');
$this->config['tmplDir'] = $tmplDir;
return $this->config;
}
break;
case 'display':
$this->tmpl->addGlobalVars($p);
$this->loadTemplates('display');
break;
case 'list':
default:
$this->loadTemplates('list');
break;
}
$this->displayList($foreign, $search);
// undo patch of tmplDir
$this->config['tmplDir'] = $tmplDir;
// make config available in template
$configTmpl = array();
foreach ($this->config as $i => $c) {
if (is_scalar($c)) {
$configTmpl[$i] = $c;
}
}
$this->tmpl->addGlobalVars($configTmpl, 'CONFIG_');
return $this->config;
}
/**
* Set filter clause
*
* Allow (sub classes) to append or override clause
* @param array $clause
* @param bool $append
*/
protected function setClause($clause, $append = false)
{
if ($append) {
$this->clause[] = $clause;
return;
}
$this->clause = $clause;
}
/**
* Inject filter parameter
*
* In case filters should be used, process filter form and add values to
* clause.
*
* @see onFilterValid()
*/
protected function injectFilter()
{
if (!$this->config['usefilter']) {
return;
}
$this->req->set('filter_save', 'always');
$this->processForm('filter', array(), 'filter_save');
$this->req->set('filter_save', null);
$html = $this->processForm('filter', array(), 'filter_save');
$this->tmpl->addGlobalVar('filter', $html);
}
/**
* Select filter
*
* Use filter values to create search clause and build get-parameter string
* as well as list of hidden input fields to pass filter options through pages
* and forms.
*
* @param patForms $form
* @param array $values
*/
public function onFilterValid($form, $values)
{
$get = array();
$input = array();
foreach ($values as $key => $value) {
// build search filter parameter
if (is_array($value)) {
foreach ($value as $v) {
$get[] = sprintf('%s[]=%s', $key, urlencode($v));
$input[] = sprintf('', $key, addslashes($v));
}
} else {
$get[] = sprintf('%s=%s', $key, urlencode($value));
$input[] = sprintf('', $key, addslashes($value));
}
// build clause
if ('__NULL' == $value) {
continue;
}
if ('__ZERO' == $value) {
$value = 0;
}
$field = explode('_', $key, 2);
$field = $field[1];
$clause = array(
'field' => $field,
'value' => $value
);
if (is_array($value)) {
$clause['relation'] = 'in';
}
$this->setClause($clause, true);
}
$this->tmpl->addGlobalVar('search_filter_url', implode('&', $get));
$this->tmpl->addGlobalVar('search_filter_input_hidden', implode('', $input));
return false;
}
/**
* display list of records in table
*
* In case there is a template named "list_entry", it will be filled with paged
* rows of table
*
* @param string $foreign key of user table
* @param array $search
*/
protected function displayList($foreign, $search)
{
if (!$this->tmpl->exists('list_entry')) {
return;
}
$clause = $this->clause;
$options = array();
$id = __CLASS__ . '-' . $this->part . '-'. $this->config['table'] . '-' . $this->config['tableenv'];
if ($this->config['limit'] && intval($this->config['limit']) > 0) {
$options['limit'] = intval($this->config['limit']);
}
$this->buildSearchClause($search, $clause);
$pager = $this->table->getPager($id, $this->config['table'], $foreign, $clause, $options);
$this->tmpl->addGlobalVars($pager->browse($this->config['goto']), 'pager_');
$this->tmpl->addRows('list_entry', $pager->get());
}
/**
* Build search clause from query string
*
* See for search string and try to transform it to clause. Add clause to given clause (reference).
*
* 1) Normalize search query
* split to words, remove spaces, remove shorts words (less than 3 chars)
* 2) Build like statement for all searchable fields and words
* 3) Add to clause as complex sub-clause
*
* CAUTION: make sure, that search query is passed by pager as well.
*
* @param array $query
* @param array $clause
*/
private function buildSearchClause($query, &$clause)
{
// search for strings in all searchfields
foreach ($query as $q) {
$c = array();
foreach ($this->config['searchfields'] as $f) {
$c[] = array(
'field' => $f,
'relation' => 'like',
'value' => $q
);
}
$clause[] = array(
'type' => 'complex',
'bond' => 'or',
'clause' => $c
);
}
}
/**
* Get search query array
*
* Extract relevent search query from request parameter and add as global
* template variables
*
* @return array
*/
private function getSearchQuery()
{
if (empty($this->config['searchfields'])) {
return array();
}
$query = trim($this->req->get('searchquery', ''));
if (empty($query)) {
return array();
}
$query = urldecode($query);
// normalize query
$query = explode(' ', $query);
$query = array_map('trim', $query);
foreach ($query as $i => $q) {
if (empty($q) || 3 > strlen($q)) {
unset($query[$i]);
}
}
$query = array_values($query);
$searchquery = implode(' ', $query);
$this->tmpl->addGlobalVar('search_query', $searchquery);
$this->tmpl->addGlobalVar('search_query_url', urlencode($searchquery));
return $query;
}
/**
* receive output
*
* fetch output of this content component
*
* @return string
*/
public function getString()
{
return $this->tmpl->getParsedTemplate('snippet');
}
/**
* Save user's primary data
*
* Update user's forename and surname
*
* @param patForms $form
* @param array $values
*/
public function onEditValid($form, $values)
{
$new = false;
if ('__new' == $this->id) {
$new = true;
}
// set user id
if (!empty($this->config['addcurrentuser'])) {
switch ($this->config['addcurrentuser']) {
case 'new':
if ($new) {
$values[$this->table->getIdentifier('user')] = $this->user->getId();
}
break;
case 'always':
$values[$this->table->getIdentifier('user')] = $this->user->getId();
break;
default:
case 'never':
break;
}
}
if ($this->initRelationTable()) {
$this->relTable->extract($values);
}
$this->tmpl->addGlobalVars($values);
// add value for sortcolumn
if ($new && !empty($this->config['managesortcolumn'])) {
$col = $this->config['managesortcolumn'];
$options = array(
'limit' => 1,
'order' => array(
'field' => $col,
'asc' => 0
),
'column' => array($col)
);
$clause = array();
$last = $this->table->get($this->config['table'], null, null, $clause, $options);
if (empty($last)) {
$values[$col] = 0;
} else {
$values[$col] = ++$last[0][$col];
}
}
// delete old relations
if (!$new && $this->initRelationTable()) {
$this->relTable->delete($this->id);
}
$id = $this->table->save($this->config['table'], $this->id, $values);
if ($this->initRelationTable()) {
$this->relTable->add($id);
}
$eData = array(
'table' => $this->config['table'],
'id' => $id,
'new' => intval($new),
'save' => $values
);
WBEvent::trigger('tableeditor:save:' . $this->config['table'], 'Saved record in table', $eData);
$this->tmpl->addGlobalVar('id', $id);
return true;
}
/**
* Load data from related tables
*
* Resolve foreign keys stored in other tables and merge widata
*
* @param array &$data
*/
private function addRelatedData(&$data)
{
if ('__new' == $this->id) {
return;
}
if (!$this->initRelationTable()) {
return;
}
$this->relTable->inject($data);
}
/**
* Start relation table
*
* If relations are configured, start relation table object
*/
private function initRelationTable()
{
if ($this->relTable) {
return true;
}
// load relation config
$config = WBClass::create('WBConfig');
$config->load($this->getFormConfigDir(). '/edit');
$conf = $config->get('relation', array());
if (empty($conf)) {
return false;
}
$this->relTable = WBClass::create('WBDatasource_RelationTable');
$this->relTable->setTable($this->table);
$this->relTable->setConfig($this->config['table'], $conf);
return true;
}
/**
* move record up in list
*
* Aktually swap position with next record to move current record
* up or down.
*
* @param array $data record to move
* @param bool $up move up (true) or down (false)
*/
private function moveUp($data, $up = true)
{
if (empty($this->config['managesortcolumn'])) {
return;
}
$col = $this->config['managesortcolumn'];
// shortcut applies for moving up first row (which is on top already)
if ($up && 1 > $data[$col]) {
return;
}
$levels = intval($this->config['levels']);
if ($levels < 1) {
$levels = 1;
}
// find other record to swap with
$options = array(
'limit' => $levels,
'order' => array(
'field' => $col,
'asc' => 0
),
'column' => array($col)
);
$clause = array();
$clause[] = array(
'field' => $col,
'relation' => 'lt',
'value' => $data[$col]
);
if (!$up) {
$clause[0]['relation'] = 'gt';
$options['order']['asc'] = 1;
}
$other = $this->table->get($this->config['table'], null, null, $clause, $options);
// if there is not upper entry, skip the rest
if (empty($other)) {
return;
}
// selected too many
if (count($other) > $levels) {
return;
}
// save current records in new position
$last = array_pop($other);
$save = array(
$col => $last[$col]
);
$this->table->save($this->config['table'], $this->id, $save);
$primary = $this->table->getIdentifier($this->config['table']);
// move all other items to the next position
while (0 < count($other)) {
$id = $last[$primary];
$last = array_pop($other);
$save = array(
$col => $last[$col]
);
$this->table->save($this->config['table'], $id, $save);
}
// save last item in current item's previous position
$id = $last[$primary];
$save = array(
$col => $data[$col]
);
$this->table->save($this->config['table'], $id, $save);
}
/**
* location of form config
*
* Return sub directory where form element definitions are located
*
* @return string folder
*/
protected function getFormConfigDir()
{
return 'table/' . $this->config['tableenv'];
}
/**
* Add form rules to just created form
*
*
*
* @param patForms $form object
* @param string name of the xml- and template-filename
* @param array $elements list of elements current form
* @return bool true on success
*/
protected function insertFormRules(&$form, $name, $elements)
{
return true;
}
}