commit
2b5b9efb93
@ -0,0 +1,4 @@ |
||||
.env |
||||
protected/vendor/ |
||||
protected/composer.lock |
||||
protected/images/ |
||||
@ -0,0 +1,42 @@ |
||||
# cms |
||||
|
||||
Original Author: Matt Doyle |
||||
|
||||
Updates: Robert S. |
||||
|
||||
## SETUP from MySQL Root run: |
||||
``` |
||||
Change this password SJ6G*WyaV7PvvEts@vxjm below! |
||||
$ mysql -u root -p |
||||
> CREATE USER 'zoombox'@'localhost' IDENTIFIED BY 'SJ6G*WyaV7PvvEts@vxjm'; |
||||
> GRANT ALL ON cms.* TO 'zoombox'@'localhost'; |
||||
> create database cms; |
||||
``` |
||||
## Import the tables.sql file: |
||||
``` |
||||
$ mysql -u cms -p cms < tables.sql |
||||
enter this password when prompted: SJ6G*WyaV7PvvEts@vxjm |
||||
``` |
||||
|
||||
The config.php file is in the protected/src folder. |
||||
``` |
||||
define( "BLOG_NAME", "Widgetz Newz" ); // Display Name for Titles |
||||
``` |
||||
|
||||
## Create the .env secrets file |
||||
``` |
||||
$nano .env |
||||
DB_TYPE=mysql |
||||
DB_HOST=127.0.0.1 |
||||
DB_NAME=cms |
||||
DB_USERNAME=zoombox |
||||
DB_PASSWORD=SJ6G*WyaV7PvvEts@vxjm |
||||
ADMIN_USERS=zug:SunSet@XQWET,zig:546Poker@xzyWhy |
||||
``` |
||||
|
||||
## PHP Deps |
||||
``` |
||||
PHP gd and mysql must be installed: |
||||
$ sudo apt-get install php8.3-gd |
||||
$ sudo apt-get install php8.3-mysql |
||||
``` |
||||
@ -0,0 +1,5 @@ |
||||
{ |
||||
"require": { |
||||
"vlucas/phpdotenv": "^5.6" |
||||
} |
||||
} |
||||
@ -0,0 +1,314 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Class to handle articles |
||||
*/ |
||||
|
||||
class Article |
||||
{ |
||||
// Properties |
||||
|
||||
/** |
||||
* @var int The article ID from the database |
||||
*/ |
||||
public $id = null; |
||||
|
||||
/** |
||||
* @var int When the article is to be / was first published |
||||
*/ |
||||
public $publicationDate = null; |
||||
|
||||
/** |
||||
* @var int The article category ID |
||||
*/ |
||||
public $categoryId = null; |
||||
|
||||
/** |
||||
* @var string Full title of the article |
||||
*/ |
||||
public $title = null; |
||||
|
||||
/** |
||||
* @var string A short summary of the article |
||||
*/ |
||||
public $summary = null; |
||||
|
||||
/** |
||||
* @var string The HTML content of the article |
||||
*/ |
||||
public $content = null; |
||||
|
||||
/** |
||||
* @var string The filename extension of the article's full-size and thumbnail images (empty string means the article has no image) |
||||
*/ |
||||
public $imageExtension = ""; |
||||
|
||||
|
||||
/** |
||||
* Sets the object's properties using the values in the supplied array |
||||
* |
||||
* @param assoc The property values |
||||
*/ |
||||
|
||||
public function __construct( $data=array() ) { |
||||
if ( isset( $data['id'] ) ) $this->id = (int) $data['id']; |
||||
if ( isset( $data['publicationDate'] ) ) $this->publicationDate = (int) $data['publicationDate']; |
||||
if ( isset( $data['categoryId'] ) ) $this->categoryId = (int) $data['categoryId']; |
||||
if ( isset( $data['title'] ) ) $this->title = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['title'] ); |
||||
if ( isset( $data['summary'] ) ) $this->summary = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['summary'] ); |
||||
if ( isset( $data['content'] ) ) $this->content = $data['content']; |
||||
if ( isset( $data['imageExtension'] ) ) $this->imageExtension = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\$ a-zA-Z0-9()]/", "", $data['imageExtension'] ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Sets the object's properties using the edit form post values in the supplied array |
||||
* |
||||
* @param assoc The form post values |
||||
*/ |
||||
|
||||
public function storeFormValues( $params ) { |
||||
|
||||
// Store all the parameters |
||||
$this->__construct( $params ); |
||||
|
||||
// Parse and store the publication date |
||||
if ( isset($params['publicationDate']) ) { |
||||
$publicationDate = explode ( '-', $params['publicationDate'] ); |
||||
|
||||
if ( count($publicationDate) == 3 ) { |
||||
list ( $y, $m, $d ) = $publicationDate; |
||||
$this->publicationDate = mktime ( 0, 0, 0, $m, $d, $y ); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Stores any image uploaded from the edit form |
||||
* |
||||
* @param assoc The 'image' element from the $_FILES array containing the file upload data |
||||
*/ |
||||
|
||||
public function storeUploadedImage( $image ) { |
||||
|
||||
if ( $image['error'] == UPLOAD_ERR_OK ) |
||||
{ |
||||
// Does the Article object have an ID? |
||||
if ( is_null( $this->id ) ) trigger_error( "Article::storeUploadedImage(): Attempt to upload an image for an Article object that does not have its ID property set.", E_USER_ERROR ); |
||||
|
||||
// Delete any previous image(s) for this article |
||||
$this->deleteImages(); |
||||
|
||||
// Get and store the image filename extension |
||||
$this->imageExtension = strtolower( strrchr( $image['name'], '.' ) ); |
||||
|
||||
// Store the image |
||||
|
||||
$tempFilename = trim( $image['tmp_name'] ); |
||||
|
||||
if ( is_uploaded_file ( $tempFilename ) ) { |
||||
if ( !( move_uploaded_file( $tempFilename, $this->getImagePath() ) ) ) trigger_error( "Article::storeUploadedImage(): Couldn't move uploaded file.", E_USER_ERROR ); |
||||
if ( !( chmod( $this->getImagePath(), 0664 ) ) ) trigger_error( "Article::storeUploadedImage(): Couldn't set permissions on uploaded file.", E_USER_ERROR ); |
||||
} |
||||
|
||||
// Get the image size and type |
||||
$attrs = getimagesize ( $this->getImagePath() ); |
||||
$imageWidth = $attrs[0]; |
||||
$imageHeight = $attrs[1]; |
||||
$imageType = $attrs[2]; |
||||
|
||||
// Load the image into memory |
||||
switch ( $imageType ) { |
||||
case IMAGETYPE_GIF: |
||||
$imageResource = imagecreatefromgif ( $this->getImagePath() ); |
||||
break; |
||||
case IMAGETYPE_JPEG: |
||||
$imageResource = imagecreatefromjpeg ( $this->getImagePath() ); |
||||
break; |
||||
case IMAGETYPE_PNG: |
||||
$imageResource = imagecreatefrompng ( $this->getImagePath() ); |
||||
break; |
||||
default: |
||||
trigger_error ( "Article::storeUploadedImage(): Unhandled or unknown image type ($imageType)", E_USER_ERROR ); |
||||
} |
||||
|
||||
// Copy and resize the image to create the thumbnail |
||||
$thumbHeight = intval ( $imageHeight / $imageWidth * ARTICLE_THUMB_WIDTH ); |
||||
$thumbResource = imagecreatetruecolor ( ARTICLE_THUMB_WIDTH, $thumbHeight ); |
||||
imagecopyresampled( $thumbResource, $imageResource, 0, 0, 0, 0, ARTICLE_THUMB_WIDTH, $thumbHeight, $imageWidth, $imageHeight ); |
||||
|
||||
// Save the thumbnail |
||||
switch ( $imageType ) { |
||||
case IMAGETYPE_GIF: |
||||
imagegif ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ) ); |
||||
break; |
||||
case IMAGETYPE_JPEG: |
||||
imagejpeg ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ), JPEG_QUALITY ); |
||||
break; |
||||
case IMAGETYPE_PNG: |
||||
imagepng ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ) ); |
||||
break; |
||||
default: |
||||
trigger_error ( "Article::storeUploadedImage(): Unhandled or unknown image type ($imageType)", E_USER_ERROR ); |
||||
} |
||||
|
||||
$this->update(); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Deletes any images and/or thumbnails associated with the article |
||||
*/ |
||||
|
||||
public function deleteImages() { |
||||
|
||||
// Delete all fullsize images for this article |
||||
foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_FULLSIZE . "/" . $this->id . ".*") as $filename) { |
||||
if ( !unlink( $filename ) ) trigger_error( "Article::deleteImages(): Couldn't delete image file.", E_USER_ERROR ); |
||||
} |
||||
|
||||
// Delete all thumbnail images for this article |
||||
foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_THUMB . "/" . $this->id . ".*") as $filename) { |
||||
if ( !unlink( $filename ) ) trigger_error( "Article::deleteImages(): Couldn't delete thumbnail file.", E_USER_ERROR ); |
||||
} |
||||
|
||||
// Remove the image filename extension from the object |
||||
$this->imageExtension = ""; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns the relative path to the article's full-size or thumbnail image |
||||
* |
||||
* @param string The type of image path to retrieve (IMG_TYPE_FULLSIZE or IMG_TYPE_THUMB). Defaults to IMG_TYPE_FULLSIZE. |
||||
* @return string|false The image's path, or false if an image hasn't been uploaded |
||||
*/ |
||||
|
||||
public function getImagePath( $type=IMG_TYPE_FULLSIZE ) { |
||||
return ( $this->id && $this->imageExtension ) ? ( SITE_URL . "/image.php?rel={$type}&image=" . $this->id . $this->imageExtension ) : false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns an Article object matching the given article ID |
||||
* |
||||
* @param int The article ID |
||||
* @return Article|false The article object, or false if the record was not found or there was a problem |
||||
*/ |
||||
|
||||
public static function getById( $id ) { |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "SELECT *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM articles WHERE id = :id"; |
||||
$st = $conn->prepare( $sql ); |
||||
$st->bindValue( ":id", $id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$row = $st->fetch(); |
||||
$conn = null; |
||||
if ( $row ) return new Article( $row ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns all (or a range of) Article objects in the DB |
||||
* |
||||
* @param int Optional The number of rows to return (default=all) |
||||
* @param int Optional Return just articles in the category with this ID |
||||
* @return Array|false A two-element array : results => array, a list of Article objects; totalRows => Total number of articles |
||||
*/ |
||||
|
||||
public static function getList( $numRows=1000000, $categoryId=null ) { |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$categoryClause = $categoryId ? "WHERE categoryId = :categoryId" : ""; |
||||
$sql = "SELECT SQL_CALC_FOUND_ROWS *, UNIX_TIMESTAMP(publicationDate) AS publicationDate |
||||
FROM articles $categoryClause |
||||
ORDER BY publicationDate DESC LIMIT :numRows"; |
||||
|
||||
$st = $conn->prepare( $sql ); |
||||
$st->bindValue( ":numRows", $numRows, PDO::PARAM_INT ); |
||||
if ( $categoryId ) $st->bindValue( ":categoryId", $categoryId, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$list = array(); |
||||
|
||||
while ( $row = $st->fetch() ) { |
||||
$article = new Article( $row ); |
||||
$list[] = $article; |
||||
} |
||||
|
||||
// Now get the total number of articles that matched the criteria |
||||
$sql = "SELECT FOUND_ROWS() AS totalRows"; |
||||
$totalRows = $conn->query( $sql )->fetch(); |
||||
$conn = null; |
||||
return ( array ( "results" => $list, "totalRows" => $totalRows[0] ) ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Inserts the current Article object into the database, and sets its ID property. |
||||
*/ |
||||
|
||||
public function insert() { |
||||
|
||||
// Does the Article object already have an ID? |
||||
if ( !is_null( $this->id ) ) trigger_error ( "Article::insert(): Attempt to insert an Article object that already has its ID property set (to $this->id).", E_USER_ERROR ); |
||||
|
||||
// Insert the Article |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "INSERT INTO articles ( publicationDate, categoryId, title, summary, content, imageExtension ) VALUES ( FROM_UNIXTIME(:publicationDate), :categoryId, :title, :summary, :content, :imageExtension )"; |
||||
$st = $conn->prepare ( $sql ); |
||||
$st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT ); |
||||
$st->bindValue( ":categoryId", $this->categoryId, PDO::PARAM_INT ); |
||||
$st->bindValue( ":title", $this->title, PDO::PARAM_STR ); |
||||
$st->bindValue( ":summary", $this->summary, PDO::PARAM_STR ); |
||||
$st->bindValue( ":content", $this->content, PDO::PARAM_STR ); |
||||
$st->bindValue( ":imageExtension", $this->imageExtension, PDO::PARAM_STR ); |
||||
$st->execute(); |
||||
$this->id = $conn->lastInsertId(); |
||||
$conn = null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Updates the current Article object in the database. |
||||
*/ |
||||
|
||||
public function update() { |
||||
|
||||
// Does the Article object have an ID? |
||||
if ( is_null( $this->id ) ) trigger_error ( "Article::update(): Attempt to update an Article object that does not have its ID property set.", E_USER_ERROR ); |
||||
|
||||
// Update the Article |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "UPDATE articles SET publicationDate=FROM_UNIXTIME(:publicationDate), categoryId=:categoryId, title=:title, summary=:summary, content=:content, imageExtension=:imageExtension WHERE id = :id"; |
||||
$st = $conn->prepare ( $sql ); |
||||
$st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT ); |
||||
$st->bindValue( ":categoryId", $this->categoryId, PDO::PARAM_INT ); |
||||
$st->bindValue( ":title", $this->title, PDO::PARAM_STR ); |
||||
$st->bindValue( ":summary", $this->summary, PDO::PARAM_STR ); |
||||
$st->bindValue( ":content", $this->content, PDO::PARAM_STR ); |
||||
$st->bindValue( ":imageExtension", $this->imageExtension, PDO::PARAM_STR ); |
||||
$st->bindValue( ":id", $this->id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$conn = null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Deletes the current Article object from the database. |
||||
*/ |
||||
|
||||
public function delete() { |
||||
|
||||
// Does the Article object have an ID? |
||||
if ( is_null( $this->id ) ) trigger_error ( "Article::delete(): Attempt to delete an Article object that does not have its ID property set.", E_USER_ERROR ); |
||||
|
||||
// Delete the Article |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$st = $conn->prepare ( "DELETE FROM articles WHERE id = :id LIMIT 1" ); |
||||
$st->bindValue( ":id", $this->id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$conn = null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,161 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Class to handle article categories |
||||
*/ |
||||
|
||||
class Category |
||||
{ |
||||
// Properties |
||||
|
||||
/** |
||||
* @var int The category ID from the database |
||||
*/ |
||||
public $id = null; |
||||
|
||||
/** |
||||
* @var string Name of the category |
||||
*/ |
||||
public $name = null; |
||||
|
||||
/** |
||||
* @var string A short description of the category |
||||
*/ |
||||
public $description = null; |
||||
|
||||
|
||||
/** |
||||
* Sets the object's properties using the values in the supplied array |
||||
* |
||||
* @param assoc The property values |
||||
*/ |
||||
|
||||
public function __construct( $data=array() ) { |
||||
if ( isset( $data['id'] ) ) $this->id = (int) $data['id']; |
||||
if ( isset( $data['name'] ) ) $this->name = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['name'] ); |
||||
if ( isset( $data['description'] ) ) $this->description = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['description'] ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Sets the object's properties using the edit form post values in the supplied array |
||||
* |
||||
* @param assoc The form post values |
||||
*/ |
||||
|
||||
public function storeFormValues ( $params ) { |
||||
|
||||
// Store all the parameters |
||||
$this->__construct( $params ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns a Category object matching the given category ID |
||||
* |
||||
* @param int The category ID |
||||
* @return Category|false The category object, or false if the record was not found or there was a problem |
||||
*/ |
||||
|
||||
public static function getById( $id ) { |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "SELECT * FROM categories WHERE id = :id"; |
||||
$st = $conn->prepare( $sql ); |
||||
$st->bindValue( ":id", $id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$row = $st->fetch(); |
||||
$conn = null; |
||||
if ( $row ) return new Category( $row ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns all (or a range of) Category objects in the DB |
||||
* |
||||
* @param int Optional The number of rows to return (default=all) |
||||
* @return Array|false A two-element array : results => array, a list of Category objects; totalRows => Total number of categories |
||||
*/ |
||||
|
||||
public static function getList( $numRows=1000000 ) { |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "SELECT SQL_CALC_FOUND_ROWS * FROM categories |
||||
ORDER BY name ASC LIMIT :numRows"; |
||||
|
||||
$st = $conn->prepare( $sql ); |
||||
$st->bindValue( ":numRows", $numRows, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$list = array(); |
||||
|
||||
while ( $row = $st->fetch() ) { |
||||
$category = new Category( $row ); |
||||
$list[] = $category; |
||||
} |
||||
|
||||
// Now get the total number of categories that matched the criteria |
||||
$sql = "SELECT FOUND_ROWS() AS totalRows"; |
||||
$totalRows = $conn->query( $sql )->fetch(); |
||||
$conn = null; |
||||
return ( array ( "results" => $list, "totalRows" => $totalRows[0] ) ); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Inserts the current Category object into the database, and sets its ID property. |
||||
*/ |
||||
|
||||
public function insert() { |
||||
|
||||
// Does the Category object already have an ID? |
||||
if ( !is_null( $this->id ) ) trigger_error ( "Category::insert(): Attempt to insert a Category object that already has its ID property set (to $this->id).", E_USER_ERROR ); |
||||
|
||||
// Insert the Category |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "INSERT INTO categories ( name, description ) VALUES ( :name, :description )"; |
||||
$st = $conn->prepare ( $sql ); |
||||
$st->bindValue( ":name", $this->name, PDO::PARAM_STR ); |
||||
$st->bindValue( ":description", $this->description, PDO::PARAM_STR ); |
||||
$st->execute(); |
||||
$this->id = $conn->lastInsertId(); |
||||
$conn = null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Updates the current Category object in the database. |
||||
*/ |
||||
|
||||
public function update() { |
||||
|
||||
// Does the Category object have an ID? |
||||
if ( is_null( $this->id ) ) trigger_error ( "Category::update(): Attempt to update a Category object that does not have its ID property set.", E_USER_ERROR ); |
||||
|
||||
// Update the Category |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$sql = "UPDATE categories SET name=:name, description=:description WHERE id = :id"; |
||||
$st = $conn->prepare ( $sql ); |
||||
$st->bindValue( ":name", $this->name, PDO::PARAM_STR ); |
||||
$st->bindValue( ":description", $this->description, PDO::PARAM_STR ); |
||||
$st->bindValue( ":id", $this->id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$conn = null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Deletes the current Category object from the database. |
||||
*/ |
||||
|
||||
public function delete() { |
||||
|
||||
// Does the Category object have an ID? |
||||
if ( is_null( $this->id ) ) trigger_error ( "Category::delete(): Attempt to delete a Category object that does not have its ID property set.", E_USER_ERROR ); |
||||
|
||||
// Delete the Category |
||||
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); |
||||
$st = $conn->prepare ( "DELETE FROM categories WHERE id = :id LIMIT 1" ); |
||||
$st->bindValue( ":id", $this->id, PDO::PARAM_INT ); |
||||
$st->execute(); |
||||
$conn = null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,63 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @link https://www.elated.com/cms-in-an-afternoon-php-mysql/ |
||||
* https://www.elated.com/add-image-uploading-to-your-cms/ |
||||
* https://www.elated.com/mod-rewrite-tutorial-for-absolute-beginners/#friendly-urls |
||||
*/ |
||||
|
||||
define( "UPLOAD_RND_FILE_NAMES", false ); // Randomized file names to allow Duplicates? |
||||
define( "BLOG_NAME", "Widgetz Newz" ); // Display Name for Titles |
||||
define( "HOMEPAGE_NUM_ARTICLES", 5 ); |
||||
ini_set( "display_errors", false ); |
||||
date_default_timezone_set( "America/Detroit" ); // http://www.php.net/manual/en/timezones.php |
||||
|
||||
$d = dirname(__DIR__, 1); // Up one level for Composer Vendor DIR |
||||
require_once $d . '/vendor/autoload.php'; |
||||
$e = dirname(__DIR__,2); // Up two levels for ENV file |
||||
$dotenv = Dotenv\Dotenv::createImmutable($e); |
||||
$dotenv->load(); |
||||
|
||||
// Get the protocol (http or https) |
||||
$protocol = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http'; |
||||
// Get the domain name |
||||
$domain = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost'; |
||||
// Get the port number |
||||
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '80'; |
||||
// Combine for the SiteURL... |
||||
if ($port === "80" || $port === "443") { |
||||
define( "SITE_URL", "{$protocol}://{$domain}"); |
||||
} else { |
||||
define( "SITE_URL", "{$protocol}://{$domain}:{$port}"); |
||||
} |
||||
|
||||
define( "DB_DSN", $_ENV['DB_TYPE'].":host=".$_ENV['DB_HOST'].";dbname=".$_ENV['DB_NAME'] ); |
||||
define( "DB_USERNAME", $_ENV['DB_USERNAME'] ); |
||||
define( "DB_PASSWORD", $_ENV['DB_PASSWORD']); |
||||
define( "CLASS_PATH", "../protected/src/classes" ); |
||||
define( "TEMPLATE_PATH", "../protected/src/templates" ); |
||||
|
||||
$admins = $_ENV['ADMIN_USERS']; |
||||
$usersArray = explode(',', $admins); |
||||
$a_admins = []; |
||||
foreach ($usersArray as $user) { |
||||
list($username, $password) = explode(':', $user); |
||||
$a_admins[$username] = $password; |
||||
} |
||||
|
||||
define( "ADMIN_USERS", $a_admins); |
||||
define( "ARTICLE_IMAGE_PATH", "../protected/images/articles" ); |
||||
define( "IMG_TYPE_FULLSIZE", "fullsize" ); |
||||
define( "IMG_TYPE_THUMB", "thumb" ); |
||||
define( "ARTICLE_THUMB_WIDTH", 120 ); |
||||
define( "JPEG_QUALITY", 85 ); |
||||
require( CLASS_PATH . "/Article.php" ); |
||||
require( CLASS_PATH . "/Category.php" ); |
||||
|
||||
function handleException( $exception ) { |
||||
echo "Sorry, a problem occurred. Please try later."; |
||||
// echo $exception->getMessage(); |
||||
error_log( $exception->getMessage() ); |
||||
} |
||||
|
||||
set_exception_handler( 'handleException' ); |
||||
@ -0,0 +1,28 @@ |
||||
DROP TABLE IF EXISTS categories; |
||||
CREATE TABLE categories |
||||
( |
||||
id smallint unsigned NOT NULL auto_increment, |
||||
`name` varchar(255) NOT NULL, # Name of the category |
||||
description text NOT NULL, # A short description of the category |
||||
|
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
DROP TABLE IF EXISTS articles; |
||||
CREATE TABLE articles |
||||
( |
||||
id smallint unsigned NOT NULL auto_increment, |
||||
publicationDate date NOT NULL, # When the article was published |
||||
categoryId smallint unsigned NOT NULL, # The article category ID |
||||
title varchar(255) NOT NULL, # Full title of the article |
||||
summary text NOT NULL, # A short summary of the article |
||||
content mediumtext NOT NULL, # The HTML content of the article |
||||
imageExtension varchar(255) NOT NULL, # The filename extension of the article's full-size and thumbnail images |
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
# CREATE USER 'cms'@'localhost' IDENTIFIED BY 'SJ6G*WyaV7PvvEts@vxjm'; |
||||
|
||||
# GRANT ALL ON cms.* TO 'cms'@'localhost'; |
||||
|
||||
# create database cms; |
||||
@ -0,0 +1,197 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
<?php include TEMPLATE_PATH. "/admin/include/header.php" ?> |
||||
|
||||
<script type="text/javascript" src="assets/js/ckeditor.js"></script> |
||||
|
||||
<script> |
||||
// Prevents file upload hangs in Mac Safari |
||||
// Inspired by http://airbladesoftware.com/notes/note-to-self-prevent-uploads-hanging-in-safari |
||||
function closeKeepAlive() { |
||||
if ( /AppleWebKit|MSIE/.test( navigator.userAgent) ) { |
||||
var xhr = new XMLHttpRequest(); |
||||
xhr.open( "GET", "/ping/close", false ); |
||||
xhr.send(); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<h1><?php echo $results['pageTitle']?></h1>
|
||||
|
||||
<form action="admin.php?action=<?php echo $results['formAction']?>" method="post" enctype="multipart/form-data" onsubmit="closeKeepAlive()">
|
||||
<input type="hidden" name="articleId" value="<?php echo $results['article']->id ?>"/>
|
||||
|
||||
<?php if ( isset( $results['errorMessage'] ) ) { ?> |
||||
<div class="errorMessage"><?php echo $results['errorMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
<ul> |
||||
|
||||
<li> |
||||
<label for="title">Article Title</label> |
||||
<input type="text" name="title" id="title" placeholder="Name of the article" required autofocus maxlength="255" value="<?php echo htmlspecialchars( $results['article']->title )?>" />
|
||||
</li> |
||||
|
||||
<li> |
||||
<label for="summary">Article Summary</label> |
||||
<textarea name="summary" id="summary" placeholder="Brief description of the article" required maxlength="1000" style="height: 5em;"><?php echo htmlspecialchars( $results['article']->summary )?></textarea>
|
||||
</li> |
||||
|
||||
<li> |
||||
<label for="content">Article Content</label> |
||||
<textarea name="content" id="content" placeholder="The HTML content of the article" required maxlength="100000" style="height: 30em;"><?php echo htmlspecialchars( $results['article']->content ?? " ")?></textarea>
|
||||
</li> |
||||
<?php |
||||
$catID = $results['article']->categoryId ?? 0; |
||||
?> |
||||
<li> |
||||
<label for="categoryId">Article Category</label> |
||||
<select name="categoryId" id="categoryId"> |
||||
<option value="0"<?php echo ($catID > 0) ? " selected" : ""?>>(none)</option>
|
||||
<?php foreach ( $results['categories'] as $category ) { ?> |
||||
<option value="<?php echo $category->id?>"<?php echo ( $category->id == $catID ) ? " selected" : ""?>><?php echo htmlspecialchars( $category->name )?></option>
|
||||
<?php } ?> |
||||
</select> |
||||
</li> |
||||
|
||||
<li> |
||||
<label for="publicationDate">Publication Date</label> |
||||
<input type="date" name="publicationDate" id="publicationDate" placeholder="YYYY-MM-DD" required maxlength="10" value="<?php echo $results['article']->publicationDate ? date( "Y-m-d", $results['article']->publicationDate ) : "" ?>" />
|
||||
</li> |
||||
|
||||
<?php if ( $results['article'] && $imagePath = $results['article']->getImagePath() ) { ?> |
||||
<li> |
||||
<label>Current Image</label> |
||||
<img id="articleImage" src="<?php echo $imagePath ?>" alt="Article Image" />
|
||||
</li> |
||||
|
||||
<li> |
||||
<label></label> |
||||
<input type="checkbox" name="deleteImage" id="deleteImage" value="yes"/ > <label for="deleteImage">Delete</label> |
||||
</li> |
||||
<?php } ?> |
||||
|
||||
<li> |
||||
<label for="image">New Image</label> |
||||
<input type="file" name="image" id="image" placeholder="Choose an image to upload" maxlength="255" /> |
||||
</li> |
||||
|
||||
</ul> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" name="saveChanges" value="Save Changes" /> |
||||
<input type="submit" formnovalidate name="cancel" value="Cancel" /> |
||||
</div> |
||||
|
||||
</form> |
||||
|
||||
<?php if ( $results['article']->id ) { ?> |
||||
<p><a href="admin.php?action=deleteArticle&articleId=<?php echo $results['article']->id ?>" onclick="return confirm('Delete This Article?')">Delete This Article</a></p>
|
||||
<?php } ?> |
||||
|
||||
<script type="text/javascript"> |
||||
class MyUploadAdapter { |
||||
constructor(loader) { |
||||
// The file loader instance to use during the upload. |
||||
this.loader = loader; |
||||
} |
||||
|
||||
// Starts the upload process. |
||||
upload() { |
||||
return this.loader.file |
||||
.then(file => new Promise((resolve, reject) => { |
||||
this._initRequest(); |
||||
this._initListeners(resolve, reject, file); |
||||
this._sendRequest(file); |
||||
})); |
||||
} |
||||
|
||||
// Aborts the upload process. |
||||
abort() { |
||||
if (this.xhr) { |
||||
this.xhr.abort(); |
||||
} |
||||
} |
||||
|
||||
// Initializes the XMLHttpRequest object. |
||||
_initRequest() { |
||||
const xhr = this.xhr = new XMLHttpRequest(); |
||||
|
||||
// Set the request URL. |
||||
xhr.open('POST', 'upload.php', true); |
||||
|
||||
// Set up a progress listener to track the progress of the upload. |
||||
xhr.upload.addEventListener('progress', evt => { |
||||
if (evt.lengthComputable) { |
||||
this.loader.uploadTotal = evt.total; |
||||
this.loader.uploaded = evt.loaded; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// Initializes XMLHttpRequest listeners. |
||||
_initListeners(resolve, reject, file) { |
||||
const xhr = this.xhr; |
||||
const loader = this.loader; |
||||
const genericErrorText = `Couldn't upload file: ${file.name}.`; |
||||
|
||||
xhr.addEventListener('error', () => reject(genericErrorText)); |
||||
xhr.addEventListener('abort', () => reject()); |
||||
xhr.addEventListener('load', () => { |
||||
try { |
||||
const response = JSON.parse(xhr.response); |
||||
// console.log(response); |
||||
// Check if the upload was successful. |
||||
if (!response || response.error) { |
||||
alert(response.error); |
||||
return reject(response && response.error ? response.error.message : genericErrorText); |
||||
} |
||||
//console.info("Success!!!"); |
||||
//console.info(response.url); |
||||
// If the upload is successful, resolve the loader with the uploaded file's URL. |
||||
resolve({ |
||||
default: response.url |
||||
}); |
||||
} catch (error) { |
||||
console.error('An error occurred:', error); |
||||
} |
||||
}); |
||||
|
||||
if (xhr.upload) { |
||||
xhr.upload.addEventListener('progress', evt => { |
||||
if (evt.lengthComputable) { |
||||
loader.uploadTotal = evt.total; |
||||
loader.uploaded = evt.loaded; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
// Sends the request to the server. |
||||
_sendRequest(file) { |
||||
const data = new FormData(); |
||||
|
||||
// Append any additional parameters needed by your server. |
||||
// data.append('param1', 'value1'); |
||||
|
||||
// Append the file to the form data. |
||||
data.append('upload', file); |
||||
|
||||
// Send the request. |
||||
this.xhr.send(data); |
||||
} |
||||
} |
||||
|
||||
ClassicEditor |
||||
.create( document.querySelector( '#content' ), { |
||||
// toolbar: ['imageUpload', 'heading', '|', 'bold', 'italic', 'link'] |
||||
} ) |
||||
.then( editor => { |
||||
editor.plugins.get('FileRepository').createUploadAdapter = loader => new MyUploadAdapter(loader); |
||||
window.editor = editor; |
||||
} ) |
||||
.catch( err => { |
||||
console.error( err.stack ); |
||||
} ); |
||||
</script> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,38 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
<?php include TEMPLATE_PATH. "/admin/include/header.php" ?> |
||||
|
||||
<h1><?php echo $results['pageTitle']?></h1>
|
||||
|
||||
<form action="admin.php?action=<?php echo $results['formAction']?>" method="post">
|
||||
<input type="hidden" name="categoryId" value="<?php echo $results['category']->id ?>"/>
|
||||
|
||||
<?php if ( isset( $results['errorMessage'] ) ) { ?> |
||||
<div class="errorMessage"><?php echo $results['errorMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
<ul> |
||||
|
||||
<li> |
||||
<label for="name">Category Name</label> |
||||
<input type="text" name="name" id="name" placeholder="Name of the category" required autofocus maxlength="255" value="<?php echo htmlspecialchars( $results['category']->name )?>" />
|
||||
</li> |
||||
|
||||
<li> |
||||
<label for="description">Description</label> |
||||
<textarea name="description" id="description" placeholder="Brief description of the category" required maxlength="1000" style="height: 5em;"><?php echo htmlspecialchars( $results['category']->description )?></textarea>
|
||||
</li> |
||||
|
||||
</ul> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" name="saveChanges" value="Save Changes" /> |
||||
<input type="submit" formnovalidate name="cancel" value="Cancel" /> |
||||
</div> |
||||
|
||||
</form> |
||||
|
||||
<?php if ( $results['category']->id ) { ?> |
||||
<p><a href="admin.php?action=deleteCategory&categoryId=<?php echo $results['category']->id ?>" onclick="return confirm('Delete This Category?')">Delete This Category</a></p>
|
||||
<?php } ?> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,4 @@ |
||||
<div id="adminHeader"> |
||||
<h2><?= BLOG_NAME ?> Admin</h2>
|
||||
<p>You are logged in as <b><?php echo htmlspecialchars( $_SESSION['username']) ?></b>. <a href="admin.php?action=listArticles">Edit Articles</a> <a href="admin.php?action=listCategories">Edit Categories</a> <a href="admin.php?action=logout"?>Log Out</a></p>
|
||||
</div> |
||||
@ -0,0 +1,46 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
<?php include TEMPLATE_PATH. "/admin/include/header.php" ?> |
||||
|
||||
<h1>All Articles</h1> |
||||
|
||||
<?php if ( isset( $results['errorMessage'] ) ) { ?> |
||||
<div class="errorMessage"><?php echo $results['errorMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
|
||||
<?php if ( isset( $results['statusMessage'] ) ) { ?> |
||||
<div class="statusMessage"><?php echo $results['statusMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
<table> |
||||
<tr> |
||||
<th>Publication Date</th> |
||||
<th>Article</th> |
||||
<th>Category</th> |
||||
</tr> |
||||
|
||||
<?php foreach ( $results['articles'] as $article ) { ?> |
||||
|
||||
<tr onclick="location='admin.php?action=editArticle&articleId=<?php echo $article->id?>'">
|
||||
<td><?php echo date('j M Y', $article->publicationDate)?></td>
|
||||
<td> |
||||
<?php echo $article->title?> |
||||
</td> |
||||
<td> |
||||
<?php
|
||||
if ( isset( $results['categories'] ) && isset( $results['categories'][$article->categoryId] ) ) { |
||||
echo $results['categories'][$article->categoryId]?->name; |
||||
} |
||||
?> |
||||
</td> |
||||
</tr> |
||||
|
||||
<?php } ?> |
||||
|
||||
</table> |
||||
|
||||
<p><?php echo $results['totalRows']?> article<?php echo ( $results['totalRows'] != 1 ) ? 's' : '' ?> in total.</p>
|
||||
|
||||
<p><a href="admin.php?action=newArticle">Add a New Article</a></p> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,36 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
<?php include TEMPLATE_PATH. "/admin/include/header.php" ?> |
||||
|
||||
<h1>Article Categories</h1> |
||||
|
||||
<?php if ( isset( $results['errorMessage'] ) ) { ?> |
||||
<div class="errorMessage"><?php echo $results['errorMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
|
||||
<?php if ( isset( $results['statusMessage'] ) ) { ?> |
||||
<div class="statusMessage"><?php echo $results['statusMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
<table> |
||||
<tr> |
||||
<th>Category</th> |
||||
</tr> |
||||
|
||||
<?php foreach ( $results['categories'] as $category ) { ?> |
||||
|
||||
<tr onclick="location='admin.php?action=editCategory&categoryId=<?php echo $category->id?>'">
|
||||
<td> |
||||
<?php echo $category->name?> |
||||
</td> |
||||
</tr> |
||||
|
||||
<?php } ?> |
||||
|
||||
</table> |
||||
|
||||
<p><?php echo $results['totalRows']?> categor<?php echo ( $results['totalRows'] != 1 ) ? 'ies' : 'y' ?> in total.</p>
|
||||
|
||||
<p><a href="admin.php?action=newCategory">Add a New Category</a></p> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,30 @@ |
||||
<?php include TEMPLATE_PATH."/include/header.php" ?> |
||||
|
||||
<form action="admin.php?action=login" method="post" style="width: 50%;"> |
||||
<input type="hidden" name="login" value="true" /> |
||||
|
||||
<?php if ( isset( $results['errorMessage'] ) ) { ?> |
||||
<div class="errorMessage"><?php echo $results['errorMessage'] ?></div>
|
||||
<?php } ?> |
||||
|
||||
<ul> |
||||
|
||||
<li> |
||||
<label for="username">Username</label> |
||||
<input type="text" name="username" id="username" placeholder="Your admin username" required autofocus maxlength="20" /> |
||||
</li> |
||||
|
||||
<li> |
||||
<label for="password">Password</label> |
||||
<input type="password" name="password" id="password" placeholder="Your admin password" required maxlength="20" /> |
||||
</li> |
||||
|
||||
</ul> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" name="login" value="Login" /> |
||||
</div> |
||||
|
||||
</form> |
||||
|
||||
<?php include TEMPLATE_PATH."/include/footer.php"; |
||||
@ -0,0 +1,35 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
|
||||
<h1><?php echo htmlspecialchars( $results['pageHeading'] ) ?></h1>
|
||||
<?php if ( $results['category'] ) { ?> |
||||
<h3 class="categoryDescription"><?php echo htmlspecialchars( $results['category']->description ) ?></h3>
|
||||
<?php } ?> |
||||
|
||||
<ul id="headlines" class="archive"> |
||||
|
||||
<?php foreach ( $results['articles'] as $article ) { ?> |
||||
|
||||
<li> |
||||
<h2> |
||||
<span class="pubDate"><?php echo date('j F Y', $article->publicationDate)?></span><a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><?php echo htmlspecialchars( $article->title )?></a>
|
||||
<?php if ( !$results['category'] && $article->categoryId ) { ?> |
||||
<span class="category">in <a href=".?action=archive&categoryId=<?php echo $article->categoryId?>"><?php echo htmlspecialchars( $results['categories'][$article->categoryId]->name ) ?></a></span>
|
||||
<?php } ?>
|
||||
</h2> |
||||
<p class="summary"> |
||||
<?php if ( $imagePath = $article->getImagePath( IMG_TYPE_THUMB ) ) { ?> |
||||
<a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><img class="articleImageThumb" src="<?php echo $imagePath?>" alt="Article Thumbnail" /></a>
|
||||
<?php } ?> |
||||
<?php echo htmlspecialchars( $article->summary )?> |
||||
</p> |
||||
</li> |
||||
|
||||
<?php } ?> |
||||
|
||||
</ul> |
||||
|
||||
<p><?php echo $results['totalRows']?> article<?php echo ( $results['totalRows'] != 1 ) ? 's' : '' ?> in total.</p>
|
||||
|
||||
<p><a href="./">Return to Homepage</a></p> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,34 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
|
||||
<ul id="headlines"> |
||||
|
||||
<?php
|
||||
$c = 0; |
||||
foreach ( $results['articles'] as $article ) { ?> |
||||
|
||||
<li> |
||||
<h2> |
||||
<span class="pubDate"><?php echo date('j M Y', $article->publicationDate)?></span><a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><?php echo htmlspecialchars( $article->title )?></a>
|
||||
<?php if ( $article->categoryId ) { ?> |
||||
<span class="category">in <a href=".?action=archive&categoryId=<?php echo $article->categoryId?>"><?php echo htmlspecialchars( $results['categories'][$article->categoryId]->name )?></a></span>
|
||||
<?php } ?>
|
||||
</h2> |
||||
<p class="summary"> |
||||
<?php if ( $imagePath = $article->getImagePath( IMG_TYPE_THUMB ) ) { ?> |
||||
<a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><img class="articleImageThumb" src="<?php echo $imagePath?>" alt="Article Thumbnail" /></a>
|
||||
<?php } ?> |
||||
<?php echo htmlspecialchars( $article->summary )?> |
||||
</p> |
||||
</li> |
||||
|
||||
<?php |
||||
$c++; |
||||
} ?> |
||||
|
||||
</ul> |
||||
|
||||
<?php if ($c < $results['totalRows']) { ?> |
||||
<p><a href="./?action=archive">Article Archive</a></p> |
||||
<?php } ?> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,7 @@ |
||||
<div id="footer"> |
||||
<?= BLOG_NAME ?> © <?= date('Y') ?>. All rights reserved. <a href="admin.php">Site Admin</a>
|
||||
</div> |
||||
|
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,12 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<title><?php echo htmlspecialchars( $results['pageTitle'] )?></title>
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/style.css" /> |
||||
</head> |
||||
<body> |
||||
<div id="container"> |
||||
|
||||
<a href="."><img id="logo" src="assets/images/logo.jpg" alt="Logo for Blog post" /></a> |
||||
<div id="logo-hr"></div> |
||||
|
||||
@ -0,0 +1,19 @@ |
||||
<?php include TEMPLATE_PATH. "/include/header.php" ?> |
||||
|
||||
<h1 style="width: 75%;"><?php echo htmlspecialchars( $results['article']->title )?></h1>
|
||||
<div style="width: 75%; font-style: italic;"><?php echo htmlspecialchars( $results['article']->summary ) ?></div>
|
||||
<div style="width: 75%; min-height: 300px;"> |
||||
<?php if ( $imagePath = $results['article']->getImagePath() ) { ?> |
||||
<img id="articleImageFullsize" src="<?php echo $imagePath ?>" alt="Article Image" />
|
||||
<?php } ?> |
||||
<?php echo $results['article']->content ?> |
||||
</div> |
||||
<p class="pubDate">Published on <?php echo date('j F Y', $results['article']->publicationDate)?> |
||||
<?php if ( $results['category'] ) { ?> |
||||
in <a href="./?action=archive&categoryId=<?php echo $results['category']->id?>"><?php echo htmlspecialchars( $results['category']->name ) ?></a>
|
||||
<?php } ?>
|
||||
</p> |
||||
|
||||
<p><a href="./">Return to Homepage</a></p> |
||||
|
||||
<?php include TEMPLATE_PATH. "/include/footer.php"; |
||||
@ -0,0 +1,304 @@ |
||||
<?php |
||||
|
||||
require "../protected/src/config.php"; |
||||
session_start(); |
||||
$action = isset( $_GET['action'] ) ? $_GET['action'] : ""; |
||||
$username = isset( $_SESSION['username'] ) ? $_SESSION['username'] : ""; |
||||
|
||||
if ( $action != "login" && $action != "logout" && !$username ) { |
||||
login(); |
||||
exit; |
||||
} |
||||
|
||||
switch ( $action ) { |
||||
case 'login': |
||||
login(); |
||||
break; |
||||
case 'logout': |
||||
logout(); |
||||
break; |
||||
case 'newArticle': |
||||
newArticle(); |
||||
break; |
||||
case 'editArticle': |
||||
editArticle(); |
||||
break; |
||||
case 'deleteArticle': |
||||
deleteArticle(); |
||||
break; |
||||
case 'listCategories': |
||||
listCategories(); |
||||
break; |
||||
case 'newCategory': |
||||
newCategory(); |
||||
break; |
||||
case 'editCategory': |
||||
editCategory(); |
||||
break; |
||||
case 'deleteCategory': |
||||
deleteCategory(); |
||||
break; |
||||
default: |
||||
listArticles(); |
||||
} |
||||
|
||||
// Function to check if a user and password match |
||||
function isValidUser($username, $password) { |
||||
// Check if the username exists in the ADMIN_USERS array |
||||
if (array_key_exists($username, ADMIN_USERS)) { |
||||
// Check if the provided password matches the stored password for the username |
||||
if (ADMIN_USERS[$username] === $password) { |
||||
return true; // Username and password are valid |
||||
} |
||||
} |
||||
return false; // Username and/or password are invalid |
||||
} |
||||
|
||||
function invalidUser() { |
||||
// Login failed: display an error message to the user |
||||
$results['errorMessage'] = "Incorrect username or password. Please try again."; |
||||
require( TEMPLATE_PATH . "/admin/loginForm.php" ); |
||||
exit; |
||||
} |
||||
|
||||
function login() { |
||||
|
||||
$results = array(); |
||||
$results['pageTitle'] = "Admin Login | Widget News"; |
||||
|
||||
if ( isset( $_POST['login'] ) ) { |
||||
|
||||
$usr = $_POST['username'] ?? false; |
||||
$pwd = $_POST['password'] ?? false; |
||||
if ($usr === false || $pwd === false) { |
||||
invalidUser(); |
||||
} |
||||
|
||||
// User has posted the login form: attempt to log the user in |
||||
if (isValidUser($usr, $pwd)) { |
||||
|
||||
// Login successful: Create a session and redirect to the admin homepage |
||||
$_SESSION['username'] = $usr; |
||||
header( "Location: admin.php" ); |
||||
|
||||
} else { |
||||
invalidUser(); |
||||
} |
||||
|
||||
} else { |
||||
|
||||
// User has not posted the login form yet: display the form |
||||
require( TEMPLATE_PATH . "/admin/loginForm.php" ); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
function logout() { |
||||
unset( $_SESSION['username'] ); |
||||
header( "Location: admin.php" ); |
||||
} |
||||
|
||||
|
||||
function newArticle() { |
||||
|
||||
$results = array(); |
||||
$results['pageTitle'] = "New Article"; |
||||
$results['formAction'] = "newArticle"; |
||||
|
||||
if ( isset( $_POST['saveChanges'] ) ) { |
||||
|
||||
// User has posted the article edit form: save the new article |
||||
$article = new Article; |
||||
$article->storeFormValues( $_POST ); |
||||
$article->insert(); |
||||
if ( isset( $_FILES['image'] ) ) $article->storeUploadedImage( $_FILES['image'] ); |
||||
header( "Location: admin.php?status=changesSaved" ); |
||||
|
||||
} elseif ( isset( $_POST['cancel'] ) ) { |
||||
|
||||
// User has cancelled their edits: return to the article list |
||||
header( "Location: admin.php" ); |
||||
} else { |
||||
|
||||
// User has not posted the article edit form yet: display the form |
||||
$results['article'] = new Article; |
||||
$data = Category::getList(); |
||||
$results['categories'] = $data['results']; |
||||
require( TEMPLATE_PATH . "/admin/editArticle.php" ); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
function editArticle() { |
||||
|
||||
$results = array(); |
||||
$results['pageTitle'] = "Edit Article"; |
||||
$results['formAction'] = "editArticle"; |
||||
|
||||
if ( isset( $_POST['saveChanges'] ) ) { |
||||
|
||||
// User has posted the article edit form: save the article changes |
||||
|
||||
if ( !$article = Article::getById( (int)$_POST['articleId'] ) ) { |
||||
header( "Location: admin.php?error=articleNotFound" ); |
||||
return; |
||||
} |
||||
|
||||
$article->storeFormValues( $_POST ); |
||||
if ( isset($_POST['deleteImage']) && $_POST['deleteImage'] == "yes" ) $article->deleteImages(); |
||||
$article->update(); |
||||
if ( isset( $_FILES['image'] ) ) $article->storeUploadedImage( $_FILES['image'] ); |
||||
header( "Location: admin.php?status=changesSaved" ); |
||||
|
||||
} elseif ( isset( $_POST['cancel'] ) ) { |
||||
|
||||
// User has cancelled their edits: return to the article list |
||||
header( "Location: admin.php" ); |
||||
} else { |
||||
|
||||
// User has not posted the article edit form yet: display the form |
||||
$results['article'] = Article::getById( (int)$_GET['articleId'] ); |
||||
$data = Category::getList(); |
||||
$results['categories'] = $data['results']; |
||||
require( TEMPLATE_PATH . "/admin/editArticle.php" ); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
function deleteArticle() { |
||||
|
||||
if ( !$article = Article::getById( (int)$_GET['articleId'] ) ) { |
||||
header( "Location: admin.php?error=articleNotFound" ); |
||||
return; |
||||
} |
||||
|
||||
$article->deleteImages(); |
||||
$article->delete(); |
||||
header( "Location: admin.php?status=articleDeleted" ); |
||||
} |
||||
|
||||
|
||||
function listArticles() { |
||||
$results = array(); |
||||
$data = Article::getList(); |
||||
$results['articles'] = $data['results']; |
||||
$results['totalRows'] = $data['totalRows']; |
||||
$data = Category::getList(); |
||||
$results['categories'] = array(); |
||||
foreach ( $data['results'] as $category ) $results['categories'][$category->id] = $category; |
||||
$results['pageTitle'] = "All Articles"; |
||||
|
||||
if ( isset( $_GET['error'] ) ) { |
||||
if ( $_GET['error'] == "articleNotFound" ) $results['errorMessage'] = "Error: Article not found."; |
||||
} |
||||
|
||||
if ( isset( $_GET['status'] ) ) { |
||||
if ( $_GET['status'] == "changesSaved" ) $results['statusMessage'] = "Your changes have been saved."; |
||||
if ( $_GET['status'] == "articleDeleted" ) $results['statusMessage'] = "Article deleted."; |
||||
} |
||||
|
||||
require( TEMPLATE_PATH . "/admin/listArticles.php" ); |
||||
} |
||||
|
||||
function listCategories() { |
||||
$results = array(); |
||||
$data = Category::getList(); |
||||
$results['categories'] = $data['results']; |
||||
$results['totalRows'] = $data['totalRows']; |
||||
$results['pageTitle'] = "Article Categories"; |
||||
|
||||
if ( isset( $_GET['error'] ) ) { |
||||
if ( $_GET['error'] == "categoryNotFound" ) $results['errorMessage'] = "Error: Category not found."; |
||||
if ( $_GET['error'] == "categoryContainsArticles" ) $results['errorMessage'] = "Error: Category contains articles. Delete the articles, or assign them to another category, before deleting this category."; |
||||
} |
||||
|
||||
if ( isset( $_GET['status'] ) ) { |
||||
if ( $_GET['status'] == "changesSaved" ) $results['statusMessage'] = "Your changes have been saved."; |
||||
if ( $_GET['status'] == "categoryDeleted" ) $results['statusMessage'] = "Category deleted."; |
||||
} |
||||
|
||||
require( TEMPLATE_PATH . "/admin/listCategories.php" ); |
||||
} |
||||
|
||||
|
||||
function newCategory() { |
||||
|
||||
$results = array(); |
||||
$results['pageTitle'] = "New Article Category"; |
||||
$results['formAction'] = "newCategory"; |
||||
|
||||
if ( isset( $_POST['saveChanges'] ) ) { |
||||
|
||||
// User has posted the category edit form: save the new category |
||||
$category = new Category; |
||||
$category->storeFormValues( $_POST ); |
||||
$category->insert(); |
||||
header( "Location: admin.php?action=listCategories&status=changesSaved" ); |
||||
|
||||
} elseif ( isset( $_POST['cancel'] ) ) { |
||||
|
||||
// User has cancelled their edits: return to the category list |
||||
header( "Location: admin.php?action=listCategories" ); |
||||
} else { |
||||
|
||||
// User has not posted the category edit form yet: display the form |
||||
$results['category'] = new Category; |
||||
require( TEMPLATE_PATH . "/admin/editCategory.php" ); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
function editCategory() { |
||||
|
||||
$results = array(); |
||||
$results['pageTitle'] = "Edit Article Category"; |
||||
$results['formAction'] = "editCategory"; |
||||
|
||||
if ( isset( $_POST['saveChanges'] ) ) { |
||||
|
||||
// User has posted the category edit form: save the category changes |
||||
|
||||
if ( !$category = Category::getById( (int)$_POST['categoryId'] ) ) { |
||||
header( "Location: admin.php?action=listCategories&error=categoryNotFound" ); |
||||
return; |
||||
} |
||||
|
||||
$category->storeFormValues( $_POST ); |
||||
$category->update(); |
||||
header( "Location: admin.php?action=listCategories&status=changesSaved" ); |
||||
|
||||
} elseif ( isset( $_POST['cancel'] ) ) { |
||||
|
||||
// User has cancelled their edits: return to the category list |
||||
header( "Location: admin.php?action=listCategories" ); |
||||
} else { |
||||
|
||||
// User has not posted the category edit form yet: display the form |
||||
$results['category'] = Category::getById( (int)$_GET['categoryId'] ); |
||||
require( TEMPLATE_PATH . "/admin/editCategory.php" ); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
function deleteCategory() { |
||||
|
||||
if ( !$category = Category::getById( (int)$_GET['categoryId'] ) ) { |
||||
header( "Location: admin.php?action=listCategories&error=categoryNotFound" ); |
||||
return; |
||||
} |
||||
|
||||
$articles = Article::getList( 1000000, $category->id ); |
||||
|
||||
if ( $articles['totalRows'] > 0 ) { |
||||
header( "Location: admin.php?action=listCategories&error=categoryContainsArticles" ); |
||||
return; |
||||
} |
||||
|
||||
$category->delete(); |
||||
header( "Location: admin.php?action=listCategories&status=categoryDeleted" ); |
||||
} |
||||
@ -0,0 +1,391 @@ |
||||
/* Style the body and outer container */ |
||||
|
||||
:root { |
||||
--background: #051622; |
||||
--container: #ffffff; |
||||
--h1: red; |
||||
--h2: darkblue; |
||||
--pubDate: red; |
||||
--containerWidth: 75vw; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
color: #333; |
||||
background-color: var(--background); |
||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; |
||||
line-height: 1.5em; |
||||
} |
||||
|
||||
#container { |
||||
width: var(--containerWidth); |
||||
background: var(--container); |
||||
margin: 20px auto; |
||||
padding: 20px; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
} |
||||
|
||||
|
||||
/* The logo and footer */ |
||||
|
||||
#logo { |
||||
display: block; |
||||
/* width: 300px; */ |
||||
border: none; |
||||
margin-bottom: 40px; |
||||
/* make Round Image */ |
||||
border-radius:50%; |
||||
overflow:hidden; |
||||
} |
||||
|
||||
#logo-hr { |
||||
border-top: 1px solid var(--background); |
||||
margin-top: 40px; |
||||
padding: 20px 0 0 0; |
||||
font-size: .8em; |
||||
} |
||||
|
||||
#footer { |
||||
border-top: 1px solid var(--background); |
||||
margin-top: 40px; |
||||
padding: 20px 0 0 0; |
||||
font-size: .8em; |
||||
} |
||||
|
||||
|
||||
/* Headings */ |
||||
|
||||
h1 { |
||||
color: var(--h1); |
||||
margin-bottom: 30px; |
||||
line-height: 1.2em; |
||||
} |
||||
|
||||
h2, h2 a { |
||||
color: var(--h2); |
||||
} |
||||
|
||||
h2 a { |
||||
text-decoration: none; |
||||
} |
||||
|
||||
h3.categoryDescription { |
||||
margin-top: -20px; |
||||
margin-bottom: 40px; |
||||
} |
||||
|
||||
/* Article headlines */ |
||||
|
||||
#headlines { |
||||
list-style: none; |
||||
padding-left: 0; |
||||
width: 75%; |
||||
} |
||||
|
||||
#headlines li { |
||||
margin-bottom: 2em; |
||||
clear: both; |
||||
} |
||||
|
||||
.pubDate { |
||||
font-size: .8em; |
||||
color: var(--pubDate); |
||||
text-transform: uppercase; |
||||
} |
||||
|
||||
#headlines .pubDate { |
||||
display: block; |
||||
width: 100px; |
||||
padding-top: 4px; |
||||
float: left; |
||||
font-size: .5em; |
||||
vertical-align: middle; |
||||
} |
||||
|
||||
#headlines.archive .pubDate { |
||||
width: 130px; |
||||
} |
||||
|
||||
#headlines .articleImageThumb { |
||||
width: 120px; |
||||
float: left; |
||||
margin: 4px 20px 0 0; |
||||
border: 1px solid #00a0b0; |
||||
} |
||||
|
||||
.summary { |
||||
padding-left: 100px; |
||||
} |
||||
|
||||
#headlines.archive .summary { |
||||
padding-left: 130px; |
||||
} |
||||
|
||||
|
||||
/* Article pages */ |
||||
|
||||
#articleImageFullsize { |
||||
width: 250px; |
||||
float: left; |
||||
margin: 4px 20px 10px 0; |
||||
border: 1px solid #00a0b0; |
||||
} |
||||
|
||||
.category { |
||||
font-style: italic; |
||||
font-weight: normal; |
||||
font-size: 60%; |
||||
color: #999; |
||||
display: block; |
||||
line-height: 2em; |
||||
} |
||||
|
||||
.category a { |
||||
color: #999; |
||||
text-decoration: underline; |
||||
} |
||||
|
||||
/* "You are logged in..." header on admin pages */ |
||||
|
||||
#adminHeader { |
||||
width: 940px; |
||||
padding: 0 10px; |
||||
border-bottom: 1px solid #00a0b0; |
||||
margin: -30px 0 40px 0; |
||||
font-size: 0.8em; |
||||
} |
||||
|
||||
|
||||
/* Style the form with a coloured background, along with curved corners and a drop shadow */ |
||||
|
||||
form { |
||||
margin: 20px auto; |
||||
padding: 40px 20px; |
||||
overflow: auto; |
||||
background: #fff4cf; |
||||
border: 1px solid #666; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
-moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
-webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
} |
||||
|
||||
|
||||
/* Give form elements consistent margin, padding and line height */ |
||||
|
||||
form ul { |
||||
list-style: none; |
||||
margin: 0; |
||||
padding: 0; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
form ul li { |
||||
margin: .9em 0 0 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
form * { |
||||
line-height: 1em; |
||||
} |
||||
|
||||
|
||||
/* The field labels */ |
||||
|
||||
label { |
||||
display: block; |
||||
float: left; |
||||
clear: left; |
||||
text-align: right; |
||||
width: 15%; |
||||
padding: .4em 0 0 0; |
||||
margin: .15em .5em 0 0; |
||||
} |
||||
|
||||
|
||||
/* The fields */ |
||||
|
||||
input, select, textarea { |
||||
display: block; |
||||
margin: 0; |
||||
padding: .4em; |
||||
width: 80%; |
||||
} |
||||
|
||||
input, textarea, .date { |
||||
border: 2px solid #666; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
background: #fff; |
||||
} |
||||
|
||||
input { |
||||
font-size: .9em; |
||||
} |
||||
|
||||
input[type="checkbox"] { |
||||
display: inline-block; |
||||
padding: 0; |
||||
margin: 0 0 .8em 0; |
||||
width: auto; |
||||
} |
||||
|
||||
select { |
||||
padding: 0; |
||||
margin-bottom: 2.5em; |
||||
position: relative; |
||||
top: .7em; |
||||
} |
||||
|
||||
textarea { |
||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; |
||||
font-size: .9em; |
||||
height: 5em; |
||||
line-height: 1.5em; |
||||
} |
||||
|
||||
textarea#content { |
||||
font-family: "Courier New", courier, fixed; |
||||
} |
||||
|
||||
#articleImage { |
||||
border: 2px solid #666; |
||||
} |
||||
|
||||
#deleteImage { |
||||
clear: both; |
||||
} |
||||
|
||||
label[for="deleteImage"] { |
||||
float: none; |
||||
display: inline; |
||||
} |
||||
|
||||
input[type="file"] { |
||||
float: left; |
||||
} |
||||
|
||||
|
||||
/* Place a border around focused fields */ |
||||
|
||||
form *:focus { |
||||
border: 2px solid #7c412b; |
||||
outline: none; |
||||
} |
||||
|
||||
|
||||
/* Display correctly filled-in fields with a green background */ |
||||
|
||||
input:valid, textarea:valid { |
||||
background: #efe; |
||||
} |
||||
|
||||
|
||||
/* Submit buttons */ |
||||
|
||||
.buttons { |
||||
text-align: center; |
||||
margin: 40px 0 0 0; |
||||
} |
||||
|
||||
input[type="submit"] { |
||||
display: inline; |
||||
margin: 0 20px; |
||||
width: 12em; |
||||
padding: 10px; |
||||
border: 2px solid #7c412b; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
-moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
-webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
color: #fff; |
||||
background: #ef7d50; |
||||
font-weight: bold; |
||||
-webkit-appearance: none; |
||||
} |
||||
|
||||
input[type="submit"]:hover, input[type="submit"]:active { |
||||
cursor: pointer; |
||||
background: #fff; |
||||
color: #ef7d50; |
||||
} |
||||
|
||||
input[type="submit"]:active { |
||||
background: #eee; |
||||
-moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset; |
||||
-webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset; |
||||
box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset; |
||||
} |
||||
|
||||
|
||||
/* Tables */ |
||||
|
||||
table { |
||||
width: 100%; |
||||
border-collapse: collapse; |
||||
} |
||||
|
||||
tr, th, td { |
||||
padding: 10px; |
||||
margin: 0; |
||||
text-align: left; |
||||
} |
||||
|
||||
table, th { |
||||
border: 1px solid #00a0b0; |
||||
} |
||||
|
||||
th { |
||||
border-left: none; |
||||
border-right: none; |
||||
background: #ef7d50; |
||||
color: #fff; |
||||
cursor: default; |
||||
} |
||||
|
||||
tr:nth-child(odd) { |
||||
background: #fff4cf; |
||||
} |
||||
|
||||
tr:nth-child(even) { |
||||
background: #fff; |
||||
} |
||||
|
||||
tr:hover { |
||||
background: #ddd; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
|
||||
/* Status and error boxes */ |
||||
|
||||
.statusMessage, .errorMessage { |
||||
font-size: .8em; |
||||
padding: .5em; |
||||
margin: 2em 0; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
-moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
-webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
-box-shadow: 0 0 .5em rgba(0, 0, 0, .8); |
||||
} |
||||
|
||||
.statusMessage { |
||||
background-color: #2b2; |
||||
border: 1px solid #080; |
||||
color: #fff; |
||||
} |
||||
|
||||
.errorMessage { |
||||
background-color: #f22; |
||||
border: 1px solid #800; |
||||
color: #fff; |
||||
} |
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,39 @@ |
||||
/** |
||||
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved. |
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/ |
||||
import { ClassicEditor as ClassicEditorBase } from '@ckeditor/ckeditor5-editor-classic'; |
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials'; |
||||
import { UploadAdapter } from '@ckeditor/ckeditor5-adapter-ckfinder'; |
||||
import { Autoformat } from '@ckeditor/ckeditor5-autoformat'; |
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles'; |
||||
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote'; |
||||
import { CKBox } from '@ckeditor/ckeditor5-ckbox'; |
||||
import { CKFinder } from '@ckeditor/ckeditor5-ckfinder'; |
||||
import { EasyImage } from '@ckeditor/ckeditor5-easy-image'; |
||||
import { Heading } from '@ckeditor/ckeditor5-heading'; |
||||
import { Image, ImageCaption, ImageStyle, ImageToolbar, ImageUpload, PictureEditing } from '@ckeditor/ckeditor5-image'; |
||||
import { Indent } from '@ckeditor/ckeditor5-indent'; |
||||
import { Link } from '@ckeditor/ckeditor5-link'; |
||||
import { List } from '@ckeditor/ckeditor5-list'; |
||||
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed'; |
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph'; |
||||
import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office'; |
||||
import { Table, TableToolbar } from '@ckeditor/ckeditor5-table'; |
||||
import { TextTransformation } from '@ckeditor/ckeditor5-typing'; |
||||
import { CloudServices } from '@ckeditor/ckeditor5-cloud-services'; |
||||
export default class ClassicEditor extends ClassicEditorBase { |
||||
static builtinPlugins: (typeof TextTransformation | typeof Essentials | typeof UploadAdapter | typeof Paragraph | typeof Heading | typeof Autoformat | typeof Bold | typeof Italic | typeof BlockQuote | typeof Image | typeof ImageCaption | typeof ImageStyle | typeof ImageToolbar | typeof ImageUpload | typeof CloudServices | typeof CKBox | typeof CKFinder | typeof EasyImage | typeof List | typeof Indent | typeof Link | typeof MediaEmbed | typeof PasteFromOffice | typeof Table | typeof TableToolbar | typeof PictureEditing)[]; |
||||
static defaultConfig: { |
||||
toolbar: { |
||||
items: string[]; |
||||
}; |
||||
image: { |
||||
toolbar: string[]; |
||||
}; |
||||
table: { |
||||
contentToolbar: string[]; |
||||
}; |
||||
language: string; |
||||
}; |
||||
} |
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,45 @@ |
||||
<?php |
||||
require "../protected/src/config.php"; |
||||
|
||||
// Assuming private_images is a directory outside public_html |
||||
$privateImagePath = ARTICLE_IMAGE_PATH . "/"; |
||||
|
||||
// Get the image filename from the query string |
||||
if (isset($_GET['image'])) { |
||||
$imageFilename = basename($_GET['image']); |
||||
$pth = $_GET['rel'] ?? false; |
||||
$relPath = match($pth) { |
||||
'fullsize'=>"fullsize/", |
||||
'thumb'=>'thumb/', |
||||
default=>"", |
||||
}; |
||||
|
||||
$imagePath = $privateImagePath . $relPath . $imageFilename; |
||||
|
||||
// Check if the file exists |
||||
if (file_exists($imagePath)) { |
||||
// Get the file extension |
||||
$fileExtension = pathinfo($imagePath, PATHINFO_EXTENSION); |
||||
|
||||
// Set the appropriate Content-Type header based on the file extension |
||||
$header = match($fileExtension) { |
||||
'jpg'=>'Content-Type: image/jpeg', |
||||
'jpeg'=>'Content-Type: image/jpeg', |
||||
'gif'=>'Content-Type: image/gif', |
||||
'png'=>'Content-Type: image/png', |
||||
'webp'=>'Content-Type: image/webp', |
||||
default=>'HTTP/1.0 404 Not Found', |
||||
}; |
||||
header($header); |
||||
|
||||
if (str_contains($header, "Content-Type:")) { |
||||
// Read and output the image content |
||||
readfile($imagePath); |
||||
} |
||||
exit(); |
||||
} else { |
||||
// Image not found, return a 404 error |
||||
header('HTTP/1.0 404 Not Found'); |
||||
exit(); |
||||
} |
||||
} |
||||
@ -0,0 +1,56 @@ |
||||
<?php |
||||
|
||||
require "../protected/src/config.php"; |
||||
$action = isset( $_GET['action'] ) ? $_GET['action'] : ""; |
||||
|
||||
switch ( $action ) { |
||||
case 'archive': |
||||
archive(); |
||||
break; |
||||
case 'viewArticle': |
||||
viewArticle(); |
||||
break; |
||||
default: |
||||
homepage(); |
||||
} |
||||
|
||||
|
||||
function archive() { |
||||
$results = array(); |
||||
$categoryId = ( isset( $_GET['categoryId'] ) && $_GET['categoryId'] ) ? (int)$_GET['categoryId'] : null; |
||||
$results['category'] = Category::getById( $categoryId ); |
||||
$data = Article::getList( 100000, $results['category'] ? $results['category']->id : null ); |
||||
$results['articles'] = $data['results']; |
||||
$results['totalRows'] = $data['totalRows']; |
||||
$data = Category::getList(); |
||||
$results['categories'] = array(); |
||||
foreach ( $data['results'] as $category ) $results['categories'][$category->id] = $category; |
||||
$results['pageHeading'] = $results['category'] ? $results['category']->name : "Article Archive"; |
||||
$results['pageTitle'] = $results['pageHeading'] . " | " . BLOG_NAME; |
||||
require( TEMPLATE_PATH . "/archive.php" ); |
||||
} |
||||
|
||||
function viewArticle() { |
||||
if ( !isset($_GET["articleId"]) || !$_GET["articleId"] ) { |
||||
homepage(); |
||||
return; |
||||
} |
||||
|
||||
$results = array(); |
||||
$results['article'] = Article::getById( (int)$_GET["articleId"] ); |
||||
$results['category'] = Category::getById( $results['article']->categoryId ); |
||||
$results['pageTitle'] = $results['article']->title . " | " . BLOG_NAME; |
||||
require( TEMPLATE_PATH . "/viewArticle.php" ); |
||||
} |
||||
|
||||
function homepage() { |
||||
$results = array(); |
||||
$data = Article::getList( HOMEPAGE_NUM_ARTICLES ); |
||||
$results['articles'] = $data['results']; |
||||
$results['totalRows'] = $data['totalRows']; |
||||
$data = Category::getList(); |
||||
$results['categories'] = array(); |
||||
foreach ( $data['results'] as $category ) $results['categories'][$category->id] = $category; |
||||
$results['pageTitle'] = BLOG_NAME; |
||||
require( TEMPLATE_PATH . "/homepage.php" ); |
||||
} |
||||
@ -0,0 +1,78 @@ |
||||
<?php |
||||
|
||||
require "../protected/src/config.php"; |
||||
|
||||
// Set the Content-Type header to indicate that the response is in JSON format |
||||
header('Content-Type: application/json'); |
||||
|
||||
// Allow credentials (if needed) |
||||
header('Access-Control-Allow-Credentials: true'); |
||||
|
||||
// Optionally, set additional headers (e.g., CORS headers), Set the allowed origin |
||||
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] == SITE_URL) { |
||||
header('Access-Control-Allow-Origin: ' . SITE_URL); |
||||
} |
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); |
||||
header('Access-Control-Allow-Headers: Content-Type'); |
||||
|
||||
// Set the maximum file size to 10MB (in bytes) |
||||
$maxFileSize = 10 * 1024 * 1024; // 10MB |
||||
|
||||
// Define valid image types |
||||
$validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; |
||||
|
||||
// Check if the file is uploaded successfully |
||||
if (isset($_FILES['upload']) && $_FILES['upload']['error'] === UPLOAD_ERR_OK) { |
||||
$uploadDir = ARTICLE_IMAGE_PATH . "/"; // Set your upload directory |
||||
$tempFile = $_FILES['upload']['tmp_name']; |
||||
|
||||
// Check file size |
||||
if ($_FILES['upload']['size'] >= $maxFileSize) { |
||||
echo json_encode(['error' => 'Uploaded file too big! Limit of 10MB']); |
||||
unlink($tempFile); |
||||
exit; |
||||
} |
||||
|
||||
// Check file type |
||||
if (! in_array($_FILES['upload']['type'], $validImageTypes)) { |
||||
echo json_encode(['error' => 'Uploaded file not allowed image type (JPG, PNG, GIF)!']); |
||||
unlink($tempFile); |
||||
exit; |
||||
} |
||||
|
||||
// Check if the temporary file contains PHP tags |
||||
$fileContent = file_get_contents($tempFile); |
||||
if (strpos($fileContent, '<?php') !== false) { |
||||
// PHP tags detected, do not move the file and send an error response |
||||
echo json_encode(['error' => 'Danger: Uploaded file contains PHP start tags!']); |
||||
unlink($tempFile); |
||||
exit; |
||||
} |
||||
|
||||
// No PHP tags detected, move the uploaded file to the specified directory |
||||
if (UPLOAD_RND_FILE_NAMES) { |
||||
// Generate a unique filename to prevent overwriting |
||||
$fn = basename(uniqid('image_') . '.' . pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)); |
||||
$uploadedFile = $uploadDir . $fn; |
||||
} else { |
||||
$fn = basename($_FILES['upload']['name']); |
||||
$uploadedFile = $uploadDir . $fn; |
||||
if (file_exists($uploadedFile)) { |
||||
echo json_encode(['error' => 'Failed to uploaded file: Filename already exists!']); |
||||
exit; |
||||
} |
||||
} |
||||
|
||||
if (move_uploaded_file($tempFile, $uploadedFile)) { |
||||
// File moved successfully, send the file URL |
||||
$fileUrl = SITE_URL . '/image.php?image=' . $fn; |
||||
echo json_encode(['url' => $fileUrl]); |
||||
} else { |
||||
// Failed to move the uploaded file, send an error response |
||||
echo json_encode(['error' => 'Failed to move the uploaded file.']); |
||||
} |
||||
|
||||
} else { |
||||
// File upload failed, send an error response |
||||
echo json_encode(['error' => 'File upload failed.']); |
||||
} |
||||
Loading…
Reference in new issue