Template/Update (Part 40)

* convert 'guides' (listing, viewing, writing & management)
 * don't allow comments on WIP guides
This commit is contained in:
Sarjuuk
2025-08-18 00:22:24 +02:00
parent cb523353fd
commit fef27c58e6
38 changed files with 1437 additions and 1165 deletions

81
endpoints/admin/guide.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminGuideResponse extends TextResponse
{
private const /* int */ ERR_NONE = 0;
private const /* int */ ERR_GUIDE = 1;
private const /* int */ ERR_STATUS = 2;
private const /* int */ ERR_WRITE_DB = 3;
private const /* int */ ERR_MISCELLANEOUS = 999;
protected int $requiredUserGroup = U_GROUP_STAFF;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => GuideMgr::STATUS_APPROVED, 'max_range' => GuideMgr::STATUS_REJECTED]],
'msg' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id', 'status'))
{
trigger_error('AdminGuideResponse - malformed request received', E_USER_ERROR);
$this->result = self::ERR_MISCELLANEOUS;
return;
}
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
if (!$guide)
{
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' not found', E_USER_ERROR);
$this->result = self::ERR_GUIDE;
return;
}
if ($this->_post['status'] == $guide['status'])
{
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' already has status #'.$this->_post['status'], E_USER_ERROR);
$this->result = self::ERR_STATUS;
return;
}
// status can only be APPROVED or REJECTED due to input validation
if (!$this->update($this->_post['id'], $this->_post['status'], $this->_post['msg']))
{
trigger_error('AdminGuideResponse - write to db failed for guide #'.$this->_post['id'], E_USER_ERROR);
$this->result = self::ERR_WRITE_DB;
return;
}
if ($this->_post['status'] == GuideMgr::STATUS_APPROVED)
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
$this->result = self::ERR_NONE;
}
private function update(int $id, int $status, ?string $msg = null) : bool
{
if ($status == GuideMgr::STATUS_APPROVED) // set display rev to latest
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d, `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', $status, Type::GUIDE, $id, User::$id, time(), $id);
else
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id);
if (!$ok)
return false;
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
if ($msg)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg);
return true;
}
}
?>

View File

@@ -0,0 +1,46 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminGuidesResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'list-page-generic';
protected string $pageName = 'guides';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 25]; // Staff > Content > Guides Awaiting Approval
protected function generate() : void
{
$this->h1 = 'Pending Guides';
array_unshift($this->title, $this->h1);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
parent::generate();
$pending = new GuideList([['status', GuideMgr::STATUS_REVIEW]]);
if ($pending->error)
$data = [];
else
{
$data = $pending->getListviewData();
$latest = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, MAX(`rev`) FROM ?_articles WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `rev`', Type::GUIDE, $pending->getFoundIDs());
foreach ($latest as $id => $rev)
$data[$id]['rev'] = $rev;
}
$this->lvTabs->addListviewTab(new Listview(array(
'data' => array_values($data),
'hiddenCols' => ['patch', 'comments', 'views', 'rating'],
'extraCols' => '$_'
), GuideList::$brickFile, 'guideAdminCol'));
}
}
?>

48
endpoints/edit/image.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EditImageResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedGET = array(
'qqfile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'guide' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]]
);
/*
success: bool
id: image enumerator
type: 3 ? png : jpg
name: old filename
error: errString
*/
protected function generate() : void
{
if (!$this->assertGET('qqfile', 'guide'))
{
$this->result = Util::toJSON(['success' => false, 'error' => Lang::main('genericError')]);
return;
}
if (!User::canWriteGuide())
{
$this->result = Util::toJSON(['success' => false, 'error' => Lang::main('genericError')]);
return;
}
$this->result = GuideMgr::handleUpload();
if (isset($this->result['success']))
$this->result += ['name' => $this->_get['qqfile']];
$this->result = Util::toJSON($this->result);
}
}
?>

View File

@@ -0,0 +1,35 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GetdescriptionBaseResponse extends TextResponse
{
protected string $contentType = MIME_TYPE_TEXT;
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'description' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
public function __construct(string $param)
{
if ($param) // should be empty
$this->generate404();
parent::__construct($param);
}
protected function generate() : void
{
if (!User::canWriteGuide())
return;
$this->result = GuideMgr::createDescription($this->_post['description']);
}
}
?>

View File

@@ -0,0 +1,104 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideChangelogResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'guide=changelog';
protected ?int $activeTab = parent::TAB_GUIDES;
protected array $breadcrumb = [6];
// why is this here: is there a mediawiki like diff function for staff?
protected array $scripts = [[SC_CSS_STRING, 'li input[type="radio"] { margin:0px 5px 0px 0px; }']];
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
// main container should be tagged: <div class="text guide-changelog">
if (!$this->assertGET('id'))
$this->generateNotFound(Lang::game('guide'), Lang::guide('notFound'));
$guide = new GuideList(array(['id', $this->_get['id']]));
if ($guide->error)
$this->generateNotFound(Lang::game('guide'), Lang::guide('notFound'));
if (!$guide->canBeViewed() && !$guide->userCanView())
$this->forward('?guides='.$guide->getField('category'));
$this->h1 = lang::guide('clTitle', [$this->_get['id'], $guide->getField('title')]);
if (!$this->h1)
$this->h1 = $guide->getField('name');
$this->gPageInfo += ['name' => $guide->getField('name')];
$this->breadcrumb[] = $guide->getField('category');
parent::generate();
/* - NYI (see "&& false")
$this->addScript([SC_JS_STRING,
<<<JS
$(document).ready(function() {
var radios = $("input[type=radio]");
function limit(col, val) {
radios.each(function(i, e) {
if (col == e.name)
return;
if (col == "b")
e.disabled = (val <= parseInt(e.value));
else if (col == "a")
e.disabled = (val >= parseInt(e.value));
});
};
radios.each(function (i, e) {
e.onchange = limit.bind(this, e.name, parseInt(e.value));
if (i < 2 && e.name == "b") // first pair
$(e).trigger("click");
else if (e.value == 0 && e.name == "a") // last pair
$(e).trigger("click");
});
});
JS
]);
*/
$buff = '<ul>';
$inp = fn($rev) => User::isInGroup(U_GROUP_STAFF) && false ? ($rev !== null ? '<input name="a" value="'.$rev.'" type="radio"/><input name="b" value="'.$rev.'" type="radio"/><b>' : '<b style="margin-left:38px;">') : '';
$logEntries = DB::Aowow()->select('SELECT a.`username` AS `name`, gcl.`date`, gcl.`status`, gcl.`msg`, gcl.`rev` FROM ?_guides_changelog gcl JOIN ?_account a ON a.`id` = gcl.`userId` WHERE gcl.`id` = ?d ORDER BY gcl.`date` DESC', $this->_get['id']);
foreach ($logEntries as $log)
{
if ($log['status'] != GuideMgr::STATUS_NONE)
$buff .= '<li class="guide-changelog-status-change">'.$inp($log['rev']).'<b>'.Lang::guide('clStatusSet', [Lang::guide('status', $log['status'])]).'</b>'.Util::formatTimeDiff($log['date'])."</li>\n";
else if ($log['msg'])
$buff .= '<li>'.$inp($log['rev']).'<b>'.Util::formatTimeDiff($log['date']).Lang::main('colon').'</b>'.$log['msg'].' <i class="q0">'.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."</i></li>\n";
else
$buff .= '<li class="guide-changelog-minor-edit">'.$inp($log['rev']).'<b>'.Util::formatTimeDiff($log['date']).Lang::main('colon').'</b><i>'.Lang::guide('clMinorEdit').'</i> <i class="q0">'.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."</i></li>\n";
}
// append creation
$buff .= '<li class="guide-changelog-created">'.$inp(0).'<b>'.Lang::guide('clCreated').'</b>'.Util::formatTimeDiff($guide->getField('date'))."</li>\n</ul>\n";
if (User::isInGroup(U_GROUP_STAFF) && false)
$buff .= '<input type="button" value="Compare" onclick="alert(\'NYI\');" style="margin-left: 40px;"/>';
$this->extraHTML = $buff;
}
}
?>

214
endpoints/guide/edit.php Normal file
View File

@@ -0,0 +1,214 @@
<?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' => [Locale::class, 'tryFrom'] ],
'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));
}
}
?>

250
endpoints/guide/guide.php Normal file
View File

@@ -0,0 +1,250 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'guide';
protected ?int $activeTab = parent::TAB_GUIDES;
protected array $breadcrumb = [6];
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT],
'rev' => ['filter' => FILTER_VALIDATE_INT]
);
public int $type = Type::GUIDE;
public int $typeId = 0;
public int $guideStatus = 0;
public array $guideRating = [];
public ?int $guideRevision = null;
private GuideList $subject;
public function __construct(string $nameOrId)
{
parent::__construct($nameOrId);
/**********************/
/* get mode + guideId */
/**********************/
if (Util::checkNumeric($nameOrId, NUM_CAST_INT))
$this->typeId = $nameOrId;
else if (preg_match(GuideMgr::VALID_URL, $nameOrId))
{
if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `url` = ?', Util::lower($nameOrId)))
{
$this->typeId = intVal($id);
$this->articleUrl = Util::lower($nameOrId);
}
}
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new GuideList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('guide'), Lang::guide('notFound'));
if (!$this->subject->canBeViewed() && !$this->subject->userCanView())
$this->forward('?guides='.$this->subject->getField('category'));
$this->guideStatus = $this->subject->getField('status');
if ($this->guideStatus != GuideMgr::STATUS_APPROVED && $this->guideStatus != GuideMgr::STATUS_ARCHIVED)
{
$this->cacheType = CACHE_TYPE_NONE;
$this->contribute = CONTRIBUTE_NONE;
}
if ($this->articleUrl)
$this->guideRevision = $this->subject->getField('rev');
else if ($this->subject->userCanView())
$this->guideRevision = $this->_get['rev'] ?? $this->subject->getField('latest');
else
$this->subject->getField('rev');
$this->h1 = $this->subject->getField('name');
$this->gPageInfo += array(
'name' => $this->h1,
'author' => $this->subject->getField('author')
);
/*************/
/* Menu Path */
/*************/
if ($x = $this->subject?->getField('category'))
$this->breadcrumb[] = $x;
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->subject->getField('title'), Lang::game('guides'));
/***********/
/* Infobox */
/***********/
if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_QUICKFACTS))
$this->generateInfobox();
// needs post-cache updating
if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_RATING))
$this->guideRating = array(
$this->subject->getField('rating'), // avg rating
User::canUpvote() && User::canDownvote() ? 'true' : 'false',
$this->subject->getField('_self'), // my rating amt; 0 = no vote
$this->typeId // guide Id
);
/****************/
/* Main Content */
/****************/
if ($this->subject->userCanView())
$this->redButtons[BUTTON_GUIDE_EDIT] = User::canWriteGuide() && $this->guideStatus != GuideMgr::STATUS_ARCHIVED;
$this->redButtons[BUTTON_GUIDE_LOG] = true;
$this->redButtons[BUTTON_GUIDE_REPORT] = $this->subject->canBeReported();
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], __forceTabs: true);
// the article text itself is added by PageTemplate::addArticle()
parent::generate();
$this->result->registerDisplayHook('infobox', [self::class, 'infoboxHook']);
if ($this->guideRating)
$this->result->registerDisplayHook('guideRating', [self::class, 'starsHook']);
}
private function generateInfobox() : void
{
$infobox = [];
if ($this->subject->getField('cuFlags') & CC_FLAG_STICKY)
$infobox[] = '[span class=guide-sticky]'.Lang::guide('sticky').'[/span]';
$infobox[] = Lang::guide('author').'[url=?user='.$this->subject->getField('author').']'.$this->subject->getField('author').'[/url]';
if ($this->subject->getField('category') == 1)
{
$c = $this->subject->getField('classId');
$s = $this->subject->getField('specId');
if ($c > 0)
{
$this->extendGlobalIds(Type::CHR_CLASS, $c);
$infobox[] = Util::ucFirst(Lang::game('class')).Lang::main('colon').'[class='.$c.']';
}
if ($s > -1)
$infobox[] = Lang::guide('spec').'[icon class="c'.$c.' icontiny" name='.Game::$specIconStrings[$c][$s].']'.Lang::game('classSpecs', $c, $s).'[/icon]';
}
// $infobox[] = Lang::guide('patch').Lang::main('colon').'3.3.5'; // replace with date
$infobox[] = Lang::guide('added').'[tooltip name=added]'.date('l, G:i:s', $this->subject->getField('date')).'[/tooltip][span class=tip tooltip=added]'.date(Lang::main('dateFmtShort'), $this->subject->getField('date')).'[/span]';
if ($this->guideStatus == GuideMgr::STATUS_ARCHIVED)
$infobox[] = Lang::guide('status', GuideMgr::STATUS_ARCHIVED);
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
if ($this->guideStatus == GuideMgr::STATUS_REVIEW && User::isInGroup(U_GROUP_STAFF) && $this->_get['rev'])
{
$this->addScript([SC_JS_STRING, <<<JS
$(document).ready(function() {
let send = function (status)
{
let message = "";
let id = \$WH.g_getGets().guide;
if (status == 4) // rejected
{
while (message === "")
message = prompt("Please provide your reasoning.");
if (message === null)
return false;
}
$.ajax({cache: false, url: "?admin=guide", type: "POST",
error: function() { alert("Operation failed."); },
success: function(json)
{
if (json)
alert("Operation failed.");
else
window.location.href = "?admin=guides";
},
data: { id: id, status: status, msg: message }
});
return true;
};
\$WH.ge("btn-accept").onclick = send.bind(null, 3);
\$WH.ge("btn-reject").onclick = send.bind(null, 4);
});
JS]);
$this->infobox->append('[h3 style="text-align:center"]Admin[/h3]');
$this->infobox->append('[div style="text-align:center"][url=# id="btn-accept" class=icon-tick]Approve[/url][url=# style="margin-left:20px" id="btn-reject" class=icon-delete]Reject[/url][/div]');
}
}
public static function infoboxHook(Template\PageTemplate &$pt, ?InfoboxMarkup &$infobox) : void
{
if ($pt->guideStatus != GuideMgr::STATUS_APPROVED)
return;
// increment and display views
DB::Aowow()->query('UPDATE ?_guides SET `views` = `views` + 1 WHERE `id` = ?d', $pt->typeId);
$nViews = DB::Aowow()->selectCell('SELECT `views` FROM ?_guides WHERE `id` = ?d', $pt->typeId);
$infobox->addItem(Lang::guide('views').'[n5='.$nViews.']');
// should we have a rating item in the lv?
if (!$pt->guideRating)
return;
$rating = GuideMgr::getRatings([$pt->typeId]);
if ($rating[$pt->typeId]['nvotes'] < 5)
$infobox->addItem(Lang::guide('rating').Lang::guide('noVotes'));
else
$infobox->addItem(Lang::guide('rating').Lang::guide('votes', [round($rating[$pt->typeId]['rating'], 1), $rating[$pt->typeId]['nvotes']]));
}
public static function starsHook(Template\PageTemplate &$pt, ?array &$guideRating) : void
{
if ($pt->guideStatus != GuideMgr::STATUS_APPROVED)
return;
$rating = GuideMgr::getRatings([$pt->typeId]);
$guideRating = array(
$rating[$pt->typeId]['rating'],
User::canUpvote() && User::canDownvote() ? 'true' : 'false',
$rating[$pt->typeId]['_self'] ?? 0,
$pt->typeId
);
}
}
?>

View File

@@ -0,0 +1,59 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuidePowerResponse extends TextResponse implements ICache
{
use TrCache, TrTooltip;
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerGuide(%s, %d, %s);';
protected int $type = Type::GUIDE;
protected int $typeId = 0;
protected int $cacheType = CACHE_TYPE_TOOLTIP;
protected array $expectedGET = array(
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
);
private string $url = '';
public function __construct(string $idOrName)
{
parent::__construct($idOrName);
// temp locale
if ($this->_get['domain'])
Lang::load($this->_get['domain']);
if (Util::checkNumeric($idOrName, NUM_CAST_INT))
$this->typeId = $idOrName;
else if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `url` = ?', Util::lower($idOrName)))
{
$this->typeId = intVal($id);
$this->url = Util::lower($idOrName);
}
}
protected function generate() : void
{
$opts = [];
if ($this->typeId)
if (!($guide = new GuideList(array(['id', $this->typeId])))->error)
$opts = array(
'name' => $guide->getField('name', true),
'tooltip' => $guide->renderTooltip()
);
if (!$opts)
$this->cacheType = CACHE_TYPE_NONE;
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->url ?: $this->typeId, $opts);
}
}
?>

66
endpoints/guide/new.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideNewResponse extends TemplateResponse
{
use TrGuideEditor;
protected bool $requiresLogin = true;
protected ?string $articleUrl = 'new';
protected string $template = 'guide-edit';
protected string $pageName = 'guide=new';
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]
);
public function __construct(string $param)
{
parent::__construct($param);
if (!User::canWriteGuide())
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Lang::guide('newTitle');
array_unshift($this->title, $this->h1, Lang::game('guides'));
Lang::sort('guide', 'category');
// update required template vars
$this->editLocale = Lang::getLocale();
parent::generate();
}
}
?>

50
endpoints/guide/vote.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideVoteResponse extends TextResponse
{
protected string $contentType = MIME_TYPE_TEXT;
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'rating' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 5]]
);
protected function generate() : void
{
if (!$this->assertPOST('id', 'rating'))
{
trigger_error('GuideVoteResponse - malformed request received', E_USER_ERROR);
$this->generate404();
}
if (!User::canUpvote() || !User::canDownvote()) // same logic as comments?
$this->generate403();
// by id, not own, published
$points = $votes = 0;
if ($g = DB::Aowow()->selectRow('SELECT `userId`, `cuFlags` FROM ?_guides WHERE `id` = ?d AND (`status` = ?d OR `rev` > 0)', $this->_post['id'], GuideMgr::STATUS_APPROVED))
{
// apparently you are allowed to vote on your own guide
if ($g['cuFlags'] & GUIDE_CU_NO_RATING)
$this->generate403();
if (!$this->_post['rating'])
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_GUIDE, $this->_post['id'], User::$id);
else
DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_GUIDE, $this->_post['id'], User::$id, $this->_post['rating']);
[$points, $votes] = DB::Aowow()->selectRow('SELECT IFNULL(SUM(`value`), 0) AS "0", IFNULL(COUNT(*), 0) AS "1" FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_GUIDE, $this->_post['id']);
}
$this->result = Util::toJSON($votes ? ['rating' => $points / $votes, 'nvotes' => $votes] : ['rating' => 0, 'nvotes' => 0]);
}
}
?>

View File

@@ -0,0 +1,73 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuidesBaseResponse extends TemplateResponse // implements ICache
{
use TrListPage/* , TrCache */;
// protected int $cacheType = CACHE_TYPE_PAGE; // really do? cache would need to be destroyed externally with each guide status update
protected int $type = Type::GUIDE;
protected string $template = 'list-page-generic';
protected string $pageName = 'guides';
protected ?int $activeTab = parent::TAB_GUIDES;
protected array $breadcrumb = [6];
protected array $validCats = [null, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('guides'));
if ($this->category)
$this->breadcrumb[] = $this->category[0];
array_unshift($this->title, $this->h1);
if ($this->category)
array_unshift($this->title, Lang::guide('category', $this->category[0]));
$conditions = array(
['locale', Lang::getLocale()->value],
['status', GuideMgr::STATUS_ARCHIVED, '!'], // never archived guides
[
'OR',
['status', GuideMgr::STATUS_APPROVED], // currently approved
['rev', 0, '>'] // has previously approved revision
]
);
if ($this->category)
$conditions[] = ['category', $this->category[0]];
$this->redButtons = [BUTTON_GUIDE_NEW => User::canWriteGuide()];
$guides = new GuideList($conditions);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $guides->getListviewData(),
'name' => Util::ucFirst(Lang::game('guides')),
'hiddenCols' => ['patch'], // pointless: display date instead
'extraCols' => ['$Listview.extraCols.date'] // ok
), GuideList::$brickFile));
parent::generate();
}
}
?>

View File

@@ -0,0 +1,52 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class MyguidesBaseResponse extends TemplateResponse
{
use TrListPage;
protected int $type = Type::GUIDE;
protected string $template = 'list-page-generic';
protected string $pageName = 'my-guides';
protected ?int $activeTab = parent::TAB_GUIDES;
// protected array $breadcrumb = [6]; // breadcrumb menu not displayed by WH.?
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
if (!User::isLoggedIn() || $pageParam)
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::guide('myGuides'));
array_unshift($this->title, $this->h1);
$this->redButtons = [BUTTON_GUIDE_NEW => User::canWriteGuide()];
$guides = new GuideList(array(['userId', User::$id]));
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $guides->getListviewData(),
'name' => Util::ucFirst(Lang::game('guides')),
'hiddenCols' => ['patch', 'author'],
'visibleCols' => ['status'],
'extraCols' => ['$Listview.extraCols.date']
), GuideList::$brickFile));
parent::generate();
}
}
?>

View File

@@ -253,7 +253,7 @@ class UserBaseResponse extends TemplateResponse
}
// My Guides
$guides = new GuideList(['status', [GUIDE_STATUS_APPROVED, GUIDE_STATUS_ARCHIVED]], ['userId', $this->user['id']]);
$guides = new GuideList(['status', [GuideMgr::STATUS_APPROVED, GuideMgr::STATUS_ARCHIVED]], ['userId', $this->user['id']]);
if (!$guides->error)
{
$this->lvTabs->addListviewTab(new Listview(array(

View File

@@ -7,7 +7,7 @@ if (!defined('AOWOW_REVISION'))
class AjaxAdmin extends AjaxHandler
{
protected $validParams = ['siteconfig', 'weight-presets', 'spawn-override', 'guide', 'comment'];
protected $validParams = ['siteconfig', 'weight-presets', 'spawn-override', 'comment'];
protected $_get = array(
'action' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine' ],
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdListUnsigned'],
@@ -64,13 +64,6 @@ class AjaxAdmin extends AjaxHandler
$this->handler = 'spawnPosFix';
}
else if ($this->params[0] == 'guide')
{
if (!User::isInGroup(U_GROUP_STAFF))
return;
$this->handler = 'guideManage';
}
else if ($this->params[0] == 'comment')
{
if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD))
@@ -207,57 +200,6 @@ class AjaxAdmin extends AjaxHandler
return '-1';
}
protected function guideManage() : string
{
$update = function (int $id, int $status, ?string $msg = null) : bool
{
if (!DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id))
return false;
// set display rev to latest
if ($status == GUIDE_STATUS_APPROVED)
DB::Aowow()->query('UPDATE ?_guides SET `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', Type::GUIDE, $id, User::$id, time(), $id);
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
if ($msg)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg);
return true;
};
if (!$this->_post['id'])
trigger_error('AjaxHander::guideManage - malformed request: id: '.$this->_post['id'].', status: '.$this->_post['status']);
else
{
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
if (!$guide)
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' not found');
else
{
if ($this->_post['status'] == $guide['status'])
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' already has status #'.$this->_post['status']);
else
{
if ($this->_post['status'] == GUIDE_STATUS_APPROVED)
{
if ($update($this->_post['id'], GUIDE_STATUS_APPROVED, $this->_post['msg']))
{
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
return '1';
}
else
return '-2';
}
else if ($this->_post['status'] == GUIDE_STATUS_REJECTED)
return $update($this->_post['id'], GUIDE_STATUS_REJECTED, $this->_post['msg']) ? '1' : '-2';
else
trigger_error('AjaxHander::guideManage - unhandled status change request');
}
}
}
return '-1';
}
protected function commentOutOfDate() : string
{
$ok = false;

View File

@@ -1,82 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxEdit extends AjaxHandler
{
protected $_get = array(
'qqfile' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
'guide' => ['filter' => FILTER_SANITIZE_NUMBER_INT ]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$params)
return;
if ($params[0] == 'image')
$this->handler = 'handleUpload';
else if ($params[0] == 'article') // has it's own editor page
$this->handler = null;
}
/*
success: bool
id: image enumerator
type: 3 ? png : jpg
name: old filename
error: errString
*/
protected function handleUpload() : string
{
if (!User::canWriteGuide() || $this->_get['guide'] != 1)
return Util::toJSON(['success' => false, 'error' => '']);
require_once('includes/libs/qqFileUploader.class.php');
$targetPath = 'static/uploads/guide/images/';
$tmpPath = 'static/uploads/temp/';
$tmpFile = User::$username.'-'.Type::GUIDE.'-0-'.Util::createHash(16);
$uploader = new \qqFileUploader(['jpg', 'jpeg', 'png'], 10 * 1024 * 1024);
$result = $uploader->handleUpload($tmpPath, $tmpFile, true);
if (isset($result['success']))
{
$finfo = new \finfo(FILEINFO_MIME);
$mime = $finfo->file($tmpPath.$result['newFilename']);
if (preg_match('/^image\/(png|jpe?g)/i', $mime, $m))
{
$i = 1; // image index
if ($files = scandir($targetPath, SCANDIR_SORT_DESCENDING))
if (rsort($files, SORT_NATURAL) && $files[0] != '.' && $files[0] != '..')
$i = explode('.', $files[0])[0] + 1;
$targetFile = $i . ($m[1] == 'png' ? '.png' : '.jpg');
// move to final location
if (!rename($tmpPath.$result['newFilename'], $targetPath.$targetFile))
return Util::toJSON(['error' => Lang::main('intError')]);
// send success
return Util::toJSON(array(
'success' => true,
'id' => $i,
'type' => $m[1] == 'png' ? 3 : 2,
'name' => $this->_get['qqfile']
));
}
return Util::toJSON(['error' => Lang::screenshot('error', 'unkFormat')]);
}
return Util::toJSON($result);
}
}
?>

View File

@@ -1,37 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGetdescription extends AjaxHandler
{
protected $_post = array(
'description' => [FILTER_CALLBACK, ['options' => 'Aowow\AjaxHandler::checkTextBlob']]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$params || $params[0]) // should be empty
return;
$this->handler = 'handleDescription';
}
protected function handleDescription() : string
{
$this->contentType = MIME_TYPE_TEXT;
if (!User::canWriteGuide())
return '';
$desc = Markup::stripTags($this->_post['description']);
return Lang::trimTextClean($desc, 120);
}
}
?>

View File

@@ -1,63 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGuide extends AjaxHandler
{
protected $_post = array(
'id' => [FILTER_SANITIZE_NUMBER_INT, null],
'rating' => [FILTER_SANITIZE_NUMBER_INT, null]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params || count($this->params) != 1)
return;
$this->contentType = MIME_TYPE_TEXT;
// select handler
if ($this->params[0] == 'vote')
$this->handler = 'voteGuide';
}
protected function voteGuide() : string
{
if (!$this->_post['id'] || $this->_post['rating'] < 0 || $this->_post['rating'] > 5)
{
header('HTTP/1.0 404 Not Found', true, 404);
return '';
}
else if (!User::canUpvote() || !User::canDownvote()) // same logic as comments?
{
header('HTTP/1.0 403 Forbidden', true, 403);
return '';
}
// by id, not own, published
if ($g = DB::Aowow()->selectRow('SELECT `userId`, `cuFlags` FROM ?_guides WHERE `id` = ?d AND (`status` = ?d OR `rev` > 0)', $this->_post['id'], GUIDE_STATUS_APPROVED))
{
if ($g['cuFlags'] & GUIDE_CU_NO_RATING || $g['userId'] == User::$id)
{
header('HTTP/1.0 403 Forbidden', true, 403);
return '';
}
if (!$this->_post['rating'])
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_GUIDE, $this->_post['id'], User::$id);
else
DB::Aowow()->query('REPLACE INTO ?_user_ratings VALUES (?d, ?d, ?d, ?d)', RATING_GUIDE, $this->_post['id'], User::$id, $this->_post['rating']);
$res = DB::Aowow()->selectRow('SELECT IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n` FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_GUIDE, $this->_post['id']);
return Util::toJSON($res['n'] ? ['rating' => $res['t'] / $res['n'], 'nvotes' => $res['n']] : ['rating' => 0, 'nvotes' => 0]);
}
return Util::toJSON(['rating' => 0, 'nvotes' => 0]);
}
}
?>

View File

@@ -0,0 +1,106 @@
<?php
namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideMgr
{
public const /* int */ STATUS_NONE = 0;
public const /* int */ STATUS_DRAFT = 1;
public const /* int */ STATUS_REVIEW = 2;
public const /* int */ STATUS_APPROVED = 3;
public const /* int */ STATUS_REJECTED = 4;
public const /* int */ STATUS_ARCHIVED = 5;
private const /* string */ IMG_DEST_DIR = 'static/uploads/guide/images/';
private const /* string */ IMG_TMP_DIR = 'static/uploads/temp/';
public const VALID_URL = '/^[a-z0-9_\-]{2,64}$/i';
public const STATUS_COLORS = array(
self::STATUS_DRAFT => '#71D5FF',
self::STATUS_REVIEW => '#FFFF00',
self::STATUS_APPROVED => '#1EFF00',
self::STATUS_REJECTED => '#FF4040',
self::STATUS_ARCHIVED => '#FFD100'
);
private static array $ratingsStore = [];
private static ?int $imgUploadIdx = null;
public static function createDescription(string $text) : string
{
return Lang::trimTextClean(Markup::stripTags($text), 120);
}
public static function getRatings(array $guideIds) : array
{
if (!$guideIds)
return [];
if (array_keys(self::$ratingsStore) == $guideIds)
return self::$ratingsStore;
self::$ratingsStore = array_fill_keys($guideIds, ['nvotes' => 0, 'rating' => -1]);
$ratings = DB::Aowow()->select('SELECT `entry` AS ARRAY_KEY, IFNULL(SUM(`value`), 0) AS "0", IFNULL(COUNT(*), 0) AS "1", IFNULL(MAX(IF(`userId` = ?d, `value`, 0)), 0) AS "2" FROM ?_user_ratings WHERE `type` = ?d AND `entry` IN (?a) GROUP BY `entry`', User::$id, RATING_GUIDE, $guideIds);
foreach ($ratings as $id => [$total, $count, $self])
{
self::$ratingsStore[$id]['nvotes'] = (int)$count;
self::$ratingsStore[$id]['_self'] = (int)$self;
if ($count >= 5 )
self::$ratingsStore[$id]['rating'] = $total / $count;
}
return self::$ratingsStore;
}
public static function handleUpload() : array
{
require_once('includes/libs/qqFileUploader.class.php');
$tmpFile = User::$username.'-'.Type::GUIDE.'-0-'.Util::createHash(16);
$uploader = new \qqFileUploader(['jpg', 'jpeg', 'png'], 10 * 1024 * 1024);
$result = $uploader->handleUpload(self::IMG_TMP_DIR, $tmpFile, true);
if (isset($result['error']))
return $result;
$mime = (new \finfo(FILEINFO_MIME))?->file(self::IMG_TMP_DIR . $result['newFilename']);
if (!preg_match('/^image\/(png|jpe?g)/i', $mime, $m))
return ['error' => Lang::screenshot('error', 'unkFormat')];
// find next empty image name (an int)
if (is_null(self::$imgUploadIdx))
{
if ($files = scandir(self::IMG_DEST_DIR, SCANDIR_SORT_DESCENDING))
if (rsort($files, SORT_NATURAL) && $files[0] != '.' && $files[0] != '..')
$i = explode('.', $files[0])[0] + 1;
self::$imgUploadIdx = $i ?? 1;
}
$targetFile = self::$imgUploadIdx . ($m[1] == 'png' ? '.png' : '.jpg');
// move to final location
if (!rename(self::IMG_TMP_DIR.$result['newFilename'], self::IMG_DEST_DIR.$targetFile))
{
trigger_error('GuideMgr::handleUpload - failed to move file', E_USER_ERROR);
return ['error' => Lang::main('intError')];
}
return array(
'success' => true,
'id' => self::$imgUploadIdx,
'type' => $m[1] == 'png' ? 3 : 2
);
}
}
?>

View File

@@ -27,7 +27,6 @@ class PageTemplate
private array $pageData = []; // processed by display hooks
// template data that needs further processing .. ! WARNING ! they will not get aut fetched from $context as they are already defined here
protected array $guideRating = [];
private string $gStaticUrl;
private string $gHost;
private string $gServerTime;

View File

@@ -71,13 +71,15 @@ trait TrGuideEditor
public int $editClassId = 0;
public int $editSpecId = 0;
public int $editRev = 0;
public int $editStatus = GUIDE_STATUS_DRAFT;
public string $editStatusColor = GuideMgr::STATUS_COLORS[GUIDE_STATUS_DRAFT];
public int $editStatus = GuideMgr::STATUS_DRAFT;
public string $editStatusColor = GuideMgr::STATUS_COLORS[GuideMgr::STATUS_DRAFT];
public string $editTitle = '';
public string $editName = '';
public string $editDescription = '';
public string $editText = '';
public string $error = '';
public Locale $editLocale = Locale::EN;
public bool $isDraft = false;
}
class TemplateResponse extends BaseResponse

View File

@@ -10,14 +10,6 @@ class GuideList extends DBTypeList
{
use ListviewHelper;
public const /* array */ STATUS_COLORS = array(
GUIDE_STATUS_DRAFT => '#71D5FF',
GUIDE_STATUS_REVIEW => '#FFFF00',
GUIDE_STATUS_APPROVED => '#1EFF00',
GUIDE_STATUS_REJECTED => '#FF4040',
GUIDE_STATUS_ARCHIVED => '#FFD100'
);
public static int $type = Type::GUIDE;
public static string $brickFile = 'guide';
public static string $dataTable = '?_guides';
@@ -28,9 +20,10 @@ class GuideList extends DBTypeList
protected string $queryBase = 'SELECT g.*, g.`id` AS ARRAY_KEY FROM ?_guides g';
protected array $queryOpts = array(
'g' => [['a', 'c'], 'g' => 'g.`id`'],
'a' => ['j' => ['?_account a ON a.`id` = g.`userId`', true], 's' => ', IFNULL(a.`username`, "") AS "author"'],
'c' => ['j' => ['?_comments c ON c.`type` = '.Type::GUIDE.' AND c.`typeId` = g.`id` AND (c.`flags` & '.CC_FLAG_DELETED.') = 0', true], 's' => ', COUNT(c.`id`) AS "comments"']
'g' => [['a', 'c', 'ar'], 'g' => 'g.`id`'],
'a' => ['j' => ['?_account a ON a.`id` = g.`userId`', true], 's' => ', IFNULL(a.`username`, "") AS "author"'],
'c' => ['j' => ['?_comments c ON c.`type` = '.Type::GUIDE.' AND c.`typeId` = g.`id` AND (c.`flags` & '.CC_FLAG_DELETED.') = 0', true], 's' => ', COUNT(c.`id`) AS "comments"'],
'ar' => ['j' => ['?_articles ar ON ar.`type` = 300 AND ar.`typeId` = g.`id`'], 's' => ', MAX(ar.`rev`) AS "latest"']
);
public function __construct(array $conditions = [], array $miscData = [])
@@ -40,23 +33,11 @@ class GuideList extends DBTypeList
if ($this->error)
return;
$ratings = DB::Aowow()->select('SELECT `entry` AS ARRAY_KEY, IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n`, IFNULL(MAX(IF(`userId` = ?d, `value`, 0)), 0) AS `s` FROM ?_user_ratings WHERE `type` = ?d AND `entry` IN (?a)', User::$id, RATING_GUIDE, $this->getFoundIDs());
$ratings = GuideMgr::getRatings($this->getFoundIDs());
// post processing
foreach ($this->iterate() as $id => &$_curTpl)
{
if (isset($ratings[$id]))
{
$_curTpl['nvotes'] = $ratings[$id]['n'];
$_curTpl['rating'] = $ratings[$id]['n'] < 5 ? -1 : $ratings[$id]['t'] / $ratings[$id]['n'];
$_curTpl['_self'] = $ratings[$id]['s'];
}
else
{
$_curTpl['nvotes'] = 0;
$_curTpl['rating'] = -1;
}
}
$_curTpl = array_merge($_curTpl, $ratings[$id]);
}
public static function getName(int $id) : ?LocString
@@ -133,13 +114,13 @@ class GuideList extends DBTypeList
public function canBeViewed() : bool
{
// currently approved || has prev. approved version
return $this->getField('status') == GUIDE_STATUS_APPROVED || $this->getField('rev') > 0;
return $this->getField('status') == GuideMgr::STATUS_APPROVED || $this->getField('rev') > 0;
}
public function canBeReported() : bool
{
// not own guide && is not archived
return $this->getField('userId') != User::$id && $this->getField('status') != GUIDE_STATUS_ARCHIVED;
return $this->getField('userId') != User::$id && $this->getField('status') != GuideMgr::STATUS_ARCHIVED;
}
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array

View File

@@ -227,13 +227,6 @@ define('STR_ALLOW_SHORT', 0x4);
define('RATING_COMMENT', 1);
define('RATING_GUIDE', 2);
define('GUIDE_STATUS_NONE', 0);
define('GUIDE_STATUS_DRAFT', 1);
define('GUIDE_STATUS_REVIEW', 2);
define('GUIDE_STATUS_APPROVED', 3);
define('GUIDE_STATUS_REJECTED', 4);
define('GUIDE_STATUS_ARCHIVED', 5);
define('DEFAULT_ICON', 'inv_misc_questionmark');
define('MENU_IDX_ID', 0); // ID: A number or string; null makes the menu item a separator

View File

@@ -132,7 +132,7 @@ class qqFileUploader
private function toBytes(string $str) : int
{
$val = trim($str);
$val = substr(trim($str), 0, -1);
$last = strtolower(substr($str, -1, 1));
switch ($last)
{

View File

@@ -111,7 +111,7 @@ abstract class Type
self::CURRENCY => [CurrencyList::class, 'currency', 'g_gatheredcurrencies', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
self::SOUND => [SoundList::class, 'sound', 'g_sounds', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
self::ICON => [IconList::class, 'icon', 'g_icons', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
self::GUIDE => [GuideList::class, 'guide', '', self::FLAG_NONE],
self::GUIDE => [GuideList::class, 'guide', '', self::FLAG_DB_TYPE],
self::PROFILE => [ProfileList::class, 'profile', '', self::FLAG_FILTRABLE], // x - not known in javascript
self::GUILD => [GuildList::class, 'guild', '', self::FLAG_FILTRABLE], // x
self::ARENA_TEAM => [ArenaTeamList::class, 'arena-team', '', self::FLAG_FILTRABLE], // x

View File

@@ -681,7 +681,7 @@ class User
if (!self::isLoggedIn() || self::isBanned(ACC_BAN_GUIDE))
return $result;
if ($guides = DB::Aowow()->select('SELECT `id`, `title`, `url` FROM ?_guides WHERE `userId` = ?d AND `status` <> ?d', self::$id, GUIDE_STATUS_ARCHIVED))
if ($guides = DB::Aowow()->select('SELECT `id`, `title`, `url` FROM ?_guides WHERE `userId` = ?d AND `status` <> ?d', self::$id, GuideMgr::STATUS_ARCHIVED))
{
// fix url
array_walk($guides, fn(&$x) => $x['url'] = '?guide='.($x['url'] ?: $x['id']));

View File

@@ -75,8 +75,6 @@ abstract class Util
public static $mapSelectorString = '<a href="javascript:;" onclick="myMapper.update({zone: %d}); g_setSelectedLink(this, \'mapper\'); return false" onmousedown="return false">%s</a>&nbsp;(%d)';
public static $guideratingString = " $(document).ready(function() {\n $('#guiderating').append(GetStars(%.10F, %s, %u, %u));\n });";
public static $expansionString = [null, 'bc', 'wotlk'];
public static $tcEncoding = '0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789';

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "Meine Leitfäden",
'editTitle' => "Eigenen Leitfaden bearbeiten",
'newTitle' => "Leitfaden erstellen",
'author' => "Autor",
'spec' => "Spezialisierung",
'author' => "Autor: ",
'spec' => "Spezialisierung: ",
'sticky' => "Angeheftet",
'views' => "Ansichten",
'views' => "Ansichten: ",
'patch' => "Patch",
'added' => "Hinzugefügt",
'rating' => "Wertung",
'votes' => "[span id=guiderating-value]%d[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Bewertungen) [span id=guiderating][/span]",
'added' => "Hinzugefügt: ",
'rating' => "Wertung: ",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Bewertungen) [span id=guiderating][/span]",
'noVotes' => "nicht genug Bewertungen [span id=guiderating][/span]",
'byAuthor' => "Von %s",
'notFound' => "Dieser Leitfaden existiert nicht.",
'clTitle' => 'Änderungsprotokoll für "<a href="?guide=%1$d">%2$s</a>"',
'clStatusSet' => 'Status gesetzt auf %s',
'clCreated' => 'Erstellt',
'clStatusSet' => 'Status gesetzt auf %s: ',
'clCreated' => 'Erstellt: ',
'clMinorEdit' => 'Kleinere Bearbeitung',
'editor' => array(
'fullTitle' => 'Ganze Überschrift',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Name',
'nameTip' => 'Dies sollte ein einfacher und klarer Name für den Leitfaden sein, der an Orten wie Menüs und Leitfadenlisten verwendet werden kann.',
'description' => 'Beschreibung',
'descriptionTip' => 'Beschreibung, die für Suchmaschinen verwendet wird.&lt;br /&gt;&lt;br /&gt;Wenn leer, wird es automatisch generiert.',
'descriptionTip' => 'Beschreibung, die für Suchmaschinen verwendet wird.<br /><br />Wenn leer, wird es automatisch generiert.',
// 'commentEmail' => 'Emailbenachrichtigung',
// 'commentEmailTip' => 'Soll der Autor darüber benachrichtigt werden, dass Nutzer diesen Guide kommentieren?',
'changelog' => 'Änderungsprotokoll für diese Änderung',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'Sehen Sie, wie Ihr Leitfaden aussehen wird',
'images' => 'Bilder',
'statusTip' => array(
GUIDE_STATUS_DRAFT => 'Ihr Leitfaden ist im &quot;Entwurfs&quot;-Status und Sie sind der einzige der ihn sehen kann. Bearbeiten Sie ihn so lange Sie wollen und wenn Sie fertig sind reichen Sie ihn zur Überprüfung ein.',
GUIDE_STATUS_REVIEW => 'Ihr Leitfaden wird überprüft.',
GUIDE_STATUS_APPROVED => 'Ihr Leitfaden wurde veröffentlicht.',
GUIDE_STATUS_REJECTED => 'Ihr Leitfaden wurde abgewiesen. Nachdem die Mängel behoben wurde kann er erneut zur Überprüfung eingereicht werden.',
GUIDE_STATUS_ARCHIVED => 'Ihr Leitfaden ist veraltet und wurde archiviert. Er wird nicht mehr in der Übersicht gelistet und ist kann nicht mehr bearbeitet werden.]',
GuideMgr::STATUS_DRAFT => 'Ihr Leitfaden ist im &quot;Entwurfs&quot;-Status und Sie sind der einzige der ihn sehen kann. Bearbeiten Sie ihn so lange Sie wollen und wenn Sie fertig sind reichen Sie ihn zur Überprüfung ein.',
GuideMgr::STATUS_REVIEW => 'Ihr Leitfaden wird überprüft.',
GuideMgr::STATUS_APPROVED => 'Ihr Leitfaden wurde veröffentlicht.',
GuideMgr::STATUS_REJECTED => 'Ihr Leitfaden wurde abgewiesen. Nachdem die Mängel behoben wurde kann er erneut zur Überprüfung eingereicht werden.',
GuideMgr::STATUS_ARCHIVED => 'Ihr Leitfaden ist veraltet und wurde archiviert. Er wird nicht mehr in der Übersicht gelistet und ist kann nicht mehr bearbeitet werden.]',
)
),
'category' => array(

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "My Guides",
'editTitle' => "Edit your Guide",
'newTitle' => "Create New Guide",
'author' => "Author",
'spec' => "Specialization",
'author' => "Author: ",
'spec' => "Specialization: ",
'sticky' => "Sticky Status",
'views' => "Views",
'views' => "Views: ",
'patch' => "Patch",
'added' => "Added",
'rating' => "Rating",
'votes' => "[span id=guiderating-value]%d[/span]/5 ([span id=guiderating-votes][n5=%d][/span] votes) [span id=guiderating][/span]",
'added' => "Added: ",
'rating' => "Rating: ",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 ([span id=guiderating-votes][n5=%d][/span] votes) [span id=guiderating][/span]",
'noVotes' => "not enough votes [span id=guiderating][/span]",
'byAuthor' => "By %s",
'notFound' => "This guide doesn't exist.",
'clTitle' => 'Changelog For "<a href="?guide=%1$d">%2$s</a>"',
'clStatusSet' => 'Status set to %s',
'clCreated' => 'Created',
'clStatusSet' => 'Status set to %s: ',
'clCreated' => 'Created: ',
'clMinorEdit' => 'Minor Edit',
'editor' => array(
'fullTitle' => 'Full Title',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Name',
'nameTip' => 'This should be a simple and clear name of what the guide is, for use in places like menus and guide lists.',
'description' => 'Description',
'descriptionTip' => 'Description that will be used for search engines.&lt;br&gt;&lt;br&gt;If left empty, it will be generated automatically.',
'descriptionTip' => "Description that will be used for search engines.<br /> <br />If left empty, it will be generated automatically.",
// 'commentEmail' => 'Comment Emails',
// 'commentEmailTip' => 'Should the author get emailed whenever a user comments on this guide?',
'changelog' => 'Changelog For This Edit',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'See how your guide will look',
'images' => 'Images',
'statusTip' => array(
GUIDE_STATUS_DRAFT => 'Your guide is in &quot;Draft&quot; status and you are the only one able to see it. Keep editing it as long as you like, and when you feel it&apos;s ready submit it for review.',
GUIDE_STATUS_REVIEW => 'Your guide is being reviewed.',
GUIDE_STATUS_APPROVED => 'Your guide has been published.',
GUIDE_STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GUIDE_STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
GuideMgr::STATUS_DRAFT => 'Your guide is in &quot;Draft&quot; status and you are the only one able to see it. Keep editing it as long as you like, and when you feel it&apos;s ready submit it for review.',
GuideMgr::STATUS_REVIEW => 'Your guide is being reviewed.',
GuideMgr::STATUS_APPROVED => 'Your guide has been published.',
GuideMgr::STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GuideMgr::STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
)
),
'category' => array(

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "Mis Guías",
'editTitle' => "Editar tu Guía",
'newTitle' => "Crear Nueva Guía",
'author' => "Autor",
'spec' => "Especialización",
'author' => "Autor: ",
'spec' => "Especialización: ",
'sticky' => "Estado Fijo",
'views' => "Visto",
'views' => "Visto: ",
'patch' => "Parche",
'added' => "Añadido",
'rating' => "Valoración",
'votes' => "[span id=guiderating-value]%d[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Votos) [span id=guiderating][/span]",
'added' => "Añadido: ",
'rating' => "Valoración: ",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Votos) [span id=guiderating][/span]",
'noVotes' => "necesita más votaciones [span id=guiderating][/span]",
'byAuthor' => "Por %s",
'notFound' => "Este/a guía no existe.",
'clTitle' => 'Historial de cambios para "<a href="?guide=%1$d">%2$s</a>"',
'clStatusSet' => 'Estado cambiado a %s',
'clCreated' => 'Creado',
'clStatusSet' => 'Estado cambiado a %s: ',
'clCreated' => 'Creado: ',
'clMinorEdit' => 'Modificación menor',
'editor' => array(
'fullTitle' => 'Título completo',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Nombre',
'nameTip' => 'Este debe ser un nombre simple y claro de lo que es la guía, para usar en menús y listas de guías.',
'description' => 'Descripción',
'descriptionTip' => 'Descripción utilizada para los motores de búsqueda.&lt;br /&gt;&lt;br /&gt;Si se deja vacío, se generará automáticamente.',
'descriptionTip' => 'Descripción utilizada para los motores de búsqueda.<br /><br />Si se deja vacío, se generará automáticamente.',
// 'commentEmail' => 'Enviar comentarios por email',
// 'commentEmailTip' => '¿El autor debería ser notificado por correo cuando un usuario escriba comentarios en esta guía?',
'changelog' => 'Historial de cambios para esta modificación',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'Mira el aspecto de tu guía.',
'images' => 'Imágenes',
'statusTip' => array(
GUIDE_STATUS_DRAFT => 'Tu guía está en estado &quot;borrador&quot; y solo tú puedes verla. Tienes todo el tiempo del mundo para editarla y, cuando creas que ya está lista, envíala para su revisión.',
GUIDE_STATUS_REVIEW => 'Tu guía está siendo revisada.',
GUIDE_STATUS_APPROVED => 'Tu guía ha sido publicada.',
GUIDE_STATUS_REJECTED => 'Tu guía ha sido rechazada. Una vez que se hayan corregido las deficiencias, puedes volver a enviarla para revisión.',
GUIDE_STATUS_ARCHIVED => 'Tu guía está desactualizada y ha sido archivada. Ya no aparecerá en la lista y no se puede editar.',
GuideMgr::STATUS_DRAFT => 'Tu guía está en estado &quot;borrador&quot; y solo tú puedes verla. Tienes todo el tiempo del mundo para editarla y, cuando creas que ya está lista, envíala para su revisión.',
GuideMgr::STATUS_REVIEW => 'Tu guía está siendo revisada.',
GuideMgr::STATUS_APPROVED => 'Tu guía ha sido publicada.',
GuideMgr::STATUS_REJECTED => 'Tu guía ha sido rechazada. Una vez que se hayan corregido las deficiencias, puedes volver a enviarla para revisión.',
GuideMgr::STATUS_ARCHIVED => 'Tu guía está desactualizada y ha sido archivada. Ya no aparecerá en la lista y no se puede editar.',
)
),
'category' => array(

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "Mes guides",
'editTitle' => "Editez votre Guide",
'newTitle' => "Créer un nouveau Guide",
'author' => "Auteur",
'spec' => "Spécialisation",
'author' => "Auteur : ",
'spec' => "Spécialisation : ",
'sticky' => "Statut coller",
'views' => "Vues",
'views' => "Vues : ",
'patch' => "Patch",
'added' => "Ajouté",
'rating' => "Note",
'votes' => "[span id=guiderating-value]%d[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Votes) [span id=guiderating][/span]",
'added' => "Ajouté : ",
'rating' => "Note : ",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 ([span id=guiderating-votes][n5=%d][/span] Votes) [span id=guiderating][/span]",
'noVotes' => "pas assez de votes [span id=guiderating][/span]",
'byAuthor' => "Par %s",
'notFound' => "Ce guide n'existe pas.",
'clTitle' => 'Journal des changements pour "<a href="?guide=%1$d">%2$s</a>"',
'clStatusSet' => 'Statut défini comme %s',
'clCreated' => 'Créé',
'clStatusSet' => 'Statut défini comme %s : ',
'clCreated' => 'Créé : ',
'clMinorEdit' => 'Modification mineure',
'editor' => array(
'fullTitle' => 'Titre complet',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Nom',
'nameTip' => 'Ceci devrait être un nom clair et concis de ce en quoi consiste le guide, qui sera affiché dans les menus et listes de guides.',
'description' => 'Description',
'descriptionTip' => 'Description qui sera utilisée par les moteurs de recherche.&lt;br /&gt;&lt;br /&gt;S&apos;il est laissé vide, le résumé sera généré automatiquement.',
'descriptionTip' => 'Description qui sera utilisée par les moteurs de recherche.<br /><br />S&apos;il est laissé vide, le résumé sera généré automatiquement.',
// 'commentEmail' => 'Recevoir les commentaires par courriel',
// 'commentEmailTip' => 'L&apos;auteur doit-il recevoir un courriel chaque fois qu&apos;un utilisateur commente ce guide ?',
'changelog' => 'Journal des changements pour cette modification',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'Ayez un aperçu de votre guide',
'images' => 'Images',
'statusTip' => array(
GUIDE_STATUS_DRAFT => 'Votre guide est en statut &quot;Brouillon&quot; et vous êtes le seul à pouvoir le lire. Continuez de l&apos;écrire comme vous le voulez, et quand vous sentez qu&apos;il est prêt, soumettez-le pour approbation.',
GUIDE_STATUS_REVIEW => 'Your guide is being reviewed.',
GUIDE_STATUS_APPROVED => 'Your guide has been published.',
GUIDE_STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GUIDE_STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
GuideMgr::STATUS_DRAFT => 'Votre guide est en statut &quot;Brouillon&quot; et vous êtes le seul à pouvoir le lire. Continuez de l&apos;écrire comme vous le voulez, et quand vous sentez qu&apos;il est prêt, soumettez-le pour approbation.',
GuideMgr::STATUS_REVIEW => 'Your guide is being reviewed.',
GuideMgr::STATUS_APPROVED => 'Your guide has been published.',
GuideMgr::STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GuideMgr::STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
)
),
'category' => array(

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "Мои руководства",
'editTitle' => "Редактировать руководство",
'newTitle' => "Написать новое руководство",
'author' => "Автор",
'spec' => "Спек",
'author' => "Автор: ",
'spec' => "Спек: ",
'sticky' => "Закрепленный",
'views' => "Просмотры",
'views' => "Просмотры: ",
'patch' => "Обновление",
'added' => "Добавлено",
'rating' => "Рейтинг",
'votes' => "[span id=guiderating-value]%d[/span]/5 ([span id=guiderating-votes][n5=%d][/span] проголосовало) [span id=guiderating][/span]",
'added' => "Добавлено: ",
'rating' => "Рейтинг: ",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 ([span id=guiderating-votes][n5=%d][/span] проголосовало) [span id=guiderating][/span]",
'noVotes' => "недостаточно голосов [span id=guiderating][/span]",
'byAuthor' => "От %s",
'notFound' => "Такого руководство не существует.",
'clTitle' => 'История изменений «<a href="?guide=%1$d">%2$s</a>»',
'clStatusSet' => 'Присвоен статус «%s»',
'clCreated' => 'Создано',
'clStatusSet' => 'Присвоен статус «%s»: ',
'clCreated' => 'Создано: ',
'clMinorEdit' => 'Небольшое изменение',
'editor' => array(
'fullTitle' => 'Полный заголовок',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Имя',
'nameTip' => 'Укажите краткое и понятное название руководства. Оно будет использоватья в меню и перечнях руководств.',
'description' => 'Описание',
'descriptionTip' => 'Описание для поисковых систем.&lt;br&gt;&lt;br&gt;Если поле будет оставлено пустым, то сайт сгенерирует описание автоматически.',
'descriptionTip' => 'Описание для поисковых систем.<br /><br />Если поле будет оставлено пустым, то сайт сгенерирует описание автоматически.',
// 'commentEmail' => 'E-mail уведомления',
// 'commentEmailTip' => 'Должен ли автор руководства получать e-mail оповещения, когда к руководству оставляют комментарий?',
'changelog' => 'История изменений, внесенных этой правкой',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'Посмотрите, как будет выглядеть руководство',
'images' => 'Images',
'statusTip' => array(
GUIDE_STATUS_DRAFT => 'Руководство сохранено как &quot;Черновик&quot; — видеть его можете только вы. Правьте руководство так долго, как сочтете нужным, а когда решите, что оно готово — отправьте на одобрение.',
GUIDE_STATUS_REVIEW => 'Your guide is being reviewed.',
GUIDE_STATUS_APPROVED => 'Your guide has been published.',
GUIDE_STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GUIDE_STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
GuideMgr::STATUS_DRAFT => 'Руководство сохранено как &quot;Черновик&quot; — видеть его можете только вы. Правьте руководство так долго, как сочтете нужным, а когда решите, что оно готово — отправьте на одобрение.',
GuideMgr::STATUS_REVIEW => 'Your guide is being reviewed.',
GuideMgr::STATUS_APPROVED => 'Your guide has been published.',
GuideMgr::STATUS_REJECTED => 'Your guide has been rejected. After it\'s shortcomings have been remedied you may resubmit it for review.',
GuideMgr::STATUS_ARCHIVED => 'Your guide is outdated and has been archived. Is will no longer be listed and can\'t be edited.',
)
),
'category' => array(

View File

@@ -167,20 +167,20 @@ $lang = array(
'myGuides' => "我的指南",
'editTitle' => "编辑你的指南",
'newTitle' => "创建新指南",
'author' => "作者",
'spec' => "专精",
'author' => "作者",
'spec' => "专精",
'sticky' => "置顶状态",
'views' => "浏览量",
'views' => "浏览量",
'patch' => "补丁",
'added' => "已添加",
'rating' => "评分",
'votes' => "[span id=guiderating-value]%d[/span]/5 [span id=guiderating-votes][n5=%d][/span] 投票)[span id=guiderating][/span]",
'added' => "已添加",
'rating' => "评分",
'votes' => "[span id=guiderating-value]%.2g[/span]/5 [span id=guiderating-votes][n5=%d][/span] 投票)[span id=guiderating][/span]",
'noVotes' => "投票数量不足 [span id=guiderating][/span]",
'byAuthor' => "来自 %s",
'notFound' => "该指南不存在。",
'clTitle' => '修改日志 "<a href="?guide=%1$d">%2$s</a>"',
'clStatusSet' => '状态已设置为 %s',
'clCreated' => '已创建',
'clStatusSet' => '状态已设置为 %s',
'clCreated' => '已创建',
'clMinorEdit' => '小修改',
'editor' => array(
'fullTitle' => '完整标题',
@@ -188,7 +188,7 @@ $lang = array(
'name' => '名称',
'nameTip' => '这应该是一个简单明了的指南名称,用于菜单和指南列表',
'description' => '描述',
'descriptionTip' => '描述将用于说明片段&lt;br /&gt;&lt;br /&gt;如果不填,则自动生成。',
'descriptionTip' => '描述将用于说明片段<br /><br />如果不填,则自动生成。',
// 'commentEmail' => '评论电子邮件',
// 'commentEmailTip' => '当用户对此指南发表评论时,作者是否收到电子邮件通知?',
'changelog' => '当前编辑的修改日志',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => '自我浏览你的指南',
'images' => '图片',
'statusTip' => array(
GUIDE_STATUS_DRAFT => '你的指南目前是草稿状态,只有你自己可见。试着输入更多的文字,当你觉得可以了的时候就提交送审吧。',
GUIDE_STATUS_REVIEW => '你的指南正在审核中',
GUIDE_STATUS_APPROVED => '你的指南已发布',
GUIDE_STATUS_REJECTED => '你的指南已被拒绝。在修正问题后,你可以重新提交审核。',
GUIDE_STATUS_ARCHIVED => '你的指南已过时,并已归档。它将不再列出,也无法编辑。',
GuideMgr::STATUS_DRAFT => '你的指南目前是草稿状态,只有你自己可见。试着输入更多的文字,当你觉得可以了的时候就提交送审吧。',
GuideMgr::STATUS_REVIEW => '你的指南正在审核中',
GuideMgr::STATUS_APPROVED => '你的指南已发布',
GuideMgr::STATUS_REJECTED => '你的指南已被拒绝。在修正问题后,你可以重新提交审核。',
GuideMgr::STATUS_ARCHIVED => '你的指南已过时,并已归档。它将不再列出,也无法编辑。',
)
),
'category' => array(

View File

@@ -225,7 +225,7 @@ class AdminPage extends GenericPage
private function handleGuideApprove() : void
{
$pending = new GuideList([['status', GUIDE_STATUS_REVIEW]]);
$pending = new GuideList([['status', GuideMgr::STATUS_REVIEW]]);
if ($pending->error)
$data = [];
else

View File

@@ -1,549 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId ?: Category g_initPath()
// tabid 6: Guides g_initHeader()
class GuidePage extends GenericPage
{
use TrDetailPage;
const SHOW_NEW = 1;
const SHOW_EDITOR = 2;
const SHOW_GUIDE = 3;
const SHOW_CHANGELOG = 4;
const VALID_URL = '/^[a-z0-9=_&\.\/\-]{2,64}$/i';
protected /* array */ $guideRating = [];
protected /* ?string */$extraHTML = null;
protected /* int */ $type = Type::GUIDE;
protected /* int */ $typeId = 0;
protected /* int */ $guideRevision = -1;
protected /* string */ $tpl = 'detail-page-generic';
protected /* array */ $path = [6];
protected /* int */ $tabId = 6;
protected /* int */ $mode = CACHE_TYPE_PAGE;
protected /* string */ $author = '';
protected /* array */ $gPageInfo = [];
protected /* int */ $show = self::SHOW_GUIDE;
private /* array */ $validCats = [1, 2, 3, 4, 5, 6, 7, 8, 9];
private /* string */ $extra = '';
private /* string */ $powerTpl = '$WowheadPower.registerGuide(%s, %d, %s);';
private /* array */ $editorFields = [];
private /* bool */ $save = false;
protected /* array */ $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'],
'rev' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt']
);
protected /* array */ $_post = array(
'save' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkEmptySet'],
'submit' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkEmptySet'],
'title' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkTextLine'],
'name' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkTextLine'],
'description' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GuidePage::checkDescription'],
'changelog' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkTextBlob'],
'body' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkTextBlob'],
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'],
'category' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'],
'specId' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'],
'classId' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt']
);
public function __construct($pageCall, $pageParam)
{
$guide = explode( "&", $pageParam, 2);
parent::__construct($pageCall, $pageParam);
if (isset($guide[1]) && preg_match(self::VALID_URL, $guide[1]))
$this->extra = $guide[1];
/**********************/
/* get mode + guideId */
/**********************/
if (Util::checkNumeric($guide[0], NUM_CAST_INT))
$this->typeId = $guide[0];
else if (preg_match(self::VALID_URL, $guide[0]))
{
switch ($guide[0])
{
case 'changelog':
if (!$this->_get['id'])
break;
$this->show = self::SHOW_CHANGELOG;
$this->tpl = 'text-page-generic';
$this->article = false; // do not include article from db
// main container should be tagged: <div class="text guide-changelog">
// why is this here: is there a mediawiki like diff function for staff?
$this->addScript([SC_CSS_STRING, 'li input[type="radio"] {margin:0}']);
$this->typeId = $this->_get['id']; // just to display sensible not-found msg
if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `id` = ?d', $this->typeId))
$this->typeId = intVal($id);
break;
case 'new':
if (User::canWriteGuide())
{
$this->show = self::SHOW_NEW;
$this->guideRevision = null;
$this->initNew();
return; // do not create new GuideList
}
break;
case 'edit':
if (User::canWriteGuide())
{
if (!$this->initEdit())
$this->notFound(Lang::game('guide'), Lang::guide('notFound'));
$this->show = self::SHOW_EDITOR;
}
break;
default:
if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `url` = ?', Util::lower($guide[0])))
{
$this->typeId = intVal($id);
$this->guideRevision = null;
$this->articleUrl = Util::lower($guide[0]);
}
}
}
/*********************/
/* load actual guide */
/*********************/
$this->subject = new GuideList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Lang::game('guide'), Lang::guide('notFound'));
if (!$this->subject->canBeViewed() && !$this->subject->userCanView())
header('Location: ?guides='.$this->subject->getField('category'), true, 302);
if ($this->show == self::SHOW_GUIDE && $this->_get['rev'] !== null && !$this->articleUrl && $this->subject->userCanView())
$this->guideRevision = $this->_get['rev'];
else if ($this->show == self::SHOW_GUIDE && !$this->articleUrl)
$this->guideRevision = $this->subject->getField('rev');
else
$this->guideRevision = null;
if (!$this->name)
$this->name = $this->subject->getField('name');
}
protected function generateContent() : void
{
match ($this->show)
{
self::SHOW_NEW => $this->displayNew(),
self::SHOW_EDITOR => $this->displayEditor(),
self::SHOW_GUIDE => $this->displayGuide(),
self::SHOW_CHANGELOG => $this->displayChangelog(),
default => trigger_error('GuidePage::generateContent - what content!?')
};
}
private function displayNew() : void
{
// init required template vars
$this->editorFields = array(
'locale' => Lang::getLocale()->value,
'status' => GUIDE_STATUS_DRAFT
);
}
private function displayEditor() : void
{
// can't check in init as subject is unknown
if ($this->subject->getField('status') == GUIDE_STATUS_ARCHIVED)
$this->notFound(Lang::game('guide'), Lang::guide('notFound'));
$status = GUIDE_STATUS_NONE;
$rev = DB::Aowow()->selectCell('SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1', Type::GUIDE, $this->typeId);
$curStatus = DB::Aowow()->selectCell('SELECT `status` FROM ?_guides WHERE `id` = ?d ', $this->typeId);
if ($rev === null)
$rev = 0;
if ($this->save)
{
$rev++;
// insert Article
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'], $rev, User::$groups & U_GROUP_STAFF ? User::$groups : User::$groups | U_GROUP_BLOGGER, $this->_post['body']);
// link to Guide
$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'] ?: Lang::trimTextClean(Markup::stripTags($this->_post['body']), 120),
'locale' => $this->_post['locale'],
'roles' => User::$groups,
'status' => GUIDE_STATUS_DRAFT
);
DB::Aowow()->query('UPDATE ?_guides SET ?a WHERE `id` = ?d', $guideData, $this->typeId);
// new guide -> reload editor
if ($this->_get['id'] === 0)
header('Location: ?guide=edit&id='.$this->typeId, true, 302);
else
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `rev`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?d, ?)', $this->typeId, $rev, time(), User::$id, $this->_post['changelog']);
if ($this->_post['submit'])
{
$status = GUIDE_STATUS_REVIEW;
if ($curStatus != GUIDE_STATUS_REVIEW)
{
DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', GUIDE_STATUS_REVIEW, $this->typeId);
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $this->typeId, time(), User::$id, GUIDE_STATUS_REVIEW);
}
}
}
// init required template vars
$this->editorFields = array(
'category' => $this->_post['category'] ?? $this->subject->getField('category'),
'title' => $this->_post['title'] ?? $this->subject->getField('title'),
'name' => $this->_post['name'] ?? $this->subject->getField('name'),
'description' => $this->_post['description'] ?? $this->subject->getField('description'),
'text' => $this->_post['body'] ?? $this->subject->getArticle(),
'status' => $status ?: $this->subject->getField('status'),
'classId' => $this->_post['classId'] ?? $this->subject->getField('classId'),
'specId' => $this->_post['specId'] ?? $this->subject->getField('specId'),
'locale' => $this->_post['locale'] ?? $this->subject->getField('locale'),
'rev' => $rev
);
$this->extendGlobalData($this->subject->getJSGlobals());
}
private function displayGuide() : void
{
if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_QUICKFACTS))
{
$qf = [];
if ($this->subject->getField('cuFlags') & CC_FLAG_STICKY)
$qf[] = '[span class=guide-sticky]'.Lang::guide('sticky').'[/span]';
$qf[] = Lang::guide('author').Lang::main('colon').'[url=?user='.$this->subject->getField('author').']'.$this->subject->getField('author').'[/url]';
if ($this->subject->getField('category') == 1)
{
$c = $this->subject->getField('classId');
$s = $this->subject->getField('specId');
if ($c > 0)
{
$this->extendGlobalIds(Type::CHR_CLASS, $c);
$qf[] = Util::ucFirst(Lang::game('class')).Lang::main('colon').'[class='.$c.']';
}
if ($s > -1)
$qf[] = Lang::guide('spec').Lang::main('colon').'[icon class="c'.$c.' icontiny" name='.Game::$specIconStrings[$c][$s].']'.Lang::game('classSpecs', $c, $s).'[/icon]';
}
// $qf[] = Lang::guide('patch').Lang::main('colon').'3.3.5'; // replace with date
$qf[] = Lang::guide('added').Lang::main('colon').'[tooltip name=added]'.date('l, G:i:s', $this->subject->getField('date')).'[/tooltip][span class=tip tooltip=added]'.date(Lang::main('dateFmtShort'), $this->subject->getField('date')).'[/span]';
switch ($this->subject->getField('status'))
{
case GUIDE_STATUS_APPROVED:
$qf[] = Lang::guide('views').Lang::main('colon').'[n5='.$this->subject->getField('views').']';
if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_RATING))
{
$this->guideRating = array(
$this->subject->getField('rating'), // avg rating
User::canUpvote() && User::canDownvote() ? 'true' : 'false',
$this->subject->getField('_self'), // my rating amt; 0 = no vote
$this->typeId // guide Id
);
if ($this->subject->getField('nvotes') < 5)
$qf[] = Lang::guide('rating').Lang::main('colon').Lang::guide('noVotes');
else
$qf[] = Lang::guide('rating').Lang::main('colon').Lang::guide('votes', [round($this->subject->getField('rating'), 1), $this->subject->getField('nvotes')]);
}
break;
case GUIDE_STATUS_ARCHIVED:
$qf[] = Lang::guide('status', GUIDE_STATUS_ARCHIVED);
break;
}
$qf = '[ul][li]'.implode('[/li][li]', $qf).'[/li][/ul]';
if ($this->subject->getField('status') == GUIDE_STATUS_REVIEW && User::isInGroup(U_GROUP_STAFF) && $this->_get['rev'])
{
$this->addScript([SC_JS_STRING, '
DomContentLoaded.addEvent(function() {
let send = function (status)
{
let message = "";
let id = $WH.g_getGets().guide;
if (status == 4) // rejected
{
while (message === "")
message = prompt("Please provide your reasoning.");
if (message === null)
return false;
}
$.ajax({cache: false, url: "?admin=guide", type: "POST",
error: function() {
alert("Operation failed.");
},
success: function(json) {
if (json != 1)
alert("Operation failed.");
else
window.location.href = "?admin=guides";
},
data: { id: id, status: status, msg: message }
})
return true;
};
$WH.ge("btn-accept").onclick = send.bind(null, 3);
$WH.ge("btn-reject").onclick = send.bind(null, 4);
});
']);
$qf .= '[h3 style="text-align:center"]Admin[/h3]';
$qf .= '[div style="text-align:center"][url=# id="btn-accept" class=icon-tick]Approve[/url][url=# style="margin-left:20px" id="btn-reject" class=icon-delete]Reject[/url][/div]';
}
}
$this->redButtons[BUTTON_GUIDE_LOG] = true;
$this->redButtons[BUTTON_GUIDE_REPORT] = $this->subject->canBeReported();
$this->infobox = $qf ?? '';
$this->author = $this->subject->getField('author'); // add to g_pageInfo in GenericPage:prepareContent()
if ($this->subject->userCanView())
$this->redButtons[BUTTON_GUIDE_EDIT] = User::canWriteGuide() && $this->subject->getField('status') != GUIDE_STATUS_ARCHIVED;
// the article text itself is added by GenericPage::addArticle()
}
private function displayChangelog() : void
{
$this->addScript([SC_JS_STRING, '
$(document).ready(function() {
var radios = $("input[type=radio]");
function limit(col, val) {
radios.each(function(i, e) {
if (col == e.name)
return;
if (col == "b")
e.disabled = (val <= parseInt(e.value));
else if (col == "a")
e.disabled = (val >= parseInt(e.value));
});
};
radios.each(function (i, e) {
e.onchange = limit.bind(this, e.name, parseInt(e.value));
if (i < 2 && e.name == "b") // first pair
$(e).trigger("click");
else if (e.value == 0 && e.name == "a") // last pair
$(e).trigger("click");
});
});
']);
$buff = '<ul>';
$inp = fn($rev) => User::isInGroup(U_GROUP_STAFF) ? ($rev !== null ? '<input name="a" value="'.$rev.'" type="radio"/><input name="b" value="'.$rev.'" type="radio"/><b>' : '<b style="margin-left:28px;">') : '';
$logEntries = DB::Aowow()->select('SELECT a.`username` AS `name`, gcl.`date`, gcl.`status`, gcl.`msg`, gcl.`rev` FROM ?_guides_changelog gcl JOIN ?_account a ON a.`id` = gcl.`userId` WHERE gcl.`id` = ?d ORDER BY gcl.`date` DESC', $this->typeId);
foreach ($logEntries as $log)
{
if ($log['status'] != GUIDE_STATUS_NONE)
$buff .= '<li class="guide-changelog-status-change">'.$inp($log['rev']).Lang::guide('clStatusSet', [Lang::guide('status', $log['status'])]).Lang::main('colon').'</b>'.Util::formatTimeDiff($log['date'])."</li>\n";
else if ($log['msg'])
$buff .= '<li>'.$inp($log['rev']).Util::formatTimeDiff($log['date']).Lang::main('colon').'</b>'.$log['msg'].' <i class="q0">'.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."</i></li>\n";
else
$buff .= '<li class="guide-changelog-minor-edit">'.$inp($log['rev']).Util::formatTimeDiff($log['date']).Lang::main('colon').'</b><i>'.Lang::guide('clMinorEdit').'</i> <i class="q0">'.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."</i></li>\n";
}
// append creation
$buff .= '<li class="guide-changelog-created">'.$inp(0).'<b>'.Lang::guide('clCreated').Lang::main('colon').'</b>'.Util::formatTimeDiff($this->subject->getField('date'))."</li>\n</ul>\n";
if (User::isInGroup(U_GROUP_STAFF))
$buff .= '<input type="button" value="Compare" onclick="alert(\'NYI\');"/>';
$this->name = lang::guide('clTitle', [$this->typeId, $this->subject->getField('title')]);
$this->extraHTML = $buff;
}
private function initNew() : void
{
$this->addScript(
[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, '#upload-result input[type=text] { padding: 0px 2px; font-size: 12px; }'],
[SC_CSS_STRING, '#upload-result > span { display:block; height: 22px; }'],
[SC_CSS_STRING, '#upload-result { display: inline-block; text-align:right; }'],
[SC_CSS_STRING, '#upload-progress { display: inline-block; margin-right:8px; }']
);
$this->articleUrl = 'new';
$this->tpl = 'guide-edit';
$this->name = Lang::guide('newTitle');
Lang::sort('guide', 'category');
$this->typeId = 0; // signals 'edit' to create new guide
}
private function initEdit() : bool
{
$this->addScript(
[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, '#upload-result input[type=text] { padding: 0px 2px; font-size: 12px; }'],
[SC_CSS_STRING, '#upload-result > span { display:block; height: 22px; }'],
[SC_CSS_STRING, '#upload-result { display: inline-block; text-align:right; }'],
[SC_CSS_STRING, '#upload-progress { display: inline-block; margin-right:8px; }']
);
$this->articleUrl = 'edit';
$this->tpl = 'guide-edit';
$this->name = Lang::guide('editTitle');
$this->save = $this->_post['save'] || $this->_post['submit'];
// reject inconsistent guide data
if ($this->save)
{
// req: set data
if (!$this->_post['title'] || !$this->_post['name'] || !$this->_post['body'] || $this->_post['locale'] === null)
return false;
// req: valid data
if (!in_array($this->_post['category'], $this->validCats) || !(Cfg::get('LOCALES') & (1 << $this->_post['locale'])))
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 (!in_array($this->_post['specId'], [-1, 0, 1, 2]))
$this->_post['specId'] = -1;
if ($this->_post['specId'] > -1 && !$this->_post['classId'])
$this->_post['specId'] = -1;
}
else
{
$this->_post['classId'] = 0;
$this->_post['specId'] = -1;
}
}
if ($this->_get['id']) // edit existing guide
{
$this->typeId = $this->_get['id']; // just to display sensible not-found msg
if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `id` = ?d AND `status` <> ?d {AND `userId` = ?d}', $this->typeId, GUIDE_STATUS_ARCHIVED, User::isInGroup(U_GROUP_STAFF) ? DBSIMPLE_SKIP : User::$id))
$this->typeId = intVal($id);
}
else if ($this->_get['id'] === 0) // create new guide and load in editor
$this->typeId = DB::Aowow()->query('INSERT INTO ?_guides (`userId`, `date`, `status`) VALUES (?d, ?d, ?d)', User::$id, time(), GUIDE_STATUS_DRAFT);
return $this->typeId > 0;
}
protected function editorFields(string $field, bool $asInt = false) : string|int
{
return $this->editorFields[$field] ?? ($asInt ? 0 : '');
}
protected function generateTooltip()
{
$power = new \StdClass();
if (!$this->subject->error)
{
$power->{'name_'.Lang::getLocale()->json()} = strip_tags($this->name);
$power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip();
}
return sprintf($this->powerTpl, Util::toJSON($this->articleUrl ?: $this->typeId), Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER));
}
protected function generatePath() : void
{
if ($x = $this->subject?->getField('category'))
$this->path[] = $x;
}
protected function generateTitle() : void
{
if ($this->show == self::SHOW_EDITOR)
array_unshift($this->title, Lang::guide('editTitle').Lang::main('colon').$this->subject->getField('title'), Lang::game('guides'));
if ($this->show == self::SHOW_NEW)
array_unshift($this->title, Lang::guide('newTitle'), Lang::game('guides'));
else
array_unshift($this->title, $this->subject->getField('title'), Lang::game('guides'));
}
protected function postCache() : void
{
// increment views of published guide; ignore caching
if ($this->subject?->getField('status') == GUIDE_STATUS_APPROVED)
DB::Aowow()->query('UPDATE ?_guides SET `views` = `views` + 1 WHERE `id` = ?d', $this->typeId);
}
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));
}
}
?>

View File

@@ -1,102 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId ?:Category g_initPath()
// tabid 6:Guides g_initHeader()
class GuidesPage extends GenericPage
{
use TrListPage;
protected $type = Type::GUIDE;
protected $tpl = 'list-page-generic';
protected $path = [6];
protected $tabId = 6;
protected $mode = CACHE_TYPE_PAGE;
protected $validCats = [null, 1, 2, 3, 4, 5, 6, 7, 8, 9];
private $myGuides = false;
public function __construct($pageCall, $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageCall, $pageParam);
if ($pageCall == 'my-guides')
{
if (!User::isLoggedIn())
$this->error();
$this->name = Util::ucFirst(Lang::guide('myGuides'));
$this->myGuides = true;
}
else
$this->name = Util::ucFirst(Lang::game('guides'));
}
protected function generateContent()
{
$hCols = ['patch']; // pointless: display date instead
$vCols = [];
$xCols = ['$Listview.extraCols.date']; // ok
if ($this->myGuides)
{
$conditions = [['userId', User::$id]];
$hCols[] = 'author';
$vCols[] = 'status';
}
else
{
$conditions = array(
['locale', Lang::getLocale()->value],
['status', GUIDE_STATUS_ARCHIVED, '!'], // never archived guides
[
'OR',
['status', GUIDE_STATUS_APPROVED], // currently approved
['rev', 0, '>'] // has previously approved revision
]
);
if (isset($this->category[0]))
$conditions[] = ['category', $this->category];
}
$data = [];
$guides = new GuideList($conditions);
if (!$guides->error)
$data = array_values($guides->getListviewData());
$tabData = array(
'data' => $data,
'name' => Util::ucFirst(Lang::game('guides')),
'hiddenCols' => $hCols,
'visibleCols' => $vCols,
'extraCols' => $xCols
);
$this->lvTabs[] = [GuideList::$brickFile, $tabData];
$this->redButtons = [BUTTON_GUIDE_NEW => User::canWriteGuide()];
}
protected function generateTitle()
{
array_unshift($this->title, $this->name);
if (isset($this->category[0]))
array_unshift($this->title, Lang::guide('category', $this->category[0]));
}
protected function generatePath()
{
if (isset($this->category[0]))
$this->path[] = $this->category[0];
}
}
?>

View File

@@ -22987,17 +22987,14 @@ function g_modifyUrl(url, params, opt) {
}
function g_enhanceTextarea (ta, opt) {
if (!(ta instanceof jQuery)) {
if (!(ta instanceof jQuery))
ta = $(ta);
}
if (ta.data("wh-enhanced") || ta.prop("tagName") != "TEXTAREA") {
if (ta.data("wh-enhanced") || ta.prop("tagName") != "TEXTAREA")
return;
}
if (typeof opt != "object") {
if (typeof opt != "object")
opt = {};
}
var canResize = (function(el) {
if (!el.dynamicResizeOption)
@@ -23016,63 +23013,66 @@ function g_enhanceTextarea (ta, opt) {
var wrapper = $("<div/>", { "class": "enhanced-textarea-wrapper" }).insertBefore(ta).append(ta);
if (!opt.hasOwnProperty("color"))
wrapper.addClass("enhanced-textarea-dark")
wrapper.addClass("enhanced-textarea-dark");
else if (opt.color)
wrapper.addClass("enhanced-textarea-" + opt.color)
wrapper.addClass("enhanced-textarea-" + opt.color);
if (!opt.hasOwnProperty("dynamicSizing") || opt.dynamicSizing || opt.dynamicResizeOption) {
var expander = $("<div/>", { "class": "enhanced-textarea-expander" }).prependTo(wrapper);
var n = function(E, D, F) {
if (!F())
var dynamicResize = function(textarea, exactHeight, canResizeFn) {
if (!canResizeFn())
return;
// E.css("height", E.siblings(".enhanced-textarea-expander").html($WH.htmlentities(E.val()).replace(/\n/g, "<br>") + "<br>").height() + (D ? 14 : 34) + "px")
E.css("height", E.siblings(".enhanced-textarea-expander").html($WH.htmlentities(E.val()) + "<br>").height() + (D ? 14 : 34) + "px")
// E.css("height", E.siblings(".enhanced-textarea-expander").html($WH.htmlentities(E.val()).replace(/\n/g, "<br>") + "<br>").height() + (D ? 14 : 34) + "px");
textarea.css("height", textarea.siblings(".enhanced-textarea-expander").html($WH.htmlentities(textarea.val()) + "<br>").height() + (exactHeight ? 14 : 34) + "px");
};
ta.bind("keydown keyup change", n.bind(this, ta, opt.exactLineHeights, canResize));
n(ta, opt.exactLineHeights, canResize);
var setWidth = function(D) {
D.css("width", D.parent().width() + "px")
};
ta.bind("keydown keyup change", dynamicResize.bind(this, ta, opt.exactLineHeights, canResize));
dynamicResize(ta, opt.exactLineHeights, canResize);
var setWidth = function(el) { el.css("width", el.parent().width() + "px"); };
setWidth(expander);
setTimeout(setWidth.bind(null, expander), 1);
if (!opt.dynamicResizeOption || (opt.dynamicResizeOption && canResize())) {
wrapper.addClass("enhanced-textarea-dynamic-sizing")
}
}
if (!opt.hasOwnProperty("focusChanges") || opt.focusChanges) {
wrapper.addClass("enhanced-textarea-focus-changes")
if (!opt.dynamicResizeOption || (opt.dynamicResizeOption && canResize()))
wrapper.addClass("enhanced-textarea-dynamic-sizing");
}
if (!opt.hasOwnProperty("focusChanges") || opt.focusChanges)
wrapper.addClass("enhanced-textarea-focus-changes");
if (opt.markup) {
var w = $("<div/>", { "class": "enhanced-textarea-markup-wrapper" }).prependTo(wrapper);
var y = $("<div/>", { "class": "enhanced-textarea-markup" }).appendTo(w);
var z = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(y);
var k = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(y);
var _markupMenu = $("<div/>", { "class": "enhanced-textarea-markup-wrapper" }).prependTo(wrapper);
var _segments = $("<div/>", { "class": "enhanced-textarea-markup" }).appendTo(_markupMenu);
var _toolbar = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments);
var _menu = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments);
if (opt.markup == "inline")
ar_AddInlineToolbar(ta.get(0), z.get(0), k.get(0));
ar_AddInlineToolbar(ta.get(0), _toolbar.get(0), _menu.get(0));
else
ar_AddToolbar(ta.get(0), z.get(0), k.get(0));
ar_AddToolbar(ta.get(0), _toolbar.get(0), _menu.get(0));
if (opt.dynamicResizeOption) {
var t = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(y);
var C = $("<label/>").appendTo(t);
var A = $("<input/>", { type: "checkbox", checked: canResize() }).appendTo(C);
A.change((function(E, D, H, J, F, G) {
var I = this.is(":checked");
$WH.localStorage.set("dynamic-textarea-resizing", JSON.stringify(I));
if (I) {
D.addClass("enhanced-textarea-dynamic-sizing");
G(H, E.exactLineHeights, J);
var _dynResize = $("<div/>", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments);
var _lblDynResize = $("<label/>").appendTo(_dynResize);
var _iDynResize = $("<input/>", { type: "checkbox", checked: canResize() }).appendTo(_lblDynResize);
_iDynResize.change((function(_opt, taWrapper, textarea, resizable, areaHeight, dynResizeFn) {
var isChecked = this.is(":checked");
$WH.localStorage.set("dynamic-textarea-resizing", JSON.stringify(isChecked));
if (isChecked) {
taWrapper.addClass("enhanced-textarea-dynamic-sizing");
dynResizeFn(textarea, _opt.exactLineHeights, resizable);
}
else {
D.removeClass("enhanced-textarea-dynamic-sizing");
H.css("height", F + "px");
taWrapper.removeClass("enhanced-textarea-dynamic-sizing");
textarea.css("height", areaHeight + "px");
}
}).bind(A, opt, wrapper, ta, canResize, height, n));
}).bind(_iDynResize, opt, wrapper, ta, canResize, height, dynamicResize));
$("<span/>", { text: LANG.autoresizetextbox }).appendTo(C);
$("<span/>", { text: LANG.autoresizetextbox }).appendTo(_lblDynResize);
}
if (opt.scrollingMarkup) {
@@ -23081,40 +23081,47 @@ function g_enhanceTextarea (ta, opt) {
else
g_enhanceTextarea.scrollerCount = 1;
var B = "fixable-markup-controls-" + g_enhanceTextarea.scrollerCount;
var o = "fixed-markup-controls-" + g_enhanceTextarea.scrollerCount;
var setBGColor = function(el) {
var cssClassA = "fixable-markup-controls-" + g_enhanceTextarea.scrollerCount;
var cssClassB = "fixed-markup-controls-" + g_enhanceTextarea.scrollerCount;
var getBGColor = function(el) {
var color = el.css("backgroundColor");
if (color == "rgba(0, 0, 0, 0)" || color == "transparent")
return setBGColor(el.parent());
return getBGColor(el.parent());
else
return color;
};
var r = setBGColor(y);
for (var m, l = 0;
(m = window.document.styleSheets[l]) && m.href; l++) {}
if (!m) {
var bgColor = getBGColor(_segments);
for (var css, i = 0; (css = window.document.styleSheets[i]) && css.href; i++) {}
if (!css) {
window.document.head.appendChild(document.createElement("style"));
m = window.document.styleSheets[l];
css = window.document.styleSheets[i];
}
m.insertRule("." + o + " ." + B + " .enhanced-textarea-markup {background:" + r + ";padding-bottom:5px;padding-top:10px;position:fixed;top:0;z-index:3}", m.cssRules.length);
m.insertRule(".touch-device ." + o + " ." + B + " .enhanced-textarea-markup {padding-top:50px}", m.cssRules.length);
w.addClass(B);
var s = function(F, D, cssClass, offset) {
var H = this.scrollY || this.pageYOffset || 0;
if (H > F.offset().top - 10 - offset && H < D.offset().top + D.height() - 100 - offset)
css.insertRule("." + cssClassB + " ." + cssClassA + " .enhanced-textarea-markup {background:" + bgColor + ";padding-bottom:5px;padding-top:10px;position:fixed;top:0;z-index:3}", css.cssRules.length);
css.insertRule(".touch-device ." + cssClassB + " ." + cssClassA + " .enhanced-textarea-markup {padding-top:50px}", css.cssRules.length);
_markupMenu.addClass(cssClassA);
var toggleFixedStyles = function(menuContainer, taContainer, cssClass, offset) {
var pageY = this.scrollY || this.pageYOffset || 0;
if (pageY > menuContainer.offset().top - 10 - offset && pageY < taContainer.offset().top + taContainer.height() - 100 - offset)
$("body").addClass(cssClass);
else
$("body").removeClass(cssClass);
};
$(window).scroll(s.bind(window, w, wrapper, o, 0));
s.call(window, w, wrapper, o, 0);
$(window).scroll(toggleFixedStyles.bind(window, _markupMenu, wrapper, cssClassB, 0));
toggleFixedStyles.call(window, _markupMenu, wrapper, cssClassB, 0);
var setSize = (function(D, E) {
E.css("width", D.width() + "px");
D.css("height", E.height() + "px");
}).bind(null, w, y);
}).bind(null, _markupMenu, _segments);
setSize();
$(window).on("resize", setSize);
$(function() { setTimeout(setSize, 2000) })
}
@@ -23139,119 +23146,123 @@ $WH.createOptionsMenuWidget = function (id, txt, opt) {
if (opt.className)
container.className += ' ' + opt.className;
if (opt.options instanceof Array) {
var c = [];
for (var itr = 0, d; d = opt.options[itr]; itr++) {
var menu = [itr, d[MENU_IDX_NAME]];
if ((typeof opt.selected == 'number' || typeof opt.selected == 'string') && opt.selected == d[MENU_IDX_ID]) {
container.innerHTML = d[MENU_IDX_NAME] + chevron;
if (d[MENU_IDX_SUB]) {
switch (typeof d[MENU_IDX_SUB].className) {
if (opt.options instanceof Array) {
var widgetmenu = [];
for (var itr = 0, submenu; submenu = opt.options[itr]; itr++) {
var menu = [itr, submenu[MENU_IDX_NAME]];
if ((typeof opt.selected == 'number' || typeof opt.selected == 'string') && opt.selected == submenu[MENU_IDX_ID]) {
container.innerHTML = submenu[MENU_IDX_NAME] + chevron;
if (submenu[MENU_IDX_SUB]) {
switch (typeof submenu[MENU_IDX_SUB].className) {
case 'string':
$.data(container, 'options-menu-widget-class', d[MENU_IDX_SUB].className);
container.className += ' ' + d[MENU_IDX_SUB].className;
$.data(container, 'options-menu-widget-class', submenu[MENU_IDX_SUB].className);
container.className += ' ' + submenu[MENU_IDX_SUB].className;
break;
case 'function':
$.data(container, 'options-menu-widget-class', d[MENU_IDX_SUB].className(d, true));
container.className += ' ' + d[MENU_IDX_SUB].className(d, true);
break
$.data(container, 'options-menu-widget-class', submenu[MENU_IDX_SUB].className(submenu, true));
container.className += ' ' + submenu[MENU_IDX_SUB].className(submenu, true);
break;
}
}
}
if (d[MENU_IDX_URL]) {
menu.push(function (chevron, container, i, opt) {
switch (typeof i[MENU_IDX_URL]) {
if (submenu[MENU_IDX_URL]) {
menu.push(function (chevron, container, submenu, opt) {
switch (typeof submenu[MENU_IDX_URL]) {
case 'string':
window.location = i[MENU_IDX_URL];
window.location = submenu[MENU_IDX_URL];
break;
case 'function':
if (typeof opt.updateWidgetText == 'undefined' || opt.updateWidgetText) {
container.innerHTML = i[MENU_IDX_NAME] + chevron;
container.innerHTML = submenu[MENU_IDX_NAME] + chevron;
var o = $.data(container, 'options-menu-widget-class');
if (o)
container.className = container.className.replace(new RegExp(' *\\b' + o + '\\b'), '');
if (i[MENU_IDX_SUB]) {
switch (typeof i[MENU_IDX_SUB].className) {
if (submenu[MENU_IDX_SUB]) {
switch (typeof submenu[MENU_IDX_SUB].className) {
case 'string':
$.data(container, 'options-menu-widget-class', i[MENU_IDX_SUB].className);
container.className += ' ' + i[MENU_IDX_SUB].className;
$.data(container, 'options-menu-widget-class', submenu[MENU_IDX_SUB].className);
container.className += ' ' + submenu[MENU_IDX_SUB].className;
break;
case 'function':
$.data(container, 'options-menu-widget-class', i[MENU_IDX_SUB].className(i, true));
container.className += ' ' + i[MENU_IDX_SUB].className(i, true);
$.data(container, 'options-menu-widget-class', submenu[MENU_IDX_SUB].className(submenu, true));
container.className += ' ' + submenu[MENU_IDX_SUB].className(submenu, true);
break;
}
}
}
i[MENU_IDX_URL](container, i);
submenu[MENU_IDX_URL](container, submenu);
break;
}
}.bind(null, chevron, Menu.add, d, opt))
}.bind(null, chevron, Menu.add, submenu, opt))
}
else if (!d[MENU_IDX_SUB] || !d[MENU_IDX_SUB].menu)
else if (!submenu[MENU_IDX_SUB] || !submenu[MENU_IDX_SUB].menu)
menu[0] = null;
menu[MENU_IDX_OPT] = {};
if (d[MENU_IDX_SUB]) {
switch (typeof d[MENU_IDX_SUB].className) {
if (submenu[MENU_IDX_SUB]) {
switch (typeof submenu[MENU_IDX_SUB].className) {
case 'string':
menu[MENU_IDX_OPT].className = d[MENU_IDX_SUB].className;
menu[MENU_IDX_OPT].className = submenu[MENU_IDX_SUB].className;
break;
case 'function':
menu[MENU_IDX_OPT].className = d[MENU_IDX_SUB].className.bind(null, d, false);
menu[MENU_IDX_OPT].className = submenu[MENU_IDX_SUB].className.bind(null, submenu, false);
break;
}
switch (typeof d[MENU_IDX_SUB].column) {
switch (typeof submenu[MENU_IDX_SUB].column) {
case 'number':
case 'string':
menu[MENU_IDX_OPT].column = d[MENU_IDX_SUB].column;
menu[MENU_IDX_OPT].column = submenu[MENU_IDX_SUB].column;
break;
case 'function':
menu[MENU_IDX_OPT].column = d[MENU_IDX_SUB].column.bind(null, d);
menu[MENU_IDX_OPT].column = submenu[MENU_IDX_SUB].column.bind(null, submenu);
break;
}
switch (typeof d[MENU_IDX_SUB].tinyIcon) {
switch (typeof submenu[MENU_IDX_SUB].tinyIcon) {
case 'string':
menu[MENU_IDX_OPT].tinyIcon = d[MENU_IDX_SUB].tinyIcon;
menu[MENU_IDX_OPT].tinyIcon = submenu[MENU_IDX_SUB].tinyIcon;
break;
case 'function':
menu[MENU_IDX_OPT].tinyIcon = d[MENU_IDX_SUB].tinyIcon.bind(null, d);
menu[MENU_IDX_OPT].tinyIcon = submenu[MENU_IDX_SUB].tinyIcon.bind(null, submenu);
break;
}
switch (typeof d[MENU_IDX_SUB].fontIcon) {
switch (typeof submenu[MENU_IDX_SUB].fontIcon) {
case 'string':
menu[MENU_IDX_OPT].fontIcon = d[MENU_IDX_SUB].fontIcon;
menu[MENU_IDX_OPT].fontIcon = submenu[MENU_IDX_SUB].fontIcon;
break;
case 'function':
menu[MENU_IDX_OPT].fontIcon = d[MENU_IDX_SUB].fontIcon.bind(null, d);
menu[MENU_IDX_OPT].fontIcon = submenu[MENU_IDX_SUB].fontIcon.bind(null, submenu);
break;
}
if (typeof d[MENU_IDX_SUB].isChecked == 'function')
menu[MENU_IDX_OPT].checkedFunc = d[MENU_IDX_SUB].isChecked.bind(null, d);
if (typeof submenu[MENU_IDX_SUB].isChecked == 'function')
menu[MENU_IDX_OPT].checkedFunc = submenu[MENU_IDX_SUB].isChecked.bind(null, submenu);
if (typeof d[MENU_IDX_SUB].menu == 'object' && d[MENU_IDX_SUB].menu instanceof Array)
Menu.setSubmenu(menu, d[MENU_IDX_SUB].menu); // <-- n.d. !
if (typeof submenu[MENU_IDX_SUB].menu == 'object' && submenu[MENU_IDX_SUB].menu instanceof Array)
Menu.setSubmenu(menu, submenu[MENU_IDX_SUB].menu); // <-- n.d. !
}
c.push(menu);
widgetmenu.push(menu);
}
container.menu = c;
container.menu = widgetmenu;
if (opt.menuOnClick) {
container.onmousedown = $WH.rf;
Menu.add(container, c, { showAtElement: true });
Menu.add(container, widgetmenu, { showAtElement: true });
}
else
Menu.add(container, c);
Menu.add(container, widgetmenu);
}
}
if (opt.target)
if (opt.target)
$(opt.target).append(container);
else
else
return container;
};
$WH.createOptionsMenuWidget.chevron = ' <i class="fa fa-chevron-down fa-color-gray">~</i>';

View File

@@ -44,8 +44,8 @@ var _ = [
alert('Operation failed.');
},
success: function(json) {
if (json != 1)
alert('Operation failed.');
if (json)
alert('Operation failed: ' + json);
else
$WH.de(el.parentNode);
},

View File

@@ -1,7 +1,10 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
<?php $this->brick('header'); ?>
use \Aowow\Lang;
$this->brick('header');
?>
<div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents">
@@ -12,9 +15,9 @@ $this->brick('announcement');
$this->brick('pageTemplate');
?>
<div class="text">
<h1><?=$this->name; ?></h1>
<h1><?=$this->h1; ?></h1>
<?php
$this->brick('article');
$this->brick('markup', ['markup' => $this->article]);
?>
</div>
<div class="pad"></div>
@@ -24,14 +27,14 @@ $this->brick('pageTemplate');
<form id="guide-form" method="post" action="?guide=edit&id=<?=$this->typeId;?>" onsubmit="leavePage(1)">
<table class="responsive-collapse guide-form-main">
<tr class="guide-form-guide-link">
<td colspan="2"><h2 style="margin:0" class="heading-size-2"><a href="?guide=<?=$this->typeId;?>" target="_blank"><?=$this->editorFields('title');?></a></h2></td>
<td colspan="2"><h2 style="margin:0" class="heading-size-2"><a href="?guide=<?=$this->typeId;?>" target="_blank"><?=$this->editTitle;?></a></h2></td>
</tr>
<tr>
<th><label for="title"><dfn title="<?=Lang::guide('editor', 'fullTitleTip');?>"><?=Lang::guide('editor', 'fullTitle');?></dfn></label></th>
<td>
<input required="required" type="text" maxlength="100" name="title" id="title"
value="<?=$this->editorFields('title');?>"
value="<?=$this->editTitle;?>"
placeholder="<?=Lang::guide('editor', 'fullTitleTip');?>"
data-charwarning="title-char-warning">
<small id="title-char-warning" class="char-warning"></small>
@@ -43,7 +46,7 @@ $this->brick('pageTemplate');
<th><label for="name"><dfn title="<?=Lang::guide('editor', 'nameTip');?>"><?=Lang::guide('editor', 'name');?></dfn></label></th>
<td>
<input required="required" type="text" maxlength="100" name="name" id="name"
value="<?=$this->editorFields('name');?>"
value="<?=$this->editName;?>"
placeholder="<?=Lang::guide('editor', 'nameTip');?>"
data-charwarning="name-char-warning">
<small id="name-char-warning" class="char-warning"></small>
@@ -54,13 +57,7 @@ $this->brick('pageTemplate');
<tr>
<th><label for="locale"><?=Lang::main('language');?></label></th>
<td><select name="locale" id="locale" required="required" size="1">
<?php
foreach (Locale::cases() as $l):
if ($l->validate()):
echo ' <option value="'.$l->value.'"'.($this->editorFields('locale', true) == $l->value ? ' selected="selected"' : '').'>'.$l->title()."</option>\n";
endif;
endforeach;
?>
<?=$this->makeOptionsList(Lang::getLocale()::cases(), $this->editLocale, 24, function(&$v, &$k) { $k = $v->value; return $v = $v->validate()?->title(); }); ?>
</select></td>
</tr>
@@ -69,13 +66,7 @@ endforeach;
<th><label for="category"><?=Lang::guide('editor', 'category');?></label></th>
<td>
<select id="category" name="category" required="required"><option></option>
<?php
foreach (Lang::guide('category') as $i => $c):
if ($c):
echo ' <option value="'.$i.'"'.($this->editorFields('category', true) == $i ? ' selected="selected"' : '').'>'.$c."</option>\n";
endif;
endforeach;
?>
<?=$this->makeOptionsList(Lang::guide('category'), $this->editCategory, 24); ?>
</select>
<script>
(function() {
@@ -98,8 +89,8 @@ endforeach;
<tr id="class-guide-specialization-options">
<th><label for="specId"><?=Lang::guide('editor', 'class-spec');?></label></th>
<td>
<input name="specId" id="specId" type="hidden" value="<?=$this->editorFields('specId');?>">
<input name="classId" id="classId" type="hidden" value="<?=$this->editorFields('classId');?>">
<input name="specId" id="specId" type="hidden" value="<?=$this->editSpecId;?>">
<input name="classId" id="classId" type="hidden" value="<?=$this->editClassId;?>">
<script>
setTimeout(function() {
// const PC = WH.Wow.PlayerClass;
@@ -205,7 +196,7 @@ endforeach;
<td colspan="3">
<textarea rows="1" name="description" cols="100" id="description" style="height:69px"
placeholder="<?=Lang::guide('editor', 'descriptionTip');?>"
><?=$this->editorFields('description');?></textarea>
><?=$this->editDescription;?></textarea>
<script>g_enhanceTextarea('#description')</script>
</td>
</tr>
@@ -213,7 +204,6 @@ endforeach;
<td></td>
<td colspan="3"><span id="desc-info"></span></td>
</tr>
<?php
/*
<tr>
@@ -227,13 +217,12 @@ endforeach;
</tr>
*/
?>
<tr>
<th><?=Lang::main('status');?></th>
<td colspan="3"><dfn title="<?=Lang::guide('editor', 'statusTip', $this->editorFields('status'));?>" style="color:<?=Guidelist::STATUS_COLORS[$this->editorFields('status')];?>"><?=Lang::guide('status', $this->editorFields('status'));?></dfn>
<td colspan="3"><dfn title="<?=Lang::guide('editor', 'statusTip', $this->editStatus);?>" style="color:<?=$this->editStatusColor;?>"><?=Lang::guide('status', $this->editStatus);?></dfn>
<?php
if ($this->editorFields('status') == GUIDE_STATUS_DRAFT):
echo '<small>(<a href="?guide='.$this->typeId.'&rev='.$this->editorFields('rev').'" target="_blank" class="q1">'.Lang::guide('editor', 'testGuide')."</a>)</small>\n";
if ($this->isDraft && $this->typeId):
echo ' <small>(<a href="?guide='.$this->typeId.'&rev='.$this->editRev.'" target="_blank" class="q1">'.Lang::guide('editor', 'testGuide')."</a>)</small>\n";
endif;
?>
</td>
@@ -263,7 +252,7 @@ endif;
onchange="updatePreview(false, this)"
rows="8"
cols="40"
style="width:95%"><?=$this->editorFields('text');?></textarea>
style="width:95%"><?=$this->editText;?></textarea>
<script>
g_enhanceTextarea('#editBox', {
markup: true,
@@ -274,6 +263,12 @@ endif;
</script>
</div>
<?php if ($this->error): ?>
<div class="box msg-failure">
<?=$this->error . PHP_EOL;?>
</div>
<?php endif; ?>
<div class="guide-submission">
<div class="guide-submission-options">
<button type="button" class="btn btn-site" data-type="save" onclick="$('.guide-submission').attr('data-type', 'save'); $('#changelog').focus();"><?=Lang::guide('editor', 'save');?></button>
@@ -288,7 +283,7 @@ endif;
</div>
</div>
<img src="<?=Cfg::get('STATIC_URL');?>/images/icons/ajax.gif" style="display:none" class="spinning-circle">
<img src="<?=$this->gStaticUrl;?>/images/icons/ajax.gif" style="display:none" class="spinning-circle">
<span id="save-status"></span>
</form>
</div>