Skip to content

Commit

Permalink
Split wiki functionality between Manager and BaseObject
Browse files Browse the repository at this point in the history
  • Loading branch information
Spine authored and itismadness committed Oct 4, 2021
1 parent 43e9113 commit 6e5628a
Show file tree
Hide file tree
Showing 22 changed files with 589 additions and 594 deletions.
31 changes: 28 additions & 3 deletions app/BaseObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ abstract class BaseObject extends Base {

/* used for handling updates */
protected $updateField = [];
protected $updateFieldPassThru = [];
protected $updateFieldRaw = [];

public function __construct(int $id) {
parent::__construct();
Expand All @@ -21,21 +23,44 @@ public function id(): int {
return $this->id;
}

public function dirty(): bool {
return !empty($this->updateField)
|| !empty($this->updateFieldPassThru)
|| !empty($this->updateFieldRaw);
}

public function setUpdate(string $field, $value) {
$this->updateField[$field] = $value;
return $this;
}

public function setUpdatePassThru(string $field, $value) {
$this->updateFieldPassThru[$field] = $value;
return $this;
}

public function setUpdateRaw(string $field) {
$this->updateFieldRaw[] = $field;
return $this;
}

public function field(string $field) {
return $this->updateField[$field] ?? null;
}

public function modify(): bool {
if (!$this->updateField) {
if (!$this->dirty()) {
return false;
}
$set = implode(', ', array_map(function ($f) { return "$f = ?"; }, array_keys($this->updateField)));
$args = array_values($this->updateField);
$set = implode(', ', array_merge(
array_map(function ($f) { return "$f = ?"; }, array_keys($this->updateField)),
array_keys($this->updateFieldPassThru),
$this->updateFieldRaw
));
$args = array_merge(
array_values($this->updateField),
array_values($this->updateFieldPassThru)
);
$args[] = $this->id;
$this->db->prepared_query(
"UPDATE " . $this->tableName() . " SET
Expand Down
277 changes: 39 additions & 238 deletions app/Manager/Wiki.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,44 @@
namespace Gazelle\Manager;

class Wiki extends \Gazelle\Base {
protected $aliases;
protected const CACHE_KEY = 'wiki_article_v3_%d';

/**
* Find a wiki article based on its id.
*
* @return \Gazelle\Wiki|null id of article if it exists
*/
public function findById(int $id): ?\Gazelle\Wiki {
$id = $this->db->scalar("
SELECT ID FROM wiki_articles WHERE ID = ?
", $id
);
return $id ? new \Gazelle\Wiki($id) : null;
}

/**
* Find a wiki article based on its title.
*
* @param string Title
* @return int|null id of article if it exists
* @return \Gazelle\Wiki|null id of article if it exists
*/
public function findByTitle(string $title): ?int {
return $this->db->scalar("
SELECT ID
FROM wiki_articles
WHERE Title = ?
public function findByTitle(string $title): ?\Gazelle\Wiki {
$id = $this->db->scalar("
SELECT ID FROM wiki_articles WHERE Title = ?
", trim($title)
);
return $id ? new \Gazelle\Wiki($id) : null;
}

/**
* Find a wiki article based on an alias
*
* @return \Gazelle\Wiki|null id of article if it exists
*/
public function findByAlias(string $alias): ?\Gazelle\Wiki {
$id = $this->db->scalar("
SELECT ArticleID FROM wiki_aliases WHERE Alias = ?
", $alias
);
return $id ? new \Gazelle\Wiki($id) : null;
}

/**
Expand All @@ -40,68 +62,10 @@ public function create(string $title, string $body, int $minRead, int $minEdit,
VALUES (?, ?, ?, ?, ?)
", $title, trim($body), $minRead, $minEdit, $userId
);
$articleId = $this->db->inserted_id();
$alias = $this->normalizeAlias($title);
if ($alias && !$this->alias($alias)) {
$this->addAlias($articleId, $alias, $userId);
}
$this->db->commit();
$this->flushArticle($articleId);
return $articleId;
}

/**
* Modifiy a wiki article
*
* @param int article id
* @param string title
* @param string body
* @param int minimum class to read
* @param int minimum class to modify
* @param int author id
* @return int article id
*/
public function modify(int $articleId, string $title, string $body, int $minRead, int $minEdit, int $userId) {
$this->db->begin_transaction();
$this->db->prepared_query("
INSERT INTO wiki_revisions
(ID, Revision, Title, Body, Author, Date)
SELECT ID, Revision, Title, Body, Author, Date
FROM wiki_articles
WHERE ID = ?
ORDER BY Revision DESC
LIMIT 1
", $articleId
);
$this->db->prepared_query("
UPDATE wiki_articles SET
Date = now(),
Title = ?,
Body = ?,
MinClassRead = ?,
MinClassEdit = ?,
Author = ?,
Revision = 1 + (SELECT max(Revision) FROM wiki_articles WHERE ID = ?)
WHERE ID = ?
", trim($title), trim($body), $minRead, $minEdit, $userId, $articleId,
$articleId
);
$article = new \Gazelle\Wiki($this->db->inserted_id());
$article->addAlias($title, $userId);
$this->db->commit();
return $this->flushArticle($articleId);
}

/**
* Remove an article
*
* param int the article to remove
*/
public function remove(int $articleId) {
$this->db->begin_transaction();
$this->db->prepared_query("DELETE FROM wiki_articles WHERE ID = ?", $articleId);
$this->db->prepared_query("DELETE FROM wiki_aliases WHERE ArticleID = ?", $articleId);
$this->db->prepared_query("DELETE FROM wiki_revisions WHERE ID = ?", $articleId);
$this->db->commit();
return $this->flushArticle($articleId);
return $article;
}

/**
Expand All @@ -114,7 +78,10 @@ public function remove(int $articleId) {
* @return array [read class, edit class, error]
* The error entry will be non-null in case of an error and read and edit will be null.
*/
public function configureAccess(int $isAdmin, int $class, int $minRead, int $minEdit) {
public function configureAccess(\Gazelle\User $user, int $minRead, int $minEdit) {
$isAdmin = $user->permitted('admin_manage_wiki');
$class = $user->effectiveClass();

if (!$isAdmin) {
return [100, 100, null];
}
Expand All @@ -136,99 +103,6 @@ public function configureAccess(int $isAdmin, int $class, int $minRead, int $min
return [$minRead, $minEdit, null];
}

/**
* Normalize an alias
* @param string $str
* @return string
*/
public function normalizeAlias(string $alias): string {
return trim(substr(preg_replace('/[^a-z0-9]/', '', strtolower(htmlentities(trim($alias)))), 0, 50));
}

/**
* Get all aliases in an associative array of Alias => ArticleID
* @return array
*/
public function aliasList(): array {
$this->aliases = $this->cache->get_value('wiki_aliases');
if (!$this->aliases) {
$qid = $this->db->get_query_id();
$this->db->prepared_query("
SELECT Alias, ArticleID FROM wiki_aliases
");
$this->aliases = [];
while ([$alias, $articleId] = $this->db->next_row()) {
$this->aliases[$alias] = (int)$articleId;
}
$this->db->set_query_id($qid);
$this->cache->cache_value('wiki_aliases', $this->aliases, 3600 * 24 * 14); // 2 weeks
}
return $this->aliases;
}

/**
* Flush the alias cache. Call this whenever you touch the wiki_aliases table.
*/
public function flush() {
$this->cache->delete_value('wiki_aliases');
return $this;
}

/**
* Get the article an alias points to
*
* @param string $alias
* @return int|null
*/
public function alias(string $alias): ?int {
$aliases = $this->aliasList();
return $aliases[$this->normalizeAlias($alias)] ?? null;
}

/**
* Get an article
* @param int $articleId
* @throws Gazelle\Exception\ResourceNotFoundException
* @return array
*/
public function article(int $articleId): array {
$key = sprintf(self::CACHE_KEY, $articleId);
$contents = $this->cache->get_value($key);
if (!$contents) {
$qid = $this->db->get_query_id();
$contents = $this->db->row("
SELECT w.Revision,
w.Title,
w.Body,
w.MinClassRead,
w.MinClassEdit,
w.Date,
w.Author,
GROUP_CONCAT(a.Alias) as aliases,
GROUP_CONCAT(a.UserID) as users
FROM wiki_articles AS w
LEFT JOIN wiki_aliases AS a ON (w.ID = a.ArticleID)
LEFT JOIN users_main AS u ON (u.ID = w.Author)
WHERE w.ID = ?
GROUP BY w.ID
", $articleId
);
if (empty($contents)) {
throw new \Gazelle\Exception\ResourceNotFoundException($articleId);
}
$this->db->set_query_id($qid);
$this->cache->cache_value($key, $contents, 3600 * 24 * 14); // 2 weeks
}
return $contents;
}

/**
* Get the list of wiki articles
*
* @param int class of viewer, to return only the articles they are allowed to read
* @param string first letter of articles to see, or 'All' for all
* @return array [id, title, date, authorId]
*/
public function articles(int $class, $letter): array {
$sql = "
SELECT ID,
Expand All @@ -245,79 +119,6 @@ public function articles(int $class, $letter): array {
}
$sql .= " ORDER BY Title";
$this->db->prepared_query($sql, ...$args);
return $this->db->to_array(false, MYSQLI_ASSOC);
}

public function revisions(int $articleId) {
$this->db->prepared_query("
SELECT Revision,
Title,
Author,
Date
FROM wiki_revisions
WHERE ID = ?
ORDER BY Revision DESC
", $articleId
);
return $this->db;
}

/**
* Flush an article's cache. Call this whenever you edited a wiki article or its aliases.
* @param int $articleId
*/
public function flushArticle(int $articleId) {
$this->cache->delete_value(sprintf(self::CACHE_KEY, $articleId));
return $this->flush();
}

/**
* Can the viewer edit this article?
* NB: currently there is no equivalent readAllowed() method.
* Further restructuring is necessary before that makes sense.
*
* @param int article id
* @param int viewer class
* @return bool viewer can edit
*/
public function editAllowed(int $articleId, int $class): bool {
return $class >= $this->db->scalar("
SELECT MinClassEdit FROM wiki_articles WHERE ID = ?
", $articleId
);
}

/**
* Add an alias to an existing article
*
* @param int article id
* @param string alias
* @param int user id of the person adding the alias
*/
public function addAlias(int $articleId, string $alias, int $userId) {
$this->db->prepared_query("
INSERT INTO wiki_aliases
(ArticleID, Alias, UserID)
VALUES (?, ?, ?)
", $articleId, $this->normalizeAlias($alias), $userId
);
return $this->flushArticle($articleId);
}

/**
* Remove an alias of an article.
*
* @param string the alias to remove
*/
public function removeAlias(string $alias) {
$articleId = $this->alias($alias);
if (!$articleId) {
return $this;
}
$this->db->prepared_query("
DELETE FROM wiki_aliases WHERE Alias = ?
", $this->normalizeAlias(trim($alias))
);
return $this->flushArticle($articleId);
return $this->db->to_array(false, MYSQLI_ASSOC, false);
}
}
Loading

0 comments on commit 6e5628a

Please sign in to comment.