<?php
/**
 * @brief		Content Application Class
 * @author		<a href=''>Invision Power Services, Inc.</a>
 * @copyright	(c) 2014 Invision Power Services, Inc.
 * @package		IPS Social Suite
 * @subpackage	Content
 * @since		13 Jan 2014
 * @version		
 */
 
namespace IPS\cms;

spl_autoload_register( function( $class )
{
	if ( mb_substr( $class, 0, 15 ) === 'IPS\cms\Records' and is_numeric( mb_substr( $class, 15, 1 ) ) )
	{
		$databaseId   = mb_substr( $class, 15 );
		$titleField   = \IPS\cms\Databases::load( $databaseId )->field_title;
		$contentField = \IPS\cms\Databases::load( $databaseId )->field_content;
		$titleLang    = 'content_db_lang_sl_' . $databaseId;

		$data = <<<EOF
		namespace IPS\cms;
		class Records{$databaseId} extends Records
		{
			protected static \$multitons = array();
			public static \$customDatabaseId = $databaseId;
			public static \$commentClass = 'IPS\cms\Records\Comment{$databaseId}';
			public static \$reviewClass = 'IPS\cms\Records\Review{$databaseId}';
			public static \$containerNodeClass = 'IPS\cms\Categories{$databaseId}';
			public static \$databaseTable = 'cms_custom_database_{$databaseId}';
			public static \$title = '{$titleLang}';
			public static \$module = 'records{$databaseId}';
			public static \$reputationType = 'record_id_{$databaseId}';
			public static \$databaseColumnMap = array(
				'author'				=> 'member_id',
				'container'				=> 'category_id',
				'date'					=> 'record_publish_date',
				'is_future_entry'       => 'record_future_date',
				'future_date'           => 'record_publish_date',
				'title'					=> 'field_{$titleField}',
				'content'				=> 'field_{$contentField}',
				'num_comments'			=> 'record_comments',
				'unapproved_comments'	=> 'record_comments_queued',
				'hidden_comments'		=> 'record_comments_hidden',
				'last_comment'			=> 'record_last_comment',
				'last_comment_by'		=> 'record_last_comment_by',
				'last_comment_name'		=> 'record_last_comment_name',
				'last_comment_name_seo'	=> 'record_last_comment_seo',
				'views'					=> 'record_views',
				'approved'				=> 'record_approved',
				'pinned'				=> 'record_pinned',
				'locked'				=> 'record_locked',
				'featured'				=> 'record_featured',
				'rating_hits'			=> 'rating_hits',
				'rating'		        => 'record_rating',
				'rating_total'			=> 'rating_value',
				'num_reviews'	        => 'record_reviews',
				'last_review'	        => 'record_last_review',
				'last_review_by'        => 'record_last_review_by',
				'last_review_name'      => 'record_last_review_name',
				'updated'				=> 'record_last_comment'
			);
		}
EOF;
		eval( $data );
	}
	
	if ( mb_substr( $class, 0, 23 ) === 'IPS\cms\Records\Comment' and is_numeric( mb_substr( $class, 23, 1 ) ) )
	{
		$databaseId = mb_substr( $class, 23 );
	
		$data = <<<EOF
		namespace IPS\cms\Records;
		class Comment{$databaseId} extends Comment
		{ 
			public static \$customDatabaseId = $databaseId;
			public static \$itemClass = 'IPS\cms\Records{$databaseId}';
			public static \$title     = 'content_record_comments_title_{$databaseId}';
		}
EOF;
		eval( $data );
	}

	if ( mb_substr( $class, 0, 22 ) === 'IPS\cms\Records\Review' and is_numeric( mb_substr( $class, 22, 1 ) ) )
	{
		$databaseId = mb_substr( $class, 22 );

		$data = <<<EOF
		namespace IPS\cms\Records;
		class Review{$databaseId} extends Review
		{
			public static \$customDatabaseId = $databaseId;
			public static \$itemClass = 'IPS\cms\Records{$databaseId}';
			public static \$title     = 'content_record_reviews_title_{$databaseId}';
		}
EOF;
		eval( $data );
	}
	
	if ( mb_substr( $class, 0, 18 ) === 'IPS\cms\Categories' and is_numeric( mb_substr( $class, 18, 1 ) ) )
	{
		$databaseId = mb_substr( $class, 18 );
	
		$data = <<<EOF
		namespace IPS\cms;
		class Categories{$databaseId} extends Categories
		{ 
			public static \$customDatabaseId = $databaseId;
			public static \$contentItemClass = 'IPS\cms\Records{$databaseId}';
		}
EOF;

		eval( $data );
	}
	
	if ( mb_substr( $class, 0, 32 ) === 'IPS\cms\Records\RecordsTopicSync' )
	{
		$databaseId = mb_substr( $class, 32 );
	
		$data = <<<EOF
		namespace IPS\cms\Records;
		class RecordsTopicSync{$databaseId} extends \IPS\cms\Records{$databaseId}
		{ 
			public static \$customDatabaseId = $databaseId;
			public static \$databaseTable = 'cms_custom_database_{$databaseId}';
			public static \$databaseColumnId = 'record_topicid';
			public static \$commentClass = 'IPS\cms\Records\CommentTopicSync{$databaseId}';
		}
EOF;
		eval( $data );
	}

	if ( mb_substr( $class, 0, 32 ) === 'IPS\cms\Records\CommentTopicSync' )
	{
		$databaseId = mb_substr( $class, 32 );

		$data = <<<EOF
		namespace IPS\cms\Records;
		class CommentTopicSync{$databaseId} extends CommentTopicSync
		{ 
			public static \$customDatabaseId = $databaseId;
			public static \$itemClass = 'IPS\cms\Records\RecordsTopicSync{$databaseId}';
			public static \$title     = 'content_record_comments_title_{$databaseId}';
		}
EOF;
		eval( $data );
	}
	
	if ( mb_substr( $class, 0, 14 ) === 'IPS\cms\Fields' and is_numeric( mb_substr( $class, 14, 1 ) ) )
	{
		$databaseId = mb_substr( $class, 14 );
		eval( "namespace IPS\\cms; class Fields{$databaseId} extends Fields { public static \$customDatabaseId = $databaseId; }" );
	}

	if ( mb_substr( $class, 0, 47 ) === 'IPS\cms\extensions\core\EditorLocations\Records' and is_numeric( mb_substr( $class, 47, 1 ) ) )
	{
		$databaseId = mb_substr( $class, 47 );
		eval( "namespace IPS\\cms\\extensions\\core\\EditorLocations; class Records{$databaseId} extends \\IPS\\cms\\extensions\\core\\EditorLocations\\Records { public static \$customDatabaseId = $databaseId; }" );
	}
} );

/**
 * Content Application Class
 */
class _Application extends \IPS\Application
{
	/**
	 * Returns the ACP Menu JSON for this application.
	 *
	 * @return array
	 */
	public function acpMenu()
	{
		$menu = parent::acpMenu();
		
		if ( ! \IPS\Db::i()->checkForTable('cms_databases') )
		{
			return $menu;
		}
		
		/* Now add in the databases... */
		foreach( \IPS\cms\Databases::acpMenu() as $database )
		{
			$menu['databases'][ 'records_' . $database['id'] ] = array(
				'tab' 		  => 'cms',
				'controller'  => 'records',
				'do' 		  => 'manage&database_id=' . $database['id'],
				'restriction' => 'records_manage'
			);

			if ( $database['use_categories'] )
			{
				$menu['databases'][ 'categories_' . $database['id'] ] = array(
					'tab' 		  => 'cms',
					'controller'  => 'categories',
					'do' 		  => 'manage&database_id=' . $database['id'],
					'restriction' => 'categories_manage'
				);
			}
			
			$menu['databases'][ 'fields_' . $database['id'] ] = array(
				'tab' 		  => 'cms',
				'controller'  => 'fields',
				'do' 		  => 'manage&database_id=' . $database['id'],
				'restriction' => 'fields_manage'
			);
			
			\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_records_' . $database['id'] ]    = \IPS\Member::loggedIn()->language()->addToStack('content_db_' . $database['id']);
			\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_categories_' . $database['id'] ] = '&nbsp;&nbsp;' . \IPS\Member::loggedIn()->language()->addToStack('menu__cms_categories');
			\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_fields_' . $database['id'] ]     = '&nbsp;&nbsp;' . \IPS\Member::loggedIn()->language()->addToStack('menu__cms_fields');
		}
		
		/* Check for, and build IN_DEV templates if required */
		$this->_writeInDevTemplates();
		
		return $menu;
	}

	/**
	 * Get Extensions
	 *
	 * @param	\IPS\Application|string		$app		    The app key of the application which owns the extension
	 * @param	string						$extension	    Extension Type
	 * @param	bool						$construct	    Should an object be returned? (If false, just the classname will be returned)
	 * @param	\IPS\Member|bool			$checkAccess	Check access permission for extension against supplied member (or logged in member, if TRUE)
	 * @return	array
	 */
	public function extensions( $app, $extension, $construct=TRUE, $checkAccess=FALSE )
	{
		$classes = parent::extensions( $app, $extension, $construct );

		if ( $extension === 'EditorLocations' )
		{
			foreach( \IPS\cms\Databases::databases() as $obj )
			{
				$classname = '\\IPS\\cms\\extensions\\core\\EditorLocations\\Records' . $obj->_id;

				if ( method_exists( $classname, 'generate' ) )
				{
					$classes = array_merge( $classes, $classname::generate() );
				}
				elseif ( !$construct )
				{
					$classes[ 'Records' . $obj->_id ] = $classname;
				}
				else
				{
					try
					{
						$classes[ 'Records' . $obj->_id ] = new $classname;
					}
					catch( \RuntimeException $e ){}
				}
			}
		}
		else if ( $checkAccess and $extension === 'ContentRouter' )
		{
			if ( isset( $classes['Records'] ) )
			{
				foreach ( $classes['Records']->classes as $id => $name )
				{
					if ( ! $name::database()->can( 'view' ) )
					{
						unset( $classes['Records']->classes[ $id ] );
						continue;
					}

					if ( isset( $name::$reviewClass ) and ! $name::database()->options['reviews'] )
					{
						$_class = $classes['Records']->classes[ $id ];
						if ( class_exists( $_class ) )
						{
							$_class::$reviewClass = NULL;
						}
					}
				}
			}
		}

		return $classes;
	}

	/**
	 * Developer sync items
	 *
	 * @param   int     $lastSync       Last time syncd
	 * @return  boolean                 Updated (true), nothing updated(false)
	 */
	public function developerSync( $lastSync )
	{
		$updated = false;

		if ( $lastSync < filemtime( \IPS\ROOT_PATH . "/applications/{$this->directory}/data/databaseschema.json" ) )
		{
			foreach( \IPS\cms\Databases::databases() as $key => $db )
			{
				\IPS\cms\Databases::checkandFixDatabaseSchema( $db->_id );

				$updated = TRUE;
			}
		}

		return $updated;
	}

	/**
	 * Install 'other' items.
	 *
	 * @return void
	 */
	public function installOther()
	{
		/* Install default database and page */
		$database = new \IPS\cms\Databases;
		$database->key = 'articles';
		$database->save();

		/* Add in permissions */
		$groups = array();
		foreach( \IPS\Db::i()->select( 'g_id', 'core_groups', array( 'g_access_cp=1' ) ) as $row )
		{
			$groups[] = $row;
		}

		$default = implode( ',', $groups );

		\IPS\Db::i()->insert( 'core_permission_index', array(
             'app'			=> 'cms',
             'perm_type'	=> 'databases',
             'perm_type_id'	=> $database->id,
             'perm_view'	=> '*', # view
             'perm_2'		=> '*', # read
             'perm_3'		=> $default, # add
             'perm_4'		=> $default, # edit
             'perm_5'		=> $default, # reply
             'perm_6'		=> $default  # rate
        ) );

		/* Needs to be added before createDatabase is called */
		\IPS\Lang::saveCustom( 'cms', "content_db_" . $database->id, "Articles" );
		\IPS\Lang::saveCustom( 'cms', "module__cms_records" . $database->id, "Articles" );
		\IPS\Lang::saveCustom( 'cms', "content_db_" . $database->id . '_desc', "Our website articles" );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_sl_" . $database->id, 'article' );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_pl_" . $database->id, 'articles' );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_su_" . $database->id, 'Article' );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_pu_" . $database->id, 'Articles' );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_ia_" . $database->id, 'an article' );
		\IPS\Lang::saveCustom( 'cms', "content_db_lang_sl_" . $database->id . '_pl', 'Articles' );
		\IPS\Lang::saveCustom( 'cms', "cms_create_menu_records_" . $database->id, 'Article in Articles' );
		\IPS\Lang::saveCustom( 'cms', "digest_area_records" . $database->id, "Articles" );
		\IPS\Lang::saveCustom( 'cms', "cms_records" . $database->id . '_pl', 'Article' );

		try
		{
			\IPS\cms\Databases::createDatabase( $database );
		}
		catch ( \Exception $ex )
		{
			$database->delete();

			\IPS\Log::i( LOG_ERR )->write( $ex, 'pages_create_db_error' );

			throw new \LogicException( $ex->getMessage() );
		}

		$database->all_editable = 0;
		$database->revisions    = 1;
		$database->search       = 1;
		$database->comment_bump = 1; # Just new comments bump record
		$database->rss	        = 1;
		$database->options['comments'] = 1;
		$database->field_sort      = 'primary_id_field';
		$database->field_direction = 'asc';
		$database->field_perpage   = 25;
		$database->save();

		/* Create the page */
		$pageValues = array(
			'page_name'         => "Articles",
			'page_title'        => "Articles",
			'page_seo_name'     => "articles.html",
			'page_folder_id'    => 0,
			'page_ipb_wrapper'  => 1,
			'page_show_sidebar' => 1,
			'page_content'      => '{database="' . $database->id . '"}',
		);

		$page = \IPS\cms\Pages\Page::createFromForm( $pageValues, 'html' );

		$page->setAsDefault();

		/* Add block container (custom)*/
		$container = new \IPS\cms\Blocks\Container;
		$container->parent_id = 0;
		$container->name      = "Custom";
		$container->type      = 'block';
		$container->key       = 'block_custom';
		$container->save();

		/* Add block container (plugins) */
		$container = new \IPS\cms\Blocks\Container;
		$container->parent_id = 0;
		$container->name      = "Plugins";
		$container->type      = 'block';
		$container->key       = 'block_plugins';
		$container->save();
		
		\IPS\cms\Templates::importXml( \IPS\ROOT_PATH . "/applications/cms/data/cms_theme.xml", NULL, NULL, FALSE );
	}

	/**
	 * Install the application's templates
	 * Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded.
	 *
	 * @param	bool		$update	If set to true, do not overwrite current theme setting values
	 * @param	int|null	$offset Offset to begin import from
	 * @param	int|null	$limit	Number of rows to import	
	 * @return	int			Rows inserted
	 */
	public function installTemplates( $update=FALSE, $offset=null, $limit=null )
	{
		$inserted = parent::installTemplates( $update, $offset, $limit );
		
		if ( ( ! $inserted or ( $inserted < $limit ) ) AND $update )
		{
			\IPS\cms\Templates::importXml( \IPS\ROOT_PATH . "/applications/cms/data/cms_theme.xml", NULL, NULL, $update );
		}
	}

	/**
	 * Build skin templates for an app
	 *
	 * @return	void
	 * @throws	\RuntimeException
	 */
	public function buildThemeTemplates()
	{
		parent::buildThemeTemplates();

		foreach( array( 'database', 'block', 'page' ) as $location )
		{
			\IPS\cms\Theme::importInDev( $location );
		}

		/* Build XML and write to app directory */
		$xml = new \XMLWriter;
		$xml->openMemory();
		$xml->setIndent( TRUE );
		$xml->startDocument( '1.0', 'UTF-8' );

		/* Root tag */
		$xml->startElement('theme');
		$xml->startAttribute('name');
		$xml->text( "Default" );
		$xml->endAttribute();
		$xml->startAttribute('author_name');
		$xml->text( "Invision Power Services, Inc" );
		$xml->endAttribute();
		$xml->startAttribute('author_url');
		$xml->text( "http://www.invisionpower.com" );
		$xml->endAttribute();

		/* Templates */
		foreach ( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0' ), 'template_group, template_title' ) as $template )
		{
			/* Initiate the <template> tag */
			$xml->startElement('template');

			foreach( $template as $k => $v )
			{
				if ( in_array( \substr( $k, 9 ), array('key', 'title', 'desc', 'location', 'group', 'params', 'app', 'type' ) ) )
				{
					$xml->startAttribute( $k );
					$xml->text( $v );
					$xml->endAttribute();
				}
			}

			/* Write value */
			if ( preg_match( '/<|>|&/', $template['template_content'] ) )
			{
				$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $template['template_content'] ) );
			}
			else
			{
				$xml->text( $template['template_content'] );
			}

			/* Close the <template> tag */
			$xml->endElement();
		}

		/* Finish */
		$xml->endDocument();

		/* Write it */
		if ( is_writable( \IPS\ROOT_PATH . '/applications/' . $this->directory . '/data' ) )
		{
			\file_put_contents( \IPS\ROOT_PATH . '/applications/' . $this->directory . '/data/cms_theme.xml', $xml->outputMemory() );
		}
		else
		{
			throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_could_not_write_data') );
		}

	}

	/**
	 * Write the IN_DEV templates to disk
	 *
	 * @return void
	 */
	public function _writeInDevTemplates()
	{
		if ( \IPS\IN_DEV )
		{
			//\IPS\cms\Theme::writeInDev();
		}
		
	}
	
}