remove non-standard plugins

This commit is contained in:
Joey Kimsey
2024-08-15 12:49:34 -04:00
parent 431b1214fa
commit 4ca6f7b43f
174 changed files with 0 additions and 13262 deletions

View File

@ -1,402 +0,0 @@
<?php
/**
* app/plugins/bugreport/controllers/bugreport.php
*
* This is the bug reports controller.
*
* @package TP BugReports
* @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\Hermes\Functions\Redirect;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Input;
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\Bookmarks as Bookmark;
use TheTempusProject\Models\Folders;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Forms as HoudiniForms;
use TheTempusProject\Houdini\Classes\Navigation;
class Bookmarks extends Controller {
protected static $bookmarks;
protected static $folders;
public function __construct() {
parent::__construct();
if ( !App::$isLoggedIn ) {
Session::flash( 'notice', 'You must be logged in to create or manage bookmarks.' );
return Redirect::home();
}
self::$bookmarks = new Bookmark;
self::$folders = new Folders;
self::$title = 'Bookmarks - {SITENAME}';
self::$pageDescription = 'Add and save url bookmarks here.';
$folderTabs = Views::simpleView( 'bookmarks.nav.folderTabs' );
if ( stripos( Input::get('url'), 'bookmarks/bookmarks' ) !== false ) {
$tabsView = Navigation::activePageSelect( $folderTabs, '/bookmarks/folders/', false, true );
$userFolderTabs = Views::simpleView('bookmarks.nav.userFolderTabs', self::$folders->simpleObjectByUser(true) );
$userFolderTabsView = Navigation::activePageSelect( $userFolderTabs, Input::get( 'url' ), false, true );
} else {
$tabsView = Navigation::activePageSelect( $folderTabs, Input::get( 'url' ), false, true );
$userFolderTabsView = '';
}
Components::set( 'userFolderTabs', $userFolderTabsView );
Views::raw( $tabsView );
}
public function index() {
$bookmarks = self::$bookmarks->noFolder();
$folders = self::$folders->byUser();
$panelArray = [];
if ( !empty( $folders ) ) {
foreach ( $folders as $folder ) {
$panel = new \stdClass();
$folderObject = new \stdClass();
$folderObject->bookmarks = self::$bookmarks->byFolder( $folder->ID );
$folderObject->ID = $folder->ID;
$folderObject->title = $folder->title;
$folderObject->color = $folder->color;
$folderObject->bookmarkListRows = Views::simpleView( 'bookmarks.components.bookmarkListRows', $folderObject->bookmarks );
$panel->panel = Views::simpleView( 'bookmarks.components.bookmarkListPanel', [$folderObject] );
$panelArray[] = $panel;
}
}
Components::set( 'foldersList', Views::simpleView( 'bookmarks.folders.list', $folders ) );
Components::set( 'folderPanels', Views::simpleView( 'bookmarks.components.folderPanelList', $panelArray ) );
Components::set( 'bookmarksList', Views::simpleView( 'bookmarks.bookmarks.list', $bookmarks ) );
return Views::view( 'bookmarks.dash' );
}
/**
* Bookmarks
*/
public function bookmark( $id = 0 ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/bookmark/' . $id );
return Views::view( 'bookmarks.bookmarks.view', $bookmark );
}
public function bookmarks( $id = null ) {
$folder = self::$folders->findById( $id );
if ( $folder == false ) {
Session::flash( 'error', 'Folder not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $folder->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to view this folder.' );
return Redirect::to( 'bookmarks/index' );
}
Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/bookmarks/' . $id );
$bookmarks = self::$bookmarks->noFolder();
$panelArray = [];
$panel = new \stdClass();
$folderObject = new \stdClass();
$folderObject->bookmarks = self::$bookmarks->byFolder( $folder->ID );
$folderObject->ID = $folder->ID;
$folderObject->title = $folder->title;
$folderObject->color = $folder->color;
$folderObject->bookmarkListRows = Views::simpleView( 'bookmarks.components.bookmarkListRows', $folderObject->bookmarks );
$panel->panel = Views::simpleView( 'bookmarks.components.bookmarkListPanel', [$folderObject] );
$panelArray[] = $panel;
return Views::view( 'bookmarks.components.folderPanelList', $panelArray );
}
public function createBookmark( $id = null ) {
$folderID = Input::get('folder_id') ? Input::get('folder_id') : $id;
$folderID = Input::post('folder_id') ? Input::post('folder_id') : $id;
$folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() );
Components::set( 'folderSelect', $folderSelect );
if ( ! Input::exists() ) {
return Views::view( 'bookmarks.bookmarks.create' );
}
if ( ! Forms::check( 'createBookmark' ) ) {
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
return Views::view( 'bookmarks.bookmarks.create' );
}
$result = self::$bookmarks->create(
Input::post('title'),
Input::post('url'),
$folderID,
Input::post('description'),
Input::post('color'),
Input::post('privacy'),
);
if ( ! $result ) {
Issues::add( 'error', [ 'There was an error creating your bookmark.' => Check::userErrors() ] );
return Views::view( 'bookmarks.bookmarks.create' );
}
self::$bookmarks->refreshInfo( $result );
Session::flash( 'success', 'Your Bookmark has been created.' );
Redirect::to( 'bookmarks/bookmarks/'. $folderID );
}
public function editBookmark( $id = null ) {
$folderID = Input::exists('folder_id') ? Input::post('folder_id') : '';
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Issues::add( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
if ( empty( $folderID ) ) {
$folderID = $bookmark->folderID;
}
$folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() );
Components::set( 'folderSelect', $folderSelect );
Components::set( 'color', $bookmark->color );
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'bookmarks.bookmarks.edit', $bookmark );
}
if ( ! Forms::check( 'editBookmark' ) ) {
Issues::add( 'error', [ 'There was an error updating your bookmark.' => Check::userErrors() ] );
return Views::view( 'bookmarks.bookmarks.edit', $bookmark );
}
$result = self::$bookmarks->update(
$id,
Input::post('title'),
Input::post('url'),
$folderID,
Input::post('description'),
Input::post('color'),
Input::post('privacy'),
);
if ( ! $result ) {
Issues::add( 'error', [ 'There was an error updating your bookmark.' => Check::userErrors() ] );
return Views::view( 'bookmarks.bookmarks.edit', $bookmark );
}
Session::flash( 'success', 'Your Bookmark has been updated.' );
Redirect::to( 'bookmarks/folders/'. $bookmark->folderID );
}
public function deleteBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Issues::add( 'error', 'Bookmark not found.' );
return $this->index();
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this bookmark.' );
return $this->index();
}
$result = self::$bookmarks->delete( $id );
if ( !$result ) {
Session::flash( 'error', 'There was an error deleting the bookmark(s)' );
} else {
Session::flash( 'success', 'Bookmark deleted' );
}
Redirect::to( 'bookmarks/folders/'. $bookmark->folderID );
}
/**
* Folders
*/
public function folders( $id = null) {
$folder = self::$folders->findById( $id );
if ( $folder == false ) {
$folders = self::$folders->byUser();
return Views::view( 'bookmarks.folders.list', $folders );
}
if ( $folder->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to view this folder.' );
return Redirect::to( 'bookmarks/index' );
}
Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/folders/' . $id );
return Views::view( 'bookmarks.folders.view', $folder );
}
public function createFolder( $id = 0 ) {
$folderID = Input::exists('folder_id') ? Input::post('folder_id') : $id;
$folders = self::$folders->simpleByUser();
if ( ! empty( $folders ) ) {
$folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, $folders );
} else {
$folderSelect = '';
}
Components::set( 'folderSelect', $folderSelect );
if ( ! Input::exists() ) {
return Views::view( 'bookmarks.folders.create' );
}
if ( ! Forms::check( 'createFolder' ) ) {
Issues::add( 'error', [ 'There was an error creating your folder.' => Check::userErrors() ] );
return Views::view( 'bookmarks.folders.create' );
}
$folder = self::$folders->create( Input::post('title'), $folderID, Input::post('description'), Input::post('color'), Input::post('privacy') );
if ( ! $folder ) {
return Views::view( 'bookmarks.folders.create' );
}
Session::flash( 'success', 'Your Folder has been created.' );
Redirect::to( 'bookmarks/folders' );
}
public function editFolder( $id = null ) {
$folder = self::$folders->findById( $id );
if ( $folder == false ) {
Issues::add( 'error', 'Folder not found.' );
return $this->index();
}
if ( $folder->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this folder.' );
return $this->index();
}
$folderID = ( false === Input::exists('folder_id') ) ? $folder->ID : Input::post('folder_id');
$folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() );
Components::set( 'folderSelect', $folderSelect );
Components::set( 'color', $folder->color );
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'bookmarks.folders.edit', $folder );
}
if ( !Forms::check( 'editFolder' ) ) {
Issues::add( 'error', [ 'There was an error editing your folder.' => Check::userErrors() ] );
return Views::view( 'bookmarks.folders.edit', $folder );
}
$result = self::$folders->update( $id, Input::post('title'), $folderID, Input::post('description'), Input::post('color'), Input::post('privacy') );
if ( !$result ) {
Issues::add( 'error', [ 'There was an error updating your folder.' => Check::userErrors() ] );
return Views::view( 'bookmarks.folders.edit', $folder );
}
Session::flash( 'success', 'Your Folder has been updated.' );
Redirect::to( 'bookmarks/folders/'. $folder->ID );
}
public function deleteFolder( $id = null ) {
$folder = self::$folders->findById( $id );
if ( $folder == false ) {
Issues::add( 'error', 'Folder not found.' );
return $this->index();
}
if ( $folder->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this folder.' );
return $this->index();
}
$results = self::$bookmarks->deleteByFolder( $id );
$result = self::$folders->delete( $id );
if ( !$result ) {
Session::flash( 'error', 'There was an error deleting the folder(s)' );
} else {
Session::flash( 'success', 'Folder deleted' );
}
Redirect::to( 'bookmarks/folders' );
}
/**
* Functionality
*/
public function hideBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
self::$bookmarks->hide( $id );
Session::flash( 'success', 'Bookmark hidden.' );
return Redirect::to( 'bookmarks/index' );
}
public function archiveBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
self::$bookmarks->archive( $id );
Session::flash( 'success', 'Bookmark archived.' );
return Redirect::to( 'bookmarks/index' );
}
public function showBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
self::$bookmarks->show( $id );
Session::flash( 'success', 'Bookmark shown.' );
return Redirect::to( 'bookmarks/index' );
}
public function unarchiveBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
self::$bookmarks->unarchive( $id );
Session::flash( 'success', 'Bookmark un-archived.' );
return Redirect::to( 'bookmarks/index' );
}
public function refreshBookmark( $id = null ) {
$bookmark = self::$bookmarks->findById( $id );
if ( $bookmark == false ) {
Session::flash( 'error', 'Bookmark not found.' );
return Redirect::to( 'bookmarks/index' );
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this bookmark.' );
return Redirect::to( 'bookmarks/index' );
}
$info = self::$bookmarks->refreshInfo( $id );
if ( false == $info ) {
Session::flash( 'error', 'Issue refreshing your bookmark.' );
return Redirect::to( 'bookmarks/bookmark/' . $bookmark->ID );
}
Session::flash( 'success', 'Bookmark data refreshed.' );
return Redirect::to( 'bookmarks/bookmark/' . $bookmark->ID );
}
}

View File

@ -1,122 +0,0 @@
<?php
/**
* app/plugins/bookmarks/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Bookmarks
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Bookmarks;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class BookmarksForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'createBookmark', __CLASS__, 'createBookmark' );
self::addHandler( 'createFolder', __CLASS__, 'createFolder' );
self::addHandler( 'editBookmark', __CLASS__, 'editBookmark' );
self::addHandler( 'editFolder', __CLASS__, 'editFolder' );
}
public static function createBookmark() {
// if ( ! Input::exists( 'title' ) ) {
// Check::addUserError( 'You must include a title.' );
// return false;
// }
if ( ! Input::exists( 'url' ) ) {
Check::addUserError( 'You must include a url.' );
return false;
}
// if ( ! Input::exists( 'color' ) ) {
// Check::addUserError( 'You must include a color.' );
// return false;
// }
// if ( ! Input::exists( 'privacy' ) ) {
// Check::addUserError( 'You must include a privacy.' );
// return false;
// }
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function createFolder() {
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
// if ( ! Input::exists( 'color' ) ) {
// Check::addUserError( 'You must include a color.' );
// return false;
// }
// if ( ! Input::exists( 'privacy' ) ) {
// Check::addUserError( 'You must include a privacy.' );
// return false;
// }
// if ( ! self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editBookmark() {
// if ( ! Input::exists( 'title' ) ) {
// Check::addUserError( 'You must include a title.' );
// return false;
// }
if ( ! Input::exists( 'url' ) ) {
Check::addUserError( 'You must include a url.' );
return false;
}
// if ( ! Input::exists( 'color' ) ) {
// Check::addUserError( 'You must include a color.' );
// return false;
// }
// if ( ! Input::exists( 'privacy' ) ) {
// Check::addUserError( 'You must include a privacy.' );
// return false;
// }
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editFolder() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
// if ( ! Input::exists( 'color' ) ) {
// Check::addUserError( 'You must include a color.' );
// return false;
// }
// if ( ! Input::exists( 'privacy' ) ) {
// Check::addUserError( 'You must include a privacy.' );
// return false;
// }
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
}
new BookmarksForms;

View File

@ -1,705 +0,0 @@
<?php
/**
* app/plugins/bookmarks/models/bookmarks.php
*
* This class is used for the manipulation of the bookmarks database table.
*
* @package TP Bookmarks
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Bedrock\Classes\CustomException;
class Bookmarks extends DatabaseModel {
public $tableName = 'bookmarks';
public $linkTypes = [
'Open in New Tab' => 'external',
'Open in Same Tab' => 'internal',
];
public $databaseMatrix = [
[ 'title', 'varchar', '256' ],
[ 'url', 'text', '' ],
[ 'color', 'varchar', '48' ],
[ 'privacy', 'varchar', '48' ],
[ 'folderID', 'int', '11' ],
[ 'description', 'text', '' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
[ 'meta', 'text', '' ],
[ 'icon', 'text', '' ],
[ 'archivedAt', 'int', '11' ],
[ 'refreshedAt', 'int', '11' ],
[ 'hiddenAt', 'int', '11' ],
[ 'order', 'int', '11' ],
[ 'linkType', 'varchar', '32' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $title, $url, $folderID = 0, $description = '', $color = 'default', $privacy = 'private', $type = 'external' ) {
$fields = [
'title' => $title,
'url' => $url,
'description' => $description,
'color' => $color,
'privacy' => $privacy,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( !empty( $folderID ) ) {
$fields['folderID'] = $folderID;
} else {
$fields['folderID'] = null;
}
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( 'bookmarkCreate' );
Debug::error( "Bookmarks: not created " . var_export($fields,true) );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $url, $folderID = 0, $description = '', $color = 'default', $privacy = 'private', $type = 'external', $order = 0 ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$fields = [
'title' => $title,
'url' => $url,
'description' => $description,
'color' => $color,
'privacy' => $privacy,
// 'linkType' => $type,
// 'order' => $order,
];
if ( !empty( $folderID ) ) {
$fields['folderID'] = $folderID;
}
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
public function byUser( $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
if ( empty( $limit ) ) {
$bookmarks = self::$db->get( $this->tableName, $whereClause );
} else {
$bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$bookmarks->count() ) {
Debug::info( 'No Bookmarks found.' );
return false;
}
return $this->filter( $bookmarks->results() );
}
public function byFolder( $id, $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID, 'AND'];
$whereClause = array_merge( $whereClause, [ 'folderID', '=', $id ] );
if ( empty( $limit ) ) {
$bookmarks = self::$db->get( $this->tableName, $whereClause );
} else {
$bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$bookmarks->count() ) {
Debug::info( 'No Bookmarks found.' );
return false;
}
return $this->filter( $bookmarks->results() );
}
public function noFolder( $id = 0, $limit = 10 ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID, 'AND'];
if ( !empty( $id ) ) {
$whereClause = array_merge( $whereClause, ['folderID', '!=', $id] );
} else {
$whereClause = array_merge( $whereClause, [ 'folderID', 'IS', null] );
}
if ( empty( $limit ) ) {
$bookmarks = self::$db->get( $this->tableName, $whereClause );
} else {
$bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [ 0, $limit ] );
}
if ( !$bookmarks->count() ) {
Debug::info( 'No Bookmarks found.' );
return false;
}
return $this->filter( $bookmarks->results() );
}
public function getName( $id ) {
$bookmarks = self::findById( $id );
if (false == $bookmarks) {
return 'unknown';
}
return $bookmarks->title;
}
public function getColor( $id ) {
$bookmarks = self::findById( $id );
if (false == $bookmarks) {
return 'default';
}
return $bookmarks->color;
}
public function simpleByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$bookmarks = self::$db->get( $this->tableName, $whereClause );
if ( !$bookmarks->count() ) {
Debug::warn( 'Could not find any bookmarks' );
return false;
}
$bookmarks = $bookmarks->results();
$out = [];
foreach ( $bookmarks as $bookmarks ) {
$out[ $bookmarks->title ] = $bookmarks->ID;
}
return $out;
}
public function simpleObjectByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$bookmarks = self::$db->get( $this->tableName, $whereClause );
if ( !$bookmarks->count() ) {
Debug::warn( 'Could not find any bookmarks' );
return false;
}
$bookmarks = $bookmarks->results();
$out = [];
foreach ( $bookmarks as $bookmarks ) {
$obj = new \stdClass();
$obj->title = $bookmarks->title;
$obj->ID = $bookmarks->ID;
$out[] = $obj;
}
return $out;
}
public function deleteByFolder( $folderID ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
$whereClause = array_merge( $whereClause, [ 'folderID', '=', $folderID ] );
$bookmarks = self::$db->get( $this->tableName, $whereClause );
if ( ! $bookmarks->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
foreach( $bookmarks->results() as $bookmark ) {
$this->delete( $bookmark->ID );
}
return true;
}
private function resolveShortenedUrl( $url ) {
$ch = curl_init($url);
// Set curl options
curl_setopt($ch, CURLOPT_NOBODY, true); // We don't need the body
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the response
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Set a timeout
// curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // Maybe sketchy?
// Maybe sketchy?
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disable SSL host verification
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL peer verification
// curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
// added to support the regex site
// curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
// =
// curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'AES256+EECDH:AES256+EDH' );
// Execute curl
$response = curl_exec( $ch );
// Check if there was an error
if ( curl_errno( $ch ) ) {
// Get error details
$errorCode = curl_errno($ch);
$errorMessage = curl_error($ch);
// Log or display the error details
dv('cURL Error: ' . $errorMessage . ' (Error Code: ' . $errorCode . ')');
curl_close($ch);
// return $url; // Return the original URL if there was an error
$url = rtrim( $url, '/' );
$ch2 = curl_init($url);
curl_setopt($ch2, CURLOPT_NOBODY, true); // We don't need the body
curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); // Return the response
curl_setopt($ch2, CURLOPT_TIMEOUT, 5); // Set a timeout
curl_exec($ch2);
if ( curl_errno( $ch2 ) ) {
}
curl_close( $ch );
return $url;
}
// Get the effective URL (the final destination after redirects)
$finalUrl = curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );
curl_close( $ch );
return $finalUrl;
// $headers = get_headers( $url, 1 );
$headers = @get_headers($url, 1);
if ( $headers === false ) {
}
if ( isset( $headers['Location'] ) ) {
if (is_array($headers['Location'])) {
return end($headers['Location']);
} else {
return $headers['Location'];
}
} else {
return $url;
}
}
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
$base_url = $this->getBaseUrl( $instance->url );
if ( empty( $instance->icon ) ) {
$instance->iconHtml = '<i class="glyphicon glyphicon-link"></i>';
} else {
if (strpos($instance->icon, 'http') !== false) {
$instance->iconHtml = '<img src="' . $instance->icon .'" />';
} else {
$instance->iconHtml = '<img src="' . $base_url . ltrim( $instance->icon, '/' ) .'" />';
}
}
if ( empty( $instance->hiddenAt ) ) {
$instance->hideBtn = '
<a href="{ROOT_URL}bookmarks/hideBookmark/'.$instance->ID.'" class="btn btn-sm btn-warning" role="button">
<i class="glyphicon glyphicon-eye-open"></i>
</a>';
} else {
$instance->hideBtn = '
<a href="{ROOT_URL}bookmarks/showBookmark/'.$instance->ID.'" class="btn btn-sm btn-default" role="button">
<i class="glyphicon glyphicon-eye-open"></i>
</a>';
}
if ( empty( $instance->archivedAt ) ) {
$instance->archiveBtn = '
<a href="{ROOT_URL}bookmarks/archiveBookmark/'.$instance->ID.'" class="btn btn-sm btn-info" role="button">
<i class="glyphicon glyphicon-briefcase"></i>
</a>';
} else {
$instance->archiveBtn = '
<a href="{ROOT_URL}bookmarks/unarchiveBookmark/'.$instance->ID.'" class="btn btn-sm btn-default" role="button">
<i class="glyphicon glyphicon-briefcase"></i>
</a>';
}
if ( ! empty( $instance->refreshedAt ) && time() < ( $instance->refreshedAt + ( 60 * 10 ) ) ) {
$instance->refreshBtn = '
<a href="{ROOT_URL}bookmarks/refreshBookmark/'.$instance->ID.'" class="btn btn-sm btn-danger" role="button">
<i class="glyphicon glyphicon-refresh"></i>
</a>';
} else {
$instance->refreshBtn = '
<a href="{ROOT_URL}bookmarks/refreshBookmark/'.$instance->ID.'" class="btn btn-sm btn-success" role="button">
<i class="glyphicon glyphicon-refresh"></i>
</a>';
}
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
public function hide( $id ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$fields = [
'hiddenAt' => time(),
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
public function show( $id ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$fields = [
'hiddenAt' => 0,
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
public function archive( $id ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$fields = [
'archivedAt' => time(),
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
public function unarchive( $id ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$fields = [
'archivedAt' => 0,
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
public function extractMetaTags($htmlContent) {
$doc = new \DOMDocument();
@$doc->loadHTML($htmlContent);
$metaTags = [];
foreach ($doc->getElementsByTagName('meta') as $meta) {
$name = $meta->getAttribute('name') ?: $meta->getAttribute('property');
$content = $meta->getAttribute('content');
if ($name && $content) {
$metaTags[$name] = $content;
}
}
return $metaTags;
}
public function fetchUrlData($url) {
$ch = curl_init();
// Set cURL options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in the output
curl_setopt($ch, CURLOPT_NOBODY, false); // Include the body in the output
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Set a timeout
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disable SSL host verification for testing
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL peer verification for testing
// Execute cURL request
$response = curl_exec($ch);
// Check if there was an error
if (curl_errno($ch)) {
$errorMessage = curl_error($ch);
curl_close($ch);
throw new \Exception('cURL Error: ' . $errorMessage);
}
// Get HTTP status code
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Separate headers and body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);
// Parse headers into an associative array
$headerLines = explode("\r\n", trim($headers));
$headerArray = [];
foreach ($headerLines as $line) {
$parts = explode(': ', $line, 2);
if (count($parts) == 2) {
$headerArray[$parts[0]] = $parts[1];
}
}
return [
'http_code' => $httpCode,
'headers' => $headerArray,
'body' => $body,
];
}
private function getMetaTagsAndFavicon( $url ) {
try {
// $url = 'https://runescape.wiki';
$data = $this->fetchUrlData($url);
// iv($data);
// Get headers
$headers = $data['headers'];
iv($headers);
// Get meta tags
$metaTags = $this->extractMetaTags($data['body']);
} catch (Exception $e) {
dv( 'Error: ' . $e->getMessage());
}
$metaInfo = [
'url' => $url,
'title' => null,
'description' => null,
'image' => null,
'favicon' => null
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$html = curl_exec($ch);
curl_close($ch);
if ($html === false) {
return null;
}
$meta = @get_meta_tags( $url );
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->strictErrorChecking = false;
$dom->loadHTML($html, LIBXML_NOERROR);
$xml = simplexml_import_dom($dom);
$arr = $xml->xpath('//link[@rel="shortcut icon"]');
// Get the title of the page
$titles = $dom->getElementsByTagName('title');
if ($titles->length > 0) {
$metaInfo['title'] = $titles->item(0)->nodeValue;
}
// Get the meta tags
$metaTags = $dom->getElementsByTagName('meta');
$metadata = [];
foreach ($metaTags as $meta) {
$metadata[] = [
'name' => $meta->getAttribute('name'),
'property' => $meta->getAttribute('property'),
'content' => $meta->getAttribute('content')
];
if ($meta->getAttribute('name') === 'description') {
$metaInfo['description'] = $meta->getAttribute('content');
}
if ($meta->getAttribute('itemprop') === 'image') {
$metaInfo['google_image'] = $meta->getAttribute('content');
}
if ($meta->getAttribute('property') === 'og:image') {
$metaInfo['image'] = $meta->getAttribute('content');
}
}
// Get the link tags to find the favicon
$linkTags = $dom->getElementsByTagName('link');
$metadata['links'] = [];
foreach ($linkTags as $link) {
$metadata['links'][] = [ $link->getAttribute('rel') => $link->getAttribute('href') ];
if ( $link->getAttribute('rel') === 'icon' || $link->getAttribute('rel') === 'shortcut icon') {
$metaInfo['favicon'] = $link->getAttribute('href');
break;
}
}
$metaInfo['metadata'] = $metadata;
return $metaInfo;
}
public function retrieveInfo( $url ) {
$finalDestination = $this->resolveShortenedUrl( $url );
$info = $this->getMetaTagsAndFavicon( $finalDestination );
$base_url = $this->getBaseUrl( $finalDestination );
if ( ! empty( $info['favicon'] ) ) {
echo 'favicon exists' . PHP_EOL;
if ( stripos( $info['favicon'], 'http' ) !== false) {
echo 'favicon is full url' . PHP_EOL;
$imageUrl = $info['favicon'];
} else {
echo 'favicon is not full url' . PHP_EOL;
$imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $info['favicon'], '/' );
}
if ( $this->isValidImageUrl( $imageUrl ) ) {
echo 'image is valid' . PHP_EOL;
$info['favicon'] = $imageUrl;
} else {
echo 'image is not valid' . PHP_EOL;
$base_info = $this->getMetaTagsAndFavicon( $base_url );
if ( ! empty( $base_info['favicon'] ) ) {
echo 'parent favicon exists!';
if ( stripos( $base_info['favicon'], 'http' ) !== false) {
echo 'parent favicon is full url' . PHP_EOL;
$imageUrl = $base_info['favicon'];
} else {
echo 'parent favicon is not full url' . PHP_EOL;
$imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $base_info['favicon'], '/' );
}
if ( $this->isValidImageUrl( $imageUrl ) ) {
echo 'parent favicon image is valid' . PHP_EOL;
$info['favicon'] = $imageUrl;
} else {
echo 'parent favicon image is not valid' . PHP_EOL;
}
}
}
} else {
echo 'favicon does not exist' . PHP_EOL;
$base_info = $this->getMetaTagsAndFavicon( $base_url );
if ( ! empty( $base_info['favicon'] ) ) {
echo 'parent favicon exists!' . PHP_EOL;
if ( stripos( $base_info['favicon'], 'http' ) !== false) {
echo 'parent favicon is full url' . PHP_EOL;
$imageUrl = $base_info['favicon'];
} else {
echo 'parent favicon is not full url' . PHP_EOL;
$imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $base_info['favicon'], '/' );
}
if ( $this->isValidImageUrl( $imageUrl ) ) {
echo 'parent favicon image is valid' . PHP_EOL;
$info['favicon'] = $imageUrl;
} else {
echo 'parent favicon image is not valid' . PHP_EOL;
}
}
}
return $info;
}
public function refreshInfo( $id ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Bookmarks: illegal ID.' );
return false;
}
$bookmark = self::findById( $id );
if ( $bookmark == false ) {
Debug::info( 'Bookmarks not found.' );
return false;
}
if ( $bookmark->createdBy != App::$activeUser->ID ) {
Debug::info( 'You do not have permission to modify this bookmark.' );
return false;
}
if ( time() < ( $bookmark->refreshedAt + ( 60 * 10 ) ) ) {
Debug::info( 'You may only fetch bookmarks once every 10 minutes.' );
return false;
}
$info = $this->retrieveInfo( $bookmark->url );
$fields = [
// 'refreshedAt' => time(),
];
if ( empty( $bookmark->title ) && ! empty( $info['title'] ) ) {
$fields['title'] = $info['title'];
}
if ( empty( $bookmark->description ) && ! empty( $info['description'] ) ) {
$fields['description'] = $info['description'];
}
if ( ( empty( $bookmark->icon ) || ! $this->isValidImageUrl( $bookmark->icon ) ) && ! empty( $info['favicon'] ) ) {
$fields['icon'] = $info['favicon'];
}
$fields['meta'] = json_encode( $info['metadata'] );
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'bookmarkUpdate' );
Debug::error( "Bookmarks: $id not updated" );
return false;
}
return true;
}
private function getBaseUrl ($url ) {
$parsedUrl = parse_url($url);
if (isset($parsedUrl['scheme']) && isset($parsedUrl['host'])) {
return $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . '/';
} else {
return null; // URL is not valid or cannot be parsed
}
}
function isValidImageUrl($url) {
$headers = @get_headers($url);
if ($headers && strpos($headers[0], '200') !== false) {
return true;
// Further check to ensure it's an image
foreach ($headers as $header) {
if (strpos(strtolower($header), 'content-type:') !== false) {
if (strpos(strtolower($header), 'image/') !== false) {
return true;
}
}
}
}
return false;
}
}

View File

@ -1,149 +0,0 @@
<?php
/**
* app/plugins/bookmarks/models/bookmarkViews.php
*
* This class is used for the manipulation of the bookmark_views database table.
*
* @package TP Bookmarks
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Bedrock\Classes\CustomException;
class Bookmarkviews extends DatabaseModel {
public $tableName = 'bookmark_views';
public $databaseMatrix = [
[ 'title', 'varchar', '256' ],
[ 'description', 'text', '' ],
[ 'privacy', 'varchar', '48' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
[ 'updatedAt', 'int', '11' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $title, $description = '', $privacy = 'private' ) {
if ( ! Check::dataTitle( $title ) ) {
Debug::info( 'Views: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'privacy' => $privacy,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( 'viewCreate' );
Debug::error( "Views: not created " . var_export($fields,true) );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $description = '', $privacy = 'private' ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Views: illegal ID.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'Views: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'privacy' => $privacy,
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'viewUpdate' );
Debug::error( "Views: $id not updated: $fields" );
return false;
}
return true;
}
public function byUser( $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
if ( empty( $limit ) ) {
$views = self::$db->get( $this->tableName, $whereClause );
} else {
$views = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$views->count() ) {
Debug::info( 'No Views found.' );
return false;
}
return $this->filter( $views->results() );
}
public function getName( $id ) {
$views = self::findById( $id );
if (false == $views) {
return 'unknown';
}
return $views->title;
}
public function simpleByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$views = self::$db->get( $this->tableName, $whereClause );
if ( !$views->count() ) {
Debug::warn( 'Could not find any Views' );
return false;
}
$views = $views->results();
$out = [];
foreach ( $views as $view ) {
$out[ $view->title ] = $view->ID;
}
return $out;
}
public function simpleObjectByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$views = self::$db->get( $this->tableName, $whereClause );
if ( !$views->count() ) {
Debug::warn( 'Could not find any Views' );
return false;
}
$views = $views->results();
$out = [];
foreach ( $views as $view ) {
$obj = new \stdClass();
$obj->title = $view->title;
$obj->ID = $view->ID;
$out[] = $obj;
}
return $out;
}
}

View File

@ -1,157 +0,0 @@
<?php
/**
* app/plugins/bookmarks/models/folders.php
*
* This class is used for the manipulation of the folders database table.
*
* @package TP Bookmarks
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Bedrock\Classes\CustomException;
class Folders extends DatabaseModel {
public $tableName = 'folders';
public $databaseMatrix = [
[ 'title', 'varchar', '256' ],
[ 'color', 'varchar', '48' ],
[ 'privacy', 'varchar', '48' ],
[ 'description', 'text', '' ],
[ 'folderID', 'int', '11' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $title, $folderID = 0, $description = '', $color = 'default', $privacy = 'private' ) {
if ( ! Check::dataTitle( $title ) ) {
Debug::info( 'Folders: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'color' => $color,
'privacy' => $privacy,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( !empty( $folderID ) ) {
$fields['folderID'] = $folderID;
}
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( 'folderCreate' );
Debug::error( "Folders: not created " . var_export($fields,true) );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $folderID = 0, $description = '', $color = 'default', $privacy = 'private' ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Folders: illegal ID.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'Folders: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'color' => $color,
'privacy' => $privacy,
];
if ( !empty( $folderID ) ) {
$fields['folderID'] = $folderID;
}
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'folderUpdate' );
Debug::error( "Folders: $id not updated: $fields" );
return false;
}
return true;
}
public function byUser( $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
if ( empty( $limit ) ) {
$folders = self::$db->get( $this->tableName, $whereClause );
} else {
$folders = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$folders->count() ) {
Debug::info( 'No Folders found.' );
return false;
}
return $this->filter( $folders->results() );
}
public function getName( $id ) {
$folder = self::findById( $id );
if (false == $folder) {
return 'unknown';
}
return $folder->title;
}
public function getColor( $id ) {
$folder = self::findById( $id );
if (false == $folder) {
return 'default';
}
return $folder->color;
}
public function simpleByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$folders = self::$db->get( $this->tableName, $whereClause );
if ( !$folders->count() ) {
Debug::warn( 'Could not find any folders' );
return false;
}
$folders = $folders->results();
$out = [];
$out[ 'No Folder' ] = 0;
foreach ( $folders as $folder ) {
$out[ $folder->title ] = $folder->ID;
}
return $out;
}
public function simpleObjectByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$folders = self::$db->get( $this->tableName, $whereClause );
if ( !$folders->count() ) {
Debug::warn( 'Could not find any folders' );
return false;
}
$folders = $folders->results();
$out = [];
foreach ( $folders as $folder ) {
$obj = new \stdClass();
$obj->title = $folder->title;
$obj->ID = $folder->ID;
$out[] = $obj;
}
return $out;
}
}

View File

@ -1,50 +0,0 @@
<?php
/**
* app/plugins/bookmarks/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Bookmarks
* @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;
use TheTempusProject\Models\Events;
use TheTempusProject\Models\Bookmarks as Bookmark;
use TheTempusProject\Models\Folders;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
class Bookmarks extends Plugin {
public $pluginName = 'TP Bookmarks';
public $configName = 'bookmarks';
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 bookmark system.';
public $permissionMatrix = [
'useBookmarks' => [
'pretty' => 'Can use the bookmarks feature',
'default' => false,
],
];
public $main_links = [
[
'text' => 'Bookmarks',
'url' => '{ROOT_URL}bookmarks/index',
'filter' => 'loggedin',
],
];
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Bookmarks.',
'default' => true,
],
];
}

View File

@ -1,45 +0,0 @@
<legend>Create Bookmark</legend>
{BookmarkBreadCrumbs}
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title">
</div>
</div>
<div class="form-group">
<label for="url" class="col-lg-3 control-label">URL:</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="url" id="url">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description:</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description"></textarea>
</div>
</div>
{folderSelect}
<div class="form-group">
<label for="privacy" class="col-lg-3 control-label">Privacy</label>
<div class="col-lg-3 select-container" id="colorContainer">
<select id="privacy" name="privacy" class="form-control custom-select">
<option value="private">Private</option>
<option value="public">Public</option>
</select>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,45 +0,0 @@
<legend>Edit Bookmark</legend>
{BookmarkBreadCrumbs}
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<label for="url" class="col-lg-3 control-label">URL:</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="url" id="url" value="{url}">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description:</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description">{description}</textarea>
</div>
</div>
{folderSelect}
<div class="form-group">
<label for="privacy" class="col-lg-3 control-label">Privacy</label>
<div class="col-lg-3 select-container" id="colorContainer">
<select id="privacy" name="privacy" class="form-control custom-select" value="{privacy}">
<option value="private">Private</option>
<option value="public">Public</option>
</select>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,37 +0,0 @@
{BookmarkBreadCrumbs}
<table class="table table-striped">
<thead>
<tr>
<th style="width: 75%">Bookmark</th>
<th style="width: 10%"></th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td style="text-align: center;">
<a href="{url}" role="button">
{title}
</a>
</td>
<td style="text-align: center;">
{privacy}
</td>
<td><a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a></td>
<td><a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td style="text-align: center;" colspan="6">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}bookmarks/createBookmark" class="btn btn-sm btn-primary" role="button">Create</a>

View File

@ -1,84 +0,0 @@
{BookmarkBreadCrumbs}<br />
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-{color}">
<div class="panel-heading">
<h3 class="panel-title">Bookmark</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="left" width="200"><b>Title</b></td>
<td align="right">{title}</td>
</tr>
<tr>
<td align="left" width="200"><b>URL</b></td>
<td align="right">{url}</td>
</tr>
<tr>
<td align="left" width="200"><b>Type</b></td>
<td align="right">{linkType}</td>
</tr>
<tr>
<td align="left" width="200"><b>Privacy</b></td>
<td align="right">{privacy}</td>
</tr>
<tr>
<td align="left" width="200"><b>Color</b></td>
<td align="right">{color}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Description</b></td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Icon</b></td>
</tr>
<tr>
<td colspan="2">{iconHtml}</td>
</tr>
<tr>
<td colspan="2">{icon}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Meta</b></td>
</tr>
<tr>
<td colspan="2">{meta}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<td><b>Archived</b></td>
<td align="right">{DTC}{archivedAt}{/DTC}</td>
</tr>
<tr>
<td><b>Hidden</b></td>
<td align="right">{DTC}{hiddenAt}{/DTC}</td>
</tr>
<tr>
<td><b>Last Refreshed</b></td>
<td align="right">{DTC}{refreshedAt}{/DTC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
{refreshBtn}
{hideBtn}
{archiveBtn}
<a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</div>
</div>
</div>
</div>

View File

@ -1,21 +0,0 @@
<div class="panel panel-{color}">
<div class="panel-heading" data-target="#Collapse{ID}" data-toggle="collapse" aria-expanded="true" aria-controls="#Collapse{ID}">
{title}
</div>
<div id="Collapse{ID}" class="panel-collapse collapse in" style="width:100%; position: relative;" role="tabpanel" aria-expanded="true">
<div class="panel-body">
<ul class="list-group">
{bookmarkListRows}
</ul>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}bookmarks/createBookmark/{ID}" class="btn btn-sm btn-success" role="button"><i class="glyphicon glyphicon-plus-sign"></i></a></td>
<span class="pull-right">
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-th-list"></i></a>
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-info-sign"></i></a></td>
<a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<a href="{ROOT_URL}bookmarks/deleteFolder/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</span>
</div>
</div>
</div>

View File

@ -1,18 +0,0 @@
{LOOP}
<li class="list-group-item list-group-item-{color}">
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm" role="button">{iconHtml}</a>
<a href="{url}" class="list-group"> {title}</a>
<span class="pull-right">
{hideBtn}
{archiveBtn}
<a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</span>
</li>
{/LOOP}
{ALT}
<li class="list-group-item">
<a href="#" class="list-group">No Bookmarks</a>
</li>
{/ALT}

View File

@ -1,10 +0,0 @@
{LOOP}
<div class="col-xlg-6 col-lg-6 col-md-6 col-sm-6 col-xs-6">
{panel}
</div>
{/LOOP}
{ALT}
<div class="col-xlg-6 col-lg-6 col-md-6 col-sm-6 col-xs-6">
<p>no folders</p>
</div>
{/ALT}

View File

@ -1,16 +0,0 @@
{BookmarkBreadCrumbs}
<div class="row">
<div class="col-xlg-6 col-lg-6 col-md-6 col-sm-6 col-xs-6">
<legend>Unsorted Bookmarks</legend>
{bookmarksList}
</div>
<div class="col-xlg-6 col-lg-6 col-md-6 col-sm-6 col-xs-6">
<legend>Folders List</legend>
{foldersList}
</div>
</div>
<legend>Folders</legend>
<div class="row">
{folderPanels}
</div>

View File

@ -1,39 +0,0 @@
<legend>Create Folder</legend>
{BookmarkBreadCrumbs}
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description:</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description"></textarea>
</div>
</div>
{folderSelect}
<div class="form-group">
<label for="privacy" class="col-lg-3 control-label">Privacy</label>
<div class="col-lg-3 select-container" id="colorContainer">
<select id="privacy" name="privacy" class="form-control custom-select">
<option value="private">Private</option>
<option value="public">Public</option>
</select>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,39 +0,0 @@
<legend>Edit Folder</legend>
{BookmarkBreadCrumbs}
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description:</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description">{description}</textarea>
</div>
</div>
{folderSelect}
<div class="form-group">
<label for="privacy" class="col-lg-3 control-label">Privacy</label>
<div class="col-lg-3 select-container" id="colorContainer">
<select id="privacy" name="privacy" class="form-control custom-select" value="{privacy}">
<option value="private">Private</option>
<option value="public">Public</option>
</select>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,33 +0,0 @@
{BookmarkBreadCrumbs}
<table class="table table-striped">
<thead>
<tr>
<th style="width: 35%">Title</th>
<th style="width: 20%">Privacy</th>
<th style="width: 30%">Description</th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td align="center">{title}</td>
<td align="center">{privacy}</td>
<td>{description}</td>
<td><a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-info-sign"></i></a></td>
<td><a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}bookmarks/deleteFolder/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="7">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}bookmarks/createFolder" class="btn btn-sm btn-primary" role="button">Create</a>

View File

@ -1,47 +0,0 @@
{BookmarkBreadCrumbs}<br />
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-{color}">
<div class="panel-heading">
<h3 class="panel-title">Bookmark Folder</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="left" width="200"><b>Title</b></td>
<td align="right">{title}</td>
</tr>
<tr>
<td align="left" width="200"><b>Privacy</b></td>
<td align="right">{privacy}</td>
</tr>
<tr>
<td align="left" width="200"><b>Color</b></td>
<td align="right">{color}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Description</b></td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-th-list"></i></a>
<a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}bookmarks/deleteFolder/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</div>
</div>
</div>
</div>

View File

@ -1,5 +0,0 @@
<ul class="nav nav-tabs">
<li><a href="{ROOT_URL}bookmarks/index/">Dashboard</a></li>
<li><a href="{ROOT_URL}bookmarks/folders/">Folders</a></li>
</ul>
{userFolderTabs}

View File

@ -1,9 +0,0 @@
<ul class="nav nav-tabs">
<li><a href="{ROOT_URL}bookmarks/folders/">All</a></li>
{LOOP}
<li><a href="{ROOT_URL}bookmarks/bookmarks/{ID}">{title}</a></li>
{/LOOP}
{ALT}
<li></li>
{/ALT}
</ul>

View File

@ -1,7 +0,0 @@
<?php
# bugTracker
define( 'TRACKER_STATUS_IN_PROGRESS', 'In Progress' );
define( 'TRACKER_STATUS_TESTING', 'Needs Testing' );
define( 'TRACKER_STATUS_NEW', 'New' );
define( 'TRACKER_STATUS_FIXED', 'Fixed' );
define( 'TRACKER_STATUS_CLOSED', 'Closed' );

View File

@ -1,206 +0,0 @@
<?php
/**
* app/plugins/bugtracker/controllers/admin/bugtracker.php
*
* This is the Bug-Tracker admin controller.
*
* @package TP BugTracker
* @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\Upload;
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\Houdini\Classes\Forms as FormGen;
use TheTempusProject\Classes\AdminController;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Models\Bugtracker as BugtrackerModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Models\Comments;
class Bugtracker extends AdminController {
protected static $tracker;
protected static $comments;
public function __construct() {
parent::__construct();
self::$tracker = new BugtrackerModel;
self::$title = 'Admin - Bug-Tracker';
$view = Navigation::activePageSelect( 'nav.admin', '/admin/bugtracker' );
Components::set( 'ADMINNAV', $view );
}
public function index( $data = null ) {
Views::view( 'bugtracker.admin.list', self::$tracker->list() );
}
public function create( $data = null ) {
$form = '';
$form .= FormGen::getFormFieldHtml( 'title', 'Title', 'text', Input::post('title') );
$form .= FormGen::getFormFieldHtml( 'description', 'Description', 'block', Input::post('description') );
$form .= FormGen::getFormFieldHtml( 'bugImage', 'Screenshot', 'file', '' );
$form .= FormGen::getFormFieldHtml( 'url', 'URL (if possible)', 'text', '' );
$form .= FormGen::getFormFieldHtml( 'repeat', 'Has this happened more than once', 'radio' );
Components::set( 'TRACKER_FORM', $form );
if ( !Input::exists( 'submit' ) ) {
return Views::view( 'bugtracker.admin.create' );
}
if ( !Forms::check( 'newBugTracker' ) ) {
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
return Views::view( 'bugtracker.admin.create' );
}
$image = '';
if ( Input::exists( 'bugImage' ) ) {
$folder = IMAGE_UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( !Upload::image( 'bugImage', $folder ) ) {
Issues::add( 'error', [ 'There was an error with your upload.' => Check::systemErrors() ] );
} else {
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$image = $route . Upload::last();
}
}
$result = self::$tracker->create(
App::$activeUser->ID,
Input::post( 'url' ),
$image,
Input::post( 'repeat' ),
Input::post( 'description' ),
Input::post( 'title' ),
);
if ( $result ) {
Issues::add( 'success', 'Your tracker 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' ) ) {
$bug = self::$tracker->findById( $data );
$statusList = [
TRACKER_STATUS_IN_PROGRESS => TRACKER_STATUS_IN_PROGRESS,
TRACKER_STATUS_TESTING => TRACKER_STATUS_TESTING,
TRACKER_STATUS_NEW => TRACKER_STATUS_NEW,
TRACKER_STATUS_FIXED => TRACKER_STATUS_FIXED,
TRACKER_STATUS_CLOSED => TRACKER_STATUS_CLOSED,
];
$form = '';
$form .= FormGen::getFormFieldHtml( 'title', 'Title', 'text', $bug->title );
$form .= FormGen::getFormFieldHtml( 'description', 'Description', 'block', $bug->description );
$form .= FormGen::getFormFieldHtml( 'bugImage', 'Screenshot', 'file', $bug->image );
$form .= FormGen::getFormFieldHtml( 'url', 'Page you were on', 'text', $bug->url );
$form .= FormGen::getFormFieldHtml( 'repeat', 'Has this happened more than once', 'radio', $bug->repeatable );
$form .= FormGen::getFormFieldHtml( 'status', 'Status', 'customSelect', $bug->status, $statusList );
Components::set( 'TRACKER_FORM', $form );
return Views::view( 'bugtracker.admin.edit', $bug );
}
if ( !Forms::check( 'editBugTracker' ) ) {
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
return $this->index();
}
$image = '';
if ( Input::exists( 'bugImage' ) ) {
$folder = IMAGE_UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( !Upload::image( 'bugImage', $folder ) ) {
Issues::add( 'error', [ 'There was an error with your upload.' => Check::systemErrors() ] );
} else {
$image = $folder . Upload::last();
}
}
$tracker = self::$tracker->updateTracker(
$data,
Input::post( 'url' ),
$image,
Input::post( 'repeat' ),
Input::post( 'description' ),
Input::post( 'title' ),
Input::post( 'status' )
);
if ( true === $tracker ) {
Issues::add( 'success', 'Tracker Updated.' );
return $this->index();
}
Issues::add( 'error', 'There was an error with your request.' );
$this->index();
}
public function view( $id = null ) {
if ( empty( self::$comments ) ) {
self::$comments = new Comments;
}
$data = self::$tracker->findById( $id );
if ( $data === false ) {
Issues::add( 'error', 'Tracker not found.' );
return $this->index();
}
if ( Input::exists( 'contentId' ) ) {
$this->comments( 'post', Input::post( 'contentId' ) );
}
if ( empty( self::$comments ) ) {
self::$comments = new Comments;
}
Components::set( 'CONTENT_ID', $id );
Components::set( 'COMMENT_TYPE', 'admin/bugtracker' );
Components::set( 'count', self::$comments->count( 'tracker', $id ) );
Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
Components::set( 'COMMENTS', Views::simpleView( 'comments.list', self::$comments->display( 10, 'tracker', $id ) ) );
Views::view( 'bugtracker.admin.view', $data );
}
public function delete( $data = null ) {
if ( $data == null ) {
if ( Input::exists( 'T_' ) ) {
$data = Input::post( 'T_' );
}
}
if ( !self::$tracker->delete( $data ) ) {
Issues::add( 'error', 'There was an error with your request.' );
} else {
Issues::add( 'success', 'Tracker has been deleted' );
}
$this->index();
}
public function comments( $sub = null, $data = null ) {
if ( empty( self::$comments ) ) {
self::$comments = new Comments;
}
if ( empty( $sub ) || empty( $data ) ) {
Session::flash( 'error', 'Whoops, try again.' );
Redirect::to( 'admin/bugtracker' );
}
switch ( $sub ) {
case 'post':
$content = self::$tracker->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Post.' );
Redirect::to( 'admin/bugtracker' );
}
return self::$comments->formPost( 'tracker', $content, 'admin/bugtracker/view/' );
case 'edit':
$content = self::$comments->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Comment.' );
Redirect::to( 'admin/bugtracker' );
}
return self::$comments->formEdit( 'tracker', $content, 'admin/bugtracker/view/' );
case 'delete':
$content = self::$comments->findById( $data );
if ( empty( $content ) ) {
Session::flash( 'error', 'Unknown Comment.' );
Redirect::to( 'admin/bugtracker' );
}
return self::$comments->formDelete( 'tracker', $content, 'admin/bugtracker/view/' );
}
}
}

View File

@ -1,92 +0,0 @@
<?php
/**
* app/plugins/bugtracker/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP BugTracker
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Bugreport;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Classes\Forms;
class BugTrackerForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'newBugTracker', __CLASS__, 'newBugTracker' );
self::addHandler( 'editBugTracker', __CLASS__, 'editBugTracker' );
}
/**
* Validates the bug tracker create form.
*
* @return {bool}
*/
public static function newBugTracker() {
if ( !empty(Input::post( 'url' )) && !self::url( Input::post( 'url' ) ) ) {
self::addUserError( 'Invalid url: <code>' . Input::post( 'url' ) . '</code>' );
return false;
}
if ( !self::tf( Input::post( 'repeat' ) ) ) {
self::addUserError( 'Invalid repeat value: ' . Input::post( 'repeat' ) );
return false;
}
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( 'description' ) ) {
self::addUserError( 'You must specify a description' );
return false;
}
// if ( !self::token() ) {
// return false;
// }
return true;
}
/**
* Validates the bug tracker create form.
*
* @return {bool}
*/
public static function editBugTracker() {
if ( !empty(Input::post( 'url' )) && !self::url( Input::post( 'url' ) ) ) {
self::addUserError( 'Invalid url.' . Input::post( 'url' ) );
return false;
}
if ( !self::tf( Input::post( 'repeat' ) ) ) {
self::addUserError( 'Invalid repeat value.' );
return false;
}
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( 'description' ) ) {
self::addUserError( 'You must specify a description' );
return false;
}
// if ( !self::token() ) {
// return false;
// }
return true;
}
}
new BugTrackerForms;

View File

@ -1,142 +0,0 @@
<?php
/**
* app/plugins/bugtracker/models/bugtracker.php
*
* This class is used for the manipulation of the bugs database table.
*
* @package TP BugTracker
* @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\Bin\Canary as Debug;
use TheTempusProject\Bedrock\Classes\CustomException;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Plugins\Bugtracker as Plugin;
class Bugtracker extends DatabaseModel {
public $tableName = 'bugs';
public $databaseMatrix = [
[ 'userID', 'int', '11' ],
[ 'time', 'int', '10' ],
[ 'title', 'varchar', '128' ],
[ 'status', 'varchar', '64' ],
[ 'description', 'text', '' ],
[ 'image', 'varchar', '256' ],
[ 'repeatable', 'varchar', '5' ],
[ 'url', 'varchar', '256' ],
];
public $plugin;
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
$this->plugin = new Plugin;
}
/**
* This function parses the bug reports description and
* separates it into separate keys in the array.
*
* @param array $data - The data being parsed.
*
* @return array
*/
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
$instance->submittedBy = self::$user->getUsername( $instance->userID );
$instance->repeatText = ( 'false' == $instance->repeatable ? 'no' : 'yes' );
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
/**
* Logs a Bug Report 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( $ID, $url, $image, $repeat, $description, $title ) {
if ( !$this->plugin->checkEnabled() ) {
Debug::info( 'Bug Tracking is disabled in the config.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'bugTracker: illegal title.' );
return false;
}
$fields = [
'userID' => App::$activeUser->ID,
'time' => time(),
'title' => $title,
'status' => TRACKER_STATUS_NEW,
'description' => $description,
'image' => $image,
'repeatable' => $repeat,
'url' => $url,
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( $this->tableName );
return false;
}
return self::$db->lastId();
}
public function updateTracker( $ID, $url, $image, $repeat, $description, $title, $status ) {
if ( !$this->plugin->checkEnabled() ) {
Debug::info( 'Bug Tracking is disabled in the config.' );
return false;
}
if ( empty( self::$log ) ) {
self::$log = new Log;
}
if ( !Check::id( $ID ) ) {
Debug::info( 'bugTracker: illegal ID.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'bugTracker: illegal title.' );
return false;
}
$fields = [
'url' => $url,
'description' => $description,
'repeatable' => $repeat,
'title' => $title,
'status' => $status,
];
if ( !empty( $image ) ) {
$fields['image'] = $image;
}
if ( !self::$db->update( $this->tableName, $ID, $fields ) ) {
new CustomException( $this->tableName );
Debug::error( "Tracker Post: $ID not updated: $fields" );
return false;
}
self::$log->admin( "Updated Tracker Post: $ID" );
return true;
}
}

View File

@ -1,48 +0,0 @@
<?php
/**
* app/plugins/bugtracker/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP BugTracker
* @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 Bugtracker extends Plugin {
public $pluginName = 'TP BugTracker';
public $pluginAuthor = 'JoeyK';
public $pluginWebsite = 'https://TheTempusProject.com';
public $modelVersion = '1.0';
public $pluginVersion = '3.0';
public $pluginDescription = '';
public $configName = 'bugtracker';
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Bug tracking.',
'default' => true,
],
];
public $permissionMatrix = [
'bugTrack' => [
'pretty' => 'Can Track Bugs',
'default' => false,
],
];
public $admin_links = [
[
'text' => '<i class="fa fa-fw fa-bug"></i> Bug Tracker',
'url' => '{ROOT_URL}admin/bugtracker',
],
];
}

View File

@ -1,6 +0,0 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
<legend>Create Bug Tracker</legend>
{TRACKER_FORM}
<input type="hidden" name="token" value="{TOKEN}">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Save</button><br>
</form>

View File

@ -1,30 +0,0 @@
<legend>New Bugs</legend>
<table class="table table-striped">
<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>{description}</td>
<td><a href="{ROOT_URL}admin/bugtracker/view/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a></td>
<td><a href="{ROOT_URL}admin/bugtracker/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td width="30px"><a href="{ROOT_URL}admin/bugtracker/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="5">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>

View File

@ -1,6 +0,0 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
<legend>Edit Bug Tracker</legend>
{TRACKER_FORM}
<input type="hidden" name="token" value="{TOKEN}">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Save</button><br>
</form>

View File

@ -1,45 +0,0 @@
<legend>Bug Trackers</legend>
{PAGINATION}
<form action="{ROOT_URL}admin/bugtracker/delete" method="post">
<table class="table table-striped">
<thead>
<tr>
<th style="width: 30%">Title</th>
<th style="width: 40%">Description</th>
<th style="width: 10%">Status</th>
<th style="width: 5%">Time Submitted</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="T_[]"/>
</th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td><a href="{ROOT_URL}admin/bugtracker/view/{ID}">{title}</a></td>
<td>{description}</td>
<td>{status}</td>
<td align="center">{DTC}{time}{/DTC}</td>
<td><a href="{ROOT_URL}admin/bugtracker/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}admin/bugtracker/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="T_[]">
</td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="7">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger">Delete</button>
<a href="{ROOT_URL}admin/bugtracker/create" class="btn btn-sm btn-primary" role="button">Create</a>
</form>
<br />
<a href="{ROOT_URL}admin/bugtracker/clear">clear all</a>

View File

@ -1,74 +0,0 @@
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xs-offset-0 col-sm-offset-0 col-md-offset-3 col-lg-offset-3 top-pad" >
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Bug Tracker</h3>
</div>
<div class="panel-body">
<div class="row">
<div class=" col-md-12 col-lg-12 ">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="center" colspan="2">Title</td>
</tr>
<tr>
<td colspan="2">{title}</td>
</tr>
<tr>
<td align="center" colspan="2">Description</td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
</tr>
<tr>
<td colspan="2">
<div align="center">
<a href="{ROOT_URL}{image}"><img alt="Screenshot" src="{ROOT_URL}{image}" class="img-responsive"></a>
</div>
</td>
</tr>
<tr>
<td align="left" width="200">Status</td>
<td align="right">{status}</td>
</tr>
<tr>
<td align="left" width="200">ID</td>
<td align="right">{ID}</td>
</tr>
<tr>
<td>Time submitted</td>
<td align="right">{DTC}{time}{/DTC}</td>
</tr>
<tr>
<td>Submitted by</td>
<td align="right"><a href="{ROOT_URL}admin/users/view/{userID}">{submittedBy}</a></td>
</tr>
<tr>
<td>URL:</td>
<td align="right">{URL}</td>
</tr>
<tr>
<td>Multiple occurrences?</td>
<td align="right">{repeatText}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
{ADMIN}
<a href="{ROOT_URL}admin/bugtracker/delete/{ID}" class="btn btn-md btn-danger" role="button">Delete</a>
<a href="{ROOT_URL}admin/bugtracker/edit/{ID}" class="btn btn-md btn-warning" role="button">Edit</a>
<a href="{ROOT_URL}admin/comments/tracker/{ID}" class="btn btn-md btn-primary" role="button">View Comments</a>
{/ADMIN}
</div>
</div>
{COMMENTS}
{NEWCOMMENT}
</div>
</div>
</div>

View File

@ -1,593 +0,0 @@
<?php
/**
* app/plugins/calendar/controllers/calendar.php
*
* This is the calendar controller.
*
* @package TP Calendar
* @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\Bedrock\Functions\Session;
use TheTempusProject\Bedrock\Functions\Date;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Houdini\Classes\Forms as HoudiniForms;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Models\Events;
use TheTempusProject\Models\Calendars;
use TheTempusProject\Houdini\Classes\Navigation;
class Calendar extends Controller {
protected static $events;
protected static $calendars;
protected static $defaultView;
protected static $defaultCalendar;
protected static $selectedDate;
public function __construct() {
if ( !App::$isLoggedIn ) {
Session::flash( 'notice', 'You must be logged in to create or manage calendars.' );
return Redirect::home();
}
parent::__construct();
self::$title = 'Calendar - {SITENAME}';
self::$pageDescription = 'The {SITENAME} calendar is where you can find various features for creating and managing events and schedules.';
self::$events = new Events;
self::$calendars = new Calendars;
$prefs = App::$activePrefs;
if ( ! empty( $prefs['calendarPreference'] ) ) {
$view = $prefs['calendarPreference'];
} else {
Session::flash( 'info', 'You can select a default view for this page in your user settings in the top right.' );
$view = 'byMonth';
}
self::$defaultView = $view;
self::$selectedDate = intval( strtotime( Date::determineDateInput() ) );
Template::setTemplate( 'calendar' );
// Date Dropdown
$dateDropdownView = Views::simpleView('calendar.nav.dateDropdown', Date::getDateBreakdown( self::$selectedDate ) );
Components::set( 'dateDropdown', $dateDropdownView );
// Calendar Top Tabs
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, Input::get( 'url' ), false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
// must be done after dropdown gets added
Components::set( 'CalendarNav', Template::parse( $calendarTabs ) );
}
public function index() {
if ( method_exists( $this, self::$defaultView ) ) {
Redirect::to( 'calendar/'.self::$defaultView.'/' );
} else {
Redirect::to( 'calendar/byMonth/' );
}
}
/**
* Events
*/
public function event( $id = null ) {
$event = $this->findEventOrFail( $id );
Views::view( 'calendar.events.view', $event );
}
public function createEvent() {
// get the url calendar input
$newCalendar = Input::get('calendar_id') ? Input::get('calendar_id') : '';// for loading the form
$calendarSelect = HoudiniForms::getFormFieldHtml( 'calendar_id', 'Calendar', 'select', $newCalendar, self::$calendars->simpleByUser() );
Components::set( 'calendarSelect', $calendarSelect );
$repeatSelect = HoudiniForms::getFormFieldHtml( 'repeats', 'Frequency', 'select', 'none', self::$events->repeatOptions );
Components::set( 'repeatSelect', $repeatSelect );
$dateSelect = Views::simpleView( 'calendar.dateSelect', $this->getEventBreakdown( self::$selectedDate ) );
Components::set( 'dateSelect', $dateSelect );
if ( ! Input::exists() ) {
return Views::view( 'calendar.events.create' );
}
/** Attempt to save the form data somewhat */
// get the form calendar input
$calendarID = Input::post('calendar_id') ? Input::post('calendar_id') : ''; // for submitting the form
$calendarSelect = HoudiniForms::getFormFieldHtml( 'calendar_id', 'Calendar', 'select', $calendarID, self::$calendars->simpleByUser() );
Components::set( 'calendarSelect', $calendarSelect );
$repeatSelect = HoudiniForms::getFormFieldHtml( 'repeats', 'Frequency', 'select', Input::post('repeats'), self::$events->repeatOptions );
Components::set( 'repeatSelect', $repeatSelect );
if ( ! Forms::check( 'createEvent' ) ) {
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
return Views::view( 'calendar.events.create' );
}
$event_id = self::$events->create(
$calendarID,
Input::post('title'),
Date::applyUtcToDate( Date::determineDateInput() ),
Input::post('description'),
Input::post('location'),
Input::post('repeats'),
Input::post('color'),
0,
);
if ( ! $event_id ) {
Issues::add( 'error', [ 'There was an error creating your event.' => Check::userErrors() ] );
return Views::view( 'calendar.events.create' );
}
if ( Input::post('repeats') != 'none' ) {
$childrens = self::$events->generateChildEvents( $event_id, true, true );
}
Session::flash( 'success', 'Your Event has been created.' );
Redirect::to( 'calendar/'.self::$defaultView.'/'. $calendarID );
}
public function editEvent( $id = null ) {
$calendar = Input::exists('calendar_id') ? Input::post('calendar_id') : '';
$event = self::$events->findById( $id );
$readableDate = date('Y-m-d H:i:s', $event->event_time);
if ( $event == false ) {
Session::flash( 'error', 'Event not found.' );
return Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
if ( $event->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'You do not have permission to modify this event.' );
return Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
$repeatSelect = HoudiniForms::getFormFieldHtml( 'repeats', 'Frequency', 'select', $event->repeats, self::$events->repeatOptions );
Components::set( 'repeatSelect', $repeatSelect );
// there should be a way to do this natively
$event_at = Date::getDateBreakdown( $event->event_time, true );
// $readableDate = date('Y-m-d H:i:s', $readableDate);
$dateSelect = Views::simpleView( 'calendar.dateSelect', $event_at );
Components::set( 'dateSelect', $dateSelect );
Components::set( 'color', $event->color );
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'calendar.events.edit', $event );
}
if ( ! Forms::check( 'editEvent' ) ) {
Issues::add( 'error', [ 'There was an error updating your event.' => Check::userErrors() ] );
return Views::view( 'calendar.events.edit', $event );
}
$result = self::$events->update(
$id,
Input::post('title'),
Date::applyUtcToDate( Date::determineDateInput() ),
Input::post('description'),
Input::post('location'),
Input::post('repeats'),
Input::post('color'),
);
if ( Input::post('repeats') != 'none' && $event->parent_id == 0 ) {
$childrens = self::$events->generateChildEvents( $event->ID, true, true );
}
if ( ! $result ) {
Issues::add( 'error', [ 'There was an error updating your event.' => Check::userErrors() ] );
return Views::view( 'calendar.events.edit', $event->ID );
}
Session::flash( 'success', 'Your Event has been updated.' );
Redirect::to( 'calendar/'.self::$defaultView.'/'. $event->calendar_id );
}
public function deleteEvent( $id = null ) {
$event = self::$events->findById( $id );
if ( $event == false ) {
Issues::add( 'error', 'Event not found.' );
return $this->index();
}
if ( $event->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this event.' );
return $this->index();
}
$result = self::$events->delete( $id );
if ( !$result ) {
Session::flash( 'error', 'There was an error deleting the event(s)' );
} else {
Session::flash( 'success', 'Event deleted' );
}
return $this->index();
}
/**
* Calendars
*/
public function calendar( $id = null ) {
$calendar = $this->findCalendarOrFail( $id );
Views::view( 'calendar.calendar.view', $calendar );
}
public function createCalendar() {
$timezoneSelect = HoudiniForms::getTimezoneHtml( Date::getTimezone() );
Components::set( 'timezoneSelect', $timezoneSelect );
if ( ! Input::exists() ) {
return Views::view( 'calendar.calendar.create' );
}
if ( ! Forms::check( 'createCalendar' ) ) {
Issues::add( 'error', [ 'There was an error creating your calendar111.' => Check::userErrors() ] );
return Views::view( 'calendar.calendar.create' );
}
$calendar = self::$calendars->create( Input::post('title'), Input::post('description'), Input::post('timezone'), Input::post('color') );
if ( ! $calendar ) {
return Views::view( 'calendar.calendar.create' );
}
Session::flash( 'success', 'Your Calendar has been created.' );
Redirect::to( 'calendar/'.self::$defaultView.'/');
}
public function editCalendar( $id = null ) {
$calendar = $this->findCalendarOrFail( $id );
$timezoneSelect = HoudiniForms::getTimezoneHtml( Date::getTimezone() );
Components::set( 'timezoneSelect', $timezoneSelect );
Components::set( 'color', $calendar->color );
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'calendar.calendar.edit', $calendar );
}
if ( !Forms::check( 'editCalendar' ) ) {
Issues::add( 'error', [ 'There was an error editing your calendar.' => Check::userErrors() ] );
return Views::view( 'calendar.calendar.edit', $calendar );
}
$result = self::$calendars->update( $id, Input::post('title'), Input::post('description'), Input::post('timezone'), Input::post('color') );
if ( !$result ) {
Issues::add( 'error', [ 'There was an error updating your calendar.' => Check::userErrors() ] );
return Views::view( 'calendar.calendar.edit', $calendar );
}
Session::flash( 'success', 'Your Calendar has been updated.' );
Redirect::to( 'calendar/'.self::$defaultView.'/'. $calendar->ID );
}
public function deleteCalendar( $id = null ) {
$calendar = self::$calendars->findById( $id );
if ( $calendar == false ) {
Issues::add( 'error', 'Calendar not found.' );
return $this->index();
}
if ( $calendar->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this calendar.' );
return $this->index();
}
$results = self::$events->deleteByCalendar( $id );
$result = self::$calendars->delete( $id );
if ( !$result ) {
Session::flash( 'error', 'There was an error deleting the calendar(s)' );
} else {
Session::flash( 'success', 'Calendar deleted' );
}
Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
/**
* Views
*/
public function byDay( $id = 0 ) {
// Set base components
Components::set( 'currentView', __FUNCTION__ );
Components::set( 'calendarID', $id );
// Set components for next / back arrows
$nextDayMonth = date( 'M', strtotime( 'tomorrow', self::$selectedDate ) );
$nextDay = date( 'd', strtotime( 'tomorrow', self::$selectedDate ) );
$lastDayMonth = date( 'M', strtotime( 'yesterday', self::$selectedDate ) );
$lastDay = date( 'd', strtotime( 'yesterday', self::$selectedDate ) );
Components::set( 'nextDayMonth', $nextDayMonth );
Components::set( 'nextDay', $nextDay );
Components::set( 'lastDayMonth', $lastDayMonth );
Components::set( 'lastDay', $lastDay );
// Get the data
$dayArray = self::$events->dayArray( $id, self::$selectedDate );
// build the results
$selectedHour = Date::getCurrentHour();
$day = date( 'd', self::$selectedDate );
$month = date( 'M', self::$selectedDate );
$year = date( 'Y', self::$selectedDate );
$fullDay = [];
foreach ( $dayArray as $hour => $events ) {
Components::set( 'hour', $hour );
$hourCell = new \stdClass();
$hourCell->hour = $hour;
if ( $hour == $selectedHour ) {
$hourCell->hourSelected = ' hour-active';
} else {
$hourCell->hourSelected = '';
}
$hourCell->day = $day;
$hourCell->month = $month;
$hourCell->year = $year;
$hourCell->EventCells = Views::simpleView( 'calendar.nav.row', $events );
$fullDay[] = $hourCell;
}
// Calendar Top Tabs
Components::unset( 'calendarDropdown');
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, '/calendar/byDay/', false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
Components::set( 'CalendarNav', Template::parse( $tabsView ) );
Components::set( 'activeDate', date( 'F d, Y', self::$selectedDate ) );
Views::view( 'calendar.byDay', $fullDay );
}
public function byWeek( $id = 0 ) {
// Set base components
Components::set( 'currentView', __FUNCTION__ );
Components::set( 'calendarID', $id );
// Set components for next / back arrows
$lastWeekMonth = date( 'M', strtotime( '-7 days', self::$selectedDate) );
$lastWeekDay = date( 'd', strtotime( '-7 days', self::$selectedDate) );
$lastWeekYear = date( 'Y', strtotime( '-7 days', self::$selectedDate) );
$nextWeekMonth = date( 'M', strtotime( '+7 days', self::$selectedDate) );
$nextWeekDay = date( 'd', strtotime( '+7 days', self::$selectedDate) );
$nextWeekYear = date( 'Y', strtotime( '+7 days', self::$selectedDate) );
Components::set( 'lastWeekMonth', $lastWeekMonth );
Components::set( 'lastWeek', $lastWeekDay );
Components::set( 'lastYear', $lastWeekYear );
Components::set( 'nextWeekMonth', $nextWeekMonth );
Components::set( 'nextWeek', $nextWeekDay );
Components::set( 'nextYear', $nextWeekYear );
// Get the data
$weekArray = self::$events->weekArray( $id, self::$selectedDate );
// build the results
$presentMonth = date( 'F', self::$selectedDate );
$presentDay = date( 'd', self::$selectedDate );
foreach ( $weekArray as $week => $days ) {
$weekFormat = [];
foreach ( $days as $day => $events ) {
if ( empty( $first ) ) {
$first = true;
if ( intval( $presentDay ) >= $day ) {
$month = date( 'F', self::$selectedDate );
} else {
$month = date( 'F', strtotime( '-7 days', self::$selectedDate ) );
}
}
if ( $day == '01' ) {
$month = date( 'F', strtotime( '+7 days', self::$selectedDate ) );
}
Components::set( 'currentMonth', $month );
Components::set( 'currentDay', $day );
$dayCell = new \stdClass();
if ( $presentDay == $day && $presentMonth == $month ) {
$dayCell->highlightedDate = ' today';
} else {
$dayCell->highlightedDate = '';
}
$dayCell->day = $day;
$dayCell->dayEventList = Views::simpleView( 'calendar.nav.cell', $events );
$weekFormat[] = $dayCell;
}
$weekView = Views::simpleView( 'calendar.nav.week', $weekFormat );
Components::set( $week . 'Element', $weekView );
}
// Calendar Top Tabs
Components::unset( 'calendarDropdown');
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, '/calendar/byWeek/', false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
Components::set( 'CalendarNav', Template::parse( $tabsView ) );
Components::set( 'dateWeek', 'Week of ' . date( 'F d, Y', self::$selectedDate ) );
Views::view( 'calendar.byWeek' );
}
public function byMonth( $id = 0 ) {
// Set base components
Components::set( 'currentView', __FUNCTION__ );
Components::set( 'calendarID', $id );
// Set components for next / back arrows
$month = date( 'F', strtotime( 'first day of last month', self::$selectedDate ) );
$lastMonthYear = date( 'Y', strtotime( 'first day of last month', self::$selectedDate ) );
$nextMonth = date( 'F', strtotime( 'first day of next month', self::$selectedDate ) );
$nextMonthYear = date( 'Y', strtotime( 'first day of next month', self::$selectedDate ) );
Components::set( 'lastMonth', $month );
Components::set( 'lastMonthYear', $lastMonthYear );
Components::set( 'nextMonth', $nextMonth );
Components::set( 'nextMonthYear', $nextMonthYear );
// Get the data
$monthArray = self::$events->monthArray( $id, self::$selectedDate );
// build the results
$lastMonth = strtotime( 'last month', self::$selectedDate );
$presentMonth = date( 'F', self::$selectedDate );
$presentDay = date( 'd', self::$selectedDate );
foreach ( $monthArray as $week => $days ) {
$weekFormat = [];
foreach ( $days as $day => $events ) {
if ( $day == '01' ) {
$month = date( 'F', strtotime( '+1 month', $lastMonth ) );
$lastMonth = strtotime( '+1 month', $lastMonth) ;
}
Components::set( 'currentMonth', $month );
Components::set( 'currentDay', $day );
$dayCell = new \stdClass();
if ( $presentDay == $day && $presentMonth == $month ) {
$dayCell->highlightedDate = ' today';
} else {
$dayCell->highlightedDate = '';
}
$dayCell->day = $day;
// prevent duplicated entries for previous/next month
if ( $lastMonth < strtotime( 'this month',self::$selectedDate ) || $lastMonth == strtotime( 'next month',self::$selectedDate )) {
$events = [];
}
$dayCell->dayEventList = Views::simpleView( 'calendar.nav.cell', $events );
$weekFormat[] = $dayCell;
}
$weekView = Views::simpleView( 'calendar.nav.week', $weekFormat );
Components::set( $week . 'Element', $weekView );
}
// Calendar Top Tabs
Components::unset( 'calendarDropdown');
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, '/calendar/byMonth/', false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
Components::set( 'CalendarNav', Template::parse( $tabsView ) );
Components::set( 'dateMonth', date( 'F Y', self::$selectedDate ) );
Views::view( 'calendar.byMonth' );
}
public function byYear( $id = 0 ) {
// Set base components
Components::set( 'calendarID', $id );
Components::set( 'currentView', __FUNCTION__ );
// Set components for next / back arrows
$nextYear = date( 'Y', strtotime( 'next year', self::$selectedDate ) );
$lastYear = date( 'Y', strtotime( 'last year', self::$selectedDate ) );
Components::set( 'lastYear', $lastYear );
Components::set( 'nextYear', $nextYear );
// Get the data
$eventList = self::$events->yearArray( $id, self::$selectedDate );
// Calendar Top Tabs
Components::unset( 'calendarDropdown');
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, '/calendar/byYear/', false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
Components::set( 'CalendarNav', Template::parse( $tabsView ) );
Components::set( 'dateYear', date( 'Y', self::$selectedDate ) );
Views::view( 'calendar.byYear', $eventList );
}
public function events( $id = 0 ) {
Components::set( 'currentView', __FUNCTION__ );
Components::set( 'calendarID', $id );
if ( ! empty( $id ) ) {
$calendar = self::$calendars->findById( $id );
if ( $calendar == false ) {
Issues::add( 'error', 'Calendar not found.' );
return $this->index();
}
if ( $calendar->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to view this calendar.' );
return $this->index();
}
Components::set( 'calendarName', $calendar->title );
$eventList = self::$events->findByCalendar( $id );
} else {
$eventList = self::$events->userEvents( 5 );
}
// Calendar Top Tabs
Components::unset( 'calendarDropdown');
$calendarTabs = Views::simpleView('calendar.nav.topTabs');
$tabsView = Navigation::activePageSelect( $calendarTabs, '/calendar/events/', false, true );
// must be done after top-nav gets selected
$calendarDropdown = Views::simpleView('calendar.nav.calendarDropdown', self::$calendars->simpleObjectByUser() );
$calendarDropdownView = Navigation::activePageSelect( $calendarDropdown, Input::get( 'url' ), false, true ); // add notebookID as second param
Components::set( 'calendarDropdown', $calendarDropdownView);
Components::set( 'CalendarNav', Template::parse( $tabsView ) );
Views::view( 'calendar.events.list', $eventList );
}
/**
* Support Functions
*/
private function findCalendarOrFail( $id = null ) {
$calendar = self::$calendars->findById( $id );
if ( $calendar == false ) {
Session::flash( 'error', 'Calendar not found.' );
Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
if ( $calendar->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'Permissions Error.' );
Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
return $calendar;
}
private function findEventOrFail( $id = null ) {
$event = self::$events->findById( $id );
if ( $event == false ) {
Session::flash( 'error', 'Event not found.' );
Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
if ( $event->createdBy != App::$activeUser->ID ) {
Session::flash( 'error', 'Permissions Error.' );
Redirect::to( 'calendar/'.self::$defaultView.'/' );
}
return $event;
}
private function getEventBreakdown( $start_timestamp = 0, $end_timestamp = 0, $with_timezone = false ) {
$out = new \stdClass();
if ( empty( $end_timestamp ) ) {
$out->allDay = 'true';
// $out->date = ;
// $out->time = ;
$out->endDate = $out->date;
$out->endTime = $out->time;
} else {
$out->allDay = 'false';
// $out->date = ;
// $out->time = ;
$out->endDate = $out->date;
$out->endTime = $out->time;
}
$timestamp = intval( $timestamp );
$init = date('Y-m-d H:i:s', $timestamp);
if ( true === $with_timezone ) {
$readableDate = self::applyTimezoneToTimestamp( $timestamp );
} else {
$readableDate = date('Y-m-d H:i:s', $timestamp);
}
$date = date( 'Y-m-d', strtotime( $readableDate ) );
$time = date( 'H:i', strtotime( $readableDate ) );
return [
'time' => $time,
'date' => $date,
];
}
}

View File

@ -1,153 +0,0 @@
.calendar-container {
margin-left: 20px;
margin-right: 20px;
}
.calendar-row {
display: flex;
justify-content: space-between;
width: 100%;
}
.calendar-cell {
flex: 1;
border: 1px solid #ddd;
text-align: center;
padding: 10px 0;
margin: 0 5px;
}
.calendar-cell h4 {
margin: 0;
padding: 5px;
border-radius: 4px;
background-color: #f8f8f8;
}
.hour-row {
display: flex;
justify-content: space-between;
width: 100%;
}
.hour-body {
display: flex; /* Add this line to use flexbox */
flex-direction: row; /* Ensure the direction is row */
flex: 4;
border: 1px solid #ddd;
text-align: center;
padding: 10px 0;
margin-left: 5px;
margin-right: 5px;
align-items: center;
}
.hour-cell {
flex: 1;
border: 1px solid #ddd;
text-align: center;
padding: 10px 0;
margin-left: 5px;
margin-right: 5px;
align-items: center;
max-width: 250px;
}
.today {
background-color: #5cb85c !important;
}
.hour-header, .hour-footer {
flex: 0 0 10%;
background-color: #f8f8f8;
text-align: center;
padding: 10px 0;
margin: 0 5px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
/* Adjusting the first and last cell margins to align with the container edges */
.calendar-cell:first-child {
margin-left: 0;
}
.calendar-cell:last-child {
margin-right: 0;
}
.select-container {
color: white;
}
.custom-select {
/* color: red; */
color: #000;
}
.custom-select .white {
background-color: #ffffff;
}
.custom-select .primary {
background-color: #337ab7;
}
.custom-select .success {
background-color: #5cb85c;
}
.custom-select .info {
background-color: #5bc0de;
}
.custom-select .warning {
background-color: #f0ad4e;
}
.custom-select .danger {
background-color: #d9534f;
}
.select-container .primary {
background-color: #337ab7;
}
.select-container .white {
background-color: #ffffff;
}
.select-container .success {
background-color: #5cb85c;
}
.select-container .info {
background-color: #5bc0de;
}
.select-container .warning {
background-color: #f0ad4e;
}
.select-container .danger {
background-color: #d9534f;
}
.dateDayContainer {
width: 250px;
display: inline-block;
text-align: center;
}
.dateWeekContainer {
width: 450px;
display: inline-block;
text-align: center;
}
.dateMonthContainer {
width: 250px;
display: inline-block;
text-align: center;
}
.dateYearContainer {
width: 150px;
display: inline-block;
text-align: center;
}
.hour-active {
background-color: #217025 !important;
/* color: #00ff0d !important; */
background-image:none;
}

View File

@ -1,136 +0,0 @@
<?php
/**
* app/plugins/calendar/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Calendar
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Calendar;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class CalendarForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'calendarSelect', __CLASS__, 'calendarSelect' );
self::addHandler( 'createEvent', __CLASS__, 'createEvent' );
self::addHandler( 'createCalendar', __CLASS__, 'createCalendar' );
self::addHandler( 'editEvent', __CLASS__, 'editEvent' );
self::addHandler( 'editCalendar', __CLASS__, 'editCalendar' );
}
/**
* Validates the password re-send form.
*
* @return {bool}
*/
public static function calendarSelect() {
if ( ! Input::exists( 'calendar_id' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function createEvent() {
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'date' ) ) {
Check::addUserError( 'You must include a date.' );
return false;
}
if ( ! Input::exists( 'time' ) ) {
Check::addUserError( 'You must include a time.' );
return false;
}
if ( ! Input::exists( 'repeats' ) ) {
Check::addUserError( 'You must include a frequency.' );
return false;
}
if ( ! Input::exists( 'calendar_id' ) ) {
Check::addUserError( 'You must select a calendar.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function createCalendar() {
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'timezone' ) ) {
Check::addUserError( 'You must include a timezone.' );
return false;
}
// if ( ! self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editEvent() {
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'date' ) ) {
Check::addUserError( 'You must include a date.' );
return false;
}
if ( ! Input::exists( 'time' ) ) {
Check::addUserError( 'You must include a time.' );
return false;
}
if ( ! Input::exists( 'repeats' ) ) {
Check::addUserError( 'You must include a frequency.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editCalendar() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'timezone' ) ) {
Check::addUserError( 'You must include a timezone.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
}
new CalendarForms;

View File

@ -1,157 +0,0 @@
<?php
/**
* app/plugins/calendar/models/events.php
*
* This class is used for the manipulation of the calendars database table.
*
* @package TP Calendar
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Date;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Bedrock\Classes\CustomException;
class Calendars extends DatabaseModel {
public $tableName = 'calendars';
public $databaseMatrix = [
[ 'title', 'varchar', '256' ],
[ 'color', 'varchar', '48' ],
[ 'privacy', 'varchar', '48' ],
[ 'description', 'text', '' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
[ 'timezone', 'varchar', '256' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $title, $description = '', $timezone = '', $color = 'default' ) {
if ( ! Check::dataTitle( $title ) ) {
Debug::info( 'Calendars: illegal title.' );
return false;
}
if ( empty( $timezone ) ) {
$timezone = Date::getTimezone();
}
$fields = [
'title' => $title,
'description' => $description,
'timezone' => $timezone,
'color' => $color,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( 'calendarCreate' );
Debug::error( "Calendar: not created " . var_export($fields,true) );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $description = '', $timezone = '', $color = 'default' ) {
if ( empty( $timezone ) ) {
$timezone = Date::getTimezone();
}
if ( !Check::id( $id ) ) {
Debug::info( 'Calendars: illegal ID.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'Calendars: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'timezone' => $timezone,
'color' => $color,
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'calendarUpdate' );
Debug::error( "Calendar: $id not updated: $fields" );
return false;
}
return true;
}
public function byUser( $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
if ( empty( $limit ) ) {
$calendars = self::$db->get( $this->tableName, $whereClause );
} else {
$calendars = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$calendars->count() ) {
Debug::info( 'No Calendars found.' );
return false;
}
return $this->filter( $calendars->results() );
}
public function getName( $id ) {
$calendar = self::findById( $id );
if (false == $calendar) {
return 'unknown';
}
return $calendar->title;
}
public function getColor( $id ) {
$calendar = self::findById( $id );
if (false == $calendar) {
return 'default';
}
return $calendar->color;
}
public function simpleByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$calendars = self::$db->get( $this->tableName, $whereClause );
if ( !$calendars->count() ) {
Debug::warn( 'Could not find any calendars' );
return false;
}
$calendars = $calendars->results();
$out = [];
foreach ( $calendars as &$calendar ) {
$out[ $calendar->title ] = $calendar->ID;
}
return $out;
}
public function simpleObjectByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$calendars = self::$db->get( $this->tableName, $whereClause );
if ( !$calendars->count() ) {
Debug::warn( 'Could not find any calendars' );
return false;
}
$calendars = $calendars->results();
$out = [];
foreach ( $calendars as &$calendar ) {
$obj = new \stdClass();
$obj->title = $calendar->title;
$obj->ID = $calendar->ID;
$out[] = $obj;
}
return $out;
}
}

View File

@ -1,509 +0,0 @@
<?php
/**
* app/plugins/calendar/models/events.php
*
* This class is used for the manipulation of the events database table.
*
* @package TP Calendar
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Models\Calendars;
use TheTempusProject\Bedrock\Classes\CustomException;
use TheTempusProject\Bedrock\Functions\Date;
use TheTempusProject\Bedrock\Classes\Pagination;
class Events extends DatabaseModel {
public $tableName = 'events';
public $repeatOptions = [
'Does Not Repeat' => 'none',
'Every Day' => 'daily',
'Every Week' => 'weekly',
'Every Month' => 'monthly',
'Every Year' => 'yearly',
];
public $databaseMatrix = [
[ 'calendar_id', 'int', '11' ],
[ 'title', 'varchar', '256' ],
[ 'event_time', 'int', '11' ],
[ 'event_ends', 'int', '11' ],
[ 'description', 'text', '' ],
[ 'location', 'varchar', '256' ],
[ 'repeats', 'varchar', '48' ],
[ 'color', 'varchar', '48' ],
[ 'parent_id', 'int', '11' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
];
protected static $calendars;
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
self::$calendars = new Calendars;
}
/**
* Saves a chat form to the db.
*
* @param string $message -contents of the chat form.
* @return bool
*/
public function create( $calendar_id, $title, $event_time, $description = '', $location = '', $repeats = 'none', $color = 'default', $parent_id = 0 ) {
if ( ! Check::id( $calendar_id ) ) {
Debug::info( 'calendar event: illegal calendar ID.' );
return false;
}
$fields = [
'calendar_id' => $calendar_id,
'title' => $title,
'event_time' => $event_time,
'description' => $description,
'location' => $location,
'repeats' => $repeats,
'color' => $color,
'parent_id' => $parent_id,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
Debug::info( 'Events::create - failed to insert to db' );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $event_time, $description = '', $location ='', $repeats = 'none', $color = 'default', $parent_id = 0, $calendar_id = '' ) {
if ( ! Check::id( $id ) ) {
Debug::info( 'calendar event: illegal ID.' );
return false;
}
$fields = [
'title' => $title,
'event_time' => $event_time,
'description' => $description,
'location' => $location,
'repeats' => $repeats,
'color' => $color,
'parent_id' => $parent_id,
];
if ( ! self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'calendarEventUpdate' );
Debug::error( "Event: $id not updated: $fields" );
return false;
}
return true;
}
public function dayArray( $calendar_id = 0, $date = 0 ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
if ( ! empty( $calendar_id ) ) {
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] );
}
$whereClause = array_merge( $whereClause, [
'event_time', '>=', Date::getDayStartTimestamp( $date, true ), 'AND',
'event_time', '<=', Date::getDayEndTimestamp( $date, true ),
] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
$results = [];
} else {
$results = $events->results();
}
$events = $this->sortByEventTimeHour(
$this->filter(
$results
)
);
// Generate day array
$currentDay = [];
$currentHour = Date::getDayStartTimestamp( $date );
$lastHour = Date::getDayEndTimestamp( $date );
while ( $currentHour <= $lastHour ) {
$hour = date( 'H', $currentHour );
if ( ! empty( $events[ $hour ] ) ) {
$dailyEvents = $events[ $hour ];
} else {
$dailyEvents = [];
}
$currentDay[ $hour ] = $dailyEvents;
$currentHour = strtotime('+1 hour', $currentHour);
}
return $currentDay;
}
public function weekArray( $calendar_id = 0, $date = null ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
if ( ! empty( $calendar_id ) ) {
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] );
}
$whereClause = array_merge( $whereClause, [
'event_time', '>=', Date::getWeekStartTimestamp( $date, true ), 'AND',
'event_time', '<=', Date::getWeekEndTimestamp( $date, true ),
] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
$results = [];
} else {
$results = $events->results();
}
$events = $this->sortByEventTime(
$this->filter(
$results
),
);
// Generate weeks array
$output = [];
$currentWeek = [];
$currentDay = Date::getWeekStartTimestamp( $date );
$weekCounter = 1;
$dayCounter = 1;
// Re-index the array using the date derived from event_time as the key
while ( $currentDay <= Date::getWeekEndTimestamp( $date ) ) {
$month = date( 'F', $currentDay );
$dayOfMonth = date( 'd', $currentDay );
if ( ! empty( $events[ $month ][ $dayOfMonth ] ) ) {
$dailyEvents = $events[ $month ][ $dayOfMonth ];
} else {
$dailyEvents = [];
}
$currentWeek[ $dayOfMonth ] = $dailyEvents;
$currentDay = strtotime('+1 day', $currentDay);
}
// Handle any remaining days in the last week
if ( ! empty( $currentWeek ) ) {
$output["week$weekCounter"] = $currentWeek;
}
return $output;
}
public function monthArray( $calendar_id = 0, $date = null ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
if ( ! empty( $calendar_id ) ) {
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] );
}
$whereClause = array_merge( $whereClause, [
'event_time', '>=', Date::getMonthStartTimestamp( $date, true ), 'AND',
'event_time', '<=', Date::getMonthEndTimestamp( $date, true ),
] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
$results = [];
} else {
$results = $events->results();
}
$events = $this->sortByEventTime(
$this->filter(
$results
),
);
// Generate weeks array
$output = [];
$currentWeek = [];
$currentDay = Date::getMonthStartTimestamp( $date );
$weekCounter = 1;
$dayCounter = 1;
// Re-index the array using the date derived from event_time as the key
while ( $currentDay <= Date::getMonthEndTimestamp( $date ) ) {
$month = date( 'F', $currentDay );
$dayOfMonth = date( 'd', $currentDay );
if ( ! empty( $events[ $month ][ $dayOfMonth ] ) ) {
$dailyEvents = $events[ $month ][ $dayOfMonth ];
} else {
$dailyEvents = [];
}
$currentWeek[$dayOfMonth] = $dailyEvents;
if (count($currentWeek) == 7) {
$output["week$weekCounter"] = $currentWeek;
$currentWeek = [];
$weekCounter++;
}
$currentDay = strtotime('+1 day', $currentDay);
}
// Handle any remaining days in the last week
if ( ! empty( $currentWeek ) ) {
$output["week$weekCounter"] = $currentWeek;
}
while ( $weekCounter < 7 ) {
$output["week$weekCounter"] = [];
$weekCounter++;
}
return $output;
}
public function yearArray( $calendar_id = 0, $date = null ) {
if ( empty( $date ) ) {
$date = time();
}
$year = date( 'Y', $date );
$firstDayUnix = date( 'U', strtotime( "Jan 1, $year" ) );
$lastDayUnix = date( 'U', strtotime( "December 31, $year" ) );
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
if ( ! empty( $calendar_id ) ) {
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] );
}
$whereClause = array_merge( $whereClause, [
'event_time', '<=', $lastDayUnix, 'AND',
'event_time', '>=', $firstDayUnix,
] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
return $this->filter( $events->results() );
}
public function compareEventTime($a, $b) {
return $a->event_time - $b->event_time;
}
public function sortByEventTimeHour( $results ) {
usort( $results, [ $this,'compareEventTime' ] );
$sortedResults = array();
foreach ( $results as $result ) {
$date = new \DateTime();
$date->setTimestamp( $result->event_time );
$timezone = new \DateTimeZone( Date::getTimezone() );
$date->setTimezone( $timezone );
$dateKey = $date->format('H');
$sortedResults[ $dateKey ][] = $result;
}
return $sortedResults;
}
public function sortByEventTime( $results ) {
usort( $results, [ $this,'compareEventTime' ] );
$sortedResults = array();
foreach ( $results as $result ) {
$date = new \DateTime();
$date->setTimestamp( $result->event_time );
$timezone = new \DateTimeZone( Date::getTimezone() );
$date->setTimezone( $timezone );
$month = $date->format('F');
$dateKey = $date->format('d');
if ( ! isset( $sortedResults[ $month ] ) ) {
$sortedResults[ $month ] = [];
}
if ( ! isset( $sortedResults[ $month ][ $dateKey ] ) ) {
$sortedResults[ $month ][ $dateKey ] = [];
}
$sortedResults[ $month ][ $dateKey ][] = $result;
}
return $sortedResults;
}
public function findByCalendar( $id ) {
$calendar = $this->verifyCalendar( $id );
if ( empty( $calendar ) ) {
return [];
}
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $id ] );
$events = self::$db->getPaginated( $this->tableName, $whereClause );
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
return $this->filter( $events->results() );
}
public function userEvents( $limit = 0 ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID ];
$events = self::$db->getPaginated( $this->tableName, $whereClause );
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
return $this->filter( $events->results() );
}
public function filter( $data, $params = [] ) {
$out = [];
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
$instance->repeatsText = array_search('none', $this->repeatOptions);
$instance->calendarName = self::$calendars->getName( $instance->calendar_id );
if ( empty( $instance->color ) || $instance->color == 'default' || $instance->color == 'none') {
$instance->displayColor = self::$calendars->getColor( $instance->calendar_id );
} else {
$instance->displayColor = $instance->color;
}
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
public function today( $limit = 0 ) {
$date = time();
$day = date('d', $date);
$month = date('M', $date);
$year = date( 'Y', $date );
$firstDayUnix = date( 'U', strtotime( "00:00:00 $day $month $year" ) );
$lastDayUnix = date( 'U', strtotime( "23:59:59 $day $month $year" ) );
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
$whereClause = array_merge( $whereClause, [
'event_time', '<=', $lastDayUnix, 'AND',
'event_time', '>=', $firstDayUnix,
] );
if ( empty( $limit ) ) {
$events = self::$db->get( $this->tableName, $whereClause );
} else {
$events = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
return $this->filter( $events->results() );
}
private function verifyCalendar( $calendar_id ) {
if ( ! Check::id( $calendar_id ) ) {
Debug::info( 'Invalid Calendar ID' );
return false;
}
$calendar = self::$calendars->findById( $calendar_id );
if ( $calendar == false ) {
Debug::info( 'Calendar not found' );
return false;
}
if ( $calendar->createdBy != App::$activeUser->ID ) {
Debug::info( 'You do not have permission to view this calendar' );
return false;
}
return $calendar;
}
public function deleteByCalendar( $calendar_id ) {
$calendar = $this->verifyCalendar( $calendar_id );
if ( empty( $calendar ) ) {
return [];
}
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
$whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id ] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
foreach( $events->results() as $event ) {
$this->deleteByParent( $event->ID );
}
return true;
}
public function deleteByParent( $event_id ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ];
$whereClause = array_merge( $whereClause, [ 'parent_id', '=', $event_id ] );
$events = self::$db->get( $this->tableName, $whereClause );
if ( ! $events->count() ) {
Debug::info( 'No ' . $this->tableName . ' data found.' );
return [];
}
foreach( $events->results() as $event ) {
$this->delete( $event->ID );
}
// need to delete all child events accordingly
return true;
}
public function delete( $idArray ) {
if ( !is_array( $idArray ) ) {
$idArray = [ $idArray ];
}
return parent::delete( $idArray );
}
public function generateChildEvents( $event_id, $remove_existing_children = true, $remove_history = false ) {
$event = self::findById( $event_id );
if (empty($event)) {
return false;
}
if ($event->parent_id != 0) {
return false;
}
if (!in_array($event->repeats, $this->repeatOptions)) {
return false;
}
$startDate = time();
$whereClause = [
'parent_id', '=', $event_id,
];
if ( $remove_history && ! $remove_existing_children) {
$whereClause = array_merge( $whereClause, [ 'AND', 'event_time', '<=', $startDate ] );
} elseif ( $remove_existing_children && ! $remove_history ) {
$whereClause = array_merge( $whereClause, [ 'AND', 'event_time', '>=', $startDate ] );
} elseif ( !$remove_existing_children && !$remove_history ) {
return false;
}
self::$db->delete($this->tableName, $whereClause);
switch ($event->repeats) {
case 'daily':
return $this->generateEvents( $event, 'P90D' );
case 'weekly':
return $this->generateEvents( $event, 'P1W' );
case 'monthly':
return $this->generateEvents( $event, 'P1M' );
case 'yearly':
return $this->generateEvents( $event, 'P1Y' );
default:
return false;
}
}
private function generateEvents( $event, $interval, $years = 2 ) {
$endDate = strtotime('+'.$years.' years');
$interval = new \DateInterval( $interval );
$period = new \DatePeriod(new \DateTime('@' . $event->event_time), $interval, new \DateTime('@' . $endDate));
foreach ($period as $date) {
if ( $date->getTimestamp() <= time() ) {
continue;
}
$result = $this->create(
$event->calendar_id,
$event->title,
$date->getTimestamp(),
$event->description,
$event->location,
$event->repeats,
$event->color,
$event->ID,
);
}
}
}

View File

@ -1,84 +0,0 @@
<?php
/**
* app/plugins/calendar/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Calendar
* @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;
use TheTempusProject\Models\Events;
use TheTempusProject\Models\Calendars;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
class Calendar extends Plugin {
public $pluginName = 'TP Calendar';
public $configName = 'calendar';
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 calendar system.';
public $permissionMatrix = [
'useCalendar' => [
'pretty' => 'Can use the calendar feature',
'default' => false,
],
'createEvents' => [
'pretty' => 'Can add events to calendars',
'default' => false,
],
];
public $main_links = [
[
'text' => 'Calendar',
'url' => '{ROOT_URL}calendar/index',
'filter' => 'loggedin',
],
];
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Calendar.',
'default' => true,
],
];
public $preferenceMatrix = [
'calendarPreference' => [
'pretty' => 'Default Calendar View',
'type' => 'select',
'default' => 'byMonth',
'options' => [
'Daily' => 'byDay',
'Weekly' => 'byWeek',
'Monthly' => 'byMonth',
'Yearly' => 'byYear',
'All Events' => 'events',
],
],
'weekStart' => [
'pretty' => 'First day of the week for the Calendar',
'type' => 'select',
'default' => 'sunday',
'options' => [
'Sunday' => '6',
'Monday' => '7',
],
],
];
public $events;
public $calendars;
public function __construct( $load = false ) {
$this->events = new Events;
$this->calendars = new Calendars;
parent::__construct( $load );
}
}

View File

@ -1,41 +0,0 @@
<?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\Plugins\Calendar;
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;
use TheTempusProject\Models\Events;
use TheTempusProject\Models\Calendars;
class CalendarLoader extends DefaultLoader {
/**
* This is the function used to generate any components that may be
* needed by this template.
*/
public function __construct() {
$events = new Events;
$calendars = new Calendars;
Navigation::setCrumbComponent( 'CALENDAR_BREADCRUMBS', Input::get( 'url' ) );
Components::set( 'todaysDate', date( 'F d, Y', time()) );
Components::set( 'currentDay', date( 'F d, Y', time()) );
Components::set( 'weekOf', 'Week of ' . date( 'F d, Y', strtotime( 'this week' ) ) );
Components::set( 'year', date( 'Y', time() ));
Components::set( 'month', date( 'F', time() ));
parent::__construct();
}
}

View File

@ -1,96 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
* app/plugins/calendar/templates/calendar.tpl
*
* @package TP Calendar
* @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' />
<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="alternate" hreflang="en-us" href="alternateURL">
<link rel="icon" href="{ROOT_URL}images/favicon.ico">
<!-- Required CSS -->
<link rel="stylesheet" href="{FONT_AWESOME_URL}font-awesome.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap-theme.min.css" crossorigin="anonymous">
<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}
<link rel="stylesheet" href="{ROOT_URL}app/plugins/calendar/css/calendar.css" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!--Brand and toggle should get grouped for better mobile display -->
<div class="navbar-header">
<a href="{ROOT_URL}" class="navbar-brand">{SITENAME}</a>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" style="">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="container-fluid">
<div class="collapse navbar-collapse navbar-ex1-collapse">
{topNavLeft}
<div class="navbar-right">
<ul class="nav navbar-nav">
{topNavRight}
</ul>
</div>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="foot-pad">
{ISSUES}
<div class="row">
<div class="container">
{ERROR}
{NOTICE}
{SUCCESS}
</div>
</div>
{/ISSUES}
<div class="row">
<div class="container-fluid calendar-container">
<div class="row">
{CalendarNav}
{CONTENT}
</div>
</div>
</div>
</div>
</div>
<footer>
{COPY}
</footer>
<!-- Bootstrap core JavaScript and jquery -->
<script src="{JQUERY_CDN}jquery.min.js" crossorigin="anonymous"></script>
<script src="{BOOTSTRAP_CDN}js/bootstrap.min.js" crossorigin="anonymous"></script>
<!-- Custom javascript for this template -->
{TEMPLATE_JS_INCLUDES}
</body>
</html>

View File

@ -1,25 +0,0 @@
<h2 class="pull-left">
<a href="{ROOT_URL}calendar/byDay/{calendarID}?day={lastDay}&month={lastDayMonth}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-left"></i>
</a>
<span class="dateDayContainer">{activeDate}</span>
<a href="{ROOT_URL}calendar/byDay/{calendarID}?day={nextDay}&month={nextDayMonth}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
</h2>
{dateDropdown}
<div class="container-fluid">
{LOOP}
<div class="hour-row{hourSelected}">
<h2 class="hour-header">{hour}:00</h2>
{EventCells}
<span class="hour-footer">
<a href="{ROOT_URL}calendar/createEvent?calendar_id={calendarID}&hour={hour}&day={day}" class="btn btn-sm btn-success" role="button"><i class="glyphicon glyphicon-plus"></i></a>
</span>
</div>
{/LOOP}
{ALT}
<div class="hour-row{hourSelected}">
</div>
{/ALT}
</div>

View File

@ -1,34 +0,0 @@
<h2 class="pull-left">
<a href="{ROOT_URL}calendar/byMonth/{calendarID}?month={lastMonth}&year={lastMonthYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-left"></i>
</a>
<span class="dateMonthContainer">{dateMonth}</span>
<a href="{ROOT_URL}calendar/byMonth/{calendarID}?month={nextMonth}&year={nextMonthYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
</h2>
{dateDropdown}
<div class="container-fluid">
<!-- Header -->
<div class="row calendar-row">
<div class="col-xs-1 calendar-cell"><h4>Sunday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Monday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Tuesday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Wednesday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Thursday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Friday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Saturday</h4></div>
</div>
<!-- Week 1 -->
{week1Element}
<!-- Week 2 -->
{week2Element}
<!-- Week 3 -->
{week3Element}
<!-- Week 4 -->
{week4Element}
<!-- Week 5 -->
{week5Element}
<!-- Week 6 -->
{week6Element}
</div>

View File

@ -1,25 +0,0 @@
<h2 class="pull-left">
<a href="{ROOT_URL}calendar/byWeek/{calendarID}?month={lastWeekMonth}&day={lastWeek}&year={lastYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-left"></i>
</a>
<span class="dateWeekContainer">{dateWeek}</span>
<a href="{ROOT_URL}calendar/byWeek/{calendarID}?month={nextWeekMonth}&day={nextWeek}&year={nextYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
</h2>
{dateDropdown}
<div class="container-fluid">
<!-- Header -->
<div class="row calendar-row">
<div class="col-xs-1 calendar-cell"><h4>Sunday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Monday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Tuesday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Wednesday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Thursday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Friday</h4></div>
<div class="col-xs-1 calendar-cell"><h4>Saturday</h4></div>
</div>
<!-- Week 1 -->
{week1Element}
</div>

View File

@ -1,42 +0,0 @@
<h2 class="pull-left">
<a href="{ROOT_URL}calendar/byYear/{calendarID}?year={lastYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-left"></i>
</a>
<span class="dateYearContainer">{dateYear}</span>
<a href="{ROOT_URL}calendar/byYear/{calendarID}?year={nextYear}" role="button" class="btn btn-primary">
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
</h2>
{dateDropdown}
<table class="table table-striped">
<thead>
<tr>
<th style="width: 10%">ID</th>
<th style="width: 20%">Time</th>
<th style="width: 40%">Title</th>
<th style="width: 10%"></th>
<th style="width: 10%"></th>
<th style="width: 10%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td style="text-align: center;">{ID}</td>
<td style="text-align: center;">{DTC}{event_time}{/DTC}</td>
<td style="text-align: center;">{title}</td>
<td><a href="{ROOT_URL}calendar/event/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a></td>
<td><a href="{ROOT_URL}calendar/editEvent/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}calendar/deleteEvent/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td style="text-align: center;" colspan="6">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}calendar/createEvent" class="btn btn-sm btn-primary" role="button">Create</a>

View File

@ -1,34 +0,0 @@
<legend>Create Calendar</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description"></textarea>
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Timezone</label>
<div class="col-lg-3">
{timezoneSelect}
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Calendar Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,29 +0,0 @@
<legend>Edit Calendar</legend>
<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 form-control" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-6">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description">{description}</textarea>
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Timezone</label>
<div class="col-lg-3">
{timezoneSelect}
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Calendar Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<input type="hidden" name="token" value="{TOKEN}">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button>
</form>

View File

@ -1,45 +0,0 @@
<legend>Calendars</legend>
<form action="{ROOT_URL}calendar/deleteCalendar" method="post">
<table class="table table-striped">
<thead>
<tr>
<th style="width: 5%">ID</th>
<th style="width: 20%">Title</th>
<th style="width: 50%">Description</th>
<th style="width: 5%"></th>
<th style="width: 5%"></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="CAL_[]"/>
</th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td align="center">{ID}</td>
<td align="center">{title}</td>
<td>{description}</td>
<td><a href="{ROOT_URL}calendar/byMonth/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-share-alt"></i></a></td>
<td><a href="{ROOT_URL}calendar/calendar/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-info-sign"></i></a></td>
<td><a href="{ROOT_URL}calendar/editCalendar/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}calendar/deleteCalendar/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
<td>
<input type="checkbox" value="{ID}" name="CAL_[]">
</td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="7">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}calendar/createCalendar" class="btn btn-sm btn-primary" role="button">Create</a>
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger">Delete</button>
</form>
<br />

View File

@ -1,42 +0,0 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Calendar</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="left" width="200"><b>Title</b></td>
<td align="right">{title}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Description</b></td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
<tr>
<td><b>TimeZone</b></td>
<td align="right">{timezone}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}calendar/deleteCalendar/{ID}" class="btn btn-md btn-danger" role="button">Delete</a>
<a href="{ROOT_URL}calendar/editCalendar/{ID}" class="btn btn-md btn-warning" role="button">Edit</a>
<a href="{ROOT_URL}calendar/byMonth/{ID}" class="btn btn-md btn-primary" role="button">View Events</a>
</div>
</div>
</div>
</div>

View File

@ -1,20 +0,0 @@
<div class="form-group">
<label for="day" class="col-lg-3 control-label">All-Day Event</label>
<div class="col-lg-3">
<input class="" type="checkbox" name="allDay" id="allDay" value="true" {CHECKED:allDay=true}>
</div>
</div>
<div class="form-group">
<label for="month" class="col-lg-3 control-label">Event Start</label>
<div class="col-lg-3">
<input type="date" name="date" id="date" class="form-control" value="{date}" />
<input type="time" name="time" id="time" class="form-control" value="{time}" />
</div>
</div>
<div class="form-group">
<label for="month" class="col-lg-3 control-label">Event End</label>
<div class="col-lg-3">
<input type="date" name="endDate" id="endDate" class="form-control" value="{endDate}" />
<input type="time" name="endTime" id="endTime" class="form-control" value="{endTime}" />
</div>
</div>

View File

@ -1,37 +0,0 @@
<legend>Create Event</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
{calendarSelect}
<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-control" name="title" id="title">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="6" cols="30" id="description"></textarea>
</div>
</div>
<div class="form-group">
<label for="location" class="col-lg-3 control-label">Location</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="location" id="location">
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Event Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
{dateSelect}
{repeatSelect}
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button>
</div>
</div>
</form>

View File

@ -1,31 +0,0 @@
<legend>Edit Event</legend>
<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-control" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-6">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description">{description}</textarea>
</div>
</div>
<div class="form-group">
<label for="location" class="col-lg-3 control-label">Location</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="location" id="location" value="{location}">
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Event Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
{dateSelect}
{repeatSelect}
<input type="hidden" name="token" value="{TOKEN}">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button>
</form>

View File

@ -1,34 +0,0 @@
{PAGINATION}
<table class="table table-striped">
<thead>
<tr>
<th style="width: 10%">ID</th>
<th style="width: 20%">Time</th>
<th style="width: 40%">Title</th>
<th style="width: 10%"></th>
<th style="width: 10%"></th>
<th style="width: 10%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td style="text-align: center;">{ID}</td>
<td style="text-align: center;">{DTC}{event_time}{/DTC}</td>
<td style="text-align: center;">{title}</td>
<td><a href="{ROOT_URL}calendar/event/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a></td>
<td><a href="{ROOT_URL}calendar/editEvent/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}calendar/deleteEvent/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td style="text-align: center;" colspan="6">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
{PAGINATION}
<a href="{ROOT_URL}calendar/createEvent" class="btn btn-sm btn-primary" role="button">Create</a>

View File

@ -1,52 +0,0 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Event</h3>
</div>
<div class="panel-body">
<div class="row">
<table class="table table-user-primary">
<tbody>
<tr>
<td style="text-align: left;" width="200"><b>Title</b></td>
<td style="text-align: right;">{title}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td style="text-align: right;">{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<td><b>Event Time</b></td>
<td style="text-align: right;">{DTC}{event_time}{/DTC}</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><b>Description</b></td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
<tr>
<td><b>Location</b></td>
<td style="text-align: right;">{location}</td>
</tr>
<tr>
<td><b>Frequency</b></td>
<td style="text-align: right;">{repeatsText}</td>
</tr>
<tr>
<td><b>Calendar</b></td>
<td style="text-align: right;">{calendarName}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}calendar/deleteEvent/{ID}" class="btn btn-md btn-danger" role="button">Delete</a>
<a href="{ROOT_URL}calendar/editEvent/{ID}" class="btn btn-md btn-warning" role="button">Edit</a>
<a href="{ROOT_URL}calendar/byMonth/{calendar_id}" class="btn btn-md btn-primary" role="button">View Events</a>
</div>
</div>
</div>
</div>

View File

@ -1,16 +0,0 @@
<li class="pull-right dropdown">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Calendars <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="{ROOT_URL}calendar/{currentView}/">All</a></li>
<li role="separator" class="divider"></li>
{LOOP}
<li><a href="{ROOT_URL}calendar/{currentView}/{ID}">{title}</a></li>
{/LOOP}
<li role="separator" class="divider"></li>
<li><a href="{ROOT_URL}calendar/createCalendar">Create Calendar</a></li>
<li><a href="{ROOT_URL}calendar/editCalendar/{calendarID}">Edit Calendar</a></li>
<li><a href="{ROOT_URL}calendar/deleteCalendar/{calendarID}">Delete Calendar</a></li>
</ul>
</li>

View File

@ -1,23 +0,0 @@
<ul class="list-group">
{LOOP}
<li class="list-group-item btn-{displayColor}">
<p style="text-align: center;">{title}</p>
<p style="text-align: center;">{DTC}{event_time}{/DTC}</p>
<p>
<a href="{ROOT_URL}calendar/event/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a>
<a href="{ROOT_URL}calendar/editEvent/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}calendar/deleteEvent/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</p>
</li>
{/LOOP}
{ALT}
<li class="list-group-item">
<br />
<br />
<br />
<br />
<br />
</li>
{/ALT}
<a href="{ROOT_URL}calendar/createEvent?calendar_id={calendarID}&month={currentMonth}&day={currentDay}" class="list-group-item list-group-item-success"><i class="glyphicon glyphicon-plus"></i></a>
</ul>

View File

@ -1,18 +0,0 @@
<ul class="nav nav-pills">
<li class="dropdown pull-right">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Switch Date <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<form action="" method="post" class="form-horizontal">
<li><input type="date" name="date" id="date" class="form-control" value="{date}" /></li>
<li role="separator" class="divider"></li>
<li>
<button name="submit" value="submit" type="submit" class="btn btn-primary center-block">
Select Date
</button>
</li>
</form>
</ul>
</li>
</ul>

View File

@ -1,12 +0,0 @@
<span class="hour-body">
{LOOP}
<div class="hour-cell btn-{displayColor}">
<p class="event-title">{title}</p>
<a href="{ROOT_URL}calendar/event/{ID}" class="btn btn-sm btn-primary" role="button"><i class="glyphicon glyphicon-open"></i></a>
<a href="{ROOT_URL}calendar/editEvent/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}calendar/deleteEvent/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</div>
{/LOOP}
{ALT}
{/ALT}
</span>

View File

@ -1,8 +0,0 @@
<ul class="nav nav-tabs">
<li><a href="{ROOT_URL}calendar/byDay/{calendarID}">Daily</a></li>
<li><a href="{ROOT_URL}calendar/byWeek/{calendarID}">Weekly</a></li>
<li><a href="{ROOT_URL}calendar/byMonth/{calendarID}">Monthly</a></li>
<li><a href="{ROOT_URL}calendar/byYear/{calendarID}">Yearly</a></li>
<li><a href="{ROOT_URL}calendar/events/{calendarID}">Events</a></li>
{calendarDropdown}
</ul>

View File

@ -1,9 +0,0 @@
<div class="row calendar-row">
{LOOP}
<div class="col-xs-1 calendar-cell">
<h4 class="{highlightedDate}">{day}</h4>{dayEventList}
</div>
{/LOOP}
{ALT}
{/ALT}
</div>

View File

@ -1,37 +0,0 @@
<?php
/**
* app/plugins/chat/controllers/admin/chat.php
*
* This is the chat admin controller.
*
* @package TP Chat
* @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\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\Models\Chat as ChatModel;
class Chat extends AdminController {
protected static $chat;
public function __construct() {
parent::__construct();
self::$title = 'Admin - Chat';
self::$chat = new ChatModel;
$view = Navigation::activePageSelect( 'nav.admin', '/admin/chat' );
Components::set( 'ADMINNAV', $view );
}
public function index() {
// Views::view( 'chat.admin.list', self::$chat->list() );
}
}

View File

@ -1,45 +0,0 @@
<?php
/**
* app/controllers/api/users.php
*
* This is the users' api controller.
*
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Controllers\Api;
use TheTempusProject\Models\User;
use TheTempusProject\Classes\ApiController;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Models\Chat;
class Messages extends ApiController {
public static $chat;
public function __construct() {
parent::__construct();
self::$chat = new Chat;
Template::setTemplate( 'api' );
}
public function sendMessage() {
$response = true;
if ( ! Forms::check( 'newChatMessage' ) ) {
$response = 'Invalid Form';
} else {
$response = self::$chat->create( Input::post( 'chatMessage' ) );
}
Views::view( 'api.response', ['response' => json_encode( [ 'data' => $response ], true )]);
}
public function getMessages() {
$response = Views::simpleView( 'chat.chat', self::$chat->recent( 50 ) );
Views::view( 'api.response', ['response' => json_encode( [ 'data' => $response ], true )]);
}
}

View File

@ -1,191 +0,0 @@
<?php
/**
* app/plugins/chat/controllers/chat.php
*
* This is the public chat controller.
*
* @package TP Chat
* @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\Hermes\Functions\Redirect;
use TheTempusProject\Bedrock\Functions\Upload;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Input;
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\Chat as ChatModel;
use TheTempusProject\Models\Upload as UploadModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Canary\Bin\Canary as Debug;
class Chat extends Controller {
protected static $chat;
public function __construct() {
parent::__construct();
self::$chat = new ChatModel;
Template::setTemplate( 'chat' );
}
public function index() {
if ( !App::$isMember ) {
Session::flash( 'error', 'You do not have permission to view this page.' );
return Redirect::home();
}
self::$title = '{SITENAME} Chat';
self::$pageDescription = 'One of the privleges of membership is the ability to chat with your fellow members.';
if ( App::$isLoggedIn ) {
Components::set( 'CREATE_MESSAGE', Views::simpleView( 'chat.create' ) );
} else {
Components::set( 'CREATE_MESSAGE', '' );
}
$upload = '';
$sharePlugin = 'TheTempusProject\Plugins\Fileshare';
if ( class_exists( $sharePlugin ) ) {
$plugin = new $sharePlugin;
if ( $plugin->checkEnabled() ) {
$upload = Views::simpleView( 'chat.upload' );
}
}
Components::set( 'FILE_UPLOAD', $upload );
Components::set( 'CHAT', Views::simpleView( 'chat.chat' ) );
return Views::view( 'chat.index' );
}
public function sendMessage() {
Template::setTemplate( 'api' );
$out = [ 'response' => true ];
if ( !Forms::check( 'newChatMessage' ) ) {
$out = [ 'response' => false ];
echo json_encode($out);
return;
}
self::$chat->create(
Input::post( 'chatMessage' ),
);
echo json_encode($out);
return;
}
public function uploadFile() {
Template::setTemplate( 'api' );
$out = [ 'response' => true ];
if ( ! Forms::check( 'newFileUpload' ) ) {
$out = [ 'response' => false ];
echo json_encode($out);
return;
}
$sharePlugin = 'TheTempusProject\Plugins\Fileshare';
if ( ! class_exists( $sharePlugin ) ) {
$out = [ 'error' => 'Fileshare must be installed and enabled for this feature to work1.' ];
echo json_encode($out);
return;
}
$plugin = new $sharePlugin;
if ( ! $plugin->checkEnabled() ) {
$out = [ 'error' => 'Fileshare must be installed and enabled for this feature to work2.' ];
echo json_encode($out);
return;
}
$folder = UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( ! Upload::image( 'file', $folder ) ) {
$out = [ 'error' => 'could not upload image' ];
echo json_encode($out);
return;
}
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$location = $route . Upload::last();
$uploads = new UploadModel;
$result = $uploads->create( 'Chat Upload', $location );
if ( ! $result ) {
$out = [ 'error' => 'could not add upload to fileshare.' ];
echo json_encode( $out );
return;
}
self::$chat->create(
'Shared a file with the chat: ' .
'<a href="/' . $location . '" target="_blank">Upload</a>'
);
echo json_encode($out);
return;
}
public function getMessages() {
Template::setTemplate( 'api' );
echo Views::simpleView( 'chat.chat', self::$chat->recent( 50 ) );
return;
}
public function getMessageEvents() {
if ($_SERVER['HTTP_ACCEPT'] !== 'text/event-stream') {
Debug::info( 'connection refused, wrong HTTP_ACCEPT' );
exit();
}
header("X-Accel-Buffering: no");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// Ensure unlimited script execution time
set_time_limit(0);
// Disable output buffering
ini_set('output_buffering', 'off');
ini_set('zlib.output_compression', false);
if (Input::exists('lastId')) {
$lastId = Input::get('lastId');
} else {
$lastId = 0;
}
while ( true ) {
echo "id: 0\n";
echo "event: ping\n";
echo 'data: {"time": "' . time() . '"}';
echo "\n\n";
if ( connection_aborted() ) {
Debug::info( 'getMessageEvents connection aborted' );
break;
}
$newMessages = self::$chat->sinceMessage( $lastId );
if ( ! empty( $newMessages )) {
foreach ( $newMessages as $message ) {
$lastId = $message->ID;
echo "id: {$message->ID}\n";
echo "data: " . json_encode($message) . "\n\n";
}
}
// If there were any messages added, flush the output buffer
if (ob_get_contents()) {
ob_end_flush();
}
flush();
// sessions will block the end-user from sending messages unless we close the session
session_write_close();
sleep(1);
}
}
}

View File

@ -1,116 +0,0 @@
* {
margin: 0;
padding: 0;
}
body {
margin: 20px auto;
font-family: "Lato";
font-weight: 300;
}
form {
padding: 15px 25px;
display: flex;
gap: 10px;
justify-content: center;
}
form label {
font-size: 1.5rem;
font-weight: bold;
}
input {
font-family: "Lato";
}
a {
color: #0000ff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#chatMessages {
text-align: left;
margin: 0 auto;
padding: 10px;
height: 300px;
border: 1px solid #a7a7a7;
overflow: auto;
}
#usermsg {
flex: 1;
border-radius: 4px;
border: 1px solid #ff9800;
}
#name {
border-radius: 4px;
border: 1px solid #ff9800;
padding: 2px 8px;
}
#submitmsg,
#enter{
background: #ff9800;
border: 2px solid #e65100;
color: white;
padding: 4px 10px;
font-weight: bold;
border-radius: 4px;
}
.error {
color: #ff0000;
}
#menu {
padding: 15px 25px;
display: flex;
}
#menu p.welcome {
flex: 1;
}
a#exit {
color: white;
background: #c62828;
padding: 4px 8px;
border-radius: 4px;
font-weight: bold;
}
.msgln {
margin: 0 0 5px 0;
}
.span.left-info {
color: orangered;
}
span .chat-time {
color: #666;
font-size: 80%;
vertical-align: super;
width: 100px;
}
.msgln b.user-name, .msgln b.user-name-left {
font-weight: bold;
background: #546e7a;
color: white;
padding: 2px 4px;
font-size: 90%;
border-radius: 4px;
margin: 0 5px 0 0;
}
.msgln b.user-name-left {
background: orangered;
}

View File

@ -1,52 +0,0 @@
<?php
/**
* app/plugins/chat/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Chat;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class ChatForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'newChatMessage', __CLASS__, 'newChatMessage' );
self::addHandler( 'newFileUpload', __CLASS__, 'newFileUpload' );
}
/**
* Validates the new blog post form.
*
* @return {bool}
*/
public static function newChatMessage() {
if ( !Input::exists( 'chatMessage' ) ) {
self::addUserError( 'You must includes a message' );
return false;
}
// if (!self::token()) {
// return false;
// }
return true;
}
public static function newFileUpload() {
if ( !Input::exists( 'file' ) ) {
self::addUserError( 'You must includes a file' );
return false;
}
return true;
}
}
new ChatForms;

View File

@ -1,116 +0,0 @@
$(document).ready(function() {
var chatform = $('#sendChatMessage');
var uploadForm = $('#uploadFile');
chatform.bind('submit', function(event) {
event.preventDefault(); // Prevent page reload
var msg = $("#chatMessage").val();
console.log("Submitting message:", msg);
var ajax_params = {
url: '/chat/sendMessage',
type: 'POST',
data: { chatMessage: msg },
success: function(response) {
console.log("Message sent successfully:", response);
$("#chatMessage").val("");
},
error: function(xhr, status, error) {
console.error("Error sending message:", error, status, xhr);
},
complete: function() {
console.log("AJAX request complete");
}
};
$.ajax(ajax_params);
return false;
});
uploadForm.bind('submit', function(event) {
event.preventDefault(); // Prevent page reload
var formData = new FormData(this); // Create FormData object
$.ajax({
url: '/chat/uploadFile',
type: 'POST',
data: formData,
processData: false, // Don't process the files
contentType: false, // Set content type to false as jQuery will tell the server its a query string request
success: function(response) {
console.log("File uploaded successfully:", response);
$("#file").val("");
},
error: function(xhr, status, error) {
console.error("Error uploading file:", error, status, xhr);
},
complete: function() {
console.log("AJAX request complete");
}
});
return false;
});
const eventSource = new EventSource('/chat/getMessageEvents');
eventSource.onmessage = function( event ) {
const message = JSON.parse( event.data );
var userPopup = "<div class='media'>" +
"<span class='pull-left'>" +
"<img class='media-object avatar-round-40' src='/"+message.avatar+"' alt=''>" +
"</span>" +
"<div class='media-body'>" +
"<h5 class='media-heading'>" +
"<strong>"+message.submittedByName+"</strong>" +
"</h5>" +
"<a href='/home/profile/"+message.submittedByName+"' class='btn btn-sm btn-primary' role='button'><i class='glyphicon glyphicon-open'></i> View Profile</a>" +
"</div>" +
"</div>";
$("#chatMessages").append(
"<span class='chat-time'>" +
new Date(message.submittedAt * 1000).toLocaleString('en-US', { month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true }) +
// new Date(message.submittedAt * 1000).toLocaleString() +
"</span> " +
'<a tabindex="0" role="button" data-toggle="popover" data-html="true" data-trigger="focus" title="'+message.submittedByName+'" data-content="'+userPopup+'">' +
message.submittedByName +
'</a> ' +
message.chatMessage + "<br>"
);
$("[data-toggle=popover]").popover();
$("#chatMessages").scrollTop($("#chatMessages")[0].scrollHeight);
};
eventSource.onerror = function(event) {
console.error('EventSource failed:', event);
};
window.addEventListener('beforeunload', function() {
eventSource.close();
});
window.addEventListener('unload', function() {
eventSource.close();
});
// function getUserProfile( id ) {
// var ajax_params = {
// url: '/api/users/find/' + id,
// type: 'GET',
// success: function(response) {
// console.log("User retrieved:", response);
// $("#chatMessage").val("");
// },
// error: function(xhr, status, error) {
// console.error("Error retrieved user:", error, status, xhr);
// },
// complete: function() {
// console.log("AJAX request complete");
// }
// };
// $.ajax(ajax_params);
// }
});

View File

@ -1,145 +0,0 @@
<?php
/**
* app/plugins/chat/models/chat.php
*
* This class is used for the manipulation of the chat database table.
*
* @todo make this send a confirmation email
*
* @package TP Chat
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
class Chat extends DatabaseModel {
public $tableName = 'chat';
public $databaseMatrix = [
[ 'submittedAt', 'int', '10' ],
[ 'submittedBy', 'int', '11' ],
[ 'chatMessage', 'text', '' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Saves a chat form to the db.
*
* @param string $message -contents of the chat form.
* @return bool
*/
public function create( $message ) {
$fields = [
'submittedBy' => App::$activeUser->ID,
'submittedAt' => time(),
'chatMessage' => $message,
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
Debug::info( 'Chat::create - failed to insert to db' );
return false;
}
return self::$db->lastId();
}
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
$instance->chatMessage = Filters::applyOne( 'mentions.0', $instance->chatMessage, true );
$instance->chatMessage = Filters::applyOne( 'hashtags.0', $instance->chatMessage, true );
$user = self::$user->findById( $instance->submittedBy );
if ( ! empty( $user ) ) {
$instance->submittedByName = $user->username;
$instance->profileUrl = '/home/profile/' . $user->username;
} else {
$instance->submittedByName = 'Unknown';
$instance->profileUrl = '#';
}
$instance->avatar = self::$user->getAvatar( $instance->submittedBy );
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
/**
* Function to clear chat from the DB.
*
* @todo is there a way i could check for success here I'm pretty sure this is just a bad idea?
* @return bool
*/
public function clear() {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
self::$db->delete( $this->tableName, ['ID', '>=', '0'] );
self::$log->admin( 'Cleared Chat' );
Debug::info( 'Chat Cleared' );
return true;
}
public function recent( $limit = null ) {
if ( empty( $limit ) ) {
$postData = self::$db->get( $this->tableName, '*' );
} else {
$postData = self::$db->get( $this->tableName, '*', 'ID', 'ASC', [0, $limit] );
}
if ( !$postData->count() ) {
Debug::info( 'No messages found.' );
return false;
}
return $this->filter( $postData->results() );
}
public function sinceMessage($id = 0) {
if (empty($id)) {
$postData = self::$db->get($this->tableName, '*', 'ID', 'DESC', [0, 20]);
} else {
$postData = self::$db->get($this->tableName, ['ID', '>', $id], 'ID', 'ASC', [0, 20]);
}
if ( ! $postData->count() ) {
Debug::debug( 'No messages found.' );
return false;
}
if (empty($id)) {
$results = array_reverse($postData->results()); // Reverse the order to get ascending IDs
} else {
$results = $postData->results();
}
return $this->filter($results);
}
public function sinceMessageSingle( $id = 0 ) {
if ( empty( $id ) ) {
$postData = self::$db->get( $this->tableName, '*', 'ID', 'ASC', [0, 1] );
} else {
$postData = self::$db->get( $this->tableName, [ 'ID', '>', $id ], 'ID', 'ASC', [0, 1] );
}
if ( ! $postData->count() ) {
Debug::debug( 'No messages found.' );
return false;
}
return $this->filter( $postData->results() );
}
}

View File

@ -1,55 +0,0 @@
<?php
/**
* app/plugins/chat/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Chat
* @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;
use TheTempusProject\TheTempusProject as App;
class Chat extends Plugin {
public $pluginName = 'TP Chat';
public $configName = 'chat';
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 chat system.';
public $permissionMatrix = [
'chat' => [
'pretty' => 'Can use chat',
'default' => false,
],
];
public $admin_links = [
[
'text' => '<i class="fa fa-fw fa-copy"></i> Chat',
'url' => '{ROOT_URL}admin/chat',
],
];
public $main_links = [
[
'text' => 'Chat',
'url' => '{ROOT_URL}chat/index',
'filter' => 'loggedin',
],
];
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Chat.',
'default' => true,
],
];
public function __construct( $load = false ) {
parent::__construct( $load );
}
}

View File

@ -1,32 +0,0 @@
<?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\Plugins\Calendar;
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 ChatLoader extends DefaultLoader {
/**
* This is the function used to generate any components that may be
* needed by this template.
*/
public function __construct() {
$this->addJs( '<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{ROOT_URL}app/plugins/chat/js/chat.js"></script>' );
parent::__construct();
}
}

View File

@ -1,88 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
* app/templates/default/default.tpl
*
* @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">
{AUTHOR}
{ROBOT}
<link rel="icon" href="{ROOT_URL}images/favicon.ico">
<!-- Required CSS -->
<link rel="stylesheet" href="{FONT_AWESOME_URL}font-awesome.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap-theme.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap.min.css" crossorigin="anonymous">
<!-- Custom styles for this template -->
{TEMPLATE_CSS_INCLUDES}
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!--Brand and toggle should get grouped for better mobile display but I had to account for additional menus-->
<div class="navbar-header">
<a href="{ROOT_URL}" class="navbar-brand">{SITENAME}</a>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" style="">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="container-fluid">
<div class="collapse navbar-collapse navbar-ex1-collapse">
{topNavLeft}
<div class="navbar-right">
<ul class="nav navbar-nav">
{topNavRight}
</ul>
</div>
</div>
</div>
</nav>
<div class="container-fluid top-pad foot-pad">
{ISSUES}
<div class="container">
<div class="row">
{ERROR}
{NOTICE}
{SUCCESS}
{INFO}
</div>
</div>
{/ISSUES}
<div class="container">
<div class="row">
{CONTENT}
</div>
</div>
</div>
<footer>
{FOOT}
{COPY}
</footer>
<!-- 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="{BOOTSTRAP_CDN}js/bootstrap.min.js"></script>
<!-- Custom javascript for this template -->
{TEMPLATE_JS_INCLUDES}
</body>
</html>

View File

@ -1,10 +0,0 @@
{LOOP}
<div class='msgln'>
<span class='chat-time'>{DTC time}{submittedAt}{/DTC}</span> <b class='user-name'>{submittedByName}</b>: {chatMessage}<br>
</div>
{/LOOP}
{ALT}
<div class="msgln">
No messages Found.<br>
</div>
{/ALT}

View File

@ -1,9 +0,0 @@
<form class="form-horizontal" id="sendChatMessage">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group col-lg-10">
<input type="text" class="form-control" name="chatMessage" id="chatMessage">
</div>
<div class="form-group col-lg-2">
<button id="submitChatMessage" name="submitChatMessage" value="button" class="btn btn-sm btn-primary center-block ">Send</button>
</div>
</form>

View File

@ -1,18 +0,0 @@
<link rel="stylesheet" href="{ROOT_URL}app/plugins/chat/css/chat.css" crossorigin="anonymous">
<span class="col-lg-12" id="Chat">
<div class="panel panel-default" role="tab">
<div class="panel-heading">
<button type="button" class="btn btn-default btn-sm" data-target="#Collapse${id}" data-toggle="collapse" aria-expanded="true" aria-controls="#Collapse${id}">
<i class="glyphicon glyphicon-th-list"></i> {USERNAME}
</button>
</div>
<div id="Collapse${id}" class="panel-collapse collapse in" style="width:100%; position: relative;" role="tabpanel" aria-expanded="true">
<div class="panel-body" id="chatMessages">
</div>
<div class="panel-footer">
{CREATE_MESSAGE}
{FILE_UPLOAD}
</div>
</div>
</div>
</span>

View File

@ -1,9 +0,0 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data" id="uploadFile">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group col-lg-3 col-lg-offset-6">
<input type="file" class="form-control" name="file" id="file">
</div>
<div class="form-group col-lg-3">
<button id="submitUpload" name="submit" value="submit" class="btn btn-sm btn-primary center-block">Upload</button>
</div>
</form>

View File

@ -1,92 +0,0 @@
<?php
/**
* app/plugins/contacts/controllers/contacts.php
*
* This is the contacts controller.
*
* @package TP Contacts
* @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\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Houdini\Classes\Forms as FormBuilder;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Classes\Config;
use TheTempusProject\Classes\Controller;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Models\Contact;
use TheTempusProject\Models\Phonebook;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Houdini\Classes\Navigation;
class Contacts extends Controller {
protected static $contacts;
protected static $phonebooks;
public function __construct() {
parent::__construct();
if ( !App::$isLoggedIn ) {
Session::flash( 'notice', 'You must be logged in to use this feature.' );
return Redirect::home();
}
self::$contacts = new Contact;
self::$phonebooks = new Phonebook;
self::$title = 'Contacts - {SITENAME}';
self::$pageDescription = 'On this page you can create and manage contacts and phonebooks.';
}
public function index() {
$phonebooks = Views::simpleView( 'contacts.phonebooks.list', self::$phonebooks->byUser() );
Components::set( 'phonebookList', $phonebooks );
$contacts = Views::simpleView( 'contacts.contacts.list', self::$contacts->byUser() );
Components::set( 'contactList', $contacts );
Views::view( 'contacts.dashboard' );
}
/**
* Contacts
*/
public function viewContact( $id = null ) {
// stuff here
}
public function createContact( $id = null ) {
// stuff here
}
public function editContact( $id = null ) {
// stuff here
}
public function deleteContact( $id = null ) {
// stuff here
}
/**
* Phonebooks
*/
public function viewPhonebook( $id = null ) {
// stuff here
}
public function createPhonebook() {
// stuff here
}
public function editPhonebook( $id = null ) {
// stuff here
}
public function deletePhonebook( $id = null ) {
// stuff here
}
}

View File

@ -1,99 +0,0 @@
<?php
/**
* app/plugins/notes/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Notes
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Notes;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class ContactsForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'createContact', __CLASS__, 'createContact' );
self::addHandler( 'createPhonebook', __CLASS__, 'createPhonebook' );
self::addHandler( 'editContact', __CLASS__, 'editContact' );
self::addHandler( 'editPhonebook', __CLASS__, 'editPhonebook' );
}
public static function createContact() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'first_name' ) && ! Input::exists( 'last_name' ) && ! Input::exists( 'nickname' ) ) {
Check::addUserError( 'You must include a first, last, or nick-name.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function createPhonebook() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'color' ) ) {
Check::addUserError( 'You must include a color.' );
return false;
}
// if ( ! self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editContact() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'first_name' ) && ! Input::exists( 'last_name' ) && ! Input::exists( 'nickname' ) ) {
Check::addUserError( 'You must include a first, last, or nick-name.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
public static function editPhonebook() {
if ( ! Input::exists( 'submit' ) ) {
return false;
}
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must include a title.' );
return false;
}
if ( ! Input::exists( 'color' ) ) {
Check::addUserError( 'You must include a color.' );
return false;
}
// if ( !self::token() ) {
// Check::addUserError( 'token - comment out later.' );
// return false;
// }
return true;
}
}
new ContactsForms;

View File

@ -1,88 +0,0 @@
<?php
/**
* app/plugins/contacts/models/contact.php
*
* This class is used for the manipulation of the contacts database table.
*
* @package TP Contacts
* @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\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
class Contact extends DatabaseModel {
public $tableName = 'contacts';
public $databaseMatrix = [
[ 'avatar', 'text', ''],
[ 'first_name', 'varchar', '128'],
[ 'middle_name', 'varchar', '128'],
[ 'last_name', 'varchar', '128'],
[ 'nickname', 'varchar', '128'],
[ 'company', 'varchar', '128'],
[ 'job_title', 'varchar', '128'],
[ 'email', 'varchar', '128'],
[ 'email_2', 'varchar', '128'],
[ 'phone', 'varchar', '128'],
[ 'phone_2', 'varchar', '128'],
[ 'address_1_primary', 'varchar', '128'],
[ 'address_1_secondary', 'varchar', '128'],
[ 'city', 'varchar', '128'],
[ 'state', 'varchar', '128'],
[ 'zipcode', 'varchar', '128'],
[ 'country', 'varchar', '128'],
[ 'address_2_primary', 'varchar', '128'],
[ 'address_2_secondary', 'varchar', '128'],
[ 'city_2', 'varchar', '128'],
[ 'state_2', 'varchar', '128'],
[ 'zipcode_2', 'varchar', '128'],
[ 'country_2', 'varchar', '128'],
[ 'notes', 'text', ''],
[ 'color', 'varchar', '48' ],
[ 'icon', 'varchar', '48' ],
[ 'createdAt', 'int', '11'],
[ 'createdBy', 'int', '11'],
[ 'phonebookID', 'int', '11'],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function byUser( $limit = null ) {
$whereClause = [ 'createdBy', '=', App::$activeUser->ID ];
if ( empty( $limit ) ) {
$phonebooks = self::$db->get( $this->tableName, $whereClause );
} else {
$phonebooks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$phonebooks->count() ) {
Debug::info( 'No Phonebooks found.' );
return false;
}
return $this->filter( $phonebooks->results() );
}
public function byPhonebook( $phonebookID, $limit = null ) {
$whereClause = [ 'phonebookID', '=', $phonebookID ];
if ( empty( $limit ) ) {
$phonebooks = self::$db->get( $this->tableName, $whereClause );
} else {
$phonebooks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$phonebooks->count() ) {
Debug::info( 'No Phonebooks found.' );
return false;
}
return $this->filter( $phonebooks->results() );
}
}

View File

@ -1,165 +0,0 @@
<?php
/**
* app/plugins/contacts/models/phonebook.php
*
* This class is used for the manipulation of the phonebook database table.
*
* @package TP Contacts
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Bedrock\Classes\CustomException;
class Phonebook extends DatabaseModel {
public $tableName = 'phonebooks';
public $databaseMatrix = [
[ 'title', 'varchar', '128' ],
[ 'description', 'text', '' ],
[ 'color', 'varchar', '48' ],
[ 'icon', 'varchar', '48' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $title, $description = '', $color = 'default', $icon = '' ) {
if ( ! Check::dataTitle( $title ) ) {
Debug::info( 'Phonebooks: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'color' => $color,
'icon' => $icon,
'createdBy' => App::$activeUser->ID,
'createdAt' => time(),
];
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
new CustomException( 'phonebookCreate' );
Debug::error( "Phonebooks: not created " . var_export($fields,true) );
return false;
}
return self::$db->lastId();
}
public function update( $id, $title, $description = '', $color = 'default', $icon = '' ) {
if ( !Check::id( $id ) ) {
Debug::info( 'Phonebooks: illegal ID.' );
return false;
}
if ( !Check::dataTitle( $title ) ) {
Debug::info( 'Phonebooks: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'description' => $description,
'color' => $color,
'icon' => $icon,
];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'phonebookUpdate' );
Debug::error( "Phonebooks: $id not updated" );
return false;
}
return true;
}
public function byUser( $limit = null ) {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
if ( empty( $limit ) ) {
$phonebooks = self::$db->get( $this->tableName, $whereClause );
} else {
$phonebooks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$phonebooks->count() ) {
Debug::info( 'No Phonebooks found.' );
return false;
}
return $this->filter( $phonebooks->results() );
}
public function getName( $id ) {
$phonebook = self::findById( $id );
return $phonebook->title;
}
public function simpleList( $param = '') {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$phonebooks = self::$db->get( $this->tableName, $whereClause );
if ( !$phonebooks->count() ) {
Debug::warn( 'Could not find any Phonebooks' );
return [];
}
$phonebooks = $phonebooks->results();
$out = [ 'None' => '0'];
foreach ( $phonebooks as &$phonebook ) {
$out[ $phonebook->title ] = $phonebook->ID;
}
return $out;
}
public function simpleObjectByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$phonebooks = self::$db->get( $this->tableName, $whereClause );
if ( !$phonebooks->count() ) {
Debug::warn( 'Could not find any Phonebooks' );
return false;
}
$phonebooks = $phonebooks->results();
$out = [];
foreach ( $phonebooks as &$phonebook ) {
$obj = new \stdClass();
$obj->title = $phonebook->title;
$obj->ID = $phonebook->ID;
$out[] = $obj;
}
return $out;
}
public function getTree() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$phonebooks = self::$db->get( $this->tableName, $whereClause );
if ( !$phonebooks->count() ) {
Debug::warn( 'Could not find any Phonebooks' );
return [];
}
$phonebooks = $phonebooks->results();
$formattedPhonebooks = [];
foreach ($phonebooks as $phonebook) {
if ( !empty($phonebook->phonebookID) ) {
$phonebookID = $phonebook->phonebookID;
if ( ! isset( $formattedPhonebooks[ $phonebookID ])) {
$formattedPhonebooks[ $phonebookID ][ 'phonebooks' ] = [];
}
$formattedPhonebooks[ $phonebookID ][ 'phonebooks' ][] = $phonebook;
} else {
$phonebookID = $phonebook->ID;
$formattedPhonebooks[ $phonebookID ][ 'phonebook' ] = $phonebook;
}
}
return $formattedPhonebooks;
}
}

View File

@ -1,57 +0,0 @@
<?php
/**
* app/plugins/contacts/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Contacts
* @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;
use TheTempusProject\Models\Contact;
use TheTempusProject\Models\Phonebook;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
class Contacts extends Plugin {
public $pluginName = 'TP Contacts';
public $configName = 'contacts';
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 contacts system.';
public $permissionMatrix = [
'useContacts' => [
'pretty' => 'Can use the contacts feature',
'default' => false,
],
];
public $main_links = [
[
'text' => 'Contacts',
'url' => '{ROOT_URL}contacts/index/',
'filter' => 'loggedin',
],
];
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Contacts.',
'default' => true,
],
];
// public $contacts;
// public $phonebooks;
public function __construct( $load = false ) {
// $this->contacts = new Contact;
// $this->phonebooks = new Phonebook;
parent::__construct( $load );
}
}

View File

@ -1,162 +0,0 @@
<legend>New Contact</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<fieldset>
<!-- Name -->
<div class="form-group">
<label for="avatar" class="col-lg-3 control-label">Avatar:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="avatar" id="avatar">
</div>
</div>
<div class="form-group">
<label for="first_name" class="col-lg-3 control-label">First Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="first_name" id="first_name">
</div>
</div>
<div class="form-group">
<label for="middle_name" class="col-lg-3 control-label">Middle Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="middle_name" id="middle_name">
</div>
</div>
<div class="form-group">
<label for="last_name" class="col-lg-3 control-label">Last Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="last_name" id="last_name">
</div>
</div>
<div class="form-group">
<label for="nickname" class="col-lg-3 control-label">Nickname:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="nickname" id="nickname">
</div>
</div>
<!-- Work -->
<div class="form-group">
<label for="company" class="col-lg-3 control-label">Company:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="company" id="company">
</div>
</div>
<div class="form-group">
<label for="job_title" class="col-lg-3 control-label">Job Title:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="job_title" id="job_title">
</div>
</div>
<!-- Email -->
<div class="form-group">
<label for="email" class="col-lg-3 control-label">Email:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="email" id="email">
</div>
</div>
<div class="form-group">
<label for="email_2" class="col-lg-3 control-label">Secondary Email:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="email_2" id="email_2">
</div>
</div>
<!-- Phone -->
<div class="form-group">
<label for="phone" class="col-lg-3 control-label">Phone:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="phone" id="phone">
</div>
</div>
<div class="form-group">
<label for="phone_2" class="col-lg-3 control-label">Secondary Phone:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="phone_2" id="phone_2">
</div>
</div>
<!-- Addresses -->
<div class="form-group">
<label for="address_1_primary" class="col-lg-3 control-label">Address Line 1:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_1_primary" id="address_1_primary">
</div>
</div>
<div class="form-group">
<label for="address_1_secondary" class="col-lg-3 control-label">Address Line 2:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_1_secondary" id="address_1_secondary">
</div>
</div>
<div class="form-group">
<label for="city" class="col-lg-3 control-label">City:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="city" id="city">
</div>
</div>
<div class="form-group">
<label for="state" class="col-lg-3 control-label">State:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="state" id="state">
</div>
</div>
<div class="form-group">
<label for="zipcode" class="col-lg-3 control-label">Zipcode:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="zipcode" id="zipcode">
</div>
</div>
<div class="form-group">
<label for="country" class="col-lg-3 control-label">Country:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="country" id="country">
</div>
</div>
<div class="form-group">
<label for="address_2_primary" class="col-lg-3 control-label">Address Line 1:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_2_primary" id="address_2_primary">
</div>
</div>
<div class="form-group">
<label for="address_2_secondary" class="col-lg-3 control-label">Address Line 2:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_2_secondary" id="address_2_secondary">
</div>
</div>
<div class="form-group">
<label for="city_2" class="col-lg-3 control-label">City:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="city_2" id="city_2">
</div>
</div>
<div class="form-group">
<label for="state_2" class="col-lg-3 control-label">State:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="state_2" id="state_2">
</div>
</div>
<div class="form-group">
<label for="zipcode_2" class="col-lg-3 control-label">Zipcode:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="zipcode_2" id="zipcode_2">
</div>
</div>
<div class="form-group">
<label for="country_2" class="col-lg-3 control-label">Country:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="country_2" id="country_2">
</div>
</div>
<!-- Misc -->
<div class="form-group">
<label for="notes" class="col-lg-3 control-label">Notes:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="notes" id="notes">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Create</button><br>
</div>
</div>
</fieldset>
</form>

View File

@ -1,162 +0,0 @@
<legend>Edit Contact</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<fieldset>
<!-- Name -->
<div class="form-group">
<label for="avatar" class="col-lg-3 control-label">Avatar:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="avatar" id="avatar" value="{avatar}">
</div>
</div>
<div class="form-group">
<label for="first_name" class="col-lg-3 control-label">First Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="first_name" id="first_name" value="{first_name}">
</div>
</div>
<div class="form-group">
<label for="middle_name" class="col-lg-3 control-label">Middle Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="middle_name" id="middle_name" value="{middle_name}">
</div>
</div>
<div class="form-group">
<label for="last_name" class="col-lg-3 control-label">Last Name:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="last_name" id="last_name" value="{last_name}">
</div>
</div>
<div class="form-group">
<label for="nickname" class="col-lg-3 control-label">Nickname:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="nickname" id="nickname" value="{nickname}">
</div>
</div>
<!-- Work -->
<div class="form-group">
<label for="company" class="col-lg-3 control-label">Company:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="company" id="company" value="{company}">
</div>
</div>
<div class="form-group">
<label for="job_title" class="col-lg-3 control-label">Job Title:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="job_title" id="job_title" value="{job_title}">
</div>
</div>
<!-- Email -->
<div class="form-group">
<label for="email" class="col-lg-3 control-label">Email:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="email" id="email" value="{email}">
</div>
</div>
<div class="form-group">
<label for="email_2" class="col-lg-3 control-label">Secondary Email:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="email_2" id="email_2" value="{email_2}">
</div>
</div>
<!-- Phone -->
<div class="form-group">
<label for="phone" class="col-lg-3 control-label">Phone:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="phone" id="phone" value="{phone}">
</div>
</div>
<div class="form-group">
<label for="phone_2" class="col-lg-3 control-label">Secondary Phone:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="phone_2" id="phone_2" value="{phone_2}">
</div>
</div>
<!-- Addresses -->
<div class="form-group">
<label for="address_1_primary" class="col-lg-3 control-label">Address Line 1:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_1_primary" id="address_1_primary" value="{address_1_primary}">
</div>
</div>
<div class="form-group">
<label for="address_1_secondary" class="col-lg-3 control-label">Address Line 2:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_1_secondary" id="address_1_secondary" value="{address_1_secondary}">
</div>
</div>
<div class="form-group">
<label for="city" class="col-lg-3 control-label">City:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="city" id="city" value="{city}">
</div>
</div>
<div class="form-group">
<label for="state" class="col-lg-3 control-label">State:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="state" id="state" value="{state}">
</div>
</div>
<div class="form-group">
<label for="zipcode" class="col-lg-3 control-label">Zipcode:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="zipcode" id="zipcode" value="{zipcode}">
</div>
</div>
<div class="form-group">
<label for="country" class="col-lg-3 control-label">Country:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="country" id="country" value="{country}">
</div>
</div>
<div class="form-group">
<label for="address_2_primary" class="col-lg-3 control-label">Address Line 1:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_2_primary" id="address_2_primary" value="{address_2_primary}">
</div>
</div>
<div class="form-group">
<label for="address_2_secondary" class="col-lg-3 control-label">Address Line 2:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="address_2_secondary" id="address_2_secondary" value="{address_2_secondary}">
</div>
</div>
<div class="form-group">
<label for="city_2" class="col-lg-3 control-label">City:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="city_2" id="city_2" value="{city_2}">
</div>
</div>
<div class="form-group">
<label for="state_2" class="col-lg-3 control-label">State:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="state_2" id="state_2" value="{state_2}">
</div>
</div>
<div class="form-group">
<label for="zipcode_2" class="col-lg-3 control-label">Zipcode:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="zipcode_2" id="zipcode_2" value="{zipcode_2}">
</div>
</div>
<div class="form-group">
<label for="country_2" class="col-lg-3 control-label">Country:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="country_2" id="country_2" value="{country_2}">
</div>
</div>
<!-- Misc -->
<div class="form-group">
<label for="notes" class="col-lg-3 control-label">Notes:</label>
<div class="col-lg-3">
<input class="form-control" type="text" name="notes" id="notes" value="{notes}">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Create</button><br>
</div>
</div>
</fieldset>
</form>

View File

@ -1,40 +0,0 @@
<div class="row" style="margin-top: 30px; margin-bottom: 50px;">
<form action="" method="post">
<table class="table table-striped">
<thead>
<tr>
<th style="width: 5%">ID</th>
<th style="width: 15%">Nickname</th>
<th style="width: 20%">First Name</th>
<th style="width: 20%">Last Name</th>
<th style="width: 15%">Email</th>
<th style="width: 15%">Phone</th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td><a href='{ROOT_URL}contacts/viewContact/{ID}'>{ID}</a></td>
<td>{nickname}</td>
<td>{first_name}</td>
<td>{last_name}</td>
<td>{email}</td>
<td>{phone}</td>
<td><a href="{ROOT_URL}contacts/editContact/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}contacts/deleteContact/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td colspan="7">
No Contacts
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}contacts/createContact" class="btn btn-sm btn-primary" role="button">Add Contact</a>
</form>
</div>

View File

@ -1,87 +0,0 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-{color}">
<div class="panel-heading">
<h3 class="panel-title">Contact</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="left" width="200"><b>First Name</b></td>
<td align="right">{first_name}</td>
</tr>
<tr>
<td align="left" width="200"><b>Middle Name</b></td>
<td align="right">{middle_name}</td>
</tr>
<tr>
<td align="left" width="200"><b>Last Name</b></td>
<td align="right">{last_name}</td>
</tr>
<tr>
<td align="left" width="200"><b>Nickname</b></td>
<td align="right">{nickname}</td>
</tr>
<tr>
<td align="left" width="200"><b>Company</b></td>
<td align="right">{company}</td>
</tr>
<tr>
<td align="left" width="200"><b>Job Title</b></td>
<td align="right">{job_title}</td>
</tr>
<tr>
<td align="left" width="200"><b>Email</b></td>
<td align="right">
{email}<br />
{email_2}
</td>
</tr>
<tr>
<td align="left" width="200"><b>Phone</b></td>
<td align="right">
{phone}<br />
{phone_2}
</td>
</tr>
<tr>
<td align="left" width="200"><b>Address 1</b></td>
<td align="right">
{address_1_primary}<br />
{address_1_secondary}<br />
{city} {state} {country} {zipcode}
</td>
</tr>
<tr>
<td align="left" width="200"><b>Address 2</b></td>
<td align="right">
{address_2_primary}<br />
{address_2_secondary}<br />
{city_2} {state_2} {country_2} {zipcode_2}
</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Notes</b></td>
</tr>
<tr>
<td colspan="2">{notes}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}contacts/editContact/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}contacts/deleteContact/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</div>
</div>
</div>
</div>

View File

@ -1,3 +0,0 @@
<legend>Contacts</legend>
{phonebookList}
{contactList}

View File

@ -1,35 +0,0 @@
<legend>Create Phonebook</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description"></textarea>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="dropdown" class="col-lg-3 control-label">Icon</label>
<div class="dropdow col-lg-3">
{iconSelect}
</div>
<input type="hidden" id="iconValue" name="icon" value="">
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,33 +0,0 @@
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<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-control" name="title" id="title" value="{title}">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-3 control-label">Description</label>
<div class="col-lg-3">
<textarea class="form-control" name="description" maxlength="2000" rows="10" cols="50" id="description">{description}</textarea>
</div>
</div>
<div class="form-group">
<label for="color" class="col-lg-3 control-label">Color</label>
<div class="col-lg-3 select-container" id="colorContainer">
{colorSelect}
</div>
</div>
<div class="form-group">
<label for="dropdown" class="col-lg-3 control-label">Icon</label>
<div class="dropdow col-lg-3">
{iconSelect}
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>

View File

@ -1,33 +0,0 @@
<div class="row" style="margin-top: 30px; margin-bottom: 50px;">
<form action="" method="post">
<table class="table table-striped">
<thead>
<tr>
<th style="width: 55%">Title</th>
<th style="width: 35%">Description</th>
<th style="width: 5%"></th>
<th style="width: 5%"></th>
</tr>
</thead>
<tbody>
{LOOP}
<tr>
<td><a href='{ROOT_URL}contacts/viewPhonebook/{ID}'>{title}</a></td>
<td>{description}</td>
<td><a href="{ROOT_URL}contacts/editPhonebook/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a></td>
<td><a href="{ROOT_URL}contacts/deletePhonebook/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td colspan="7">
No Phonebooks
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}contacts/createPhonebook" class="btn btn-sm btn-primary" role="button">New Phonebook</a>
</form>
</div>

View File

@ -1,45 +0,0 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-{color}">
<div class="panel-heading">
<h3 class="panel-title">Phonebook</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<tbody>
<tr>
<td align="left" width="200"><b>Title</b></td>
<td align="right">{title}</td>
</tr>
<tr>
<td align="left" width="200"><b>Icon</b></td>
<td align="right">{icon}</td>
</tr>
<tr>
<td align="left" width="200"><b>Color</b></td>
<td align="right">{color}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Description</b></td>
</tr>
<tr>
<td colspan="2">{description}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}contacts/editPhonebook/{ID}" class="btn btn-sm btn-warning" role="button"><i class="glyphicon glyphicon-edit"></i></a>
<a href="{ROOT_URL}contacts/deletePhonebook/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a>
</div>
</div>
</div>
</div>

View File

@ -1,121 +0,0 @@
<?php
/**
* app/plugins/fileshare/controllers/fileshare.php
*
* This is the home controller for the fileshare plugin.
*
* @package TP FileShare
* @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\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Upload;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Models\Upload as UploadModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Hermes\Functions\Redirect;
class Fileshare extends Controller {
protected static $uploads;
public function __construct() {
parent::__construct();
self::$uploads = new UploadModel;
self::$title = 'FileShare - {SITENAME}';
self::$pageDescription = 'Your saved Files for the site.';
}
public function index() {
$uploads = self::$uploads->getByUser();
Views::view( 'fileshare.list', $uploads );
}
public function delete( $id = null ) {
$upload = self::$uploads->findById( $id );
if ( $upload == false ) {
Issues::add( 'error', 'Upload not found.' );
return $this->index();
}
if ( $upload->createdBy != App::$activeUser->ID ) {
Issues::add( 'error', 'You do not have permission to modify this upload.' );
return $this->index();
}
$result = self::$uploads->delete( $id );
if ( $result == true ) {
Issues::add( 'success', 'Upload deleted.' );
} else {
Issues::add( 'notice', 'There was an problem deleting your upload.' );
}
return $this->index();
}
public function edit( $id = null ) {
$upload = self::$uploads->findById( $id );
if ( $upload == false ) {
Issues::add( 'error', 'Upload not found.' );
return $this->index();
}
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'calendar.calendar.edit', $upload );
}
if ( ! Forms::check( 'editUpload' ) ) {
Issues::add( 'error', [ 'There was an error editing your upload.' => Check::userErrors() ] );
return Views::view( 'fileshare.edit', $upload );
}
if ( Input::exists( 'file' ) ) {
$folder = UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( ! Upload::image( 'file', $folder ) ) {
Issues::add( 'error', [ 'There was an error with your upload.' => Check::systemErrors() ] );
return Views::view( 'fileshare.edit', $upload );
} else {
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$location = $route . Upload::last();
}
} else {
$location = '';
}
$result = self::$uploads->update( $id, Input::post('name'), $location );
if ( ! $result ) {
Issues::add( 'error', [ 'There was an error updating your upload.' => Check::userErrors() ] );
return Views::view( 'fileshare.edit', $upload );
}
Session::flash( 'success', 'Your Upload has been updated.' );
Redirect::to( 'fileshare/index/');
}
public function upload( $id = null ) {
if ( ! Input::exists() ) {
return Views::view( 'fileshare.create' );
}
if ( ! Forms::check( 'newUpload' ) ) {
Issues::add( 'error', [ 'There was an error creating your upload.' => Check::userErrors() ] );
return Views::view( 'fileshare.create' );
}
$folder = UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( ! Upload::image( 'file', $folder ) ) {
Issues::add( 'error', [ 'There was an error with your upload.' => Check::systemErrors() ] );
return Views::view( 'fileshare.create' );
} else {
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$location = $route . Upload::last();
}
$upload = self::$uploads->create( Input::post('name'), $location );
if ( ! $upload ) {
return Views::view( 'fileshare.create' );
}
Session::flash( 'success', 'Your Upload has been saved.' );
Redirect::to( 'fileshare/index/');
}
}

View File

@ -1,47 +0,0 @@
<?php
/**
* app/plugins/fileshare/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP FileShare
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Feedback;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class FileShareForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'newUpload', __CLASS__, 'newUpload' );
self::addHandler( 'editUpload', __CLASS__, 'editUpload' );
}
public static function newUpload() {
if ( ! Input::exists( 'name' ) ) {
Check::addUserError( 'You must provide a name.' );
return false;
}
if ( ! Input::exists( 'file' ) ) {
Check::addUserError( 'You must provide a file.' );
return false;
}
return true;
}
public static function editUpload() {
if ( ! Input::exists( 'name' ) ) {
Check::addUserError( 'You must provide a name.' );
return false;
}
return true;
}
}
new FileShareForms;

View File

@ -1,181 +0,0 @@
<?php
/**
* app/plugins/fileshare/models/upload.php
*
* This class is used for the manipulation of the uploads database table.
*
* @package TP FileShare
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Bedrock\Classes\CustomException;
class Upload extends DatabaseModel {
public $tableName = 'uploads';
public $databaseMatrix = [
[ 'name', 'varchar', '256' ],
[ 'location', 'text', '64' ],
[ 'size', 'int', '20' ],
[ 'file_type', 'varchar', '64' ],
[ 'createdBy', 'int', '11' ],
[ 'createdAt', 'int', '11' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
public function create( $name, $location ) {
$info = $this->getFileInfo( $location );
$size = $info['size'];
$file_type = $info['file_type'];
$fields = [
'name' => $name,
'location' => '/' . $location,
'size' => $size,
'file_type' => $file_type,
'createdAt' => time(),
'createdBy' => App::$activeUser->ID,
];
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
Debug::info( 'Uploads::create - failed to insert to db' );
return false;
}
return self::$db->lastId();
}
public function update( $id, $name, $location = '' ) {
$fields = [
'name' => $name,
];
if ( ! empty( $location ) ) {
$info = $this->getFileInfo( $location );
$fields['location'] = $location;
$fields['size'] = $info['size'];
$fields['file_type'] = $info['file_type'];
}
if ( ! self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'UploadsUpdate' );
Debug::error( "Uploads: $id not updated: $fields" );
return false;
}
return true;
}
public function simple() {
$uploads = self::$db->get( $this->tableName, '*' );
if ( !$uploads->count() ) {
Debug::warn( 'Could not find any uploads' );
return false;
}
$uploads = $uploads->results();
$out = [];
foreach ( $uploads as $upload ) {
$out[ $upload->name ] = $upload->location;
}
return $out;
}
// public function processFileUpload() {
// }
public function getByUser( $limit = 0 ) {
$whereClause = [
'createdBy', '=', App::$activeUser->ID,
];
if ( empty( $limit ) ) {
$uploads = self::$db->get( $this->tableName, $whereClause );
} else {
$uploads = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( ! $uploads->count() ) {
Debug::info( 'No Uploads found.' );
return false;
}
return $this->filter( $uploads->results() );
}
public function delete( $idArray ) {
if ( !is_array( $idArray ) ) {
$idArray = [ $idArray ];
}
foreach ( $idArray as $id ) {
$upload = self::findById( $id );
if ( $upload ) {
$fileLocation = APP_ROOT_DIRECTORY . ltrim( $upload->location, '/' );
if (file_exists($fileLocation)) {
if (!unlink($fileLocation)) {
Debug::error("Failed to delete file: $fileLocation");
return false;
}
} else {
Debug::warn("File does not exist: $fileLocation");
}
}
}
return parent::delete( $idArray );
}
private function getFileInfo( $imagePath )
{
$data = [];
$fileLocation = APP_ROOT_DIRECTORY . ltrim( $imagePath, '/' );
if ( file_exists( $fileLocation ) ) {
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($fileInfo, $fileLocation);
finfo_close($fileInfo);
$fileSize = filesize($fileLocation);
$data['file_type'] = $mimeType;
$data['size'] = $fileSize;
$data['path'] = $fileLocation;
} else {
Debug::warn("File does not exist: $fileLocation");
}
return $data;
}
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
if (isset($instance->size)) {
$instance->readableSize = $this->formatSize($instance->size);
}
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
public function formatSize($size) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($size >= 1024 && $i < count($units) - 1) {
$size /= 1024;
$i++;
}
return round($size, 2) . ' ' . $units[$i];
}
}

View File

@ -1,45 +0,0 @@
<?php
/**
* app/plugins/fileshare/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP FileShare
* @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\TheTempusProject as App;
use TheTempusProject\Classes\Plugin;
use TheTempusProject\Models\Upload;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Views;
class Fileshare extends Plugin {
public $pluginName = 'TP FileShare';
public $configName = 'fileshare';
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 file-sharing system.';
public $permissionMatrix = [
'uploadFiles' => [
'pretty' => 'Can upload files',
'default' => false,
],
];
private static $loaded = false;
public function __construct( $load = false ) {
parent::__construct( $load );
if ( $this->checkEnabled() && App::$isLoggedIn ) {
if ( ! self::$loaded ) {
App::$topNavRightDropdown .= '<li><a href="{ROOT_URL}fileshare/index/"><i class="glyphicon glyphicon-cloud"></i> FileShare</a></li>';
self::$loaded = true;
}
}
}
}

View File

@ -1,22 +0,0 @@
<legend>File Upload</legend>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group">
<label for="name" class="col-lg-3 control-label">Name</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="name" id="name">
</div>
</div>
<div class="form-group">
<label for="file" class="col-lg-3 control-label">File</label>
<div class="col-lg-3">
<input type="file" class="form-control" name="file" id="file">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Upload</button>
</div>
</div>
</form>

View File

@ -1,22 +0,0 @@
<legend>Edit File</legend>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group">
<label for="name" class="col-lg-3 control-label">Name</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="name" id="name" value="{name}">
</div>
</div>
<div class="form-group">
<label for="file" class="col-lg-3 control-label">File (uploading a new file will replace the existing file)</label>
<div class="col-lg-3">
<input type="file" class="form-control" name="file" id="file">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Save</button>
</div>
</div>
</form>

View File

@ -1,24 +0,0 @@
<div class="row">
{LOOP}
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<img src="{location}" alt="{name}">
<div class="caption">
<h3>{name}</h3>
<p>{readableSize}</p>
<p>{file_type}</p>
<p>
<a href="/fileshare/edit/{ID}" class="btn btn-warning" role="button">edit</a>
<a href="/fileshare/delete/{ID}" class="btn btn-danger" role="button">Delete</a>
</p>
</div>
</div>
</div>
{/LOOP}
{ALT}
<div class="col-sm-6 col-md-4">
No Uploads Found
</div>
{/ALT}
</div>
<a href="{ROOT_URL}fileshare/upload" class="btn btn-sm btn-primary" role="button">Upload File</a>

View File

@ -1,43 +0,0 @@
<?php
/**
* app/plugins/initiativetracker/controllers/initiative.php
*
* This is the home controller for the initiativetracker plugin.
*
* @package TP InitiativeTracker
* @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\Houdini\Classes\Views;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Models\Inithistory;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\TheTempusProject as App;
class Initiative extends Controller {
protected static $initiativeHistory;
public function __construct() {
self::$title = 'Initiative Tracker - {SITENAME}';
self::$pageDescription = 'This is for tracking player and creature turns and initiatives.';
self::$initiativeHistory = new Inithistory;
if ( ! App::$isLoggedIn ) {
Issues::add( 'info', 'If you register an account and log in, you can save your initiatives.' );
Components::set( 'initiativeHistory', '' );
} else {
$history = self::$initiativeHistory->userHistory();
$historyView = Views::simpleView( 'initiativetracker.historyElement', $history );
Components::set( 'initiativeHistory', $historyView );
}
parent::__construct();
}
public function index() {
Views::view( 'initiativetracker.index' );
}
}

View File

@ -1,35 +0,0 @@
.initiative-container {
margin-bottom: 50px;
}
.character-form {
margin-bottom: 50px;
}
.list-controls {
margin-bottom: 30px;
}
.round-footer {
padding-right: 15px;
padding-bottom: 20px;
padding-left: 15px;
}
.list-controls-center,.list-controls,.character-form {
display: flex;
justify-content: center;
align-items: center;
}
.list-controls-last {
display: flex;
justify-content: right;
align-items: right;
}
.list-controls-first {
display: flex;
justify-content: left;
align-items: left;
}

View File

@ -1,414 +0,0 @@
$(document).ready(function() {
// Top Checkbox Controls
$('input[type="checkbox"]').change(function() {
var name = $(this).attr('name');
if ( 'trackAC' == name ) {
var eles = document.getElementsByClassName('ac-tracker');
}
if ( 'trackHP' == name ) {
var eles = document.getElementsByClassName('hp-tracker');
}
if ( 'trackRounds' == name ) {
var eles = document.getElementsByClassName('rounds-tracker');
}
if ($(this).is(':checked')) {
Array.prototype.forEach.call(eles, function(ele) {
ele.style.display = '';
});
} else {
Array.prototype.forEach.call(eles, function(ele) {
ele.style.display = 'none';
});
}
});
$(document).on('click', '.character-remove', function() {
var characterId = $(this).closest('tr').data('character-id');
var allTrsWithCharacterId = document.querySelectorAll(`tr[data-round-character-id="${characterId}"]`);
allTrsWithCharacterId.forEach(function(tr) {
tr.remove();
});
var allTrsWithCharacterId = document.querySelectorAll(`tr[data-character-id="${characterId}"]`);
allTrsWithCharacterId.forEach(function(tr) {
tr.remove();
});
findByIdAndRemove( characterId, characterInitiativeList );
});
$(document).on('click', '.hp-minus', function() {
var characterId = $(this).closest('tr').data('character-id');
var dataValue = parseInt($(this).data('value'), 10);
var allTrsWithCharacterId = document.querySelectorAll(`tr[data-character-id="${characterId}"]`);
allTrsWithCharacterId.forEach(function(tr) {
var $input = $(tr).find('input[name="hp"]');
var newValue = parseInt($input.val(), 10) - dataValue;
$input.val(newValue);
});
var hpIndex = findById( characterId );
hpIndex.hp = parseInt(hpIndex.hp) - dataValue;
});
$(document).on('click', '.hp-plus', function() {
var characterId = $(this).closest('tr').data('character-id');
var dataValue = parseInt($(this).data('value'), 10);
var allTrsWithCharacterId = document.querySelectorAll(`tr[data-character-id="${characterId}"]`);
allTrsWithCharacterId.forEach(function(tr) {
var $input = $(tr).find('input[name="hp"]');
var newValue = parseInt($input.val(), 10) + dataValue;
$input.val(newValue);
});
var hpIndex = findById( characterId );
hpIndex.hp = parseInt(hpIndex.hp) + dataValue;
});
});
let currentCharacter;
let roundCount = 0;
let roundHistory = [];
let characterInitiativeList = [];
function findById(id) {
return characterInitiativeList.find(item => item.id === id);
}
function findByIdAndRemove(id, array) {
const index = array.findIndex(item => item.id === id);
if (index !== -1) {
array.splice(index, 1);
}
}
const resetCharBtn = document.getElementById('character-reset');
const resetCharacter = async function (event) {
formReset();
}
const resetListBtn = document.getElementById('list-reset');
const resetListEvent = async function (event) {
resetList();
}
const nextCharBtn = document.getElementById('list-next');
const nextCharacter = async function (event) {
cycleCharacter();
}
function cycleCharacter() {
if ( !currentCharacter ) {
currentCharacter = 0;
}
if ( currentCharacter == characterInitiativeList.length ) {
currentCharacter = 0;
roundCount++;
addRoundHistory();
updateRoundCount();
}
if ( !characterInitiativeList[currentCharacter] ) {
currentCharacter = 0;
}
var allTrs = document.querySelectorAll(`tr`);
allTrs.forEach(function(tr) {
tr.classList.remove("success");
});
var allTrsWithCharacterId = document.querySelectorAll(`tr[data-character-id="${characterInitiativeList[currentCharacter].id}"]`);
allTrsWithCharacterId.forEach(function(tr) {
tr.classList.add("success");
});
currentCharacter++;
}
const clearListBtn = document.getElementById('list-clear');
const clearListEvent = async function (event) {
clearList();
hideList();
}
const sortCharBtn = document.getElementById('character-sort');
const sortCharacters = async function (event) {
event.preventDefault();
sortthem();
}
function sortthem() {
var fieldName = document.getElementById('sortBy').value;
clearListHTML();
resetList();
var newList = sortCharacterListByField( fieldName, characterInitiativeList );
for (let i = 0; i < newList.length; i++) {
addCharacterRow( newList[i] );
}
}
function sortCharacterListByField( fieldName, list ) {
if ( 'id' == fieldName ) {
return list;
}
let sortedList = [...list];
sortedList.sort( ( a, b ) => {
let fieldA = a[fieldName];
let fieldB = b[fieldName];
if ( 'type' == fieldName || 'name' == fieldName ) {
return fieldA.localeCompare(fieldB);
}
if (fieldA > fieldB) {
return -1;
}
if (fieldA < fieldB) {
return 1;
}
return 0;
});
return sortedList;
}
const addCharBtn = document.getElementById('character-add');
const addCharacter = function (event) {
createCharacterFromForm();
}
function createCharacterFromForm() {
showList();
var id = Date.now().toString(36) + Math.random().toString(36).substring(2);;
var name = document.getElementById('name').value;
if ( !name ) {
name = '4816';
}
var initiative = document.getElementById('initiative').value;
if ( !initiative ) {
initiative = getRandomInt( 1, 21 );
}
initiative = parseInt( initiative );
var ac = document.getElementById('ac').value;
if ( !ac ) {
ac = 0;
}
ac = parseInt( ac );
var hp = document.getElementById('hp').value;
if ( !hp ) {
hp = 0;
}
hp = parseInt( hp );
var characterType = document.querySelector('input[name="characterType"]:checked').value;
var character = {
id: id,
name: name,
initiative: initiative,
ac: ac,
hp: hp,
type: characterType,
};
characterInitiativeList.push( character );
// re-order the list by initiative to keep the next character working properly
characterInitiativeList = sortCharacterListByField( 'initiative', characterInitiativeList );
addCharacterRow( character );
addRoundRow( character );
}
const d20Btn = document.getElementById('d20-roll');
const rollD20 = async function (event) {
event.preventDefault();
var resultDiv = document.getElementById('initiative');
resultDiv.value = getRandomInt( 1, 21 );
}
const resetRoundsBtn = document.getElementById('rounds-reset');
const resetRoundsEvent = async function (event) {
resetRoundCount();
}
const clearRoundsBtn = document.getElementById('rounds-clear');
const clearRoundsEvent = async function (event) {
clearRounds();
}
window.addEventListener('DOMContentLoaded', (event) => {
d20Btn.addEventListener('click', rollD20);
addCharBtn.addEventListener('click', addCharacter);
resetCharBtn.addEventListener('click', resetCharacter);
resetListBtn.addEventListener('click', resetListEvent);
clearListBtn.addEventListener('click', clearListEvent);
nextCharBtn.addEventListener('click', nextCharacter);
resetRoundsBtn.addEventListener('click', resetRoundsEvent);
clearRoundsBtn.addEventListener('click', clearRoundsEvent);
sortCharBtn.addEventListener('click', sortCharacters);
updateRoundCount();
});
function formReset() {
document.getElementById('name').value = null;
document.getElementById('initiative').value = null;
document.getElementById('ac').value = null;
document.getElementById('hp').value = null;
}
function showList() {
var tbody = document.getElementById('character-container');
tbody.style.display = '';
}
function addCharacterRow( character ) {
var characterList = document.getElementById('character-list');
var pcList = document.getElementById('character-list-pc');
var npcList = document.getElementById('character-list-npc');
var row = document.createElement('tr');
row.setAttribute('data-character-id', character.id);
var hpStyle = '';
var acStyle = '';
var checkbox = document.getElementById('trackHP');
if ( !checkbox.checked ) {
var hpStyle = 'style="display: none;"';
}
var checkbox = document.getElementById('trackAC');
if ( !checkbox.checked ) {
var acStyle = 'style="display: none;"';
}
row.innerHTML = `
<td>${character.name}</td>
<td class="text-center">${character.initiative}</td>
<td class="ac-tracker text-center"${acStyle}>${character.ac}</td>
<td class="hp-tracker text-center"${hpStyle}>
<div class="form-group hp-tracker list-controls-center">
<div class="input-group col-lg-6">
<div class="input-group-btn">
<button class="hp-minus btn btn-danger" data-value="1" aria-label="Remove 1 hp">-1</button>
<button class="hp-minus btn btn-danger" data-value="5" aria-label="Remove 5 hp">-5</button>
<button class="hp-minus btn btn-danger" data-value="10" aria-label="Remove 10 hp">-10</button>
</div>
<input type="number" class="form-control" name="hp" value="${character.hp}">
<div class="input-group-btn">
<button class="hp-plus btn btn-success" data-value="10" aria-label="Add 10 hp">+10</button>
<button class="hp-plus btn btn-success" data-value="5" aria-label="Add 5 hp">+5</button>
<button class="hp-plus btn btn-success" data-value="1" aria-label="Add 1 hp">+1</button>
</div>
</div>
</div>
</td>
<td class="text-center">${character.type}</td>
<td class="text-right"><button class="btn btn-danger btn-sm character-remove" aria-label="Add 10 hp"><span class="glyphicon glyphicon-trash"></span></button></td>
`;
// Append the new row to the table body
characterList.appendChild(row);
if ( 'pc' == character.type ) {
var pc_row = row.cloneNode(true);
pcList.appendChild(pc_row);
}
if ( 'npc' == character.type ) {
var npc_row = row.cloneNode(true);
npcList.appendChild(npc_row);
}
}
function hideList() {
var tbody = document.getElementById('character-container');
tbody.style.display = 'none';
}
function clearList() {
characterInitiativeList = [];
clearListHTML();
}
function clearListHTML() {
var tbody = document.getElementById('character-list');
tbody.innerHTML = '';
var tbody = document.getElementById('character-list-pc');
tbody.innerHTML = '';
var tbody = document.getElementById('character-list-npc');
tbody.innerHTML = '';
}
function resetList() {
currentCharacter = 0;
var allTrsWithCharacterId = document.querySelectorAll(`tr`);
allTrsWithCharacterId.forEach(function(tr) {
tr.classList.remove("success");
});
}
function clearRounds() {
var headers = document.getElementById('rounds-history-header-row');
var thElements = headers.getElementsByTagName('th');
// Loop through the th elements in reverse order to safely remove them without affecting the iteration
for (var i = thElements.length - 1; i > 0; i--) {
thElements[i].parentNode.removeChild(thElements[i]);
}
var history = document.getElementById('rounds-history');
var trElements = history.getElementsByTagName('tr');
for (var i = 0; i < trElements.length; i++) {
var elements = trElements[i].getElementsByTagName('td');
for (var x = elements.length - 1; x > 0; x--) {
elements[x].parentNode.removeChild(elements[x]);
}
}
roundCount = 0;
updateRoundCount();
}
function clearCharacters() {
var history = document.getElementById('rounds-history');
var trElements = history.getElementsByTagName('tr');
for (var i = 0; i < trElements.length; i++) {
trElements[i].remove();
}
}
function resetRoundCount() {
roundCount = 0;
updateRoundCount();
}
function updateRoundCount() {
var round = document.getElementById('round-count');
round.innerText = roundCount;
}
function addRoundRow( character ) {
var characterList = document.getElementById('rounds-history');
var row = document.createElement('tr');
row.setAttribute('data-round-character-id', character.id);
row.innerHTML = `<td class="char-name">${character.name}</td>`;
characterList.appendChild(row);
}
function addRoundHistory() {
var headers = document.getElementById('rounds-history-header');
var newHeader = document.createElement('th');
newHeader.innerText = "Round " + roundCount;
newHeader.classList.add("text-center");
headers.after( newHeader );
for (let i = 0; i < characterInitiativeList.length; i++) {
var char = characterInitiativeList[i];
var newCol = document.createElement('td');
newCol.innerText = char.hp;
newCol.classList.add("text-center");
var allTrsWithCharacterId = document.querySelector(`tr[data-round-character-id="${char.id}"]`);
var indexd = allTrsWithCharacterId.getElementsByClassName('char-name');
indexd[0].after( newCol );
}
}

View File

@ -1,105 +0,0 @@
<?php
/**
* app/plugins/feedback/models/feedback.php
*
* This class is used for the manipulation of the feedback database table.
*
* @todo make this send a confirmation email
*
* @package TP Feedback
* @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\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\Plugins\Feedback as Plugin;
use TheTempusProject\TheTempusProject as App;
class Inithistory extends DatabaseModel {
public $tableName = 'initiative_history';
public $databaseMatrix = [
[ 'name', 'varchar', '128' ],
[ 'time', 'int', '10' ],
[ 'email', 'varchar', '128' ],
[ 'ip', 'varchar', '64' ],
[ 'feedback', 'text', '' ],
];
public $plugin;
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
$this->plugin = new Plugin;
}
/**
* Saves a feedback form to the db.
*
* @param string $name -the name on the form
* @param string $email -the email provided
* @param string $feedback -contents of the feedback form.
* @return bool
*/
public function create( $name, $email, $feedback ) {
if ( !$this->plugin->checkEnabled() ) {
Debug::info( 'Feedback is disabled in the config.' );
return false;
}
$fields = [
'name' => $name,
'email' => $email,
'feedback' => $feedback,
'time' => time(),
'ip' => $_SERVER['REMOTE_ADDR'],
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
Debug::info( 'Feedback::create - failed to insert to db' );
return false;
}
return self::$db->lastId();
}
/**
* Function to clear feedback from the DB.
*
* @todo is there a way i could check for success here I'm pretty sure this is just a bad idea?
* @return bool
*/
public function clear() {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
self::$db->delete( $this->tableName, ['ID', '>=', '0'] );
self::$log->admin( 'Cleared Feedback' );
Debug::info( 'Feedback Cleared' );
return true;
}
public function userHistory( $limit = 0 ) {
$whereClause = [
'userID', '=', App::$activeUser->ID,
'AND',
'deletedAt', '=', '0',
'AND',
'expiresAt', '<', time(),
];
if ( empty( $limit ) ) {
$notifications = self::$db->get( $this->tableName, $whereClause );
} else {
$notifications = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$notifications->count() ) {
Debug::info( 'No Notifications found.' );
return false;
}
return $this->filter( $notifications->results() );
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* app/plugins/initiativetracker/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP InitiativeTracker
* @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 Initiativetracker extends Plugin {
public $pluginName = 'TP InitiativeTracker';
public $pluginAuthor = 'JoeyK';
public $pluginWebsite = 'https://TheTempusProject.com';
public $modelVersion = '1.0';
public $pluginVersion = '3.0';
public $pluginDescription = 'A simple plugin which adds a simple initiative tracker.';
public $configName = 'initiativeTracker';
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable tracker usage.',
'default' => true,
],
];
public $permissionMatrix = [
'useInitiativeTracker' => [
'pretty' => 'Can use the tracker',
'default' => true,
],
];
public $main_links = [
[
'text' => 'Initiative',
'url' => '{ROOT_URL}initiative/index',
],
];
}
// need to add a way to save the initiative history ( both personal, and for the table )
// need a way to save characters for the autofill
// need to add a "clear" button for initiative roller
// if you have tables, and are a DM, your group can access the initiative history

View File

@ -1,210 +0,0 @@
<link rel="stylesheet" href="{ROOT_URL}app/plugins/initiativetracker/css/initiative.css" crossorigin="anonymous">
<script src="{ROOT_URL}app/plugins/initiativetracker/js/initiative.js" type="module" defer></script>
<div id="initiative-container" class="initiative-container">
<div class="row">
<h2>Initiative Tracker</h2>
</div>
<!-- Tracker Settings -->
<div class="row well well-sm">
<div class="checkbox">
<label><input type="checkbox" name="trackHP" id="trackHP"> Track HP</label>
</div>
<div class="checkbox">
<label><input type="checkbox" name="trackAC" id="trackAC"> Track AC</label>
</div>
<div class="checkbox">
<label><input type="checkbox" name="trackRounds" id="trackRounds"> Track Rounds</label>
</div>
</div>
<!-- Character Form -->
<div id="character-form" class="character-form row well well-sm">
<div class="form-group col-lg-3">
<label for="name">Name</label>
<input type="text" class="form-control" name="name" id="name" autocomplete="on">
</div>
<div class="form-group col-lg-2">
<label for="initiative">Initiative</label>
<div class="input-group">
<div class="input-group-btn">
<button id="d20-roll" class="btn btn-default" aria-label="Roll a d20 for initiative.">Roll</button>
</div>
<input type="number" class="form-control" name="initiative" id="initiative">
</div>
</div>
<div class="form-group col-lg-1 ac-tracker" style="display: none;">
<label for="ac">AC</label>
<input type="number" class="form-control" name="ac" id="ac">
</div>
<div class="form-group col-lg-1 hp-tracker" style="display: none;">
<label for="hp">HP</label>
<input type="number" class="form-control" name="hp" id="hp">
</div>
<div class="form-group col-lg-2">
<label for="pc">Type</label><br>
<label class="radio-inline">
<input type="radio" name="characterType" id="pc" value="pc" checked> PC
</label>
<label class="radio-inline">
<input type="radio" name="characterType" id="npc" value="npc"> NPC
</label>
</div>
<div class="form-group col-lg-3">
<button id="character-add" class="btn btn-primary">
Add
</button>
<button id="character-reset" class="btn btn-danger">
Reset
</button>
</div>
</div>
<!-- Initiative List -->
<div id="character-container" style="display: none;">
<div id="initiative-list" class="well well-sm">
<!-- Tab Header -->
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a data-target="#cmb-list" role="tab" data-toggle="tab">
Combined
</a>
</li>
<li>
<a data-target="#pc-list" role="tab" data-toggle="tab">
PC
</a>
</li>
<li>
<a data-target="#npc-list" role="tab" data-toggle="tab">
NPC
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<!-- Combined List -->
<div role="tabpanel" class="tab-pane active" id="cmb-list">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th class="text-center">Initiative</th>
<th class="ac-tracker text-center" style="display: none;">AC</th>
<th class="hp-tracker text-center" style="display: none;">HP</th>
<th class="text-center">Type</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody id="character-list">
</tbody>
</table>
</div>
<!-- PC List -->
<div role="tabpanel" class="tab-pane" id="pc-list">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th class="text-center">Initiative</th>
<th class="ac-tracker text-center" style="display: none;">AC</th>
<th class="hp-tracker text-center" style="display: none;">HP</th>
<th class="text-center">Type</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody id="character-list-pc">
</tbody>
</table>
</div>
<!-- NPC List -->
<div role="tabpanel" class="tab-pane" id="npc-list">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th class="text-center">Initiative</th>
<th class="ac-tracker text-center" style="display: none;">AC</th>
<th class="hp-tracker text-center" style="display: none;">HP</th>
<th class="text-center">Type</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody id="character-list-npc">
</tbody>
</table>
</div>
</div>
<!-- List Controls -->
<div class="row list-controls row">
<div class="form-group col-lg-3">
<button id="list-reset" class="d4-die die-btn btn btn-warning">
Reset Round
</button>
</div>
<div class="form-group col-lg-3">
<div class="input-group">
<div class="input-group-btn">
<button id="character-sort" class="btn btn-primary" aria-label="Sort the List.">Sort</button>
</div>
<select class="form-control" id="sortBy">
<option value="initiative">Initiative</option>
<option value="name">Name</option>
<option value="ac">AC</option>
<option value="hp">HP</option>
<option value="type">Type</option>
</select>
</div>
</div>
<div class="form-group col-lg-3">
<button id="list-next" class="btn btn-success">
Next Character
</button>
</div>
<div class="form-group col-lg-3 list-controls-last">
<button id="list-clear" class="btn btn-danger">
Clear List
</button>
</div>
</div>
</div>
<!-- Round Controls -->
<div class="row rounds-tracker" style="display: none;">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Round History</h3>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr id="rounds-history-header-row">
<th id="rounds-history-header">Character</th>
</tr>
</thead>
<tbody id="rounds-history">
</tbody>
</table>
<div class="form-group col-lg-2">
<button id="rounds-clear" class="d4-die die-btn btn btn-danger">
Clear History
</button>
</div>
</div>
<div class="panel-footer round-footer">
<b>Round Count</b>: <span id="round-count"></span>
<span class="pull-right round-reset">
<button id="rounds-reset" class="btn btn-warning">
Reset Round Count
</button>
</span>
</div>
</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More