hfkfhkfhgjkuhgfkjfghkj
This commit is contained in:
102
app/plugins/blog/controllers/admin/blog.php
Executable file
102
app/plugins/blog/controllers/admin/blog.php
Executable file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/controllers/admin/blog.php
|
||||
*
|
||||
* This is the Blog admin controller.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Controllers\Admin;
|
||||
|
||||
use TheTempusProject\Bedrock\Functions\Check;
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
use TheTempusProject\Houdini\Classes\Issues;
|
||||
use TheTempusProject\Houdini\Classes\Views;
|
||||
use TheTempusProject\Houdini\Classes\Navigation;
|
||||
use TheTempusProject\Houdini\Classes\Components;
|
||||
use TheTempusProject\Classes\AdminController;
|
||||
use TheTempusProject\Classes\Forms;
|
||||
use TheTempusProject\Models\Posts;
|
||||
|
||||
class Blog extends AdminController {
|
||||
public static $posts;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
self::$posts = new Posts;
|
||||
self::$title = 'Admin - Blog';
|
||||
}
|
||||
|
||||
public function index( $data = null ) {
|
||||
Views::view( 'blog.admin.list', self::$posts->listPosts( ['includeDrafts' => true] ) );
|
||||
}
|
||||
|
||||
public function create( $data = null ) {
|
||||
if ( !Input::exists( 'submit' ) ) {
|
||||
return Views::view( 'blog.admin.create' );
|
||||
}
|
||||
if ( !Forms::check( 'newBlogPost' ) ) {
|
||||
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
$result = self::$posts->newPost( Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'slug' ), Input::post( 'submit' ) );
|
||||
if ( $result ) {
|
||||
Issues::add( 'success', 'Your post has been created.' );
|
||||
return $this->index();
|
||||
} else {
|
||||
Issues::add( 'error', [ 'There was an unknown error submitting your data.' => Check::userErrors() ] );
|
||||
return $this->index();
|
||||
}
|
||||
}
|
||||
|
||||
public function edit( $data = null ) {
|
||||
if ( !Input::exists( 'submit' ) ) {
|
||||
return Views::view( 'blog.admin.edit', self::$posts->findById( $data ) );
|
||||
}
|
||||
if ( Input::post( 'submit' ) == 'preview' ) {
|
||||
return Views::view( 'blog.admin.preview', self::$posts->preview( Input::post( 'title' ), Input::post( 'blogPost' ) ) );
|
||||
}
|
||||
if ( !Forms::check( 'editBlogPost' ) ) {
|
||||
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
|
||||
return $this->index();
|
||||
}
|
||||
if ( self::$posts->updatePost( $data, Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'slug' ), Input::post( 'submit' ) ) === true ) {
|
||||
Issues::add( 'success', 'Post Updated.' );
|
||||
return $this->index();
|
||||
}
|
||||
Issues::add( 'error', 'There was an error with your request.' );
|
||||
$this->index();
|
||||
}
|
||||
|
||||
public function view( $data = null ) {
|
||||
$blogData = self::$posts->findById( $data );
|
||||
if ( $blogData !== false ) {
|
||||
return Views::view( 'blog.admin.view', $blogData );
|
||||
}
|
||||
Issues::add( 'error', 'Post not found.' );
|
||||
$this->index();
|
||||
}
|
||||
|
||||
public function delete( $data = null ) {
|
||||
if ( $data == null ) {
|
||||
if ( Input::exists( 'B_' ) ) {
|
||||
$data = Input::post( 'B_' );
|
||||
}
|
||||
}
|
||||
if ( !self::$posts->delete( (array) $data ) ) {
|
||||
Issues::add( 'error', 'There was an error with your request.' );
|
||||
} else {
|
||||
Issues::add( 'success', 'Post has been deleted' );
|
||||
}
|
||||
$this->index();
|
||||
}
|
||||
|
||||
public function preview( $data = null ) {
|
||||
Views::view( 'blog.admin.preview', self::$posts->preview( Input::post( 'title' ), Input::post( 'blogPost' ) ) );
|
||||
}
|
||||
}
|
186
app/plugins/blog/controllers/blog.php
Executable file
186
app/plugins/blog/controllers/blog.php
Executable file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/controllers/blog.php
|
||||
*
|
||||
* This is the blog controller.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Controllers;
|
||||
|
||||
use TheTempusProject\Bedrock\Functions\Check;
|
||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
use TheTempusProject\Houdini\Classes\Components;
|
||||
use TheTempusProject\Houdini\Classes\Issues;
|
||||
use TheTempusProject\Houdini\Classes\Views;
|
||||
use TheTempusProject\Houdini\Classes\Template;
|
||||
use TheTempusProject\Classes\Controller;
|
||||
use TheTempusProject\Classes\Forms;
|
||||
use TheTempusProject\Hermes\Functions\Redirect;
|
||||
use TheTempusProject\Bedrock\Functions\Session;
|
||||
use TheTempusProject\Plugins\Blog as BlogPlugin;
|
||||
use TheTempusProject\TheTempusProject as App;
|
||||
use TheTempusProject\Plugins\Comments;
|
||||
use TheTempusProject\Models\Comments as CommentsModel;
|
||||
use TheTempusProject\Models\Posts as PostsModel;
|
||||
|
||||
class Blog extends Controller {
|
||||
protected static $blog;
|
||||
protected static $posts;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
Template::setTemplate( 'blog' );
|
||||
self::$posts = new PostsModel;
|
||||
}
|
||||
|
||||
public function index() {
|
||||
self::$title = '{SITENAME} Blog';
|
||||
self::$pageDescription = 'The {SITENAME} blog is where you can find various posts containing information ranging from current projects and general information to editorial and opinion based content.';
|
||||
Views::view( 'blog.list', self::$posts->listPosts() );
|
||||
}
|
||||
|
||||
public function rss() {
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
self::$title = '{SITENAME} RSS Feed';
|
||||
self::$pageDescription = '{SITENAME} blog RSS feed.';
|
||||
Template::setTemplate( 'rss' );
|
||||
header( 'Content-Type: text/xml' );
|
||||
return Views::view( 'blog.rss', self::$posts->listPosts( ['stripHtml' => true] ) );
|
||||
}
|
||||
|
||||
public function comments( $sub = null, $data = null ) {
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
|
||||
if ( empty( $sub ) || empty( $data ) ) {
|
||||
Issues::add( 'error', 'There was an issue with your request. Please check the url and try again.' );
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
|
||||
$plugin = new Comments;
|
||||
if ( ! $plugin->checkEnabled() ) {
|
||||
Issues::add( 'error', 'Comments are disabled.' );
|
||||
return $this->index();
|
||||
}
|
||||
$comments = new CommentsModel;
|
||||
} else {
|
||||
Debug::info( 'error', 'Comments plugin missing.' );
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
switch ( $sub ) {
|
||||
case 'post':
|
||||
$content = self::$posts->findById( (int) $data );
|
||||
if ( empty( $content ) ) {
|
||||
Issues::add( 'error', 'Unknown Content.' );
|
||||
return $this->index();
|
||||
}
|
||||
return $plugin->formPost( self::$posts->tableName, $content, 'blog/post/' );
|
||||
case 'edit':
|
||||
$content = $comments->findById( $data );
|
||||
if ( empty( $content ) ) {
|
||||
Issues::add( 'error', 'Unknown Comment.' );
|
||||
return $this->index();
|
||||
}
|
||||
return $plugin->formEdit( self::$posts->tableName, $content, 'blog/post/' );
|
||||
case 'delete':
|
||||
$content = $comments->findById( $data );
|
||||
if ( empty( $content ) ) {
|
||||
Issues::add( 'error', 'Unknown Comment.' );
|
||||
return $this->index();
|
||||
}
|
||||
return $plugin->formDelete( self::$posts->tableName, $content, 'blog/post/' );
|
||||
}
|
||||
}
|
||||
|
||||
public function post( $id = null ) {
|
||||
if ( empty( $id ) ) {
|
||||
return $this->index();
|
||||
}
|
||||
$post = self::$posts->findById( $id );
|
||||
if ( empty( $post ) ) {
|
||||
$post = self::$posts->findBySlug( $id );
|
||||
if ( empty( $post ) ) {
|
||||
return $this->index();
|
||||
}
|
||||
}
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
self::$title = 'Blog Post';
|
||||
|
||||
if ( Input::exists( 'contentId' ) ) {
|
||||
$this->comments( 'post', Input::post( 'contentId' ) );
|
||||
}
|
||||
|
||||
Components::set( 'CONTENT_ID', $id );
|
||||
Components::set( 'COMMENT_TYPE', self::$posts->tableName );
|
||||
Components::set( 'NEWCOMMENT', '' );
|
||||
Components::set( 'count', '0' );
|
||||
Components::set( 'COMMENTS', '' );
|
||||
|
||||
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
|
||||
$plugin = new Comments;
|
||||
if ( $plugin->checkEnabled() ) {
|
||||
$comments = new CommentsModel;
|
||||
if ( App::$isLoggedIn ) {
|
||||
Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
|
||||
} else {
|
||||
Components::set( 'NEWCOMMENT', '' );
|
||||
}
|
||||
Components::set( 'count', $comments->count( self::$posts->tableName, $post->ID ) );
|
||||
Components::set( 'COMMENTS', Views::simpleView( 'comments.list', $comments->display( 10, self::$posts->tableName, $post->ID ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
self::$title .= ' - ' . $post->title;
|
||||
self::$pageDescription = strip_tags( $post->contentSummaryNoLink );
|
||||
Views::view( 'blog.post', $post );
|
||||
}
|
||||
|
||||
public function author( $data = null ) {
|
||||
if ( empty( $data ) ) {
|
||||
return $this->index();
|
||||
}
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
self::$title = 'Posts by author - {SITENAME}';
|
||||
self::$pageDescription = '{SITENAME} blog posts easily and conveniently sorted by author.';
|
||||
Views::view( 'blog.list', self::$posts->byAuthor( $data ) );
|
||||
}
|
||||
|
||||
public function month( $month = null, $year = 0 ) {
|
||||
if ( empty( $month ) ) {
|
||||
return $this->index();
|
||||
}
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
self::$title = 'Posts By Month - {SITENAME}';
|
||||
self::$pageDescription = '{SITENAME} blog posts easily and conveniently sorted by month.';
|
||||
Views::view( 'blog.list', self::$posts->byMonth( $month, $year ) );
|
||||
}
|
||||
|
||||
public function year( $year = null ) {
|
||||
if ( empty( $year ) ) {
|
||||
return $this->index();
|
||||
}
|
||||
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
|
||||
self::$title = 'Posts by Year - {SITENAME}';
|
||||
self::$pageDescription = '{SITENAME} blog posts easily and conveniently sorted by years.';
|
||||
Views::view( 'blog.list', self::$posts->byYear( $year ) );
|
||||
}
|
||||
|
||||
public function search() {
|
||||
$results = [];
|
||||
if ( Input::exists( 'submit' ) ) {
|
||||
$dbResults = self::$posts->search( Input::post('searchTerm') );
|
||||
if ( ! empty( $dbResults ) ) {
|
||||
$results = $dbResults;
|
||||
}
|
||||
}
|
||||
Components::set( 'searchResults', Views::simpleView( 'blog.list', $results ) );
|
||||
Views::view( 'blog.searchResults' );
|
||||
}
|
||||
}
|
81
app/plugins/blog/forms.php
Executable file
81
app/plugins/blog/forms.php
Executable file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/forms.php
|
||||
*
|
||||
* This houses all of the form checking functions for this plugin.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Plugins\Blog;
|
||||
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
use TheTempusProject\Bedrock\Functions\Check;
|
||||
use TheTempusProject\Classes\Forms;
|
||||
|
||||
class BlogForms extends Forms {
|
||||
/**
|
||||
* Adds these functions to the form list.
|
||||
*/
|
||||
public function __construct() {
|
||||
self::addHandler( 'newBlogPost', __CLASS__, 'newBlogPost' );
|
||||
self::addHandler( 'editBlogPost', __CLASS__, 'editBlogPost' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the new blog post form.
|
||||
*
|
||||
* @return {bool}
|
||||
*/
|
||||
public static function newBlogPost() {
|
||||
if ( !Input::exists( 'title' ) ) {
|
||||
self::addUserError( 'You must specify title' );
|
||||
return false;
|
||||
}
|
||||
if ( !self::dataTitle( Input::post( 'title' ) ) ) {
|
||||
self::addUserError( 'Invalid title' );
|
||||
return false;
|
||||
}
|
||||
if ( !Input::exists( 'blogPost' ) ) {
|
||||
self::addUserError( 'You must specify a post' );
|
||||
return false;
|
||||
}
|
||||
/** You cannot use the token check due to how tinymce reloads the page
|
||||
if (!self::token()) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the edit blog post form.
|
||||
*
|
||||
* @return {bool}
|
||||
*/
|
||||
public static function editBlogPost() {
|
||||
if ( !Input::exists( 'title' ) ) {
|
||||
self::addUserError( 'You must specify title' );
|
||||
return false;
|
||||
}
|
||||
if ( !self::dataTitle( Input::post( 'title' ) ) ) {
|
||||
self::addUserError( 'Invalid title' );
|
||||
return false;
|
||||
}
|
||||
if ( !Input::exists( 'blogPost' ) ) {
|
||||
self::addUserError( 'You must specify a post' );
|
||||
return false;
|
||||
}
|
||||
/** You cannot use the token check due to how tinymce reloads the page
|
||||
if (!self::token()) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
new BlogForms;
|
356
app/plugins/blog/models/posts.php
Executable file
356
app/plugins/blog/models/posts.php
Executable file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/models/blog.php
|
||||
*
|
||||
* This class is used for the manipulation of the blog database table.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Models;
|
||||
|
||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||
use TheTempusProject\Bedrock\Functions\Check;
|
||||
use TheTempusProject\Bedrock\Functions\Sanitize;
|
||||
use TheTempusProject\Classes\DatabaseModel;
|
||||
use TheTempusProject\TheTempusProject as App;
|
||||
use TheTempusProject\Canary\Classes\CustomException;
|
||||
use TheTempusProject\Houdini\Classes\Filters;
|
||||
use TheTempusProject\Plugins\Comments as CommentPlugin;
|
||||
use TheTempusProject\Models\Comments;
|
||||
|
||||
class Posts extends DatabaseModel {
|
||||
public $tableName = 'posts';
|
||||
public $searchFields = [
|
||||
'title',
|
||||
'slug',
|
||||
'content',
|
||||
];
|
||||
public static $comments = false;
|
||||
|
||||
public $databaseMatrix = [
|
||||
[ 'author', 'int', '11' ],
|
||||
[ 'created', 'int', '10' ],
|
||||
[ 'edited', 'int', '10' ],
|
||||
[ 'draft', 'int', '1' ],
|
||||
[ 'title', 'varchar', '86' ],
|
||||
[ 'slug', 'varchar', '64' ],
|
||||
[ 'content', 'text', '' ],
|
||||
];
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
|
||||
$comments = new CommentPlugin;
|
||||
if ( $comments->checkEnabled() ) {
|
||||
self::$comments = new Comments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function newPost( $title, $post, $slug, $draft ) {
|
||||
if ( !Check::dataTitle( $title ) ) {
|
||||
Debug::info( 'modelBlog: illegal title.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
if ( $draft === 'saveDraft' ) {
|
||||
$draft = 1;
|
||||
} else {
|
||||
$draft = 0;
|
||||
}
|
||||
$fields = [
|
||||
'author' => App::$activeUser->ID,
|
||||
'draft' => $draft,
|
||||
'slug' => $slug,
|
||||
'created' => time(),
|
||||
'edited' => time(),
|
||||
'content' => Sanitize::rich( $post ),
|
||||
'title' => $title,
|
||||
];
|
||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
||||
Debug::error( "Blog Post: $data not updated: $fields" );
|
||||
new customException( 'blogCreate' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updatePost( $id, $title, $content, $slug, $draft ) {
|
||||
if ( empty( self::$log ) ) {
|
||||
self::$log = new Log;
|
||||
}
|
||||
if ( !Check::id( $id ) ) {
|
||||
Debug::info( 'modelBlog: illegal ID.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
if ( !Check::dataTitle( $title ) ) {
|
||||
Debug::info( 'modelBlog: illegal title.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
if ( $draft === 'saveDraft' ) {
|
||||
$draft = 1;
|
||||
} else {
|
||||
$draft = 0;
|
||||
}
|
||||
$fields = [
|
||||
'draft' => $draft,
|
||||
'slug' => $slug,
|
||||
'edited' => time(),
|
||||
'content' => Sanitize::rich( $content ),
|
||||
'title' => $title,
|
||||
];
|
||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||
new CustomException( 'blogUpdate' );
|
||||
Debug::error( "Blog Post: $id not updated: $fields" );
|
||||
|
||||
return false;
|
||||
}
|
||||
self::$log->admin( "Updated Blog Post: $id" );
|
||||
return true;
|
||||
}
|
||||
|
||||
public function preview( $title, $content ) {
|
||||
if ( !Check::dataTitle( $title ) ) {
|
||||
Debug::info( 'modelBlog: illegal characters.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
$fields = [
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'authorName' => App::$activeUser->username,
|
||||
'created' => time(),
|
||||
];
|
||||
return (object) $fields;
|
||||
}
|
||||
|
||||
public function filter( $postArray, $params = [] ) {
|
||||
foreach ( $postArray as $instance ) {
|
||||
if ( !is_object( $instance ) ) {
|
||||
$instance = $postArray;
|
||||
$end = true;
|
||||
}
|
||||
$draft = '';
|
||||
$authorName = self::$user->getUsername( $instance->author );
|
||||
|
||||
// Summarize
|
||||
if ( ! empty( $instance->slug ) ) {
|
||||
$identifier = $instance->slug;
|
||||
} else {
|
||||
$identifier = $instance->ID;
|
||||
}
|
||||
|
||||
$cleanPost = Sanitize::contentShort( $instance->content );
|
||||
// By Word
|
||||
$wordsArray = explode( ' ', $cleanPost );
|
||||
$wordSummary = implode( ' ', array_splice( $wordsArray, 0, 100 ) );
|
||||
// By Line
|
||||
$linesArray = explode( "\n", $cleanPost );
|
||||
$lineSummary = implode( "\n", array_splice( $linesArray, 0, 5 ) );
|
||||
|
||||
if ( strlen( $wordSummary ) < strlen( $lineSummary ) ) {
|
||||
$contentSummaryNoLink = $wordSummary;
|
||||
$contentSummary = $wordSummary . '... <a href="{ROOT_URL}blog/post/' . $identifier . '" class="text-decoration-none">Read More</a>';
|
||||
} else {
|
||||
$contentSummaryNoLink = $lineSummary;
|
||||
$contentSummary = $lineSummary . '... <a href="{ROOT_URL}blog/post/' . $identifier . '" class="text-decoration-none">Read More</a>';
|
||||
}
|
||||
|
||||
$instance->contentSummaryNoLink = $contentSummaryNoLink;
|
||||
$instance->contentSummary = $contentSummary;
|
||||
|
||||
if ( isset( $params['stripHtml'] ) && $params['stripHtml'] === true ) {
|
||||
$instance->contentSummary = strip_tags( $instance->content );
|
||||
}
|
||||
if ( $instance->draft != '0' ) {
|
||||
$draft = ' <b>Draft</b>';
|
||||
}
|
||||
$instance->isDraft = $draft;
|
||||
$instance->authorName = \ucfirst( $authorName );
|
||||
if ( self::$comments !== false ) {
|
||||
$instance->commentCount = self::$comments->count( 'blog', $instance->ID );
|
||||
} else {
|
||||
$instance->commentCount = 0;
|
||||
}
|
||||
$instance->content = Filters::applyOne( 'mentions.0', $instance->content, true );
|
||||
$instance->content = Filters::applyOne( 'hashtags.0', $instance->content, true );
|
||||
|
||||
$out[] = $instance;
|
||||
if ( !empty( $end ) ) {
|
||||
$out = $out[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function archive( $includeDraft = false ) {
|
||||
$whereClause = [];
|
||||
$currentTimeUnix = time();
|
||||
$x = 0;
|
||||
$dataOut = [];
|
||||
$month = date( 'F', $currentTimeUnix );
|
||||
$year = date( 'Y', $currentTimeUnix );
|
||||
$previous = date( 'U', strtotime( "$month 1st $year" ) );
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0', 'AND'];
|
||||
}
|
||||
while ( $x <= 5 ) {
|
||||
$where = array_merge( $whereClause, ['created', '<=', $currentTimeUnix, 'AND', 'created', '>=', $previous] );
|
||||
$data = self::$db->get( $this->tableName, $where );
|
||||
$x++;
|
||||
$month = date( 'm', $previous );
|
||||
$montht = date( 'F', $previous );
|
||||
$year = date( 'Y', $previous );
|
||||
if ( !$data ) {
|
||||
$count = 0;
|
||||
} else {
|
||||
$count = $data->count();
|
||||
}
|
||||
$dataOut[] = (object) [
|
||||
'count' => $count,
|
||||
'month' => $month,
|
||||
'year' => $year,
|
||||
'monthText' => $montht,
|
||||
];
|
||||
$currentTimeUnix = $previous;
|
||||
$previous = date( 'U', strtotime( '-1 months', $currentTimeUnix ) );
|
||||
}
|
||||
if ( !$data ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return (object) $dataOut;
|
||||
}
|
||||
|
||||
public function recent( $limit = null, $includeDraft = false ) {
|
||||
$whereClause = [];
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0'];
|
||||
} else {
|
||||
$whereClause = '*';
|
||||
}
|
||||
if ( empty( $limit ) ) {
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
} else {
|
||||
$postData = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
|
||||
}
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return $this->filter( $postData->results() );
|
||||
}
|
||||
|
||||
public function listPosts( $params = [] ) {
|
||||
if ( isset( $params['includeDrafts'] ) && $params['includeDrafts'] === true ) {
|
||||
$whereClause = '*';
|
||||
} else {
|
||||
$whereClause = ['draft', '=', '0'];
|
||||
}
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
if ( isset( $params['stripHtml'] ) && $params['stripHtml'] === true ) {
|
||||
return $this->filter( $postData->results(), ['stripHtml' => true] );
|
||||
}
|
||||
return $this->filter( $postData->results() );
|
||||
}
|
||||
|
||||
public function byYear( $year, $includeDraft = false ) {
|
||||
if ( !Check::id( $year ) ) {
|
||||
Debug::info( 'Invalid Year' );
|
||||
return false;
|
||||
}
|
||||
$whereClause = [];
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0', 'AND'];
|
||||
}
|
||||
$firstDayUnix = date( 'U', strtotime( "first day of $year" ) );
|
||||
$lastDayUnix = date( 'U', strtotime( "last day of $year" ) );
|
||||
$whereClause = array_merge( $whereClause, ['created', '<=', $lastDayUnix, 'AND', 'created', '>=', $firstDayUnix] );
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return $this->filter( $postData->results() );
|
||||
}
|
||||
|
||||
public function byAuthor( $ID, $includeDraft = false ) {
|
||||
if ( !Check::id( $ID ) ) {
|
||||
Debug::info( 'Invalid Author' );
|
||||
return false;
|
||||
}
|
||||
$whereClause = [];
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0', 'AND'];
|
||||
}
|
||||
$whereClause = array_merge( $whereClause, ['author' => $ID] );
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return $this->filter( $postData->results() );
|
||||
}
|
||||
|
||||
public function findBySlug( $slug, $includeDraft = false ) {
|
||||
$whereClause = [];
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0', 'AND'];
|
||||
}
|
||||
|
||||
$whereClause = array_merge( $whereClause, ['slug', '=', $slug] );
|
||||
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
return false;
|
||||
}
|
||||
return $this->filter( $postData->first() );
|
||||
}
|
||||
|
||||
public function byMonth( $month, $year = 0, $includeDraft = false ) {
|
||||
if ( 0 === $year ) {
|
||||
$year = date( 'Y' );
|
||||
}
|
||||
if ( !Check::id( $month ) ) {
|
||||
Debug::info( 'Invalid Month' );
|
||||
return false;
|
||||
}
|
||||
if ( !Check::id( $year ) ) {
|
||||
Debug::info( 'Invalid Year' );
|
||||
return false;
|
||||
}
|
||||
$whereClause = [];
|
||||
if ( $includeDraft !== true ) {
|
||||
$whereClause = ['draft', '=', '0', 'AND'];
|
||||
}
|
||||
$firstDayUnix = date( 'U', strtotime( "$month/01/$year" ) );
|
||||
$month = date( 'F', $firstDayUnix );
|
||||
$lastDayUnix = date( 'U', strtotime( "last day of $month $year" ) );
|
||||
$whereClause = array_merge( $whereClause, ['created', '<=', $lastDayUnix, 'AND', 'created', '>=', $firstDayUnix] );
|
||||
$postData = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( !$postData->count() ) {
|
||||
Debug::info( 'No Blog posts found.' );
|
||||
|
||||
return false;
|
||||
}
|
||||
return $this->filter( $postData->results() );
|
||||
}
|
||||
}
|
49
app/plugins/blog/plugin.php
Executable file
49
app/plugins/blog/plugin.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/plugin.php
|
||||
*
|
||||
* This houses all of the main plugin info and functionality.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Plugins;
|
||||
|
||||
use TheTempusProject\Classes\Plugin;
|
||||
|
||||
class Blog extends Plugin {
|
||||
public $pluginName = 'TP Blog';
|
||||
public $pluginAuthor = 'JoeyK';
|
||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
||||
public $modelVersion = '1.0';
|
||||
public $pluginVersion = '3.0';
|
||||
public $pluginDescription = 'A simple plugin to add a blog to your installation.';
|
||||
public $admin_links = [
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-font"></i> Blog',
|
||||
'url' => '{ROOT_URL}admin/blog',
|
||||
],
|
||||
];
|
||||
public $info_footer_links = [
|
||||
[
|
||||
'text' => 'Blog',
|
||||
'url' => '{ROOT_URL}blog/index',
|
||||
],
|
||||
];
|
||||
public $resourceMatrix = [
|
||||
'posts' => [
|
||||
[
|
||||
'title' => 'Welcome',
|
||||
'slug' => 'welcome',
|
||||
'content' => '<p>This is just a simple message to say thank you for installing The Tempus Project. If you have any questions you can find everything through our website <a href="https://TheTempusProject.com">here</a>.</p>',
|
||||
'author' => 1,
|
||||
'created' => '{time}',
|
||||
'edited' => '{time}',
|
||||
'draft' => 0,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
39
app/plugins/blog/templates/blog.inc.php
Executable file
39
app/plugins/blog/templates/blog.inc.php
Executable file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* app/plugins/blog/templates/blog.inc.php
|
||||
*
|
||||
* This is the loader for the blog template.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Templates;
|
||||
|
||||
use TheTempusProject\Models\Posts;
|
||||
use TheTempusProject\Houdini\Classes\Components;
|
||||
use TheTempusProject\Houdini\Classes\Navigation;
|
||||
use TheTempusProject\Houdini\Classes\Views;
|
||||
use TheTempusProject\Houdini\Classes\Template;
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
|
||||
class BlogLoader extends DefaultLoader {
|
||||
/**
|
||||
* This is the function used to generate any components that may be
|
||||
* needed by this template.
|
||||
*/
|
||||
public function __construct() {
|
||||
$posts = new Posts;
|
||||
Components::set('SIDEBAR', Views::simpleView('blog.widgets.recent', $posts->recent(5)));
|
||||
Components::set('SIDEBAR2', Views::simpleView('blog.widgets.archive', $posts->archive()));
|
||||
Components::set('SIDEBARABOUT', Views::simpleView('blog.widgets.about'));
|
||||
Components::set('SIDEBARSEARCH', Views::simpleView('blog.widgets.search'));
|
||||
Components::set('BLOGFEATURES', '');
|
||||
Navigation::setCrumbComponent( 'BLOG_BREADCRUMBS', Input::get( 'url' ) );
|
||||
Components::set( 'BLOG_TEMPLATE_URL', Template::parse( '{ROOT_URL}app/plugins/comments/' ) );
|
||||
$this->addCss( '<link rel="stylesheet" href="{BLOG_TEMPLATE_URL}css/comments.css">' );
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
139
app/plugins/blog/templates/blog.tpl
Executable file
139
app/plugins/blog/templates/blog.tpl
Executable file
@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
* app/plugins/blog/templates/blog.tpl
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
-->
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta property="og:url" content="{CURRENT_URL}">
|
||||
<meta name='twitter:card' content='summary_large_image'>
|
||||
<title>{TITLE}</title>
|
||||
<meta itemprop="name" content="{TITLE}">
|
||||
<meta name="twitter:title" content="{TITLE}">
|
||||
<meta property="og:title" content="{TITLE}">
|
||||
<meta name="description" content="{PAGE_DESCRIPTION}">
|
||||
<meta itemprop="description" content="{PAGE_DESCRIPTION}">
|
||||
<meta name="twitter:description" content="{PAGE_DESCRIPTION}">
|
||||
<meta property="og:description" content="{PAGE_DESCRIPTION}">
|
||||
<meta itemprop="image" content="{META_IMAGE}">
|
||||
<meta name="twitter:image" content="{META_IMAGE}">
|
||||
<meta property="og:image" content="{META_IMAGE}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="The Tempus Project">
|
||||
{ROBOT}
|
||||
<link rel="icon" href="{ROOT_URL}images/favicon.ico">
|
||||
<!-- Required CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap.min.css" crossorigin="anonymous">
|
||||
<!-- RSS -->
|
||||
<link rel="alternate" href="{ROOT_URL}blog/rss" title="{TITLE} Feed" type="application/rss+xml">
|
||||
<!-- Custom styles for this template -->
|
||||
{TEMPLATE_CSS_INCLUDES}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<header class="p-3 text-bg-dark">
|
||||
<div class="container">
|
||||
<div class="d-flex align-items-center position-relative">
|
||||
<!-- Navbar Toggler (Left) -->
|
||||
|
||||
<!-- Centered Logo (Now inside normal document flow) -->
|
||||
<a href="/" class="align-items-center text-white text-decoration-none d-flex d-md-none">
|
||||
<img src="{ROOT_URL}{LOGO}" width="40" height="32" alt="{SITENAME} Logo" class="bi">
|
||||
</a>
|
||||
|
||||
<!-- Logo (Normal Position for Large Screens) -->
|
||||
<a href="/" class="align-items-center text-white text-decoration-none d-none d-md-flex">
|
||||
<img src="{ROOT_URL}{LOGO}" width="40" height="32" alt="{SITENAME} Logo" class="bi">
|
||||
</a>
|
||||
|
||||
<div class="navbar-expand-md flex-grow-1">
|
||||
<div class="collapse navbar-collapse d-md-flex" id="mainMenu">
|
||||
<!-- Centered Navigation -->
|
||||
<div class="d-none d-md-block d-flex justify-content-center position-absolute start-50 translate-middle-x">
|
||||
{topNavLeft}
|
||||
</div>
|
||||
<div class="d-flex justify-content-center flex-grow-1 d-md-none">
|
||||
{topNavLeft}
|
||||
</div>
|
||||
|
||||
<!-- Right-Side Content (Push to End) -->
|
||||
<div class="d-flex flex-row justify-content-center align-items-center mt-3 mt-md-0 ms-md-auto">
|
||||
{topNavRight}
|
||||
</div>
|
||||
</div> <!-- End Collapse -->
|
||||
</div> <!-- End Navbar Expand -->
|
||||
|
||||
<button class="me-3 d-md-none btn btn-md btn-outline-light" type="button" data-bs-toggle="collapse" data-bs-target="#mainMenu" aria-controls="mainMenu" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
<div class="flex-container flex-grow-1">
|
||||
{ISSUES}
|
||||
<div class="container pt-4">
|
||||
<div class="row">
|
||||
{ERROR}
|
||||
{NOTICE}
|
||||
{SUCCESS}
|
||||
{INFO}
|
||||
</div>
|
||||
</div>
|
||||
{/ISSUES}
|
||||
|
||||
<!-- Leading Content -->
|
||||
<div class="container">
|
||||
{BLOGFEATURES}
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<div class="container">
|
||||
<h3 class="pb-4 mb-4 fst-italic border-bottom context-main-border">
|
||||
{SITENAME} Blog
|
||||
</h3>
|
||||
<div class="d-md-flex g-5">
|
||||
<!-- Main Content -->
|
||||
<div class="col-12 col-md-8">
|
||||
{CONTENT}
|
||||
</div>
|
||||
<!-- Sidebar Content -->
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="position-sticky" style="top: 2rem;">
|
||||
<div class="ps-md-2 ps-lg-5">
|
||||
{SIDEBARABOUT}
|
||||
</div>
|
||||
<div class="ps-md-2 ps-lg-5">
|
||||
{SIDEBARSEARCH}
|
||||
</div>
|
||||
<div class="ps-md-2 ps-lg-5">
|
||||
{SIDEBAR}
|
||||
</div>
|
||||
<div class="ps-md-2 ps-lg-5">
|
||||
{SIDEBAR2}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{FOOT}
|
||||
</div>
|
||||
<!-- Bootstrap core JavaScript and jquery -->
|
||||
<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{JQUERY_CDN}jquery.min.js"></script>
|
||||
<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||
<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{BOOTSTRAP_CDN}js/bootstrap.min.js"></script>
|
||||
<!-- Custom javascript for this template -->
|
||||
{TEMPLATE_JS_INCLUDES}
|
||||
</body>
|
||||
</html>
|
19
app/plugins/blog/templates/rss.inc.php
Executable file
19
app/plugins/blog/templates/rss.inc.php
Executable file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* app/templates/rss/rss.inc.php
|
||||
*
|
||||
* This is the loader for the rss template.
|
||||
*
|
||||
* @package TP Blog
|
||||
* @version 3.0
|
||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||
* @link https://TheTempusProject.com
|
||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||
*/
|
||||
namespace TheTempusProject\Templates;
|
||||
|
||||
class RssLoader extends DefaultLoader {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
11
app/plugins/blog/templates/rss.tpl
Executable file
11
app/plugins/blog/templates/rss.tpl
Executable file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>{TITLE}</title>
|
||||
<link>{ROOT_URL}blog</link>
|
||||
<description>{PAGE_DESCRIPTION}</description>
|
||||
<language>en-us</language>
|
||||
<copyright>Copyright (C) 2023 {SITENAME}</copyright>
|
||||
{CONTENT}
|
||||
</channel>
|
||||
</rss>
|
55
app/plugins/blog/views/admin/create.html
Executable file
55
app/plugins/blog/views/admin/create.html
Executable file
@ -0,0 +1,55 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Add Blog Post</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<form method="post">
|
||||
<fieldset>
|
||||
<!-- Title -->
|
||||
<div class="mb-3 row">
|
||||
<label for="title" class="col-lg-3 col-form-label text-end">Title:</label>
|
||||
<div class="col-lg-6">
|
||||
<input type="text" class="form-control" name="title" id="title" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slug -->
|
||||
<div class="mb-3 row">
|
||||
<label for="slug" class="col-lg-3 col-form-label text-end">URL Slug (for pretty linking):</label>
|
||||
<div class="col-lg-6">
|
||||
<input type="text" class="form-control" name="slug" id="slug" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- form buttons -->
|
||||
<div class="mb-3 row">
|
||||
<div class="offset-3 col-lg-6">
|
||||
<div class="btn-group w-100">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'c');">✔</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'x');">✖</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '!');">❕</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '?');">❔</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message -->
|
||||
<div class="mb-3 row">
|
||||
<label for="blogPost" class="col-lg-3 col-form-label text-end">Post:</label>
|
||||
<div class="col-lg-6">
|
||||
<textarea class="form-control" name="blogPost" id="blogPost" rows="6" maxlength="2000" required></textarea>
|
||||
<small class="form-text text-muted">Max: 2000 characters</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden Token -->
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
</fieldset>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button name="submit" value="publish" type="submit" class="mx-2 btn btn-lg btn-primary">Publish</button>
|
||||
<button name="submit" value="saveDraft" type="submit" class="mx-2 btn btn-lg btn-primary">Save as Draft</button>
|
||||
<button name="submit" value="preview" type="submit" class="mx-2 btn btn-lg btn-primary">Preview</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
30
app/plugins/blog/views/admin/dashboard.html
Executable file
30
app/plugins/blog/views/admin/dashboard.html
Executable file
@ -0,0 +1,30 @@
|
||||
<legend>New Posts</legend>
|
||||
<table class="table context-main">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20%"></th>
|
||||
<th style="width: 65%"></th>
|
||||
<th style="width: 5%"></th>
|
||||
<th style="width: 5%"></th>
|
||||
<th style="width: 5%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td>{title}</td>
|
||||
<td>{contentSummary}</td>
|
||||
<td><a href="{ROOT_URL}admin/blog/view/{ID}" class="btn btn-sm btn-primary"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}admin/blog/edit/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<td width="30px"><a href="{ROOT_URL}admin/blog/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td align="center" colspan="5">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
55
app/plugins/blog/views/admin/edit.html
Executable file
55
app/plugins/blog/views/admin/edit.html
Executable file
@ -0,0 +1,55 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Edit Blog Post</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<form method="post">
|
||||
<fieldset>
|
||||
<!-- Title -->
|
||||
<div class="mb-3 row">
|
||||
<label for="title" class="col-lg-3 col-form-label text-end">Title:</label>
|
||||
<div class="col-lg-6">
|
||||
<input type="text" class="form-control" name="title" id="title" value="{title}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slug -->
|
||||
<div class="mb-3 row">
|
||||
<label for="slug" class="col-lg-3 col-form-label text-end">URL Slug (for pretty linking):</label>
|
||||
<div class="col-lg-6">
|
||||
<input type="text" class="form-control" name="slug" id="slug" value="{slug}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- form buttons -->
|
||||
<div class="mb-3 row">
|
||||
<div class="offset-3 col-lg-6">
|
||||
<div class="btn-group w-100">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'c');">✔</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'x');">✖</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '!');">❕</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '?');">❔</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message -->
|
||||
<div class="mb-3 row">
|
||||
<label for="blogPost" class="col-lg-3 col-form-label text-end">Post:</label>
|
||||
<div class="col-lg-6">
|
||||
<textarea class="form-control" name="blogPost" id="blogPost" rows="6" maxlength="2000" required>{content}</textarea>
|
||||
<small class="form-text text-muted">Max: 2000 characters</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden Token -->
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
</fieldset>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button name="submit" value="publish" type="submit" class="mx-2 btn btn-lg btn-primary">Publish</button>
|
||||
<button name="submit" value="saveDraft" type="submit" class="mx-2 btn btn-lg btn-primary">Save as Draft</button>
|
||||
<button name="submit" value="preview" type="submit" class="mx-2 btn btn-lg btn-primary">Preview</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
48
app/plugins/blog/views/admin/list.html
Executable file
48
app/plugins/blog/views/admin/list.html
Executable file
@ -0,0 +1,48 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Blog Posts</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<form action="{ROOT_URL}admin/blog/delete" method="post">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30%">Title</th>
|
||||
<th style="width: 20%">Author</th>
|
||||
<th style="width: 10%">comments</th>
|
||||
<th style="width: 10%">Created</th>
|
||||
<th style="width: 10%">Updated</th>
|
||||
<th style="width: 5%"></th>
|
||||
<th style="width: 5%"></th>
|
||||
<th style="width: 10%">
|
||||
<input type="checkbox" onchange="checkAll(this)" name="check.b" value="B_[]">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td><a href="{ROOT_URL}admin/blog/view/{ID}" class="text-decoration-none">{title}</a>{isDraft}</td>
|
||||
<td>{authorName}</td>
|
||||
<td>{commentCount}</td>
|
||||
<td>{DTC}{created}{/DTC}</td>
|
||||
<td>{DTC}{edited}{/DTC}</td>
|
||||
<td><a href="{ROOT_URL}admin/blog/edit/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<td><a href="{ROOT_URL}admin/blog/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
<td>
|
||||
<input type="checkbox" value="{ID}" name="B_[]">
|
||||
</td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{ROOT_URL}admin/blog/create" class="btn btn-sm btn-primary">Create</a>
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
40
app/plugins/blog/views/admin/preview.html
Executable file
40
app/plugins/blog/views/admin/preview.html
Executable file
@ -0,0 +1,40 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-8 blog-main">
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">{title}</h2>
|
||||
<p class="blog-post-meta">{DTC}{created}{/DTC} by <a href="{ROOT_URL}admin/user/view/{author}">{authorName}</a></p>
|
||||
{content}
|
||||
</div><!-- /.blog-post -->
|
||||
</div><!-- /.blog-main -->
|
||||
</div><!-- /.row -->
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<legend>New Blog Post</legend>
|
||||
<div class="form-group">
|
||||
<label for="title" class="col-lg-3 control-label">Title</label>
|
||||
<div class="col-lg-3">
|
||||
<input type="text" class="form-check-input" name="title" id="title" value="{title}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-6 col-lg-offset-3 btn-group">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', 'c');">✔</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', 'x');">✖</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', '!');">❕</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', '?');">❔</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="blogPost" class="col-lg-3 control-label">Post</label>
|
||||
<div class="col-lg-6">
|
||||
<textarea class="form-control" name="blogPost" maxlength="2000" rows="10" cols="50" id="blogPost">{content}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-6 col-lg-offset-3">
|
||||
<button name="submit" value="publish" type="submit" class="btn btn-lg btn-primary">Publish</button>
|
||||
<button name="submit" value="saveasdraft" type="submit" class="btn btn-lg btn-primary">Save as Draft</button>
|
||||
<button name="submit" value="preview" type="submit" class="btn btn-lg btn-primary">Preview</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
</form>
|
14
app/plugins/blog/views/admin/view.html
Executable file
14
app/plugins/blog/views/admin/view.html
Executable file
@ -0,0 +1,14 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Blog Post: {title}</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}admin/users/view/{author}">{authorName}</a></p>
|
||||
<div class="well">{content}</div>
|
||||
{ADMIN}
|
||||
<hr>
|
||||
<a href="{ROOT_URL}admin/blog/delete/{ID}" class="btn btn-md btn-danger"><i class="fa fa-fw fa-trash"></i></a>
|
||||
<a href="{ROOT_URL}admin/blog/edit/{ID}" class="btn btn-md btn-warning"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a href="{ROOT_URL}admin/comments/blog/{ID}" class="btn btn-md btn-primary">View Comments</a>
|
||||
<hr>
|
||||
{/ADMIN}
|
||||
</div>
|
7
app/plugins/blog/views/largeFeature.html
Executable file
7
app/plugins/blog/views/largeFeature.html
Executable file
@ -0,0 +1,7 @@
|
||||
<div class="p-4 p-md-5 mb-4 rounded text-bg-dark">
|
||||
<div class="col-md-6 px-0">
|
||||
<h1 class="display-4 fst-italic">Title of a longer featured blog post</h1>
|
||||
<p class="lead my-3">Multiple lines of text that form the lede, informing new readers quickly and efficiently about what’s most interesting in this post’s contents.</p>
|
||||
<p class="lead mb-0"><a href="#" class="text-white fw-bold">Continue reading...</a></p>
|
||||
</div>
|
||||
</div>
|
15
app/plugins/blog/views/list.html
Executable file
15
app/plugins/blog/views/list.html
Executable file
@ -0,0 +1,15 @@
|
||||
{LOOP}
|
||||
<article class="blog-post context-main-bg p-2">
|
||||
<h2 class="blog-post-title mb-1">{title}</h2>
|
||||
<p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{authorName}" class="text-decoration-none">{authorName}</a></p>
|
||||
<div class="well">
|
||||
{contentSummary}
|
||||
</div>
|
||||
</article>
|
||||
<hr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<div class="text-center">
|
||||
<p class="h5">No Posts Found</p>
|
||||
</div>
|
||||
{/ALT}
|
18
app/plugins/blog/views/post.html
Executable file
18
app/plugins/blog/views/post.html
Executable file
@ -0,0 +1,18 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-sm-12 blog-main context-main-bg p-1 p-md-2 mb-2 rounded">
|
||||
<div class="blog-post mb-5 pb-5">
|
||||
<h2 class="blog-post-title">{title}</h2>
|
||||
<hr>
|
||||
<p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{authorName}" class="text-decoration-none">{authorName}</a></p>
|
||||
{content}
|
||||
{ADMIN}
|
||||
<hr>
|
||||
<a href="{ROOT_URL}admin/blog/delete/{ID}" class="btn btn-md btn-danger"><i class="fa fa-fw fa-trash"></i></a>
|
||||
<a href="{ROOT_URL}admin/blog/edit/{ID}" class="btn btn-md btn-warning"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<hr>
|
||||
{/ADMIN}
|
||||
</div><!-- /.blog-post -->
|
||||
{COMMENTS}
|
||||
{NEWCOMMENT}
|
||||
</div><!-- /.blog-main -->
|
||||
</div><!-- /.row -->
|
10
app/plugins/blog/views/rss.html
Executable file
10
app/plugins/blog/views/rss.html
Executable file
@ -0,0 +1,10 @@
|
||||
{LOOP}
|
||||
<item>
|
||||
<title>{title}</title>
|
||||
<description>{contentSummary}</description>
|
||||
<link>{ROOT_URL}blog/post/{ID}</link>
|
||||
<pubDate>{DTC}{created}{/DTC}</pubDate>
|
||||
</item>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
{/ALT}
|
3
app/plugins/blog/views/searchResults.html
Normal file
3
app/plugins/blog/views/searchResults.html
Normal file
@ -0,0 +1,3 @@
|
||||
<legend class="text-center my-2">Search Results</legend>
|
||||
<hr>
|
||||
{searchResults}
|
31
app/plugins/blog/views/smallFeature.html
Executable file
31
app/plugins/blog/views/smallFeature.html
Executable file
@ -0,0 +1,31 @@
|
||||
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-6">
|
||||
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
|
||||
<div class="col p-4 d-flex flex-column position-static">
|
||||
<strong class="d-inline-block mb-2 text-primary">World</strong>
|
||||
<h3 class="mb-0">Featured post</h3>
|
||||
<div class="mb-1 text-muted">Nov 12</div>
|
||||
<p class="card-text mb-auto">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
|
||||
<a href="#" class="stretched-link">Continue reading</a>
|
||||
</div>
|
||||
<div class="col-auto d-none d-lg-block">
|
||||
<svg class="bd-placeholder-img" width="200" height="250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
|
||||
<div class="col p-4 d-flex flex-column position-static">
|
||||
<strong class="d-inline-block mb-2 text-success">Design</strong>
|
||||
<h3 class="mb-0">Post title</h3>
|
||||
<div class="mb-1 text-muted">Nov 11</div>
|
||||
<p class="mb-auto">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
|
||||
<a href="#" class="stretched-link">Continue reading</a>
|
||||
</div>
|
||||
<div class="col-auto d-none d-lg-block">
|
||||
<svg class="bd-placeholder-img" width="200" height="250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
11
app/plugins/blog/views/widgets/about.html
Normal file
11
app/plugins/blog/views/widgets/about.html
Normal file
@ -0,0 +1,11 @@
|
||||
<div class="pb-2 pb-lg-3">
|
||||
<div class="card rounded context-main-bg">
|
||||
<div class="card-body">
|
||||
<h4 class="fst-italic">About</h4>
|
||||
<p class="mb-0">
|
||||
The blog is mostly here to serve ass a simple way to link to long-form content on the site.
|
||||
There won't be any breaking news or tell-all stories here. Just good ole fashioned boring crap no one wants to read.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
17
app/plugins/blog/views/widgets/archive.html
Normal file
17
app/plugins/blog/views/widgets/archive.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="pb-2 pb-lg-3">
|
||||
<div class="card rounded context-main-bg">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Archives</h3>
|
||||
</div>
|
||||
<div class="card-body context-second-bg">
|
||||
<ol class="list-unstyled">
|
||||
{LOOP}
|
||||
<li>({count}) <a href="{ROOT_URL}blog/month/{month}/{year}" class="text-decoration-none">{monthText} {year}</a></li>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<li>None To Show</li>
|
||||
{/ALT}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
20
app/plugins/blog/views/widgets/recent.html
Normal file
20
app/plugins/blog/views/widgets/recent.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="pb-2 pb-lg-3">
|
||||
<div class="card rounded context-main-bg">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Recent Posts</h3>
|
||||
</div>
|
||||
<div class="card-body context-second-bg">
|
||||
<ol class="list-unstyled">
|
||||
{LOOP}
|
||||
<li><a href="{ROOT_URL}blog/post/{ID}" class="text-decoration-none">{title}</a></li>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<li>No Posts to show</li>
|
||||
{/ALT}
|
||||
</ol>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{ROOT_URL}blog" class="text-decoration-none">View All</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
18
app/plugins/blog/views/widgets/search.html
Normal file
18
app/plugins/blog/views/widgets/search.html
Normal file
@ -0,0 +1,18 @@
|
||||
<div class="pb-2 pb-lg-3">
|
||||
<div class="card rounded context-main-bg">
|
||||
<div class="card-body">
|
||||
<form method="post" action="/blog/search">
|
||||
<fieldset>
|
||||
<!-- Hidden Token -->
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
|
||||
<!-- Search -->
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" aria-label="Search Terms" name="searchTerm" id="searchTerm">
|
||||
<button type="submit" class="btn btn-secondary bg-primary" name="submit" value="submit">Search</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Reference in New Issue
Block a user