SyntaxHighlighter is licenced under <a href="http://www.gnu.org/copyleft/lesser.html">LGPL 3</a></dd>
</dl>
<h2>Content</h2>
<h3>Obsidian Workspaces content</h3>
<dl class="definition">
<dt>Help content</dt>
<dd>The Obsidian Workspaces documentation is licensed under <a href="https://creativecommons.org/licenses/by/4.0/" rel="license">Creative Commons BY 4.0 license</a>.</dd>
</dl>
<h3>User-generated content</h3>
<dl class="definition">
<dt>Data</dt>
<dd>All works uploaded to the site by users keeps its original copyright status, under their responsibility.</dd>
Licensed under <a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache License 2.0 </a></dd>
</dl>
<h3>Icons</h3>
<dl class="definition">
<dt>Arrow obsidian</dt>
<dd>This image is a work of a United States Department of Energy (or predecessor organization) employee, taken or made during the course of an employee's official duties. As a work of the U.S. federal government, the image is in the public domain.
<br> <br>Thank you to Julien Torres for the cutout operation.</dd>
</dl>
<h3>Photography</h3>
<dl class="definition">
<dt><a href="http://www.flickr.com/photos/travelingotter/5351260684/">Gray Glacier - Torres del Paine, Chile</a></dt>
<dd>Photography by <a href="http://www.flickr.com/photos/travelingotter/">TravelingOtter</a>, licensed under <a href="http://creativecommons.org/licenses/by/2.0/" rel="license">Creative Commons BY 2.0 license</a>.</dd>
<dd class="description">Capt. Keelan McNulty surveys the majesty of the Alaskan mountain range during his climb of Mount McKinley. 2008-07-15.</dd>
<dd>Photography by 1st Lt. Graham Ward, U.S. Army, in the public domain.</dd>
<dt><a href="https://commons.wikimedia.org/wiki/File:Rock_Mesa_obsidian_flow_in_Oregon_in_2011_%289%29.JPG">Rock Mesa obsidian flow in Oregon in 2011</a></dt>
- <dd class="description">Obsidian lava flow Rock Mesa in in Three Sisters Area, protected area Three Sisters Wilderness, Oregon, USA. 3 September 2011, 01:04:12</dd>
+ <dd class="description">Obsidian lava flow Rock Mesa in Three Sisters Area, protected area Three Sisters Wilderness, Oregon, USA. 3 September 2011, 01:04:12</dd>
<dd>Photography by <a href="https://commons.wikimedia.org/wiki/User:Chmee2">Petr Brož</a>, under <a href="https://creativecommons.org/licenses/by-sa/3.0/" rel="license">Creative Commons BY-SA 3.0 license</a>.</dd>
if (substr($current_url, 0, $len) != $Config['SiteURL']) {
dieprint_r(GENERAL_ERROR, "Edit includes/config.php and specify the correct site URL<br /><strong>Current value:</strong> $Config[SiteURL]<br /><strong>Expected value:</strong> a string starting by " . get_server_url(), "Setup");
}
if (array_key_exists('REDIRECT_URL', $_SERVER)) {
//With mod_rewrite, we can use REDIRECT_URL
- //We takes the end of the URL, ie *FROM* $len position
+ //We take the end of the URL, ie *FROM* $len position
* This class represents a collection of documents, stored on MySQL.
*/
class MySQLCollection extends SQLCollection {
///
/// Singleton pattern to get the MySQLDatabase instance
///
/**
* @var MySQLDatabase The mongo client to the database the collection is hosted
*/
public static $client = null;
/**
* Gets the existing MySQLDatabase instance, or if not available, initializes one.
*
* @param Context $context
* @return MySQLDatabase The MySQLDatabase instance
*/
public static function getCurrentSiteDatabaseClient () {
if (self::$client === null) {
$client = Database::load();
if ($candidateClient instanceof MySQLDatabase) {
self::$client = $client;
} else {
throw new InvalidArgumentException("The MySQLDatabase driver is intended to be used when your main database product is MySQL. We recommend whether you pick the same engine for collections and other db use, whether you use a key/store storage solution for collections, like MongoDB.");
}
}
return self::$client;
}
///
/// Constructor
///
/**
* Initializes a new instance of MongoCollection
*
* @param string $id the collection identifiant
*/
public function __construct ($id, ?MySQLDatabase $client = null, $table = '') {
global $Config;
if ($client === null) {
self::getCurrentSiteDatabaseClient();
} else {
self::$client = $client;
}
if ($table == '') {
if (!array_key_exists('DocumentStorage', $Config) || !array_key_exists('Table', $Config['DocumentStorage'])) {
throw new Exception("Configuration parameter missing: \$Config['DocumentStorage']['Table']. Expected value for this parameter is the table to store the collections documents.");
protected function initializeCollectionsTable () {
if (defined('COLLECTIONS_MYSQL_DATABASE_READY') && COLLECTIONS_MYSQL_DATABASE_READY) {
return;
}
self::$client->query("
CREATE TABLE if not exists $this->table (
collection_id VARCHAR(255),
document_id VARCHAR(255),
document_value BLOB,
PRIMARY KEY (collection_id, document_id)
);"
);
define('COLLECTIONS_MYSQL_DATABASE_READY', true);
}
///
/// SqlCollection implementation
///
/**
* Executes a SQL query
*
* @param string $sql The SQL query
- * @return mixed If the query doesn't return any result, null. If the query return a row with one field, the scalar value. Otheriwse, an aossciative array, the fields as keys, the row as values.
+ * @return mixed If the query doesn't return any result, null. If the query return a row with one field, the scalar value. Otherwise, an associative array, the fields as keys, the row as values.
*/
public function query ($sql) {
if ($sql == "") {
return null;
}
$db = self::$client;
if (!$result = $db->query($sql, MYSQL_ASSOC)) {
throw new Exception("Can't execute collection query.");
}
if (!$row = $db->fetchRow($result)) {
return null;
}
if (count($row) == 1) {
return array_shift($row);
} else {
return $row;
}
}
/**
* Escapes the SQL string
*
* @param string $value The value to escape
* @return string The escaped value
*/
public function escape ($value) {
return self::$client->escape($value);
}
/**
* Gets all the documents from the collection
*
* @return Iterator An iterator to the documents, each item an instance of CollectionDocument
*/
public function getAll () {
$db = self::$client;
$collectionId = $this->escape($this->id);
$sql = "SELECT * FROM $this->table WHERE collection_id = '$collectionId'";
if (!$result = $db->query($sql, MYSQL_ASSOC)) {
throw new Exception("Can't get each collection documents.");
* Abstract class with SQL standard implementation of CRUD features for collections using a SQL database.
*/
abstract class SQLCollection extends Collection {
/**
* @var string The SQL collections table
*/
public $table;
/**
* Executes a SQL query
*
* @param string $sql The SQL query
- * @return mixed If the query doesn't return any null, nothing. If the query return a row with one field, the scalar value. Otheriwse, an aossciative array, the fields as keys, the row as values.
+ * @return mixed If the query doesn't return any null, nothing. If the query return a row with one field, the scalar value. Otherwise, an associative array, the fields as keys, the row as values.
*/
public abstract function query ($sql);
/**
* Escapes the SQL string
*
* @param string $value The value to escape
* @return string The escaped value
*/
public abstract function escape ($value);
/**
* Adds a document to the collection
*
* @param CollectionDocument $document The document to add
* @return boolean true if the operation succeeded; otherwise, false.
*/
public function add (CollectionDocument &$document) {
* This class represents a collection of documents, stored on MySQL.
*/
class SQLiteCollection extends SQLCollection {
///
/// Singleton pattern to get or initialize the SQLite instance
///
/**
* @var SQLite3 The SQLite client to the database the collection is hosted
*/
public static $client = null;
/**
* Gets the existing SQLite3 instance, or if not available, initializes one.
*
* @return SQLite3 The SQLite3 instance
*/
public static function getClient () {
if (self::$client === null) {
self::$client = self::initializeClient();
}
return self::$client;
}
/**
* Initializes a new SQLite3 instance
*
* @return SQLite3 the client
*/
public static function initializeClient () {
global $Config;
if (!array_key_exists('DocumentStorage', $Config) || !array_key_exists('File', $Config['DocumentStorage'])) {
throw new Exception("Configuration parameter missing: \$Config['DocumentStorage']['File']. Expected value for this parameter is the path to the SQLite database file.");
}
return new SQLite3($Config['DocumentStorage']['File']);
}
///
/// Constructor
///
/**
* Initializes a new instance of MongoCollection
*
* @param string $id the collection identifiant
*/
public function __construct ($id) {
$this->table = 'collections';
$this->id = $id;
$this->initializeCollectionsTable();
}
///
/// Helper to create SQLite3 schema if required
///
/**
- * Initializaes collections table
+ * Initializes collections table
*/
protected function initializeCollectionsTable () {
if (defined('COLLECTIONS_SQLITE_DATABASE_READY') && COLLECTIONS_SQLITE_DATABASE_READY) {
* @return mixed If the query doesn't return any result, null. If the query return a row with one field, the scalar value. Otherwise, an associative array, the fields as keys, the row as values.
*/
public function query ($sql) {
$client = static::getClient();
if (static::isStatement($sql)) {
if (!$client->exec($sql)) {
throw new Exception(
"Can't execute collection query. "
. $client->lastErrorMsg()
);
}
return null;
}
$result = $client->query($sql);
if ($result === true) {
return null;
}
if ($result === false) {
throw new Exception(
"Can't execute collection query. "
. $client->lastErrorMsg()
);
}
$row = $result->fetchArray(SQLITE3_ASSOC);
$scalar = ($result->numColumns() == 1);
$result->finalize();
if ($scalar) {
return array_shift($row);
} else {
return $row;
}
}
/**
* Escapes the SQL string
*
* @param string $value The value to escape
* @return string The escaped value
*/
public function escape ($value) {
return SQLite3::escapeString($value);
}
/**
* Gets all the documents from the collection
*
* @return Iterator An iterator to the documents, each item an instance of CollectionDocument
*/
public function getAll () {
$collectionId = $this->escape($this->id);
$sql = "SELECT document_value FROM $this->table WHERE collection_id = '$collectionId'";
$client = static::getClient();
$type = $this->documentType;
$result = $client->query($sql);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
* @var Array An array of users already loaded, the username as user id
*/
public static $hashtableById = [];
/**
* @var array|null An array of the workspaces the user has access to, each element an instance of the Workspace object. As long as the field hasn't been initialized by get_workspaces, null.
*/
private $workspaces = null;
/*
* Initializes a new instance
*
* @param int $id the primary key
*/
function __construct ($id = null) {
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Initializes a new User instance if needed or get already available one.
*
* @param iint $id the user ID
* @return User the user instance
*/
static function get ($id = NULL) {
if ($id && array_key_exists($id, User::$hashtableById)) {
return self::$hashtableById[$id];
}
$user = new self($id);
return $user;
}
/**
* Loads the object User (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('name', $_POST)) $this->name = $_POST['name'];
if (array_key_exists('password', $_POST)) $this->password = $_POST['password'];
if (array_key_exists('active', $_POST)) $this->active = $_POST['active'];
if (array_key_exists('actkey', $_POST)) $this->actkey = $_POST['actkey'];
if (array_key_exists('email', $_POST)) $this->email = $_POST['email'];
if (array_key_exists('regdate', $_POST)) $this->regdate = $_POST['regdate'];
}
/**
* Loads the object User (ie fill the properties) from the database
*/
function load_from_database () {
global $db;
$sql = "SELECT * FROM " . TABLE_USERS . " WHERE user_id = '" . $this->id . "'";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query users", '', __LINE__, __FILE__, $sql);
class WorkspaceConfiguration implements ObjectDeserializableWithContext {
/**
* @var Array applications (each element is an instance of ApplicationConfiguration)
*/
public $applications = [];
/**
* @var Array authentication methods for this workspace (each element is an instance of AuthenticationMethod)
*/
public $authenticationMethods = [];
/**
* @var Array disclaimers (each element a string)
*/
public $disclaimers = [];
/**
* @var Array collections (each key a string to the collection name, each value a string to the collection document type)
*/
public $collections = [];
/**
* Determines if internal Obsidian Workspaces authentication can be used to login on this workspace URL
*
- * @return boolean True if an user not logged in Obsidian Workspaces going to a workspace URL should be offered to login through Obsidian ; otherwise, false.
+ * @return boolean True if a user not logged in Obsidian Workspaces going to a workspace URL should be offered to login through Obsidian ; otherwise, false.
*/
public $allowInternalAuthentication = true;
/**
* @var string The overall custom header to prepend to the header site
*/
public $header = '';
/**
* @var string The overall custom footer to append to the footer site
*/
public $footer = '';
/**
* Get applications controllers binds for this workspace
*/
public function getControllersBinds () {
$controllers = [];
foreach ($this->applications as $application) {
$controllers[$application->bind] = $application;
}
return $controllers;
}
/**
- * Determines if the URL fragment matches a controller binded to it.
+ * Determines if the URL fragment matches a controller bound to it.
*
* @param ApplicationConfiguration $applicationConfiguration The application configuration
* @return boolean true if the URL fragment matches an application controller's bind
*/
public function hasControllerBind ($url, &$applicationConfiguration) {
foreach ($this->applications as $application) {
if ($application->bind == $url) {
$applicationConfiguration = $application;
return true;
}
}
return false;
}
/**
* Loads a WorkspaceConfiguration instance from an object
*
* @param object $data The object to deserialize
* @param Context The site context
* @return WorkspaceConfiguration The deserialized instance
*/
public static function loadFromObject ($data, $context) {
$instance = new WorkspaceConfiguration();
//Applications array
if (property_exists($data, 'applications')) {
foreach ($data->applications as $applicationData) {
if (!property_exists($applicationData, 'name')) {
throw new Exception("Missing required property: application name");
}
$controllerClass = $applicationData->name;
if (!class_exists($controllerClass)) {
trigger_error("Application controller doesn't exist: $controllerClass. If you've just added application code, update includes/autoload.php file to register your new classes.", E_USER_WARNING);
throw new Exception("Missing required property: login type");
}
if ($authData->type == 'internal') {
$instance->allowInternalAuthentication = true;
continue;
}
$class = $authData->type;
if (!class_exists($class)) {
throw new Exception("Authentication method doesn't exist: $class. If you've just added authentication code, update includes/autoload.php file to register your new classes.");
if (property_exists($collection, 'documentType')) {
$type = $collection->documentType;
if (!class_exists($type)) {
throw new Exception("CollectionDocument children class doesn't exist: $type. If you've just added authentication code, update includes/autoload.php file to register your new classes.");
}
} else {
$type = null;
}
$instance->collections[$name] = $type;
}
}
//Header string
if (property_exists($data, 'header')) {
$instance->header = $data->header;
}
//Footer string
if (property_exists($data, 'footer')) {
$instance->footer = $data->footer;
}
return $instance;
}
/**
* Gets the full name of a collection, with the workspace prefix
*
* @param $workspace The current workspace
* @param $workspace The collection name
* @return string The full name of the collection
*/
public static function getCollectionNameWithPrefix (Workspace $workspace, $name) {
return $workspace->code . '-' . $name;
}
/**
* Loads a WorkspaceConfiguration instance deserializing a JSON file
*/
public static function loadFromFile ($file, $context) {
$object = json_decode(file_get_contents($file));
if ($object === null) {
throw new Exception("Can't parse configuration file: " . json_last_error_msg());