Initial commit

This commit is contained in:
Joey Kimsey
2024-08-04 21:15:59 -04:00
parent c9d1fb983f
commit 0d469501ee
695 changed files with 70184 additions and 71 deletions

View File

@ -0,0 +1,119 @@
<?php
/**
* app/plugins/suggestions/controllers/admin/suggestions.php
*
* This is the suggestions admin controller.
*
* @package TP Suggest
* @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\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Classes\AdminController;
use TheTempusProject\Models\Suggestions as SuggestionsModel;
use TheTempusProject\Models\Comments;
use TheTempusProject\Houdini\Classes\Forms as TemplateForm;
class Suggestions extends AdminController {
protected static $suggestions;
protected static $comments;
public function __construct() {
parent::__construct();
self::$title = 'Admin - Suggestions';
self::$suggestions = new SuggestionsModel;
}
public function index( $data = null ) {
Views::view( 'suggestions.admin.list', self::$suggestions->list() );
}
public function view( $id = null ) {
$data = self::$suggestions->findById( $id );
if ( $data == false ) {
Issues::add( 'error', 'Suggestion not found.' );
return $this->index();
}
if ( empty( self::$comments ) ) {
self::$comments = new Comments;
}
Components::set( 'COMMENT_TYPE', 'suggestions' );
Components::set( 'count', self::$comments->count( 'suggestion', $id ) );
Components::set( 'COMMENTS', Views::simpleView( 'comments.list', self::$comments->display( 10, 'suggestion', $id ) ) );
Views::view( 'suggestions.admin.view', $data );
}
public function approve( $id = null ) {
$data = self::$suggestions->findById( $id );
if ( $data == false ) {
Issues::add( 'error', 'Suggestion not found.' );
return $this->index();
}
if ( !self::$suggestions->approve( $id ) ) {
Issues::add( 'error', 'Suggestion not approved.' );
return $this->index();
}
Issues::add( 'success', 'Suggestion Approved' );
return $this->index();
}
public function reject( $id = null ) {
$data = self::$suggestions->findById( $id );
if ( $data == false ) {
Issues::add( 'error', 'Suggestion not found.' );
return $this->index();
}
if ( !self::$suggestions->reject( $id ) ) {
Issues::add( 'error', 'Suggestion not rejected.' );
return $this->index();
}
Issues::add( 'success', 'Suggestion Rejected' );
return $this->index();
}
public function edit( $id = null ) {
$data = self::$suggestions->findById( $id );
if ( false == $data ) {
return $this->index();
}
TemplateForm::selectRadio( 'approved', $data->approved );
if ( !Input::exists( 'submit' ) ) {
return Views::view( 'suggestions.admin.edit', $data );
}
if ( !Forms::check( 'editSuggestion' ) ) {
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
return Views::view( 'suggestions.admin.edit', $data );
}
if ( self::$suggestions->update( $id, Input::post( 'title' ), Input::post( 'suggestion' ), Input::post( 'approved' ) ) ) {
Issues::add( 'success', 'Suggestion updated' );
} else {
return Views::view( 'suggestions.edit', $data );
}
}
public function delete( $data = null ) {
if ( Input::exists( 'submit' ) ) {
$data = Input::post( 'S_' );
}
if ( !self::$suggestions->delete( $data ) ) {
Issues::add( 'error', 'There was an error with your request.' );
} else {
Issues::add( 'success', 'Suggestions has been deleted' );
}
$this->index();
}
public function clear( $data = null ) {
self::$suggestions->empty();
$this->index();
}
}

View File

@ -0,0 +1,133 @@
<?php
/**
* app/plugins/suggestions/controllers/suggestions.php
*
* This is the suggestions controller.
*
* @package TP Suggest
* @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\Bedrock\Functions\Input;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Models\Suggestions as SuggestionModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Models\Comments as CommentsModel;
use TheTempusProject\Plugins\Comments;
use TheTempusProject\Houdini\Classes\Components;
class Suggestions extends Controller {
protected static $suggestions;
protected static $comments;
public function __construct() {
parent::__construct();
if ( !App::$isLoggedIn ) {
Session::flash( 'notice', 'You must be logged in to view suggestions.' );
return Redirect::home();
}
self::$suggestions = new SuggestionModel;
self::$title = 'Suggestions - {SITENAME}';
self::$pageDescription = 'On this page you can view, submit, or comment-on suggestions for the site.';
}
public function index() {
Views::view( 'suggestions.list', self::$suggestions->recent() );
}
public function view( $id = null ) {
$data = self::$suggestions->findById( $id );
if ( $data == false ) {
Issues::add( 'error', 'Suggestion not found.' );
return $this->index();
}
if ( empty( self::$comments ) ) {
self::$comments = new CommentsModel;
}
if ( Input::exists( 'contentId' ) ) {
$this->comments( 'post', Input::post( 'contentId' ) );
}
Components::set( 'CONTENT_ID', $id );
Components::set( 'COMMENT_TYPE', 'suggestions' );
Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
Components::set( 'count', self::$comments->count( 'suggestion', $id ) );
Components::set( 'COMMENTS', Views::simpleView( 'comments.list', self::$comments->display( 10, self::$suggestions->tableName, $id ) ) );
Views::view( 'suggestions.view', $data );
}
public function create() {
if ( !Input::exists() ) {
return Views::view( 'suggestions.create' );
}
if ( !Forms::check( 'newSuggestion' ) ) {
Issues::add( 'error', [ 'There was an error with your suggestion.' => Check::userErrors() ] );
return Views::view( 'suggestions.create' );
}
if ( !self::$suggestions->create( Input::post( 'title' ), Input::post( 'suggestion' ) ) ) {
Issues::add( 'error', [ 'There was an error with your suggestion.' => Check::userErrors() ] );
return Views::view( 'suggestions.create' );
}
Session::flash( 'success', 'Your Suggestion has been received. We must verify all suggestions before they are published, but as long as it doesn\'t violate our code of conduct, it should be available to view and comment on publicly within 24 hours.' );
Redirect::to( 'suggestions/index' );
}
public function edit( $data = null ) {
if ( !Input::exists( 'submit' ) ) {
return Views::view( 'suggestions.edit', self::$suggestions->findById( $data ) );
}
if ( !Forms::check( 'editSuggestion' ) ) {
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
return Views::view( 'suggestions.edit', self::$suggestions->findById( $data ) );
}
if ( self::$suggestions->update( $data, Input::post( 'title' ), Input::post( 'suggestion' ) ) ) {
Issues::add( 'success', 'Suggestion updated' );
} else {
return Views::view( 'suggestions.edit', self::$suggestions->findById( $data ) );
}
$this->index();
}
public function comments( $sub = null, $data = null ) {
if ( empty( self::$comments ) ) {
self::$comments = new CommentsModel;
}
$commentsPlugin = new Comments;
if ( empty( $sub ) || empty( $data ) ) {
Session::flash( 'error', 'Whoops, try again.' );
Redirect::to( 'suggestions' );
}
switch ( $sub ) {
case 'post':
$content = self::$suggestions->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Content.' );
Redirect::to( 'suggestions' );
}
return $commentsPlugin->formPost( self::$suggestions->tableName, $content, 'suggestions/view/' );
case 'edit':
$content = self::$comments->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Comment.' );
Redirect::to( 'suggestions' );
}
return $commentsPlugin->formEdit( self::$suggestions->tableName, $content, 'suggestions/view/' );
case 'delete':
$content = self::$comments->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Comment.' );
Redirect::to( 'suggestions' );
}
return $commentsPlugin->formDelete( self::$suggestions->tableName, $content, 'suggestions/view/' );
}
}
}

View File

@ -0,0 +1,68 @@
<?php
/**
* app/plugins/suggestions/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Suggest
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Suggestions;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Classes\Forms;
class SuggestionForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'newSuggestion', __CLASS__, 'newSuggestion' );
self::addHandler( 'editSuggestion', __CLASS__, 'editSuggestion' );
}
/**
* Validates the suggestion create form.
*
* @return {bool}
*/
public static function newSuggestion() {
if ( !Input::exists( 'title' ) ) {
self::addUserError( 'You must specify title' );
return false;
}
if ( !Input::exists( 'suggestion' ) ) {
self::addUserError( 'You must write a Suggestion' );
return false;
}
if ( !self::token() ) {
return false;
}
return true;
}
/**
* Validates the suggestion create form.
*
* @return {bool}
*/
public static function editSuggestion() {
if ( !Input::exists( 'title' ) ) {
self::addUserError( 'You must specify title' );
return false;
}
if ( !Input::exists( 'suggestion' ) ) {
self::addUserError( 'You must write a Suggestion' );
return false;
}
if ( !self::token() ) {
return false;
}
return true;
}
}
new SuggestionForms;

View File

@ -0,0 +1,183 @@
<?php
/**
* app/plugins/suggestions/models/suggestions.php
*
* This class is used for the manipulation of the suggestions database table.
*
* @package TP Suggest
* @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\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\Canary\Canary as Debug;
use TheTempusProject\Bedrock\Classes\CustomException;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
class Suggestions extends DatabaseModel {
public $tableName = 'suggestions';
public $databaseMatrix = [
[ 'title', 'varchar', '86' ],
[ 'suggestion', 'text', '' ],
[ 'suggestedOn', 'int', '10' ],
[ 'approved', 'varchar', '5' ],
[ 'approvedOn', 'int', '10' ],
[ 'approvedBy', 'int', '11' ],
[ 'author', 'int', '11' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
// @TODO: Add a config for some of these things
// public function enabled() {
// if ( true === parent::enabled() ) {
// return Config::getValue( $this->configName . '/enabled' ) === true;
// }
// return false;
// }
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
if ( !empty($instance->approvedBy)) {
$instance->approvedByName = self::$user->getUsername( $instance->approvedBy );
} else {
$instance->approvedByName = '';
}
$instance->submittedBy = self::$user->getUsername( $instance->author );
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
/**
* Logs a Suggestion form.
*
* @param int $ID the user ID submitting the form
* @param string $url the url
* @param string $o_url the original url
* @param int $repeat is repeatable?
* @param string $description_ description of the event.
*
* @return null
*/
public function create( $title, $suggestion, $approved = 'false' ) {
if ( !$this->enabled() ) {
Debug::info( 'Suggestions are disabled in the config.' );
return false;
}
$fields = [
'author' => App::$activeUser->ID,
'suggestedOn' => time(),
'suggestion' => $suggestion,
'approved' => $approved,
'title' => $title,
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( $this->tableName );
return false;
}
return self::$db->lastId();
}
public function update( $ID, $title, $suggestion, $approved = 'false' ) {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
if ( !Check::id( $ID ) ) {
Debug::info( 'Suggestion: illegal ID.' );
return false;
}
$fields = [
'suggestion' => $suggestion,
'approved' => $approved,
'title' => $title,
];
if ( !self::$db->update( $this->tableName, $ID, $fields ) ) {
new CustomException( $this->tableName );
Debug::error( "Suggestion: $ID not updated: $fields" );
return false;
}
self::$log->admin( "Updated Suggestion: $ID" );
return true;
}
public function approve( $ID ) {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
if ( !Check::id( $ID ) ) {
Debug::info( 'Suggestion: illegal ID.' );
return false;
}
$fields = [
'approved' => 'true',
'approvedOn' => time(),
'approvedBy' => App::$activeUser->ID,
];
if ( !self::$db->update( $this->tableName, $ID, $fields ) ) {
new CustomException( $this->tableName );
Debug::error( "Suggestion: $ID not Rejected: $fields" );
return false;
}
self::$log->admin( "Suggestion Rejected: $ID" );
return true;
}
public function reject( $ID ) {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
if ( !Check::id( $ID ) ) {
Debug::info( 'Suggestion: illegal ID.' );
return false;
}
$fields = [
'approved' => 'false',
'approvedOn' => null,
'approvedBy' => null,
];
if ( !self::$db->update( $this->tableName, $ID, $fields ) ) {
new CustomException( $this->tableName );
Debug::error( "Suggestion: $ID not Approved: $fields" );
return false;
}
self::$log->admin( "Suggestion Approved: $ID" );
return true;
}
public function recent( $approvedOnly = true, $limit = 0 ) {
if ( true === $approvedOnly ) {
$where = ['approved', '=', 'true'];
} else {
$where = ['ID', '>', '0'];
}
if ( empty( $limit ) ) {
$data = self::$db->getPaginated( $this->tableName, $where, 'suggestedOn', 'DESC' );
} else {
$data = self::$db->get( $this->tableName, $where, 'suggestedOn', 'DESC', [0, $limit] );
}
if ( !$data->count() ) {
Debug::info( 'No Suggestions found.' );
return false;
}
return $this->filter( $data->results() );
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* app/plugins/suggestions/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Suggest
* @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 ReflectionClass;
use TheTempusProject\Classes\Installer;
use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Classes\Plugin;
use TheTempusProject\TheTempusProject as App;
class Suggestions extends Plugin {
public $pluginName = 'TP Suggest';
public $pluginAuthor = 'JoeyK';
public $pluginWebsite = 'https://TheTempusProject.com';
public $modelVersion = '1.0';
public $pluginVersion = '3.0';
public $pluginDescription = 'A simple plugin which adds a site wide suggestion system.';
public $permissionMatrix = [
'suggest' => [
'pretty' => 'Can create suggestions',
'default' => false,
],
];
public $admin_links = [
[
'text' => '<i class="fa fa-fw fa-copy"></i> Suggestions',
'url' => '{ROOT_URL}admin/suggestions',
],
];
public $footer_links = [
[
'text' => 'Suggestions',
'url' => '{ROOT_URL}suggestions/index',
'filter' => 'loggedin',
],
];
}

View File

@ -0,0 +1,22 @@
<form action="" method="post" class="form-horizontal">
<div class="form-group">
<div class="col-lg-4 col-lg-offset-4">
<label for="title" class="col-lg-3 control-label">Title</label>
<input type="text" class="form-check-input" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<div class="col-lg-4 col-lg-offset-4">
<label for="suggestion">Suggestion</label>
<textarea class="form-control" name="suggestion" maxlength="2000" rows="10" cols="50" id="suggestion">{suggestion}</textarea>
</div>
</div>
<div class="form-group">
<div class="col-lg-4 col-lg-offset-4">
<input class="" type="checkbox" name="approved" id="approved" value="true" {CHECKED:approved=true}>
<label for="approved">Approved (Publicly available)?</label>
</div>
</div>
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Save</button>
<input type="hidden" name="token" value="{TOKEN}">
</form>

View File

@ -0,0 +1,56 @@
<legend>Suggestions</legend>
{PAGINATION}
<form action="{ROOT_URL}admin/suggestions/delete" method="post">
<table class="table table-striped">
<thead>
<tr>
<th style="width: 5%">ID</th>
<th style="width: 30%">Suggestion</th>
<th style="width: 10%">Suggested On</th>
<th style="width: 10%">Suggested By</th>
<th style="width: 10%">Approved</th>
<th style="width: 10%">Approved On</th>
<th style="width: 10%">Approved By</th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
<th style="width: 5%">
<INPUT type="checkbox" onchange="checkAll(this)" name="check.br" value="S_[]"/>
</th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td align="center">{ID}</td>
<td>{suggestion}</td>
<td align="center">{DTC}{suggestedOn}{/DTC}</td>
<td align="center">{submittedBy}</td>
<td>{approved}</td>
<td align="center">{DTC}{approvedOn}{/DTC}</td>
<td align="center">{approvedByName}</td>
<td>
<a href="{ROOT_URL}admin/suggestions/approve/{ID}" class="btn btn-sm btn-success" role="button"><i class="glyphicon glyphicon-ok"></i></a>
<a href="{ROOT_URL}admin/suggestions/reject/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-remove"></i></a>
</td>
<td>
<a href="{ROOT_URL}admin/suggestions/view/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a>
<a href="{ROOT_URL}admin/suggestions/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</td>
<td>
<input type="checkbox" value="{ID}" name="BR_[]">
</td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="6">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger">Delete</button>
</form>
<br />
<a href="{ROOT_URL}admin/suggestions/clear">clear all</a>

View File

@ -0,0 +1,17 @@
<div class="row">
<div class="col-lg-12 col-sm-12 blog-main">
<div class="blog-post">
<h2 class="blog-post-title">Suggestion</h2>
<hr>
<p class="blog-post-meta">{DTC date}{suggestedOn}{/DTC} by <a href="{ROOT_URL}home/profile/{author}">{submittedBy}</a></p>
{suggestion}
{ADMIN}
<hr>
<a href="{ROOT_URL}admin/suggestions/delete/{ID}" class="btn btn-md btn-danger" role="button">Delete</a>
<a href="{ROOT_URL}admin/suggestions/edit/{ID}" class="btn btn-md btn-warning" role="button">Edit</a>
<hr>
{/ADMIN}
</div><!-- /.suggestions-post -->
{COMMENTS}
</div><!-- /.suggestions-main -->
</div><!-- /.row -->

View File

@ -0,0 +1,16 @@
<form action="" method="post" class="form-horizontal">
<legend>Submit a Suggestion</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 center-block">
<div class="col-lg-8 col-lg-offset-2">
<textarea class="form-control" name="suggestion" maxlength="2000" rows="4" cols="50" id="suggestion"></textarea>
</div>
</div>
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button>
<input type="hidden" name="token" value="{TOKEN}">
</form>

View File

@ -0,0 +1,15 @@
<form action="" method="post" class="form-horizontal">
<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 center-block">
<div class="col-lg-4 col-lg-offset-4">
<textarea class="form-control" name="suggestion" maxlength="2000" rows="10" cols="50" id="suggestion">{suggestion}</textarea>
</div>
</div>
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Save</button>
<input type="hidden" name="token" value="{TOKEN}">
</form>

View File

@ -0,0 +1,19 @@
<legend>Suggestions</legend>
{PAGINATION}
{LOOP}
<div class="blog-post">
<h2 class="blog-post-title"><a href="{ROOT_URL}suggestions/view/{ID}">{title}</a></h2>
<div class="well">
{suggestion}
<p class="blog-post-meta">
Suggested on <i>{DTC date}{suggestedOn}{/DTC}</i> by <a href="{ROOT_URL}home/profile/{author}"><strong>{submittedBy}</strong></a>
</p>
</div>
</div>
{/LOOP}
{ALT}
<div class="blog-post">
<p class="blog-post-meta">No Suggestions Found.</p>
</div>
{/ALT}
<a href="{ROOT_URL}suggestions/create" class="btn btn-sm btn-primary" role="button">Create</a>

View File

@ -0,0 +1,18 @@
<div class="row">
<div class="col-lg-12 col-sm-12 blog-main">
<div class="blog-post">
<h2 class="blog-post-title">{title}</h2>
<hr>
<p class="blog-post-meta">{DTC date}{suggestedOn}{/DTC} by <a href="{ROOT_URL}home/profile/{author}">{submittedBy}</a></p>
{suggestion}
{ADMIN}
<hr>
<a href="{ROOT_URL}admin/suggestions/delete/{ID}" class="btn btn-md btn-danger" role="button">Delete</a>
<a href="{ROOT_URL}admin/suggestions/edit/{ID}" class="btn btn-md btn-warning" role="button">Edit</a>
<hr>
{/ADMIN}
</div><!-- /.suggestions-post -->
{COMMENTS}
{NEWCOMMENT}
</div><!-- /.suggestions-main -->
</div><!-- /.row -->