<?php
/**
* Addmodule subpage base - makes sure
* we have the two required methods preprocess and render
*/
abstract class AddModule_Base
{
/**
* Holds the instance for the currently executing step
*
* @var AddModule_Base
*/
private static $instance = false;
/**
* Instance of ConfigModule we're editing. False if not editing but creating.
*
* @var ?ConfigModule
*/
protected $edit = null;
/**
*
* @param string $step name of class representing the current step
* @param int $editId (optional) overwrite for the request parameter 'edit'
*/
public static function setStep(string $step, int $editId = null): void
{
if (empty($step) || !class_exists($step) || get_parent_class($step) !== 'AddModule_Base') {
Message::addError('invalid-action', $step);
Util::redirect('?do=SysConfig');
}
self::$instance = new $step();
if ($editId === null) {
$editId = Request::any('edit', 0, 'int');
}
if ($editId !== 0) {
self::$instance->edit = ConfigModule::get($editId);
if (self::$instance->edit === null)
ErrorHandler::traceError('Invalid module id for editing');
if ($step !== 'AddModule_Assign' && !preg_match('/^' . self::$instance->edit->moduleType() . '_/', $step))
ErrorHandler::traceError('Module to edit is of different type!');
Util::addRedirectParam('edit', self::$instance->edit->id());
}
}
protected function tmError()
{
Message::addError('main.taskmanager-error');
Util::redirect('?do=SysConfig');
}
protected function taskError($status)
{
if (isset($status['data']['error'])) {
$error = $status['data']['error'];
} elseif (isset($status['statusCode'])) {
$error = $status['statusCode'];
} else {
$error = Dictionary::translate('lang_unknwonTaskManager');
}
Message::addError('main.task-error', $error);
Util::redirect('?do=SysConfig');
}
/**
* Called before any HTML rendering happens, so you can
* prepare stuff, validate input, and optionally redirect
* early if something is wrong, or you received post
* data etc.
*/
protected function preprocessInternal()
{
// void
}
/**
* Do page rendering.
*/
protected function renderInternal()
{
// void
}
/**
* Handle ajax stuff
*/
protected function ajaxInternal()
{
// void
}
public static function preprocess()
{
if (self::$instance === false) {
ErrorHandler::traceError('No step instance yet');
}
self::$instance->preprocessInternal();
}
public static function render()
{
if (self::$instance === false) {
ErrorHandler::traceError('No step instance yet');
}
if (get_class(self::$instance) !== 'AddModule_Assign' && self::$instance->edit !== null) {
Message::addInfo('replacing-module', self::$instance->edit->title());
}
self::$instance->renderInternal();
}
public static function ajax()
{
if (self::$instance === false) {
ErrorHandler::traceError('No step instance yet');
}
self::$instance->ajaxInternal();
}
}
/**
* Start dialog for adding module. Here the user
* selects which kind of module they want to add.
*/
class AddModule_Start extends AddModule_Base
{
protected function renderInternal()
{
$title = $order = array();
$mods = ConfigModule::getList();
foreach ($mods as $module) {
$title[] = $module['title'];
$order[] = $module['sortOrder'];
}
array_multisort($order, SORT_ASC, $title, SORT_ASC, $mods);
Render::addDialog(Dictionary::translate('lang_moduleAdd'), false, 'start', array('modules' => array_values($mods)));
}
}
/**
* End dialog for adding module. Here the user
* can assign the module to configs.
*/
class AddModule_Assign extends AddModule_Base
{
protected function preprocessInternal()
{
if (!User::hasPermission('config.edit'))
Util::redirect('?do=SysConfig', false, true);
$assign = Request::any('assign', false, 'boolean');
if ($assign) {
$configIds = Request::any('configs', [], 'array');
$moduleId = $this->edit->id();
$moduleType = $this->edit->moduleType();
if (ConfigModule::getList()[$moduleType]['unique']) {
// Is a unique type module
$moduleIds = [];
foreach (ConfigModule::getAll($moduleType) ?? [] as $module) {
$moduleIds[] = $module->id();
}
// First, delete all modules of given type from all involved configs
Database::exec("DELETE FROM configtgz_x_module
WHERE configid IN (:configids)
AND moduleid IN (:moduleids)
AND moduleid <> :moduleid",
['configids' => $configIds, 'moduleids' => $moduleIds, 'moduleid' => $moduleId]);
}
// Not delete module from all configs that are not selected
Database::exec("DELETE FROM configtgz_x_module WHERE moduleid = :moduleid AND configid NOT IN (:configids)",
['configids' => $configIds, 'moduleid' => $moduleId]);
// Finally, (re)insert for all configs selected
foreach ($configIds as $configId) {
Database::exec("INSERT IGNORE INTO configtgz_x_module (configid, moduleid) VALUES (:configid, :moduleid)", array(
'configid' => $configId,
'moduleid' => $moduleId
));
$config = ConfigTgz::get($configId);
$config->markOutdated();
$config->generate();
}
Util::redirect('?do=SysConfig', false, true);
}
}
protected function renderInternal()
{
$data = ['configs' => SysConfig::getAll()];
if (count($data['configs']) === 0) // Nothing to do
Util::redirect('?do=SysConfig', false, true);
$moduleType = $this->edit->moduleType();
// If this is a module of type unique, mark all configs that already contain a module of that type
if (ConfigModule::getList()[$moduleType]['unique']) {
$modules = Database::queryAll("SELECT cm.moduleid as moduleid, cm.title as title, cxm.configid as configid
FROM configtgz_module cm
INNER JOIN configtgz_x_module cxm ON (cm.moduleid = cxm.moduleid)
WHERE cm.moduletype = :moduletype AND cxm.moduleid <> :moduleid",
['moduletype' => $moduleType, 'moduleid' => $this->edit->id()]);
$modulesByConfigId = [];
foreach ($modules as $module) {
$modulesByConfigId[$module['configid']] = $module;
}
foreach ($data['configs'] as &$config) {
if (!isset($modulesByConfigId[$config['configid']]))
continue;
$config['replaces'] = $modulesByConfigId[$config['configid']]['title'];
}
}
// Pre-check those that are already included
$tgzs = [];
foreach (ConfigTgz::getAllForModule($this->edit->id()) as $tgz) {
$tgzs[$tgz->id()] = 1;
}
foreach ($data['configs'] as &$config) {
if (!isset($tgzs[$config['configid']]))
continue;
$config['checked'] = 'checked';
}
$data['edit'] = $this->edit->id();
$data['name'] = $this->edit->title();
Render::addDialog(Dictionary::translate('lang_moduleAssign'), false, 'assign', $data);
}
}
/*
* Helper functions to set/get a batch of vars from/to post variables or a module
*/
function moduleToArray(ConfigModule $module, array &$array, array $keys): void
{
foreach ($keys as $key) {
$array[$key] = $module->getData($key);
}
}
function arrayToModule(ConfigModule $module, array $array, array $keys): void
{
foreach ($keys as $key) {
$module->setData($key, $array[$key]);
}
}
function postToArray(array &$array, array $keys, $ignoreMissing = false): void
{
foreach ($keys as $key) {
$val = Request::post($key, '--not-in-post');
if ($ignoreMissing && $val === '--not-in-post')
continue;
$array[$key] = $val;
}
}