hfkfhkfhgjkuhgfkjfghkj

This commit is contained in:
Local Dev
2025-02-03 12:03:51 -05:00
commit fd36f0f4bf
302 changed files with 22625 additions and 0 deletions

View 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' ) ) );
}
}

View 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
View 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
View 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
View 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,
],
],
];
}

View 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();
}
}

View 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>

View 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();
}
}

View 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>

View 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');">&#10004;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'x');">&#10006;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '!');">&#10069;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '?');">&#10068;</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>

View 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>

View 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');">&#10004;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', 'x');">&#10006;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '!');">&#10069;</button>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="insertTag ('blogPost', '?');">&#10068;</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>

View 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>

View 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');">&#10004;</button>
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', 'x');">&#10006;</button>
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', '!');">&#10069;</button>
<button type="button" class="btn btn-sm btn-primary" onclick="insertTag ('blogPost', '?');">&#10068;</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>

View 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>

View 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 whats most interesting in this posts contents.</p>
<p class="lead mb-0"><a href="#" class="text-white fw-bold">Continue reading...</a></p>
</div>
</div>

View 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}

View 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
View 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}

View File

@ -0,0 +1,3 @@
<legend class="text-center my-2">Search Results</legend>
<hr>
{searchResults}

View 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>

View 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>

View 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>

View 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>

View 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>