first commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/controllers/listbuilder/ListbuilderGridColumn.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ListbuilderGridColumn
|
||||
*
|
||||
* @ingroup controllers_listbuilder
|
||||
*
|
||||
* @brief Represents a column within a listbuilder.
|
||||
*/
|
||||
|
||||
namespace PKP\controllers\listbuilder;
|
||||
|
||||
use PKP\controllers\grid\GridColumn;
|
||||
use PKP\controllers\listbuilder\users\UserListbuilderGridCellProvider;
|
||||
|
||||
class ListbuilderGridColumn extends GridColumn
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param ListbuilderHandler $listbuilder The listbuilder handler this column belongs to.
|
||||
* @param string $id The optional symbolic ID for this column.
|
||||
* @param string $title The optional title for this column.
|
||||
* @param string $titleTranslated The optional translated title for this column.
|
||||
* @param string $template The optional overridden template for this column.
|
||||
* @param UserListbuilderGridCellProvider $cellProvider The optional overridden grid cell provider.
|
||||
* @param array $flags Optional set of flags for this column's display.
|
||||
*/
|
||||
public function __construct(
|
||||
$listbuilder,
|
||||
$id = '',
|
||||
$title = null,
|
||||
$titleTranslated = null,
|
||||
$template = null,
|
||||
$cellProvider = null,
|
||||
$flags = []
|
||||
) {
|
||||
// Set this here so that callers using later optional parameters don't need to
|
||||
// duplicate it.
|
||||
if ($template === null) {
|
||||
$template = 'controllers/listbuilder/listbuilderGridCell.tpl';
|
||||
}
|
||||
|
||||
// Make the listbuilder's source type available to the cell template as a flag
|
||||
$flags['sourceType'] = $listbuilder->getSourceType();
|
||||
parent::__construct($id, $title, $titleTranslated, $template, $cellProvider, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\controllers\listbuilder\ListbuilderGridColumn', '\ListbuilderGridColumn');
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/controllers/listbuilder/ListbuilderGridRow.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ListbuilderGridRow
|
||||
*
|
||||
* @ingroup controllers_listbuilder
|
||||
*
|
||||
* @brief Handle list builder row requests.
|
||||
*/
|
||||
|
||||
namespace PKP\controllers\listbuilder;
|
||||
|
||||
use PKP\controllers\grid\GridRow;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\NullAction;
|
||||
|
||||
class ListbuilderGridRow extends GridRow
|
||||
{
|
||||
/** @var bool */
|
||||
public $_hasDeleteItemLink;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bool $hasDeleteItemLink
|
||||
*/
|
||||
public function __construct($hasDeleteItemLink = true)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->setHasDeleteItemLink($hasDeleteItemLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a delete item link action or not.
|
||||
*
|
||||
* @param bool $hasDeleteItemLink
|
||||
*/
|
||||
public function setHasDeleteItemLink($hasDeleteItemLink)
|
||||
{
|
||||
$this->_hasDeleteItemLink = $hasDeleteItemLink;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Overridden template methods
|
||||
//
|
||||
/**
|
||||
* @copydoc GridRow::initialize()
|
||||
*/
|
||||
public function initialize($request, $template = 'controllers/listbuilder/listbuilderGridRow.tpl')
|
||||
{
|
||||
parent::initialize($request);
|
||||
|
||||
// Set listbuilder row template
|
||||
$this->setTemplate($template);
|
||||
|
||||
if ($this->_hasDeleteItemLink) {
|
||||
// Add deletion action (handled in JS-land)
|
||||
$this->addAction(
|
||||
new LinkAction(
|
||||
'delete',
|
||||
new NullAction(),
|
||||
'',
|
||||
'remove_item'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GridRow::addAction()
|
||||
*/
|
||||
public function addAction($action, $position = GridRow::GRID_ACTION_POSITION_ROW_LEFT)
|
||||
{
|
||||
return parent::addAction($action, $position);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\controllers\listbuilder\ListbuilderGridRow', '\ListbuilderGridRow');
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/controllers/listbuilder/ListbuilderHandler.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ListbuilderHandler
|
||||
*
|
||||
* @ingroup controllers_listbuilder
|
||||
*
|
||||
* @brief Class defining basic operations for handling Listbuilder UI elements
|
||||
*/
|
||||
|
||||
namespace PKP\controllers\listbuilder;
|
||||
|
||||
use APP\core\Request;
|
||||
use APP\template\TemplateManager;
|
||||
use PKP\controllers\grid\GridHandler;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\LinkActionRequest;
|
||||
use PKP\linkAction\request\NullAction;
|
||||
|
||||
class ListbuilderHandler extends GridHandler
|
||||
{
|
||||
/** @var int Listbuilder source types: text-based, pulldown, ... */
|
||||
public const LISTBUILDER_SOURCE_TYPE_TEXT = 0;
|
||||
public const LISTBUILDER_SOURCE_TYPE_SELECT = 1;
|
||||
|
||||
/** @var int Listbuilder save types */
|
||||
public const LISTBUILDER_SAVE_TYPE_EXTERNAL = 0;
|
||||
public const LISTBUILDER_SAVE_TYPE_INTERNAL = 1;
|
||||
|
||||
/** @var string String to identify optgroup in the returning options data. If you want to use
|
||||
* optgroup in listbuilder select, return the options data in a multidimensional array
|
||||
* array[columnIndex][optgroupId][selectItemId] and also with
|
||||
* array[columnIndex][LISTBUILDER_OPTGROUP_LABEL][optgroupId] */
|
||||
public const LISTBUILDER_OPTGROUP_LABEL = 'optGroupLabel';
|
||||
|
||||
/** @var int Definition of the type of source LISTBUILDER_SOURCE_TYPE_... */
|
||||
public $_sourceType;
|
||||
|
||||
/** @var int Constant indicating the save approach for the LB LISTBUILDER_SAVE_TYPE_... */
|
||||
public $_saveType = self::LISTBUILDER_SAVE_TYPE_INTERNAL;
|
||||
|
||||
/** @var string Field for LISTBUILDER_SAVE_TYPE_EXTERNAL naming the field used to send the saved contents of the LB */
|
||||
public $_saveFieldName = null;
|
||||
|
||||
/**
|
||||
* @copydoc GridHandler::initialize
|
||||
*
|
||||
* @param null|mixed $args
|
||||
*/
|
||||
public function initialize($request, $args = null)
|
||||
{
|
||||
parent::initialize($request, $args);
|
||||
|
||||
if ($this->canAddItems()) {
|
||||
$this->addAction($this->getAddItemLinkAction(new NullAction()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getters and Setters
|
||||
//
|
||||
/**
|
||||
* Get the listbuilder template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
if (is_null($this->_template)) {
|
||||
$this->setTemplate('controllers/listbuilder/listbuilder.tpl');
|
||||
}
|
||||
|
||||
return $this->_template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of source (Free text input, select from list, autocomplete)
|
||||
*
|
||||
* @param int $sourceType LISTBUILDER_SOURCE_TYPE_...
|
||||
*/
|
||||
public function setSourceType($sourceType)
|
||||
{
|
||||
$this->_sourceType = $sourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of source (Free text input, select from list, autocomplete)
|
||||
*
|
||||
* @return int LISTBUILDER_SOURCE_TYPE_...
|
||||
*/
|
||||
public function getSourceType()
|
||||
{
|
||||
return $this->_sourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save type (using this handler or another external one)
|
||||
*/
|
||||
public function setSaveType($saveType)
|
||||
{
|
||||
$this->_saveType = $saveType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the save type (using this handler or another external one)
|
||||
*
|
||||
* @return int LISTBUILDER_SAVE_TYPE_...
|
||||
*/
|
||||
public function getSaveType()
|
||||
{
|
||||
return $this->_saveType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save field name for LISTBUILDER_SAVE_TYPE_EXTERNAL
|
||||
*
|
||||
* @param string $fieldName
|
||||
*/
|
||||
public function setSaveFieldName($fieldName)
|
||||
{
|
||||
$this->_saveFieldName = $fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save field name for LISTBUILDER_SAVE_TYPE_EXTERNAL.
|
||||
* This will be the HTML field that receives the data upon submission.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveFieldName()
|
||||
{
|
||||
assert(isset($this->_saveFieldName));
|
||||
return $this->_saveFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "add item" link action.
|
||||
*
|
||||
* @param LinkActionRequest $actionRequest
|
||||
*
|
||||
* @return LinkAction
|
||||
*/
|
||||
public function getAddItemLinkAction($actionRequest)
|
||||
{
|
||||
return new LinkAction(
|
||||
'addItem',
|
||||
$actionRequest,
|
||||
__('grid.action.addItem'),
|
||||
'add_item'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new row ID from the request. For multi-column listbuilders,
|
||||
* this is an array representing the row. For single-column
|
||||
* listbuilders, this is a single piece of data (i.e. a string or int)
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
*/
|
||||
public function getNewRowId($request)
|
||||
{
|
||||
return $request->getUserVar('newRowId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an entry.
|
||||
*
|
||||
* @param Request $request object
|
||||
* @param mixed $rowId ID of row to modify
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteEntry($request, $rowId)
|
||||
{
|
||||
fatalError('ABSTRACT METHOD');
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist an update to an entry.
|
||||
*
|
||||
* @param Request $request object
|
||||
* @param mixed $rowId ID of row to modify
|
||||
* @param mixed $newRowId ID of the new entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateEntry($request, $rowId, $newRowId)
|
||||
{
|
||||
// This may well be overridden by a subclass to modify
|
||||
// an existing entry, e.g. to maintain referential integrity.
|
||||
// If not, we can simply delete and insert.
|
||||
if (!$this->deleteEntry($request, $rowId)) {
|
||||
return false;
|
||||
}
|
||||
return $this->insertEntry($request, $newRowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a new entry insert.
|
||||
*
|
||||
* @param Request $request object
|
||||
* @param mixed $newRowId ID of row to modify
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function insertEntry($request, $newRowId)
|
||||
{
|
||||
fatalError('ABSTRACT METHOD');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the options for a LISTBUILDER_SOURCE_TYPE_SELECT LB
|
||||
* Should return a multidimensional array:
|
||||
* array(
|
||||
* array('column 1 option 1', 'column 2 option 1'),
|
||||
* array('column 1 option 2', 'column 2 option 2'
|
||||
* );
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions($request)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
//
|
||||
// Publicly (remotely) available listbuilder functions
|
||||
//
|
||||
/**
|
||||
* Fetch the listbuilder.
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*/
|
||||
public function fetch($args, $request)
|
||||
{
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$options = $this->getOptions($request);
|
||||
$availableOptions = false;
|
||||
if (is_array($options) && !empty($options)) {
|
||||
$firstColumnOptions = current($options);
|
||||
$optionsCount = count($firstColumnOptions);
|
||||
if (is_array(current($firstColumnOptions))) { // Options with opt group, count only the selectable options.
|
||||
unset($firstColumnOptions[self::LISTBUILDER_OPTGROUP_LABEL]);
|
||||
$optionsCount--;
|
||||
$optionsCount = count($firstColumnOptions, COUNT_RECURSIVE) - $optionsCount;
|
||||
}
|
||||
|
||||
$listElements = $this->getGridDataElements($request);
|
||||
if (count($listElements) < $optionsCount) {
|
||||
$availableOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
$templateMgr->assign('availableOptions', $availableOptions);
|
||||
|
||||
return $this->fetchGrid($args, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack data to save using an external handler.
|
||||
*
|
||||
* @param string $data (the json encoded data from the listbuilder itself)
|
||||
* @param array $deletionCallback callback to be used for each deleted element
|
||||
* @param array $insertionCallback callback to be used for each updated element
|
||||
* @param array $updateCallback callback to be used for each updated element
|
||||
*/
|
||||
public static function unpack($request, $data, $deletionCallback, $insertionCallback, $updateCallback)
|
||||
{
|
||||
$data = json_decode($data);
|
||||
$status = true;
|
||||
|
||||
// Handle deletions
|
||||
if (isset($data->deletions) && $data->deletions !== '') {
|
||||
foreach (explode(' ', trim($data->deletions)) as $rowId) {
|
||||
if (!call_user_func($deletionCallback, $request, $rowId, $data->numberOfRows)) {
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle changes and insertions
|
||||
if (isset($data->changes)) {
|
||||
foreach ($data->changes as $entry) {
|
||||
// Get the row ID, if any, from submitted data
|
||||
if (isset($entry->rowId)) {
|
||||
$rowId = $entry->rowId;
|
||||
unset($entry->rowId);
|
||||
} else {
|
||||
$rowId = null;
|
||||
}
|
||||
|
||||
// $entry should now contain only submitted modified or new rows.
|
||||
// Go through each and unpack the data in prep for application.
|
||||
$changes = [];
|
||||
foreach ($entry as $key => $value) {
|
||||
// Match the column name and localization data, if any.
|
||||
if (!preg_match('/^newRowId\[([a-zA-Z]+)\](\[([a-z][a-z](_[A-Z][A-Z])?(@([A-Za-z0-9]{5,8}|\d[A-Za-z0-9]{3}))?)\])?$/', $key, $matches)) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Get the column name
|
||||
$column = $matches[1];
|
||||
|
||||
// If this is a multilingual input, fetch $locale; otherwise null
|
||||
$locale = $matches[3] ?? null;
|
||||
|
||||
if ($locale) {
|
||||
$changes[$column][$locale] = $value;
|
||||
} else {
|
||||
$changes[$column] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// $changes should now contain e.g.:
|
||||
// array ('localizedColumnName' => array('en' => 'englishValue'),
|
||||
// 'nonLocalizedColumnName' => 'someNonLocalizedValue');
|
||||
if (is_null($rowId)) {
|
||||
if (!call_user_func($insertionCallback, $request, $changes)) {
|
||||
$status = false;
|
||||
}
|
||||
} else {
|
||||
if (!call_user_func($updateCallback, $request, $rowId, $changes)) {
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the listbuilder using the internal handler.
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*/
|
||||
public function save($args, $request)
|
||||
{
|
||||
// The ListbuilderHandler will post a list of changed
|
||||
// data in the "data" post var. Need to go through it
|
||||
// and reconcile the data against this list, adding/
|
||||
// updating/deleting as needed.
|
||||
$data = $request->getUserVar('data');
|
||||
self::unpack(
|
||||
$request,
|
||||
$data,
|
||||
[$this, 'deleteEntry'],
|
||||
[$this, 'insertEntry'],
|
||||
[$this, 'updateEntry']
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the set of options for a select list type listbuilder.
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function fetchOptions($args, $request)
|
||||
{
|
||||
$options = $this->getOptions($request);
|
||||
return new JSONMessage(true, $options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Can items be added to this list builder?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canAddItems()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Overridden methods from GridHandler
|
||||
//
|
||||
/**
|
||||
* @see GridHandler::getRowInstance()
|
||||
*
|
||||
* @return ListbuilderGridRow
|
||||
*/
|
||||
protected function getRowInstance()
|
||||
{
|
||||
// Return a citation row
|
||||
return new ListbuilderGridRow();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\controllers\listbuilder\ListbuilderHandler', '\ListbuilderHandler');
|
||||
foreach ([
|
||||
'LISTBUILDER_SOURCE_TYPE_TEXT',
|
||||
'LISTBUILDER_SOURCE_TYPE_SELECT',
|
||||
'LISTBUILDER_SAVE_TYPE_EXTERNAL',
|
||||
'LISTBUILDER_SAVE_TYPE_INTERNAL',
|
||||
'LISTBUILDER_OPTGROUP_LABEL',
|
||||
] as $constantName) {
|
||||
define($constantName, constant('\ListbuilderHandler::' . $constantName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/controllers/listbuilder/MultilingualListbuilderGridColumn.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class MultilingualListbuilderGridColumn
|
||||
*
|
||||
* @ingroup controllers_listbuilder
|
||||
*
|
||||
* @brief Represents a multilingual text column within a listbuilder.
|
||||
*/
|
||||
|
||||
namespace PKP\controllers\listbuilder;
|
||||
|
||||
use PKP\facades\Locale;
|
||||
|
||||
class MultilingualListbuilderGridColumn extends ListbuilderGridColumn
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param null|mixed $title
|
||||
* @param null|mixed $titleTranslated
|
||||
* @param null|mixed $template
|
||||
* @param null|mixed $cellProvider
|
||||
* @param null|mixed $availableLocales
|
||||
*/
|
||||
public function __construct(
|
||||
$listbuilder,
|
||||
$id = '',
|
||||
$title = null,
|
||||
$titleTranslated = null,
|
||||
$template = null,
|
||||
$cellProvider = null,
|
||||
$availableLocales = null,
|
||||
$flags = []
|
||||
) {
|
||||
// Make sure this is a text input
|
||||
assert($listbuilder->getSourceType() == ListbuilderHandler::LISTBUILDER_SOURCE_TYPE_TEXT);
|
||||
|
||||
// Provide a default set of available locales if not specified
|
||||
if (!$availableLocales) {
|
||||
$availableLocales = Locale::getSupportedFormLocales();
|
||||
}
|
||||
|
||||
// Set some flags for multilingual support
|
||||
$flags['multilingual'] = true; // This is a multilingual column.
|
||||
$flags['availableLocales'] = $availableLocales; // Provide available locales
|
||||
|
||||
parent::__construct($listbuilder, $id, $title, $titleTranslated, $template, $cellProvider, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\controllers\listbuilder\MultilingualListbuilderGridColumn', '\MultilingualListbuilderGridColumn');
|
||||
}
|
||||
Reference in New Issue
Block a user