Passed
Push — master ( c44554...a6aba4 )
by Francis
01:13
created

libraries/Blogger.php (4 issues)

1
<?php
2
declare(strict_types=1);
3
defined('BASEPATH') OR exit('No direct script access allowed');
4
5
class Blogger {
6
7
  /**
8
   * Code Igniter Instance.
9
   * @var object
10
   */
11
  private $ci;
12
  /**
13
   * Code Igniter DB Forge instance reference for simplicity.
14
   * @var object
15
   */
16
  private $dbforge;
17
  /**
18
   * Current Blog Table Name.
19
   * @var string
20
   */
21
  private $table_name;
22
  /**
23
   * String prefixed with ever blog name.
24
   * @var string
25
   */
26
  const TABLE_PREFIX = "blogger_posts";
27
  /**
28
   * Name of this package for simplicity.
29
   * @var string
30
   */
31
  const PACKAGE = "francis94c/blog";
32
  /**
33
   * Name of the dependent package for markdown.
34
   * @var string
35
   */
36
  const MARKDOWN_PACKAGE = "francis94c/ci-parsedown";
37
  /**
38
   * Blog post create action.
39
   * @var string
40
   */
41
  const CREATE = "create";
42
  /**
43
   * Blog post create and publish action.
44
   * @var string
45
   */
46
  const CREATE_AND_PUBLISH = "createAndPublish";
47
  /**
48
   * Blog post edit action.
49
   * @var string
50
   */
51
  const EDIT = "edit";
52
  /**
53
   * Blog post publish action.
54
   * @var string
55
   */
56
  const PUBLISH = "publish";
57
  /**
58
   * Blog post delete action.
59
   * @var string
60
   */
61
  const DELETE = "delete";
62
  /**
63
   * Blog post abort acction. This is an action taken internally when other
64
   * actions fail.
65
   * @var string
66
   */
67
  const ABORT = "abortAction";
68
  /**
69
   * Blog post no action.
70
   * @var string
71
   */
72
  const NO_ACTION = "no_action";
73
  /**
74
   * Constructor
75
   * @param mixed $params associative array of parameters. See README.md
76
   */
77
  function __construct($params=null) {
78
    $this->ci =& /** @scrutinizer ignore-call */ get_instance();
79
    $this->ci->load->database();
80
    $this->table_name = self::TABLE_PREFIX . (isset($params["name"]) ? "_" . $params["name"] : "");
81
    $this->ci->load->database();
82
    $this->ci->load->splint(self::PACKAGE, "*BlogManager", "bmanager");
83
    $this->ci->load->splint(self::MARKDOWN_PACKAGE, "+Parsedown", null, "parsedown");
84
    $this->ci->bmanager->setBlogName(isset($params["name"]) ? $params["name"] : null);
85
    $this->ci->load->helper("url");
86
  }
87
  /**
88
   * Installs a blog with the given table name and paramters.
89
   *
90
   * @param  string $blogName                Name of blog tabke to install.
91
   *
92
   * @param  string $adminTableName          Name of admi table to restrict post to.
93
   *
94
   * @param  string $adminIdColumnName       Name of the column to add a foreign
95
   *                                         key constarint to the blog table with.
96
   *
97
   * @param  int    $adminIdColumnConstraint The column constarint or limit of
98
   *                                         $adminIdColumnName.
99
   *
100
   * @return bool                            True on Success, False if Not.
101
   */
102
  public function install(string $blogName=null, string $adminTableName=null, string $adminIdColumnName=null, int $adminIdColumnConstraint=null): bool {
103
    $blogName = $blogName === null ? $this->table_name : self::TABLE_PREFIX . "_" . $blogName;
104
    $this->ci->load->dbforge();
105
    $this->ci->dbforge->add_field("id");
106
    $fields = array(
107
      "title" => array(
108
        "type"       => "VARCHAR",
109
        "constraint" => 70,
110
      ),
111
      "content" => array(
112
        "type" => "TEXT"
113
      ),
114
      "date_published" => array(
115
        "type" => "TIMESTAMP",
116
        "null" => true
117
      ),
118
      "published" => array(
119
        "type" => "TINYINT",
120
        "default" => 0
121
      ),
122
      "hits"      => array(
123
        "type"       => "INT",
124
        "constraint" => 7,
125
        "default"    => 0
126
      ),
127
      "slug"      => array(
128
        "type"       => "VARCHAR",
129
        "constraint" => 80,
130
        "unique"     => true
131
      )
132
    );
133
    $this->ci->dbforge->add_field($fields);
134
    $constrain = $adminTableName !== null && $adminIdColumnName !== null &&
135
    $adminIdColumnConstraint !== null;
136
    if ($constrain) {
137
      $this->ci->dbforge->add_field(
138
        "poster_id INT($adminIdColumnConstraint), FOREIGN KEY (poster_id) REFERENCES $adminTableName($adminIdColumnName)");
139
    }
140
    $this->ci->dbforge->add_field("date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP");
141
    $attributes = array('ENGINE' => 'InnoDB');
142
    if (!$this->ci->dbforge->create_table($blogName, true, $attributes)) return false;
143
    return true;
144
  }
145
  /**
146
   * Sets the name of the current blog table.
147
   *
148
   * @param string $name name of a blog table.
149
   *
150
   * @deprecated
151
   */
152
  public function setName(string $name): void {
153
    $this->table_name = self::TABLE_PREFIX . "_" . $name;
154
    $this->ci->bmanager->setBlogName($name != "" ? $name : null);
155
  }
156
  /**
157
   * Same as the deprecated setName. Sets the name of the current blog table.
158
   *
159
   * @param string $blog [description]
160
   */
161
  public function setBlog(string $blog): void {
162
    $this->table_name = self::TABLE_PREFIX . "_" . $blog;
163
    $this->ci->bmanager->setBlogName($blog != "" ? $blog : null);
164
  }
165
  /**
166
   * Gets the name of the blog.
167
   * @return string The name of the blog.
168
   */
169
  public function getName(): string {
170
    return $this->table_name;
171
  }
172
  /**
173
   * Loads/echoes the client side scripts needed for the blog to render it's
174
   * post editor and other views.
175
   *
176
   * @param  bool $w3css If true, additionally loads the W3.CSS file for additional
177
   *                     styling. Defaults internally on Blogmanager to true
178
   */
179
  private function loadScripts(bool $w3css): void {
180
    $this->ci->load->splint(self::PACKAGE, "-header_scripts", array(
181
      "w3css" => $w3css
182
    ));
183
  }
184
  /**
185
   * Returns the W3.CSS client side script loading tag.
186
   * @return string W3.CSS link tag.
187
   */
188
  public function w3css(): string {
189
    return "<link rel=\"stylesheet\" href=\"https://www.w3schools.com/w3css/4/w3.css\">";
190
  }
191
  /**
192
   * Returns the Fonts Awesome CSS link loading tag.
193
   * @return string Fonts Awesome CSS link loading tag.
194
   */
195
  public function fontsAwesome(): string {
196
    return "<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.3.1/css/all.css\"/>";
197
  }
198
  /**
199
   * Echoes to the browser a 'SimpleMDE' markdown editor for editing post
200
   * contents, as part of a form.
201
   *
202
   * @param  string  $callback The URI callback that will be passed to the Code
203
   *                           Igniter form_open method when outputing the form.
204
   *                           The call back is where you should read the contents
205
   *                           of the submited form.
206
   *                           The contents of the form should be read or handled
207
   *                           by a call to the 'savePost($posterId)' function.
208
   *                           You don't need to worry about reading it your self.
209
   *
210
   * @param  int     $postId   (Optional) The ID of the post whose content should
211
   *                           be present in the editor when loaded. provide this
212
   *                           parameter when you want to edit a post.
213
   *
214
   * @param  bool    $w3css    If true, echoes the W3.CSS link tag as well.
215
   *
216
   * @return bool              True  if sucessfull without errors, false if not.
217
   */
218
  public function loadEditor(string $callback, int $postId=null, bool $w3css=true): bool {
219
    $this->loadScripts($w3css);
220
    $this->ci->load->helper("form");
221
    $data = array(
222
      "callback" => "Admin/token",
223
      "type"     => $postId === null ? "create" : "edit",
224
      "callback" => $callback
225
    );
226
    if ($postId !== null) {
227
      $data["id"] = $postId;
228
      $post = $this->getPost($postId, false);
229
      $data["title"] = $post["title"];
230
      $data["content"] = $post["content"];
231
    }
232
    $this->ci->load->splint("francis94c/blog", "-post_edit", $data);
233
    return true;
234
  }
235
  /**
236
   * Handles form data from the Editor loaded by a call to 'loadEditor'.
237
   * Traditionally, this \function is to be called ath the controller function
238
   * specified by the callback URI provided to the loadEditor method.
239
   *
240
   * @param  int $posterId ID of the poster. A valid admin ID from the table
241
   *                       specified as a foreign key constraint during the
242
   *                       installation of the selected blog.
243
   *
244
   * @return string        The final action reached in processing the form
245
   *                       inputs. These are public string constants declared in
246
   *                       this file.
247
   */
248
  public function savePost(int $posterId=null): string {
249
    $action = $this->ci->security->xss_clean($this->ci->input->post("action"));
250
    switch ($action) {
251
      case "save":
252
        return $this->handleSavePost($posterId);
253
      case "publish":
254
        return $this->handlePublishPost($posterId);
255
      case "createAndPublish":
256
        if ($this->ci->bmanager->createAndPublishPost($this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId) !== false) return self::CREATE_AND_PUBLISH;
257
        return self::ABORT;
258
      case "delete":
259
        if ($this->ci->bmanager->deletePost($this->ci->security->xss_clean($this->ci->input->post("id")))) return self::DELETE;
260
        return self::ABORT;
261
      default:
262
        return self::NO_ACTION;
263
    }
264
  }
265
  /**
266
   * [handleSavePost handles save pot actions; edit & create]
267
   *
268
   * @param  int     $posterId  Poster ID or Admin ID.
269
   *
270
   * @return string             Action taken during the pocess; Blogger::CREATE Or Blogger::EDIT
271
   */
272
  private function handleSavePost(int $posterId=null): string {
273
    $id = $this->ci->security->xss_clean($this->ci->input->post("id"));
274
    if ($id != "") {
275
      if (!$this->ci->bmanager->savePost($this->ci->input->post("id"), $this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId)) return self::ABORT;
276
      return self::EDIT;
277
    } else {
278
      if ($this->ci->bmanager->createPost($this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId) == 0) return self::ABORT;
279
      return self::CREATE;
280
    }
281
  }
282
  /**
283
   * [handlePublishPost handles the publishing of a post using inputs from the
284
   *  submited form.]
285
   *
286
   * @param  int    $posterId ID of the publishing Admin.
287
288
   * @return string           Action reached while processing form inputs.
289
   */
290
  private function handlePublishPost(int $posterId=null): string {
291
    $id = $this->ci->security->xss_clean($this->ci->input->post("id"));
292
    if ($id == "") return self::ABORT;
293
    if (!$this->ci->bmanager->savePost($id, $this->ci->security->xss_clean($this->ci->input->post("title")), $this->ci->security->xss_clean($this->ci->input->post("editor")), $posterId)) return self::ABORT;
294
    if (!$this->ci->bmanager->publishPost($id, true)) return self::ABORT;
295
    return self::PUBLISH;
296
  }
297
  /**
298
   * getPosts get posts from the database by the given $page starting from the
299
   * value of 1 and returns $limit number of rows.
300
   *
301
   * @param  int     $page   Page number starting from 1.
302
   *
303
   * @param  int     $limit  Number of posts to return.
304
   *
305
   * @param  bool    $filter if true, returns only published posts, if false
306
   *                         return all posts. false by default.
307
   *
308
   * @param  bool    $hits   If truem orders the returned posts by number of hits.
309
   *
310
   * @return array           Array of posts for a given page.
311
   */
312
  public function getPosts(int $page, int $limit, bool $filter=false, bool $hits=false): array {
313
    return $this->ci->bmanager->getPosts($page, $limit, $filter, $hits);
314
  }
315
  /**
316
   * [renderPosts render post items by page number, max page count, and specified
317
   * view files.]
318
   * @param  string  $view       Custom view per post summary to use, from
319
   *                             end-user application context.
320
   *
321
   * @param  string  $callback   Callback URI that should render a full post for
322
   *                             each of the post items. This callback URI will
323
   *                             have the ID of each respective post, appended to
324
   *                             the end of it for each of the post itesm.
325
   *
326
   * @param  string  $empty_view View to load if blog has no posts, form end-user context.
327
   *
328
   * @param  int     $page       Page Number of pot list.
329
   *
330
   * @param  int     $limit      Max post summary per page.
331
   *
332
   * @param  boolean $filter     If true, ommits un-published posts.
333
   *
334
   * @param  boolean $hits       If true, orders returned posts by hits.
335
   *
336
   * @return bool                True if successful, false if not.
337
   */
338
  public function renderPostItems(string $view=null, string $callback=null, string $empty_view=null, int $page=1, int $limit=5, bool $filter=false, bool $hits=false, bool $slug=true): bool {
339
    if ($view === null || $empty_view === null) $this->ci->load->bind("francis94c/blog", $blogger);
340
    $posts = $this->getPosts($page, $limit, $filter, $hits);
341
    if (count($posts) == 0) {
342
      if ($empty_view === null) { $blogger->load->view("empty"); } else {
343
        $this->ci->load->view($empty_view);
344
        return true;
345
      }
346
    }
347
    $this->ci->load->helper("text");
348
    foreach ($posts as $post) {
349
      $post["callback"] = $callback !== null ? trim($callback, "/") . "/" . ($slug ? $post["slug"] : $post["id"]) : "";
350
      $post["filter"] = $filter;
351
      $post["content"] = $this->ci->parsedown->text(ellipsize($post["content"], 300));
352
      if ($view === null) {$blogger->load->view("post_list_item", $post); } else {
353
        $this->ci->load->view($view, $post);
354
      }
355
    }
356
    return true;
357
  }
358
  /**
359
   * [getRecentPosts gets most recent post, limits number returned with $limit.]
360
   *
361
   * @param  int  $limit  Number of Posts.
362
   *
363
   * @param  bool $filter If true, returns only published posts.
364
   *
365
   * @return array        Array of posts ordered by ID (Most recent first).
366
   */
367
  public function getRecentPosts(int $limit=5, bool $filter=false): array {
368
    return $this->ci->bmanager->getRecentPosts($limit, $filter);
369
  }
370
  /**
371
   * [renderPost renders a single blog post. uses it's default view if not
372
   * provided a $view]
373
   *
374
   * @param  array|int|string $post A post array, ID or Slug.
375
   *
376
   * @param  string           $view An alternate view to use for the body of the
377
   *                                post.
378
   *
379
   * @return bool             True if successful, false if not.
380
   */
381
  public function renderPost($post, string $view=null): bool {
382
    if (!is_array($post)) $post = $this->ci->bmanager->getPost($post);
383
    if (!$post) return false;
384
    $post["content"] = $this->ci->parsedown->text($post["content"]);
385
    if ($view === null) {
386
      $this->ci->load->splint("francis94c/blog", "-post_item", $post);
387
    } else {
388
      $this->ci->load->view($view, $post);
389
    }
390
    return true;
391
  }
392
  /**
393
   * [metaOg A utility \function that generates Open Graph HTML tags from a post
394
   * array. This \function should be hit from the controller method that loads a
395
   * post for correct results as there's a call to the 'current_url' of Code
396
   * Igniter's url helper here.]
397
   *
398
   * @param  array   $post Post array.
399
   *
400
   * @return string        Open Graph HTML Meta Tags.
401
   */
402
  public function metaOg(array $post): string {
403
    $data = array();
404
    $data["title"] = $post["title"];
405
    $data["description"] = substr($post["content"], 0, 154);
406
    if (isset($post["share_image"])) $data["image_link"] = $post["share_image"];
407
    $data["url"] = current_url();
408
    // Return Meta OG View String.
409
    return $this->ci->load->splint(self::PACKAGE, "-meta_og", $data, true);
410
  }
411
  /**
412
   * [getPostsCount gets total number of posts in the current blog]
413
   *
414
   * TODO: Get for a given blog.
415
   *
416
   * @param  bool $filter If true, counts only published posts. True by default.
417
   *
418
   * @return int          Posts count.
419
   */
420
  public function getPostsCount(bool $filter=true): int {
421
    return $this->ci->bmanager->getPostsCount($filter);
422
  }
423
  /**
424
   * [getPost gets a post array.]
425
   *
426
   * @param  int    $postId ID of the post to retrieve.
427
   *
428
   * @param  bool   $hit    Increment the post's hit count.
429
   *
430
   * @return array          An associative array of a single post.
431
   */
432
  public function getPost($postId, $hit=true) {
433
    return $this->ci->bmanager->getPost($postId, $hit);
434
  }
435
  /**
436
   * [getHits gets the number of hits for a particular post.]
437
   *
438
   * @param  int    $postId Post ID.
439
   *
440
   * @return int            Number of hits (Times viewed or read).
441
   */
442
  public function getHits(int $postId): int {
443
    return $this->ci->bmanager->getHits($postId);
444
  }
445
  /**
446
   * [publishPost Publish or Un Publish a Post.]
447
   * @param  int    $postId  Post ID
448
   * @param  bool   $publish Publish Or Un-Publish
449
   * @return bool            True if operation was successful, False if not.
450
   */
451
  public function publishPost(int $postId, bool $publish): bool {
452
    return $this->ci->bmanager->publishPost($postId, $publish);
453
  }
454
  /**
455
   * [deletePost description]
456
   * @param  [type] $postId [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
457
   * @return [type]         [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
458
   */
459
  public function deletePost($postId) {
460
    return $this->ci->bmanager->deletePost($postId);
461
  }
462
  /**
463
   * [searchPosts description]
464
   * @param  [type]  $words  [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
465
   * @param  [type]  $page   [description]
466
   * @param  integer $limit  [description]
467
   * @param  boolean $filter [description]
468
   * @return [type]          [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
469
   */
470
  public function searchPosts($words, $page, $limit=0, $filter=false) {
471
    return $this->ci->bmanager->searchPosts($words, $page, $limit, $filter);
472
  }
473
}
474