bludit/bl-kernel/dbpages.class.php
ADTC c5dde14783 Add a unique identifier for blog posts and pages
The unique identifier is permanently attached to the post or page and
doesn't change during its lifetime. It can be used for many purposes that
require identification, such as Disqus comment threads (to avoid thread
duplication), redirection of old URLs, etc.

Signed-off-by: ADTC <send2adtc@gmail.com>
2017-04-26 13:28:59 +08:00

621 lines
14 KiB
PHP

<?php defined('BLUDIT') or die('Bludit CMS.');
class dbPages extends dbJSON
{
private $parentKeyList = array();
private $dbFields = array(
'title'=> array('inFile'=>true, 'value'=>''),
'content'=> array('inFile'=>true, 'value'=>''),
'uniqueId'=> array('inFile'=>false, 'value'=>''),
'description'=> array('inFile'=>false, 'value'=>''),
'username'=> array('inFile'=>false, 'value'=>''),
'tags'=> array('inFile'=>false, 'value'=>array()),
'status'=> array('inFile'=>false, 'value'=>'draft'),
'date'=> array('inFile'=>false, 'value'=>''),
'dateModified'=> array('inFile'=>false, 'value'=>''),
'position'=> array('inFile'=>false, 'value'=>0),
'coverImage'=> array('inFile'=>false, 'value'=>'')
);
function __construct()
{
parent::__construct(PATH_DATABASES.'pages.php');
}
public function add($args)
{
$dataForDb = array(); // This data will be saved in the database
$dataForFile = array(); // This data will be saved in the file
$key = $this->generateKey($args['slug'], $args['parent']);
// Generate unique identifier
if( empty($args['uniqueId']) ) {
$args['uniqueId'] = generateUniqueId();
}
// The user is always the one loggued.
$args['username'] = Session::get('username');
if( Text::isEmpty($args['username']) ) {
return false;
}
// Current date.
$args['date'] = Date::current(DB_DATE_FORMAT);
// Verify arguments with the database fields.
foreach($this->dbFields as $field=>$options)
{
if( isset($args[$field]) )
{
if($field=='tags') {
$tmpValue = $this->generateTags($args['tags']);
}
else {
// Sanitize if will be saved on database.
if( !$options['inFile'] ) {
$tmpValue = Sanitize::html($args[$field]);
}
else {
$tmpValue = $args[$field];
}
}
}
// Default value for the field.
else
{
$tmpValue = $options['value'];
}
// Check where the field will be written, in file or database.
if($options['inFile']) {
$dataForFile[$field] = Text::firstCharUp($field).': '.$tmpValue;
}
else
{
// Set type
settype($tmpValue, gettype($options['value']));
// Save on database
$dataForDb[$field] = $tmpValue;
}
}
// Make the directory. Recursive.
if( Filesystem::mkdir(PATH_PAGES.$key, true) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to create the directory '.PATH_PAGES.$key);
return false;
}
// Make the index.txt and save the file.
$data = implode("\n", $dataForFile);
if( file_put_contents(PATH_PAGES.$key.DS.FILENAME, $data) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to put the content in the file index.txt');
return false;
}
// Save the database
$this->db[$key] = $dataForDb;
if( $this->save() === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.');
return false;
}
return $key;
}
public function edit($args)
{
$dataForDb = array();
$dataForFile = array();
$newKey = $this->generateKey($args['slug'], $args['parent'], false, $args['key']);
// Generate unique identifier
if( empty($args['uniqueId']) ) {
$args['uniqueId'] = generateUniqueId();
}
// The user is always the one loggued.
$args['username'] = Session::get('username');
if( Text::isEmpty($args['username']) ) {
return false;
}
// If the page is draft then the time created is now.
if( $this->db[$args['key']]['status']=='draft' ) {
$args['date'] = Date::current(DB_DATE_FORMAT);
}
else {
$args['date'] = $this->db[$args['key']]['date'];
}
// Modified date
$args['dateModified'] = Date::current(DB_DATE_FORMAT);
// Verify arguments with the database fields.
foreach($this->dbFields as $field=>$options)
{
if( isset($args[$field]) )
{
if($field=='tags') {
$tmpValue = $this->generateTags($args['tags']);
}
else {
// Sanitize if will be saved on database.
if( !$options['inFile'] ) {
$tmpValue = Sanitize::html($args[$field]);
}
else {
$tmpValue = $args[$field];
}
}
}
// Default value for the field.
else
{
$tmpValue = $options['value'];
}
// Check where the field will be written, if in the file or in the database.
if($options['inFile']) {
$dataForFile[$field] = Text::firstCharUp($field).': '.$tmpValue;
}
else
{
// Set type
settype($tmpValue, gettype($options['value']));
// Save on database
$dataForDb[$field] = $tmpValue;
}
}
// Move the directory from old key to new key.
if($newKey!==$args['key'])
{
if( Filesystem::mv(PATH_PAGES.$args['key'], PATH_PAGES.$newKey) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to move the directory to '.PATH_PAGES.$newKey);
return false;
}
}
// Make the index.txt and save the file.
$data = implode("\n", $dataForFile);
if( file_put_contents(PATH_PAGES.$newKey.DS.FILENAME, $data) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to put the content in the file index.txt');
return false;
}
// Remove the old key.
unset($this->db[$args['key']]);
// Save the database
$this->db[$newKey] = $dataForDb;
if( $this->save() === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.');
return false;
}
return $newKey;
}
public function delete($key)
{
// Page doesn't exist in database.
if(!$this->pageExists($key)) {
Log::set(__METHOD__.LOG_SEP.'The page does not exist. Key: '.$key);
}
// Delete the index.txt file.
if( Filesystem::rmfile(PATH_PAGES.$key.DS.FILENAME) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to delete the file index.txt');
}
// Delete the directory.
if( Filesystem::rmdir(PATH_PAGES.$key) === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to delete the directory '.PATH_PAGES.$key);
}
// Remove from database.
unset($this->db[$key]);
// Save the database.
if( $this->save() === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.');
}
return true;
}
// Return an array with the database for a page, FALSE otherwise.
public function getPageDB($key)
{
if($this->pageExists($key)) {
return $this->db[$key];
}
return false;
}
public function setPageDb($key, $field, $value)
{
if($this->pageExists($key)) {
$this->db[$key][$field] = $value;
}
return false;
}
// Return TRUE if the page exists, FALSE otherwise.
public function pageExists($key)
{
return isset($this->db[$key]);
}
public function parentKeyList()
{
return $this->parentKeyList;
}
public function parentKeyExists($key)
{
return isset($this->parentKeyList[$key]);
}
public function addParentKey($key)
{
$this->parentKeyList[$key] = $key;
}
// Generate a valid Key/Slug.
public function generateKey($text, $parent=NO_PARENT_CHAR, $returnSlug=false, $oldKey='')
{
if(Text::isEmpty($text)) {
$text = 'empty';
}
if( Text::isEmpty($parent) || ($parent==NO_PARENT_CHAR) ) {
$newKey = Text::cleanUrl($text);
}
else {
$newKey = Text::cleanUrl($parent).'/'.Text::cleanUrl($text);
}
if($newKey!==$oldKey)
{
// Verify if the key is already been used.
if( isset($this->db[$newKey]) )
{
if( !Text::endsWithNumeric($newKey) ) {
$newKey = $newKey.'-0';
}
while( isset($this->db[$newKey]) ) {
$newKey++;
}
}
}
if($returnSlug)
{
$explode = explode('/', $newKey);
if(isset($explode[1])) {
return $explode[1];
}
return $explode[0];
}
return $newKey;
}
// Returns the database
public function getDB()
{
return $this->db;
}
// Returns an Array, array('tagSlug'=>'tagName')
// (string) $tags, tag list separeted by comma.
public function generateTags($tags)
{
$tmp = array();
$tags = trim($tags);
if(empty($tags)) {
return $tmp;
}
// Make array
$tags = explode(',', $tags);
foreach($tags as $tag)
{
$tag = trim($tag);
$tagKey = Text::cleanUrl($tag);
$tmp[$tagKey] = $tag;
}
return $tmp;
}
// Change all posts with the old category key for the new category key
public function changeCategory($oldCategoryKey, $newCategoryKey)
{
foreach($this->db as $key=>$value) {
if($value['category']==$oldCategoryKey) {
$this->db[$key]['category'] = $newCategoryKey;
}
}
// Save database
return $this->save();
}
public function count()
{
$count = parent::count();
// DEBUG: Less than - 1 because the error page.
return $count - 1;
}
public function cliMode()
{
// LOG
Log::set('CLI MODE - PAGES - Starting...');
$pageList = array();
$pagesDirectories = Filesystem::listDirectories(PATH_PAGES);
foreach( $pagesDirectories as $directory ) {
if( Sanitize::pathFile($directory.DS.FILENAME) ) {
// The key is the directory name
$key = basename($directory);
// Add the page key to the list
$pageList[$key] = true;
// LOG
Log::set('CLI MODE - Page found, key: '.$key);
// Search sub-pages
$subPaths = Filesystem::listDirectories($directory.DS);
foreach( $subPaths as $subDirectory )
{
// The key of the sub-page
$subKey = basename($subDirectory);
if( Sanitize::pathFile($subDirectory.DS.FILENAME) ) {
// Add the key of the sub-page, the key is composed by the directory/subdirectory
$pageList[$key.'/'.$subKey] = true;
// LOG
Log::set('CLI MODE - Page found, key: '.$key);
}
}
}
}
foreach( $pageList as $key=>$value ) {
if( !isset($this->db[$key]) ) {
// LOG
Log::set('CLI MODE - The page is not in the database, key: '.$key);
// Insert new post
$this->cliModeInsert($key);
}
else {
$checksum = md5_file(PATH_PAGES.$key.DS.FILENAME);
// If checksum is different, update the post
if( !isset($this->db[$key]['md5file']) ||
$this->db[$key]['md5file']!==$checksum ) {
// LOG
Log::set('CLI MODE - Different md5 checksum, key: '.$key);
// Update the post
$this->cliModeInsert($key, $update=true);
}
}
}
// LOG
Log::set('CLI MODE - Cleaning database...');
foreach( array_diff_key($this->db, $pageList) as $key=>$data ) {
// LOG
Log::set('CLI MODE - Removing page from database, key: '.$key);
// Remove the page from database
unset( $this->db[$key] );
}
// Save the database
$this->save();
// LOG
Log::set('CLI MODE - PAGES - Finishing...');
return true;
}
private function cliModeInsert($key, $update=false)
{
if($update) {
// LOG
Log::set('CLI MODE - cliModeInsert() - Updating the page, key: '.$key);
// Database from the current database
$dataForDb = $this->db[$key];
$dataForDb['dateModified'] = Date::current(DB_DATE_FORMAT);
}
else {
// LOG
Log::set('CLI MODE - cliModeInsert() - Inserting the new post, key: '.$key);
// Database for the new page, fields with the default values
$dataForDb = array();
foreach( $this->dbFields as $field=>$options ) {
if( !$options['inFile'] ) {
$dataForDb[$field] = $options['value'];
}
}
// Fields and value predefined in init.php
$dataForDb['username'] = CLI_USERNAME;
$dataForDb['status'] = CLI_STATUS;
$dataForDb['date'] = Date::current(DB_DATE_FORMAT);
}
// MD5 checksum
$dataForDb['md5file'] = md5_file(PATH_PAGES.$key.DS.FILENAME);
// Generate the Object from the file
$Page = new Page($key);
foreach( $this->dbFields as $field=>$options ) {
if( !$options['inFile'] ) {
// Get the field from the file
// If the field doesn't exist, the function returns FALSE
$data = $Page->getField($field);
if( $data!==false ) {
$tmpValue = '';
if( $field=='tags' ) {
$tmpValue = $this->generateTags($data);
}
elseif( $field=='date' ) {
// Validate format date from file
if( Valid::date($data, DB_DATE_FORMAT) ) {
$tmpValue = $data;
}
}
else {
$tmpValue = Sanitize::html($data);
}
settype($tmpValue, gettype($options['value']));
$dataForDb[$field] = $tmpValue;
}
}
}
// Insert row in the database
$this->db[$key] = $dataForDb;
return true;
}
public function regenerateCli()
{
$db = $this->db;
$newPaths = array();
$fields = array();
// Default fields and value
foreach($this->dbFields as $field=>$options) {
if(!$options['inFile']) {
$fields[$field] = $options['value'];
}
}
//$tmpPaths = glob(PATH_PAGES.'*', GLOB_ONLYDIR);
$tmpPaths = Filesystem::listDirectories(PATH_PAGES);
foreach($tmpPaths as $directory)
{
$key = basename($directory);
if(file_exists($directory.DS.FILENAME)) {
// The key is the directory name
$newPaths[$key] = true;
}
// Recovery pages from subdirectories
//$subPaths = glob($directory.DS.'*', GLOB_ONLYDIR);
$subPaths = Filesystem::listDirectories($directory.DS);
foreach($subPaths as $subDirectory)
{
$subKey = basename($subDirectory);
if(file_exists($subDirectory.DS.FILENAME)) {
// The key is composed by the directory/subdirectory
$newPaths[$key.'/'.$subKey] = true;
}
}
}
foreach($newPaths as $key=>$value)
{
if(!isset($this->db[$key]))
{
// Default values for the new pages.
$fields['status'] = CLI_STATUS;
$fields['date'] = Date::current(DB_DATE_FORMAT);
$fields['username'] = 'admin';
// Create the entry for the new page.
$this->db[$key] = $fields;
}
$Page = new Page($key);
// Update all fields from FILE to DATABASE.
foreach($fields as $f=>$v)
{
// If the field exists on the FILE, update it.
if($Page->getField($f))
{
$valueFromFile = $Page->getField($f);
if($f=='tags') {
// Generate tags array.
$this->db[$key]['tags'] = $this->generateTags($valueFromFile);
}
elseif($f=='date') {
// Validate Date from file
if(Valid::date($valueFromFile, DB_DATE_FORMAT)) {
$this->db[$key]['date'] = $valueFromFile;
}
}
else {
// Sanitize the values from file.
$this->db[$key][$f] = Sanitize::html($valueFromFile);
}
}
}
}
// Remove old pages from db
foreach( array_diff_key($db, $newPaths) as $key=>$data ) {
unset($this->db[$key]);
}
// Save the database.
if( $this->save() === false ) {
Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to save the database file.');
return false;
}
return $this->db!=$db;
}
}