Files
aowow/endpoints/guide/edit.php
Sarjuuk 05f5b0ed34 Response/Params
* so we can't directly use BackedEnum::tryFrom as validator, because if
   the Enum is of <int> and the string is not what php considers numeric,
   we get a straight TypeError Exception instead of null for failing the tryFrom.
2025-10-06 17:06:49 +02:00

215 lines
9.6 KiB
PHP

<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideEditResponse extends TemplateResponse
{
use TrGuideEditor;
protected ?string $articleUrl = 'edit';
protected string $template = 'guide-edit';
protected string $pageName = 'guide=edit';
protected ?int $activeTab = parent::TAB_GUIDES;
protected array $breadcrumb = [6];
protected array $scripts = array(
[SC_JS_FILE, 'js/article-description.js'],
[SC_JS_FILE, 'js/article-editing.js'],
[SC_JS_FILE, 'js/guide-editing.js'],
[SC_JS_FILE, 'js/fileuploader.js'],
[SC_JS_FILE, 'js/toolbar.js'],
[SC_JS_FILE, 'js/AdjacentPreview.js'],
[SC_CSS_FILE, 'css/article-editing.css'],
[SC_CSS_FILE, 'css/fileuploader.css'],
[SC_CSS_FILE, 'css/guide-edit.css'],
[SC_CSS_FILE, 'css/AdjacentPreview.css'],
[SC_CSS_STRING, <<<CSS
#upload-result input[type=text] { padding: 0px 2px; font-size: 12px; }
#upload-result > span { display: block; height: 22px; }
#upload-result { display: inline-block; text-align: right; }
#upload-progress { display: inline-block; margin-right: 8px; }
CSS]
);
protected array $expectedPOST = array(
'save' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ], // saved for more editing
'submit' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ], // submitted for review
'title' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'name' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'description' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkDescription'] ],
'changelog' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
'locale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkLocale'] ],
'category' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_value' => 1, 'max_value' => 9] ],
'specId' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_value' => -1, 'max_value' => 2, 'default' => -1]],
'classId' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_value' => 1, 'max_value' => 11, 'default' => 0]]
);
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT],
'rev' => ['filter' => FILTER_VALIDATE_INT]
);
public function __construct(string $param)
{
parent::__construct($param);
if (!User::canWriteGuide())
$this->generateError();
if (!is_int($this->_get['id'])) // edit existing guide
return;
$this->typeId = $this->_get['id']; // just to display sensible not-found msg
$status = DB::Aowow()->selectCell('SELECT `status` FROM ?_guides WHERE `id` = ?d AND `status` <> ?d { AND `userId` = ?d }', $this->typeId, GuideMgr::STATUS_ARCHIVED, User::isInGroup(U_GROUP_STAFF) ? DBSIMPLE_SKIP : User::$id);
if (!$status && $this->typeId)
$this->generateNotFound(Lang::game('guide'), Lang::guide('notFound'));
else if (!$this->typeId)
return;
// just so we don't have to access GuideMgr from template
$this->isDraft = $status == GuideMgr::STATUS_DRAFT;
$this->editStatus = $status;
$this->editRev = DB::Aowow()->selectCell('SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC', Type::GUIDE, $this->typeId);
}
protected function generate() : void
{
if ($this->_post['save'] || $this->_post['submit'])
{
if (!$this->saveGuide())
$this->error = Lang::main('intError');
else if ($this->_get['id'] === 0)
$this->forward('?guide=edit&id='.$this->typeId);
}
$guide = new GuideList(array(['id', $this->typeId]));
$this->h1 = Lang::guide('editTitle');
array_unshift($this->title, $this->h1.Lang::main('colon').$guide->getField('title'), Lang::game('guides'));
Lang::sort('guide', 'category');
// init required template vars
$this->editCategory = $this->_post['category'] ?? $guide->getField('category');
$this->editTitle = $this->_post['title'] ?? $guide->getField('title');
$this->editName = $this->_post['name'] ?? $guide->getField('name');
$this->editDescription = $this->_post['description'] ?? $guide->getField('description');
$this->editText = $this->_post['body'] ?? $guide->getArticle();
$this->editClassId = $this->_post['classId'] ?? $guide->getField('classId');
$this->editSpecId = $this->_post['specId'] ?? $guide->getField('specId');
$this->editLocale = $this->_post['locale'] ?? Locale::tryFrom($guide->getField('locale'));
$this->editStatus = $this->editStatus ?: $guide->getField('status');
$this->editStatusColor = GuideMgr::STATUS_COLORS[$this->editStatus];
$this->extendGlobalData($guide->getJSGlobals());
parent::generate();
}
private function saveGuide() : bool
{
// test requiered fields set
if (!$this->assertPOST('title', 'name', 'body', 'locale', 'category'))
{
trigger_error('GuideEditResponse::saveGuide - received malformed request', E_USER_ERROR);
return false;
}
// test required fields context
if (!$this->_post['locale']->validate())
return false;
// sanitize: spec / class
if ($this->_post['category'] == 1) // Classes
{
if ($this->_post['classId'] && !ChrClass::tryFrom($this->_post['classId']))
$this->_post['classId'] = 0;
if ($this->_post['specId'] > -1 && !$this->_post['classId'])
$this->_post['specId'] = -1;
}
else
{
$this->_post['classId'] = 0;
$this->_post['specId'] = -1;
}
$guideData = array(
'category' => $this->_post['category'],
'classId' => $this->_post['classId'],
'specId' => $this->_post['specId'],
'title' => $this->_post['title'],
'name' => $this->_post['name'],
'description' => $this->_post['description'] ?: GuideMgr::createDescription($this->_post['body']),
'locale' => $this->_post['locale']->value,
'roles' => User::$groups,
'status' => $this->_post['submit'] ? GuideMgr::STATUS_REVIEW : GuideMgr::STATUS_DRAFT,
'date' => time()
);
// new guide > reload editor
if ($this->_get['id'] === 0)
{
$guideData += ['userId' => User::$id];
if (!($this->typeId = (int)DB::Aowow()->query('INSERT INTO ?_guides (?#) VALUES (?a)', array_keys($guideData), array_values($guideData))))
{
trigger_error('GuideEditResponse::saveGuide - failed to save guide to db', E_USER_ERROR);
return false;
}
}
// existing guide > :shrug:
else if (DB::Aowow()->query('UPDATE ?_guides SET ?a WHERE `id` = ?d', $guideData, $this->typeId))
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `rev`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?d, ?)', $this->typeId, $this->editRev, time(), User::$id, $this->_post['changelog']);
else
{
trigger_error('GuideEditResponse::saveGuide - failed to update guide in db', E_USER_ERROR);
return false;
}
// insert Article
$articleId = DB::Aowow()->query(
'INSERT INTO ?_articles (`type`, `typeId`, `locale`, `rev`, `editAccess`, `article`) VALUES (?d, ?d, ?d, ?d, ?d, ?)',
Type::GUIDE,
$this->typeId,
$this->_post['locale']->value,
++$this->editRev,
User::$groups & U_GROUP_STAFF ? User::$groups : User::$groups | U_GROUP_BLOGGER,
$this->_post['body']
);
if (!is_int($articleId))
{
if ($this->_get['id'] === 0)
DB::Aowow()->query('DELETE FROM ?_guides WHERE `id` = ?d', $this->typeId);
trigger_error('GuideEditResponse::saveGuide - failed to save article to db', E_USER_ERROR);
return false;
}
if ($this->_post['submit'] && $this->editStatus != GuideMgr::STATUS_REVIEW)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $this->typeId, time(), User::$id, GuideMgr::STATUS_REVIEW);
$this->editStatus = $guideData['status'];
return true;
}
protected static function checkDescription(string $str) : string
{
// run checkTextBlob and also replace \n => \s and \s+ => \s
$str = preg_replace(parent::PATTERN_TEXT_BLOB, '', $str);
$str = strtr($str, ["\n" => ' ', "\r" => ' ']);
return preg_replace('/\s+/', ' ', trim($str));
}
}
?>