\n";
+
+ if (User::isInGroup(U_GROUP_STAFF) && false)
+ $buff .= '';
+
+ $this->extraHTML = $buff;
+ }
+}
+
+?>
diff --git a/endpoints/guide/edit.php b/endpoints/guide/edit.php
new file mode 100644
index 00000000..b2897167
--- /dev/null
+++ b/endpoints/guide/edit.php
@@ -0,0 +1,214 @@
+ 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));
+ }
+}
+
+?>
diff --git a/endpoints/guide/guide.php b/endpoints/guide/guide.php
new file mode 100644
index 00000000..90de7eb8
--- /dev/null
+++ b/endpoints/guide/guide.php
@@ -0,0 +1,250 @@
+ ['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, <<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
+ );
+ }
+}
+
+?>
diff --git a/endpoints/guide/guide_power.php b/endpoints/guide/guide_power.php
new file mode 100644
index 00000000..99fd4dba
--- /dev/null
+++ b/endpoints/guide/guide_power.php
@@ -0,0 +1,59 @@
+ ['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);
+ }
+}
+
+?>
diff --git a/endpoints/guide/new.php b/endpoints/guide/new.php
new file mode 100644
index 00000000..95de5c4d
--- /dev/null
+++ b/endpoints/guide/new.php
@@ -0,0 +1,66 @@
+ 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();
+ }
+}
+
+?>
diff --git a/endpoints/guide/vote.php b/endpoints/guide/vote.php
new file mode 100644
index 00000000..ca7ee945
--- /dev/null
+++ b/endpoints/guide/vote.php
@@ -0,0 +1,50 @@
+ ['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]);
+ }
+}
+
+?>
diff --git a/endpoints/guides/guides.php b/endpoints/guides/guides.php
new file mode 100644
index 00000000..9c0db0ee
--- /dev/null
+++ b/endpoints/guides/guides.php
@@ -0,0 +1,73 @@
+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();
+ }
+}
+
+?>
diff --git a/endpoints/my-guides/my-guides.php b/endpoints/my-guides/my-guides.php
new file mode 100644
index 00000000..288d87aa
--- /dev/null
+++ b/endpoints/my-guides/my-guides.php
@@ -0,0 +1,52 @@
+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();
+ }
+}
+
+?>
diff --git a/endpoints/user/user.php b/endpoints/user/user.php
index 7f4fee64..b3e8f7b8 100644
--- a/endpoints/user/user.php
+++ b/endpoints/user/user.php
@@ -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(
diff --git a/includes/ajaxHandler/admin.class.php b/includes/ajaxHandler/admin.class.php
index 899c607e..1b2b6f95 100644
--- a/includes/ajaxHandler/admin.class.php
+++ b/includes/ajaxHandler/admin.class.php
@@ -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;
diff --git a/includes/ajaxHandler/edit.class.php b/includes/ajaxHandler/edit.class.php
deleted file mode 100644
index 1275b7c4..00000000
--- a/includes/ajaxHandler/edit.class.php
+++ /dev/null
@@ -1,82 +0,0 @@
- ['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);
- }
-}
-
-?>
diff --git a/includes/ajaxHandler/getdescription.class.php b/includes/ajaxHandler/getdescription.class.php
deleted file mode 100644
index cabf84d9..00000000
--- a/includes/ajaxHandler/getdescription.class.php
+++ /dev/null
@@ -1,37 +0,0 @@
- [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);
- }
-}
-
-?>
diff --git a/includes/ajaxHandler/guide.class.php b/includes/ajaxHandler/guide.class.php
deleted file mode 100644
index eb425f15..00000000
--- a/includes/ajaxHandler/guide.class.php
+++ /dev/null
@@ -1,63 +0,0 @@
- [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]);
- }
-}
-
-?>
diff --git a/includes/components/guidemgr.class.php b/includes/components/guidemgr.class.php
new file mode 100644
index 00000000..3b738404
--- /dev/null
+++ b/includes/components/guidemgr.class.php
@@ -0,0 +1,106 @@
+ '#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
+ );
+ }
+}
+
+?>
diff --git a/includes/components/pagetemplate.class.php b/includes/components/pagetemplate.class.php
index c53c58b5..1923a192 100644
--- a/includes/components/pagetemplate.class.php
+++ b/includes/components/pagetemplate.class.php
@@ -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;
diff --git a/includes/components/response/templateresponse.class.php b/includes/components/response/templateresponse.class.php
index 76fb8672..bef62121 100644
--- a/includes/components/response/templateresponse.class.php
+++ b/includes/components/response/templateresponse.class.php
@@ -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
diff --git a/includes/dbtypes/guide.class.php b/includes/dbtypes/guide.class.php
index 5f262f55..492b47a0 100644
--- a/includes/dbtypes/guide.class.php
+++ b/includes/dbtypes/guide.class.php
@@ -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
diff --git a/includes/defines.php b/includes/defines.php
index a9f440ef..1c423f5e 100644
--- a/includes/defines.php
+++ b/includes/defines.php
@@ -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
diff --git a/includes/libs/qqFileUploader.class.php b/includes/libs/qqFileUploader.class.php
index 27b27ab8..dae92a58 100644
--- a/includes/libs/qqFileUploader.class.php
+++ b/includes/libs/qqFileUploader.class.php
@@ -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)
{
diff --git a/includes/type.class.php b/includes/type.class.php
index 189e7cea..822b31f1 100644
--- a/includes/type.class.php
+++ b/includes/type.class.php
@@ -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
diff --git a/includes/user.class.php b/includes/user.class.php
index dc9fe853..46d11f6d 100644
--- a/includes/user.class.php
+++ b/includes/user.class.php
@@ -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']));
diff --git a/includes/utilities.php b/includes/utilities.php
index 10d84995..b435c06b 100644
--- a/includes/utilities.php
+++ b/includes/utilities.php
@@ -75,8 +75,6 @@ abstract class Util
public static $mapSelectorString = '%s (%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';
diff --git a/localization/locale_dede.php b/localization/locale_dede.php
index c3e0007b..d934a107 100644
--- a/localization/locale_dede.php
+++ b/localization/locale_dede.php
@@ -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 "%2$s"',
- '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.<br /><br />Wenn leer, wird es automatisch generiert.',
+ 'descriptionTip' => 'Beschreibung, die für Suchmaschinen verwendet wird.
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 "Entwurfs"-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 "Entwurfs"-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(
diff --git a/localization/locale_enus.php b/localization/locale_enus.php
index e4a7fe76..bc8d7ff0 100644
--- a/localization/locale_enus.php
+++ b/localization/locale_enus.php
@@ -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 "%2$s"',
- '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.<br><br>If left empty, it will be generated automatically.',
+ 'descriptionTip' => "Description that will be used for search engines.
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 "Draft" status and you are the only one able to see it. Keep editing it as long as you like, and when you feel it'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 "Draft" status and you are the only one able to see it. Keep editing it as long as you like, and when you feel it'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(
diff --git a/localization/locale_eses.php b/localization/locale_eses.php
index 95a75707..1b8ac56b 100644
--- a/localization/locale_eses.php
+++ b/localization/locale_eses.php
@@ -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 "%2$s"',
- '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.<br /><br />Si se deja vacío, se generará automáticamente.',
+ 'descriptionTip' => 'Descripción utilizada para los motores de búsqueda.
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 "borrador" 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 "borrador" 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(
diff --git a/localization/locale_frfr.php b/localization/locale_frfr.php
index 7123dde1..8d94d2c5 100644
--- a/localization/locale_frfr.php
+++ b/localization/locale_frfr.php
@@ -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 "%2$s"',
- '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.<br /><br />S'il est laissé vide, le résumé sera généré automatiquement.',
+ 'descriptionTip' => 'Description qui sera utilisée par les moteurs de recherche.
S'il est laissé vide, le résumé sera généré automatiquement.',
// 'commentEmail' => 'Recevoir les commentaires par courriel',
// 'commentEmailTip' => 'L'auteur doit-il recevoir un courriel chaque fois qu'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 "Brouillon" et vous êtes le seul à pouvoir le lire. Continuez de l'écrire comme vous le voulez, et quand vous sentez qu'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 "Brouillon" et vous êtes le seul à pouvoir le lire. Continuez de l'écrire comme vous le voulez, et quand vous sentez qu'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(
diff --git a/localization/locale_ruru.php b/localization/locale_ruru.php
index 6e0de9a1..7304a86d 100644
--- a/localization/locale_ruru.php
+++ b/localization/locale_ruru.php
@@ -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' => 'История изменений «%2$s»',
- 'clStatusSet' => 'Присвоен статус «%s»',
- 'clCreated' => 'Создано',
+ 'clStatusSet' => 'Присвоен статус «%s»: ',
+ 'clCreated' => 'Создано: ',
'clMinorEdit' => 'Небольшое изменение',
'editor' => array(
'fullTitle' => 'Полный заголовок',
@@ -188,7 +188,7 @@ $lang = array(
'name' => 'Имя',
'nameTip' => 'Укажите краткое и понятное название руководства. Оно будет использоватья в меню и перечнях руководств.',
'description' => 'Описание',
- 'descriptionTip' => 'Описание для поисковых систем.<br><br>Если поле будет оставлено пустым, то сайт сгенерирует описание автоматически.',
+ 'descriptionTip' => 'Описание для поисковых систем.
Если поле будет оставлено пустым, то сайт сгенерирует описание автоматически.',
// 'commentEmail' => 'E-mail уведомления',
// 'commentEmailTip' => 'Должен ли автор руководства получать e-mail оповещения, когда к руководству оставляют комментарий?',
'changelog' => 'История изменений, внесенных этой правкой',
@@ -203,11 +203,11 @@ $lang = array(
'testGuide' => 'Посмотрите, как будет выглядеть руководство',
'images' => 'Images',
'statusTip' => array(
- GUIDE_STATUS_DRAFT => 'Руководство сохранено как "Черновик" — видеть его можете только вы. Правьте руководство так долго, как сочтете нужным, а когда решите, что оно готово — отправьте на одобрение.',
- 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 => 'Руководство сохранено как "Черновик" — видеть его можете только вы. Правьте руководство так долго, как сочтете нужным, а когда решите, что оно готово — отправьте на одобрение.',
+ 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(
diff --git a/localization/locale_zhcn.php b/localization/locale_zhcn.php
index 42e090cb..2ebf767e 100644
--- a/localization/locale_zhcn.php
+++ b/localization/locale_zhcn.php
@@ -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' => '修改日志 "%2$s"',
- 'clStatusSet' => '状态已设置为 %s',
- 'clCreated' => '已创建',
+ 'clStatusSet' => '状态已设置为 %s:',
+ 'clCreated' => '已创建:',
'clMinorEdit' => '小修改',
'editor' => array(
'fullTitle' => '完整标题',
@@ -188,7 +188,7 @@ $lang = array(
'name' => '名称',
'nameTip' => '这应该是一个简单明了的指南名称,用于菜单和指南列表',
'description' => '描述',
- 'descriptionTip' => '描述将用于说明片段<br /><br />如果不填,则自动生成。',
+ 'descriptionTip' => '描述将用于说明片段