diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/l10n/ast.php | 7 | ||||
-rwxr-xr-x | lib/private/activitymanager.php | 194 | ||||
-rw-r--r-- | lib/private/appconfig.php | 4 | ||||
-rw-r--r-- | lib/private/connector/sabre/aborteduploaddetectionplugin.php | 98 | ||||
-rw-r--r-- | lib/private/connector/sabre/file.php | 20 | ||||
-rw-r--r-- | lib/private/db/mdb2schemareader.php | 1 | ||||
-rw-r--r-- | lib/private/files/cache/cache.php | 2 | ||||
-rw-r--r-- | lib/private/l10n.php | 3 | ||||
-rw-r--r-- | lib/private/preferences.php | 11 | ||||
-rw-r--r-- | lib/private/repair.php | 3 | ||||
-rw-r--r-- | lib/private/setup/mysql.php | 2 | ||||
-rw-r--r-- | lib/public/activity/iextension.php | 120 | ||||
-rw-r--r-- | lib/public/activity/imanager.php | 72 | ||||
-rw-r--r-- | lib/repair/collation.php | 75 |
14 files changed, 499 insertions, 113 deletions
diff --git a/lib/l10n/ast.php b/lib/l10n/ast.php index 078aad4529a..1ec3d8e28d3 100644 --- a/lib/l10n/ast.php +++ b/lib/l10n/ast.php @@ -93,14 +93,17 @@ $TRANSLATIONS = array( "Cannot write into \"apps\" directory" => "Nun pue escribise nel direutoriu \"apps\"", "Cannot create \"data\" directory (%s)" => "Nun pue crease'l direutoriu \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\">giving the webserver write access to the root directory</a>." => "Esto pue iguase davezu <a href=\"%s\" target=\"_blank\">dándo-y accesu d'escritura al direutoriu raigañu</a>.", +"Please install one of theses locales on your system and restart your webserver." => "Instala ún d'estos locales nel to sistema y reanicia'l sirvidor web", "Please ask your server administrator to install the module." => "Por favor, entrúga-y al to alministrador del sirvidor pa instalar el módulu.", "PHP module %s not installed." => "Nun ta instaláu'l módulu PHP %s", +"PHP %s or higher is required." => "Necesítase PHP %s o superior", "PHP Safe Mode is enabled. ownCloud requires that it is disabled to work properly." => "Ta habilitáu'l mou seguru de PHP. ownCloud requier que tea deshabilitáu pa furrular afayadízamente", "Please ask your server administrator to restart the web server." => "Por favor, entruga al to alministrador pa reaniciar el sirvidor web.", "PostgreSQL >= 9 required" => "PostgreSQL >= 9 requeríu", -"Please upgrade your database version" => "Por favor, anova la versión de la to base de datos", -"Error occurred while checking PostgreSQL version" => "Asocedió un fallu entrín se comprobaba la versión de PostgreSQL", +"Please upgrade your database version" => "Por favor, anueva la versión de la to base de datos", +"Error occurred while checking PostgreSQL version" => "Asocedió un fallu mientres se comprobaba la versión de PostgreSQL", "Please change the permissions to 0770 so that the directory cannot be listed by other users." => "Por favor, camuda los permisos a 0770 pa que'l direutoriu nun pueda llistase por otros usuarios.", +"Data directory (%s) is readable by other users" => "El direutoriu de datos (%s) ye llexible por otros usuarios", "Data directory (%s) is invalid" => "Ye inválidu'l direutoriu de datos (%s)", "Could not obtain lock type %d on \"%s\"." => "Nun pudo facese'l bloquéu %d en \"%s\"." ); diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php index 66aa039eb18..f31b121c8e8 100755 --- a/lib/private/activitymanager.php +++ b/lib/private/activitymanager.php @@ -10,13 +10,22 @@ namespace OC; use OCP\Activity\IConsumer; +use OCP\Activity\IExtension; use OCP\Activity\IManager; class ActivityManager implements IManager { + /** + * @var \Closure[] + */ private $consumers = array(); /** + * @var \Closure[] + */ + private $extensions = array(); + + /** * @param $app * @param $subject * @param $subjectParams @@ -65,4 +74,189 @@ class ActivityManager implements IManager { array_push($this->consumers, $callable); } + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * activity consumers are actually requested + * + * $callable has to return an instance of OCA\Activity\IConsumer + * + * @param \Closure $callable + * @return void + */ + function registerExtension(\Closure $callable) { + array_push($this->extensions, $callable); + } + + /** + * Will return additional notification types as specified by other apps + * + * @param string $languageCode + * @return array + */ + function getNotificationTypes($languageCode) { + $notificationTypes = array(); + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $result = $c->getNotificationTypes($languageCode); + if (is_array($result)) { + $notificationTypes = array_merge($notificationTypes, $result); + } + } + } + + return $notificationTypes; + } + + /** + * @param array $types + * @param string $filter + * @return array + */ + function filterNotificationTypes($types, $filter) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $result = $c->filterNotificationTypes($types, $filter); + if (is_array($result)) { + $types = $result; + } + } + } + return $types; + } + + /** + * @param string $method + * @return array + */ + function getDefaultTypes($method) { + $defaultTypes = array(); + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $types = $c->getDefaultTypes($method); + if (is_array($types)) { + $defaultTypes = array_merge($types, $defaultTypes); + } + } + } + return $defaultTypes; + } + + /** + * @param string $app + * @param string $text + * @param array $params + * @param boolean $stripPath + * @param boolean $highlightParams + * @param string $languageCode + * @return string|false + */ + function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); + if (is_string($translation)) { + return $translation; + } + } + } + + return false; + } + + /** + * @param string $type + * @return string + */ + function getTypeIcon($type) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $icon = $c->getTypeIcon($type); + if (is_string($icon)) { + return $icon; + } + } + } + + return ''; + } + + /** + * @param array $activity + * @return integer|false + */ + function getGroupParameter($activity) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $parameter = $c->getGroupParameter($activity); + if ($parameter !== false) { + return $parameter; + } + } + } + + return false; + } + + /** + * @return array + */ + function getNavigation() { + $entries = array( + 'apps' => array(), + 'top' => array(), + ); + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $additionalEntries = $c->getNavigation(); + if (is_array($additionalEntries)) { + $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']); + $entries['top'] = array_merge($entries['top'], $additionalEntries['top']); + } + } + } + + return $entries; + } + + /** + * @param string $filterValue + * @return boolean + */ + function isFilterValid($filterValue) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + if ($c->isFilterValid($filterValue) === true) { + return true; + } + } + } + + return false; + } + + /** + * @param string $filter + * @return array + */ + function getQueryForFilter($filter) { + foreach($this->extensions as $extension) { + $c = $extension(); + if ($c instanceof IExtension) { + $result = $c->getQueryForFilter($filter); + if (is_array($result)) { + return $result; + } + } + } + + return array(null, null); + } } diff --git a/lib/private/appconfig.php b/lib/private/appconfig.php index f20c4a08426..1874d9f2b19 100644 --- a/lib/private/appconfig.php +++ b/lib/private/appconfig.php @@ -183,6 +183,10 @@ class AppConfig implements \OCP\IAppConfig { ); $this->conn->insert('*PREFIX*appconfig', $data); } else { + $oldValue = $this->getValue($app, $key); + if($oldValue === strval($value)) { + return true; + } $data = array( 'configvalue' => $value, ); diff --git a/lib/private/connector/sabre/aborteduploaddetectionplugin.php b/lib/private/connector/sabre/aborteduploaddetectionplugin.php deleted file mode 100644 index b569f9a83c3..00000000000 --- a/lib/private/connector/sabre/aborteduploaddetectionplugin.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -/** - * Class OC_Connector_Sabre_AbortedUploadDetectionPlugin - * - * This plugin will verify if the uploaded data has been stored completely. - * This is done by comparing the content length of the request with the file size on storage. - */ -class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends \Sabre\DAV\ServerPlugin { - - /** - * Reference to main server object - * - * @var \Sabre\DAV\Server - */ - private $server; - - /** - * @var \OC\Files\View - */ - private $fileView; - - /** - * @param \OC\Files\View $view - */ - public function __construct($view) { - $this->fileView = $view; - } - - /** - * This initializes the plugin. - * - * This function is called by \Sabre\DAV\Server, after - * addPlugin is called. - * - * This method should set up the requires event subscriptions. - * - * @param \Sabre\DAV\Server $server - */ - public function initialize(\Sabre\DAV\Server $server) { - - $this->server = $server; - - $server->subscribeEvent('afterCreateFile', array($this, 'verifyContentLength'), 10); - $server->subscribeEvent('afterWriteContent', array($this, 'verifyContentLength'), 10); - } - - /** - * @param string $filePath - * @param \Sabre\DAV\INode $node - * @throws \Sabre\DAV\Exception\BadRequest - */ - public function verifyContentLength($filePath, \Sabre\DAV\INode $node = null) { - - // we should only react on PUT which is used for upload - // e.g. with LOCK this will not work, but LOCK uses createFile() as well - if ($this->server->httpRequest->getMethod() !== 'PUT') { - return; - } - - // ownCloud chunked upload will be handled in its own plugin - $chunkHeader = $this->server->httpRequest->getHeader('OC-Chunked'); - if ($chunkHeader) { - return; - } - - // compare expected and actual size - $expected = $this->getLength(); - if (!$expected) { - return; - } - $actual = $this->fileView->filesize($filePath); - if ($actual != $expected) { - $this->fileView->unlink($filePath); - throw new \Sabre\DAV\Exception\BadRequest('expected filesize ' . $expected . ' got ' . $actual); - } - - } - - /** - * @return string - */ - public function getLength() { - $req = $this->server->httpRequest; - $length = $req->getHeader('X-Expected-Entity-Length'); - if (!$length) { - $length = $req->getHeader('Content-Length'); - } - - return $length; - } -} diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 7591cc5c066..ece6885f24f 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -71,13 +71,13 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } // mark file as partial while uploading (ignored by the scanner) - $partpath = $this->path . '.ocTransferId' . rand() . '.part'; + $partFilePath = $this->path . '.ocTransferId' . rand() . '.part'; try { - $putOkay = $this->fileView->file_put_contents($partpath, $data); + $putOkay = $this->fileView->file_put_contents($partFilePath, $data); if ($putOkay === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR); - $this->fileView->unlink($partpath); + $this->fileView->unlink($partFilePath); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new \Sabre\DAV\Exception('Could not write file contents'); } @@ -102,13 +102,22 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ throw new OC_Connector_Sabre_Exception_FileLocked($e->getMessage(), $e->getCode(), $e); } + // double check if the file was fully received + // compare expected and actual size + $expected = $_SERVER['CONTENT_LENGTH']; + $actual = $this->fileView->filesize($partFilePath); + if ($actual != $expected) { + $this->fileView->unlink($partFilePath); + throw new \Sabre\DAV\Exception\BadRequest('expected filesize ' . $expected . ' got ' . $actual); + } + // rename to correct path try { - $renameOkay = $this->fileView->rename($partpath, $this->path); + $renameOkay = $this->fileView->rename($partFilePath, $this->path); $fileExists = $this->fileView->file_exists($this->path); if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); - $this->fileView->unlink($partpath); + $this->fileView->unlink($partFilePath); throw new \Sabre\DAV\Exception('Could not rename part file to final file'); } } @@ -259,5 +268,4 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ return null; } - } diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php index 61f58a1f200..288eef5cda0 100644 --- a/lib/private/db/mdb2schemareader.php +++ b/lib/private/db/mdb2schemareader.php @@ -82,6 +82,7 @@ class MDB2SchemaReader { $name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name); $name = $this->platform->quoteIdentifier($name); $table = $schema->createTable($name); + $table->addOption('collate', 'utf8_bin'); break; case 'create': case 'overwrite': diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 72af474adf8..cfa3e916185 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -472,6 +472,8 @@ class Cache { $sql .= 'REGEXP_LIKE(`name`, ?, \'i\')'; } else if($dbtype === 'pgsql') { $sql .= '`name` ILIKE ?'; + } else if ($dbtype === 'mysql') { + $sql .= '`name` COLLATE utf8_general_ci LIKE ?'; } else { $sql .= '`name` LIKE ?'; } diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 3e44be88150..28b35e92a2f 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -404,14 +404,15 @@ class OC_L10N implements \OCP\IL10N { /** * find the best language + * * @param array|string $app details below - * string language * * If $app is an array, ownCloud assumes that these are the available * languages. Otherwise ownCloud tries to find the files in the l10n * folder. * * If nothing works it returns 'en' + * @return string language */ public function getLanguageCode($app=null) { return self::findLanguage($app); diff --git a/lib/private/preferences.php b/lib/private/preferences.php index d1db25bbf09..a849cc23e1a 100644 --- a/lib/private/preferences.php +++ b/lib/private/preferences.php @@ -173,11 +173,16 @@ class Preferences { */ public function setValue($user, $app, $key, $value, $preCondition = null) { // Check if the key does exist - $query = 'SELECT COUNT(*) FROM `*PREFIX*preferences`' + $query = 'SELECT `configvalue` FROM `*PREFIX*preferences`' . ' WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'; - $count = $this->conn->fetchColumn($query, array($user, $app, $key)); - $exists = $count > 0; + $oldValue = $this->conn->fetchColumn($query, array($user, $app, $key)); + $exists = $oldValue !== false; + if($oldValue === strval($value)) { + // no changes + return true; + } + $affectedRows = 0; if (!$exists && $preCondition === null) { diff --git a/lib/private/repair.php b/lib/private/repair.php index 89886dd9316..e6943c5d057 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -81,7 +81,8 @@ class Repair extends BasicEmitter { */ public static function getBeforeUpgradeRepairSteps() { return array( - new \OC\Repair\InnoDB() + new \OC\Repair\InnoDB(), + new \OC\Repair\Collation(\OC::$server->getConfig(), \OC_DB::getConnection()) ); } diff --git a/lib/private/setup/mysql.php b/lib/private/setup/mysql.php index b2c28173b1c..3327965fb49 100644 --- a/lib/private/setup/mysql.php +++ b/lib/private/setup/mysql.php @@ -61,7 +61,7 @@ class MySQL extends AbstractDatabase { $name = $this->dbname; $user = $this->dbuser; //we cant use OC_BD functions here because we need to connect as the administrative user. - $query = "CREATE DATABASE IF NOT EXISTS `$name`"; + $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8 COLLATE utf8_bin;"; $result = mysql_query($query, $connection); if(!$result) { $entry = $this->trans->t('DB Error: "%s"', array(mysql_error($connection))) . '<br />'; diff --git a/lib/public/activity/iextension.php b/lib/public/activity/iextension.php new file mode 100644 index 00000000000..6bb403a8896 --- /dev/null +++ b/lib/public/activity/iextension.php @@ -0,0 +1,120 @@ +<?php +/** + * ownCloud + * + * @author Thomas Müller + * @copyright 2014 Thomas Müller deepdiver@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Public interface of ownCloud for apps to use. + * Activity/IExtension interface + */ + +// use OCP namespace for all classes that are considered public. +// This means that they should be used by apps instead of the internal ownCloud classes +namespace OCP\Activity; + +interface IExtension { + /** + * The extension can return an array of additional notification types. + * If no additional types are to be added false is to be returned + * + * @param string $languageCode + * @return array|false + */ + public function getNotificationTypes($languageCode); + + /** + * The extension can filter the types based on the filter if required. + * In case no filter is to be applied false is to be returned unchanged. + * + * @param array $types + * @param string $filter + * @return array|false + */ + public function filterNotificationTypes($types, $filter); + + /** + * For a given method additional types to be displayed in the settings can be returned. + * In case no additional types are to be added false is to be returned. + * + * @param string $method + * @return array|false + */ + public function getDefaultTypes($method); + + /** + * The extension can translate a given message to the requested languages. + * If no translation is available false is to be returned. + * + * @param string $app + * @param string $text + * @param array $params + * @param boolean $stripPath + * @param boolean $highlightParams + * @param string $languageCode + * @return string|false + */ + public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); + + /** + * A string naming the css class for the icon to be used can be returned. + * If no icon is known for the given type false is to be returned. + * + * @param string $type + * @return string|false + */ + public function getTypeIcon($type); + + /** + * The extension can define the parameter grouping by returning the index as integer. + * In case no grouping is required false is to be returned. + * + * @param array $activity + * @return integer|false + */ + public function getGroupParameter($activity); + + /** + * The extension can define additional navigation entries. The array returned has to contain two keys 'top' + * and 'apps' which hold arrays with the relevant entries. + * If no further entries are to be added false is no be returned. + * + * @return array|false + */ + public function getNavigation(); + + /** + * The extension can check if a customer filter (given by a query string like filter=abc) is valid or not. + * + * @param string $filterValue + * @return boolean + */ + public function isFilterValid($filterValue); + + /** + * For a given filter the extension can specify the sql query conditions including parameters for that query. + * In case the extension does not know the filter false is to be returned. + * The query condition and the parameters are to be returned as array with two elements. + * E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%')); + * + * @param string $filter + * @return array|false + */ + public function getQueryForFilter($filter); +} diff --git a/lib/public/activity/imanager.php b/lib/public/activity/imanager.php index 449fdcc934d..0a49fdf4999 100644 --- a/lib/public/activity/imanager.php +++ b/lib/public/activity/imanager.php @@ -50,11 +50,81 @@ interface IManager { * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * - * $callable has to return an instance of OCA\Activity\IConsumer + * $callable has to return an instance of \OCP\Activity\IConsumer * * @param \Closure $callable * @return void */ function registerConsumer(\Closure $callable); + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * activity consumers are actually requested + * + * $callable has to return an instance of \OCP\Activity\IExtension + * + * @param \Closure $callable + * @return void + */ + function registerExtension(\Closure $callable); + + /** + * Will return additional notification types as specified by other apps + * @param string $languageCode + * @return array + */ + function getNotificationTypes($languageCode); + + /** + * @param array $types + * @param string $filter + * @return array + */ + function filterNotificationTypes($types, $filter); + + /** + * @param string $method + * @return array + */ + function getDefaultTypes($method); + + /** + * @param string $app + * @param string $text + * @param array $params + * @param boolean $stripPath + * @param boolean $highlightParams + * @param string $languageCode + * @return string|false + */ + function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); + + /** + * @param string $type + * @return string + */ + function getTypeIcon($type); + + /** + * @param array $activity + * @return integer|false + */ + function getGroupParameter($activity); + + /** + * @return array + */ + function getNavigation(); + + /** + * @param string $filterValue + * @return boolean + */ + function isFilterValid($filterValue); + + /** + * @param string $filter + * @return array + */ + function getQueryForFilter($filter); } diff --git a/lib/repair/collation.php b/lib/repair/collation.php new file mode 100644 index 00000000000..2247cf82d0a --- /dev/null +++ b/lib/repair/collation.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use OC\Hooks\BasicEmitter; + +class Collation extends BasicEmitter implements \OC\RepairStep { + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @var \OC\DB\Connection + */ + protected $connection; + + /** + * @param \OCP\IConfig $config + * @param \OC\DB\Connection $connection + */ + public function __construct($config, $connection) { + $this->connection = $connection; + $this->config = $config; + } + + public function getName() { + return 'Repair MySQL collation'; + } + + /** + * Fix mime types + */ + public function run() { + if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + $this->emit('\OC\Repair', 'info', array('Not a mysql database -> nothing to no')); + return; + } + + $tables = $this->getAllNonUTF8BinTables($this->connection); + foreach ($tables as $table) { + $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;'); + $query->execute(); + } + } + + /** + * @param \Doctrine\DBAL\Connection $connection + * @return string[] + */ + protected function getAllNonUTF8BinTables($connection) { + $dbName = $this->config->getSystemValue("dbname"); + $rows = $connection->fetchAll( + "SELECT DISTINCT(TABLE_NAME) AS `table`" . + " FROM INFORMATION_SCHEMA . COLUMNS" . + " WHERE TABLE_SCHEMA = ?" . + " AND (COLLATION_NAME <> 'utf8_bin' OR CHARACTER_SET_NAME <> 'utf8')" . + " AND TABLE_NAME LIKE \"*PREFIX*%\"", + array($dbName) + ); + $result = array(); + foreach ($rows as $row) { + $result[] = $row['table']; + } + return $result; + } +} + |