Преглед на файлове

Add checking installation feature

Marien Fressinaud преди 11 години
родител
ревизия
7080a32650

+ 13 - 1
app/Controllers/updateController.php

@@ -12,7 +12,6 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 
 		invalidateHttpCache();
 
-		Minz_View::prependTitle(_t('update_system') . ' · ');
 		$this->view->update_to_apply = false;
 		$this->view->last_update_time = 'unknown';
 		$this->view->check_last_hour = false;
@@ -24,6 +23,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 	}
 
 	public function indexAction() {
+		Minz_View::prependTitle(_t('update_system') . ' · ');
+
 		if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) {
 			$this->view->message = array(
 				'status' => 'bad',
@@ -126,4 +127,15 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 			}
 		}
 	}
+
+	/**
+	 * This action displays information about installation.
+	 */
+	public function checkInstallAction() {
+		Minz_View::prependTitle(_t('gen.title.check_install') . ' · ');
+
+		$this->view->status_php = check_install_php();
+		$this->view->status_files = check_install_files();
+		$this->view->status_database = check_install_database();
+	}
 }

+ 83 - 0
app/Models/DatabaseDAO.php

@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * This class is used to test database is well-constructed.
+ */
+class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
+	public function tablesAreCorrect() {
+		$sql = 'SHOW TABLES';
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		
+		$tables = array(
+			$this->prefix . 'category' => false,
+			$this->prefix . 'feed' => false,
+			$this->prefix . 'entry' => false,
+		);
+		foreach ($res as $value) {
+			$tables[array_pop($value)] = true;
+		}
+
+		return count(array_keys($tables, true, true)) == count($tables);
+	}
+
+	public function getSchema($table) {
+		$sql = 'DESC ' . $this->prefix . $table;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+
+		return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC));
+	}
+
+	public function checkTable($table, $schema) {
+		$columns = $this->getSchema($table);
+
+		$ok = (count($columns) == count($schema));
+		foreach ($columns as $c) {
+			$ok &= in_array($c['name'], $schema);
+		}
+
+		return $ok;
+	}
+
+	public function categoryIsCorrect() {
+		return $this->checkTable('category', array(
+			'id', 'name'
+		));
+	}
+
+	public function feedIsCorrect() {
+		return $this->checkTable('feed', array(
+			'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate',
+			'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl',
+			'cache_nbEntries', 'cache_nbUnreads'
+		));
+	}
+
+	public function entryIsCorrect() {
+		return $this->checkTable('entry', array(
+			'id', 'guid', 'title', 'author', 'content_bin', 'link', 'date', 'is_read',
+			'is_favorite', 'id_feed', 'tags'
+		));
+	}
+
+	public function daoToSchema($dao) {
+		return array(
+			'name' => $dao['Field'],
+			'type' => strtolower($dao['Type']),
+			'notnull' => (bool)$dao['Null'],
+			'default' => $dao['Default'],
+		);
+	}
+
+	public function listDaoToSchema($listDAO) {
+		$list = array();
+
+		foreach ($listDAO as $dao) {
+			$list[] = $this->daoToSchema($dao);
+		}
+
+		return $list;
+	}
+}

+ 48 - 0
app/Models/DatabaseDAOSQLite.php

@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * This class is used to test database is well-constructed (SQLite).
+ */
+class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
+	public function tablesAreCorrect() {
+		$sql = 'SELECT name FROM sqlite_master WHERE type="table"';
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		
+		$tables = array(
+			'category' => false,
+			'feed' => false,
+			'entry' => false,
+		);
+		foreach ($res as $value) {
+			$tables[$value['name']] = true;
+		}
+
+		return count(array_keys($tables, true, true)) == count($tables);
+	}
+
+	public function getSchema($table) {
+		$sql = 'PRAGMA table_info(' . $table . ')';
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+
+		return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC));
+	}
+
+	public function entryIsCorrect() {
+		return $this->checkTable('entry', array(
+			'id', 'guid', 'title', 'author', 'content', 'link', 'date', 'is_read',
+			'is_favorite', 'id_feed', 'tags'
+		));
+	}
+
+	public function daoToSchema($dao) {
+		return array(
+			'name' => $dao['name'],
+			'type' => strtolower($dao['type']),
+			'notnull' => $dao['notnull'] === '1' ? true : false,
+			'default' => $dao['dflt_value'],
+		);
+	}
+}

+ 9 - 0
app/Models/Factory.php

@@ -29,4 +29,13 @@ class FreshRSS_Factory {
 		}
 	}
 
+	public static function createDatabaseDAO($username = null) {
+		$db = Minz_Configuration::dataBase();
+		if ($db['type'] === 'sqlite') {
+			return new FreshRSS_DatabaseDAOSQLite($username);
+		} else {
+			return new FreshRSS_DatabaseDAO($username);
+		}
+	}
+
 }

+ 0 - 2
app/SQL/install.sql.mysql.php

@@ -57,5 +57,3 @@ INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s");
 ');
 
 define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory');
-
-define('SQL_SHOW_TABLES', 'SHOW tables;');

+ 0 - 2
app/SQL/install.sql.sqlite.php

@@ -55,5 +55,3 @@ $SQL_CREATE_TABLES = array(
 );
 
 define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory');
-
-define('SQL_SHOW_TABLES', 'SELECT name FROM sqlite_master WHERE type="table"');

+ 6 - 1
app/layout/aside_configure.phtml

@@ -31,7 +31,12 @@
 	<li class="item<?php echo Minz_Request::controllerName() === 'auth' ? ' active' : ''; ?>">
 		<a href="<?php echo _url('auth', 'index'); ?>"><?php echo _t('gen.menu.authentication'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::controllerName() === 'update' ? ' active' : ''; ?>">
+	<li class="item<?php echo Minz_Request::controllerName() === 'update' &&
+	                          Minz_Request::actionName() === 'checkInstall' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a>
+	</li>
+	<li class="item<?php echo Minz_Request::controllerName() === 'update' &&
+	                          Minz_Request::actionName() === 'index' ? ' active' : ''; ?>">
 		<a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a>
 	</li>
 	<?php } ?>

+ 1 - 0
app/layout/header.phtml

@@ -68,6 +68,7 @@ if (Minz_Configuration::canLogIn()) {
 				<li class="dropdown-header"><?php echo _t('gen.menu.admin'); ?></li>
 				<li class="item"><a href="<?php echo _url('user', 'manage'); ?>"><?php echo _t('gen.menu.manage_users'); ?></a></li>
 				<li class="item"><a href="<?php echo _url('auth', 'index'); ?>"><?php echo _t('gen.menu.authentication'); ?></a></li>
+				<li class="item"><a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a></li>
 				<li class="item"><a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a></li>
 				<?php } ?>
 				<li class="separator"></li>

+ 30 - 0
app/views/update/checkInstall.phtml

@@ -0,0 +1,30 @@
+<?php $this->partial('aside_configure'); ?>
+
+<div class="post">
+	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
+
+	<h2><?php echo _t('admin.check_install.php'); ?></h2>
+
+	<?php foreach ($this->status_php as $key => $status) { ?>
+	<p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>">
+		<?php echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); ?>
+	</p>
+	<?php } ?>
+
+	<h2><?php echo _t('admin.check_install.files'); ?></h2>
+
+	<?php foreach ($this->status_files as $key => $status) { ?>
+	<p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>">
+		<?php echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); ?>
+	</p>
+	<?php } ?>
+
+	<h2><?php echo _t('admin.check_install.database'); ?></h2>
+
+	<?php foreach ($this->status_database as $key => $status) { ?>
+	<p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>">
+		<?php echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); ?>
+	</p>
+	<?php } ?>
+
+</div>

+ 62 - 0
lib/lib_rss.php

@@ -245,3 +245,65 @@ function is_referer_from_same_domain() {
 	}
 	return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0);
 }
+
+
+/**
+ *
+ */
+function check_install_php() {
+	return array(
+		'php' => version_compare(PHP_VERSION, '5.2.1') >= 0,
+		'minz' => file_exists(LIB_PATH . '/Minz'),
+		'curl' => extension_loaded('curl'),
+		'pdo_mysql' => extension_loaded('pdo_mysql'),
+		'pdo_sqlite' => extension_loaded('pdo_sqlite'),
+		'pdo' => extension_loaded('pdo_mysql') || extension_loaded('pdo_sqlite'),
+		'pcre' => extension_loaded('pcre'),
+		'ctype' => extension_loaded('ctype'),
+		'dom' => class_exists('DOMDocument'),
+		'json' => extension_loaded('json'),
+		'zip' => extension_loaded('zip'),
+	);
+}
+
+
+/**
+ *
+ */
+function check_install_files() {
+	return array(
+		'data' => DATA_PATH && is_writable(DATA_PATH),
+		'cache' => CACHE_PATH && is_writable(CACHE_PATH),
+		'logs' => LOG_PATH && is_writable(LOG_PATH),
+		'favicons' => is_writable(DATA_PATH . '/favicons'),
+		'persona' => is_writable(DATA_PATH . '/persona'),
+		'tokens' => is_writable(DATA_PATH . '/tokens'),
+	);
+}
+
+
+/**
+ *
+ */
+function check_install_database() {
+	$status = array(
+		'connection' => true,
+		'tables' => false,
+		'categories' => false,
+		'feeds' => false,
+		'entries' => false,
+	);
+
+	try {
+		$dbDAO = FreshRSS_Factory::createDatabaseDAO();
+
+		$status['tables'] = $dbDAO->tablesAreCorrect();
+		$status['categories'] = $dbDAO->categoryIsCorrect();
+		$status['feeds'] = $dbDAO->feedIsCorrect();
+		$status['entries'] = $dbDAO->entryIsCorrect();
+	} catch(Minz_PDOConnectionException $e) {
+		$status['connection'] = false;
+	}
+
+	return $status;
+}