Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/HuasoFoundries/phpPgAdmin6.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmenadiel <amenadiel@gmail.com>2016-09-07 21:41:52 +0300
committerAmenadiel <amenadiel@gmail.com>2016-09-07 21:41:52 +0300
commitb44050b160c0986ee7f8e69e4c08d41daf5ffcde (patch)
treee442fd5cd642f3bf7b2aed080d99fafaa0e76bff
parent7f9b6c0512e67d5cd029779bbb70442e4e495c4b (diff)
parent4a01919ac895fe67fd91fa768044b25c3d846d49 (diff)
Merge branch 'release/6.0.0-alpha3'v6.0.0-alpha3
-rw-r--r--composer.json9
-rw-r--r--composer.lock26
-rw-r--r--help/PostgresDoc95.php12
-rw-r--r--images/datatables/sort_asc.pngbin0 -> 160 bytes
-rw-r--r--images/datatables/sort_asc_disabled.pngbin0 -> 148 bytes
-rw-r--r--images/datatables/sort_both.pngbin0 -> 201 bytes
-rw-r--r--images/datatables/sort_desc.pngbin0 -> 158 bytes
-rw-r--r--images/datatables/sort_desc_disabled.pngbin0 -> 146 bytes
-rw-r--r--images/themes/default/MViews.pngbin0 -> 374 bytes
-rwxr-xr-xindex.php173
-rw-r--r--js/ac_insert_row.js21
-rw-r--r--js/database.js45
-rw-r--r--js/datatables.min.js14787
-rw-r--r--js/xloadtree/xtree2.js253
-rw-r--r--logout.php16
-rw-r--r--src/classes/Highlight.php1112
-rw-r--r--src/classes/Misc.php1103
-rw-r--r--src/controllers/ACInsertController.php111
-rw-r--r--src/controllers/AdminTrait.php64
-rw-r--r--src/controllers/AggregateController.php112
-rw-r--r--src/controllers/AllDBController.php134
-rw-r--r--src/controllers/BaseController.php138
-rw-r--r--src/controllers/BrowserController.php46
-rw-r--r--src/controllers/CastController.php62
-rw-r--r--src/controllers/ColPropertyController.php70
-rw-r--r--src/controllers/ConstraintController.php115
-rw-r--r--src/controllers/ConversionController.php70
-rw-r--r--src/controllers/DBExportController.php170
-rw-r--r--src/controllers/DataExportController.php399
-rw-r--r--src/controllers/DataImportController.php310
-rw-r--r--src/controllers/DatabaseController.php143
-rw-r--r--src/controllers/DisplayController.php1007
-rw-r--r--src/controllers/DomainController.php134
-rw-r--r--src/controllers/FulltextController.php255
-rw-r--r--src/controllers/FunctionController.php183
-rw-r--r--src/controllers/GroupController.php20
-rw-r--r--src/controllers/HistoryController.php4
-rw-r--r--src/controllers/IndexController.php109
-rw-r--r--src/controllers/InfoController.php17
-rw-r--r--src/controllers/IntroController.php34
-rw-r--r--src/controllers/LangController.php56
-rw-r--r--src/controllers/LoginController.php139
-rw-r--r--src/controllers/MaterializedViewController.php833
-rw-r--r--src/controllers/OpClassesController.php55
-rw-r--r--src/controllers/OperatorController.php100
-rw-r--r--src/controllers/PrivilegeController.php405
-rw-r--r--src/controllers/RolesController.php860
-rw-r--r--src/controllers/RuleController.php83
-rw-r--r--src/controllers/SQLEditController.php198
-rw-r--r--src/controllers/SQLQueryController.php324
-rw-r--r--src/controllers/SchemaController.php211
-rw-r--r--src/controllers/SequenceController.php137
-rw-r--r--src/controllers/ServerController.php205
-rw-r--r--src/controllers/TableController.php663
-rw-r--r--src/controllers/TablePropertyController.php142
-rw-r--r--src/controllers/TableSpacesController.php379
-rw-r--r--src/controllers/TriggerController.php122
-rw-r--r--src/controllers/TypeController.php162
-rw-r--r--src/controllers/UserController.php29
-rw-r--r--src/controllers/ViewController.php226
-rw-r--r--src/controllers/ViewPropertyController.php150
-rw-r--r--src/database/ADODB_base.php (renamed from src/classes/database/ADODB_base.php)0
-rwxr-xr-xsrc/database/Connection.php (renamed from src/classes/database/Connection.php)0
-rwxr-xr-xsrc/database/Postgres.php (renamed from src/classes/database/Postgres.php)21
-rw-r--r--src/database/Postgres74.php (renamed from src/classes/database/Postgres74.php)0
-rw-r--r--src/database/Postgres80.php (renamed from src/classes/database/Postgres80.php)0
-rw-r--r--src/database/Postgres81.php (renamed from src/classes/database/Postgres81.php)0
-rw-r--r--src/database/Postgres82.php (renamed from src/classes/database/Postgres82.php)0
-rw-r--r--src/database/Postgres83.php (renamed from src/classes/database/Postgres83.php)0
-rwxr-xr-xsrc/database/Postgres84.php (renamed from src/classes/database/Postgres84.php)0
-rwxr-xr-xsrc/database/Postgres90.php (renamed from src/classes/database/Postgres90.php)0
-rwxr-xr-xsrc/database/Postgres91.php (renamed from src/classes/database/Postgres91.php)0
-rw-r--r--src/database/Postgres92.php (renamed from src/classes/database/Postgres92.php)0
-rw-r--r--src/database/Postgres93.php (renamed from src/classes/database/Postgres93.php)0
-rw-r--r--src/decorator.inc.php86
-rw-r--r--src/decorators/ActionUrlDecorator.php (renamed from src/classes/decorators/ActionUrlDecorator.php)6
-rw-r--r--src/decorators/ArrayMergeDecorator.php (renamed from src/classes/decorators/ArrayMergeDecorator.php)4
-rw-r--r--src/decorators/BranchUrlDecorator.php (renamed from src/classes/decorators/BranchUrlDecorator.php)10
-rw-r--r--src/decorators/CallbackDecorator.php (renamed from src/classes/decorators/CallbackDecorator.php)2
-rw-r--r--src/decorators/ConcatDecorator.php (renamed from src/classes/decorators/ConcatDecorator.php)2
-rw-r--r--src/decorators/Decorator.php (renamed from src/classes/decorators/Decorator.php)45
-rw-r--r--src/decorators/FieldDecorator.php (renamed from src/classes/decorators/FieldDecorator.php)2
-rw-r--r--src/decorators/IfEmptyDecorator.php (renamed from src/classes/decorators/IfEmptyDecorator.php)6
-rw-r--r--src/decorators/RedirectUrlDecorator.php (renamed from src/classes/decorators/RedirectUrlDecorator.php)10
-rw-r--r--src/decorators/UrlDecorator.php (renamed from src/classes/decorators/UrlDecorator.php)6
-rw-r--r--src/decorators/replaceDecorator.php (renamed from src/classes/decorators/replaceDecorator.php)2
-rw-r--r--src/errorhandler.inc.php48
-rw-r--r--src/highlight.php1082
-rw-r--r--src/lib.inc.php91
-rw-r--r--src/themes/datatables.min.css625
-rw-r--r--src/themes/default/global.css14
-rw-r--r--src/themes/global.css49
-rw-r--r--src/tree/aggregates.php36
-rw-r--r--src/tree/all_db.php24
-rw-r--r--src/tree/casts.php30
-rw-r--r--src/tree/constraints.php41
-rw-r--r--src/tree/conversions.php29
-rwxr-xr-xsrc/tree/database.php30
-rw-r--r--src/tree/domains.php37
-rw-r--r--src/tree/fulltext.php91
-rw-r--r--src/tree/functions.php41
-rw-r--r--src/tree/indexes.php39
-rw-r--r--src/tree/languages.php28
-rw-r--r--src/tree/opclasses.php32
-rw-r--r--src/tree/operators.php42
-rw-r--r--src/tree/rules.php26
-rwxr-xr-xsrc/tree/schemas.php77
-rw-r--r--src/tree/sequences.php38
-rw-r--r--src/tree/servers.php47
-rw-r--r--src/tree/tables.php78
-rw-r--r--src/tree/tblproperties.php42
-rw-r--r--src/tree/triggers.php26
-rw-r--r--src/tree/types.php34
-rwxr-xr-xsrc/tree/viewproperties.php45
-rw-r--r--src/tree/views.php65
-rw-r--r--src/views/aggregates.php48
-rw-r--r--src/views/ajax-ac-insert.php93
-rw-r--r--src/views/all_db.php48
-rw-r--r--src/views/browser.php9
-rw-r--r--src/views/casts.php12
-rw-r--r--src/views/colproperties.php31
-rw-r--r--src/views/constraints.php81
-rw-r--r--src/views/conversions.php12
-rwxr-xr-xsrc/views/database.php68
-rw-r--r--src/views/dataexport.php367
-rw-r--r--src/views/dataimport.php296
-rw-r--r--src/views/dbexport.php144
-rw-r--r--src/views/display.php962
-rw-r--r--src/views/domains.php69
-rw-r--r--src/views/fulltext.php102
-rw-r--r--src/views/functions.php47
-rw-r--r--src/views/indexes.php52
-rwxr-xr-xsrc/views/intro.php12
-rw-r--r--src/views/languages.php11
-rwxr-xr-xsrc/views/login.php88
-rw-r--r--src/views/materialized_views.php14
-rw-r--r--src/views/opclasses.php12
-rw-r--r--src/views/operators.php37
-rw-r--r--src/views/privileges.php383
-rw-r--r--src/views/roles.php826
-rw-r--r--src/views/rules.php35
-rwxr-xr-xsrc/views/schemas.php42
-rw-r--r--src/views/sequences.php70
-rw-r--r--src/views/servers.php104
-rw-r--r--src/views/sql.php258
-rw-r--r--src/views/sqledit.php146
-rw-r--r--src/views/tables.php78
-rwxr-xr-xsrc/views/tablespaces.php351
-rw-r--r--src/views/tblproperties.php55
-rw-r--r--src/views/triggers.php67
-rw-r--r--src/views/types.php53
-rwxr-xr-xsrc/views/viewproperties.php59
-rw-r--r--src/views/views.php63
-rw-r--r--src/xhtml/HTMLController.php135
-rw-r--r--src/xhtml/HTMLNavbarController.php535
-rw-r--r--src/xhtml/HTMLTableController.php339
-rw-r--r--src/xhtml/XHTML_Button.php (renamed from src/classes/xhtml/XHTML_Button.php)0
-rw-r--r--src/xhtml/XHTML_Option.php (renamed from src/classes/xhtml/XHTML_Option.php)0
-rw-r--r--src/xhtml/XHTML_Select.php (renamed from src/classes/xhtml/XHTML_Select.php)0
-rw-r--r--src/xhtml/XHtmlElement.php (renamed from src/classes/xhtml/XHtmlElement.php)0
-rw-r--r--src/xhtml/XHtmlSimpleElement.php (renamed from src/classes/xhtml/XHtmlSimpleElement.php)0
-rw-r--r--templates/browser.twig5
-rw-r--r--templates/datatables_header.twig24
-rw-r--r--templates/header.twig7
-rw-r--r--templates/home.twig24
-rw-r--r--templates/home_header.twig13
-rw-r--r--templates/home_rtl.twig22
-rw-r--r--templates/select2_header.twig23
-rw-r--r--templates/sqledit.twig21
-rw-r--r--templates/sqledit_header.twig12
-rw-r--r--templates/table_list_footer.twig7
171 files changed, 26789 insertions, 9415 deletions
diff --git a/composer.json b/composer.json
index a053d060..c161868c 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "huasofoundries/phppgadmin",
- "version": "6.0.0-alpha2",
+ "version": "6.0.0-alpha3",
"description": "Like phpmyadmin but for postgres",
"type": "project",
"license": "MIT",
@@ -13,9 +13,10 @@
"psr-4": {
"PHPPgAdmin\\": "src/classes",
"PHPPgAdmin\\Controller\\": "src/controllers",
- "PHPPgAdmin\\Database\\": "src/classes/database",
- "PHPPgAdmin\\XHtml\\": "src/classes/xhtml",
- "PHPPgAdmin\\Decorators\\": "src/classes/decorators"
+
+ "PHPPgAdmin\\Database\\": "src/database",
+ "PHPPgAdmin\\XHtml\\": "src/xhtml",
+ "PHPPgAdmin\\Decorators\\": "src/decorators"
}
},
"require": {
diff --git a/composer.lock b/composer.lock
index cc5fff32..744b7315 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,21 +4,21 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "95f57a2ebe79ac38abaf9ddf5aa10968",
- "content-hash": "4c894ea7295f7f51cce76ca342f6b6b2",
+ "hash": "b5caa7fbaa65851bbec4d8d6bfc0dc9e",
+ "content-hash": "a561ee70b411e722ceb6ff4a20b9d564",
"packages": [
{
"name": "adodb/adodb-php",
- "version": "v5.20.5",
+ "version": "v5.20.6",
"source": {
"type": "git",
"url": "https://github.com/ADOdb/ADOdb.git",
- "reference": "7ce997d4c5957a1a96e7ab483ad088b15f02b191"
+ "reference": "66b60ff95a969d7d79d385a25779e7d375a8040d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ADOdb/ADOdb/zipball/7ce997d4c5957a1a96e7ab483ad088b15f02b191",
- "reference": "7ce997d4c5957a1a96e7ab483ad088b15f02b191",
+ "url": "https://api.github.com/repos/ADOdb/ADOdb/zipball/66b60ff95a969d7d79d385a25779e7d375a8040d",
+ "reference": "66b60ff95a969d7d79d385a25779e7d375a8040d",
"shasum": ""
},
"require": {
@@ -51,7 +51,7 @@
}
],
"description": "ADOdb is a PHP database abstraction layer library",
- "homepage": "http://adodb.sourceforge.net/",
+ "homepage": "http://adodb.org/",
"keywords": [
"abstraction",
"database",
@@ -59,7 +59,7 @@
"library",
"php"
],
- "time": "2016-08-10 11:08:40"
+ "time": "2016-08-31 14:21:42"
},
{
"name": "container-interop/container-interop",
@@ -451,16 +451,16 @@
},
{
"name": "twig/twig",
- "version": "v1.24.1",
+ "version": "v1.24.2",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512"
+ "reference": "33093f6e310e6976baeac7b14f3a6ec02f2d79b7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512",
- "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/33093f6e310e6976baeac7b14f3a6ec02f2d79b7",
+ "reference": "33093f6e310e6976baeac7b14f3a6ec02f2d79b7",
"shasum": ""
},
"require": {
@@ -508,7 +508,7 @@
"keywords": [
"templating"
],
- "time": "2016-05-30 09:11:59"
+ "time": "2016-09-01 17:50:53"
}
],
"packages-dev": [],
diff --git a/help/PostgresDoc95.php b/help/PostgresDoc95.php
new file mode 100644
index 00000000..acf2745f
--- /dev/null
+++ b/help/PostgresDoc95.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Help links for PostgreSQL 9.5 documentation
+ *
+ */
+
+include BASE_PATH . '/help/PostgresDoc94.php';
+
+$this->help_base = sprintf($GLOBALS['conf']['help_base'], '9.5');
+
+$this->help_page['pg.matview'] = 'sql-creatematerializedview.html';
diff --git a/images/datatables/sort_asc.png b/images/datatables/sort_asc.png
new file mode 100644
index 00000000..e1ba61a8
--- /dev/null
+++ b/images/datatables/sort_asc.png
Binary files differ
diff --git a/images/datatables/sort_asc_disabled.png b/images/datatables/sort_asc_disabled.png
new file mode 100644
index 00000000..fb11dfe2
--- /dev/null
+++ b/images/datatables/sort_asc_disabled.png
Binary files differ
diff --git a/images/datatables/sort_both.png b/images/datatables/sort_both.png
new file mode 100644
index 00000000..af5bc7c5
--- /dev/null
+++ b/images/datatables/sort_both.png
Binary files differ
diff --git a/images/datatables/sort_desc.png b/images/datatables/sort_desc.png
new file mode 100644
index 00000000..0e156deb
--- /dev/null
+++ b/images/datatables/sort_desc.png
Binary files differ
diff --git a/images/datatables/sort_desc_disabled.png b/images/datatables/sort_desc_disabled.png
new file mode 100644
index 00000000..c9fdd8a1
--- /dev/null
+++ b/images/datatables/sort_desc_disabled.png
Binary files differ
diff --git a/images/themes/default/MViews.png b/images/themes/default/MViews.png
new file mode 100644
index 00000000..40e65a96
--- /dev/null
+++ b/images/themes/default/MViews.png
Binary files differ
diff --git a/index.php b/index.php
index fb733e30..64bcde5d 100755
--- a/index.php
+++ b/index.php
@@ -7,7 +7,7 @@
*/
// Include application functions
-$_no_db_connection = true;
+
require_once './src/lib.inc.php';
$app->post('/redirect[/{subject}]', function ($request, $response, $args) use ($msg) {
@@ -42,7 +42,7 @@ $app->post('/redirect[/{subject}]', function ($request, $response, $args) use ($
$misc->printHeader($this->lang['strdatabases']);
$misc->printBody();
- return $all_db_controller->doDefault();
+ $all_db_controller->doDefault();
$misc->setReloadBrowser(true);
$misc->printFooter();
@@ -54,9 +54,8 @@ $app->post('/redirect[/{subject}]', function ($request, $response, $args) use ($
if (!isset($_server_info['username'])) {
- include BASE_PATH . '/src/views/login.php';
-
- $body->write(doLoginForm($this, $msg));
+ $login_controller = new \PHPPgAdmin\Controller\LoginController($this);
+ $body->write($login_controller->doLoginForm($msg));
}
}
@@ -76,10 +75,11 @@ $app->get('/redirect[/{subject}]', function ($request, $response, $args) use ($m
$body = $response->getBody();
if (!isset($_server_info['username'])) {
- include BASE_PATH . '/src/views/login.php';
- $body->write(doLoginForm($this, $msg));
+ $this->misc->setNoDBConnection(true);
+ $login_controller = new \PHPPgAdmin\Controller\LoginController($this);
+
+ $body->write($login_controller->doLoginForm($msg));
- //\Kint::dump($request->getParams());
return $response;
} else {
@@ -87,19 +87,17 @@ $app->get('/redirect[/{subject}]', function ($request, $response, $args) use ($m
$include_file = $url['url'];
+ \PC::debug($url, 'url');
+
// Load query vars into superglobal arrays
if (isset($url['urlvars'])) {
-
- /*echo '<pre>';
- print_r($url['urlvars']);
- */
$urlvars = [];
foreach ($url['urlvars'] as $key => $urlvar) {
if (strpos($key, '?') !== FALSE) {
$key = explode('?', $key)[1];
}
- $urlvars[$key] = value($urlvar, $_REQUEST);
+ $urlvars[$key] = \PHPPgAdmin\Decorators\Decorator::get_sanitized_value($urlvar, $_REQUEST);
}
$_REQUEST = array_merge($_REQUEST, $urlvars);
@@ -108,9 +106,6 @@ $app->get('/redirect[/{subject}]', function ($request, $response, $args) use ($m
$actionurl = \PHPPgAdmin\Decorators\Decorator::actionurl($include_file, $_GET);
- //PC::debug($url['url'], 'redirect.php will include');
- //PC::debug($actionurl->value($_GET), '$actionurl');
-
if (is_readable($include_file)) {
include $include_file;
} else {
@@ -122,149 +117,21 @@ $app->get('/redirect[/{subject}]', function ($request, $response, $args) use ($m
}
});
-$app->get('/sqledit[/{action}]', function ($request, $response, $args) use ($msg) {
-
- $action = (isset($args['action'])) ? $args['action'] : 'sql';
-
- include './src/views/sqledit.php';
- $body = $response->getBody();
-
- switch ($action) {
- case 'find':
-
- $header_html = $this->view->fetch('sqledit_header.twig', ['title' => $this->lang['strfind']]);
- $body->write($header_html);
- $body->write(doFind($this));
-
- break;
- case 'sql':
- default:
-
- $header_html = $this->view->fetch('sqledit_header.twig', ['title' => $this->lang['strsql']]);
- $body->write($header_html);
- $body->write(doDefault($this));
-
- break;
- }
-
- $footer_html = $this->view->fetch('sqledit.twig');
- $body->write($footer_html);
-
- $this->misc->setWindowName('sqledit');
- return $response;
-
-});
-
-$app->get('/tree/browser', function ($request, $response, $args) use ($msg) {
-
- $viewVars = $this->lang;
- $viewVars['appName'] = $this->get('settings')['appName'];
- $viewVars['icon'] = [
- 'blank' => $this->misc->icon('blank'),
- 'I' => $this->misc->icon('I'),
- 'L' => $this->misc->icon('L'),
- 'Lminus' => $this->misc->icon('Lminus'),
- 'Loading' => $this->misc->icon('Loading'),
- 'Lplus' => $this->misc->icon('Lplus'),
- 'ObjectNotFound' => $this->misc->icon('ObjectNotFound'),
- 'Refresh' => $this->misc->icon('Refresh'),
- 'Servers' => $this->misc->icon('Servers'),
- 'T' => $this->misc->icon('T'),
- 'Tminus' => $this->misc->icon('Tminus'),
- 'Tplus' => $this->misc->icon('Tplus'),
-
- ];
-
- $viewVars['cols'] = $cols;
- $viewVars['rtl'] = $rtl;
-
- $this->view->render($response, 'browser.twig', $viewVars);
-
-});
-
-$app->get('/tree/{node}[/{action}]', function ($request, $response, $args) use ($msg) {
-
- $newResponse = $response
- ->withHeader('Content-type', 'text/xml')
- ->withHeader('Cache-Control', 'no-cache');
-
- $phpscript = './src/tree/' . $args['node'] . '.php';
-
- if (is_readable($phpscript)) {
- include $phpscript;
- if (isset($args['action']) && $args['action'] == 'subtree') {
- doSubTree($this);
- } else {
- doTree($this);
- }
- }
-
- return $newResponse;
-
-});
-
-$app->get('/src/views/servers[/{action}]', function ($request, $response, $args) use ($msg) {
-
- $action = (isset($args['action'])) ? $args['action'] : '';
-
- include './src/views/servers.php';
-
- $body = $response->getBody();
-
- $header_html = $this->misc->printHeader($this->lang['strservers'], null, false);
- $body->write($header_html);
-
- $body_html = $this->misc->printBody(false);
- $body->write($body_html);
-
- $trail_html = $this->misc->printTrail('root', false);
- $body->write($trail_html);
-
- switch ($action) {
- case 'logout':
- $body->write(doLogout($this));
-
- break;
- default:
- $body->write(doDefault($this, $msg));
- break;
- }
-
- $footer_html = $this->misc->printFooter(false);
- $body->write($footer_html);
- return $response;
-
-});
-
$app->get('/', function ($request, $response, $args) use ($msg) {
- $rtl = (strcasecmp($this->lang['applangdir'], 'rtl') == 0);
- $cols = $rtl ? '*,' . $this->conf['left_width'] : $this->conf['left_width'] . ',*';
-
$viewVars = $this->lang;
$viewVars['appName'] = $this->get('settings')['appName'];
- $viewVars['cols'] = $cols;
- $viewVars['rtl'] = $rtl;
-
- return $this->view->render($response, 'home.twig', $viewVars);
-
-});
-
-$app->get('/src/views/intro', function ($request, $response, $args) use ($msg) {
- include './src/views/intro.php';
-
- $body = $response->getBody();
- $body->write(doDefault($this));
-
- return $response;
+ $viewVars['rtl'] = (strcasecmp($this->lang['applangdir'], 'rtl') == 0);
-});
+ if ($viewVars['rtl']) {
+ $viewVars['cols'] = '*,' . $this->conf['left_width'];
+ $template = 'home_rtl.twig';
+ } else {
+ $viewVars['cols'] = $this->conf['left_width'] . ',*';
+ $template = 'home.twig';
+ }
-$app->get('/views/{script}', function ($request, $response, $args) use ($msg) {
- $body = $response->getBody();
- $body->write('Not found ' . $args['script']);
- \PC::debug($args, 'args');
- return $response;
+ return $this->view->render($response, $template, $viewVars);
});
diff --git a/js/ac_insert_row.js b/js/ac_insert_row.js
index 8fabf28f..f18c2621 100644
--- a/js/ac_insert_row.js
+++ b/js/ac_insert_row.js
@@ -74,13 +74,13 @@ function openlist(e) {
};
jQuery.ajax({
- url: 'ajax-ac-insert.php?server=' + server,
+ url: '/srv/views/ajax-ac-insert.php?server=' + server,
type: 'post',
data: datas,
dataType: 'html',
cache: false,
contentType: 'application/x-www-form-urlencoded',
- success: function(ret) {
+ success: function (ret) {
jQuery.ppa.i = 0;
jQuery.ppa.fkbg.show();
with(jQuery.ppa.fklist) {
@@ -162,11 +162,11 @@ function autocomplete(event) {
/* bind actions on values lines: hover for style change, click for select */
with(jQuery('tr.acline')) {
- live('mouseover', function() {
+ live('mouseover', function () {
selectVal(jQuery('table.ac_values tr').index(this));
});
- live('click', function() {
+ live('click', function () {
var a = jQuery(this).find('td > a.fkval');
for (i = 0; i < a.length; i++) {
@@ -177,8 +177,7 @@ with(jQuery('tr.acline')) {
}
-
-jQuery(document).ready(function() {
+jQuery(document).ready(function () {
/* register some global value in the ppa namespace */
jQuery.ppa = {
fklist: jQuery('#fklist'),
@@ -188,7 +187,7 @@ jQuery(document).ready(function() {
o: 0 // offset when navigating prev/next
};
- jQuery.ppa.fklist.on('click', '#fkprev', function() {
+ jQuery.ppa.fklist.on('click', '#fkprev', function () {
jQuery.ppa.o -= 11;
/* get the field that is the previous html elt from the #fklist
* and trigger its focus to refresh the list AND actualy
@@ -197,7 +196,7 @@ jQuery(document).ready(function() {
});
- jQuery.ppa.fklist.on('click', '#fknext', function() {
+ jQuery.ppa.fklist.on('click', '#fknext', function () {
jQuery.ppa.o += 11;
/* get the field that is the previous html elt from the #fklist
* and trigger its focus to refresh the list AND actualy
@@ -206,20 +205,20 @@ jQuery(document).ready(function() {
});
/* close the list when clicking outside of it */
- jQuery.ppa.fkbg.click(function(e) {
+ jQuery.ppa.fkbg.click(function (e) {
hideAc();
});
/* do not submit the form when selecting a value by pressing enter */
jQuery.ppa.attrs
- .keydown(function(e) {
+ .keydown(function (e) {
if (e.keyCode == 13 && jQuery.ppa.fklist[0].style.display == 'block')
return false;
});
/* enable/disable auto-complete according to the checkbox */
triggerAc(
- jQuery('#no_ac').click(function() {
+ jQuery('#no_ac').click(function () {
triggerAc(this.checked);
})[0].checked
);
diff --git a/js/database.js b/js/database.js
index f70f7078..3ec12411 100644
--- a/js/database.js
+++ b/js/database.js
@@ -1,55 +1,58 @@
-$(document).ready(function() {
+$(document).ready(function () {
var timeid = query = null;
var controlLink = $('#control');
- var errmsg = $('<p class="errmsg">'+Database.errmsg+'</p>')
+
+ var errmsg = $('<p class="errmsg">' + Database.errmsg + '</p>')
.insertBefore(controlLink)
.hide();
- var loading = $('<img class="loading" alt="[loading]" src="'+ Database.load_icon +'" />')
+
+ var loading = $('<img class="loading" alt="[loading]" src="' + Database.load_icon + '" />')
.insertAfter(controlLink)
.hide();
+
function refreshTable() {
if (Database.ajax_time_refresh > 0) {
loading.show();
query = $.ajax({
type: 'GET',
dataType: 'html',
- data: {server: Database.server, database: Database.dbname, action: Database.action},
- url: 'database.php',
+ data: {
+ server: Database.server,
+ database: Database.dbname,
+ action: Database.action
+ },
+ url: '/src/views/database.php',
cache: false,
contentType: 'application/x-www-form-urlencoded',
- success: function(html) {
+ success: function (html) {
$('#data_block').html(html);
timeid = window.setTimeout(refreshTable, Database.ajax_time_refresh)
},
- error: function() {
+ error: function () {
controlLink.click();
- errmsg.show();
+ //errmsg.show();
},
complete: function () {
- loading.hide();
+ //loading.hide();
}
});
}
}
controlLink.toggle(
- function() {
- $(errmsg).hide();
+ function () {
+ //$(errmsg).hide();
timeid = window.setTimeout(refreshTable, Database.ajax_time_refresh);
- controlLink.html('<img src="'+ Database.str_stop.icon +'" alt="" />&nbsp;'
- + Database.str_stop.text + '&nbsp;&nbsp;&nbsp;'
- );
+ controlLink.html('<img src="' + Database.str_stop.icon + '" alt="" />&nbsp;' + Database.str_stop.text + '&nbsp;&nbsp;&nbsp;');
},
- function() {
- $(errmsg).hide();
- $(loading).hide();
+ function () {
+ //$(errmsg).hide();
+ //$(loading).hide();
window.clearInterval(timeid);
if (query) query.abort();
- controlLink.html('<img src="'+ Database.str_start.icon +'" alt="" />&nbsp;'
- + Database.str_start.text
- );
+ controlLink.html('<img src="' + Database.str_start.icon + '" alt="" />&nbsp;' + Database.str_start.text);
}
);
@@ -58,7 +61,7 @@ $(document).ready(function() {
.attr('src', Database.str_start.icon)
.attr('src', Database.str_stop.icon)
.show();
-
+
/* start refreshing */
controlLink.click();
});
diff --git a/js/datatables.min.js b/js/datatables.min.js
new file mode 100644
index 00000000..bd282fca
--- /dev/null
+++ b/js/datatables.min.js
@@ -0,0 +1,14787 @@
+/*! DataTables 1.10.12
+ * ©2008-2015 SpryMedia Ltd - datatables.net/license
+ */
+
+/**
+ * @summary DataTables
+ * @description Paginate, search and order HTML tables
+ * @version 1.10.12
+ * @file jquery.dataTables.js
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
+ * @contact www.sprymedia.co.uk/contact
+ * @copyright Copyright 2008-2015 SpryMedia Ltd.
+ *
+ * This source file is free software, available under the following license:
+ * MIT license - http://datatables.net/license
+ *
+ * This source file 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 license files for details.
+ *
+ * For details please refer to: http://www.datatables.net
+ */
+
+/*jslint evil: true, undef: true, browser: true */
+/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
+
+(function (factory) {
+ "use strict";
+
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['jquery'], function ($) {
+ return factory($, window, document);
+ });
+ } else if (typeof exports === 'object') {
+ // CommonJS
+ module.exports = function (root, $) {
+ if (!root) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
+
+ if (!$) {
+ $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
+ require('jquery') :
+ require('jquery')(root);
+ }
+
+ return factory($, root, root.document);
+ };
+ } else {
+ // Browser
+ factory(jQuery, window, document);
+ }
+ }
+ (function ($, window, document, undefined) {
+ "use strict";
+
+ /**
+ * DataTables is a plug-in for the jQuery Javascript library. It is a highly
+ * flexible tool, based upon the foundations of progressive enhancement,
+ * which will add advanced interaction controls to any HTML table. For a
+ * full list of features please refer to
+ * [DataTables.net](href="http://datatables.net).
+ *
+ * Note that the `DataTable` object is not a global variable but is aliased
+ * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
+ * be accessed.
+ *
+ * @class
+ * @param {object} [init={}] Configuration object for DataTables. Options
+ * are defined by {@link DataTable.defaults}
+ * @requires jQuery 1.7+
+ *
+ * @example
+ * // Basic initialisation
+ * $(document).ready( function {
+ * $('#example').dataTable();
+ * } );
+ *
+ * @example
+ * // Initialisation with configuration options - in this case, disable
+ * // pagination and sorting.
+ * $(document).ready( function {
+ * $('#example').dataTable( {
+ * "paginate": false,
+ * "sort": false
+ * } );
+ * } );
+ */
+ var DataTable = function (options) {
+ /**
+ * Perform a jQuery selector action on the table's TR elements (from the tbody) and
+ * return the resulting jQuery object.
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
+ * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
+ * criterion ("applied") or all TR elements (i.e. no filter).
+ * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
+ * Can be either 'current', whereby the current sorting of the table is used, or
+ * 'original' whereby the original order the data was read into the table is used.
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
+ * 'current' and filter is 'applied', regardless of what they might be given as.
+ * @returns {object} jQuery object, filtered by the given selector.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Highlight every second row
+ * oTable.$('tr:odd').css('backgroundColor', 'blue');
+ * } );
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Filter to rows with 'Webkit' in them, add a background colour and then
+ * // remove the filter, thus highlighting the 'Webkit' rows only.
+ * oTable.fnFilter('Webkit');
+ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
+ * oTable.fnFilter('');
+ * } );
+ */
+ this.$ = function (sSelector, oOpts) {
+ return this.api(true).$(sSelector, oOpts);
+ };
+
+
+ /**
+ * Almost identical to $ in operation, but in this case returns the data for the matched
+ * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
+ * rather than any descendants, so the data can be obtained for the row/cell. If matching
+ * rows are found, the data returned is the original data array/object that was used to
+ * create the row (or a generated array if from a DOM source).
+ *
+ * This method is often useful in-combination with $ where both functions are given the
+ * same parameters and the array indexes will match identically.
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
+ * @param {string} [oOpts.filter=none] Select elements that meet the current filter
+ * criterion ("applied") or all elements (i.e. no filter).
+ * @param {string} [oOpts.order=current] Order of the data in the processed array.
+ * Can be either 'current', whereby the current sorting of the table is used, or
+ * 'original' whereby the original order the data was read into the table is used.
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
+ * 'current' and filter is 'applied', regardless of what they might be given as.
+ * @returns {array} Data for the matched elements. If any elements, as a result of the
+ * selector, were not TR, TD or TH elements in the DataTable, they will have a null
+ * entry in the array.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Get the data from the first row in the table
+ * var data = oTable._('tr:first');
+ *
+ * // Do something useful with the data
+ * alert( "First cell is: "+data[0] );
+ * } );
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Filter to 'Webkit' and get all data for
+ * oTable.fnFilter('Webkit');
+ * var data = oTable._('tr', {"search": "applied"});
+ *
+ * // Do something with the data
+ * alert( data.length+" rows matched the search" );
+ * } );
+ */
+ this._ = function (sSelector, oOpts) {
+ return this.api(true).rows(sSelector, oOpts).data();
+ };
+
+
+ /**
+ * Create a DataTables Api instance, with the currently selected tables for
+ * the Api's context.
+ * @param {boolean} [traditional=false] Set the API instance's context to be
+ * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
+ * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
+ * or if all tables captured in the jQuery object should be used.
+ * @return {DataTables.Api}
+ */
+ this.api = function (traditional) {
+ return traditional ?
+ new _Api(
+ _fnSettingsFromNode(this[_ext.iApiIndex])
+ ) :
+ new _Api(this);
+ };
+
+
+ /**
+ * Add a single new row or multiple rows of data to the table. Please note
+ * that this is suitable for client-side processing only - if you are using
+ * server-side processing (i.e. "bServerSide": true), then to add data, you
+ * must add it to the data source, i.e. the server-side, through an Ajax call.
+ * @param {array|object} data The data to be added to the table. This can be:
+ * <ul>
+ * <li>1D array of data - add a single row with the data provided</li>
+ * <li>2D array of arrays - add multiple rows in a single call</li>
+ * <li>object - data object when using <i>mData</i></li>
+ * <li>array of objects - multiple data objects when using <i>mData</i></li>
+ * </ul>
+ * @param {bool} [redraw=true] redraw the table or not
+ * @returns {array} An array of integers, representing the list of indexes in
+ * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
+ * the table.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * // Global var for counter
+ * var giCount = 2;
+ *
+ * $(document).ready(function() {
+ * $('#example').dataTable();
+ * } );
+ *
+ * function fnClickAddRow() {
+ * $('#example').dataTable().fnAddData( [
+ * giCount+".1",
+ * giCount+".2",
+ * giCount+".3",
+ * giCount+".4" ]
+ * );
+ *
+ * giCount++;
+ * }
+ */
+ this.fnAddData = function (data, redraw) {
+ var api = this.api(true);
+
+ /* Check if we want to add multiple rows or not */
+ var rows = $.isArray(data) && ($.isArray(data[0]) || $.isPlainObject(data[0])) ?
+ api.rows.add(data) :
+ api.row.add(data);
+
+ if (redraw === undefined || redraw) {
+ api.draw();
+ }
+
+ return rows.flatten().toArray();
+ };
+
+
+ /**
+ * This function will make DataTables recalculate the column sizes, based on the data
+ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
+ * through the sWidth parameter). This can be useful when the width of the table's
+ * parent element changes (for example a window resize).
+ * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable( {
+ * "sScrollY": "200px",
+ * "bPaginate": false
+ * } );
+ *
+ * $(window).bind('resize', function () {
+ * oTable.fnAdjustColumnSizing();
+ * } );
+ * } );
+ */
+ this.fnAdjustColumnSizing = function (bRedraw) {
+ var api = this.api(true).columns.adjust();
+ var settings = api.settings()[0];
+ var scroll = settings.oScroll;
+
+ if (bRedraw === undefined || bRedraw) {
+ api.draw(false);
+ } else if (scroll.sX !== "" || scroll.sY !== "") {
+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
+ _fnScrollDraw(settings);
+ }
+ };
+
+
+ /**
+ * Quickly and simply clear a table
+ * @param {bool} [bRedraw=true] redraw the table or not
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
+ * oTable.fnClearTable();
+ * } );
+ */
+ this.fnClearTable = function (bRedraw) {
+ var api = this.api(true).clear();
+
+ if (bRedraw === undefined || bRedraw) {
+ api.draw();
+ }
+ };
+
+
+ /**
+ * The exact opposite of 'opening' a row, this function will close any rows which
+ * are currently 'open'.
+ * @param {node} nTr the table row to 'close'
+ * @returns {int} 0 on success, or 1 if failed (can't find the row)
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable;
+ *
+ * // 'open' an information row when a row is clicked on
+ * $('#example tbody tr').click( function () {
+ * if ( oTable.fnIsOpen(this) ) {
+ * oTable.fnClose( this );
+ * } else {
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
+ * }
+ * } );
+ *
+ * oTable = $('#example').dataTable();
+ * } );
+ */
+ this.fnClose = function (nTr) {
+ this.api(true).row(nTr).child.hide();
+ };
+
+
+ /**
+ * Remove a row for the table
+ * @param {mixed} target The index of the row from aoData to be deleted, or
+ * the TR element you want to delete
+ * @param {function|null} [callBack] Callback function
+ * @param {bool} [redraw=true] Redraw the table or not
+ * @returns {array} The row that was deleted
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Immediately remove the first row
+ * oTable.fnDeleteRow( 0 );
+ * } );
+ */
+ this.fnDeleteRow = function (target, callback, redraw) {
+ var api = this.api(true);
+ var rows = api.rows(target);
+ var settings = rows.settings()[0];
+ var data = settings.aoData[rows[0][0]];
+
+ rows.remove();
+
+ if (callback) {
+ callback.call(this, settings, data);
+ }
+
+ if (redraw === undefined || redraw) {
+ api.draw();
+ }
+
+ return data;
+ };
+
+
+ /**
+ * Restore the table to it's original state in the DOM by removing all of DataTables
+ * enhancements, alterations to the DOM structure of the table and event listeners.
+ * @param {boolean} [remove=false] Completely remove the table from the DOM
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * // This example is fairly pointless in reality, but shows how fnDestroy can be used
+ * var oTable = $('#example').dataTable();
+ * oTable.fnDestroy();
+ * } );
+ */
+ this.fnDestroy = function (remove) {
+ this.api(true).destroy(remove);
+ };
+
+
+ /**
+ * Redraw the table
+ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
+ * oTable.fnDraw();
+ * } );
+ */
+ this.fnDraw = function (complete) {
+ // Note that this isn't an exact match to the old call to _fnDraw - it takes
+ // into account the new data, but can hold position.
+ this.api(true).draw(complete);
+ };
+
+
+ /**
+ * Filter the input based on data
+ * @param {string} sInput String to filter the table on
+ * @param {int|null} [iColumn] Column to limit filtering to
+ * @param {bool} [bRegex=false] Treat as regular expression or not
+ * @param {bool} [bSmart=true] Perform smart filtering or not
+ * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
+ * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Sometime later - filter...
+ * oTable.fnFilter( 'test string' );
+ * } );
+ */
+ this.fnFilter = function (sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive) {
+ var api = this.api(true);
+
+ if (iColumn === null || iColumn === undefined) {
+ api.search(sInput, bRegex, bSmart, bCaseInsensitive);
+ } else {
+ api.column(iColumn).search(sInput, bRegex, bSmart, bCaseInsensitive);
+ }
+
+ api.draw();
+ };
+
+
+ /**
+ * Get the data for the whole table, an individual row or an individual cell based on the
+ * provided parameters.
+ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
+ * a TR node then the data source for the whole row will be returned. If given as a
+ * TD/TH cell node then iCol will be automatically calculated and the data for the
+ * cell returned. If given as an integer, then this is treated as the aoData internal
+ * data index for the row (see fnGetPosition) and the data for that row used.
+ * @param {int} [col] Optional column index that you want the data of.
+ * @returns {array|object|string} If mRow is undefined, then the data for all rows is
+ * returned. If mRow is defined, just data for that row, and is iCol is
+ * defined, only data for the designated cell is returned.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * // Row data
+ * $(document).ready(function() {
+ * oTable = $('#example').dataTable();
+ *
+ * oTable.$('tr').click( function () {
+ * var data = oTable.fnGetData( this );
+ * // ... do something with the array / object of data for the row
+ * } );
+ * } );
+ *
+ * @example
+ * // Individual cell data
+ * $(document).ready(function() {
+ * oTable = $('#example').dataTable();
+ *
+ * oTable.$('td').click( function () {
+ * var sData = oTable.fnGetData( this );
+ * alert( 'The cell clicked on had the value of '+sData );
+ * } );
+ * } );
+ */
+ this.fnGetData = function (src, col) {
+ var api = this.api(true);
+
+ if (src !== undefined) {
+ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
+
+ return col !== undefined || type == 'td' || type == 'th' ?
+ api.cell(src, col).data() :
+ api.row(src).data() || null;
+ }
+
+ return api.data().toArray();
+ };
+
+
+ /**
+ * Get an array of the TR nodes that are used in the table's body. Note that you will
+ * typically want to use the '$' API method in preference to this as it is more
+ * flexible.
+ * @param {int} [iRow] Optional row index for the TR element you want
+ * @returns {array|node} If iRow is undefined, returns an array of all TR elements
+ * in the table's body, or iRow is defined, just the TR element requested.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Get the nodes from the table
+ * var nNodes = oTable.fnGetNodes( );
+ * } );
+ */
+ this.fnGetNodes = function (iRow) {
+ var api = this.api(true);
+
+ return iRow !== undefined ?
+ api.row(iRow).node() :
+ api.rows().nodes().flatten().toArray();
+ };
+
+
+ /**
+ * Get the array indexes of a particular cell from it's DOM element
+ * and column index including hidden columns
+ * @param {node} node this can either be a TR, TD or TH in the table's body
+ * @returns {int} If nNode is given as a TR, then a single index is returned, or
+ * if given as a cell, an array of [row index, column index (visible),
+ * column index (all)] is given.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * $('#example tbody td').click( function () {
+ * // Get the position of the current data from the node
+ * var aPos = oTable.fnGetPosition( this );
+ *
+ * // Get the data array for this row
+ * var aData = oTable.fnGetData( aPos[0] );
+ *
+ * // Update the data array and return the value
+ * aData[ aPos[1] ] = 'clicked';
+ * this.innerHTML = 'clicked';
+ * } );
+ *
+ * // Init DataTables
+ * oTable = $('#example').dataTable();
+ * } );
+ */
+ this.fnGetPosition = function (node) {
+ var api = this.api(true);
+ var nodeName = node.nodeName.toUpperCase();
+
+ if (nodeName == 'TR') {
+ return api.row(node).index();
+ } else if (nodeName == 'TD' || nodeName == 'TH') {
+ var cell = api.cell(node).index();
+
+ return [
+ cell.row,
+ cell.columnVisible,
+ cell.column
+ ];
+ }
+ return null;
+ };
+
+
+ /**
+ * Check to see if a row is 'open' or not.
+ * @param {node} nTr the table row to check
+ * @returns {boolean} true if the row is currently open, false otherwise
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable;
+ *
+ * // 'open' an information row when a row is clicked on
+ * $('#example tbody tr').click( function () {
+ * if ( oTable.fnIsOpen(this) ) {
+ * oTable.fnClose( this );
+ * } else {
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
+ * }
+ * } );
+ *
+ * oTable = $('#example').dataTable();
+ * } );
+ */
+ this.fnIsOpen = function (nTr) {
+ return this.api(true).row(nTr).child.isShown();
+ };
+
+
+ /**
+ * This function will place a new row directly after a row which is currently
+ * on display on the page, with the HTML contents that is passed into the
+ * function. This can be used, for example, to ask for confirmation that a
+ * particular record should be deleted.
+ * @param {node} nTr The table row to 'open'
+ * @param {string|node|jQuery} mHtml The HTML to put into the row
+ * @param {string} sClass Class to give the new TD cell
+ * @returns {node} The row opened. Note that if the table row passed in as the
+ * first parameter, is not found in the table, this method will silently
+ * return.
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable;
+ *
+ * // 'open' an information row when a row is clicked on
+ * $('#example tbody tr').click( function () {
+ * if ( oTable.fnIsOpen(this) ) {
+ * oTable.fnClose( this );
+ * } else {
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
+ * }
+ * } );
+ *
+ * oTable = $('#example').dataTable();
+ * } );
+ */
+ this.fnOpen = function (nTr, mHtml, sClass) {
+ return this.api(true)
+ .row(nTr)
+ .child(mHtml, sClass)
+ .show()
+ .child()[0];
+ };
+
+
+ /**
+ * Change the pagination - provides the internal logic for pagination in a simple API
+ * function. With this function you can have a DataTables table go to the next,
+ * previous, first or last pages.
+ * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
+ * or page number to jump to (integer), note that page 0 is the first page.
+ * @param {bool} [bRedraw=true] Redraw the table or not
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ * oTable.fnPageChange( 'next' );
+ * } );
+ */
+ this.fnPageChange = function (mAction, bRedraw) {
+ var api = this.api(true).page(mAction);
+
+ if (bRedraw === undefined || bRedraw) {
+ api.draw(false);
+ }
+ };
+
+
+ /**
+ * Show a particular column
+ * @param {int} iCol The column whose display should be changed
+ * @param {bool} bShow Show (true) or hide (false) the column
+ * @param {bool} [bRedraw=true] Redraw the table or not
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Hide the second column after initialisation
+ * oTable.fnSetColumnVis( 1, false );
+ * } );
+ */
+ this.fnSetColumnVis = function (iCol, bShow, bRedraw) {
+ var api = this.api(true).column(iCol).visible(bShow);
+
+ if (bRedraw === undefined || bRedraw) {
+ api.columns.adjust().draw();
+ }
+ };
+
+
+ /**
+ * Get the settings for a particular table for external manipulation
+ * @returns {object} DataTables settings object. See
+ * {@link DataTable.models.oSettings}
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ * var oSettings = oTable.fnSettings();
+ *
+ * // Show an example parameter from the settings
+ * alert( oSettings._iDisplayStart );
+ * } );
+ */
+ this.fnSettings = function () {
+ return _fnSettingsFromNode(this[_ext.iApiIndex]);
+ };
+
+
+ /**
+ * Sort the table by a particular column
+ * @param {int} iCol the data index to sort on. Note that this will not match the
+ * 'display index' if you have hidden data entries
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Sort immediately with columns 0 and 1
+ * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
+ * } );
+ */
+ this.fnSort = function (aaSort) {
+ this.api(true).order(aaSort).draw();
+ };
+
+
+ /**
+ * Attach a sort listener to an element for a given column
+ * @param {node} nNode the element to attach the sort listener to
+ * @param {int} iColumn the column that a click on this node will sort on
+ * @param {function} [fnCallback] callback function when sort is run
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ *
+ * // Sort on column 1, when 'sorter' is clicked on
+ * oTable.fnSortListener( document.getElementById('sorter'), 1 );
+ * } );
+ */
+ this.fnSortListener = function (nNode, iColumn, fnCallback) {
+ this.api(true).order.listener(nNode, iColumn, fnCallback);
+ };
+
+
+ /**
+ * Update a table cell or row - this method will accept either a single value to
+ * update the cell with, an array of values with one element for each column or
+ * an object in the same format as the original data source. The function is
+ * self-referencing in order to make the multi column updates easier.
+ * @param {object|array|string} mData Data to update the cell/row with
+ * @param {node|int} mRow TR element you want to update or the aoData index
+ * @param {int} [iColumn] The column to update, give as null or undefined to
+ * update a whole row.
+ * @param {bool} [bRedraw=true] Redraw the table or not
+ * @param {bool} [bAction=true] Perform pre-draw actions or not
+ * @returns {int} 0 on success, 1 on error
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
+ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
+ * } );
+ */
+ this.fnUpdate = function (mData, mRow, iColumn, bRedraw, bAction) {
+ var api = this.api(true);
+
+ if (iColumn === undefined || iColumn === null) {
+ api.row(mRow).data(mData);
+ } else {
+ api.cell(mRow, iColumn).data(mData);
+ }
+
+ if (bAction === undefined || bAction) {
+ api.columns.adjust();
+ }
+
+ if (bRedraw === undefined || bRedraw) {
+ api.draw();
+ }
+ return 0;
+ };
+
+
+ /**
+ * Provide a common method for plug-ins to check the version of DataTables being used, in order
+ * to ensure compatibility.
+ * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
+ * formats "X" and "X.Y" are also acceptable.
+ * @returns {boolean} true if this version of DataTables is greater or equal to the required
+ * version, or false if this version of DataTales is not suitable
+ * @method
+ * @dtopt API
+ * @deprecated Since v1.10
+ *
+ * @example
+ * $(document).ready(function() {
+ * var oTable = $('#example').dataTable();
+ * alert( oTable.fnVersionCheck( '1.9.0' ) );
+ * } );
+ */
+ this.fnVersionCheck = _ext.fnVersionCheck;
+
+
+ var _that = this;
+ var emptyInit = options === undefined;
+ var len = this.length;
+
+ if (emptyInit) {
+ options = {};
+ }
+
+ this.oApi = this.internal = _ext.internal;
+
+ // Extend with old style plug-in API methods
+ for (var fn in DataTable.ext.internal) {
+ if (fn) {
+ this[fn] = _fnExternApiFunc(fn);
+ }
+ }
+
+ this.each(function () {
+ // For each initialisation we want to give it a clean initialisation
+ // object that can be bashed around
+ var o = {};
+ var oInit = len > 1 ? // optimisation for single table case
+ _fnExtend(o, options, true) :
+ options;
+
+ /*global oInit,_that,emptyInit*/
+ var i = 0,
+ iLen, j, jLen, k, kLen;
+ var sId = this.getAttribute('id');
+ var bInitHandedOff = false;
+ var defaults = DataTable.defaults;
+ var $this = $(this);
+
+
+ /* Sanity check */
+ if (this.nodeName.toLowerCase() != 'table') {
+ _fnLog(null, 0, 'Non-table node initialisation (' + this.nodeName + ')', 2);
+ return;
+ }
+
+ /* Backwards compatibility for the defaults */
+ _fnCompatOpts(defaults);
+ _fnCompatCols(defaults.column);
+
+ /* Convert the camel-case defaults to Hungarian */
+ _fnCamelToHungarian(defaults, defaults, true);
+ _fnCamelToHungarian(defaults.column, defaults.column, true);
+
+ /* Setting up the initialisation object */
+ _fnCamelToHungarian(defaults, $.extend(oInit, $this.data()));
+
+
+ /* Check to see if we are re-initialising a table */
+ var allSettings = DataTable.settings;
+ for (i = 0, iLen = allSettings.length; i < iLen; i++) {
+ var s = allSettings[i];
+
+ /* Base check on table node */
+ if (s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this)) {
+ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
+ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
+
+ if (emptyInit || bRetrieve) {
+ return s.oInstance;
+ } else if (bDestroy) {
+ s.oInstance.fnDestroy();
+ break;
+ } else {
+ _fnLog(s, 0, 'Cannot reinitialise DataTable', 3);
+ return;
+ }
+ }
+
+ /* If the element we are initialising has the same ID as a table which was previously
+ * initialised, but the table nodes don't match (from before) then we destroy the old
+ * instance by simply deleting it. This is under the assumption that the table has been
+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
+ */
+ if (s.sTableId == this.id) {
+ allSettings.splice(i, 1);
+ break;
+ }
+ }
+
+ /* Ensure the table has an ID - required for accessibility */
+ if (sId === null || sId === "") {
+ sId = "DataTables_Table_" + (DataTable.ext._unique++);
+ this.id = sId;
+ }
+
+ /* Create the settings object for this table and set some of the default parameters */
+ var oSettings = $.extend(true, {}, DataTable.models.oSettings, {
+ "sDestroyWidth": $this[0].style.width,
+ "sInstance": sId,
+ "sTableId": sId
+ });
+ oSettings.nTable = this;
+ oSettings.oApi = _that.internal;
+ oSettings.oInit = oInit;
+
+ allSettings.push(oSettings);
+
+ // Need to add the instance after the instance after the settings object has been added
+ // to the settings array, so we can self reference the table instance if more than one
+ oSettings.oInstance = (_that.length === 1) ? _that : $this.dataTable();
+
+ // Backwards compatibility, before we apply all the defaults
+ _fnCompatOpts(oInit);
+
+ if (oInit.oLanguage) {
+ _fnLanguageCompat(oInit.oLanguage);
+ }
+
+ // If the length menu is given, but the init display length is not, use the length menu
+ if (oInit.aLengthMenu && !oInit.iDisplayLength) {
+ oInit.iDisplayLength = $.isArray(oInit.aLengthMenu[0]) ?
+ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
+ }
+
+ // Apply the defaults and init options to make a single init object will all
+ // options defined from defaults and instance options.
+ oInit = _fnExtend($.extend(true, {}, defaults), oInit);
+
+
+ // Map the initialisation options onto the settings object
+ _fnMap(oSettings.oFeatures, oInit, [
+ "bPaginate",
+ "bLengthChange",
+ "bFilter",
+ "bSort",
+ "bSortMulti",
+ "bInfo",
+ "bProcessing",
+ "bAutoWidth",
+ "bSortClasses",
+ "bServerSide",
+ "bDeferRender"
+ ]);
+ _fnMap(oSettings, oInit, [
+ "asStripeClasses",
+ "ajax",
+ "fnServerData",
+ "fnFormatNumber",
+ "sServerMethod",
+ "aaSorting",
+ "aaSortingFixed",
+ "aLengthMenu",
+ "sPaginationType",
+ "sAjaxSource",
+ "sAjaxDataProp",
+ "iStateDuration",
+ "sDom",
+ "bSortCellsTop",
+ "iTabIndex",
+ "fnStateLoadCallback",
+ "fnStateSaveCallback",
+ "renderer",
+ "searchDelay",
+ "rowId", ["iCookieDuration", "iStateDuration"], // backwards compat
+ ["oSearch", "oPreviousSearch"],
+ ["aoSearchCols", "aoPreSearchCols"],
+ ["iDisplayLength", "_iDisplayLength"],
+ ["bJQueryUI", "bJUI"]
+ ]);
+ _fnMap(oSettings.oScroll, oInit, [
+ ["sScrollX", "sX"],
+ ["sScrollXInner", "sXInner"],
+ ["sScrollY", "sY"],
+ ["bScrollCollapse", "bCollapse"]
+ ]);
+ _fnMap(oSettings.oLanguage, oInit, "fnInfoCallback");
+
+ /* Callback functions which are array driven */
+ _fnCallbackReg(oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user');
+ _fnCallbackReg(oSettings, 'aoServerParams', oInit.fnServerParams, 'user');
+ _fnCallbackReg(oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user');
+ _fnCallbackReg(oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user');
+ _fnCallbackReg(oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user');
+ _fnCallbackReg(oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user');
+ _fnCallbackReg(oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user');
+ _fnCallbackReg(oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user');
+ _fnCallbackReg(oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user');
+ _fnCallbackReg(oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user');
+ _fnCallbackReg(oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user');
+
+ oSettings.rowIdFn = _fnGetObjectDataFn(oInit.rowId);
+
+ /* Browser support detection */
+ _fnBrowserDetect(oSettings);
+
+ var oClasses = oSettings.oClasses;
+
+ // @todo Remove in 1.11
+ if (oInit.bJQueryUI) {
+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
+ * you want to have multiple tables with multiple independent classes
+ */
+ $.extend(oClasses, DataTable.ext.oJUIClasses, oInit.oClasses);
+
+ if (oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip") {
+ /* Set the DOM to use a layout suitable for jQuery UI's theming */
+ oSettings.sDom = '<"H"lfr>t<"F"ip>';
+ }
+
+ if (!oSettings.renderer) {
+ oSettings.renderer = 'jqueryui';
+ } else if ($.isPlainObject(oSettings.renderer) && !oSettings.renderer.header) {
+ oSettings.renderer.header = 'jqueryui';
+ }
+ } else {
+ $.extend(oClasses, DataTable.ext.classes, oInit.oClasses);
+ }
+ $this.addClass(oClasses.sTable);
+
+
+ if (oSettings.iInitDisplayStart === undefined) {
+ /* Display start point, taking into account the save saving */
+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
+ oSettings._iDisplayStart = oInit.iDisplayStart;
+ }
+
+ if (oInit.iDeferLoading !== null) {
+ oSettings.bDeferLoading = true;
+ var tmp = $.isArray(oInit.iDeferLoading);
+ oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
+ oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
+ }
+
+ /* Language definitions */
+ var oLanguage = oSettings.oLanguage;
+ $.extend(true, oLanguage, oInit.oLanguage);
+
+ if (oLanguage.sUrl !== "") {
+ /* Get the language definitions from a file - because this Ajax call makes the language
+ * get async to the remainder of this function we use bInitHandedOff to indicate that
+ * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
+ */
+ $.ajax({
+ dataType: 'json',
+ url: oLanguage.sUrl,
+ success: function (json) {
+ _fnLanguageCompat(json);
+ _fnCamelToHungarian(defaults.oLanguage, json);
+ $.extend(true, oLanguage, json);
+ _fnInitialise(oSettings);
+ },
+ error: function () {
+ // Error occurred loading language file, continue on as best we can
+ _fnInitialise(oSettings);
+ }
+ });
+ bInitHandedOff = true;
+ }
+
+ /*
+ * Stripes
+ */
+ if (oInit.asStripeClasses === null) {
+ oSettings.asStripeClasses = [
+ oClasses.sStripeOdd,
+ oClasses.sStripeEven
+ ];
+ }
+
+ /* Remove row stripe classes if they are already on the table row */
+ var stripeClasses = oSettings.asStripeClasses;
+ var rowOne = $this.children('tbody').find('tr').eq(0);
+ if ($.inArray(true, $.map(stripeClasses, function (el, i) {
+ return rowOne.hasClass(el);
+ })) !== -1) {
+ $('tbody tr', this).removeClass(stripeClasses.join(' '));
+ oSettings.asDestroyStripes = stripeClasses.slice();
+ }
+
+ /*
+ * Columns
+ * See if we should load columns automatically or use defined ones
+ */
+ var anThs = [];
+ var aoColumnsInit;
+ var nThead = this.getElementsByTagName('thead');
+ if (nThead.length !== 0) {
+ _fnDetectHeader(oSettings.aoHeader, nThead[0]);
+ anThs = _fnGetUniqueThs(oSettings);
+ }
+
+ /* If not given a column array, generate one with nulls */
+ if (oInit.aoColumns === null) {
+ aoColumnsInit = [];
+ for (i = 0, iLen = anThs.length; i < iLen; i++) {
+ aoColumnsInit.push(null);
+ }
+ } else {
+ aoColumnsInit = oInit.aoColumns;
+ }
+
+ /* Add the columns */
+ for (i = 0, iLen = aoColumnsInit.length; i < iLen; i++) {
+ _fnAddColumn(oSettings, anThs ? anThs[i] : null);
+ }
+
+ /* Apply the column definitions */
+ _fnApplyColumnDefs(oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
+ _fnColumnOptions(oSettings, iCol, oDef);
+ });
+
+ /* HTML5 attribute detection - build an mData object automatically if the
+ * attributes are found
+ */
+ if (rowOne.length) {
+ var a = function (cell, name) {
+ return cell.getAttribute('data-' + name) !== null ? name : null;
+ };
+
+ $(rowOne[0]).children('th, td').each(function (i, cell) {
+ var col = oSettings.aoColumns[i];
+
+ if (col.mData === i) {
+ var sort = a(cell, 'sort') || a(cell, 'order');
+ var filter = a(cell, 'filter') || a(cell, 'search');
+
+ if (sort !== null || filter !== null) {
+ col.mData = {
+ _: i + '.display',
+ sort: sort !== null ? i + '.@data-' + sort : undefined,
+ type: sort !== null ? i + '.@data-' + sort : undefined,
+ filter: filter !== null ? i + '.@data-' + filter : undefined
+ };
+
+ _fnColumnOptions(oSettings, i);
+ }
+ }
+ });
+ }
+
+ var features = oSettings.oFeatures;
+
+ /* Must be done after everything which can be overridden by the state saving! */
+ if (oInit.bStateSave) {
+ features.bStateSave = true;
+ _fnLoadState(oSettings, oInit);
+ _fnCallbackReg(oSettings, 'aoDrawCallback', _fnSaveState, 'state_save');
+ }
+
+
+ /*
+ * Sorting
+ * @todo For modularisation (1.11) this needs to do into a sort start up handler
+ */
+
+ // If aaSorting is not defined, then we use the first indicator in asSorting
+ // in case that has been altered, so the default sort reflects that option
+ if (oInit.aaSorting === undefined) {
+ var sorting = oSettings.aaSorting;
+ for (i = 0, iLen = sorting.length; i < iLen; i++) {
+ sorting[i][1] = oSettings.aoColumns[i].asSorting[0];
+ }
+ }
+
+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
+ * account, and also will apply sorting disabled classes if disabled
+ */
+ _fnSortingClasses(oSettings);
+
+ if (features.bSort) {
+ _fnCallbackReg(oSettings, 'aoDrawCallback', function () {
+ if (oSettings.bSorted) {
+ var aSort = _fnSortFlatten(oSettings);
+ var sortedColumns = {};
+
+ $.each(aSort, function (i, val) {
+ sortedColumns[val.src] = val.dir;
+ });
+
+ _fnCallbackFire(oSettings, null, 'order', [oSettings, aSort, sortedColumns]);
+ _fnSortAria(oSettings);
+ }
+ });
+ }
+
+ _fnCallbackReg(oSettings, 'aoDrawCallback', function () {
+ if (oSettings.bSorted || _fnDataSource(oSettings) === 'ssp' || features.bDeferRender) {
+ _fnSortingClasses(oSettings);
+ }
+ }, 'sc');
+
+
+ /*
+ * Final init
+ * Cache the header, body and footer as required, creating them if needed
+ */
+
+ // Work around for Webkit bug 83867 - store the caption-side before removing from doc
+ var captions = $this.children('caption').each(function () {
+ this._captionSide = $this.css('caption-side');
+ });
+
+ var thead = $this.children('thead');
+ if (thead.length === 0) {
+ thead = $('<thead/>').appendTo(this);
+ }
+ oSettings.nTHead = thead[0];
+
+ var tbody = $this.children('tbody');
+ if (tbody.length === 0) {
+ tbody = $('<tbody/>').appendTo(this);
+ }
+ oSettings.nTBody = tbody[0];
+
+ var tfoot = $this.children('tfoot');
+ if (tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "")) {
+ // If we are a scrolling table, and no footer has been given, then we need to create
+ // a tfoot element for the caption element to be appended to
+ tfoot = $('<tfoot/>').appendTo(this);
+ }
+
+ if (tfoot.length === 0 || tfoot.children().length === 0) {
+ $this.addClass(oClasses.sNoFooter);
+ } else if (tfoot.length > 0) {
+ oSettings.nTFoot = tfoot[0];
+ _fnDetectHeader(oSettings.aoFooter, oSettings.nTFoot);
+ }
+
+ /* Check if there is data passing into the constructor */
+ if (oInit.aaData) {
+ for (i = 0; i < oInit.aaData.length; i++) {
+ _fnAddData(oSettings, oInit.aaData[i]);
+ }
+ } else if (oSettings.bDeferLoading || _fnDataSource(oSettings) == 'dom') {
+ /* Grab the data from the page - only do this when deferred loading or no Ajax
+ * source since there is no point in reading the DOM data if we are then going
+ * to replace it with Ajax data
+ */
+ _fnAddTr(oSettings, $(oSettings.nTBody).children('tr'));
+ }
+
+ /* Copy the data index array */
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+
+ /* Initialisation complete - table can be drawn */
+ oSettings.bInitialised = true;
+
+ /* Check if we need to initialise the table (it might not have been handed off to the
+ * language processor)
+ */
+ if (bInitHandedOff === false) {
+ _fnInitialise(oSettings);
+ }
+ });
+ _that = null;
+
+
+ return this;
+ };
+
+
+ /*
+ * It is useful to have variables which are scoped locally so only the
+ * DataTables functions can access them and they don't leak into global space.
+ * At the same time these functions are often useful over multiple files in the
+ * core and API, so we list, or at least document, all variables which are used
+ * by DataTables as private variables here. This also ensures that there is no
+ * clashing of variable names and that they can easily referenced for reuse.
+ */
+
+
+ // Defined else where
+ // _selector_run
+ // _selector_opts
+ // _selector_first
+ // _selector_row_indexes
+
+ var _ext; // DataTable.ext
+ var _Api; // DataTable.Api
+ var _api_register; // DataTable.Api.register
+ var _api_registerPlural; // DataTable.Api.registerPlural
+
+ var _re_dic = {};
+ var _re_new_lines = /[\r\n]/g;
+ var _re_html = /<.*?>/g;
+ var _re_date_start = /^[\w\+\-]/;
+ var _re_date_end = /[\w\+\-]$/;
+
+ // Escape regular expression special characters
+ var _re_escape_regex = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-'].join('|\\') + ')', 'g');
+
+ // http://en.wikipedia.org/wiki/Foreign_exchange_market
+ // - \u20BD - Russian ruble.
+ // - \u20a9 - South Korean Won
+ // - \u20BA - Turkish Lira
+ // - \u20B9 - Indian Rupee
+ // - R - Brazil (R$) and South Africa
+ // - fr - Swiss Franc
+ // - kr - Swedish krona, Norwegian krone and Danish krone
+ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
+ // standards as thousands separators.
+ var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
+
+
+ var _empty = function (d) {
+ return !d || d === true || d === '-' ? true : false;
+ };
+
+
+ var _intVal = function (s) {
+ var integer = parseInt(s, 10);
+ return !isNaN(integer) && isFinite(s) ? integer : null;
+ };
+
+ // Convert from a formatted number with characters other than `.` as the
+ // decimal place, to a Javascript number
+ var _numToDecimal = function (num, decimalPoint) {
+ // Cache created regular expressions for speed as this function is called often
+ if (!_re_dic[decimalPoint]) {
+ _re_dic[decimalPoint] = new RegExp(_fnEscapeRegex(decimalPoint), 'g');
+ }
+ return typeof num === 'string' && decimalPoint !== '.' ?
+ num.replace(/\./g, '').replace(_re_dic[decimalPoint], '.') :
+ num;
+ };
+
+
+ var _isNumber = function (d, decimalPoint, formatted) {
+ var strType = typeof d === 'string';
+
+ // If empty return immediately so there must be a number if it is a
+ // formatted string (this stops the string "k", or "kr", etc being detected
+ // as a formatted number for currency
+ if (_empty(d)) {
+ return true;
+ }
+
+ if (decimalPoint && strType) {
+ d = _numToDecimal(d, decimalPoint);
+ }
+
+ if (formatted && strType) {
+ d = d.replace(_re_formatted_numeric, '');
+ }
+
+ return !isNaN(parseFloat(d)) && isFinite(d);
+ };
+
+
+ // A string without HTML in it can be considered to be HTML still
+ var _isHtml = function (d) {
+ return _empty(d) || typeof d === 'string';
+ };
+
+
+ var _htmlNumeric = function (d, decimalPoint, formatted) {
+ if (_empty(d)) {
+ return true;
+ }
+
+ var html = _isHtml(d);
+ return !html ?
+ null :
+ _isNumber(_stripHtml(d), decimalPoint, formatted) ?
+ true :
+ null;
+ };
+
+
+ var _pluck = function (a, prop, prop2) {
+ var out = [];
+ var i = 0,
+ ien = a.length;
+
+ // Could have the test in the loop for slightly smaller code, but speed
+ // is essential here
+ if (prop2 !== undefined) {
+ for (; i < ien; i++) {
+ if (a[i] && a[i][prop]) {
+ out.push(a[i][prop][prop2]);
+ }
+ }
+ } else {
+ for (; i < ien; i++) {
+ if (a[i]) {
+ out.push(a[i][prop]);
+ }
+ }
+ }
+
+ return out;
+ };
+
+
+ // Basically the same as _pluck, but rather than looping over `a` we use `order`
+ // as the indexes to pick from `a`
+ var _pluck_order = function (a, order, prop, prop2) {
+ var out = [];
+ var i = 0,
+ ien = order.length;
+
+ // Could have the test in the loop for slightly smaller code, but speed
+ // is essential here
+ if (prop2 !== undefined) {
+ for (; i < ien; i++) {
+ if (a[order[i]][prop]) {
+ out.push(a[order[i]][prop][prop2]);
+ }
+ }
+ } else {
+ for (; i < ien; i++) {
+ out.push(a[order[i]][prop]);
+ }
+ }
+
+ return out;
+ };
+
+
+ var _range = function (len, start) {
+ var out = [];
+ var end;
+
+ if (start === undefined) {
+ start = 0;
+ end = len;
+ } else {
+ end = start;
+ start = len;
+ }
+
+ for (var i = start; i < end; i++) {
+ out.push(i);
+ }
+
+ return out;
+ };
+
+
+ var _removeEmpty = function (a) {
+ var out = [];
+
+ for (var i = 0, ien = a.length; i < ien; i++) {
+ if (a[i]) { // careful - will remove all falsy values!
+ out.push(a[i]);
+ }
+ }
+
+ return out;
+ };
+
+
+ var _stripHtml = function (d) {
+ return d.replace(_re_html, '');
+ };
+
+
+ /**
+ * Find the unique elements in a source array.
+ *
+ * @param {array} src Source array
+ * @return {array} Array of unique items
+ * @ignore
+ */
+ var _unique = function (src) {
+ // A faster unique method is to use object keys to identify used values,
+ // but this doesn't work with arrays or objects, which we must also
+ // consider. See jsperf.com/compare-array-unique-versions/4 for more
+ // information.
+ var
+ out = [],
+ val,
+ i, ien = src.length,
+ j, k = 0;
+
+ again: for (i = 0; i < ien; i++) {
+ val = src[i];
+
+ for (j = 0; j < k; j++) {
+ if (out[j] === val) {
+ continue again;
+ }
+ }
+
+ out.push(val);
+ k++;
+ }
+
+ return out;
+ };
+
+
+ /**
+ * DataTables utility methods
+ *
+ * This namespace provides helper methods that DataTables uses internally to
+ * create a DataTable, but which are not exclusively used only for DataTables.
+ * These methods can be used by extension authors to save the duplication of
+ * code.
+ *
+ * @namespace
+ */
+ DataTable.util = {
+ /**
+ * Throttle the calls to a function. Arguments and context are maintained
+ * for the throttled function.
+ *
+ * @param {function} fn Function to be called
+ * @param {integer} freq Call frequency in mS
+ * @return {function} Wrapped function
+ */
+ throttle: function (fn, freq) {
+ var
+ frequency = freq !== undefined ? freq : 200,
+ last,
+ timer;
+
+ return function () {
+ var
+ that = this,
+ now = +new Date(),
+ args = arguments;
+
+ if (last && now < last + frequency) {
+ clearTimeout(timer);
+
+ timer = setTimeout(function () {
+ last = undefined;
+ fn.apply(that, args);
+ }, frequency);
+ } else {
+ last = now;
+ fn.apply(that, args);
+ }
+ };
+ },
+
+
+ /**
+ * Escape a string such that it can be used in a regular expression
+ *
+ * @param {string} val string to escape
+ * @returns {string} escaped string
+ */
+ escapeRegex: function (val) {
+ return val.replace(_re_escape_regex, '\\$1');
+ }
+ };
+
+
+ /**
+ * Create a mapping object that allows camel case parameters to be looked up
+ * for their Hungarian counterparts. The mapping is stored in a private
+ * parameter called `_hungarianMap` which can be accessed on the source object.
+ * @param {object} o
+ * @memberof DataTable#oApi
+ */
+ function _fnHungarianMap(o) {
+ var
+ hungarian = 'a aa ai ao as b fn i m o s ',
+ match,
+ newKey,
+ map = {};
+
+ $.each(o, function (key, val) {
+ match = key.match(/^([^A-Z]+?)([A-Z])/);
+
+ if (match && hungarian.indexOf(match[1] + ' ') !== -1) {
+ newKey = key.replace(match[0], match[2].toLowerCase());
+ map[newKey] = key;
+
+ if (match[1] === 'o') {
+ _fnHungarianMap(o[key]);
+ }
+ }
+ });
+
+ o._hungarianMap = map;
+ }
+
+
+ /**
+ * Convert from camel case parameters to Hungarian, based on a Hungarian map
+ * created by _fnHungarianMap.
+ * @param {object} src The model object which holds all parameters that can be
+ * mapped.
+ * @param {object} user The object to convert from camel case to Hungarian.
+ * @param {boolean} force When set to `true`, properties which already have a
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
+ * won't be.
+ * @memberof DataTable#oApi
+ */
+ function _fnCamelToHungarian(src, user, force) {
+ if (!src._hungarianMap) {
+ _fnHungarianMap(src);
+ }
+
+ var hungarianKey;
+
+ $.each(user, function (key, val) {
+ hungarianKey = src._hungarianMap[key];
+
+ if (hungarianKey !== undefined && (force || user[hungarianKey] === undefined)) {
+ // For objects, we need to buzz down into the object to copy parameters
+ if (hungarianKey.charAt(0) === 'o') {
+ // Copy the camelCase options over to the hungarian
+ if (!user[hungarianKey]) {
+ user[hungarianKey] = {};
+ }
+ $.extend(true, user[hungarianKey], user[key]);
+
+ _fnCamelToHungarian(src[hungarianKey], user[hungarianKey], force);
+ } else {
+ user[hungarianKey] = user[key];
+ }
+ }
+ });
+ }
+
+
+ /**
+ * Language compatibility - when certain options are given, and others aren't, we
+ * need to duplicate the values over, in order to provide backwards compatibility
+ * with older language files.
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnLanguageCompat(lang) {
+ var defaults = DataTable.defaults.oLanguage;
+ var zeroRecords = lang.sZeroRecords;
+
+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
+ * sZeroRecords - assuming that is given.
+ */
+ if (!lang.sEmptyTable && zeroRecords &&
+ defaults.sEmptyTable === "No data available in table") {
+ _fnMap(lang, lang, 'sZeroRecords', 'sEmptyTable');
+ }
+
+ /* Likewise with loading records */
+ if (!lang.sLoadingRecords && zeroRecords &&
+ defaults.sLoadingRecords === "Loading...") {
+ _fnMap(lang, lang, 'sZeroRecords', 'sLoadingRecords');
+ }
+
+ // Old parameter name of the thousands separator mapped onto the new
+ if (lang.sInfoThousands) {
+ lang.sThousands = lang.sInfoThousands;
+ }
+
+ var decimal = lang.sDecimal;
+ if (decimal) {
+ _addNumericSort(decimal);
+ }
+ }
+
+
+ /**
+ * Map one parameter onto another
+ * @param {object} o Object to map
+ * @param {*} knew The new parameter name
+ * @param {*} old The old parameter name
+ */
+ var _fnCompatMap = function (o, knew, old) {
+ if (o[knew] !== undefined) {
+ o[old] = o[knew];
+ }
+ };
+
+
+ /**
+ * Provide backwards compatibility for the main DT options. Note that the new
+ * options are mapped onto the old parameters, so this is an external interface
+ * change only.
+ * @param {object} init Object to map
+ */
+ function _fnCompatOpts(init) {
+ _fnCompatMap(init, 'ordering', 'bSort');
+ _fnCompatMap(init, 'orderMulti', 'bSortMulti');
+ _fnCompatMap(init, 'orderClasses', 'bSortClasses');
+ _fnCompatMap(init, 'orderCellsTop', 'bSortCellsTop');
+ _fnCompatMap(init, 'order', 'aaSorting');
+ _fnCompatMap(init, 'orderFixed', 'aaSortingFixed');
+ _fnCompatMap(init, 'paging', 'bPaginate');
+ _fnCompatMap(init, 'pagingType', 'sPaginationType');
+ _fnCompatMap(init, 'pageLength', 'iDisplayLength');
+ _fnCompatMap(init, 'searching', 'bFilter');
+
+ // Boolean initialisation of x-scrolling
+ if (typeof init.sScrollX === 'boolean') {
+ init.sScrollX = init.sScrollX ? '100%' : '';
+ }
+ if (typeof init.scrollX === 'boolean') {
+ init.scrollX = init.scrollX ? '100%' : '';
+ }
+
+ // Column search objects are in an array, so it needs to be converted
+ // element by element
+ var searchCols = init.aoSearchCols;
+
+ if (searchCols) {
+ for (var i = 0, ien = searchCols.length; i < ien; i++) {
+ if (searchCols[i]) {
+ _fnCamelToHungarian(DataTable.models.oSearch, searchCols[i]);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Provide backwards compatibility for column options. Note that the new options
+ * are mapped onto the old parameters, so this is an external interface change
+ * only.
+ * @param {object} init Object to map
+ */
+ function _fnCompatCols(init) {
+ _fnCompatMap(init, 'orderable', 'bSortable');
+ _fnCompatMap(init, 'orderData', 'aDataSort');
+ _fnCompatMap(init, 'orderSequence', 'asSorting');
+ _fnCompatMap(init, 'orderDataType', 'sortDataType');
+
+ // orderData can be given as an integer
+ var dataSort = init.aDataSort;
+ if (dataSort && !$.isArray(dataSort)) {
+ init.aDataSort = [dataSort];
+ }
+ }
+
+
+ /**
+ * Browser feature detection for capabilities, quirks
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnBrowserDetect(settings) {
+ // We don't need to do this every time DataTables is constructed, the values
+ // calculated are specific to the browser and OS configuration which we
+ // don't expect to change between initialisations
+ if (!DataTable.__browser) {
+ var browser = {};
+ DataTable.__browser = browser;
+
+ // Scrolling feature / quirks detection
+ var n = $('<div/>')
+ .css({
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ height: 1,
+ width: 1,
+ overflow: 'hidden'
+ })
+ .append(
+ $('<div/>')
+ .css({
+ position: 'absolute',
+ top: 1,
+ left: 1,
+ width: 100,
+ overflow: 'scroll'
+ })
+ .append(
+ $('<div/>')
+ .css({
+ width: '100%',
+ height: 10
+ })
+ )
+ )
+ .appendTo('body');
+
+ var outer = n.children();
+ var inner = outer.children();
+
+ // Numbers below, in order, are:
+ // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
+ //
+ // IE6 XP: 100 100 100 83
+ // IE7 Vista: 100 100 100 83
+ // IE 8+ Windows: 83 83 100 83
+ // Evergreen Windows: 83 83 100 83
+ // Evergreen Mac with scrollbars: 85 85 100 85
+ // Evergreen Mac without scrollbars: 100 100 100 100
+
+ // Get scrollbar width
+ browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
+
+ // IE6/7 will oversize a width 100% element inside a scrolling element, to
+ // include the width of the scrollbar, while other browsers ensure the inner
+ // element is contained without forcing scrolling
+ browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
+
+ // In rtl text layout, some browsers (most, but not all) will place the
+ // scrollbar on the left, rather than the right.
+ browser.bScrollbarLeft = Math.round(inner.offset().left) !== 1;
+
+ // IE8- don't provide height and width for getBoundingClientRect
+ browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
+
+ n.remove();
+ }
+
+ $.extend(settings.oBrowser, DataTable.__browser);
+ settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
+ }
+
+
+ /**
+ * Array.prototype reduce[Right] method, used for browsers which don't support
+ * JS 1.6. Done this way to reduce code size, since we iterate either way
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnReduce(that, fn, init, start, end, inc) {
+ var
+ i = start,
+ value,
+ isSet = false;
+
+ if (init !== undefined) {
+ value = init;
+ isSet = true;
+ }
+
+ while (i !== end) {
+ if (!that.hasOwnProperty(i)) {
+ continue;
+ }
+
+ value = isSet ?
+ fn(value, that[i], i, that) :
+ that[i];
+
+ isSet = true;
+ i += inc;
+ }
+
+ return value;
+ }
+
+ /**
+ * Add a column to the list used for the table with default values
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nTh The th element for this column
+ * @memberof DataTable#oApi
+ */
+ function _fnAddColumn(oSettings, nTh) {
+ // Add column to aoColumns array
+ var oDefaults = DataTable.defaults.column;
+ var iCol = oSettings.aoColumns.length;
+ var oCol = $.extend({}, DataTable.models.oColumn, oDefaults, {
+ "nTh": nTh ? nTh : document.createElement('th'),
+ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
+ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
+ "mData": oDefaults.mData ? oDefaults.mData : iCol,
+ idx: iCol
+ });
+ oSettings.aoColumns.push(oCol);
+
+ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
+ // passed into extend can be undefined. This allows the user to give a default
+ // with only some of the parameters defined, and also not give a default
+ var searchCols = oSettings.aoPreSearchCols;
+ searchCols[iCol] = $.extend({}, DataTable.models.oSearch, searchCols[iCol]);
+
+ // Use the default column options function to initialise classes etc
+ _fnColumnOptions(oSettings, iCol, $(nTh).data());
+ }
+
+
+ /**
+ * Apply options for a column
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iCol column index to consider
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnOptions(oSettings, iCol, oOptions) {
+ var oCol = oSettings.aoColumns[iCol];
+ var oClasses = oSettings.oClasses;
+ var th = $(oCol.nTh);
+
+ // Try to get width information from the DOM. We can't get it from CSS
+ // as we'd need to parse the CSS stylesheet. `width` option can override
+ if (!oCol.sWidthOrig) {
+ // Width attribute
+ oCol.sWidthOrig = th.attr('width') || null;
+
+ // Style attribute
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
+ if (t) {
+ oCol.sWidthOrig = t[1];
+ }
+ }
+
+ /* User specified column options */
+ if (oOptions !== undefined && oOptions !== null) {
+ // Backwards compatibility
+ _fnCompatCols(oOptions);
+
+ // Map camel case parameters to their Hungarian counterparts
+ _fnCamelToHungarian(DataTable.defaults.column, oOptions);
+
+ /* Backwards compatibility for mDataProp */
+ if (oOptions.mDataProp !== undefined && !oOptions.mData) {
+ oOptions.mData = oOptions.mDataProp;
+ }
+
+ if (oOptions.sType) {
+ oCol._sManualType = oOptions.sType;
+ }
+
+ // `class` is a reserved word in Javascript, so we need to provide
+ // the ability to use a valid name for the camel case input
+ if (oOptions.className && !oOptions.sClass) {
+ oOptions.sClass = oOptions.className;
+ }
+
+ $.extend(oCol, oOptions);
+ _fnMap(oCol, oOptions, "sWidth", "sWidthOrig");
+
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
+ * priority if defined
+ */
+ if (oOptions.iDataSort !== undefined) {
+ oCol.aDataSort = [oOptions.iDataSort];
+ }
+ _fnMap(oCol, oOptions, "aDataSort");
+ }
+
+ /* Cache the data get and set functions for speed */
+ var mDataSrc = oCol.mData;
+ var mData = _fnGetObjectDataFn(mDataSrc);
+ var mRender = oCol.mRender ? _fnGetObjectDataFn(oCol.mRender) : null;
+
+ var attrTest = function (src) {
+ return typeof src === 'string' && src.indexOf('@') !== -1;
+ };
+ oCol._bAttrSrc = $.isPlainObject(mDataSrc) && (
+ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
+ );
+ oCol._setter = null;
+
+ oCol.fnGetData = function (rowData, type, meta) {
+ var innerData = mData(rowData, type, undefined, meta);
+
+ return mRender && type ?
+ mRender(innerData, type, rowData, meta) :
+ innerData;
+ };
+ oCol.fnSetData = function (rowData, val, meta) {
+ return _fnSetObjectDataFn(mDataSrc)(rowData, val, meta);
+ };
+
+ // Indicate if DataTables should read DOM data as an object or array
+ // Used in _fnGetRowElements
+ if (typeof mDataSrc !== 'number') {
+ oSettings._rowReadObject = true;
+ }
+
+ /* Feature sorting overrides column specific when off */
+ if (!oSettings.oFeatures.bSort) {
+ oCol.bSortable = false;
+ th.addClass(oClasses.sSortableNone); // Have to add class here as order event isn't called
+ }
+
+ /* Check that the class assignment is correct for sorting */
+ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
+ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
+ if (!oCol.bSortable || (!bAsc && !bDesc)) {
+ oCol.sSortingClass = oClasses.sSortableNone;
+ oCol.sSortingClassJUI = "";
+ } else if (bAsc && !bDesc) {
+ oCol.sSortingClass = oClasses.sSortableAsc;
+ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
+ } else if (!bAsc && bDesc) {
+ oCol.sSortingClass = oClasses.sSortableDesc;
+ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
+ } else {
+ oCol.sSortingClass = oClasses.sSortable;
+ oCol.sSortingClassJUI = oClasses.sSortJUI;
+ }
+ }
+
+
+ /**
+ * Adjust the table column widths for new data. Note: you would probably want to
+ * do a redraw after calling this function!
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAdjustColumnSizing(settings) {
+ /* Not interested in doing column width calculation if auto-width is disabled */
+ if (settings.oFeatures.bAutoWidth !== false) {
+ var columns = settings.aoColumns;
+
+ _fnCalculateColumnWidths(settings);
+ for (var i = 0, iLen = columns.length; i < iLen; i++) {
+ columns[i].nTh.style.width = columns[i].sWidth;
+ }
+ }
+
+ var scroll = settings.oScroll;
+ if (scroll.sY !== '' || scroll.sX !== '') {
+ _fnScrollDraw(settings);
+ }
+
+ _fnCallbackFire(settings, null, 'column-sizing', [settings]);
+ }
+
+
+ /**
+ * Covert the index of a visible column to the index in the data array (take account
+ * of hidden columns)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iMatch Visible column index to lookup
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnVisibleToColumnIndex(oSettings, iMatch) {
+ var aiVis = _fnGetColumns(oSettings, 'bVisible');
+
+ return typeof aiVis[iMatch] === 'number' ?
+ aiVis[iMatch] :
+ null;
+ }
+
+
+ /**
+ * Covert the index of an index in the data array and convert it to the visible
+ * column index (take account of hidden columns)
+ * @param {int} iMatch Column index to lookup
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnIndexToVisible(oSettings, iMatch) {
+ var aiVis = _fnGetColumns(oSettings, 'bVisible');
+ var iPos = $.inArray(iMatch, aiVis);
+
+ return iPos !== -1 ? iPos : null;
+ }
+
+
+ /**
+ * Get the number of visible columns
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the number of visible columns
+ * @memberof DataTable#oApi
+ */
+ function _fnVisbleColumns(oSettings) {
+ var vis = 0;
+
+ // No reduce in IE8, use a loop for now
+ $.each(oSettings.aoColumns, function (i, col) {
+ if (col.bVisible && $(col.nTh).css('display') !== 'none') {
+ vis++;
+ }
+ });
+
+ return vis;
+ }
+
+
+ /**
+ * Get an array of column indexes that match a given property
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sParam Parameter in aoColumns to look for - typically
+ * bVisible or bSearchable
+ * @returns {array} Array of indexes with matched properties
+ * @memberof DataTable#oApi
+ */
+ function _fnGetColumns(oSettings, sParam) {
+ var a = [];
+
+ $.map(oSettings.aoColumns, function (val, i) {
+ if (val[sParam]) {
+ a.push(i);
+ }
+ });
+
+ return a;
+ }
+
+
+ /**
+ * Calculate the 'type' of a column
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnTypes(settings) {
+ var columns = settings.aoColumns;
+ var data = settings.aoData;
+ var types = DataTable.ext.type.detect;
+ var i, ien, j, jen, k, ken;
+ var col, cell, detectedType, cache;
+
+ // For each column, spin over the
+ for (i = 0, ien = columns.length; i < ien; i++) {
+ col = columns[i];
+ cache = [];
+
+ if (!col.sType && col._sManualType) {
+ col.sType = col._sManualType;
+ } else if (!col.sType) {
+ for (j = 0, jen = types.length; j < jen; j++) {
+ for (k = 0, ken = data.length; k < ken; k++) {
+ // Use a cache array so we only need to get the type data
+ // from the formatter once (when using multiple detectors)
+ if (cache[k] === undefined) {
+ cache[k] = _fnGetCellData(settings, k, i, 'type');
+ }
+
+ detectedType = types[j](cache[k], settings);
+
+ // If null, then this type can't apply to this column, so
+ // rather than testing all cells, break out. There is an
+ // exception for the last type which is `html`. We need to
+ // scan all rows since it is possible to mix string and HTML
+ // types
+ if (!detectedType && j !== types.length - 1) {
+ break;
+ }
+
+ // Only a single match is needed for html type since it is
+ // bottom of the pile and very similar to string
+ if (detectedType === 'html') {
+ break;
+ }
+ }
+
+ // Type is valid for all data points in the column - use this
+ // type
+ if (detectedType) {
+ col.sType = detectedType;
+ break;
+ }
+ }
+
+ // Fall back - if no type was detected, always use string
+ if (!col.sType) {
+ col.sType = 'string';
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Take the column definitions and static columns arrays and calculate how
+ * they relate to column indexes. The callback function will then apply the
+ * definition found for a column to a suitable configuration object.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
+ * @param {array} aoCols The aoColumns array that defines columns individually
+ * @param {function} fn Callback function - takes two parameters, the calculated
+ * column index and the definition for that column.
+ * @memberof DataTable#oApi
+ */
+ function _fnApplyColumnDefs(oSettings, aoColDefs, aoCols, fn) {
+ var i, iLen, j, jLen, k, kLen, def;
+ var columns = oSettings.aoColumns;
+
+ // Column definitions with aTargets
+ if (aoColDefs) {
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
+ for (i = aoColDefs.length - 1; i >= 0; i--) {
+ def = aoColDefs[i];
+
+ /* Each definition can target multiple columns, as it is an array */
+ var aTargets = def.targets !== undefined ?
+ def.targets :
+ def.aTargets;
+
+ if (!$.isArray(aTargets)) {
+ aTargets = [aTargets];
+ }
+
+ for (j = 0, jLen = aTargets.length; j < jLen; j++) {
+ if (typeof aTargets[j] === 'number' && aTargets[j] >= 0) {
+ /* Add columns that we don't yet know about */
+ while (columns.length <= aTargets[j]) {
+ _fnAddColumn(oSettings);
+ }
+
+ /* Integer, basic index */
+ fn(aTargets[j], def);
+ } else if (typeof aTargets[j] === 'number' && aTargets[j] < 0) {
+ /* Negative integer, right to left column counting */
+ fn(columns.length + aTargets[j], def);
+ } else if (typeof aTargets[j] === 'string') {
+ /* Class name matching on TH element */
+ for (k = 0, kLen = columns.length; k < kLen; k++) {
+ if (aTargets[j] == "_all" ||
+ $(columns[k].nTh).hasClass(aTargets[j])) {
+ fn(k, def);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Statically defined columns array
+ if (aoCols) {
+ for (i = 0, iLen = aoCols.length; i < iLen; i++) {
+ fn(i, aoCols[i]);
+ }
+ }
+ }
+
+ /**
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
+ * DOM source.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aData data array to be added
+ * @param {node} [nTr] TR element to add to the table - optional. If not given,
+ * DataTables will create a row automatically
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
+ * if nTr is.
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
+ * @memberof DataTable#oApi
+ */
+ function _fnAddData(oSettings, aDataIn, nTr, anTds) {
+ /* Create the object for storing information about this new row */
+ var iRow = oSettings.aoData.length;
+ var oData = $.extend(true, {}, DataTable.models.oRow, {
+ src: nTr ? 'dom' : 'data',
+ idx: iRow
+ });
+
+ oData._aData = aDataIn;
+ oSettings.aoData.push(oData);
+
+ /* Create the cells */
+ var nTd, sThisType;
+ var columns = oSettings.aoColumns;
+
+ // Invalidate the column types as the new data needs to be revalidated
+ for (var i = 0, iLen = columns.length; i < iLen; i++) {
+ columns[i].sType = null;
+ }
+
+ /* Add to the display array */
+ oSettings.aiDisplayMaster.push(iRow);
+
+ var id = oSettings.rowIdFn(aDataIn);
+ if (id !== undefined) {
+ oSettings.aIds[id] = oData;
+ }
+
+ /* Create the DOM information, or register it if already present */
+ if (nTr || !oSettings.oFeatures.bDeferRender) {
+ _fnCreateTr(oSettings, iRow, nTr, anTds);
+ }
+
+ return iRow;
+ }
+
+
+ /**
+ * Add one or more TR elements to the table. Generally we'd expect to
+ * use this for reading data from a DOM sourced table, but it could be
+ * used for an TR element. Note that if a TR is given, it is used (i.e.
+ * it is not cloned).
+ * @param {object} settings dataTables settings object
+ * @param {array|node|jQuery} trs The TR element(s) to add to the table
+ * @returns {array} Array of indexes for the added rows
+ * @memberof DataTable#oApi
+ */
+ function _fnAddTr(settings, trs) {
+ var row;
+
+ // Allow an individual node to be passed in
+ if (!(trs instanceof $)) {
+ trs = $(trs);
+ }
+
+ return trs.map(function (i, el) {
+ row = _fnGetRowElements(settings, el);
+ return _fnAddData(settings, row.data, el, row.cells);
+ });
+ }
+
+
+ /**
+ * Take a TR element and convert it to an index in aoData
+ * @param {object} oSettings dataTables settings object
+ * @param {node} n the TR element to find
+ * @returns {int} index if the node is found, null if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToDataIndex(oSettings, n) {
+ return (n._DT_RowIndex !== undefined) ? n._DT_RowIndex : null;
+ }
+
+
+ /**
+ * Take a TD element and convert it into a column data index (not the visible index)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow The row number the TD/TH can be found in
+ * @param {node} n The TD/TH element to find
+ * @returns {int} index if the node is found, -1 if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToColumnIndex(oSettings, iRow, n) {
+ return $.inArray(n, oSettings.aoData[iRow].anCells);
+ }
+
+
+ /**
+ * Get the data for a given cell from the internal cache, taking into account data mapping
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
+ * @returns {*} Cell data
+ * @memberof DataTable#oApi
+ */
+ function _fnGetCellData(settings, rowIdx, colIdx, type) {
+ var draw = settings.iDraw;
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
+ var defaultContent = col.sDefaultContent;
+ var cellData = col.fnGetData(rowData, type, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ });
+
+ if (cellData === undefined) {
+ if (settings.iDrawError != draw && defaultContent === null) {
+ _fnLog(settings, 0, "Requested unknown parameter " +
+ (typeof col.mData == 'function' ? '{function}' : "'" + col.mData + "'") +
+ " for row " + rowIdx + ", column " + colIdx, 4);
+ settings.iDrawError = draw;
+ }
+ return defaultContent;
+ }
+
+ // When the data source is null and a specific data type is requested (i.e.
+ // not the original data), we can use default column data
+ if ((cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined) {
+ cellData = defaultContent;
+ } else if (typeof cellData === 'function') {
+ // If the data source is a function, then we run it and use the return,
+ // executing in the scope of the data object (for instances)
+ return cellData.call(rowData);
+ }
+
+ if (cellData === null && type == 'display') {
+ return '';
+ }
+ return cellData;
+ }
+
+
+ /**
+ * Set the value for a specific cell, into the internal data cache
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
+ * @param {*} val Value to set
+ * @memberof DataTable#oApi
+ */
+ function _fnSetCellData(settings, rowIdx, colIdx, val) {
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
+
+ col.fnSetData(rowData, val, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ });
+ }
+
+
+ // Private variable that is used to match action syntax in the data property object
+ var __reArray = /\[.*?\]$/;
+ var __reFn = /\(\)$/;
+
+ /**
+ * Split string on periods, taking into account escaped periods
+ * @param {string} str String to split
+ * @return {array} Split string
+ */
+ function _fnSplitObjNotation(str) {
+ return $.map(str.match(/(\\.|[^\.])+/g) || [''], function (s) {
+ return s.replace(/\\./g, '.');
+ });
+ }
+
+
+ /**
+ * Return a function that can be used to get data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data get function
+ * @memberof DataTable#oApi
+ */
+ function _fnGetObjectDataFn(mSource) {
+ if ($.isPlainObject(mSource)) {
+ /* Build an object of get functions, and wrap them in a single call */
+ var o = {};
+ $.each(mSource, function (key, val) {
+ if (val) {
+ o[key] = _fnGetObjectDataFn(val);
+ }
+ });
+
+ return function (data, type, row, meta) {
+ var t = o[type] || o._;
+ return t !== undefined ?
+ t(data, type, row, meta) :
+ data;
+ };
+ } else if (mSource === null) {
+ /* Give an empty string for rendering / sorting etc */
+ return function (data) { // type, row and meta also passed, but not used
+ return data;
+ };
+ } else if (typeof mSource === 'function') {
+ return function (data, type, row, meta) {
+ return mSource(data, type, row, meta);
+ };
+ } else if (typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1)) {
+ /* If there is a . in the source string then the data source is in a
+ * nested object so we loop over the data for each level to get the next
+ * level down. On each loop we test for undefined, and if found immediately
+ * return. This allows entire objects to be missing and sDefaultContent to
+ * be used if defined, rather than throwing an error
+ */
+ var fetchData = function (data, type, src) {
+ var arrayNotation, funcNotation, out, innerSrc;
+
+ if (src !== "") {
+ var a = _fnSplitObjNotation(src);
+
+ for (var i = 0, iLen = a.length; i < iLen; i++) {
+ // Check if we are dealing with special notation
+ arrayNotation = a[i].match(__reArray);
+ funcNotation = a[i].match(__reFn);
+
+ if (arrayNotation) {
+ // Array notation
+ a[i] = a[i].replace(__reArray, '');
+
+ // Condition allows simply [] to be passed in
+ if (a[i] !== "") {
+ data = data[a[i]];
+ }
+ out = [];
+
+ // Get the remainder of the nested object to get
+ a.splice(0, i + 1);
+ innerSrc = a.join('.');
+
+ // Traverse each entry in the array getting the properties requested
+ if ($.isArray(data)) {
+ for (var j = 0, jLen = data.length; j < jLen; j++) {
+ out.push(fetchData(data[j], type, innerSrc));
+ }
+ }
+
+ // If a string is given in between the array notation indicators, that
+ // is used to join the strings together, otherwise an array is returned
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length - 1);
+ data = (join === "") ? out : out.join(join);
+
+ // The inner call to fetchData has already traversed through the remainder
+ // of the source requested, so we exit from the loop
+ break;
+ } else if (funcNotation) {
+ // Function call
+ a[i] = a[i].replace(__reFn, '');
+ data = data[a[i]]();
+ continue;
+ }
+
+ if (data === null || data[a[i]] === undefined) {
+ return undefined;
+ }
+ data = data[a[i]];
+ }
+ }
+
+ return data;
+ };
+
+ return function (data, type) { // row and meta also passed, but not used
+ return fetchData(data, type, mSource);
+ };
+ } else {
+ /* Array or flat object mapping */
+ return function (data, type) { // row and meta also passed, but not used
+ return data[mSource];
+ };
+ }
+ }
+
+
+ /**
+ * Return a function that can be used to set data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data set function
+ * @memberof DataTable#oApi
+ */
+ function _fnSetObjectDataFn(mSource) {
+ if ($.isPlainObject(mSource)) {
+ /* Unlike get, only the underscore (global) option is used for for
+ * setting data since we don't know the type here. This is why an object
+ * option is not documented for `mData` (which is read/write), but it is
+ * for `mRender` which is read only.
+ */
+ return _fnSetObjectDataFn(mSource._);
+ } else if (mSource === null) {
+ /* Nothing to do when the data source is null */
+ return function () {};
+ } else if (typeof mSource === 'function') {
+ return function (data, val, meta) {
+ mSource(data, 'set', val, meta);
+ };
+ } else if (typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1)) {
+ /* Like the get, we need to get data from a nested object */
+ var setData = function (data, val, src) {
+ var a = _fnSplitObjNotation(src),
+ b;
+ var aLast = a[a.length - 1];
+ var arrayNotation, funcNotation, o, innerSrc;
+
+ for (var i = 0, iLen = a.length - 1; i < iLen; i++) {
+ // Check if we are dealing with an array notation request
+ arrayNotation = a[i].match(__reArray);
+ funcNotation = a[i].match(__reFn);
+
+ if (arrayNotation) {
+ a[i] = a[i].replace(__reArray, '');
+ data[a[i]] = [];
+
+ // Get the remainder of the nested object to set so we can recurse
+ b = a.slice();
+ b.splice(0, i + 1);
+ innerSrc = b.join('.');
+
+ // Traverse each entry in the array setting the properties requested
+ if ($.isArray(val)) {
+ for (var j = 0, jLen = val.length; j < jLen; j++) {
+ o = {};
+ setData(o, val[j], innerSrc);
+ data[a[i]].push(o);
+ }
+ } else {
+ // We've been asked to save data to an array, but it
+ // isn't array data to be saved. Best that can be done
+ // is to just save the value.
+ data[a[i]] = val;
+ }
+
+ // The inner call to setData has already traversed through the remainder
+ // of the source and has set the data, thus we can exit here
+ return;
+ } else if (funcNotation) {
+ // Function call
+ a[i] = a[i].replace(__reFn, '');
+ data = data[a[i]](val);
+ }
+
+ // If the nested object doesn't currently exist - since we are
+ // trying to set the value - create it
+ if (data[a[i]] === null || data[a[i]] === undefined) {
+ data[a[i]] = {};
+ }
+ data = data[a[i]];
+ }
+
+ // Last item in the input - i.e, the actual set
+ if (aLast.match(__reFn)) {
+ // Function call
+ data = data[aLast.replace(__reFn, '')](val);
+ } else {
+ // If array notation is used, we just want to strip it and use the property name
+ // and assign the value. If it isn't used, then we get the result we want anyway
+ data[aLast.replace(__reArray, '')] = val;
+ }
+ };
+
+ return function (data, val) { // meta is also passed in, but not used
+ return setData(data, val, mSource);
+ };
+ } else {
+ /* Array or flat object mapping */
+ return function (data, val) { // meta is also passed in, but not used
+ data[mSource] = val;
+ };
+ }
+ }
+
+
+ /**
+ * Return an array with the full table data
+ * @param {object} oSettings dataTables settings object
+ * @returns array {array} aData Master data array
+ * @memberof DataTable#oApi
+ */
+ function _fnGetDataMaster(settings) {
+ return _pluck(settings.aoData, '_aData');
+ }
+
+
+ /**
+ * Nuke the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnClearTable(settings) {
+ settings.aoData.length = 0;
+ settings.aiDisplayMaster.length = 0;
+ settings.aiDisplay.length = 0;
+ settings.aIds = {};
+ }
+
+
+ /**
+ * Take an array of integers (index array) and remove a target integer (value - not
+ * the key!)
+ * @param {array} a Index array to target
+ * @param {int} iTarget value to find
+ * @memberof DataTable#oApi
+ */
+ function _fnDeleteIndex(a, iTarget, splice) {
+ var iTargetIndex = -1;
+
+ for (var i = 0, iLen = a.length; i < iLen; i++) {
+ if (a[i] == iTarget) {
+ iTargetIndex = i;
+ } else if (a[i] > iTarget) {
+ a[i]--;
+ }
+ }
+
+ if (iTargetIndex != -1 && splice === undefined) {
+ a.splice(iTargetIndex, 1);
+ }
+ }
+
+
+ /**
+ * Mark cached data as invalid such that a re-read of the data will occur when
+ * the cached data is next requested. Also update from the data source object.
+ *
+ * @param {object} settings DataTables settings object
+ * @param {int} rowIdx Row index to invalidate
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
+ * or 'data'
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
+ * row will be invalidated
+ * @memberof DataTable#oApi
+ *
+ * @todo For the modularisation of v1.11 this will need to become a callback, so
+ * the sort and filter methods can subscribe to it. That will required
+ * initialisation options for sorting, which is why it is not already baked in
+ */
+ function _fnInvalidate(settings, rowIdx, src, colIdx) {
+ var row = settings.aoData[rowIdx];
+ var i, ien;
+ var cellWrite = function (cell, col) {
+ // This is very frustrating, but in IE if you just write directly
+ // to innerHTML, and elements that are overwritten are GC'ed,
+ // even if there is a reference to them elsewhere
+ while (cell.childNodes.length) {
+ cell.removeChild(cell.firstChild);
+ }
+
+ cell.innerHTML = _fnGetCellData(settings, rowIdx, col, 'display');
+ };
+
+ // Are we reading last data from DOM or the data object?
+ if (src === 'dom' || ((!src || src === 'auto') && row.src === 'dom')) {
+ // Read the data from the DOM
+ row._aData = _fnGetRowElements(
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
+ )
+ .data;
+ } else {
+ // Reading from data object, update the DOM
+ var cells = row.anCells;
+
+ if (cells) {
+ if (colIdx !== undefined) {
+ cellWrite(cells[colIdx], colIdx);
+ } else {
+ for (i = 0, ien = cells.length; i < ien; i++) {
+ cellWrite(cells[i], i);
+ }
+ }
+ }
+ }
+
+ // For both row and cell invalidation, the cached data for sorting and
+ // filtering is nulled out
+ row._aSortData = null;
+ row._aFilterData = null;
+
+ // Invalidate the type for a specific column (if given) or all columns since
+ // the data might have changed
+ var cols = settings.aoColumns;
+ if (colIdx !== undefined) {
+ cols[colIdx].sType = null;
+ } else {
+ for (i = 0, ien = cols.length; i < ien; i++) {
+ cols[i].sType = null;
+ }
+
+ // Update DataTables special `DT_*` attributes for the row
+ _fnRowAttributes(settings, row);
+ }
+ }
+
+
+ /**
+ * Build a data source object from an HTML row, reading the contents of the
+ * cells that are in the row.
+ *
+ * @param {object} settings DataTables settings object
+ * @param {node|object} TR element from which to read data or existing row
+ * object from which to re-read the data from the cells
+ * @param {int} [colIdx] Optional column index
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
+ * parameter should also be given and will be used to write the data into.
+ * Only the column in question will be written
+ * @returns {object} Object with two parameters: `data` the data read, in
+ * document order, and `cells` and array of nodes (they can be useful to the
+ * caller, so rather than needing a second traversal to get them, just return
+ * them from here).
+ * @memberof DataTable#oApi
+ */
+ function _fnGetRowElements(settings, row, colIdx, d) {
+ var
+ tds = [],
+ td = row.firstChild,
+ name, col, o, i = 0,
+ contents,
+ columns = settings.aoColumns,
+ objectRead = settings._rowReadObject;
+
+ // Allow the data object to be passed in, or construct
+ d = d !== undefined ?
+ d :
+ objectRead ? {} : [];
+
+ var attr = function (str, td) {
+ if (typeof str === 'string') {
+ var idx = str.indexOf('@');
+
+ if (idx !== -1) {
+ var attr = str.substring(idx + 1);
+ var setter = _fnSetObjectDataFn(str);
+ setter(d, td.getAttribute(attr));
+ }
+ }
+ };
+
+ // Read data from a cell and store into the data object
+ var cellProcess = function (cell) {
+ if (colIdx === undefined || colIdx === i) {
+ col = columns[i];
+ contents = $.trim(cell.innerHTML);
+
+ if (col && col._bAttrSrc) {
+ var setter = _fnSetObjectDataFn(col.mData._);
+ setter(d, contents);
+
+ attr(col.mData.sort, cell);
+ attr(col.mData.type, cell);
+ attr(col.mData.filter, cell);
+ } else {
+ // Depending on the `data` option for the columns the data can
+ // be read to either an object or an array.
+ if (objectRead) {
+ if (!col._setter) {
+ // Cache the setter function
+ col._setter = _fnSetObjectDataFn(col.mData);
+ }
+ col._setter(d, contents);
+ } else {
+ d[i] = contents;
+ }
+ }
+ }
+
+ i++;
+ };
+
+ if (td) {
+ // `tr` element was passed in
+ while (td) {
+ name = td.nodeName.toUpperCase();
+
+ if (name == "TD" || name == "TH") {
+ cellProcess(td);
+ tds.push(td);
+ }
+
+ td = td.nextSibling;
+ }
+ } else {
+ // Existing row object passed in
+ tds = row.anCells;
+
+ for (var j = 0, jen = tds.length; j < jen; j++) {
+ cellProcess(tds[j]);
+ }
+ }
+
+ // Read the ID from the DOM if present
+ var rowNode = row.firstChild ? row : row.nTr;
+
+ if (rowNode) {
+ var id = rowNode.getAttribute('id');
+
+ if (id) {
+ _fnSetObjectDataFn(settings.rowId)(d, id);
+ }
+ }
+
+ return {
+ data: d,
+ cells: tds
+ };
+ }
+ /**
+ * Create a new TR element (and it's TD children) for a row
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow Row to consider
+ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
+ * DataTables will create a row automatically
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
+ * if nTr is.
+ * @memberof DataTable#oApi
+ */
+ function _fnCreateTr(oSettings, iRow, nTrIn, anTds) {
+ var
+ row = oSettings.aoData[iRow],
+ rowData = row._aData,
+ cells = [],
+ nTr, nTd, oCol,
+ i, iLen;
+
+ if (row.nTr === null) {
+ nTr = nTrIn || document.createElement('tr');
+
+ row.nTr = nTr;
+ row.anCells = cells;
+
+ /* Use a private property on the node to allow reserve mapping from the node
+ * to the aoData array for fast look up
+ */
+ nTr._DT_RowIndex = iRow;
+
+ /* Special parameters can be given by the data source to be used on the row */
+ _fnRowAttributes(oSettings, row);
+
+ /* Process each column */
+ for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ oCol = oSettings.aoColumns[i];
+
+ nTd = nTrIn ? anTds[i] : document.createElement(oCol.sCellType);
+ nTd._DT_CellIndex = {
+ row: iRow,
+ column: i
+ };
+
+ cells.push(nTd);
+
+ // Need to create the HTML if new, or if a rendering function is defined
+ if ((!nTrIn || oCol.mRender || oCol.mData !== i) &&
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i + '.display')
+ ) {
+ nTd.innerHTML = _fnGetCellData(oSettings, iRow, i, 'display');
+ }
+
+ /* Add user defined class */
+ if (oCol.sClass) {
+ nTd.className += ' ' + oCol.sClass;
+ }
+
+ // Visibility - add or remove as required
+ if (oCol.bVisible && !nTrIn) {
+ nTr.appendChild(nTd);
+ } else if (!oCol.bVisible && nTrIn) {
+ nTd.parentNode.removeChild(nTd);
+ }
+
+ if (oCol.fnCreatedCell) {
+ oCol.fnCreatedCell.call(oSettings.oInstance,
+ nTd, _fnGetCellData(oSettings, iRow, i), rowData, iRow, i
+ );
+ }
+ }
+
+ _fnCallbackFire(oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow]);
+ }
+
+ // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
+ // and deployed
+ row.nTr.setAttribute('role', 'row');
+ }
+
+
+ /**
+ * Add attributes to a row based on the special `DT_*` parameters in a data
+ * source object.
+ * @param {object} settings DataTables settings object
+ * @param {object} DataTables row object for the row to be modified
+ * @memberof DataTable#oApi
+ */
+ function _fnRowAttributes(settings, row) {
+ var tr = row.nTr;
+ var data = row._aData;
+
+ if (tr) {
+ var id = settings.rowIdFn(data);
+
+ if (id) {
+ tr.id = id;
+ }
+
+ if (data.DT_RowClass) {
+ // Remove any classes added by DT_RowClass before
+ var a = data.DT_RowClass.split(' ');
+ row.__rowc = row.__rowc ?
+ _unique(row.__rowc.concat(a)) :
+ a;
+
+ $(tr)
+ .removeClass(row.__rowc.join(' '))
+ .addClass(data.DT_RowClass);
+ }
+
+ if (data.DT_RowAttr) {
+ $(tr).attr(data.DT_RowAttr);
+ }
+
+ if (data.DT_RowData) {
+ $(tr).data(data.DT_RowData);
+ }
+ }
+ }
+
+
+ /**
+ * Create the HTML header for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnBuildHead(oSettings) {
+ var i, ien, cell, row, column;
+ var thead = oSettings.nTHead;
+ var tfoot = oSettings.nTFoot;
+ var createHeader = $('th, td', thead).length === 0;
+ var classes = oSettings.oClasses;
+ var columns = oSettings.aoColumns;
+
+ if (createHeader) {
+ row = $('<tr/>').appendTo(thead);
+ }
+
+ for (i = 0, ien = columns.length; i < ien; i++) {
+ column = columns[i];
+ cell = $(column.nTh).addClass(column.sClass);
+
+ if (createHeader) {
+ cell.appendTo(row);
+ }
+
+ // 1.11 move into sorting
+ if (oSettings.oFeatures.bSort) {
+ cell.addClass(column.sSortingClass);
+
+ if (column.bSortable !== false) {
+ cell
+ .attr('tabindex', oSettings.iTabIndex)
+ .attr('aria-controls', oSettings.sTableId);
+
+ _fnSortAttachListener(oSettings, column.nTh, i);
+ }
+ }
+
+ if (column.sTitle != cell[0].innerHTML) {
+ cell.html(column.sTitle);
+ }
+
+ _fnRenderer(oSettings, 'header')(
+ oSettings, cell, column, classes
+ );
+ }
+
+ if (createHeader) {
+ _fnDetectHeader(oSettings.aoHeader, thead);
+ }
+
+ /* ARIA role for the rows */
+ $(thead).find('>tr').attr('role', 'row');
+
+ /* Deal with the footer - add classes if required */
+ $(thead).find('>tr>th, >tr>td').addClass(classes.sHeaderTH);
+ $(tfoot).find('>tr>th, >tr>td').addClass(classes.sFooterTH);
+
+ // Cache the footer cells. Note that we only take the cells from the first
+ // row in the footer. If there is more than one row the user wants to
+ // interact with, they need to use the table().foot() method. Note also this
+ // allows cells to be used for multiple columns using colspan
+ if (tfoot !== null) {
+ var cells = oSettings.aoFooter[0];
+
+ for (i = 0, ien = cells.length; i < ien; i++) {
+ column = columns[i];
+ column.nTf = cells[i].cell;
+
+ if (column.sClass) {
+ $(column.nTf).addClass(column.sClass);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Draw the header (or footer) element based on the column visibility states. The
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
+ * the instantaneous column visibility, to construct the new layout. The grid is
+ * traversed over cell at a time in a rows x columns grid fashion, although each
+ * cell insert can cover multiple elements in the grid - which is tracks using the
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
+ * already a cell in that position.
+ * @param {object} oSettings dataTables settings object
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
+ * @memberof DataTable#oApi
+ */
+ function _fnDrawHead(oSettings, aoSource, bIncludeHidden) {
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
+ var aoLocal = [];
+ var aApplied = [];
+ var iColumns = oSettings.aoColumns.length;
+ var iRowspan, iColspan;
+
+ if (!aoSource) {
+ return;
+ }
+
+ if (bIncludeHidden === undefined) {
+ bIncludeHidden = false;
+ }
+
+ /* Make a copy of the master layout array, but without the visible columns in it */
+ for (i = 0, iLen = aoSource.length; i < iLen; i++) {
+ aoLocal[i] = aoSource[i].slice();
+ aoLocal[i].nTr = aoSource[i].nTr;
+
+ /* Remove any columns which are currently hidden */
+ for (j = iColumns - 1; j >= 0; j--) {
+ if (!oSettings.aoColumns[j].bVisible && !bIncludeHidden) {
+ aoLocal[i].splice(j, 1);
+ }
+ }
+
+ /* Prep the applied array - it needs an element for each row */
+ aApplied.push([]);
+ }
+
+ for (i = 0, iLen = aoLocal.length; i < iLen; i++) {
+ nLocalTr = aoLocal[i].nTr;
+
+ /* All cells are going to be replaced, so empty out the row */
+ if (nLocalTr) {
+ while ((n = nLocalTr.firstChild)) {
+ nLocalTr.removeChild(n);
+ }
+ }
+
+ for (j = 0, jLen = aoLocal[i].length; j < jLen; j++) {
+ iRowspan = 1;
+ iColspan = 1;
+
+ /* Check to see if there is already a cell (row/colspan) covering our target
+ * insert point. If there is, then there is nothing to do.
+ */
+ if (aApplied[i][j] === undefined) {
+ nLocalTr.appendChild(aoLocal[i][j].cell);
+ aApplied[i][j] = 1;
+
+ /* Expand the cell to cover as many rows as needed */
+ while (aoLocal[i + iRowspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i + iRowspan][j].cell) {
+ aApplied[i + iRowspan][j] = 1;
+ iRowspan++;
+ }
+
+ /* Expand the cell to cover as many columns as needed */
+ while (aoLocal[i][j + iColspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i][j + iColspan].cell) {
+ /* Must update the applied array over the rows for the columns */
+ for (k = 0; k < iRowspan; k++) {
+ aApplied[i + k][j + iColspan] = 1;
+ }
+ iColspan++;
+ }
+
+ /* Do the actual expansion in the DOM */
+ $(aoLocal[i][j].cell)
+ .attr('rowspan', iRowspan)
+ .attr('colspan', iColspan);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Insert the required TR nodes into the table for display
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnDraw(oSettings) {
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
+ var aPreDraw = _fnCallbackFire(oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings]);
+ if ($.inArray(false, aPreDraw) !== -1) {
+ _fnProcessingDisplay(oSettings, false);
+ return;
+ }
+
+ var i, iLen, n;
+ var anRows = [];
+ var iRowCount = 0;
+ var asStripeClasses = oSettings.asStripeClasses;
+ var iStripes = asStripeClasses.length;
+ var iOpenRows = oSettings.aoOpenRows.length;
+ var oLang = oSettings.oLanguage;
+ var iInitDisplayStart = oSettings.iInitDisplayStart;
+ var bServerSide = _fnDataSource(oSettings) == 'ssp';
+ var aiDisplay = oSettings.aiDisplay;
+
+ oSettings.bDrawing = true;
+
+ /* Check and see if we have an initial draw position from state saving */
+ if (iInitDisplayStart !== undefined && iInitDisplayStart !== -1) {
+ oSettings._iDisplayStart = bServerSide ?
+ iInitDisplayStart :
+ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
+ 0 :
+ iInitDisplayStart;
+
+ oSettings.iInitDisplayStart = -1;
+ }
+
+ var iDisplayStart = oSettings._iDisplayStart;
+ var iDisplayEnd = oSettings.fnDisplayEnd();
+
+ /* Server-side processing draw intercept */
+ if (oSettings.bDeferLoading) {
+ oSettings.bDeferLoading = false;
+ oSettings.iDraw++;
+ _fnProcessingDisplay(oSettings, false);
+ } else if (!bServerSide) {
+ oSettings.iDraw++;
+ } else if (!oSettings.bDestroying && !_fnAjaxUpdate(oSettings)) {
+ return;
+ }
+
+ if (aiDisplay.length !== 0) {
+ var iStart = bServerSide ? 0 : iDisplayStart;
+ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
+
+ for (var j = iStart; j < iEnd; j++) {
+ var iDataIndex = aiDisplay[j];
+ var aoData = oSettings.aoData[iDataIndex];
+ if (aoData.nTr === null) {
+ _fnCreateTr(oSettings, iDataIndex);
+ }
+
+ var nRow = aoData.nTr;
+
+ /* Remove the old striping classes and then add the new one */
+ if (iStripes !== 0) {
+ var sStripe = asStripeClasses[iRowCount % iStripes];
+ if (aoData._sRowStripe != sStripe) {
+ $(nRow).removeClass(aoData._sRowStripe).addClass(sStripe);
+ aoData._sRowStripe = sStripe;
+ }
+ }
+
+ // Row callback functions - might want to manipulate the row
+ // iRowCount and j are not currently documented. Are they at all
+ // useful?
+ _fnCallbackFire(oSettings, 'aoRowCallback', null, [nRow, aoData._aData, iRowCount, j]);
+
+ anRows.push(nRow);
+ iRowCount++;
+ }
+ } else {
+ /* Table is empty - create a row with an empty message in it */
+ var sZero = oLang.sZeroRecords;
+ if (oSettings.iDraw == 1 && _fnDataSource(oSettings) == 'ajax') {
+ sZero = oLang.sLoadingRecords;
+ } else if (oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0) {
+ sZero = oLang.sEmptyTable;
+ }
+
+ anRows[0] = $('<tr/>', {
+ 'class': iStripes ? asStripeClasses[0] : ''
+ })
+ .append($('<td />', {
+ 'valign': 'top',
+ 'colSpan': _fnVisbleColumns(oSettings),
+ 'class': oSettings.oClasses.sRowEmpty
+ }).html(sZero))[0];
+ }
+
+ /* Header and footer callbacks */
+ _fnCallbackFire(oSettings, 'aoHeaderCallback', 'header', [$(oSettings.nTHead).children('tr')[0],
+ _fnGetDataMaster(oSettings), iDisplayStart, iDisplayEnd, aiDisplay
+ ]);
+
+ _fnCallbackFire(oSettings, 'aoFooterCallback', 'footer', [$(oSettings.nTFoot).children('tr')[0],
+ _fnGetDataMaster(oSettings), iDisplayStart, iDisplayEnd, aiDisplay
+ ]);
+
+ var body = $(oSettings.nTBody);
+
+ body.children().detach();
+ body.append($(anRows));
+
+ /* Call all required callback functions for the end of a draw */
+ _fnCallbackFire(oSettings, 'aoDrawCallback', 'draw', [oSettings]);
+
+ /* Draw is complete, sorting and filtering must be as well */
+ oSettings.bSorted = false;
+ oSettings.bFiltered = false;
+ oSettings.bDrawing = false;
+ }
+
+
+ /**
+ * Redraw the table - taking account of the various features which are enabled
+ * @param {object} oSettings dataTables settings object
+ * @param {boolean} [holdPosition] Keep the current paging position. By default
+ * the paging is reset to the first page
+ * @memberof DataTable#oApi
+ */
+ function _fnReDraw(settings, holdPosition) {
+ var
+ features = settings.oFeatures,
+ sort = features.bSort,
+ filter = features.bFilter;
+
+ if (sort) {
+ _fnSort(settings);
+ }
+
+ if (filter) {
+ _fnFilterComplete(settings, settings.oPreviousSearch);
+ } else {
+ // No filtering, so we want to just use the display master
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+ }
+
+ if (holdPosition !== true) {
+ settings._iDisplayStart = 0;
+ }
+
+ // Let any modules know about the draw hold position state (used by
+ // scrolling internally)
+ settings._drawHold = holdPosition;
+
+ _fnDraw(settings);
+
+ settings._drawHold = false;
+ }
+
+
+ /**
+ * Add the options to the page HTML for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAddOptionsHtml(oSettings) {
+ var classes = oSettings.oClasses;
+ var table = $(oSettings.nTable);
+ var holding = $('<div/>').insertBefore(table); // Holding element for speed
+ var features = oSettings.oFeatures;
+
+ // All DataTables are wrapped in a div
+ var insert = $('<div/>', {
+ id: oSettings.sTableId + '_wrapper',
+ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' ' + classes.sNoFooter)
+ });
+
+ oSettings.nHolding = holding[0];
+ oSettings.nTableWrapper = insert[0];
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
+
+ /* Loop over the user set positioning and place the elements as needed */
+ var aDom = oSettings.sDom.split('');
+ var featureNode, cOption, nNewNode, cNext, sAttr, j;
+ for (var i = 0; i < aDom.length; i++) {
+ featureNode = null;
+ cOption = aDom[i];
+
+ if (cOption == '<') {
+ /* New container div */
+ nNewNode = $('<div/>')[0];
+
+ /* Check to see if we should append an id and/or a class name to the container */
+ cNext = aDom[i + 1];
+ if (cNext == "'" || cNext == '"') {
+ sAttr = "";
+ j = 2;
+ while (aDom[i + j] != cNext) {
+ sAttr += aDom[i + j];
+ j++;
+ }
+
+ /* Replace jQuery UI constants @todo depreciated */
+ if (sAttr == "H") {
+ sAttr = classes.sJUIHeader;
+ } else if (sAttr == "F") {
+ sAttr = classes.sJUIFooter;
+ }
+
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
+ * breaks the string into parts and applies them as needed
+ */
+ if (sAttr.indexOf('.') != -1) {
+ var aSplit = sAttr.split('.');
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length - 1);
+ nNewNode.className = aSplit[1];
+ } else if (sAttr.charAt(0) == "#") {
+ nNewNode.id = sAttr.substr(1, sAttr.length - 1);
+ } else {
+ nNewNode.className = sAttr;
+ }
+
+ i += j; /* Move along the position array */
+ }
+
+ insert.append(nNewNode);
+ insert = $(nNewNode);
+ } else if (cOption == '>') {
+ /* End container div */
+ insert = insert.parent();
+ }
+ // @todo Move options into their own plugins?
+ else if (cOption == 'l' && features.bPaginate && features.bLengthChange) {
+ /* Length */
+ featureNode = _fnFeatureHtmlLength(oSettings);
+ } else if (cOption == 'f' && features.bFilter) {
+ /* Filter */
+ featureNode = _fnFeatureHtmlFilter(oSettings);
+ } else if (cOption == 'r' && features.bProcessing) {
+ /* pRocessing */
+ featureNode = _fnFeatureHtmlProcessing(oSettings);
+ } else if (cOption == 't') {
+ /* Table */
+ featureNode = _fnFeatureHtmlTable(oSettings);
+ } else if (cOption == 'i' && features.bInfo) {
+ /* Info */
+ featureNode = _fnFeatureHtmlInfo(oSettings);
+ } else if (cOption == 'p' && features.bPaginate) {
+ /* Pagination */
+ featureNode = _fnFeatureHtmlPaginate(oSettings);
+ } else if (DataTable.ext.feature.length !== 0) {
+ /* Plug-in features */
+ var aoFeatures = DataTable.ext.feature;
+ for (var k = 0, kLen = aoFeatures.length; k < kLen; k++) {
+ if (cOption == aoFeatures[k].cFeature) {
+ featureNode = aoFeatures[k].fnInit(oSettings);
+ break;
+ }
+ }
+ }
+
+ /* Add to the 2D features array */
+ if (featureNode) {
+ var aanFeatures = oSettings.aanFeatures;
+
+ if (!aanFeatures[cOption]) {
+ aanFeatures[cOption] = [];
+ }
+
+ aanFeatures[cOption].push(featureNode);
+ insert.append(featureNode);
+ }
+ }
+
+ /* Built our DOM structure - replace the holding div with what we want */
+ holding.replaceWith(insert);
+ oSettings.nHolding = null;
+ }
+
+
+ /**
+ * Use the DOM source to create up an array of header cells. The idea here is to
+ * create a layout grid (array) of rows x columns, which contains a reference
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
+ * any column / row could be removed and the new grid constructed
+ * @param array {object} aLayout Array to store the calculated layout in
+ * @param {node} nThead The header/footer element for the table
+ * @memberof DataTable#oApi
+ */
+ function _fnDetectHeader(aLayout, nThead) {
+ var nTrs = $(nThead).children('tr');
+ var nTr, nCell;
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
+ var bUnique;
+ var fnShiftCol = function (a, i, j) {
+ var k = a[i];
+ while (k[j]) {
+ j++;
+ }
+ return j;
+ };
+
+ aLayout.splice(0, aLayout.length);
+
+ /* We know how many rows there are in the layout - so prep it */
+ for (i = 0, iLen = nTrs.length; i < iLen; i++) {
+ aLayout.push([]);
+ }
+
+ /* Calculate a layout array */
+ for (i = 0, iLen = nTrs.length; i < iLen; i++) {
+ nTr = nTrs[i];
+ iColumn = 0;
+
+ /* For every cell in the row... */
+ nCell = nTr.firstChild;
+ while (nCell) {
+ if (nCell.nodeName.toUpperCase() == "TD" ||
+ nCell.nodeName.toUpperCase() == "TH") {
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
+ iColspan = nCell.getAttribute('colspan') * 1;
+ iRowspan = nCell.getAttribute('rowspan') * 1;
+ iColspan = (!iColspan || iColspan === 0 || iColspan === 1) ? 1 : iColspan;
+ iRowspan = (!iRowspan || iRowspan === 0 || iRowspan === 1) ? 1 : iRowspan;
+
+ /* There might be colspan cells already in this row, so shift our target
+ * accordingly
+ */
+ iColShifted = fnShiftCol(aLayout, i, iColumn);
+
+ /* Cache calculation for unique columns */
+ bUnique = iColspan === 1 ? true : false;
+
+ /* If there is col / rowspan, copy the information into the layout grid */
+ for (l = 0; l < iColspan; l++) {
+ for (k = 0; k < iRowspan; k++) {
+ aLayout[i + k][iColShifted + l] = {
+ "cell": nCell,
+ "unique": bUnique
+ };
+ aLayout[i + k].nTr = nTr;
+ }
+ }
+ }
+ nCell = nCell.nextSibling;
+ }
+ }
+ }
+
+
+ /**
+ * Get an array of unique th elements, one for each column
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nHeader automatically detect the layout from this node - optional
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
+ * @returns array {node} aReturn list of unique th's
+ * @memberof DataTable#oApi
+ */
+ function _fnGetUniqueThs(oSettings, nHeader, aLayout) {
+ var aReturn = [];
+ if (!aLayout) {
+ aLayout = oSettings.aoHeader;
+ if (nHeader) {
+ aLayout = [];
+ _fnDetectHeader(aLayout, nHeader);
+ }
+ }
+
+ for (var i = 0, iLen = aLayout.length; i < iLen; i++) {
+ for (var j = 0, jLen = aLayout[i].length; j < jLen; j++) {
+ if (aLayout[i][j].unique &&
+ (!aReturn[j] || !oSettings.bSortCellsTop)) {
+ aReturn[j] = aLayout[i][j].cell;
+ }
+ }
+ }
+
+ return aReturn;
+ }
+
+ /**
+ * Create an Ajax call based on the table's settings, taking into account that
+ * parameters can have multiple forms, and backwards compatibility.
+ *
+ * @param {object} oSettings dataTables settings object
+ * @param {array} data Data to send to the server, required by
+ * DataTables - may be augmented by developer callbacks
+ * @param {function} fn Callback function to run when data is obtained
+ */
+ function _fnBuildAjax(oSettings, data, fn) {
+ // Compatibility with 1.9-, allow fnServerData and event to manipulate
+ _fnCallbackFire(oSettings, 'aoServerParams', 'serverParams', [data]);
+
+ // Convert to object based for 1.10+ if using the old array scheme which can
+ // come from server-side processing or serverParams
+ if (data && $.isArray(data)) {
+ var tmp = {};
+ var rbracket = /(.*?)\[\]$/;
+
+ $.each(data, function (key, val) {
+ var match = val.name.match(rbracket);
+
+ if (match) {
+ // Support for arrays
+ var name = match[0];
+
+ if (!tmp[name]) {
+ tmp[name] = [];
+ }
+ tmp[name].push(val.value);
+ } else {
+ tmp[val.name] = val.value;
+ }
+ });
+ data = tmp;
+ }
+
+ var ajaxData;
+ var ajax = oSettings.ajax;
+ var instance = oSettings.oInstance;
+ var callback = function (json) {
+ _fnCallbackFire(oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR]);
+ fn(json);
+ };
+
+ if ($.isPlainObject(ajax) && ajax.data) {
+ ajaxData = ajax.data;
+
+ var newData = $.isFunction(ajaxData) ?
+ ajaxData(data, oSettings) : // fn can manipulate data or return
+ ajaxData; // an object object or array to merge
+
+ // If the function returned something, use that alone
+ data = $.isFunction(ajaxData) && newData ?
+ newData :
+ $.extend(true, data, newData);
+
+ // Remove the data property as we've resolved it already and don't want
+ // jQuery to do it again (it is restored at the end of the function)
+ delete ajax.data;
+ }
+
+ var baseAjax = {
+ "data": data,
+ "success": function (json) {
+ var error = json.error || json.sError;
+ if (error) {
+ _fnLog(oSettings, 0, error);
+ }
+
+ oSettings.json = json;
+ callback(json);
+ },
+ "dataType": "json",
+ "cache": false,
+ "type": oSettings.sServerMethod,
+ "error": function (xhr, error, thrown) {
+ var ret = _fnCallbackFire(oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR]);
+
+ if ($.inArray(true, ret) === -1) {
+ if (error == "parsererror") {
+ _fnLog(oSettings, 0, 'Invalid JSON response', 1);
+ } else if (xhr.readyState === 4) {
+ _fnLog(oSettings, 0, 'Ajax error', 7);
+ }
+ }
+
+ _fnProcessingDisplay(oSettings, false);
+ }
+ };
+
+ // Store the data submitted for the API
+ oSettings.oAjaxData = data;
+
+ // Allow plug-ins and external processes to modify the data
+ _fnCallbackFire(oSettings, null, 'preXhr', [oSettings, data]);
+
+ if (oSettings.fnServerData) {
+ // DataTables 1.9- compatibility
+ oSettings.fnServerData.call(instance,
+ oSettings.sAjaxSource,
+ $.map(data, function (val, key) { // Need to convert back to 1.9 trad format
+ return {
+ name: key,
+ value: val
+ };
+ }),
+ callback,
+ oSettings
+ );
+ } else if (oSettings.sAjaxSource || typeof ajax === 'string') {
+ // DataTables 1.9- compatibility
+ oSettings.jqXHR = $.ajax($.extend(baseAjax, {
+ url: ajax || oSettings.sAjaxSource
+ }));
+ } else if ($.isFunction(ajax)) {
+ // Is a function - let the caller define what needs to be done
+ oSettings.jqXHR = ajax.call(instance, data, callback, oSettings);
+ } else {
+ // Object to extend the base settings
+ oSettings.jqXHR = $.ajax($.extend(baseAjax, ajax));
+
+ // Restore for next time around
+ ajax.data = ajaxData;
+ }
+ }
+
+
+ /**
+ * Update the table using an Ajax call
+ * @param {object} settings dataTables settings object
+ * @returns {boolean} Block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdate(settings) {
+ if (settings.bAjaxDataGet) {
+ settings.iDraw++;
+ _fnProcessingDisplay(settings, true);
+
+ _fnBuildAjax(
+ settings,
+ _fnAjaxParameters(settings),
+ function (json) {
+ _fnAjaxUpdateDraw(settings, json);
+ }
+ );
+
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Build up the parameters in an object needed for a server-side processing
+ * request. Note that this is basically done twice, is different ways - a modern
+ * method which is used by default in DataTables 1.10 which uses objects and
+ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
+ * the sAjaxSource option is used in the initialisation, or the legacyAjax
+ * option is set.
+ * @param {object} oSettings dataTables settings object
+ * @returns {bool} block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxParameters(settings) {
+ var
+ columns = settings.aoColumns,
+ columnCount = columns.length,
+ features = settings.oFeatures,
+ preSearch = settings.oPreviousSearch,
+ preColSearch = settings.aoPreSearchCols,
+ i, data = [],
+ dataProp, column, columnSearch,
+ sort = _fnSortFlatten(settings),
+ displayStart = settings._iDisplayStart,
+ displayLength = features.bPaginate !== false ?
+ settings._iDisplayLength :
+ -1;
+
+ var param = function (name, value) {
+ data.push({
+ 'name': name,
+ 'value': value
+ });
+ };
+
+ // DataTables 1.9- compatible method
+ param('sEcho', settings.iDraw);
+ param('iColumns', columnCount);
+ param('sColumns', _pluck(columns, 'sName').join(','));
+ param('iDisplayStart', displayStart);
+ param('iDisplayLength', displayLength);
+
+ // DataTables 1.10+ method
+ var d = {
+ draw: settings.iDraw,
+ columns: [],
+ order: [],
+ start: displayStart,
+ length: displayLength,
+ search: {
+ value: preSearch.sSearch,
+ regex: preSearch.bRegex
+ }
+ };
+
+ for (i = 0; i < columnCount; i++) {
+ column = columns[i];
+ columnSearch = preColSearch[i];
+ dataProp = typeof column.mData == "function" ? 'function' : column.mData;
+
+ d.columns.push({
+ data: dataProp,
+ name: column.sName,
+ searchable: column.bSearchable,
+ orderable: column.bSortable,
+ search: {
+ value: columnSearch.sSearch,
+ regex: columnSearch.bRegex
+ }
+ });
+
+ param("mDataProp_" + i, dataProp);
+
+ if (features.bFilter) {
+ param('sSearch_' + i, columnSearch.sSearch);
+ param('bRegex_' + i, columnSearch.bRegex);
+ param('bSearchable_' + i, column.bSearchable);
+ }
+
+ if (features.bSort) {
+ param('bSortable_' + i, column.bSortable);
+ }
+ }
+
+ if (features.bFilter) {
+ param('sSearch', preSearch.sSearch);
+ param('bRegex', preSearch.bRegex);
+ }
+
+ if (features.bSort) {
+ $.each(sort, function (i, val) {
+ d.order.push({
+ column: val.col,
+ dir: val.dir
+ });
+
+ param('iSortCol_' + i, val.col);
+ param('sSortDir_' + i, val.dir);
+ });
+
+ param('iSortingCols', sort.length);
+ }
+
+ // If the legacy.ajax parameter is null, then we automatically decide which
+ // form to use, based on sAjaxSource
+ var legacy = DataTable.ext.legacy.ajax;
+ if (legacy === null) {
+ return settings.sAjaxSource ? data : d;
+ }
+
+ // Otherwise, if legacy has been specified then we use that to decide on the
+ // form
+ return legacy ? data : d;
+ }
+
+
+ /**
+ * Data the data from the server (nuking the old) and redraw the table
+ * @param {object} oSettings dataTables settings object
+ * @param {object} json json data return from the server.
+ * @param {string} json.sEcho Tracking flag for DataTables to match requests
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
+ * @param {array} json.aaData The data to display on this page
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdateDraw(settings, json) {
+ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
+ // Support both
+ var compat = function (old, modern) {
+ return json[old] !== undefined ? json[old] : json[modern];
+ };
+
+ var data = _fnAjaxDataSrc(settings, json);
+ var draw = compat('sEcho', 'draw');
+ var recordsTotal = compat('iTotalRecords', 'recordsTotal');
+ var recordsFiltered = compat('iTotalDisplayRecords', 'recordsFiltered');
+
+ if (draw) {
+ // Protect against out of sequence returns
+ if (draw * 1 < settings.iDraw) {
+ return;
+ }
+ settings.iDraw = draw * 1;
+ }
+
+ _fnClearTable(settings);
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
+
+ for (var i = 0, ien = data.length; i < ien; i++) {
+ _fnAddData(settings, data[i]);
+ }
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+
+ settings.bAjaxDataGet = false;
+ _fnDraw(settings);
+
+ if (!settings._bInitComplete) {
+ _fnInitComplete(settings, json);
+ }
+
+ settings.bAjaxDataGet = true;
+ _fnProcessingDisplay(settings, false);
+ }
+
+
+ /**
+ * Get the data from the JSON data source to use for drawing a table. Using
+ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
+ * source object, or from a processing function.
+ * @param {object} oSettings dataTables settings object
+ * @param {object} json Data source object / array from the server
+ * @return {array} Array of data to use
+ */
+ function _fnAjaxDataSrc(oSettings, json) {
+ var dataSrc = $.isPlainObject(oSettings.ajax) && oSettings.ajax.dataSrc !== undefined ?
+ oSettings.ajax.dataSrc :
+ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
+
+ // Compatibility with 1.9-. In order to read from aaData, check if the
+ // default has been changed, if not, check for aaData
+ if (dataSrc === 'data') {
+ return json.aaData || json[dataSrc];
+ }
+
+ return dataSrc !== "" ?
+ _fnGetObjectDataFn(dataSrc)(json) :
+ json;
+ }
+
+ /**
+ * Generate the node required for filtering text
+ * @returns {node} Filter control element
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlFilter(settings) {
+ var classes = settings.oClasses;
+ var tableId = settings.sTableId;
+ var language = settings.oLanguage;
+ var previousSearch = settings.oPreviousSearch;
+ var features = settings.aanFeatures;
+ var input = '<input type="search" class="' + classes.sFilterInput + '"/>';
+
+ var str = language.sSearch;
+ str = str.match(/_INPUT_/) ?
+ str.replace('_INPUT_', input) :
+ str + input;
+
+ var filter = $('<div/>', {
+ 'id': !features.f ? tableId + '_filter' : null,
+ 'class': classes.sFilter
+ })
+ .append($('<label/>').append(str));
+
+ var searchFn = function () {
+ /* Update all other filter input elements for the new display */
+ var n = features.f;
+ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
+
+ /* Now do the filter */
+ if (val != previousSearch.sSearch) {
+ _fnFilterComplete(settings, {
+ "sSearch": val,
+ "bRegex": previousSearch.bRegex,
+ "bSmart": previousSearch.bSmart,
+ "bCaseInsensitive": previousSearch.bCaseInsensitive
+ });
+
+ // Need to redraw, without resorting
+ settings._iDisplayStart = 0;
+ _fnDraw(settings);
+ }
+ };
+
+ var searchDelay = settings.searchDelay !== null ?
+ settings.searchDelay :
+ _fnDataSource(settings) === 'ssp' ?
+ 400 :
+ 0;
+
+ var jqFilter = $('input', filter)
+ .val(previousSearch.sSearch)
+ .attr('placeholder', language.sSearchPlaceholder)
+ .bind(
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
+ searchDelay ?
+ _fnThrottle(searchFn, searchDelay) :
+ searchFn
+ )
+ .bind('keypress.DT', function (e) {
+ /* Prevent form submission */
+ if (e.keyCode == 13) {
+ return false;
+ }
+ })
+ .attr('aria-controls', tableId);
+
+ // Update the input elements whenever the table is filtered
+ $(settings.nTable).on('search.dt.DT', function (ev, s) {
+ if (settings === s) {
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame...
+ try {
+ if (jqFilter[0] !== document.activeElement) {
+ jqFilter.val(previousSearch.sSearch);
+ }
+ } catch (e) {}
+ }
+ });
+
+ return filter[0];
+ }
+
+
+ /**
+ * Filter the table using both the global filter and column based filtering
+ * @param {object} oSettings dataTables settings object
+ * @param {object} oSearch search information
+ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterComplete(oSettings, oInput, iForce) {
+ var oPrevSearch = oSettings.oPreviousSearch;
+ var aoPrevSearch = oSettings.aoPreSearchCols;
+ var fnSaveFilter = function (oFilter) {
+ /* Save the filtering values */
+ oPrevSearch.sSearch = oFilter.sSearch;
+ oPrevSearch.bRegex = oFilter.bRegex;
+ oPrevSearch.bSmart = oFilter.bSmart;
+ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
+ };
+ var fnRegex = function (o) {
+ // Backwards compatibility with the bEscapeRegex option
+ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
+ };
+
+ // Resolve any column types that are unknown due to addition or invalidation
+ // @todo As per sort - can this be moved into an event handler?
+ _fnColumnTypes(oSettings);
+
+ /* In server-side processing all filtering is done by the server, so no point hanging around here */
+ if (_fnDataSource(oSettings) != 'ssp') {
+ /* Global filter */
+ _fnFilter(oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive);
+ fnSaveFilter(oInput);
+
+ /* Now do the individual column filter */
+ for (var i = 0; i < aoPrevSearch.length; i++) {
+ _fnFilterColumn(oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
+ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive);
+ }
+
+ /* Custom filtering */
+ _fnFilterCustom(oSettings);
+ } else {
+ fnSaveFilter(oInput);
+ }
+
+ /* Tell the draw function we have been filtering */
+ oSettings.bFiltered = true;
+ _fnCallbackFire(oSettings, null, 'search', [oSettings]);
+ }
+
+
+ /**
+ * Apply custom filtering functions
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterCustom(settings) {
+ var filters = DataTable.ext.search;
+ var displayRows = settings.aiDisplay;
+ var row, rowIdx;
+
+ for (var i = 0, ien = filters.length; i < ien; i++) {
+ var rows = [];
+
+ // Loop over each row and see if it should be included
+ for (var j = 0, jen = displayRows.length; j < jen; j++) {
+ rowIdx = displayRows[j];
+ row = settings.aoData[rowIdx];
+
+ if (filters[i](settings, row._aFilterData, rowIdx, row._aData, j)) {
+ rows.push(rowIdx);
+ }
+ }
+
+ // So the array reference doesn't break set the results into the
+ // existing array
+ displayRows.length = 0;
+ $.merge(displayRows, rows);
+ }
+ }
+
+
+ /**
+ * Filter the table on a per-column basis
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sInput string to filter on
+ * @param {int} iColumn column to filter
+ * @param {bool} bRegex treat search string as a regular expression or not
+ * @param {bool} bSmart use smart filtering or not
+ * @param {bool} bCaseInsensitive Do case insenstive matching or not
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterColumn(settings, searchStr, colIdx, regex, smart, caseInsensitive) {
+ if (searchStr === '') {
+ return;
+ }
+
+ var data;
+ var display = settings.aiDisplay;
+ var rpSearch = _fnFilterCreateSearch(searchStr, regex, smart, caseInsensitive);
+
+ for (var i = display.length - 1; i >= 0; i--) {
+ data = settings.aoData[display[i]]._aFilterData[colIdx];
+
+ if (!rpSearch.test(data)) {
+ display.splice(i, 1);
+ }
+ }
+ }
+
+
+ /**
+ * Filter the data table based on user input and draw the table
+ * @param {object} settings dataTables settings object
+ * @param {string} input string to filter on
+ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
+ * @param {bool} regex treat as a regular expression or not
+ * @param {bool} smart perform smart filtering or not
+ * @param {bool} caseInsensitive Do case insenstive matching or not
+ * @memberof DataTable#oApi
+ */
+ function _fnFilter(settings, input, force, regex, smart, caseInsensitive) {
+ var rpSearch = _fnFilterCreateSearch(input, regex, smart, caseInsensitive);
+ var prevSearch = settings.oPreviousSearch.sSearch;
+ var displayMaster = settings.aiDisplayMaster;
+ var display, invalidated, i;
+
+ // Need to take account of custom filtering functions - always filter
+ if (DataTable.ext.search.length !== 0) {
+ force = true;
+ }
+
+ // Check if any of the rows were invalidated
+ invalidated = _fnFilterData(settings);
+
+ // If the input is blank - we just want the full data set
+ if (input.length <= 0) {
+ settings.aiDisplay = displayMaster.slice();
+ } else {
+ // New search - start from the master array
+ if (invalidated ||
+ force ||
+ prevSearch.length > input.length ||
+ input.indexOf(prevSearch) !== 0 ||
+ settings.bSorted // On resort, the display master needs to be
+ // re-filtered since indexes will have changed
+ ) {
+ settings.aiDisplay = displayMaster.slice();
+ }
+
+ // Search the display array
+ display = settings.aiDisplay;
+
+ for (i = display.length - 1; i >= 0; i--) {
+ if (!rpSearch.test(settings.aoData[display[i]]._sFilterRow)) {
+ display.splice(i, 1);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Build a regular expression object suitable for searching a table
+ * @param {string} sSearch string to search for
+ * @param {bool} bRegex treat as a regular expression or not
+ * @param {bool} bSmart perform smart filtering or not
+ * @param {bool} bCaseInsensitive Do case insensitive matching or not
+ * @returns {RegExp} constructed object
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterCreateSearch(search, regex, smart, caseInsensitive) {
+ search = regex ?
+ search :
+ _fnEscapeRegex(search);
+
+ if (smart) {
+ /* For smart filtering we want to allow the search to work regardless of
+ * word order. We also want double quoted text to be preserved, so word
+ * order is important - a la google. So this is what we want to
+ * generate:
+ *
+ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
+ */
+ var a = $.map(search.match(/"[^"]+"|[^ ]+/g) || [''], function (word) {
+ if (word.charAt(0) === '"') {
+ var m = word.match(/^"(.*)"$/);
+ word = m ? m[1] : word;
+ }
+
+ return word.replace('"', '');
+ });
+
+ search = '^(?=.*?' + a.join(')(?=.*?') + ').*$';
+ }
+
+ return new RegExp(search, caseInsensitive ? 'i' : '');
+ }
+
+
+ /**
+ * Escape a string such that it can be used in a regular expression
+ * @param {string} sVal string to escape
+ * @returns {string} escaped string
+ * @memberof DataTable#oApi
+ */
+ var _fnEscapeRegex = DataTable.util.escapeRegex;
+
+ var __filter_div = $('<div>')[0];
+ var __filter_div_textContent = __filter_div.textContent !== undefined;
+
+ // Update the filtering data for each row if needed (by invalidation or first run)
+ function _fnFilterData(settings) {
+ var columns = settings.aoColumns;
+ var column;
+ var i, j, ien, jen, filterData, cellData, row;
+ var fomatters = DataTable.ext.type.search;
+ var wasInvalidated = false;
+
+ for (i = 0, ien = settings.aoData.length; i < ien; i++) {
+ row = settings.aoData[i];
+
+ if (!row._aFilterData) {
+ filterData = [];
+
+ for (j = 0, jen = columns.length; j < jen; j++) {
+ column = columns[j];
+
+ if (column.bSearchable) {
+ cellData = _fnGetCellData(settings, i, j, 'filter');
+
+ if (fomatters[column.sType]) {
+ cellData = fomatters[column.sType](cellData);
+ }
+
+ // Search in DataTables 1.10 is string based. In 1.11 this
+ // should be altered to also allow strict type checking.
+ if (cellData === null) {
+ cellData = '';
+ }
+
+ if (typeof cellData !== 'string' && cellData.toString) {
+ cellData = cellData.toString();
+ }
+ } else {
+ cellData = '';
+ }
+
+ // If it looks like there is an HTML entity in the string,
+ // attempt to decode it so sorting works as expected. Note that
+ // we could use a single line of jQuery to do this, but the DOM
+ // method used here is much faster http://jsperf.com/html-decode
+ if (cellData.indexOf && cellData.indexOf('&') !== -1) {
+ __filter_div.innerHTML = cellData;
+ cellData = __filter_div_textContent ?
+ __filter_div.textContent :
+ __filter_div.innerText;
+ }
+
+ if (cellData.replace) {
+ cellData = cellData.replace(/[\r\n]/g, '');
+ }
+
+ filterData.push(cellData);
+ }
+
+ row._aFilterData = filterData;
+ row._sFilterRow = filterData.join(' ');
+ wasInvalidated = true;
+ }
+ }
+
+ return wasInvalidated;
+ }
+
+
+ /**
+ * Convert from the internal Hungarian notation to camelCase for external
+ * interaction
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToCamel(obj) {
+ return {
+ search: obj.sSearch,
+ smart: obj.bSmart,
+ regex: obj.bRegex,
+ caseInsensitive: obj.bCaseInsensitive
+ };
+ }
+
+
+ /**
+ * Convert from camelCase notation to the internal Hungarian. We could use the
+ * Hungarian convert function here, but this is cleaner
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToHung(obj) {
+ return {
+ sSearch: obj.search,
+ bSmart: obj.smart,
+ bRegex: obj.regex,
+ bCaseInsensitive: obj.caseInsensitive
+ };
+ }
+
+ /**
+ * Generate the node required for the info display
+ * @param {object} oSettings dataTables settings object
+ * @returns {node} Information element
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlInfo(settings) {
+ var
+ tid = settings.sTableId,
+ nodes = settings.aanFeatures.i,
+ n = $('<div/>', {
+ 'class': settings.oClasses.sInfo,
+ 'id': !nodes ? tid + '_info' : null
+ });
+
+ if (!nodes) {
+ // Update display on each draw
+ settings.aoDrawCallback.push({
+ "fn": _fnUpdateInfo,
+ "sName": "information"
+ });
+
+ n
+ .attr('role', 'status')
+ .attr('aria-live', 'polite');
+
+ // Table is described by our info div
+ $(settings.nTable).attr('aria-describedby', tid + '_info');
+ }
+
+ return n[0];
+ }
+
+
+ /**
+ * Update the information elements in the display
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnUpdateInfo(settings) {
+ /* Show information about the table */
+ var nodes = settings.aanFeatures.i;
+ if (nodes.length === 0) {
+ return;
+ }
+
+ var
+ lang = settings.oLanguage,
+ start = settings._iDisplayStart + 1,
+ end = settings.fnDisplayEnd(),
+ max = settings.fnRecordsTotal(),
+ total = settings.fnRecordsDisplay(),
+ out = total ?
+ lang.sInfo :
+ lang.sInfoEmpty;
+
+ if (total !== max) {
+ /* Record set after filtering */
+ out += ' ' + lang.sInfoFiltered;
+ }
+
+ // Convert the macros
+ out += lang.sInfoPostFix;
+ out = _fnInfoMacros(settings, out);
+
+ var callback = lang.fnInfoCallback;
+ if (callback !== null) {
+ out = callback.call(settings.oInstance,
+ settings, start, end, max, total, out
+ );
+ }
+
+ $(nodes).html(out);
+ }
+
+
+ function _fnInfoMacros(settings, str) {
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
+ // internally
+ var
+ formatter = settings.fnFormatNumber,
+ start = settings._iDisplayStart + 1,
+ len = settings._iDisplayLength,
+ vis = settings.fnRecordsDisplay(),
+ all = len === -1;
+
+ return str.
+ replace(/_START_/g, formatter.call(settings, start)).
+ replace(/_END_/g, formatter.call(settings, settings.fnDisplayEnd())).
+ replace(/_MAX_/g, formatter.call(settings, settings.fnRecordsTotal())).
+ replace(/_TOTAL_/g, formatter.call(settings, vis)).
+ replace(/_PAGE_/g, formatter.call(settings, all ? 1 : Math.ceil(start / len))).
+ replace(/_PAGES_/g, formatter.call(settings, all ? 1 : Math.ceil(vis / len)));
+ }
+
+
+ /**
+ * Draw the table for the first time, adding all required features
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnInitialise(settings) {
+ var i, iLen, iAjaxStart = settings.iInitDisplayStart;
+ var columns = settings.aoColumns,
+ column;
+ var features = settings.oFeatures;
+ var deferLoading = settings.bDeferLoading; // value modified by the draw
+
+ /* Ensure that the table data is fully initialised */
+ if (!settings.bInitialised) {
+ setTimeout(function () {
+ _fnInitialise(settings);
+ }, 200);
+ return;
+ }
+
+ /* Show the display HTML options */
+ _fnAddOptionsHtml(settings);
+
+ /* Build and draw the header / footer for the table */
+ _fnBuildHead(settings);
+ _fnDrawHead(settings, settings.aoHeader);
+ _fnDrawHead(settings, settings.aoFooter);
+
+ /* Okay to show that something is going on now */
+ _fnProcessingDisplay(settings, true);
+
+ /* Calculate sizes for columns */
+ if (features.bAutoWidth) {
+ _fnCalculateColumnWidths(settings);
+ }
+
+ for (i = 0, iLen = columns.length; i < iLen; i++) {
+ column = columns[i];
+
+ if (column.sWidth) {
+ column.nTh.style.width = _fnStringToCss(column.sWidth);
+ }
+ }
+
+ _fnCallbackFire(settings, null, 'preInit', [settings]);
+
+ // If there is default sorting required - let's do it. The sort function
+ // will do the drawing for us. Otherwise we draw the table regardless of the
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
+ // data (show 'loading' message possibly)
+ _fnReDraw(settings);
+
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
+ var dataSrc = _fnDataSource(settings);
+ if (dataSrc != 'ssp' || deferLoading) {
+ // if there is an ajax source load the data
+ if (dataSrc == 'ajax') {
+ _fnBuildAjax(settings, [], function (json) {
+ var aData = _fnAjaxDataSrc(settings, json);
+
+ // Got the data - add it to the table
+ for (i = 0; i < aData.length; i++) {
+ _fnAddData(settings, aData[i]);
+ }
+
+ // Reset the init display for cookie saving. We've already done
+ // a filter, and therefore cleared it before. So we need to make
+ // it appear 'fresh'
+ settings.iInitDisplayStart = iAjaxStart;
+
+ _fnReDraw(settings);
+
+ _fnProcessingDisplay(settings, false);
+ _fnInitComplete(settings, json);
+ }, settings);
+ } else {
+ _fnProcessingDisplay(settings, false);
+ _fnInitComplete(settings);
+ }
+ }
+ }
+
+
+ /**
+ * Draw the table for the first time, adding all required features
+ * @param {object} oSettings dataTables settings object
+ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
+ * with client-side processing (optional)
+ * @memberof DataTable#oApi
+ */
+ function _fnInitComplete(settings, json) {
+ settings._bInitComplete = true;
+
+ // When data was added after the initialisation (data or Ajax) we need to
+ // calculate the column sizing
+ if (json || settings.oInit.aaData) {
+ _fnAdjustColumnSizing(settings);
+ }
+
+ _fnCallbackFire(settings, null, 'plugin-init', [settings, json]);
+ _fnCallbackFire(settings, 'aoInitComplete', 'init', [settings, json]);
+ }
+
+
+ function _fnLengthChange(settings, val) {
+ var len = parseInt(val, 10);
+ settings._iDisplayLength = len;
+
+ _fnLengthOverflow(settings);
+
+ // Fire length change event
+ _fnCallbackFire(settings, null, 'length', [settings, len]);
+ }
+
+
+ /**
+ * Generate the node required for user display length changing
+ * @param {object} settings dataTables settings object
+ * @returns {node} Display length feature node
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlLength(settings) {
+ var
+ classes = settings.oClasses,
+ tableId = settings.sTableId,
+ menu = settings.aLengthMenu,
+ d2 = $.isArray(menu[0]),
+ lengths = d2 ? menu[0] : menu,
+ language = d2 ? menu[1] : menu;
+
+ var select = $('<select/>', {
+ 'name': tableId + '_length',
+ 'aria-controls': tableId,
+ 'class': classes.sLengthSelect
+ });
+
+ for (var i = 0, ien = lengths.length; i < ien; i++) {
+ select[0][i] = new Option(language[i], lengths[i]);
+ }
+
+ var div = $('<div><label/></div>').addClass(classes.sLength);
+ if (!settings.aanFeatures.l) {
+ div[0].id = tableId + '_length';
+ }
+
+ div.children().append(
+ settings.oLanguage.sLengthMenu.replace('_MENU_', select[0].outerHTML)
+ );
+
+ // Can't use `select` variable as user might provide their own and the
+ // reference is broken by the use of outerHTML
+ $('select', div)
+ .val(settings._iDisplayLength)
+ .bind('change.DT', function (e) {
+ _fnLengthChange(settings, $(this).val());
+ _fnDraw(settings);
+ });
+
+ // Update node value whenever anything changes the table's length
+ $(settings.nTable).bind('length.dt.DT', function (e, s, len) {
+ if (settings === s) {
+ $('select', div).val(len);
+ }
+ });
+
+ return div[0];
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Note that most of the paging logic is done in
+ * DataTable.ext.pager
+ */
+
+ /**
+ * Generate the node required for default pagination
+ * @param {object} oSettings dataTables settings object
+ * @returns {node} Pagination feature node
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlPaginate(settings) {
+ var
+ type = settings.sPaginationType,
+ plugin = DataTable.ext.pager[type],
+ modern = typeof plugin === 'function',
+ redraw = function (settings) {
+ _fnDraw(settings);
+ },
+ node = $('<div/>').addClass(settings.oClasses.sPaging + type)[0],
+ features = settings.aanFeatures;
+
+ if (!modern) {
+ plugin.fnInit(settings, node, redraw);
+ }
+
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
+ if (!features.p) {
+ node.id = settings.sTableId + '_paginate';
+
+ settings.aoDrawCallback.push({
+ "fn": function (settings) {
+ if (modern) {
+ var
+ start = settings._iDisplayStart,
+ len = settings._iDisplayLength,
+ visRecords = settings.fnRecordsDisplay(),
+ all = len === -1,
+ page = all ? 0 : Math.ceil(start / len),
+ pages = all ? 1 : Math.ceil(visRecords / len),
+ buttons = plugin(page, pages),
+ i, ien;
+
+ for (i = 0, ien = features.p.length; i < ien; i++) {
+ _fnRenderer(settings, 'pageButton')(
+ settings, features.p[i], i, buttons, page, pages
+ );
+ }
+ } else {
+ plugin.fnUpdate(settings, redraw);
+ }
+ },
+ "sName": "pagination"
+ });
+ }
+
+ return node;
+ }
+
+
+ /**
+ * Alter the display settings to change the page
+ * @param {object} settings DataTables settings object
+ * @param {string|int} action Paging action to take: "first", "previous",
+ * "next" or "last" or page number to jump to (integer)
+ * @param [bool] redraw Automatically draw the update or not
+ * @returns {bool} true page has changed, false - no change
+ * @memberof DataTable#oApi
+ */
+ function _fnPageChange(settings, action, redraw) {
+ var
+ start = settings._iDisplayStart,
+ len = settings._iDisplayLength,
+ records = settings.fnRecordsDisplay();
+
+ if (records === 0 || len === -1) {
+ start = 0;
+ } else if (typeof action === "number") {
+ start = action * len;
+
+ if (start > records) {
+ start = 0;
+ }
+ } else if (action == "first") {
+ start = 0;
+ } else if (action == "previous") {
+ start = len >= 0 ?
+ start - len :
+ 0;
+
+ if (start < 0) {
+ start = 0;
+ }
+ } else if (action == "next") {
+ if (start + len < records) {
+ start += len;
+ }
+ } else if (action == "last") {
+ start = Math.floor((records - 1) / len) * len;
+ } else {
+ _fnLog(settings, 0, "Unknown paging action: " + action, 5);
+ }
+
+ var changed = settings._iDisplayStart !== start;
+ settings._iDisplayStart = start;
+
+ if (changed) {
+ _fnCallbackFire(settings, null, 'page', [settings]);
+
+ if (redraw) {
+ _fnDraw(settings);
+ }
+ }
+
+ return changed;
+ }
+
+
+ /**
+ * Generate the node required for the processing node
+ * @param {object} settings dataTables settings object
+ * @returns {node} Processing element
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlProcessing(settings) {
+ return $('<div/>', {
+ 'id': !settings.aanFeatures.r ? settings.sTableId + '_processing' : null,
+ 'class': settings.oClasses.sProcessing
+ })
+ .html(settings.oLanguage.sProcessing)
+ .insertBefore(settings.nTable)[0];
+ }
+
+
+ /**
+ * Display or hide the processing indicator
+ * @param {object} settings dataTables settings object
+ * @param {bool} show Show the processing indicator (true) or not (false)
+ * @memberof DataTable#oApi
+ */
+ function _fnProcessingDisplay(settings, show) {
+ if (settings.oFeatures.bProcessing) {
+ $(settings.aanFeatures.r).css('display', show ? 'block' : 'none');
+ }
+
+ _fnCallbackFire(settings, null, 'processing', [settings, show]);
+ }
+
+ /**
+ * Add any control elements for the table - specifically scrolling
+ * @param {object} settings dataTables settings object
+ * @returns {node} Node to add to the DOM
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlTable(settings) {
+ var table = $(settings.nTable);
+
+ // Add the ARIA grid role to the table
+ table.attr('role', 'grid');
+
+ // Scrolling from here on in
+ var scroll = settings.oScroll;
+
+ if (scroll.sX === '' && scroll.sY === '') {
+ return settings.nTable;
+ }
+
+ var scrollX = scroll.sX;
+ var scrollY = scroll.sY;
+ var classes = settings.oClasses;
+ var caption = table.children('caption');
+ var captionSide = caption.length ? caption[0]._captionSide : null;
+ var headerClone = $(table[0].cloneNode(false));
+ var footerClone = $(table[0].cloneNode(false));
+ var footer = table.children('tfoot');
+ var _div = '<div/>';
+ var size = function (s) {
+ return !s ? null : _fnStringToCss(s);
+ };
+
+ if (!footer.length) {
+ footer = null;
+ }
+
+ /*
+ * The HTML structure that we want to generate in this function is:
+ * div - scroller
+ * div - scroll head
+ * div - scroll head inner
+ * table - scroll head table
+ * thead - thead
+ * div - scroll body
+ * table - table (master table)
+ * thead - thead clone for sizing
+ * tbody - tbody
+ * div - scroll foot
+ * div - scroll foot inner
+ * table - scroll foot table
+ * tfoot - tfoot
+ */
+ var scroller = $(_div, {
+ 'class': classes.sScrollWrapper
+ })
+ .append(
+ $(_div, {
+ 'class': classes.sScrollHead
+ })
+ .css({
+ overflow: 'hidden',
+ position: 'relative',
+ border: 0,
+ width: scrollX ? size(scrollX) : '100%'
+ })
+ .append(
+ $(_div, {
+ 'class': classes.sScrollHeadInner
+ })
+ .css({
+ 'box-sizing': 'content-box',
+ width: scroll.sXInner || '100%'
+ })
+ .append(
+ headerClone
+ .removeAttr('id')
+ .css('margin-left', 0)
+ .append(captionSide === 'top' ? caption : null)
+ .append(
+ table.children('thead')
+ )
+ )
+ )
+ )
+ .append(
+ $(_div, {
+ 'class': classes.sScrollBody
+ })
+ .css({
+ position: 'relative',
+ overflow: 'auto',
+ width: size(scrollX)
+ })
+ .append(table)
+ );
+
+ if (footer) {
+ scroller.append(
+ $(_div, {
+ 'class': classes.sScrollFoot
+ })
+ .css({
+ overflow: 'hidden',
+ border: 0,
+ width: scrollX ? size(scrollX) : '100%'
+ })
+ .append(
+ $(_div, {
+ 'class': classes.sScrollFootInner
+ })
+ .append(
+ footerClone
+ .removeAttr('id')
+ .css('margin-left', 0)
+ .append(captionSide === 'bottom' ? caption : null)
+ .append(
+ table.children('tfoot')
+ )
+ )
+ )
+ );
+ }
+
+ var children = scroller.children();
+ var scrollHead = children[0];
+ var scrollBody = children[1];
+ var scrollFoot = footer ? children[2] : null;
+
+ // When the body is scrolled, then we also want to scroll the headers
+ if (scrollX) {
+ $(scrollBody).on('scroll.DT', function (e) {
+ var scrollLeft = this.scrollLeft;
+
+ scrollHead.scrollLeft = scrollLeft;
+
+ if (footer) {
+ scrollFoot.scrollLeft = scrollLeft;
+ }
+ });
+ }
+
+ $(scrollBody).css(
+ scrollY && scroll.bCollapse ? 'max-height' : 'height',
+ scrollY
+ );
+
+ settings.nScrollHead = scrollHead;
+ settings.nScrollBody = scrollBody;
+ settings.nScrollFoot = scrollFoot;
+
+ // On redraw - align columns
+ settings.aoDrawCallback.push({
+ "fn": _fnScrollDraw,
+ "sName": "scrolling"
+ });
+
+ return scroller[0];
+ }
+
+
+ /**
+ * Update the header, footer and body tables for resizing - i.e. column
+ * alignment.
+ *
+ * Welcome to the most horrible function DataTables. The process that this
+ * function follows is basically:
+ * 1. Re-create the table inside the scrolling div
+ * 2. Take live measurements from the DOM
+ * 3. Apply the measurements to align the columns
+ * 4. Clean up
+ *
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnScrollDraw(settings) {
+ // Given that this is such a monster function, a lot of variables are use
+ // to try and keep the minimised size as small as possible
+ var
+ scroll = settings.oScroll,
+ scrollX = scroll.sX,
+ scrollXInner = scroll.sXInner,
+ scrollY = scroll.sY,
+ barWidth = scroll.iBarWidth,
+ divHeader = $(settings.nScrollHead),
+ divHeaderStyle = divHeader[0].style,
+ divHeaderInner = divHeader.children('div'),
+ divHeaderInnerStyle = divHeaderInner[0].style,
+ divHeaderTable = divHeaderInner.children('table'),
+ divBodyEl = settings.nScrollBody,
+ divBody = $(divBodyEl),
+ divBodyStyle = divBodyEl.style,
+ divFooter = $(settings.nScrollFoot),
+ divFooterInner = divFooter.children('div'),
+ divFooterTable = divFooterInner.children('table'),
+ header = $(settings.nTHead),
+ table = $(settings.nTable),
+ tableEl = table[0],
+ tableStyle = tableEl.style,
+ footer = settings.nTFoot ? $(settings.nTFoot) : null,
+ browser = settings.oBrowser,
+ ie67 = browser.bScrollOversize,
+ dtHeaderCells = _pluck(settings.aoColumns, 'nTh'),
+ headerTrgEls, footerTrgEls,
+ headerSrcEls, footerSrcEls,
+ headerCopy, footerCopy,
+ headerWidths = [],
+ footerWidths = [],
+ headerContent = [],
+ footerContent = [],
+ idx, correction, sanityWidth,
+ zeroOut = function (nSizer) {
+ var style = nSizer.style;
+ style.paddingTop = "0";
+ style.paddingBottom = "0";
+ style.borderTopWidth = "0";
+ style.borderBottomWidth = "0";
+ style.height = 0;
+ };
+
+ // If the scrollbar visibility has changed from the last draw, we need to
+ // adjust the column sizes as the table width will have changed to account
+ // for the scrollbar
+ var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
+
+ if (settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined) {
+ settings.scrollBarVis = scrollBarVis;
+ _fnAdjustColumnSizing(settings);
+ return; // adjust column sizing will call this function again
+ } else {
+ settings.scrollBarVis = scrollBarVis;
+ }
+
+ /*
+ * 1. Re-create the table inside the scrolling div
+ */
+
+ // Remove the old minimised thead and tfoot elements in the inner table
+ table.children('thead, tfoot').remove();
+
+ if (footer) {
+ footerCopy = footer.clone().prependTo(table);
+ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
+ footerSrcEls = footerCopy.find('tr');
+ }
+
+ // Clone the current header and footer elements and then place it into the inner table
+ headerCopy = header.clone().prependTo(table);
+ headerTrgEls = header.find('tr'); // original header is in its own table
+ headerSrcEls = headerCopy.find('tr');
+ headerCopy.find('th, td').removeAttr('tabindex');
+
+
+ /*
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
+ */
+
+ // Remove old sizing and apply the calculated column widths
+ // Get the unique column headers in the newly created (cloned) header. We want to apply the
+ // calculated sizes to this header
+ if (!scrollX) {
+ divBodyStyle.width = '100%';
+ divHeader[0].style.width = '100%';
+ }
+
+ $.each(_fnGetUniqueThs(settings, headerCopy), function (i, el) {
+ idx = _fnVisibleToColumnIndex(settings, i);
+ el.style.width = settings.aoColumns[idx].sWidth;
+ });
+
+ if (footer) {
+ _fnApplyToChildren(function (n) {
+ n.style.width = "";
+ }, footerSrcEls);
+ }
+
+ // Size the table as a whole
+ sanityWidth = table.outerWidth();
+ if (scrollX === "") {
+ // No x scrolling
+ tableStyle.width = "100%";
+
+ // IE7 will make the width of the table when 100% include the scrollbar
+ // - which is shouldn't. When there is a scrollbar we need to take this
+ // into account.
+ if (ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
+ divBody.css('overflow-y') == "scroll")) {
+ tableStyle.width = _fnStringToCss(table.outerWidth() - barWidth);
+ }
+
+ // Recalculate the sanity width
+ sanityWidth = table.outerWidth();
+ } else if (scrollXInner !== "") {
+ // legacy x scroll inner has been given - use it
+ tableStyle.width = _fnStringToCss(scrollXInner);
+
+ // Recalculate the sanity width
+ sanityWidth = table.outerWidth();
+ }
+
+ // Hidden header should have zero height, so remove padding and borders. Then
+ // set the width based on the real headers
+
+ // Apply all styles in one pass
+ _fnApplyToChildren(zeroOut, headerSrcEls);
+
+ // Read all widths in next pass
+ _fnApplyToChildren(function (nSizer) {
+ headerContent.push(nSizer.innerHTML);
+ headerWidths.push(_fnStringToCss($(nSizer).css('width')));
+ }, headerSrcEls);
+
+ // Apply all widths in final pass
+ _fnApplyToChildren(function (nToSize, i) {
+ // Only apply widths to the DataTables detected header cells - this
+ // prevents complex headers from having contradictory sizes applied
+ if ($.inArray(nToSize, dtHeaderCells) !== -1) {
+ nToSize.style.width = headerWidths[i];
+ }
+ }, headerTrgEls);
+
+ $(headerSrcEls).height(0);
+
+ /* Same again with the footer if we have one */
+ if (footer) {
+ _fnApplyToChildren(zeroOut, footerSrcEls);
+
+ _fnApplyToChildren(function (nSizer) {
+ footerContent.push(nSizer.innerHTML);
+ footerWidths.push(_fnStringToCss($(nSizer).css('width')));
+ }, footerSrcEls);
+
+ _fnApplyToChildren(function (nToSize, i) {
+ nToSize.style.width = footerWidths[i];
+ }, footerTrgEls);
+
+ $(footerSrcEls).height(0);
+ }
+
+
+ /*
+ * 3. Apply the measurements
+ */
+
+ // "Hide" the header and footer that we used for the sizing. We need to keep
+ // the content of the cell so that the width applied to the header and body
+ // both match, but we want to hide it completely. We want to also fix their
+ // width to what they currently are
+ _fnApplyToChildren(function (nSizer, i) {
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">' + headerContent[i] + '</div>';
+ nSizer.style.width = headerWidths[i];
+ }, headerSrcEls);
+
+ if (footer) {
+ _fnApplyToChildren(function (nSizer, i) {
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">' + footerContent[i] + '</div>';
+ nSizer.style.width = footerWidths[i];
+ }, footerSrcEls);
+ }
+
+ // Sanity check that the table is of a sensible width. If not then we are going to get
+ // misalignment - try to prevent this by not allowing the table to shrink below its min width
+ if (table.outerWidth() < sanityWidth) {
+ // The min width depends upon if we have a vertical scrollbar visible or not */
+ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
+ divBody.css('overflow-y') == "scroll")) ?
+ sanityWidth + barWidth :
+ sanityWidth;
+
+ // IE6/7 are a law unto themselves...
+ if (ie67 && (divBodyEl.scrollHeight >
+ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")) {
+ tableStyle.width = _fnStringToCss(correction - barWidth);
+ }
+
+ // And give the user a warning that we've stopped the table getting too small
+ if (scrollX === "" || scrollXInner !== "") {
+ _fnLog(settings, 1, 'Possible column misalignment', 6);
+ }
+ } else {
+ correction = '100%';
+ }
+
+ // Apply to the container elements
+ divBodyStyle.width = _fnStringToCss(correction);
+ divHeaderStyle.width = _fnStringToCss(correction);
+
+ if (footer) {
+ settings.nScrollFoot.style.width = _fnStringToCss(correction);
+ }
+
+
+ /*
+ * 4. Clean up
+ */
+ if (!scrollY) {
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
+ * the scrollbar height from the visible display, rather than adding it on. We need to
+ * set the height in order to sort this. Don't want to do it in any other browsers.
+ */
+ if (ie67) {
+ divBodyStyle.height = _fnStringToCss(tableEl.offsetHeight + barWidth);
+ }
+ }
+
+ /* Finally set the width's of the header and footer tables */
+ var iOuterWidth = table.outerWidth();
+ divHeaderTable[0].style.width = _fnStringToCss(iOuterWidth);
+ divHeaderInnerStyle.width = _fnStringToCss(iOuterWidth);
+
+ // Figure out if there are scrollbar present - if so then we need a the header and footer to
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
+ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
+ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right');
+ divHeaderInnerStyle[padding] = bScrolling ? barWidth + "px" : "0px";
+
+ if (footer) {
+ divFooterTable[0].style.width = _fnStringToCss(iOuterWidth);
+ divFooterInner[0].style.width = _fnStringToCss(iOuterWidth);
+ divFooterInner[0].style[padding] = bScrolling ? barWidth + "px" : "0px";
+ }
+
+ // Correct DOM ordering for colgroup - comes before the thead
+ table.children('colgroup').insertBefore(table.children('thead'));
+
+ /* Adjust the position of the header in case we loose the y-scrollbar */
+ divBody.scroll();
+
+ // If sorting or filtering has occurred, jump the scrolling back to the top
+ // only if we aren't holding the position
+ if ((settings.bSorted || settings.bFiltered) && !settings._drawHold) {
+ divBodyEl.scrollTop = 0;
+ }
+ }
+
+
+ /**
+ * Apply a given function to the display child nodes of an element array (typically
+ * TD children of TR rows
+ * @param {function} fn Method to apply to the objects
+ * @param array {nodes} an1 List of elements to look through for display children
+ * @param array {nodes} an2 Another list (identical structure to the first) - optional
+ * @memberof DataTable#oApi
+ */
+ function _fnApplyToChildren(fn, an1, an2) {
+ var index = 0,
+ i = 0,
+ iLen = an1.length;
+ var nNode1, nNode2;
+
+ while (i < iLen) {
+ nNode1 = an1[i].firstChild;
+ nNode2 = an2 ? an2[i].firstChild : null;
+
+ while (nNode1) {
+ if (nNode1.nodeType === 1) {
+ if (an2) {
+ fn(nNode1, nNode2, index);
+ } else {
+ fn(nNode1, index);
+ }
+
+ index++;
+ }
+
+ nNode1 = nNode1.nextSibling;
+ nNode2 = an2 ? nNode2.nextSibling : null;
+ }
+
+ i++;
+ }
+ }
+
+
+ var __re_html_remove = /<.*?>/g;
+
+
+ /**
+ * Calculate the width of columns for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnCalculateColumnWidths(oSettings) {
+ var
+ table = oSettings.nTable,
+ columns = oSettings.aoColumns,
+ scroll = oSettings.oScroll,
+ scrollY = scroll.sY,
+ scrollX = scroll.sX,
+ scrollXInner = scroll.sXInner,
+ columnCount = columns.length,
+ visibleColumns = _fnGetColumns(oSettings, 'bVisible'),
+ headerCells = $('th', oSettings.nTHead),
+ tableWidthAttr = table.getAttribute('width'), // from DOM element
+ tableContainer = table.parentNode,
+ userInputs = false,
+ i, column, columnIdx, width, outerWidth,
+ browser = oSettings.oBrowser,
+ ie67 = browser.bScrollOversize;
+
+
+ var styleWidth = table.style.width;
+ if (styleWidth && styleWidth.indexOf('%') !== -1) {
+ tableWidthAttr = styleWidth;
+ }
+
+ /* Convert any user input sizes into pixel sizes */
+ for (i = 0; i < visibleColumns.length; i++) {
+ column = columns[visibleColumns[i]];
+
+ if (column.sWidth !== null) {
+ column.sWidth = _fnConvertToWidth(column.sWidthOrig, tableContainer);
+
+ userInputs = true;
+ }
+ }
+
+ /* If the number of columns in the DOM equals the number that we have to
+ * process in DataTables, then we can use the offsets that are created by
+ * the web- browser. No custom sizes can be set in order for this to happen,
+ * nor scrolling used
+ */
+ if (ie67 || !userInputs && !scrollX && !scrollY &&
+ columnCount == _fnVisbleColumns(oSettings) &&
+ columnCount == headerCells.length
+ ) {
+ for (i = 0; i < columnCount; i++) {
+ var colIdx = _fnVisibleToColumnIndex(oSettings, i);
+
+ if (colIdx !== null) {
+ columns[colIdx].sWidth = _fnStringToCss(headerCells.eq(i).width());
+ }
+ }
+ } else {
+ // Otherwise construct a single row, worst case, table with the widest
+ // node in the data, assign any user defined widths, then insert it into
+ // the DOM and allow the browser to do all the hard work of calculating
+ // table widths
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
+ .css('visibility', 'hidden')
+ .removeAttr('id');
+
+ // Clean up the table body
+ tmpTable.find('tbody tr').remove();
+ var tr = $('<tr/>').appendTo(tmpTable.find('tbody'));
+
+ // Clone the table header and footer - we can't use the header / footer
+ // from the cloned table, since if scrolling is active, the table's
+ // real header and footer are contained in different table tags
+ tmpTable.find('thead, tfoot').remove();
+ tmpTable
+ .append($(oSettings.nTHead).clone())
+ .append($(oSettings.nTFoot).clone());
+
+ // Remove any assigned widths from the footer (from scrolling)
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
+
+ // Apply custom sizing to the cloned header
+ headerCells = _fnGetUniqueThs(oSettings, tmpTable.find('thead')[0]);
+
+ for (i = 0; i < visibleColumns.length; i++) {
+ column = columns[visibleColumns[i]];
+ if (!headerCells[i]) {
+ break;
+ }
+ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
+ _fnStringToCss(column.sWidthOrig) :
+ '';
+
+ // For scrollX we need to force the column width otherwise the
+ // browser will collapse it. If this width is smaller than the
+ // width the column requires, then it will have no effect
+ if (column.sWidthOrig && scrollX) {
+ $(headerCells[i]).append($('<div/>').css({
+ width: column.sWidthOrig,
+ margin: 0,
+ padding: 0,
+ border: 0,
+ height: 1
+ }));
+ }
+ }
+
+ // Find the widest cell for each column and put it into the table
+ if (oSettings.aoData.length) {
+ for (i = 0; i < visibleColumns.length; i++) {
+ columnIdx = visibleColumns[i];
+ column = columns[columnIdx];
+
+ $(_fnGetWidestNode(oSettings, columnIdx))
+ .clone(false)
+ .append(column.sContentPadding)
+ .appendTo(tr);
+ }
+ }
+
+ // Tidy the temporary table - remove name attributes so there aren't
+ // duplicated in the dom (radio elements for example)
+ $('[name]', tmpTable).removeAttr('name');
+
+ // Table has been built, attach to the document so we can work with it.
+ // A holding element is used, positioned at the top of the container
+ // with minimal height, so it has no effect on if the container scrolls
+ // or not. Otherwise it might trigger scrolling when it actually isn't
+ // needed
+ var holder = $('<div/>').css(scrollX || scrollY ? {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ height: 1,
+ right: 0,
+ overflow: 'hidden'
+ } : {})
+ .append(tmpTable)
+ .appendTo(tableContainer);
+
+ // When scrolling (X or Y) we want to set the width of the table as
+ // appropriate. However, when not scrolling leave the table width as it
+ // is. This results in slightly different, but I think correct behaviour
+ if (scrollX && scrollXInner) {
+ tmpTable.width(scrollXInner);
+ } else if (scrollX) {
+ tmpTable.css('width', 'auto');
+ tmpTable.removeAttr('width');
+
+ // If there is no width attribute or style, then allow the table to
+ // collapse
+ if (tmpTable.width() < tableContainer.clientWidth && tableWidthAttr) {
+ tmpTable.width(tableContainer.clientWidth);
+ }
+ } else if (scrollY) {
+ tmpTable.width(tableContainer.clientWidth);
+ } else if (tableWidthAttr) {
+ tmpTable.width(tableWidthAttr);
+ }
+
+ // Get the width of each column in the constructed table - we need to
+ // know the inner width (so it can be assigned to the other table's
+ // cells) and the outer width so we can calculate the full width of the
+ // table. This is safe since DataTables requires a unique cell for each
+ // column, but if ever a header can span multiple columns, this will
+ // need to be modified.
+ var total = 0;
+ for (i = 0; i < visibleColumns.length; i++) {
+ if (!headerCells[i]) {
+ break;
+ }
+ var cell = $(headerCells[i]);
+ var border = cell.outerWidth() - cell.width();
+
+ // Use getBounding... where possible (not IE8-) because it can give
+ // sub-pixel accuracy, which we then want to round up!
+ var bounding = browser.bBounding ?
+ Math.ceil(headerCells[i].getBoundingClientRect().width) :
+ cell.outerWidth();
+
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
+ // of the table might not equal the total given here (IE!).
+ total += bounding;
+
+ // Width for each column to use
+ columns[visibleColumns[i]].sWidth = _fnStringToCss(bounding - border);
+ }
+
+ table.style.width = _fnStringToCss(total);
+
+ // Finished with the table - ditch it
+ holder.remove();
+ }
+
+ // If there is a width attr, we want to attach an event listener which
+ // allows the table sizing to automatically adjust when the window is
+ // resized. Use the width attr rather than CSS, since we can't know if the
+ // CSS is a relative value or absolute - DOM read is always px.
+ if (tableWidthAttr) {
+ table.style.width = _fnStringToCss(tableWidthAttr);
+ }
+
+ if ((tableWidthAttr || scrollX) && !oSettings._reszEvt) {
+ var bindResize = function () {
+ $(window).bind('resize.DT-' + oSettings.sInstance, _fnThrottle(function () {
+ _fnAdjustColumnSizing(oSettings);
+ }));
+ };
+
+ // IE6/7 will crash if we bind a resize event handler on page load.
+ // To be removed in 1.11 which drops IE6/7 support
+ if (ie67) {
+ setTimeout(bindResize, 1000);
+ } else {
+ bindResize();
+ }
+
+ oSettings._reszEvt = true;
+ }
+ }
+
+
+ /**
+ * Throttle the calls to a function. Arguments and context are maintained for
+ * the throttled function
+ * @param {function} fn Function to be called
+ * @param {int} [freq=200] call frequency in mS
+ * @returns {function} wrapped function
+ * @memberof DataTable#oApi
+ */
+ var _fnThrottle = DataTable.util.throttle;
+
+
+ /**
+ * Convert a CSS unit width to pixels (e.g. 2em)
+ * @param {string} width width to be converted
+ * @param {node} parent parent to get the with for (required for relative widths) - optional
+ * @returns {int} width in pixels
+ * @memberof DataTable#oApi
+ */
+ function _fnConvertToWidth(width, parent) {
+ if (!width) {
+ return 0;
+ }
+
+ var n = $('<div/>')
+ .css('width', _fnStringToCss(width))
+ .appendTo(parent || document.body);
+
+ var val = n[0].offsetWidth;
+ n.remove();
+
+ return val;
+ }
+
+
+ /**
+ * Get the widest node
+ * @param {object} settings dataTables settings object
+ * @param {int} colIdx column of interest
+ * @returns {node} widest table node
+ * @memberof DataTable#oApi
+ */
+ function _fnGetWidestNode(settings, colIdx) {
+ var idx = _fnGetMaxLenString(settings, colIdx);
+ if (idx < 0) {
+ return null;
+ }
+
+ var data = settings.aoData[idx];
+ return !data.nTr ? // Might not have been created when deferred rendering
+ $('<td/>').html(_fnGetCellData(settings, idx, colIdx, 'display'))[0] :
+ data.anCells[colIdx];
+ }
+
+
+ /**
+ * Get the maximum strlen for each data column
+ * @param {object} settings dataTables settings object
+ * @param {int} colIdx column of interest
+ * @returns {string} max string length for each column
+ * @memberof DataTable#oApi
+ */
+ function _fnGetMaxLenString(settings, colIdx) {
+ var s, max = -1,
+ maxIdx = -1;
+
+ for (var i = 0, ien = settings.aoData.length; i < ien; i++) {
+ s = _fnGetCellData(settings, i, colIdx, 'display') + '';
+ s = s.replace(__re_html_remove, '');
+ s = s.replace(/&nbsp;/g, ' ');
+
+ if (s.length > max) {
+ max = s.length;
+ maxIdx = i;
+ }
+ }
+
+ return maxIdx;
+ }
+
+
+ /**
+ * Append a CSS unit (only if required) to a string
+ * @param {string} value to css-ify
+ * @returns {string} value with css unit
+ * @memberof DataTable#oApi
+ */
+ function _fnStringToCss(s) {
+ if (s === null) {
+ return '0px';
+ }
+
+ if (typeof s == 'number') {
+ return s < 0 ?
+ '0px' :
+ s + 'px';
+ }
+
+ // Check it has a unit character already
+ return s.match(/\d$/) ?
+ s + 'px' :
+ s;
+ }
+
+
+ function _fnSortFlatten(settings) {
+ var
+ i, iLen, k, kLen,
+ aSort = [],
+ aiOrig = [],
+ aoColumns = settings.aoColumns,
+ aDataSort, iCol, sType, srcCol,
+ fixed = settings.aaSortingFixed,
+ fixedObj = $.isPlainObject(fixed),
+ nestedSort = [],
+ add = function (a) {
+ if (a.length && !$.isArray(a[0])) {
+ // 1D array
+ nestedSort.push(a);
+ } else {
+ // 2D array
+ $.merge(nestedSort, a);
+ }
+ };
+
+ // Build the sort array, with pre-fix and post-fix options if they have been
+ // specified
+ if ($.isArray(fixed)) {
+ add(fixed);
+ }
+
+ if (fixedObj && fixed.pre) {
+ add(fixed.pre);
+ }
+
+ add(settings.aaSorting);
+
+ if (fixedObj && fixed.post) {
+ add(fixed.post);
+ }
+
+ for (i = 0; i < nestedSort.length; i++) {
+ srcCol = nestedSort[i][0];
+ aDataSort = aoColumns[srcCol].aDataSort;
+
+ for (k = 0, kLen = aDataSort.length; k < kLen; k++) {
+ iCol = aDataSort[k];
+ sType = aoColumns[iCol].sType || 'string';
+
+ if (nestedSort[i]._idx === undefined) {
+ nestedSort[i]._idx = $.inArray(nestedSort[i][1], aoColumns[iCol].asSorting);
+ }
+
+ aSort.push({
+ src: srcCol,
+ col: iCol,
+ dir: nestedSort[i][1],
+ index: nestedSort[i]._idx,
+ type: sType,
+ formatter: DataTable.ext.type.order[sType + "-pre"]
+ });
+ }
+ }
+
+ return aSort;
+ }
+
+ /**
+ * Change the order of the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ * @todo This really needs split up!
+ */
+ function _fnSort(oSettings) {
+ var
+ i, ien, iLen, j, jLen, k, kLen,
+ sDataType, nTh,
+ aiOrig = [],
+ oExtSort = DataTable.ext.type.order,
+ aoData = oSettings.aoData,
+ aoColumns = oSettings.aoColumns,
+ aDataSort, data, iCol, sType, oSort,
+ formatters = 0,
+ sortCol,
+ displayMaster = oSettings.aiDisplayMaster,
+ aSort;
+
+ // Resolve any column types that are unknown due to addition or invalidation
+ // @todo Can this be moved into a 'data-ready' handler which is called when
+ // data is going to be used in the table?
+ _fnColumnTypes(oSettings);
+
+ aSort = _fnSortFlatten(oSettings);
+
+ for (i = 0, ien = aSort.length; i < ien; i++) {
+ sortCol = aSort[i];
+
+ // Track if we can use the fast sort algorithm
+ if (sortCol.formatter) {
+ formatters++;
+ }
+
+ // Load the data needed for the sort, for each cell
+ _fnSortData(oSettings, sortCol.col);
+ }
+
+ /* No sorting required if server-side or no sorting array */
+ if (_fnDataSource(oSettings) != 'ssp' && aSort.length !== 0) {
+ // Create a value - key array of the current row positions such that we can use their
+ // current position during the sort, if values match, in order to perform stable sorting
+ for (i = 0, iLen = displayMaster.length; i < iLen; i++) {
+ aiOrig[displayMaster[i]] = i;
+ }
+
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
+ * follow on it's own, but this is what we want (example two column sorting):
+ * fnLocalSorting = function(a,b){
+ * var iTest;
+ * iTest = oSort['string-asc']('data11', 'data12');
+ * if (iTest !== 0)
+ * return iTest;
+ * iTest = oSort['numeric-desc']('data21', 'data22');
+ * if (iTest !== 0)
+ * return iTest;
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+ * }
+ * Basically we have a test for each sorting column, if the data in that column is equal,
+ * test the next column. If all columns match, then we use a numeric sort on the row
+ * positions in the original data array to provide a stable sort.
+ *
+ * Note - I know it seems excessive to have two sorting methods, but the first is around
+ * 15% faster, so the second is only maintained for backwards compatibility with sorting
+ * methods which do not have a pre-sort formatting function.
+ */
+ if (formatters === aSort.length) {
+ // All sort types have formatting functions
+ displayMaster.sort(function (a, b) {
+ var
+ x, y, k, test, sort,
+ len = aSort.length,
+ dataA = aoData[a]._aSortData,
+ dataB = aoData[b]._aSortData;
+
+ for (k = 0; k < len; k++) {
+ sort = aSort[k];
+
+ x = dataA[sort.col];
+ y = dataB[sort.col];
+
+ test = x < y ? -1 : x > y ? 1 : 0;
+ if (test !== 0) {
+ return sort.dir === 'asc' ? test : -test;
+ }
+ }
+
+ x = aiOrig[a];
+ y = aiOrig[b];
+ return x < y ? -1 : x > y ? 1 : 0;
+ });
+ } else {
+ // Depreciated - remove in 1.11 (providing a plug-in option)
+ // Not all sort types have formatting methods, so we have to call their sorting
+ // methods.
+ displayMaster.sort(function (a, b) {
+ var
+ x, y, k, l, test, sort, fn,
+ len = aSort.length,
+ dataA = aoData[a]._aSortData,
+ dataB = aoData[b]._aSortData;
+
+ for (k = 0; k < len; k++) {
+ sort = aSort[k];
+
+ x = dataA[sort.col];
+ y = dataB[sort.col];
+
+ fn = oExtSort[sort.type + "-" + sort.dir] || oExtSort["string-" + sort.dir];
+ test = fn(x, y);
+ if (test !== 0) {
+ return test;
+ }
+ }
+
+ x = aiOrig[a];
+ y = aiOrig[b];
+ return x < y ? -1 : x > y ? 1 : 0;
+ });
+ }
+ }
+
+ /* Tell the draw function that we have sorted the data */
+ oSettings.bSorted = true;
+ }
+
+
+ function _fnSortAria(settings) {
+ var label;
+ var nextSort;
+ var columns = settings.aoColumns;
+ var aSort = _fnSortFlatten(settings);
+ var oAria = settings.oLanguage.oAria;
+
+ // ARIA attributes - need to loop all columns, to update all (removing old
+ // attributes as needed)
+ for (var i = 0, iLen = columns.length; i < iLen; i++) {
+ var col = columns[i];
+ var asSorting = col.asSorting;
+ var sTitle = col.sTitle.replace(/<.*?>/g, "");
+ var th = col.nTh;
+
+ // IE7 is throwing an error when setting these properties with jQuery's
+ // attr() and removeAttr() methods...
+ th.removeAttribute('aria-sort');
+
+ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
+ if (col.bSortable) {
+ if (aSort.length > 0 && aSort[0].col == i) {
+ th.setAttribute('aria-sort', aSort[0].dir == "asc" ? "ascending" : "descending");
+ nextSort = asSorting[aSort[0].index + 1] || asSorting[0];
+ } else {
+ nextSort = asSorting[0];
+ }
+
+ label = sTitle + (nextSort === "asc" ?
+ oAria.sSortAscending :
+ oAria.sSortDescending
+ );
+ } else {
+ label = sTitle;
+ }
+
+ th.setAttribute('aria-label', label);
+ }
+ }
+
+
+ /**
+ * Function to run on user sort request
+ * @param {object} settings dataTables settings object
+ * @param {node} attachTo node to attach the handler to
+ * @param {int} colIdx column sorting index
+ * @param {boolean} [append=false] Append the requested sort to the existing
+ * sort if true (i.e. multi-column sort)
+ * @param {function} [callback] callback function
+ * @memberof DataTable#oApi
+ */
+ function _fnSortListener(settings, colIdx, append, callback) {
+ var col = settings.aoColumns[colIdx];
+ var sorting = settings.aaSorting;
+ var asSorting = col.asSorting;
+ var nextSortIdx;
+ var next = function (a, overflow) {
+ var idx = a._idx;
+ if (idx === undefined) {
+ idx = $.inArray(a[1], asSorting);
+ }
+
+ return idx + 1 < asSorting.length ?
+ idx + 1 :
+ overflow ?
+ null :
+ 0;
+ };
+
+ // Convert to 2D array if needed
+ if (typeof sorting[0] === 'number') {
+ sorting = settings.aaSorting = [sorting];
+ }
+
+ // If appending the sort then we are multi-column sorting
+ if (append && settings.oFeatures.bSortMulti) {
+ // Are we already doing some kind of sort on this column?
+ var sortIdx = $.inArray(colIdx, _pluck(sorting, '0'));
+
+ if (sortIdx !== -1) {
+ // Yes, modify the sort
+ nextSortIdx = next(sorting[sortIdx], true);
+
+ if (nextSortIdx === null && sorting.length === 1) {
+ nextSortIdx = 0; // can't remove sorting completely
+ }
+
+ if (nextSortIdx === null) {
+ sorting.splice(sortIdx, 1);
+ } else {
+ sorting[sortIdx][1] = asSorting[nextSortIdx];
+ sorting[sortIdx]._idx = nextSortIdx;
+ }
+ } else {
+ // No sort on this column yet
+ sorting.push([colIdx, asSorting[0], 0]);
+ sorting[sorting.length - 1]._idx = 0;
+ }
+ } else if (sorting.length && sorting[0][0] == colIdx) {
+ // Single column - already sorting on this column, modify the sort
+ nextSortIdx = next(sorting[0]);
+
+ sorting.length = 1;
+ sorting[0][1] = asSorting[nextSortIdx];
+ sorting[0]._idx = nextSortIdx;
+ } else {
+ // Single column - sort only on this column
+ sorting.length = 0;
+ sorting.push([colIdx, asSorting[0]]);
+ sorting[0]._idx = 0;
+ }
+
+ // Run the sort by calling a full redraw
+ _fnReDraw(settings);
+
+ // callback used for async user interaction
+ if (typeof callback == 'function') {
+ callback(settings);
+ }
+ }
+
+
+ /**
+ * Attach a sort handler (click) to a node
+ * @param {object} settings dataTables settings object
+ * @param {node} attachTo node to attach the handler to
+ * @param {int} colIdx column sorting index
+ * @param {function} [callback] callback function
+ * @memberof DataTable#oApi
+ */
+ function _fnSortAttachListener(settings, attachTo, colIdx, callback) {
+ var col = settings.aoColumns[colIdx];
+
+ _fnBindAction(attachTo, {}, function (e) {
+ /* If the column is not sortable - don't to anything */
+ if (col.bSortable === false) {
+ return;
+ }
+
+ // If processing is enabled use a timeout to allow the processing
+ // display to be shown - otherwise to it synchronously
+ if (settings.oFeatures.bProcessing) {
+ _fnProcessingDisplay(settings, true);
+
+ setTimeout(function () {
+ _fnSortListener(settings, colIdx, e.shiftKey, callback);
+
+ // In server-side processing, the draw callback will remove the
+ // processing display
+ if (_fnDataSource(settings) !== 'ssp') {
+ _fnProcessingDisplay(settings, false);
+ }
+ }, 0);
+ } else {
+ _fnSortListener(settings, colIdx, e.shiftKey, callback);
+ }
+ });
+ }
+
+
+ /**
+ * Set the sorting classes on table's body, Note: it is safe to call this function
+ * when bSort and bSortClasses are false
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnSortingClasses(settings) {
+ var oldSort = settings.aLastSort;
+ var sortClass = settings.oClasses.sSortColumn;
+ var sort = _fnSortFlatten(settings);
+ var features = settings.oFeatures;
+ var i, ien, colIdx;
+
+ if (features.bSort && features.bSortClasses) {
+ // Remove old sorting classes
+ for (i = 0, ien = oldSort.length; i < ien; i++) {
+ colIdx = oldSort[i].src;
+
+ // Remove column sorting
+ $(_pluck(settings.aoData, 'anCells', colIdx))
+ .removeClass(sortClass + (i < 2 ? i + 1 : 3));
+ }
+
+ // Add new column sorting
+ for (i = 0, ien = sort.length; i < ien; i++) {
+ colIdx = sort[i].src;
+
+ $(_pluck(settings.aoData, 'anCells', colIdx))
+ .addClass(sortClass + (i < 2 ? i + 1 : 3));
+ }
+ }
+
+ settings.aLastSort = sort;
+ }
+
+
+ // Get the data to sort a column, be it from cache, fresh (populating the
+ // cache), or from a sort formatter
+ function _fnSortData(settings, idx) {
+ // Custom sorting function - provided by the sort data type
+ var column = settings.aoColumns[idx];
+ var customSort = DataTable.ext.order[column.sSortDataType];
+ var customData;
+
+ if (customSort) {
+ customData = customSort.call(settings.oInstance, settings, idx,
+ _fnColumnIndexToVisible(settings, idx)
+ );
+ }
+
+ // Use / populate cache
+ var row, cellData;
+ var formatter = DataTable.ext.type.order[column.sType + "-pre"];
+
+ for (var i = 0, ien = settings.aoData.length; i < ien; i++) {
+ row = settings.aoData[i];
+
+ if (!row._aSortData) {
+ row._aSortData = [];
+ }
+
+ if (!row._aSortData[idx] || customSort) {
+ cellData = customSort ?
+ customData[i] : // If there was a custom sort function, use data from there
+ _fnGetCellData(settings, i, idx, 'sort');
+
+ row._aSortData[idx] = formatter ?
+ formatter(cellData) :
+ cellData;
+ }
+ }
+ }
+
+
+ /**
+ * Save the state of a table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnSaveState(settings) {
+ if (!settings.oFeatures.bStateSave || settings.bDestroying) {
+ return;
+ }
+
+ /* Store the interesting variables */
+ var state = {
+ time: +new Date(),
+ start: settings._iDisplayStart,
+ length: settings._iDisplayLength,
+ order: $.extend(true, [], settings.aaSorting),
+ search: _fnSearchToCamel(settings.oPreviousSearch),
+ columns: $.map(settings.aoColumns, function (col, i) {
+ return {
+ visible: col.bVisible,
+ search: _fnSearchToCamel(settings.aoPreSearchCols[i])
+ };
+ })
+ };
+
+ _fnCallbackFire(settings, "aoStateSaveParams", 'stateSaveParams', [settings, state]);
+
+ settings.oSavedState = state;
+ settings.fnStateSaveCallback.call(settings.oInstance, settings, state);
+ }
+
+
+ /**
+ * Attempt to load a saved table state
+ * @param {object} oSettings dataTables settings object
+ * @param {object} oInit DataTables init object so we can override settings
+ * @memberof DataTable#oApi
+ */
+ function _fnLoadState(settings, oInit) {
+ var i, ien;
+ var columns = settings.aoColumns;
+
+ if (!settings.oFeatures.bStateSave) {
+ return;
+ }
+
+ var state = settings.fnStateLoadCallback.call(settings.oInstance, settings);
+ if (!state || !state.time) {
+ return;
+ }
+
+ /* Allow custom and plug-in manipulation functions to alter the saved data set and
+ * cancelling of loading by returning false
+ */
+ var abStateLoad = _fnCallbackFire(settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state]);
+ if ($.inArray(false, abStateLoad) !== -1) {
+ return;
+ }
+
+ /* Reject old data */
+ var duration = settings.iStateDuration;
+ if (duration > 0 && state.time < +new Date() - (duration * 1000)) {
+ return;
+ }
+
+ // Number of columns have changed - all bets are off, no restore of settings
+ if (columns.length !== state.columns.length) {
+ return;
+ }
+
+ // Store the saved state so it might be accessed at any time
+ settings.oLoadedState = $.extend(true, {}, state);
+
+ // Restore key features - todo - for 1.11 this needs to be done by
+ // subscribed events
+ if (state.start !== undefined) {
+ settings._iDisplayStart = state.start;
+ settings.iInitDisplayStart = state.start;
+ }
+ if (state.length !== undefined) {
+ settings._iDisplayLength = state.length;
+ }
+
+ // Order
+ if (state.order !== undefined) {
+ settings.aaSorting = [];
+ $.each(state.order, function (i, col) {
+ settings.aaSorting.push(col[0] >= columns.length ? [0, col[1]] :
+ col
+ );
+ });
+ }
+
+ // Search
+ if (state.search !== undefined) {
+ $.extend(settings.oPreviousSearch, _fnSearchToHung(state.search));
+ }
+
+ // Columns
+ for (i = 0, ien = state.columns.length; i < ien; i++) {
+ var col = state.columns[i];
+
+ // Visibility
+ if (col.visible !== undefined) {
+ columns[i].bVisible = col.visible;
+ }
+
+ // Search
+ if (col.search !== undefined) {
+ $.extend(settings.aoPreSearchCols[i], _fnSearchToHung(col.search));
+ }
+ }
+
+ _fnCallbackFire(settings, 'aoStateLoaded', 'stateLoaded', [settings, state]);
+ }
+
+
+ /**
+ * Return the settings object for a particular table
+ * @param {node} table table we are using as a dataTable
+ * @returns {object} Settings object - or null if not found
+ * @memberof DataTable#oApi
+ */
+ function _fnSettingsFromNode(table) {
+ var settings = DataTable.settings;
+ var idx = $.inArray(table, _pluck(settings, 'nTable'));
+
+ return idx !== -1 ?
+ settings[idx] :
+ null;
+ }
+
+
+ /**
+ * Log an error message
+ * @param {object} settings dataTables settings object
+ * @param {int} level log error messages, or display them to the user
+ * @param {string} msg error message
+ * @param {int} tn Technical note id to get more information about the error.
+ * @memberof DataTable#oApi
+ */
+ function _fnLog(settings, level, msg, tn) {
+ msg = 'DataTables warning: ' +
+ (settings ? 'table id=' + settings.sTableId + ' - ' : '') + msg;
+
+ if (tn) {
+ msg += '. For more information about this error, please see ' +
+ 'http://datatables.net/tn/' + tn;
+ }
+
+ if (!level) {
+ // Backwards compatibility pre 1.10
+ var ext = DataTable.ext;
+ var type = ext.sErrMode || ext.errMode;
+
+ if (settings) {
+ _fnCallbackFire(settings, null, 'error', [settings, tn, msg]);
+ }
+
+ if (type == 'alert') {
+ alert(msg);
+ } else if (type == 'throw') {
+ throw new Error(msg);
+ } else if (typeof type == 'function') {
+ type(settings, tn, msg);
+ }
+ } else if (window.console && console.log) {
+ console.log(msg);
+ }
+ }
+
+
+ /**
+ * See if a property is defined on one object, if so assign it to the other object
+ * @param {object} ret target object
+ * @param {object} src source object
+ * @param {string} name property
+ * @param {string} [mappedName] name to map too - optional, name used if not given
+ * @memberof DataTable#oApi
+ */
+ function _fnMap(ret, src, name, mappedName) {
+ if ($.isArray(name)) {
+ $.each(name, function (i, val) {
+ if ($.isArray(val)) {
+ _fnMap(ret, src, val[0], val[1]);
+ } else {
+ _fnMap(ret, src, val);
+ }
+ });
+
+ return;
+ }
+
+ if (mappedName === undefined) {
+ mappedName = name;
+ }
+
+ if (src[name] !== undefined) {
+ ret[mappedName] = src[name];
+ }
+ }
+
+
+ /**
+ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
+ * shallow copy arrays. The reason we need to do this, is that we don't want to
+ * deep copy array init values (such as aaSorting) since the dev wouldn't be
+ * able to override them, but we do want to deep copy arrays.
+ * @param {object} out Object to extend
+ * @param {object} extender Object from which the properties will be applied to
+ * out
+ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
+ * independent copy with the exception of the `data` or `aaData` parameters
+ * if they are present. This is so you can pass in a collection to
+ * DataTables and have that used as your data source without breaking the
+ * references
+ * @returns {object} out Reference, just for convenience - out === the return.
+ * @memberof DataTable#oApi
+ * @todo This doesn't take account of arrays inside the deep copied objects.
+ */
+ function _fnExtend(out, extender, breakRefs) {
+ var val;
+
+ for (var prop in extender) {
+ if (extender.hasOwnProperty(prop)) {
+ val = extender[prop];
+
+ if ($.isPlainObject(val)) {
+ if (!$.isPlainObject(out[prop])) {
+ out[prop] = {};
+ }
+ $.extend(true, out[prop], val);
+ } else if (breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val)) {
+ out[prop] = val.slice();
+ } else {
+ out[prop] = val;
+ }
+ }
+ }
+
+ return out;
+ }
+
+
+ /**
+ * Bind an event handers to allow a click or return key to activate the callback.
+ * This is good for accessibility since a return on the keyboard will have the
+ * same effect as a click, if the element has focus.
+ * @param {element} n Element to bind the action to
+ * @param {object} oData Data object to pass to the triggered function
+ * @param {function} fn Callback function for when the event is triggered
+ * @memberof DataTable#oApi
+ */
+ function _fnBindAction(n, oData, fn) {
+ $(n)
+ .bind('click.DT', oData, function (e) {
+ n.blur(); // Remove focus outline for mouse users
+ fn(e);
+ })
+ .bind('keypress.DT', oData, function (e) {
+ if (e.which === 13) {
+ e.preventDefault();
+ fn(e);
+ }
+ })
+ .bind('selectstart.DT', function () {
+ /* Take the brutal approach to cancelling text selection */
+ return false;
+ });
+ }
+
+
+ /**
+ * Register a callback function. Easily allows a callback function to be added to
+ * an array store of callback functions that can then all be called together.
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sStore Name of the array storage for the callbacks in oSettings
+ * @param {function} fn Function to be called back
+ * @param {string} sName Identifying name for the callback (i.e. a label)
+ * @memberof DataTable#oApi
+ */
+ function _fnCallbackReg(oSettings, sStore, fn, sName) {
+ if (fn) {
+ oSettings[sStore].push({
+ "fn": fn,
+ "sName": sName
+ });
+ }
+ }
+
+
+ /**
+ * Fire callback functions and trigger events. Note that the loop over the
+ * callback array store is done backwards! Further note that you do not want to
+ * fire off triggers in time sensitive applications (for example cell creation)
+ * as its slow.
+ * @param {object} settings dataTables settings object
+ * @param {string} callbackArr Name of the array storage for the callbacks in
+ * oSettings
+ * @param {string} eventName Name of the jQuery custom event to trigger. If
+ * null no trigger is fired
+ * @param {array} args Array of arguments to pass to the callback function /
+ * trigger
+ * @memberof DataTable#oApi
+ */
+ function _fnCallbackFire(settings, callbackArr, eventName, args) {
+ var ret = [];
+
+ if (callbackArr) {
+ ret = $.map(settings[callbackArr].slice().reverse(), function (val, i) {
+ return val.fn.apply(settings.oInstance, args);
+ });
+ }
+
+ if (eventName !== null) {
+ var e = $.Event(eventName + '.dt');
+
+ $(settings.nTable).trigger(e, args);
+
+ ret.push(e.result);
+ }
+
+ return ret;
+ }
+
+
+ function _fnLengthOverflow(settings) {
+ var
+ start = settings._iDisplayStart,
+ end = settings.fnDisplayEnd(),
+ len = settings._iDisplayLength;
+
+ /* If we have space to show extra rows (backing up from the end point - then do so */
+ if (start >= end) {
+ start = end - len;
+ }
+
+ // Keep the start record on the current page
+ start -= (start % len);
+
+ if (len === -1 || start < 0) {
+ start = 0;
+ }
+
+ settings._iDisplayStart = start;
+ }
+
+
+ function _fnRenderer(settings, type) {
+ var renderer = settings.renderer;
+ var host = DataTable.ext.renderer[type];
+
+ if ($.isPlainObject(renderer) && renderer[type]) {
+ // Specific renderer for this type. If available use it, otherwise use
+ // the default.
+ return host[renderer[type]] || host._;
+ } else if (typeof renderer === 'string') {
+ // Common renderer - if there is one available for this type use it,
+ // otherwise use the default
+ return host[renderer] || host._;
+ }
+
+ // Use the default
+ return host._;
+ }
+
+
+ /**
+ * Detect the data source being used for the table. Used to simplify the code
+ * a little (ajax) and to make it compress a little smaller.
+ *
+ * @param {object} settings dataTables settings object
+ * @returns {string} Data source
+ * @memberof DataTable#oApi
+ */
+ function _fnDataSource(settings) {
+ if (settings.oFeatures.bServerSide) {
+ return 'ssp';
+ } else if (settings.ajax || settings.sAjaxSource) {
+ return 'ajax';
+ }
+ return 'dom';
+ }
+
+
+ /**
+ * Computed structure of the DataTables API, defined by the options passed to
+ * `DataTable.Api.register()` when building the API.
+ *
+ * The structure is built in order to speed creation and extension of the Api
+ * objects since the extensions are effectively pre-parsed.
+ *
+ * The array is an array of objects with the following structure, where this
+ * base array represents the Api prototype base:
+ *
+ * [
+ * {
+ * name: 'data' -- string - Property name
+ * val: function () {}, -- function - Api method (or undefined if just an object
+ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
+ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
+ * },
+ * {
+ * name: 'row'
+ * val: {},
+ * methodExt: [ ... ],
+ * propExt: [
+ * {
+ * name: 'data'
+ * val: function () {},
+ * methodExt: [ ... ],
+ * propExt: [ ... ]
+ * },
+ * ...
+ * ]
+ * }
+ * ]
+ *
+ * @type {Array}
+ * @ignore
+ */
+ var __apiStruct = [];
+
+
+ /**
+ * `Array.prototype` reference.
+ *
+ * @type object
+ * @ignore
+ */
+ var __arrayProto = Array.prototype;
+
+
+ /**
+ * Abstraction for `context` parameter of the `Api` constructor to allow it to
+ * take several different forms for ease of use.
+ *
+ * Each of the input parameter types will be converted to a DataTables settings
+ * object where possible.
+ *
+ * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
+ * of:
+ *
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
+ * with be found and used.
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
+ * * `object` - DataTables settings object
+ * * `DataTables.Api` - API instance
+ * @return {array|null} Matching DataTables settings objects. `null` or
+ * `undefined` is returned if no matching DataTable is found.
+ * @ignore
+ */
+ var _toSettings = function (mixed) {
+ var idx, jq;
+ var settings = DataTable.settings;
+ var tables = $.map(settings, function (el, i) {
+ return el.nTable;
+ });
+
+ if (!mixed) {
+ return [];
+ } else if (mixed.nTable && mixed.oApi) {
+ // DataTables settings object
+ return [mixed];
+ } else if (mixed.nodeName && mixed.nodeName.toLowerCase() === 'table') {
+ // Table node
+ idx = $.inArray(mixed, tables);
+ return idx !== -1 ? [settings[idx]] : null;
+ } else if (mixed && typeof mixed.settings === 'function') {
+ return mixed.settings().toArray();
+ } else if (typeof mixed === 'string') {
+ // jQuery selector
+ jq = $(mixed);
+ } else if (mixed instanceof $) {
+ // jQuery object (also DataTables instance)
+ jq = mixed;
+ }
+
+ if (jq) {
+ return jq.map(function (i) {
+ idx = $.inArray(this, tables);
+ return idx !== -1 ? settings[idx] : null;
+ }).toArray();
+ }
+ };
+
+
+ /**
+ * DataTables API class - used to control and interface with one or more
+ * DataTables enhanced tables.
+ *
+ * The API class is heavily based on jQuery, presenting a chainable interface
+ * that you can use to interact with tables. Each instance of the API class has
+ * a "context" - i.e. the tables that it will operate on. This could be a single
+ * table, all tables on a page or a sub-set thereof.
+ *
+ * Additionally the API is designed to allow you to easily work with the data in
+ * the tables, retrieving and manipulating it as required. This is done by
+ * presenting the API class as an array like interface. The contents of the
+ * array depend upon the actions requested by each method (for example
+ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
+ * return an array of objects or arrays depending upon your table's
+ * configuration). The API object has a number of array like methods (`push`,
+ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
+ * `unique` etc) to assist your working with the data held in a table.
+ *
+ * Most methods (those which return an Api instance) are chainable, which means
+ * the return from a method call also has all of the methods available that the
+ * top level object had. For example, these two calls are equivalent:
+ *
+ * // Not chained
+ * api.row.add( {...} );
+ * api.draw();
+ *
+ * // Chained
+ * api.row.add( {...} ).draw();
+ *
+ * @class DataTable.Api
+ * @param {array|object|string|jQuery} context DataTable identifier. This is
+ * used to define which DataTables enhanced tables this API will operate on.
+ * Can be one of:
+ *
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
+ * with be found and used.
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
+ * * `object` - DataTables settings object
+ * @param {array} [data] Data to initialise the Api instance with.
+ *
+ * @example
+ * // Direct initialisation during DataTables construction
+ * var api = $('#example').DataTable();
+ *
+ * @example
+ * // Initialisation using a DataTables jQuery object
+ * var api = $('#example').dataTable().api();
+ *
+ * @example
+ * // Initialisation as a constructor
+ * var api = new $.fn.DataTable.Api( 'table.dataTable' );
+ */
+ _Api = function (context, data) {
+ if (!(this instanceof _Api)) {
+ return new _Api(context, data);
+ }
+
+ var settings = [];
+ var ctxSettings = function (o) {
+ var a = _toSettings(o);
+ if (a) {
+ settings = settings.concat(a);
+ }
+ };
+
+ if ($.isArray(context)) {
+ for (var i = 0, ien = context.length; i < ien; i++) {
+ ctxSettings(context[i]);
+ }
+ } else {
+ ctxSettings(context);
+ }
+
+ // Remove duplicates
+ this.context = _unique(settings);
+
+ // Initial data
+ if (data) {
+ $.merge(this, data);
+ }
+
+ // selector
+ this.selector = {
+ rows: null,
+ cols: null,
+ opts: null
+ };
+
+ _Api.extend(this, this, __apiStruct);
+ };
+
+ DataTable.Api = _Api;
+
+ // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
+ // isPlainObject.
+ $.extend(_Api.prototype, {
+ any: function () {
+ return this.count() !== 0;
+ },
+
+
+ concat: __arrayProto.concat,
+
+
+ context: [], // array of table settings objects
+
+
+ count: function () {
+ return this.flatten().length;
+ },
+
+
+ each: function (fn) {
+ for (var i = 0, ien = this.length; i < ien; i++) {
+ fn.call(this, this[i], i, this);
+ }
+
+ return this;
+ },
+
+
+ eq: function (idx) {
+ var ctx = this.context;
+
+ return ctx.length > idx ?
+ new _Api(ctx[idx], this[idx]) :
+ null;
+ },
+
+
+ filter: function (fn) {
+ var a = [];
+
+ if (__arrayProto.filter) {
+ a = __arrayProto.filter.call(this, fn, this);
+ } else {
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
+ for (var i = 0, ien = this.length; i < ien; i++) {
+ if (fn.call(this, this[i], i, this)) {
+ a.push(this[i]);
+ }
+ }
+ }
+
+ return new _Api(this.context, a);
+ },
+
+
+ flatten: function () {
+ var a = [];
+ return new _Api(this.context, a.concat.apply(a, this.toArray()));
+ },
+
+
+ join: __arrayProto.join,
+
+
+ indexOf: __arrayProto.indexOf || function (obj, start) {
+ for (var i = (start || 0), ien = this.length; i < ien; i++) {
+ if (this[i] === obj) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ iterator: function (flatten, type, fn, alwaysNew) {
+ var
+ a = [],
+ ret,
+ i, ien, j, jen,
+ context = this.context,
+ rows, items, item,
+ selector = this.selector;
+
+ // Argument shifting
+ if (typeof flatten === 'string') {
+ alwaysNew = fn;
+ fn = type;
+ type = flatten;
+ flatten = false;
+ }
+
+ for (i = 0, ien = context.length; i < ien; i++) {
+ var apiInst = new _Api(context[i]);
+
+ if (type === 'table') {
+ ret = fn.call(apiInst, context[i], i);
+
+ if (ret !== undefined) {
+ a.push(ret);
+ }
+ } else if (type === 'columns' || type === 'rows') {
+ // this has same length as context - one entry for each table
+ ret = fn.call(apiInst, context[i], this[i], i);
+
+ if (ret !== undefined) {
+ a.push(ret);
+ }
+ } else if (type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell') {
+ // columns and rows share the same structure.
+ // 'this' is an array of column indexes for each context
+ items = this[i];
+
+ if (type === 'column-rows') {
+ rows = _selector_row_indexes(context[i], selector.opts);
+ }
+
+ for (j = 0, jen = items.length; j < jen; j++) {
+ item = items[j];
+
+ if (type === 'cell') {
+ ret = fn.call(apiInst, context[i], item.row, item.column, i, j);
+ } else {
+ ret = fn.call(apiInst, context[i], item, i, j, rows);
+ }
+
+ if (ret !== undefined) {
+ a.push(ret);
+ }
+ }
+ }
+ }
+
+ if (a.length || alwaysNew) {
+ var api = new _Api(context, flatten ? a.concat.apply([], a) : a);
+ var apiSelector = api.selector;
+ apiSelector.rows = selector.rows;
+ apiSelector.cols = selector.cols;
+ apiSelector.opts = selector.opts;
+ return api;
+ }
+ return this;
+ },
+
+
+ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start) {
+ // Bit cheeky...
+ return this.indexOf.apply(this.toArray.reverse(), arguments);
+ },
+
+
+ length: 0,
+
+
+ map: function (fn) {
+ var a = [];
+
+ if (__arrayProto.map) {
+ a = __arrayProto.map.call(this, fn, this);
+ } else {
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
+ for (var i = 0, ien = this.length; i < ien; i++) {
+ a.push(fn.call(this, this[i], i));
+ }
+ }
+
+ return new _Api(this.context, a);
+ },
+
+
+ pluck: function (prop) {
+ return this.map(function (el) {
+ return el[prop];
+ });
+ },
+
+ pop: __arrayProto.pop,
+
+
+ push: __arrayProto.push,
+
+
+ // Does not return an API instance
+ reduce: __arrayProto.reduce || function (fn, init) {
+ return _fnReduce(this, fn, init, 0, this.length, 1);
+ },
+
+
+ reduceRight: __arrayProto.reduceRight || function (fn, init) {
+ return _fnReduce(this, fn, init, this.length - 1, -1, -1);
+ },
+
+
+ reverse: __arrayProto.reverse,
+
+
+ // Object with rows, columns and opts
+ selector: null,
+
+
+ shift: __arrayProto.shift,
+
+
+ sort: __arrayProto.sort, // ? name - order?
+
+
+ splice: __arrayProto.splice,
+
+
+ toArray: function () {
+ return __arrayProto.slice.call(this);
+ },
+
+
+ to$: function () {
+ return $(this);
+ },
+
+
+ toJQuery: function () {
+ return $(this);
+ },
+
+
+ unique: function () {
+ return new _Api(this.context, _unique(this));
+ },
+
+
+ unshift: __arrayProto.unshift
+ });
+
+
+ _Api.extend = function (scope, obj, ext) {
+ // Only extend API instances and static properties of the API
+ if (!ext.length || !obj || (!(obj instanceof _Api) && !obj.__dt_wrapper)) {
+ return;
+ }
+
+ var
+ i, ien,
+ j, jen,
+ struct, inner,
+ methodScoping = function (scope, fn, struc) {
+ return function () {
+ var ret = fn.apply(scope, arguments);
+
+ // Method extension
+ _Api.extend(ret, ret, struc.methodExt);
+ return ret;
+ };
+ };
+
+ for (i = 0, ien = ext.length; i < ien; i++) {
+ struct = ext[i];
+
+ // Value
+ obj[struct.name] = typeof struct.val === 'function' ?
+ methodScoping(scope, struct.val, struct) :
+ $.isPlainObject(struct.val) ? {} :
+ struct.val;
+
+ obj[struct.name].__dt_wrapper = true;
+
+ // Property extension
+ _Api.extend(scope, obj[struct.name], struct.propExt);
+ }
+ };
+
+
+ // @todo - Is there need for an augment function?
+ // _Api.augment = function ( inst, name )
+ // {
+ // // Find src object in the structure from the name
+ // var parts = name.split('.');
+
+ // _Api.extend( inst, obj );
+ // };
+
+
+ // [
+ // {
+ // name: 'data' -- string - Property name
+ // val: function () {}, -- function - Api method (or undefined if just an object
+ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
+ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
+ // },
+ // {
+ // name: 'row'
+ // val: {},
+ // methodExt: [ ... ],
+ // propExt: [
+ // {
+ // name: 'data'
+ // val: function () {},
+ // methodExt: [ ... ],
+ // propExt: [ ... ]
+ // },
+ // ...
+ // ]
+ // }
+ // ]
+
+ _Api.register = _api_register = function (name, val) {
+ if ($.isArray(name)) {
+ for (var j = 0, jen = name.length; j < jen; j++) {
+ _Api.register(name[j], val);
+ }
+ return;
+ }
+
+ var
+ i, ien,
+ heir = name.split('.'),
+ struct = __apiStruct,
+ key, method;
+
+ var find = function (src, name) {
+ for (var i = 0, ien = src.length; i < ien; i++) {
+ if (src[i].name === name) {
+ return src[i];
+ }
+ }
+ return null;
+ };
+
+ for (i = 0, ien = heir.length; i < ien; i++) {
+ method = heir[i].indexOf('()') !== -1;
+ key = method ?
+ heir[i].replace('()', '') :
+ heir[i];
+
+ var src = find(struct, key);
+ if (!src) {
+ src = {
+ name: key,
+ val: {},
+ methodExt: [],
+ propExt: []
+ };
+ struct.push(src);
+ }
+
+ if (i === ien - 1) {
+ src.val = val;
+ } else {
+ struct = method ?
+ src.methodExt :
+ src.propExt;
+ }
+ }
+ };
+
+
+ _Api.registerPlural = _api_registerPlural = function (pluralName, singularName, val) {
+ _Api.register(pluralName, val);
+
+ _Api.register(singularName, function () {
+ var ret = val.apply(this, arguments);
+
+ if (ret === this) {
+ // Returned item is the API instance that was passed in, return it
+ return this;
+ } else if (ret instanceof _Api) {
+ // New API instance returned, want the value from the first item
+ // in the returned array for the singular result.
+ return ret.length ?
+ $.isArray(ret[0]) ?
+ new _Api(ret.context, ret[0]) : // Array results are 'enhanced'
+ ret[0] :
+ undefined;
+ }
+
+ // Non-API return - just fire it back
+ return ret;
+ });
+ };
+
+
+ /**
+ * Selector for HTML tables. Apply the given selector to the give array of
+ * DataTables settings objects.
+ *
+ * @param {string|integer} [selector] jQuery selector string or integer
+ * @param {array} Array of DataTables settings objects to be filtered
+ * @return {array}
+ * @ignore
+ */
+ var __table_selector = function (selector, a) {
+ // Integer is used to pick out a table by index
+ if (typeof selector === 'number') {
+ return [a[selector]];
+ }
+
+ // Perform a jQuery selector on the table nodes
+ var nodes = $.map(a, function (el, i) {
+ return el.nTable;
+ });
+
+ return $(nodes)
+ .filter(selector)
+ .map(function (i) {
+ // Need to translate back from the table node to the settings
+ var idx = $.inArray(this, nodes);
+ return a[idx];
+ })
+ .toArray();
+ };
+
+
+ /**
+ * Context selector for the API's context (i.e. the tables the API instance
+ * refers to.
+ *
+ * @name DataTable.Api#tables
+ * @param {string|integer} [selector] Selector to pick which tables the iterator
+ * should operate on. If not given, all tables in the current context are
+ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
+ * select multiple tables or as an integer to select a single table.
+ * @returns {DataTable.Api} Returns a new API instance if a selector is given.
+ */
+ _api_register('tables()', function (selector) {
+ // A new instance is created if there was a selector specified
+ return selector ?
+ new _Api(__table_selector(selector, this.context)) :
+ this;
+ });
+
+
+ _api_register('table()', function (selector) {
+ var tables = this.tables(selector);
+ var ctx = tables.context;
+
+ // Truncate to the first matched table
+ return ctx.length ?
+ new _Api(ctx[0]) :
+ tables;
+ });
+
+
+ _api_registerPlural('tables().nodes()', 'table().node()', function () {
+ return this.iterator('table', function (ctx) {
+ return ctx.nTable;
+ }, 1);
+ });
+
+
+ _api_registerPlural('tables().body()', 'table().body()', function () {
+ return this.iterator('table', function (ctx) {
+ return ctx.nTBody;
+ }, 1);
+ });
+
+
+ _api_registerPlural('tables().header()', 'table().header()', function () {
+ return this.iterator('table', function (ctx) {
+ return ctx.nTHead;
+ }, 1);
+ });
+
+
+ _api_registerPlural('tables().footer()', 'table().footer()', function () {
+ return this.iterator('table', function (ctx) {
+ return ctx.nTFoot;
+ }, 1);
+ });
+
+
+ _api_registerPlural('tables().containers()', 'table().container()', function () {
+ return this.iterator('table', function (ctx) {
+ return ctx.nTableWrapper;
+ }, 1);
+ });
+
+
+ /**
+ * Redraw the tables in the current context.
+ */
+ _api_register('draw()', function (paging) {
+ return this.iterator('table', function (settings) {
+ if (paging === 'page') {
+ _fnDraw(settings);
+ } else {
+ if (typeof paging === 'string') {
+ paging = paging === 'full-hold' ?
+ false :
+ true;
+ }
+
+ _fnReDraw(settings, paging === false);
+ }
+ });
+ });
+
+
+ /**
+ * Get the current page index.
+ *
+ * @return {integer} Current page index (zero based)
+ */
+ /**
+ * Set the current page.
+ *
+ * Note that if you attempt to show a page which does not exist, DataTables will
+ * not throw an error, but rather reset the paging.
+ *
+ * @param {integer|string} action The paging action to take. This can be one of:
+ * * `integer` - The page index to jump to
+ * * `string` - An action to take:
+ * * `first` - Jump to first page.
+ * * `next` - Jump to the next page
+ * * `previous` - Jump to previous page
+ * * `last` - Jump to the last page.
+ * @returns {DataTables.Api} this
+ */
+ _api_register('page()', function (action) {
+ if (action === undefined) {
+ return this.page.info().page; // not an expensive call
+ }
+
+ // else, have an action to take on all tables
+ return this.iterator('table', function (settings) {
+ _fnPageChange(settings, action);
+ });
+ });
+
+
+ /**
+ * Paging information for the first table in the current context.
+ *
+ * If you require paging information for another table, use the `table()` method
+ * with a suitable selector.
+ *
+ * @return {object} Object with the following properties set:
+ * * `page` - Current page index (zero based - i.e. the first page is `0`)
+ * * `pages` - Total number of pages
+ * * `start` - Display index for the first record shown on the current page
+ * * `end` - Display index for the last record shown on the current page
+ * * `length` - Display length (number of records). Note that generally `start
+ * + length = end`, but this is not always true, for example if there are
+ * only 2 records to show on the final page, with a length of 10.
+ * * `recordsTotal` - Full data set length
+ * * `recordsDisplay` - Data set length once the current filtering criterion
+ * are applied.
+ */
+ _api_register('page.info()', function (action) {
+ if (this.context.length === 0) {
+ return undefined;
+ }
+
+ var
+ settings = this.context[0],
+ start = settings._iDisplayStart,
+ len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
+ visRecords = settings.fnRecordsDisplay(),
+ all = len === -1;
+
+ return {
+ "page": all ? 0 : Math.floor(start / len),
+ "pages": all ? 1 : Math.ceil(visRecords / len),
+ "start": start,
+ "end": settings.fnDisplayEnd(),
+ "length": len,
+ "recordsTotal": settings.fnRecordsTotal(),
+ "recordsDisplay": visRecords,
+ "serverSide": _fnDataSource(settings) === 'ssp'
+ };
+ });
+
+
+ /**
+ * Get the current page length.
+ *
+ * @return {integer} Current page length. Note `-1` indicates that all records
+ * are to be shown.
+ */
+ /**
+ * Set the current page length.
+ *
+ * @param {integer} Page length to set. Use `-1` to show all records.
+ * @returns {DataTables.Api} this
+ */
+ _api_register('page.len()', function (len) {
+ // Note that we can't call this function 'length()' because `length`
+ // is a Javascript property of functions which defines how many arguments
+ // the function expects.
+ if (len === undefined) {
+ return this.context.length !== 0 ?
+ this.context[0]._iDisplayLength :
+ undefined;
+ }
+
+ // else, set the page length
+ return this.iterator('table', function (settings) {
+ _fnLengthChange(settings, len);
+ });
+ });
+
+
+ var __reload = function (settings, holdPosition, callback) {
+ // Use the draw event to trigger a callback
+ if (callback) {
+ var api = new _Api(settings);
+
+ api.one('draw', function () {
+ callback(api.ajax.json());
+ });
+ }
+
+ if (_fnDataSource(settings) == 'ssp') {
+ _fnReDraw(settings, holdPosition);
+ } else {
+ _fnProcessingDisplay(settings, true);
+
+ // Cancel an existing request
+ var xhr = settings.jqXHR;
+ if (xhr && xhr.readyState !== 4) {
+ xhr.abort();
+ }
+
+ // Trigger xhr
+ _fnBuildAjax(settings, [], function (json) {
+ _fnClearTable(settings);
+
+ var data = _fnAjaxDataSrc(settings, json);
+ for (var i = 0, ien = data.length; i < ien; i++) {
+ _fnAddData(settings, data[i]);
+ }
+
+ _fnReDraw(settings, holdPosition);
+ _fnProcessingDisplay(settings, false);
+ });
+ }
+ };
+
+
+ /**
+ * Get the JSON response from the last Ajax request that DataTables made to the
+ * server. Note that this returns the JSON from the first table in the current
+ * context.
+ *
+ * @return {object} JSON received from the server.
+ */
+ _api_register('ajax.json()', function () {
+ var ctx = this.context;
+
+ if (ctx.length > 0) {
+ return ctx[0].json;
+ }
+
+ // else return undefined;
+ });
+
+
+ /**
+ * Get the data submitted in the last Ajax request
+ */
+ _api_register('ajax.params()', function () {
+ var ctx = this.context;
+
+ if (ctx.length > 0) {
+ return ctx[0].oAjaxData;
+ }
+
+ // else return undefined;
+ });
+
+
+ /**
+ * Reload tables from the Ajax data source. Note that this function will
+ * automatically re-draw the table when the remote data has been loaded.
+ *
+ * @param {boolean} [reset=true] Reset (default) or hold the current paging
+ * position. A full re-sort and re-filter is performed when this method is
+ * called, which is why the pagination reset is the default action.
+ * @returns {DataTables.Api} this
+ */
+ _api_register('ajax.reload()', function (callback, resetPaging) {
+ return this.iterator('table', function (settings) {
+ __reload(settings, resetPaging === false, callback);
+ });
+ });
+
+
+ /**
+ * Get the current Ajax URL. Note that this returns the URL from the first
+ * table in the current context.
+ *
+ * @return {string} Current Ajax source URL
+ */
+ /**
+ * Set the Ajax URL. Note that this will set the URL for all tables in the
+ * current context.
+ *
+ * @param {string} url URL to set.
+ * @returns {DataTables.Api} this
+ */
+ _api_register('ajax.url()', function (url) {
+ var ctx = this.context;
+
+ if (url === undefined) {
+ // get
+ if (ctx.length === 0) {
+ return undefined;
+ }
+ ctx = ctx[0];
+
+ return ctx.ajax ?
+ $.isPlainObject(ctx.ajax) ?
+ ctx.ajax.url :
+ ctx.ajax :
+ ctx.sAjaxSource;
+ }
+
+ // set
+ return this.iterator('table', function (settings) {
+ if ($.isPlainObject(settings.ajax)) {
+ settings.ajax.url = url;
+ } else {
+ settings.ajax = url;
+ }
+ // No need to consider sAjaxSource here since DataTables gives priority
+ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
+ // value of `sAjaxSource` redundant.
+ });
+ });
+
+
+ /**
+ * Load data from the newly set Ajax URL. Note that this method is only
+ * available when `ajax.url()` is used to set a URL. Additionally, this method
+ * has the same effect as calling `ajax.reload()` but is provided for
+ * convenience when setting a new URL. Like `ajax.reload()` it will
+ * automatically redraw the table once the remote data has been loaded.
+ *
+ * @returns {DataTables.Api} this
+ */
+ _api_register('ajax.url().load()', function (callback, resetPaging) {
+ // Same as a reload, but makes sense to present it for easy access after a
+ // url change
+ return this.iterator('table', function (ctx) {
+ __reload(ctx, resetPaging === false, callback);
+ });
+ });
+
+
+ var _selector_run = function (type, selector, selectFn, settings, opts) {
+ var
+ out = [],
+ res,
+ a, i, ien, j, jen,
+ selectorType = typeof selector;
+
+ // Can't just check for isArray here, as an API or jQuery instance might be
+ // given with their array like look
+ if (!selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined) {
+ selector = [selector];
+ }
+
+ for (i = 0, ien = selector.length; i < ien; i++) {
+ a = selector[i] && selector[i].split ?
+ selector[i].split(',') : [selector[i]];
+
+ for (j = 0, jen = a.length; j < jen; j++) {
+ res = selectFn(typeof a[j] === 'string' ? $.trim(a[j]) : a[j]);
+
+ if (res && res.length) {
+ out = out.concat(res);
+ }
+ }
+ }
+
+ // selector extensions
+ var ext = _ext.selector[type];
+ if (ext.length) {
+ for (i = 0, ien = ext.length; i < ien; i++) {
+ out = ext[i](settings, opts, out);
+ }
+ }
+
+ return _unique(out);
+ };
+
+
+ var _selector_opts = function (opts) {
+ if (!opts) {
+ opts = {};
+ }
+
+ // Backwards compatibility for 1.9- which used the terminology filter rather
+ // than search
+ if (opts.filter && opts.search === undefined) {
+ opts.search = opts.filter;
+ }
+
+ return $.extend({
+ search: 'none',
+ order: 'current',
+ page: 'all'
+ }, opts);
+ };
+
+
+ var _selector_first = function (inst) {
+ // Reduce the API instance to the first item found
+ for (var i = 0, ien = inst.length; i < ien; i++) {
+ if (inst[i].length > 0) {
+ // Assign the first element to the first item in the instance
+ // and truncate the instance and context
+ inst[0] = inst[i];
+ inst[0].length = 1;
+ inst.length = 1;
+ inst.context = [inst.context[i]];
+
+ return inst;
+ }
+ }
+
+ // Not found - return an empty instance
+ inst.length = 0;
+ return inst;
+ };
+
+
+ var _selector_row_indexes = function (settings, opts) {
+ var
+ i, ien, tmp, a = [],
+ displayFiltered = settings.aiDisplay,
+ displayMaster = settings.aiDisplayMaster;
+
+ var
+ search = opts.search, // none, applied, removed
+ order = opts.order, // applied, current, index (original - compatibility with 1.9)
+ page = opts.page; // all, current
+
+ if (_fnDataSource(settings) == 'ssp') {
+ // In server-side processing mode, most options are irrelevant since
+ // rows not shown don't exist and the index order is the applied order
+ // Removed is a special case - for consistency just return an empty
+ // array
+ return search === 'removed' ? [] :
+ _range(0, displayMaster.length);
+ } else if (page == 'current') {
+ // Current page implies that order=current and fitler=applied, since it is
+ // fairly senseless otherwise, regardless of what order and search actually
+ // are
+ for (i = settings._iDisplayStart, ien = settings.fnDisplayEnd(); i < ien; i++) {
+ a.push(displayFiltered[i]);
+ }
+ } else if (order == 'current' || order == 'applied') {
+ a = search == 'none' ?
+ displayMaster.slice() : // no search
+ search == 'applied' ?
+ displayFiltered.slice() : // applied search
+ $.map(displayMaster, function (el, i) { // removed search
+ return $.inArray(el, displayFiltered) === -1 ? el : null;
+ });
+ } else if (order == 'index' || order == 'original') {
+ for (i = 0, ien = settings.aoData.length; i < ien; i++) {
+ if (search == 'none') {
+ a.push(i);
+ } else { // applied | removed
+ tmp = $.inArray(i, displayFiltered);
+
+ if ((tmp === -1 && search == 'removed') ||
+ (tmp >= 0 && search == 'applied')) {
+ a.push(i);
+ }
+ }
+ }
+ }
+
+ return a;
+ };
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Rows
+ *
+ * {} - no selector - use all available rows
+ * {integer} - row aoData index
+ * {node} - TR node
+ * {string} - jQuery selector to apply to the TR elements
+ * {array} - jQuery array of nodes, or simply an array of TR nodes
+ *
+ */
+
+
+ var __row_selector = function (settings, selector, opts) {
+ var run = function (sel) {
+ var selInt = _intVal(sel);
+ var i, ien;
+
+ // Short cut - selector is a number and no options provided (default is
+ // all records, so no need to check if the index is in there, since it
+ // must be - dev error if the index doesn't exist).
+ if (selInt !== null && !opts) {
+ return [selInt];
+ }
+
+ var rows = _selector_row_indexes(settings, opts);
+
+ if (selInt !== null && $.inArray(selInt, rows) !== -1) {
+ // Selector - integer
+ return [selInt];
+ } else if (!sel) {
+ // Selector - none
+ return rows;
+ }
+
+ // Selector - function
+ if (typeof sel === 'function') {
+ return $.map(rows, function (idx) {
+ var row = settings.aoData[idx];
+ return sel(idx, row._aData, row.nTr) ? idx : null;
+ });
+ }
+
+ // Get nodes in the order from the `rows` array with null values removed
+ var nodes = _removeEmpty(
+ _pluck_order(settings.aoData, rows, 'nTr')
+ );
+
+ // Selector - node
+ if (sel.nodeName) {
+ if (sel._DT_RowIndex !== undefined) {
+ return [sel._DT_RowIndex]; // Property added by DT for fast lookup
+ } else if (sel._DT_CellIndex) {
+ return [sel._DT_CellIndex.row];
+ } else {
+ var host = $(sel).closest('*[data-dt-row]');
+ return host.length ? [host.data('dt-row')] : [];
+ }
+ }
+
+ // ID selector. Want to always be able to select rows by id, regardless
+ // of if the tr element has been created or not, so can't rely upon
+ // jQuery here - hence a custom implementation. This does not match
+ // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
+ // but to select it using a CSS selector engine (like Sizzle or
+ // querySelect) it would need to need to be escaped for some characters.
+ // DataTables simplifies this for row selectors since you can select
+ // only a row. A # indicates an id any anything that follows is the id -
+ // unescaped.
+ if (typeof sel === 'string' && sel.charAt(0) === '#') {
+ // get row index from id
+ var rowObj = settings.aIds[sel.replace(/^#/, '')];
+ if (rowObj !== undefined) {
+ return [rowObj.idx];
+ }
+
+ // need to fall through to jQuery in case there is DOM id that
+ // matches
+ }
+
+ // Selector - jQuery selector string, array of nodes or jQuery object/
+ // As jQuery's .filter() allows jQuery objects to be passed in filter,
+ // it also allows arrays, so this will cope with all three options
+ return $(nodes)
+ .filter(sel)
+ .map(function () {
+ return this._DT_RowIndex;
+ })
+ .toArray();
+ };
+
+ return _selector_run('row', selector, run, settings, opts);
+ };
+
+
+ _api_register('rows()', function (selector, opts) {
+ // argument shifting
+ if (selector === undefined) {
+ selector = '';
+ } else if ($.isPlainObject(selector)) {
+ opts = selector;
+ selector = '';
+ }
+
+ opts = _selector_opts(opts);
+
+ var inst = this.iterator('table', function (settings) {
+ return __row_selector(settings, selector, opts);
+ }, 1);
+
+ // Want argument shifting here and in __row_selector?
+ inst.selector.rows = selector;
+ inst.selector.opts = opts;
+
+ return inst;
+ });
+
+ _api_register('rows().nodes()', function () {
+ return this.iterator('row', function (settings, row) {
+ return settings.aoData[row].nTr || undefined;
+ }, 1);
+ });
+
+ _api_register('rows().data()', function () {
+ return this.iterator(true, 'rows', function (settings, rows) {
+ return _pluck_order(settings.aoData, rows, '_aData');
+ }, 1);
+ });
+
+ _api_registerPlural('rows().cache()', 'row().cache()', function (type) {
+ return this.iterator('row', function (settings, row) {
+ var r = settings.aoData[row];
+ return type === 'search' ? r._aFilterData : r._aSortData;
+ }, 1);
+ });
+
+ _api_registerPlural('rows().invalidate()', 'row().invalidate()', function (src) {
+ return this.iterator('row', function (settings, row) {
+ _fnInvalidate(settings, row, src);
+ });
+ });
+
+ _api_registerPlural('rows().indexes()', 'row().index()', function () {
+ return this.iterator('row', function (settings, row) {
+ return row;
+ }, 1);
+ });
+
+ _api_registerPlural('rows().ids()', 'row().id()', function (hash) {
+ var a = [];
+ var context = this.context;
+
+ // `iterator` will drop undefined values, but in this case we want them
+ for (var i = 0, ien = context.length; i < ien; i++) {
+ for (var j = 0, jen = this[i].length; j < jen; j++) {
+ var id = context[i].rowIdFn(context[i].aoData[this[i][j]]._aData);
+ a.push((hash === true ? '#' : '') + id);
+ }
+ }
+
+ return new _Api(context, a);
+ });
+
+ _api_registerPlural('rows().remove()', 'row().remove()', function () {
+ var that = this;
+
+ this.iterator('row', function (settings, row, thatIdx) {
+ var data = settings.aoData;
+ var rowData = data[row];
+ var i, ien, j, jen;
+ var loopRow, loopCells;
+
+ data.splice(row, 1);
+
+ // Update the cached indexes
+ for (i = 0, ien = data.length; i < ien; i++) {
+ loopRow = data[i];
+ loopCells = loopRow.anCells;
+
+ // Rows
+ if (loopRow.nTr !== null) {
+ loopRow.nTr._DT_RowIndex = i;
+ }
+
+ // Cells
+ if (loopCells !== null) {
+ for (j = 0, jen = loopCells.length; j < jen; j++) {
+ loopCells[j]._DT_CellIndex.row = i;
+ }
+ }
+ }
+
+ // Delete from the display arrays
+ _fnDeleteIndex(settings.aiDisplayMaster, row);
+ _fnDeleteIndex(settings.aiDisplay, row);
+ _fnDeleteIndex(that[thatIdx], row, false); // maintain local indexes
+
+ // Check for an 'overflow' they case for displaying the table
+ _fnLengthOverflow(settings);
+
+ // Remove the row's ID reference if there is one
+ var id = settings.rowIdFn(rowData._aData);
+ if (id !== undefined) {
+ delete settings.aIds[id];
+ }
+ });
+
+ this.iterator('table', function (settings) {
+ for (var i = 0, ien = settings.aoData.length; i < ien; i++) {
+ settings.aoData[i].idx = i;
+ }
+ });
+
+ return this;
+ });
+
+
+ _api_register('rows.add()', function (rows) {
+ var newRows = this.iterator('table', function (settings) {
+ var row, i, ien;
+ var out = [];
+
+ for (i = 0, ien = rows.length; i < ien; i++) {
+ row = rows[i];
+
+ if (row.nodeName && row.nodeName.toUpperCase() === 'TR') {
+ out.push(_fnAddTr(settings, row)[0]);
+ } else {
+ out.push(_fnAddData(settings, row));
+ }
+ }
+
+ return out;
+ }, 1);
+
+ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
+ var modRows = this.rows(-1);
+ modRows.pop();
+ $.merge(modRows, newRows);
+
+ return modRows;
+ });
+
+
+ /**
+ *
+ */
+ _api_register('row()', function (selector, opts) {
+ return _selector_first(this.rows(selector, opts));
+ });
+
+
+ _api_register('row().data()', function (data) {
+ var ctx = this.context;
+
+ if (data === undefined) {
+ // Get
+ return ctx.length && this.length ?
+ ctx[0].aoData[this[0]]._aData :
+ undefined;
+ }
+
+ // Set
+ ctx[0].aoData[this[0]]._aData = data;
+
+ // Automatically invalidate
+ _fnInvalidate(ctx[0], this[0], 'data');
+
+ return this;
+ });
+
+
+ _api_register('row().node()', function () {
+ var ctx = this.context;
+
+ return ctx.length && this.length ?
+ ctx[0].aoData[this[0]].nTr || null :
+ null;
+ });
+
+
+ _api_register('row.add()', function (row) {
+ // Allow a jQuery object to be passed in - only a single row is added from
+ // it though - the first element in the set
+ if (row instanceof $ && row.length) {
+ row = row[0];
+ }
+
+ var rows = this.iterator('table', function (settings) {
+ if (row.nodeName && row.nodeName.toUpperCase() === 'TR') {
+ return _fnAddTr(settings, row)[0];
+ }
+ return _fnAddData(settings, row);
+ });
+
+ // Return an Api.rows() extended instance, with the newly added row selected
+ return this.row(rows[0]);
+ });
+
+
+ var __details_add = function (ctx, row, data, klass) {
+ // Convert to array of TR elements
+ var rows = [];
+ var addRow = function (r, k) {
+ // Recursion to allow for arrays of jQuery objects
+ if ($.isArray(r) || r instanceof $) {
+ for (var i = 0, ien = r.length; i < ien; i++) {
+ addRow(r[i], k);
+ }
+ return;
+ }
+
+ // If we get a TR element, then just add it directly - up to the dev
+ // to add the correct number of columns etc
+ if (r.nodeName && r.nodeName.toLowerCase() === 'tr') {
+ rows.push(r);
+ } else {
+ // Otherwise create a row with a wrapper
+ var created = $('<tr><td/></tr>').addClass(k);
+ $('td', created)
+ .addClass(k)
+ .html(r)[0].colSpan = _fnVisbleColumns(ctx);
+
+ rows.push(created[0]);
+ }
+ };
+
+ addRow(data, klass);
+
+ if (row._details) {
+ row._details.remove();
+ }
+
+ row._details = $(rows);
+
+ // If the children were already shown, that state should be retained
+ if (row._detailsShow) {
+ row._details.insertAfter(row.nTr);
+ }
+ };
+
+
+ var __details_remove = function (api, idx) {
+ var ctx = api.context;
+
+ if (ctx.length) {
+ var row = ctx[0].aoData[idx !== undefined ? idx : api[0]];
+
+ if (row && row._details) {
+ row._details.remove();
+
+ row._detailsShow = undefined;
+ row._details = undefined;
+ }
+ }
+ };
+
+
+ var __details_display = function (api, show) {
+ var ctx = api.context;
+
+ if (ctx.length && api.length) {
+ var row = ctx[0].aoData[api[0]];
+
+ if (row._details) {
+ row._detailsShow = show;
+
+ if (show) {
+ row._details.insertAfter(row.nTr);
+ } else {
+ row._details.detach();
+ }
+
+ __details_events(ctx[0]);
+ }
+ }
+ };
+
+
+ var __details_events = function (settings) {
+ var api = new _Api(settings);
+ var namespace = '.dt.DT_details';
+ var drawEvent = 'draw' + namespace;
+ var colvisEvent = 'column-visibility' + namespace;
+ var destroyEvent = 'destroy' + namespace;
+ var data = settings.aoData;
+
+ api.off(drawEvent + ' ' + colvisEvent + ' ' + destroyEvent);
+
+ if (_pluck(data, '_details').length > 0) {
+ // On each draw, insert the required elements into the document
+ api.on(drawEvent, function (e, ctx) {
+ if (settings !== ctx) {
+ return;
+ }
+
+ api.rows({
+ page: 'current'
+ }).eq(0).each(function (idx) {
+ // Internal data grab
+ var row = data[idx];
+
+ if (row._detailsShow) {
+ row._details.insertAfter(row.nTr);
+ }
+ });
+ });
+
+ // Column visibility change - update the colspan
+ api.on(colvisEvent, function (e, ctx, idx, vis) {
+ if (settings !== ctx) {
+ return;
+ }
+
+ // Update the colspan for the details rows (note, only if it already has
+ // a colspan)
+ var row, visible = _fnVisbleColumns(ctx);
+
+ for (var i = 0, ien = data.length; i < ien; i++) {
+ row = data[i];
+
+ if (row._details) {
+ row._details.children('td[colspan]').attr('colspan', visible);
+ }
+ }
+ });
+
+ // Table destroyed - nuke any child rows
+ api.on(destroyEvent, function (e, ctx) {
+ if (settings !== ctx) {
+ return;
+ }
+
+ for (var i = 0, ien = data.length; i < ien; i++) {
+ if (data[i]._details) {
+ __details_remove(api, i);
+ }
+ }
+ });
+ }
+ };
+
+ // Strings for the method names to help minification
+ var _emp = '';
+ var _child_obj = _emp + 'row().child';
+ var _child_mth = _child_obj + '()';
+
+ // data can be:
+ // tr
+ // string
+ // jQuery or array of any of the above
+ _api_register(_child_mth, function (data, klass) {
+ var ctx = this.context;
+
+ if (data === undefined) {
+ // get
+ return ctx.length && this.length ?
+ ctx[0].aoData[this[0]]._details :
+ undefined;
+ } else if (data === true) {
+ // show
+ this.child.show();
+ } else if (data === false) {
+ // remove
+ __details_remove(this);
+ } else if (ctx.length && this.length) {
+ // set
+ __details_add(ctx[0], ctx[0].aoData[this[0]], data, klass);
+ }
+
+ return this;
+ });
+
+
+ _api_register([
+ _child_obj + '.show()',
+ _child_mth + '.show()' // only when `child()` was called with parameters (without
+ ], function (show) { // it returns an object and this method is not executed)
+ __details_display(this, true);
+ return this;
+ });
+
+
+ _api_register([
+ _child_obj + '.hide()',
+ _child_mth + '.hide()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_display(this, false);
+ return this;
+ });
+
+
+ _api_register([
+ _child_obj + '.remove()',
+ _child_mth + '.remove()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_remove(this);
+ return this;
+ });
+
+
+ _api_register(_child_obj + '.isShown()', function () {
+ var ctx = this.context;
+
+ if (ctx.length && this.length) {
+ // _detailsShown as false or undefined will fall through to return false
+ return ctx[0].aoData[this[0]]._detailsShow || false;
+ }
+ return false;
+ });
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Columns
+ *
+ * {integer} - column index (>=0 count from left, <0 count from right)
+ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
+ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
+ * "{string}:name" - column name
+ * "{string}" - jQuery selector on column header nodes
+ *
+ */
+
+ // can be an array of these items, comma separated list, or an array of comma
+ // separated lists
+
+ var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
+
+
+ // r1 and r2 are redundant - but it means that the parameters match for the
+ // iterator callback in columns().data()
+ var __columnData = function (settings, column, r1, r2, rows) {
+ var a = [];
+ for (var row = 0, ien = rows.length; row < ien; row++) {
+ a.push(_fnGetCellData(settings, rows[row], column));
+ }
+ return a;
+ };
+
+
+ var __column_selector = function (settings, selector, opts) {
+ var
+ columns = settings.aoColumns,
+ names = _pluck(columns, 'sName'),
+ nodes = _pluck(columns, 'nTh');
+
+ var run = function (s) {
+ var selInt = _intVal(s);
+
+ // Selector - all
+ if (s === '') {
+ return _range(columns.length);
+ }
+
+ // Selector - index
+ if (selInt !== null) {
+ return [selInt >= 0 ?
+ selInt : // Count from left
+ columns.length + selInt // Count from right (+ because its a negative value)
+ ];
+ }
+
+ // Selector = function
+ if (typeof s === 'function') {
+ var rows = _selector_row_indexes(settings, opts);
+
+ return $.map(columns, function (col, idx) {
+ return s(
+ idx,
+ __columnData(settings, idx, 0, 0, rows),
+ nodes[idx]
+ ) ? idx : null;
+ });
+ }
+
+ // jQuery or string selector
+ var match = typeof s === 'string' ?
+ s.match(__re_column_selector) :
+ '';
+
+ if (match) {
+ switch (match[2]) {
+ case 'visIdx':
+ case 'visible':
+ var idx = parseInt(match[1], 10);
+ // Visible index given, convert to column index
+ if (idx < 0) {
+ // Counting from the right
+ var visColumns = $.map(columns, function (col, i) {
+ return col.bVisible ? i : null;
+ });
+ return [visColumns[visColumns.length + idx]];
+ }
+ // Counting from the left
+ return [_fnVisibleToColumnIndex(settings, idx)];
+
+ case 'name':
+ // match by name. `names` is column index complete and in order
+ return $.map(names, function (name, i) {
+ return name === match[1] ? i : null;
+ });
+
+ default:
+ return [];
+ }
+ }
+
+ // Cell in the table body
+ if (s.nodeName && s._DT_CellIndex) {
+ return [s._DT_CellIndex.column];
+ }
+
+ // jQuery selector on the TH elements for the columns
+ var jqResult = $(nodes)
+ .filter(s)
+ .map(function () {
+ return $.inArray(this, nodes); // `nodes` is column index complete and in order
+ })
+ .toArray();
+
+ if (jqResult.length || !s.nodeName) {
+ return jqResult;
+ }
+
+ // Otherwise a node which might have a `dt-column` data attribute, or be
+ // a child or such an element
+ var host = $(s).closest('*[data-dt-column]');
+ return host.length ? [host.data('dt-column')] : [];
+ };
+
+ return _selector_run('column', selector, run, settings, opts);
+ };
+
+
+ var __setColumnVis = function (settings, column, vis) {
+ var
+ cols = settings.aoColumns,
+ col = cols[column],
+ data = settings.aoData,
+ row, cells, i, ien, tr;
+
+ // Get
+ if (vis === undefined) {
+ return col.bVisible;
+ }
+
+ // Set
+ // No change
+ if (col.bVisible === vis) {
+ return;
+ }
+
+ if (vis) {
+ // Insert column
+ // Need to decide if we should use appendChild or insertBefore
+ var insertBefore = $.inArray(true, _pluck(cols, 'bVisible'), column + 1);
+
+ for (i = 0, ien = data.length; i < ien; i++) {
+ tr = data[i].nTr;
+ cells = data[i].anCells;
+
+ if (tr) {
+ // insertBefore can act like appendChild if 2nd arg is null
+ tr.insertBefore(cells[column], cells[insertBefore] || null);
+ }
+ }
+ } else {
+ // Remove column
+ $(_pluck(settings.aoData, 'anCells', column)).detach();
+ }
+
+ // Common actions
+ col.bVisible = vis;
+ _fnDrawHead(settings, settings.aoHeader);
+ _fnDrawHead(settings, settings.aoFooter);
+
+ _fnSaveState(settings);
+ };
+
+
+ _api_register('columns()', function (selector, opts) {
+ // argument shifting
+ if (selector === undefined) {
+ selector = '';
+ } else if ($.isPlainObject(selector)) {
+ opts = selector;
+ selector = '';
+ }
+
+ opts = _selector_opts(opts);
+
+ var inst = this.iterator('table', function (settings) {
+ return __column_selector(settings, selector, opts);
+ }, 1);
+
+ // Want argument shifting here and in _row_selector?
+ inst.selector.cols = selector;
+ inst.selector.opts = opts;
+
+ return inst;
+ });
+
+ _api_registerPlural('columns().header()', 'column().header()', function (selector, opts) {
+ return this.iterator('column', function (settings, column) {
+ return settings.aoColumns[column].nTh;
+ }, 1);
+ });
+
+ _api_registerPlural('columns().footer()', 'column().footer()', function (selector, opts) {
+ return this.iterator('column', function (settings, column) {
+ return settings.aoColumns[column].nTf;
+ }, 1);
+ });
+
+ _api_registerPlural('columns().data()', 'column().data()', function () {
+ return this.iterator('column-rows', __columnData, 1);
+ });
+
+ _api_registerPlural('columns().dataSrc()', 'column().dataSrc()', function () {
+ return this.iterator('column', function (settings, column) {
+ return settings.aoColumns[column].mData;
+ }, 1);
+ });
+
+ _api_registerPlural('columns().cache()', 'column().cache()', function (type) {
+ return this.iterator('column-rows', function (settings, column, i, j, rows) {
+ return _pluck_order(settings.aoData, rows,
+ type === 'search' ? '_aFilterData' : '_aSortData', column
+ );
+ }, 1);
+ });
+
+ _api_registerPlural('columns().nodes()', 'column().nodes()', function () {
+ return this.iterator('column-rows', function (settings, column, i, j, rows) {
+ return _pluck_order(settings.aoData, rows, 'anCells', column);
+ }, 1);
+ });
+
+ _api_registerPlural('columns().visible()', 'column().visible()', function (vis, calc) {
+ var ret = this.iterator('column', function (settings, column) {
+ if (vis === undefined) {
+ return settings.aoColumns[column].bVisible;
+ } // else
+ __setColumnVis(settings, column, vis);
+ });
+
+ // Group the column visibility changes
+ if (vis !== undefined) {
+ // Second loop once the first is done for events
+ this.iterator('column', function (settings, column) {
+ _fnCallbackFire(settings, null, 'column-visibility', [settings, column, vis, calc]);
+ });
+
+ if (calc === undefined || calc) {
+ this.columns.adjust();
+ }
+ }
+
+ return ret;
+ });
+
+ _api_registerPlural('columns().indexes()', 'column().index()', function (type) {
+ return this.iterator('column', function (settings, column) {
+ return type === 'visible' ?
+ _fnColumnIndexToVisible(settings, column) :
+ column;
+ }, 1);
+ });
+
+ _api_register('columns.adjust()', function () {
+ return this.iterator('table', function (settings) {
+ _fnAdjustColumnSizing(settings);
+ }, 1);
+ });
+
+ _api_register('column.index()', function (type, idx) {
+ if (this.context.length !== 0) {
+ var ctx = this.context[0];
+
+ if (type === 'fromVisible' || type === 'toData') {
+ return _fnVisibleToColumnIndex(ctx, idx);
+ } else if (type === 'fromData' || type === 'toVisible') {
+ return _fnColumnIndexToVisible(ctx, idx);
+ }
+ }
+ });
+
+ _api_register('column()', function (selector, opts) {
+ return _selector_first(this.columns(selector, opts));
+ });
+
+
+ var __cell_selector = function (settings, selector, opts) {
+ var data = settings.aoData;
+ var rows = _selector_row_indexes(settings, opts);
+ var cells = _removeEmpty(_pluck_order(data, rows, 'anCells'));
+ var allCells = $([].concat.apply([], cells));
+ var row;
+ var columns = settings.aoColumns.length;
+ var a, i, ien, j, o, host;
+
+ var run = function (s) {
+ var fnSelector = typeof s === 'function';
+
+ if (s === null || s === undefined || fnSelector) {
+ // All cells and function selectors
+ a = [];
+
+ for (i = 0, ien = rows.length; i < ien; i++) {
+ row = rows[i];
+
+ for (j = 0; j < columns; j++) {
+ o = {
+ row: row,
+ column: j
+ };
+
+ if (fnSelector) {
+ // Selector - function
+ host = data[row];
+
+ if (s(o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null)) {
+ a.push(o);
+ }
+ } else {
+ // Selector - all
+ a.push(o);
+ }
+ }
+ }
+
+ return a;
+ }
+
+ // Selector - index
+ if ($.isPlainObject(s)) {
+ return [s];
+ }
+
+ // Selector - jQuery filtered cells
+ var jqResult = allCells
+ .filter(s)
+ .map(function (i, el) {
+ return { // use a new object, in case someone changes the values
+ row: el._DT_CellIndex.row,
+ column: el._DT_CellIndex.column
+ };
+ })
+ .toArray();
+
+ if (jqResult.length || !s.nodeName) {
+ return jqResult;
+ }
+
+ // Otherwise the selector is a node, and there is one last option - the
+ // element might be a child of an element which has dt-row and dt-column
+ // data attributes
+ host = $(s).closest('*[data-dt-row]');
+ return host.length ? [{
+ row: host.data('dt-row'),
+ column: host.data('dt-column')
+ }] : [];
+ };
+
+ return _selector_run('cell', selector, run, settings, opts);
+ };
+
+
+ _api_register('cells()', function (rowSelector, columnSelector, opts) {
+ // Argument shifting
+ if ($.isPlainObject(rowSelector)) {
+ // Indexes
+ if (rowSelector.row === undefined) {
+ // Selector options in first parameter
+ opts = rowSelector;
+ rowSelector = null;
+ } else {
+ // Cell index objects in first parameter
+ opts = columnSelector;
+ columnSelector = null;
+ }
+ }
+ if ($.isPlainObject(columnSelector)) {
+ opts = columnSelector;
+ columnSelector = null;
+ }
+
+ // Cell selector
+ if (columnSelector === null || columnSelector === undefined) {
+ return this.iterator('table', function (settings) {
+ return __cell_selector(settings, rowSelector, _selector_opts(opts));
+ });
+ }
+
+ // Row + column selector
+ var columns = this.columns(columnSelector, opts);
+ var rows = this.rows(rowSelector, opts);
+ var a, i, ien, j, jen;
+
+ var cells = this.iterator('table', function (settings, idx) {
+ a = [];
+
+ for (i = 0, ien = rows[idx].length; i < ien; i++) {
+ for (j = 0, jen = columns[idx].length; j < jen; j++) {
+ a.push({
+ row: rows[idx][i],
+ column: columns[idx][j]
+ });
+ }
+ }
+
+ return a;
+ }, 1);
+
+ $.extend(cells.selector, {
+ cols: columnSelector,
+ rows: rowSelector,
+ opts: opts
+ });
+
+ return cells;
+ });
+
+
+ _api_registerPlural('cells().nodes()', 'cell().node()', function () {
+ return this.iterator('cell', function (settings, row, column) {
+ var data = settings.aoData[row];
+
+ return data && data.anCells ?
+ data.anCells[column] :
+ undefined;
+ }, 1);
+ });
+
+
+ _api_register('cells().data()', function () {
+ return this.iterator('cell', function (settings, row, column) {
+ return _fnGetCellData(settings, row, column);
+ }, 1);
+ });
+
+
+ _api_registerPlural('cells().cache()', 'cell().cache()', function (type) {
+ type = type === 'search' ? '_aFilterData' : '_aSortData';
+
+ return this.iterator('cell', function (settings, row, column) {
+ return settings.aoData[row][type][column];
+ }, 1);
+ });
+
+
+ _api_registerPlural('cells().render()', 'cell().render()', function (type) {
+ return this.iterator('cell', function (settings, row, column) {
+ return _fnGetCellData(settings, row, column, type);
+ }, 1);
+ });
+
+
+ _api_registerPlural('cells().indexes()', 'cell().index()', function () {
+ return this.iterator('cell', function (settings, row, column) {
+ return {
+ row: row,
+ column: column,
+ columnVisible: _fnColumnIndexToVisible(settings, column)
+ };
+ }, 1);
+ });
+
+
+ _api_registerPlural('cells().invalidate()', 'cell().invalidate()', function (src) {
+ return this.iterator('cell', function (settings, row, column) {
+ _fnInvalidate(settings, row, src, column);
+ });
+ });
+
+
+ _api_register('cell()', function (rowSelector, columnSelector, opts) {
+ return _selector_first(this.cells(rowSelector, columnSelector, opts));
+ });
+
+
+ _api_register('cell().data()', function (data) {
+ var ctx = this.context;
+ var cell = this[0];
+
+ if (data === undefined) {
+ // Get
+ return ctx.length && cell.length ?
+ _fnGetCellData(ctx[0], cell[0].row, cell[0].column) :
+ undefined;
+ }
+
+ // Set
+ _fnSetCellData(ctx[0], cell[0].row, cell[0].column, data);
+ _fnInvalidate(ctx[0], cell[0].row, 'data', cell[0].column);
+
+ return this;
+ });
+
+
+ /**
+ * Get current ordering (sorting) that has been applied to the table.
+ *
+ * @returns {array} 2D array containing the sorting information for the first
+ * table in the current context. Each element in the parent array represents
+ * a column being sorted upon (i.e. multi-sorting with two columns would have
+ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
+ * the column index that the sorting condition applies to, the second is the
+ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
+ * index of the sorting order from the `column.sorting` initialisation array.
+ */
+ /**
+ * Set the ordering for the table.
+ *
+ * @param {integer} order Column index to sort upon.
+ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
+ * @returns {DataTables.Api} this
+ */
+ /**
+ * Set the ordering for the table.
+ *
+ * @param {array} order 1D array of sorting information to be applied.
+ * @param {array} [...] Optional additional sorting conditions
+ * @returns {DataTables.Api} this
+ */
+ /**
+ * Set the ordering for the table.
+ *
+ * @param {array} order 2D array of sorting information to be applied.
+ * @returns {DataTables.Api} this
+ */
+ _api_register('order()', function (order, dir) {
+ var ctx = this.context;
+
+ if (order === undefined) {
+ // get
+ return ctx.length !== 0 ?
+ ctx[0].aaSorting :
+ undefined;
+ }
+
+ // set
+ if (typeof order === 'number') {
+ // Simple column / direction passed in
+ order = [
+ [order, dir]
+ ];
+ } else if (order.length && !$.isArray(order[0])) {
+ // Arguments passed in (list of 1D arrays)
+ order = Array.prototype.slice.call(arguments);
+ }
+ // otherwise a 2D array was passed in
+
+ return this.iterator('table', function (settings) {
+ settings.aaSorting = order.slice();
+ });
+ });
+
+
+ /**
+ * Attach a sort listener to an element for a given column
+ *
+ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
+ * listener to. This can take the form of a single DOM node, a jQuery
+ * collection of nodes or a jQuery selector which will identify the node(s).
+ * @param {integer} column the column that a click on this node will sort on
+ * @param {function} [callback] callback function when sort is run
+ * @returns {DataTables.Api} this
+ */
+ _api_register('order.listener()', function (node, column, callback) {
+ return this.iterator('table', function (settings) {
+ _fnSortAttachListener(settings, node, column, callback);
+ });
+ });
+
+
+ _api_register('order.fixed()', function (set) {
+ if (!set) {
+ var ctx = this.context;
+ var fixed = ctx.length ?
+ ctx[0].aaSortingFixed :
+ undefined;
+
+ return $.isArray(fixed) ? {
+ pre: fixed
+ } :
+ fixed;
+ }
+
+ return this.iterator('table', function (settings) {
+ settings.aaSortingFixed = $.extend(true, {}, set);
+ });
+ });
+
+
+ // Order by the selected column(s)
+ _api_register([
+ 'columns().order()',
+ 'column().order()'
+ ], function (dir) {
+ var that = this;
+
+ return this.iterator('table', function (settings, i) {
+ var sort = [];
+
+ $.each(that[i], function (j, col) {
+ sort.push([col, dir]);
+ });
+
+ settings.aaSorting = sort;
+ });
+ });
+
+
+ _api_register('search()', function (input, regex, smart, caseInsen) {
+ var ctx = this.context;
+
+ if (input === undefined) {
+ // get
+ return ctx.length !== 0 ?
+ ctx[0].oPreviousSearch.sSearch :
+ undefined;
+ }
+
+ // set
+ return this.iterator('table', function (settings) {
+ if (!settings.oFeatures.bFilter) {
+ return;
+ }
+
+ _fnFilterComplete(settings, $.extend({}, settings.oPreviousSearch, {
+ "sSearch": input + "",
+ "bRegex": regex === null ? false : regex,
+ "bSmart": smart === null ? true : smart,
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ }), 1);
+ });
+ });
+
+
+ _api_registerPlural(
+ 'columns().search()',
+ 'column().search()',
+ function (input, regex, smart, caseInsen) {
+ return this.iterator('column', function (settings, column) {
+ var preSearch = settings.aoPreSearchCols;
+
+ if (input === undefined) {
+ // get
+ return preSearch[column].sSearch;
+ }
+
+ // set
+ if (!settings.oFeatures.bFilter) {
+ return;
+ }
+
+ $.extend(preSearch[column], {
+ "sSearch": input + "",
+ "bRegex": regex === null ? false : regex,
+ "bSmart": smart === null ? true : smart,
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ });
+
+ _fnFilterComplete(settings, settings.oPreviousSearch, 1);
+ });
+ }
+ );
+
+ /*
+ * State API methods
+ */
+
+ _api_register('state()', function () {
+ return this.context.length ?
+ this.context[0].oSavedState :
+ null;
+ });
+
+
+ _api_register('state.clear()', function () {
+ return this.iterator('table', function (settings) {
+ // Save an empty object
+ settings.fnStateSaveCallback.call(settings.oInstance, settings, {});
+ });
+ });
+
+
+ _api_register('state.loaded()', function () {
+ return this.context.length ?
+ this.context[0].oLoadedState :
+ null;
+ });
+
+
+ _api_register('state.save()', function () {
+ return this.iterator('table', function (settings) {
+ _fnSaveState(settings);
+ });
+ });
+
+
+ /**
+ * Provide a common method for plug-ins to check the version of DataTables being
+ * used, in order to ensure compatibility.
+ *
+ * @param {string} version Version string to check for, in the format "X.Y.Z".
+ * Note that the formats "X" and "X.Y" are also acceptable.
+ * @returns {boolean} true if this version of DataTables is greater or equal to
+ * the required version, or false if this version of DataTales is not
+ * suitable
+ * @static
+ * @dtopt API-Static
+ *
+ * @example
+ * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
+ */
+ DataTable.versionCheck = DataTable.fnVersionCheck = function (version) {
+ var aThis = DataTable.version.split('.');
+ var aThat = version.split('.');
+ var iThis, iThat;
+
+ for (var i = 0, iLen = aThat.length; i < iLen; i++) {
+ iThis = parseInt(aThis[i], 10) || 0;
+ iThat = parseInt(aThat[i], 10) || 0;
+
+ // Parts are the same, keep comparing
+ if (iThis === iThat) {
+ continue;
+ }
+
+ // Parts are different, return immediately
+ return iThis > iThat;
+ }
+
+ return true;
+ };
+
+
+ /**
+ * Check if a `<table>` node is a DataTable table already or not.
+ *
+ * @param {node|jquery|string} table Table node, jQuery object or jQuery
+ * selector for the table to test. Note that if more than more than one
+ * table is passed on, only the first will be checked
+ * @returns {boolean} true the table given is a DataTable, or false otherwise
+ * @static
+ * @dtopt API-Static
+ *
+ * @example
+ * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
+ * $('#example').dataTable();
+ * }
+ */
+ DataTable.isDataTable = DataTable.fnIsDataTable = function (table) {
+ var t = $(table).get(0);
+ var is = false;
+
+ $.each(DataTable.settings, function (i, o) {
+ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
+ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
+
+ if (o.nTable === t || head === t || foot === t) {
+ is = true;
+ }
+ });
+
+ return is;
+ };
+
+
+ /**
+ * Get all DataTable tables that have been initialised - optionally you can
+ * select to get only currently visible tables.
+ *
+ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
+ * or visible tables only.
+ * @returns {array} Array of `table` nodes (not DataTable instances) which are
+ * DataTables
+ * @static
+ * @dtopt API-Static
+ *
+ * @example
+ * $.each( $.fn.dataTable.tables(true), function () {
+ * $(table).DataTable().columns.adjust();
+ * } );
+ */
+ DataTable.tables = DataTable.fnTables = function (visible) {
+ var api = false;
+
+ if ($.isPlainObject(visible)) {
+ api = visible.api;
+ visible = visible.visible;
+ }
+
+ var a = $.map(DataTable.settings, function (o) {
+ if (!visible || (visible && $(o.nTable).is(':visible'))) {
+ return o.nTable;
+ }
+ });
+
+ return api ?
+ new _Api(a) :
+ a;
+ };
+
+
+ /**
+ * Convert from camel case parameters to Hungarian notation. This is made public
+ * for the extensions to provide the same ability as DataTables core to accept
+ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
+ * parameters.
+ *
+ * @param {object} src The model object which holds all parameters that can be
+ * mapped.
+ * @param {object} user The object to convert from camel case to Hungarian.
+ * @param {boolean} force When set to `true`, properties which already have a
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
+ * won't be.
+ */
+ DataTable.camelToHungarian = _fnCamelToHungarian;
+
+
+ /**
+ *
+ */
+ _api_register('$()', function (selector, opts) {
+ var
+ rows = this.rows(opts).nodes(), // Get all rows
+ jqRows = $(rows);
+
+ return $([].concat(
+ jqRows.filter(selector).toArray(),
+ jqRows.find(selector).toArray()
+ ));
+ });
+
+
+ // jQuery functions to operate on the tables
+ $.each(['on', 'one', 'off'], function (i, key) {
+ _api_register(key + '()', function ( /* event, handler */ ) {
+ var args = Array.prototype.slice.call(arguments);
+
+ // Add the `dt` namespace automatically if it isn't already present
+ if (!args[0].match(/\.dt\b/)) {
+ args[0] += '.dt';
+ }
+
+ var inst = $(this.tables().nodes());
+ inst[key].apply(inst, args);
+ return this;
+ });
+ });
+
+
+ _api_register('clear()', function () {
+ return this.iterator('table', function (settings) {
+ _fnClearTable(settings);
+ });
+ });
+
+
+ _api_register('settings()', function () {
+ return new _Api(this.context, this.context);
+ });
+
+
+ _api_register('init()', function () {
+ var ctx = this.context;
+ return ctx.length ? ctx[0].oInit : null;
+ });
+
+
+ _api_register('data()', function () {
+ return this.iterator('table', function (settings) {
+ return _pluck(settings.aoData, '_aData');
+ }).flatten();
+ });
+
+
+ _api_register('destroy()', function (remove) {
+ remove = remove || false;
+
+ return this.iterator('table', function (settings) {
+ var orig = settings.nTableWrapper.parentNode;
+ var classes = settings.oClasses;
+ var table = settings.nTable;
+ var tbody = settings.nTBody;
+ var thead = settings.nTHead;
+ var tfoot = settings.nTFoot;
+ var jqTable = $(table);
+ var jqTbody = $(tbody);
+ var jqWrapper = $(settings.nTableWrapper);
+ var rows = $.map(settings.aoData, function (r) {
+ return r.nTr;
+ });
+ var i, ien;
+
+ // Flag to note that the table is currently being destroyed - no action
+ // should be taken
+ settings.bDestroying = true;
+
+ // Fire off the destroy callbacks for plug-ins etc
+ _fnCallbackFire(settings, "aoDestroyCallback", "destroy", [settings]);
+
+ // If not being removed from the document, make all columns visible
+ if (!remove) {
+ new _Api(settings).columns().visible(true);
+ }
+
+ // Blitz all `DT` namespaced events (these are internal events, the
+ // lowercase, `dt` events are user subscribed and they are responsible
+ // for removing them
+ jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
+ $(window).unbind('.DT-' + settings.sInstance);
+
+ // When scrolling we had to break the table up - restore it
+ if (table != thead.parentNode) {
+ jqTable.children('thead').detach();
+ jqTable.append(thead);
+ }
+
+ if (tfoot && table != tfoot.parentNode) {
+ jqTable.children('tfoot').detach();
+ jqTable.append(tfoot);
+ }
+
+ settings.aaSorting = [];
+ settings.aaSortingFixed = [];
+ _fnSortingClasses(settings);
+
+ $(rows).removeClass(settings.asStripeClasses.join(' '));
+
+ $('th, td', thead).removeClass(classes.sSortable + ' ' +
+ classes.sSortableAsc + ' ' + classes.sSortableDesc + ' ' + classes.sSortableNone
+ );
+
+ if (settings.bJUI) {
+ $('th span.' + classes.sSortIcon + ', td span.' + classes.sSortIcon, thead).detach();
+ $('th, td', thead).each(function () {
+ var wrapper = $('div.' + classes.sSortJUIWrapper, this);
+ $(this).append(wrapper.contents());
+ wrapper.detach();
+ });
+ }
+
+ // Add the TR elements back into the table in their original order
+ jqTbody.children().detach();
+ jqTbody.append(rows);
+
+ // Remove the DataTables generated nodes, events and classes
+ var removedMethod = remove ? 'remove' : 'detach';
+ jqTable[removedMethod]();
+ jqWrapper[removedMethod]();
+
+ // If we need to reattach the table to the document
+ if (!remove && orig) {
+ // insertBefore acts like appendChild if !arg[1]
+ orig.insertBefore(table, settings.nTableReinsertBefore);
+
+ // Restore the width of the original table - was read from the style property,
+ // so we can restore directly to that
+ jqTable
+ .css('width', settings.sDestroyWidth)
+ .removeClass(classes.sTable);
+
+ // If the were originally stripe classes - then we add them back here.
+ // Note this is not fool proof (for example if not all rows had stripe
+ // classes - but it's a good effort without getting carried away
+ ien = settings.asDestroyStripes.length;
+
+ if (ien) {
+ jqTbody.children().each(function (i) {
+ $(this).addClass(settings.asDestroyStripes[i % ien]);
+ });
+ }
+ }
+
+ /* Remove the settings object from the settings array */
+ var idx = $.inArray(settings, DataTable.settings);
+ if (idx !== -1) {
+ DataTable.settings.splice(idx, 1);
+ }
+ });
+ });
+
+
+ // Add the `every()` method for rows, columns and cells in a compact form
+ $.each(['column', 'row', 'cell'], function (i, type) {
+ _api_register(type + 's().every()', function (fn) {
+ var opts = this.selector.opts;
+ var api = this;
+
+ return this.iterator(type, function (settings, arg1, arg2, arg3, arg4) {
+ // Rows and columns:
+ // arg1 - index
+ // arg2 - table counter
+ // arg3 - loop counter
+ // arg4 - undefined
+ // Cells:
+ // arg1 - row index
+ // arg2 - column index
+ // arg3 - table counter
+ // arg4 - loop counter
+ fn.call(
+ api[type](
+ arg1,
+ type === 'cell' ? arg2 : opts,
+ type === 'cell' ? opts : undefined
+ ),
+ arg1, arg2, arg3, arg4
+ );
+ });
+ });
+ });
+
+
+ // i18n method for extensions to be able to use the language object from the
+ // DataTable
+ _api_register('i18n()', function (token, def, plural) {
+ var ctx = this.context[0];
+ var resolved = _fnGetObjectDataFn(token)(ctx.oLanguage);
+
+ if (resolved === undefined) {
+ resolved = def;
+ }
+
+ if (plural !== undefined && $.isPlainObject(resolved)) {
+ resolved = resolved[plural] !== undefined ?
+ resolved[plural] :
+ resolved._;
+ }
+
+ return resolved.replace('%d', plural); // nb: plural might be undefined,
+ });
+
+ /**
+ * Version string for plug-ins to check compatibility. Allowed format is
+ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
+ * only for non-release builds. See http://semver.org/ for more information.
+ * @member
+ * @type string
+ * @default Version number
+ */
+ DataTable.version = "1.10.12";
+
+ /**
+ * Private data store, containing all of the settings objects that are
+ * created for the tables on a given page.
+ *
+ * Note that the `DataTable.settings` object is aliased to
+ * `jQuery.fn.dataTableExt` through which it may be accessed and
+ * manipulated, or `jQuery.fn.dataTable.settings`.
+ * @member
+ * @type array
+ * @default []
+ * @private
+ */
+ DataTable.settings = [];
+
+ /**
+ * Object models container, for the various models that DataTables has
+ * available to it. These models define the objects that are used to hold
+ * the active state and configuration of the table.
+ * @namespace
+ */
+ DataTable.models = {};
+
+
+ /**
+ * Template object for the way in which DataTables holds information about
+ * search information for the global filter and individual column filters.
+ * @namespace
+ */
+ DataTable.models.oSearch = {
+ /**
+ * Flag to indicate if the filtering should be case insensitive or not
+ * @type boolean
+ * @default true
+ */
+ "bCaseInsensitive": true,
+
+ /**
+ * Applied search term
+ * @type string
+ * @default <i>Empty string</i>
+ */
+ "sSearch": "",
+
+ /**
+ * Flag to indicate if the search term should be interpreted as a
+ * regular expression (true) or not (false) and therefore and special
+ * regex characters escaped.
+ * @type boolean
+ * @default false
+ */
+ "bRegex": false,
+
+ /**
+ * Flag to indicate if DataTables is to use its smart filtering or not.
+ * @type boolean
+ * @default true
+ */
+ "bSmart": true
+ };
+
+
+ /**
+ * Template object for the way in which DataTables holds information about
+ * each individual row. This is the object format used for the settings
+ * aoData array.
+ * @namespace
+ */
+ DataTable.models.oRow = {
+ /**
+ * TR element for the row
+ * @type node
+ * @default null
+ */
+ "nTr": null,
+
+ /**
+ * Array of TD elements for each row. This is null until the row has been
+ * created.
+ * @type array nodes
+ * @default []
+ */
+ "anCells": null,
+
+ /**
+ * Data object from the original data source for the row. This is either
+ * an array if using the traditional form of DataTables, or an object if
+ * using mData options. The exact type will depend on the passed in
+ * data from the data source, or will be an array if using DOM a data
+ * source.
+ * @type array|object
+ * @default []
+ */
+ "_aData": [],
+
+ /**
+ * Sorting data cache - this array is ostensibly the same length as the
+ * number of columns (although each index is generated only as it is
+ * needed), and holds the data that is used for sorting each column in the
+ * row. We do this cache generation at the start of the sort in order that
+ * the formatting of the sort data need be done only once for each cell
+ * per sort. This array should not be read from or written to by anything
+ * other than the master sorting methods.
+ * @type array
+ * @default null
+ * @private
+ */
+ "_aSortData": null,
+
+ /**
+ * Per cell filtering data cache. As per the sort data cache, used to
+ * increase the performance of the filtering in DataTables
+ * @type array
+ * @default null
+ * @private
+ */
+ "_aFilterData": null,
+
+ /**
+ * Filtering data cache. This is the same as the cell filtering cache, but
+ * in this case a string rather than an array. This is easily computed with
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
+ * needed on every search (memory traded for performance)
+ * @type array
+ * @default null
+ * @private
+ */
+ "_sFilterRow": null,
+
+ /**
+ * Cache of the class name that DataTables has applied to the row, so we
+ * can quickly look at this variable rather than needing to do a DOM check
+ * on className for the nTr property.
+ * @type string
+ * @default <i>Empty string</i>
+ * @private
+ */
+ "_sRowStripe": "",
+
+ /**
+ * Denote if the original data source was from the DOM, or the data source
+ * object. This is used for invalidating data, so DataTables can
+ * automatically read data from the original source, unless uninstructed
+ * otherwise.
+ * @type string
+ * @default null
+ * @private
+ */
+ "src": null,
+
+ /**
+ * Index in the aoData array. This saves an indexOf lookup when we have the
+ * object, but want to know the index
+ * @type integer
+ * @default -1
+ * @private
+ */
+ "idx": -1
+ };
+
+
+ /**
+ * Template object for the column information object in DataTables. This object
+ * is held in the settings aoColumns array and contains all the information that
+ * DataTables needs about each individual column.
+ *
+ * Note that this object is related to {@link DataTable.defaults.column}
+ * but this one is the internal data store for DataTables's cache of columns.
+ * It should NOT be manipulated outside of DataTables. Any configuration should
+ * be done through the initialisation options.
+ * @namespace
+ */
+ DataTable.models.oColumn = {
+ /**
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
+ * is faster to just hold it as a variable
+ * @type integer
+ * @default null
+ */
+ "idx": null,
+
+ /**
+ * A list of the columns that sorting should occur on when this column
+ * is sorted. That this property is an array allows multi-column sorting
+ * to be defined for a column (for example first name / last name columns
+ * would benefit from this). The values are integers pointing to the
+ * columns to be sorted on (typically it will be a single integer pointing
+ * at itself, but that doesn't need to be the case).
+ * @type array
+ */
+ "aDataSort": null,
+
+ /**
+ * Define the sorting directions that are applied to the column, in sequence
+ * as the column is repeatedly sorted upon - i.e. the first value is used
+ * as the sorting direction when the column if first sorted (clicked on).
+ * Sort it again (click again) and it will move on to the next index.
+ * Repeat until loop.
+ * @type array
+ */
+ "asSorting": null,
+
+ /**
+ * Flag to indicate if the column is searchable, and thus should be included
+ * in the filtering or not.
+ * @type boolean
+ */
+ "bSearchable": null,
+
+ /**
+ * Flag to indicate if the column is sortable or not.
+ * @type boolean
+ */
+ "bSortable": null,
+
+ /**
+ * Flag to indicate if the column is currently visible in the table or not
+ * @type boolean
+ */
+ "bVisible": null,
+
+ /**
+ * Store for manual type assignment using the `column.type` option. This
+ * is held in store so we can manipulate the column's `sType` property.
+ * @type string
+ * @default null
+ * @private
+ */
+ "_sManualType": null,
+
+ /**
+ * Flag to indicate if HTML5 data attributes should be used as the data
+ * source for filtering or sorting. True is either are.
+ * @type boolean
+ * @default false
+ * @private
+ */
+ "_bAttrSrc": false,
+
+ /**
+ * Developer definable function that is called whenever a cell is created (Ajax source,
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
+ * allowing you to modify the DOM element (add background colour for example) when the
+ * element is available.
+ * @type function
+ * @param {element} nTd The TD node that has been created
+ * @param {*} sData The Data for the cell
+ * @param {array|object} oData The data for the whole row
+ * @param {int} iRow The row index for the aoData data store
+ * @default null
+ */
+ "fnCreatedCell": null,
+
+ /**
+ * Function to get data from a cell in a column. You should <b>never</b>
+ * access data directly through _aData internally in DataTables - always use
+ * the method attached to this property. It allows mData to function as
+ * required. This function is automatically assigned by the column
+ * initialisation method
+ * @type function
+ * @param {array|object} oData The data array/object for the array
+ * (i.e. aoData[]._aData)
+ * @param {string} sSpecific The specific data type you want to get -
+ * 'display', 'type' 'filter' 'sort'
+ * @returns {*} The data for the cell from the given row's data
+ * @default null
+ */
+ "fnGetData": null,
+
+ /**
+ * Function to set data for a cell in the column. You should <b>never</b>
+ * set the data directly to _aData internally in DataTables - always use
+ * this method. It allows mData to function as required. This function
+ * is automatically assigned by the column initialisation method
+ * @type function
+ * @param {array|object} oData The data array/object for the array
+ * (i.e. aoData[]._aData)
+ * @param {*} sValue Value to set
+ * @default null
+ */
+ "fnSetData": null,
+
+ /**
+ * Property to read the value for the cells in the column from the data
+ * source array / object. If null, then the default content is used, if a
+ * function is given then the return from the function is used.
+ * @type function|int|string|null
+ * @default null
+ */
+ "mData": null,
+
+ /**
+ * Partner property to mData which is used (only when defined) to get
+ * the data - i.e. it is basically the same as mData, but without the
+ * 'set' option, and also the data fed to it is the result from mData.
+ * This is the rendering method to match the data method of mData.
+ * @type function|int|string|null
+ * @default null
+ */
+ "mRender": null,
+
+ /**
+ * Unique header TH/TD element for this column - this is what the sorting
+ * listener is attached to (if sorting is enabled.)
+ * @type node
+ * @default null
+ */
+ "nTh": null,
+
+ /**
+ * Unique footer TH/TD element for this column (if there is one). Not used
+ * in DataTables as such, but can be used for plug-ins to reference the
+ * footer for each column.
+ * @type node
+ * @default null
+ */
+ "nTf": null,
+
+ /**
+ * The class to apply to all TD elements in the table's TBODY for the column
+ * @type string
+ * @default null
+ */
+ "sClass": null,
+
+ /**
+ * When DataTables calculates the column widths to assign to each column,
+ * it finds the longest string in each column and then constructs a
+ * temporary table and reads the widths from that. The problem with this
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * string - thus the calculation can go wrong (doing it properly and putting
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
+ * a "work around" we provide this option. It will append its value to the
+ * text that is found to be the longest string for the column - i.e. padding.
+ * @type string
+ */
+ "sContentPadding": null,
+
+ /**
+ * Allows a default value to be given for a column's data, and will be used
+ * whenever a null data source is encountered (this can be because mData
+ * is set to null, or because the data source itself is null).
+ * @type string
+ * @default null
+ */
+ "sDefaultContent": null,
+
+ /**
+ * Name for the column, allowing reference to the column by name as well as
+ * by index (needs a lookup to work by name).
+ * @type string
+ */
+ "sName": null,
+
+ /**
+ * Custom sorting data type - defines which of the available plug-ins in
+ * afnSortData the custom sorting will use - if any is defined.
+ * @type string
+ * @default std
+ */
+ "sSortDataType": 'std',
+
+ /**
+ * Class to be applied to the header element when sorting on this column
+ * @type string
+ * @default null
+ */
+ "sSortingClass": null,
+
+ /**
+ * Class to be applied to the header element when sorting on this column -
+ * when jQuery UI theming is used.
+ * @type string
+ * @default null
+ */
+ "sSortingClassJUI": null,
+
+ /**
+ * Title of the column - what is seen in the TH element (nTh).
+ * @type string
+ */
+ "sTitle": null,
+
+ /**
+ * Column sorting and filtering type
+ * @type string
+ * @default null
+ */
+ "sType": null,
+
+ /**
+ * Width of the column
+ * @type string
+ * @default null
+ */
+ "sWidth": null,
+
+ /**
+ * Width of the column when it was first "encountered"
+ * @type string
+ * @default null
+ */
+ "sWidthOrig": null
+ };
+
+
+ /*
+ * Developer note: The properties of the object below are given in Hungarian
+ * notation, that was used as the interface for DataTables prior to v1.10, however
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
+ * breaking backwards compatibility utterly with this change, the Hungarian
+ * version is still, internally the primary interface, but is is not documented
+ * - hence the @name tags in each doc comment. This allows a Javascript function
+ * to create a map from Hungarian notation to camel case (going the other direction
+ * would require each property to be listed, which would at around 3K to the size
+ * of DataTables, while this method is about a 0.5K hit.
+ *
+ * Ultimately this does pave the way for Hungarian notation to be dropped
+ * completely, but that is a massive amount of work and will break current
+ * installs (therefore is on-hold until v2).
+ */
+
+ /**
+ * Initialisation options that can be given to DataTables at initialisation
+ * time.
+ * @namespace
+ */
+ DataTable.defaults = {
+ /**
+ * An array of data to use for the table, passed in at initialisation which
+ * will be used in preference to any data which is already in the DOM. This is
+ * particularly useful for constructing tables purely in Javascript, for
+ * example with a custom Ajax call.
+ * @type array
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.data
+ *
+ * @example
+ * // Using a 2D array data source
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "data": [
+ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
+ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
+ * ],
+ * "columns": [
+ * { "title": "Engine" },
+ * { "title": "Browser" },
+ * { "title": "Platform" },
+ * { "title": "Version" },
+ * { "title": "Grade" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using an array of objects as a data source (`data`)
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "data": [
+ * {
+ * "engine": "Trident",
+ * "browser": "Internet Explorer 4.0",
+ * "platform": "Win 95+",
+ * "version": 4,
+ * "grade": "X"
+ * },
+ * {
+ * "engine": "Trident",
+ * "browser": "Internet Explorer 5.0",
+ * "platform": "Win 95+",
+ * "version": 5,
+ * "grade": "C"
+ * }
+ * ],
+ * "columns": [
+ * { "title": "Engine", "data": "engine" },
+ * { "title": "Browser", "data": "browser" },
+ * { "title": "Platform", "data": "platform" },
+ * { "title": "Version", "data": "version" },
+ * { "title": "Grade", "data": "grade" }
+ * ]
+ * } );
+ * } );
+ */
+ "aaData": null,
+
+
+ /**
+ * If ordering is enabled, then DataTables will perform a first pass sort on
+ * initialisation. You can define which column(s) the sort is performed
+ * upon, and the sorting direction, with this variable. The `sorting` array
+ * should contain an array for each column to be sorted initially containing
+ * the column's index and a direction string ('asc' or 'desc').
+ * @type array
+ * @default [[0,'asc']]
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.order
+ *
+ * @example
+ * // Sort by 3rd column first, and then 4th column
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "order": [[2,'asc'], [3,'desc']]
+ * } );
+ * } );
+ *
+ * // No initial sorting
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "order": []
+ * } );
+ * } );
+ */
+ "aaSorting": [
+ [0, 'asc']
+ ],
+
+
+ /**
+ * This parameter is basically identical to the `sorting` parameter, but
+ * cannot be overridden by user interaction with the table. What this means
+ * is that you could have a column (visible or hidden) which the sorting
+ * will always be forced on first - any sorting after that (from the user)
+ * will then be performed as required. This can be useful for grouping rows
+ * together.
+ * @type array
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.orderFixed
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "orderFixed": [[0,'asc']]
+ * } );
+ * } )
+ */
+ "aaSortingFixed": [],
+
+
+ /**
+ * DataTables can be instructed to load data to display in the table from a
+ * Ajax source. This option defines how that Ajax call is made and where to.
+ *
+ * The `ajax` property has three different modes of operation, depending on
+ * how it is defined. These are:
+ *
+ * * `string` - Set the URL from where the data should be loaded from.
+ * * `object` - Define properties for `jQuery.ajax`.
+ * * `function` - Custom data get function
+ *
+ * `string`
+ * --------
+ *
+ * As a string, the `ajax` property simply defines the URL from which
+ * DataTables will load data.
+ *
+ * `object`
+ * --------
+ *
+ * As an object, the parameters in the object are passed to
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
+ * of the Ajax request. DataTables has a number of default parameters which
+ * you can override using this option. Please refer to the jQuery
+ * documentation for a full description of the options available, although
+ * the following parameters provide additional options in DataTables or
+ * require special consideration:
+ *
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
+ * can also be used as a function to manipulate the data DataTables sends
+ * to the server. The function takes a single parameter, an object of
+ * parameters with the values that DataTables has readied for sending. An
+ * object may be returned which will be merged into the DataTables
+ * defaults, or you can add the items to the object that was passed in and
+ * not return anything from the function. This supersedes `fnServerParams`
+ * from DataTables 1.9-.
+ *
+ * * `dataSrc` - By default DataTables will look for the property `data` (or
+ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
+ * from an Ajax source or for server-side processing - this parameter
+ * allows that property to be changed. You can use Javascript dotted
+ * object notation to get a data source for multiple levels of nesting, or
+ * it my be used as a function. As a function it takes a single parameter,
+ * the JSON returned from the server, which can be manipulated as
+ * required, with the returned value being that used by DataTables as the
+ * data source for the table. This supersedes `sAjaxDataProp` from
+ * DataTables 1.9-.
+ *
+ * * `success` - Should not be overridden it is used internally in
+ * DataTables. To manipulate / transform the data returned by the server
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
+ *
+ * `function`
+ * ----------
+ *
+ * As a function, making the Ajax call is left up to yourself allowing
+ * complete control of the Ajax request. Indeed, if desired, a method other
+ * than Ajax could be used to obtain the required data, such as Web storage
+ * or an AIR database.
+ *
+ * The function is given four parameters and no return is required. The
+ * parameters are:
+ *
+ * 1. _object_ - Data to send to the server
+ * 2. _function_ - Callback function that must be executed when the required
+ * data has been obtained. That data should be passed into the callback
+ * as the only parameter
+ * 3. _object_ - DataTables settings object for the table
+ *
+ * Note that this supersedes `fnServerData` from DataTables 1.9-.
+ *
+ * @type string|object|function
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.ajax
+ * @since 1.10.0
+ *
+ * @example
+ * // Get JSON data from a file via Ajax.
+ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
+ * $('#example').dataTable( {
+ * "ajax": "data.json"
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": "tableData"
+ * }
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
+ * // from a plain array rather than an array in an object
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": ""
+ * }
+ * } );
+ *
+ * @example
+ * // Manipulate the data returned from the server - add a link to data
+ * // (note this can, should, be done using `render` for the column - this
+ * // is just a simple example of how the data can be manipulated).
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": function ( json ) {
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
+ * }
+ * return json;
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Add data to the request
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "data": function ( d ) {
+ * return {
+ * "extra_search": $('#extra').val()
+ * };
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Send request as POST
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "type": "POST"
+ * }
+ * } );
+ *
+ * @example
+ * // Get the data from localStorage (could interface with a form for
+ * // adding, editing and removing rows).
+ * $('#example').dataTable( {
+ * "ajax": function (data, callback, settings) {
+ * callback(
+ * JSON.parse( localStorage.getItem('dataTablesData') )
+ * );
+ * }
+ * } );
+ */
+ "ajax": null,
+
+
+ /**
+ * This parameter allows you to readily specify the entries in the length drop
+ * down menu that DataTables shows when pagination is enabled. It can be
+ * either a 1D array of options which will be used for both the displayed
+ * option and the value, or a 2D array which will use the array in the first
+ * position as the value, and the array in the second position as the
+ * displayed options (useful for language strings such as 'All').
+ *
+ * Note that the `pageLength` property will be automatically set to the
+ * first value given in this array, unless `pageLength` is also provided.
+ * @type array
+ * @default [ 10, 25, 50, 100 ]
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.lengthMenu
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
+ * } );
+ * } );
+ */
+ "aLengthMenu": [10, 25, 50, 100],
+
+
+ /**
+ * The `columns` option in the initialisation parameter allows you to define
+ * details about the way individual columns behave. For a full list of
+ * column options that can be set, please see
+ * {@link DataTable.defaults.column}. Note that if you use `columns` to
+ * define your columns, you must have an entry in the array for every single
+ * column that you have in your table (these can be null if you don't which
+ * to specify any options).
+ * @member
+ *
+ * @name DataTable.defaults.column
+ */
+ "aoColumns": null,
+
+ /**
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
+ * column, multiple columns, or all columns, using the `targets` property of
+ * each object in the array. This allows great flexibility when creating
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
+ * columns you specifically want. `columnDefs` may use any of the column
+ * options available: {@link DataTable.defaults.column}, but it _must_
+ * have `targets` defined in each object in the array. Values in the `targets`
+ * array may be:
+ * <ul>
+ * <li>a string - class name will be matched on the TH for the column</li>
+ * <li>0 or a positive integer - column index counting from the left</li>
+ * <li>a negative integer - column index counting from the right</li>
+ * <li>the string "_all" - all columns (i.e. assign a default)</li>
+ * </ul>
+ * @member
+ *
+ * @name DataTable.defaults.columnDefs
+ */
+ "aoColumnDefs": null,
+
+
+ /**
+ * Basically the same as `search`, this parameter defines the individual column
+ * filtering state at initialisation time. The array must be of the same size
+ * as the number of columns, and each element be an object with the parameters
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
+ * accepted and the default will be used.
+ * @type array
+ * @default []
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.searchCols
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "searchCols": [
+ * null,
+ * { "search": "My filter" },
+ * null,
+ * { "search": "^[0-9]", "escapeRegex": false }
+ * ]
+ * } );
+ * } )
+ */
+ "aoSearchCols": [],
+
+
+ /**
+ * An array of CSS classes that should be applied to displayed rows. This
+ * array may be of any length, and DataTables will apply each class
+ * sequentially, looping when required.
+ * @type array
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
+ * options</i>
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.stripeClasses
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
+ * } );
+ * } )
+ */
+ "asStripeClasses": null,
+
+
+ /**
+ * Enable or disable automatic column width calculation. This can be disabled
+ * as an optimisation (it takes some time to calculate the widths) if the
+ * tables widths are passed in using `columns`.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.autoWidth
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "autoWidth": false
+ * } );
+ * } );
+ */
+ "bAutoWidth": true,
+
+
+ /**
+ * Deferred rendering can provide DataTables with a huge speed boost when you
+ * are using an Ajax or JS data source for the table. This option, when set to
+ * true, will cause DataTables to defer the creation of the table elements for
+ * each row until they are needed for a draw - saving a significant amount of
+ * time.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.deferRender
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajax": "sources/arrays.txt",
+ * "deferRender": true
+ * } );
+ * } );
+ */
+ "bDeferRender": false,
+
+
+ /**
+ * Replace a DataTable which matches the given selector and replace it with
+ * one which has the properties of the new initialisation object passed. If no
+ * table matches the selector, then the new DataTable will be constructed as
+ * per normal.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.destroy
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "srollY": "200px",
+ * "paginate": false
+ * } );
+ *
+ * // Some time later....
+ * $('#example').dataTable( {
+ * "filter": false,
+ * "destroy": true
+ * } );
+ * } );
+ */
+ "bDestroy": false,
+
+
+ /**
+ * Enable or disable filtering of data. Filtering in DataTables is "smart" in
+ * that it allows the end user to input multiple words (space separated) and
+ * will match a row containing those words, even if not in the order that was
+ * specified (this allow matching across multiple columns). Note that if you
+ * wish to use filtering in DataTables this must remain 'true' - to remove the
+ * default filtering input box and retain filtering abilities, please use
+ * {@link DataTable.defaults.dom}.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.searching
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "searching": false
+ * } );
+ * } );
+ */
+ "bFilter": true,
+
+
+ /**
+ * Enable or disable the table information display. This shows information
+ * about the data that is currently visible on the page, including information
+ * about filtered data if that action is being performed.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.info
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "info": false
+ * } );
+ * } );
+ */
+ "bInfo": true,
+
+
+ /**
+ * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
+ * slightly different and additional mark-up from what DataTables has
+ * traditionally used).
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.jQueryUI
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "jQueryUI": true
+ * } );
+ * } );
+ */
+ "bJQueryUI": false,
+
+
+ /**
+ * Allows the end user to select the size of a formatted page from a select
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.lengthChange
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "lengthChange": false
+ * } );
+ * } );
+ */
+ "bLengthChange": true,
+
+
+ /**
+ * Enable or disable pagination.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.paging
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "paging": false
+ * } );
+ * } );
+ */
+ "bPaginate": true,
+
+
+ /**
+ * Enable or disable the display of a 'processing' indicator when the table is
+ * being processed (e.g. a sort). This is particularly useful for tables with
+ * large amounts of data where it can take a noticeable amount of time to sort
+ * the entries.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.processing
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "processing": true
+ * } );
+ * } );
+ */
+ "bProcessing": false,
+
+
+ /**
+ * Retrieve the DataTables object for the given selector. Note that if the
+ * table has already been initialised, this parameter will cause DataTables
+ * to simply return the object that has already been set up - it will not take
+ * account of any changes you might have made to the initialisation object
+ * passed to DataTables (setting this parameter to true is an acknowledgement
+ * that you understand this). `destroy` can be used to reinitialise a table if
+ * you need.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.retrieve
+ *
+ * @example
+ * $(document).ready( function() {
+ * initTable();
+ * tableActions();
+ * } );
+ *
+ * function initTable ()
+ * {
+ * return $('#example').dataTable( {
+ * "scrollY": "200px",
+ * "paginate": false,
+ * "retrieve": true
+ * } );
+ * }
+ *
+ * function tableActions ()
+ * {
+ * var table = initTable();
+ * // perform API operations with oTable
+ * }
+ */
+ "bRetrieve": false,
+
+
+ /**
+ * When vertical (y) scrolling is enabled, DataTables will force the height of
+ * the table's viewport to the given height at all times (useful for layout).
+ * However, this can look odd when filtering data down to a small data set,
+ * and the footer is left "floating" further down. This parameter (when
+ * enabled) will cause DataTables to collapse the table's viewport down when
+ * the result set will fit within the given Y height.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.scrollCollapse
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollY": "200",
+ * "scrollCollapse": true
+ * } );
+ * } );
+ */
+ "bScrollCollapse": false,
+
+
+ /**
+ * Configure DataTables to use server-side processing. Note that the
+ * `ajax` parameter must also be given in order to give DataTables a
+ * source to obtain the required data for each draw.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverSide
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "xhr.php"
+ * } );
+ * } );
+ */
+ "bServerSide": false,
+
+
+ /**
+ * Enable or disable sorting of columns. Sorting of individual columns can be
+ * disabled by the `sortable` option for each column.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.ordering
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "ordering": false
+ * } );
+ * } );
+ */
+ "bSort": true,
+
+
+ /**
+ * Enable or display DataTables' ability to sort multiple columns at the
+ * same time (activated by shift-click by the user).
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.orderMulti
+ *
+ * @example
+ * // Disable multiple column sorting ability
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "orderMulti": false
+ * } );
+ * } );
+ */
+ "bSortMulti": true,
+
+
+ /**
+ * Allows control over whether DataTables should use the top (true) unique
+ * cell that is found for a single column, or the bottom (false - default).
+ * This is useful when using complex headers.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.orderCellsTop
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "orderCellsTop": true
+ * } );
+ * } );
+ */
+ "bSortCellsTop": false,
+
+
+ /**
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
+ * `sorting\_3` to the columns which are currently being sorted on. This is
+ * presented as a feature switch as it can increase processing time (while
+ * classes are removed and added) so for large data sets you might want to
+ * turn this off.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.orderClasses
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "orderClasses": false
+ * } );
+ * } );
+ */
+ "bSortClasses": true,
+
+
+ /**
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
+ * used to save table display information such as pagination information,
+ * display length, filtering and sorting. As such when the end user reloads
+ * the page the display display will match what thy had previously set up.
+ *
+ * Due to the use of `localStorage` the default state saving is not supported
+ * in IE6 or 7. If state saving is required in those browsers, use
+ * `stateSaveCallback` to provide a storage solution such as cookies.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.stateSave
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "stateSave": true
+ * } );
+ * } );
+ */
+ "bStateSave": false,
+
+
+ /**
+ * This function is called when a TR element is created (and all TD child
+ * elements have been inserted), or registered if using a DOM source, allowing
+ * manipulation of the TR element (adding classes etc).
+ * @type function
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} dataIndex The index of this row in the internal aoData array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.createdRow
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "createdRow": function( row, data, dataIndex ) {
+ * // Bold the grade for all 'A' grade browsers
+ * if ( data[4] == "A" )
+ * {
+ * $('td:eq(4)', row).html( '<b>A</b>' );
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnCreatedRow": null,
+
+
+ /**
+ * This function is called on every 'draw' event, and allows you to
+ * dynamically modify any aspect you want about the created DOM.
+ * @type function
+ * @param {object} settings DataTables settings object
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.drawCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "drawCallback": function( settings ) {
+ * alert( 'DataTables has redrawn the table' );
+ * }
+ * } );
+ * } );
+ */
+ "fnDrawCallback": null,
+
+
+ /**
+ * Identical to fnHeaderCallback() but for the table footer this function
+ * allows you to modify the table footer on every 'draw' event.
+ * @type function
+ * @param {node} foot "TR" element for the footer
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
+ * display array
+ * @param {int} end Index for the current display ending point in the
+ * display array
+ * @param {array int} display Index array to translate the visual position
+ * to the full data array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.footerCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "footerCallback": function( tfoot, data, start, end, display ) {
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
+ * }
+ * } );
+ * } )
+ */
+ "fnFooterCallback": null,
+
+
+ /**
+ * When rendering large numbers in the information element for the table
+ * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
+ * to have a comma separator for the 'thousands' units (e.g. 1 million is
+ * rendered as "1,000,000") to help readability for the end user. This
+ * function will override the default method DataTables uses.
+ * @type function
+ * @member
+ * @param {int} toFormat number to be formatted
+ * @returns {string} formatted string for DataTables to show the number
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.formatNumber
+ *
+ * @example
+ * // Format a number using a single quote for the separator (note that
+ * // this can also be done with the language.thousands option)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "formatNumber": function ( toFormat ) {
+ * return toFormat.toString().replace(
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
+ * );
+ * };
+ * } );
+ * } );
+ */
+ "fnFormatNumber": function (toFormat) {
+ return toFormat.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g,
+ this.oLanguage.sThousands
+ );
+ },
+
+
+ /**
+ * This function is called on every 'draw' event, and allows you to
+ * dynamically modify the header row. This can be used to calculate and
+ * display useful information about the table.
+ * @type function
+ * @param {node} head "TR" element for the header
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
+ * display array
+ * @param {int} end Index for the current display ending point in the
+ * display array
+ * @param {array int} display Index array to translate the visual position
+ * to the full data array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.headerCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "fheaderCallback": function( head, data, start, end, display ) {
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
+ * }
+ * } );
+ * } )
+ */
+ "fnHeaderCallback": null,
+
+
+ /**
+ * The information element can be used to convey information about the current
+ * state of the table. Although the internationalisation options presented by
+ * DataTables are quite capable of dealing with most customisations, there may
+ * be times where you wish to customise the string further. This callback
+ * allows you to do exactly that.
+ * @type function
+ * @param {object} oSettings DataTables settings object
+ * @param {int} start Starting position in data for the draw
+ * @param {int} end End position in data for the draw
+ * @param {int} max Total number of rows in the table (regardless of
+ * filtering)
+ * @param {int} total Total number of rows in the data set, after filtering
+ * @param {string} pre The string that DataTables has formatted using it's
+ * own rules
+ * @returns {string} The string to be displayed in the information element.
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.infoCallback
+ *
+ * @example
+ * $('#example').dataTable( {
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
+ * return start +" to "+ end;
+ * }
+ * } );
+ */
+ "fnInfoCallback": null,
+
+
+ /**
+ * Called when the table has been initialised. Normally DataTables will
+ * initialise sequentially and there will be no need for this function,
+ * however, this does not hold true when using external language information
+ * since that is obtained using an async XHR call.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} json The JSON object request from the server - only
+ * present if client-side Ajax sourced data is used
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.initComplete
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "initComplete": function(settings, json) {
+ * alert( 'DataTables has finished its initialisation.' );
+ * }
+ * } );
+ * } )
+ */
+ "fnInitComplete": null,
+
+
+ /**
+ * Called at the very start of each table draw and can be used to cancel the
+ * draw by returning false, any other return (including undefined) results in
+ * the full draw occurring).
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @returns {boolean} False will cancel the draw, anything else (including no
+ * return) will allow it to complete.
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.preDrawCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "preDrawCallback": function( settings ) {
+ * if ( $('#test').val() == 1 ) {
+ * return false;
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnPreDrawCallback": null,
+
+
+ /**
+ * This function allows you to 'post process' each row after it have been
+ * generated for each table draw, but before it is rendered on screen. This
+ * function might be used for setting the row class name etc.
+ * @type function
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} displayIndex The display index for the current table draw
+ * @param {int} displayIndexFull The index of the data in the full list of
+ * rows (after filtering)
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.rowCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
+ * // Bold the grade for all 'A' grade browsers
+ * if ( data[4] == "A" ) {
+ * $('td:eq(4)', row).html( '<b>A</b>' );
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnRowCallback": null,
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * This parameter allows you to override the default function which obtains
+ * the data from the server so something more suitable for your application.
+ * For example you could use POST data, or pull information from a Gears or
+ * AIR database.
+ * @type function
+ * @member
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
+ * @param {array} data A key/value pair object containing the data to send
+ * to the server
+ * @param {function} callback to be called on completion of the data get
+ * process that will draw the data on the page.
+ * @param {object} settings DataTables settings object
+ *
+ * @dtopt Callbacks
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverData
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "fnServerData": null,
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * It is often useful to send extra data to the server when making an Ajax
+ * request - for example custom filtering information, and this callback
+ * function makes it trivial to send extra information to the server. The
+ * passed in parameter is the data set that has been constructed by
+ * DataTables, and you can add to this or modify it as you require.
+ * @type function
+ * @param {array} data Data array (array of objects which are name/value
+ * pairs) that has been constructed by DataTables and will be sent to the
+ * server. In the case of Ajax sourced data with server-side processing
+ * this will be an empty array, for server-side processing there will be a
+ * significant number of parameters!
+ * @returns {undefined} Ensure that you modify the data array passed in,
+ * as this is passed by reference.
+ *
+ * @dtopt Callbacks
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverParams
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "fnServerParams": null,
+
+
+ /**
+ * Load the table state. With this function you can define from where, and how, the
+ * state of a table is loaded. By default DataTables will load from `localStorage`
+ * but you might wish to use a server-side database or cookies.
+ * @type function
+ * @member
+ * @param {object} settings DataTables settings object
+ * @return {object} The DataTables state object to be loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoadCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadCallback": function (settings) {
+ * var o;
+ *
+ * // Send an Ajax request to the server to get the data. Note that
+ * // this is a synchronous request.
+ * $.ajax( {
+ * "url": "/state_load",
+ * "async": false,
+ * "dataType": "json",
+ * "success": function (json) {
+ * o = json;
+ * }
+ * } );
+ *
+ * return o;
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoadCallback": function (settings) {
+ try {
+ return JSON.parse(
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
+ 'DataTables_' + settings.sInstance + '_' + location.pathname
+ )
+ );
+ } catch (e) {}
+ },
+
+
+ /**
+ * Callback which allows modification of the saved state prior to loading that state.
+ * This callback is called when the table is loading state from the stored data, but
+ * prior to the settings object being modified by the saved state. Note that for
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
+ * a plug-in.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that is to be loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoadParams
+ *
+ * @example
+ * // Remove a saved filter, so filtering is never loaded
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
+ * }
+ * } );
+ * } );
+ *
+ * @example
+ * // Disallow state loading by returning false
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
+ * return false;
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoadParams": null,
+
+
+ /**
+ * Callback that is called when the state has been loaded from the state saving method
+ * and the DataTables settings object has been modified as a result of the loaded state.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that was loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoaded
+ *
+ * @example
+ * // Show an alert with the filtering value that was saved
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoaded": function (settings, data) {
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoaded": null,
+
+
+ /**
+ * Save the table state. This function allows you to define where and how the state
+ * information for the table is stored By default DataTables will use `localStorage`
+ * but you might wish to use a server-side database or cookies.
+ * @type function
+ * @member
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateSaveCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateSaveCallback": function (settings, data) {
+ * // Send an Ajax request to the server with the state object
+ * $.ajax( {
+ * "url": "/state_save",
+ * "data": data,
+ * "dataType": "json",
+ * "method": "POST"
+ * "success": function () {}
+ * } );
+ * }
+ * } );
+ * } );
+ */
+ "fnStateSaveCallback": function (settings, data) {
+ try {
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
+ 'DataTables_' + settings.sInstance + '_' + location.pathname,
+ JSON.stringify(data)
+ );
+ } catch (e) {}
+ },
+
+
+ /**
+ * Callback which allows modification of the state to be saved. Called when the table
+ * has changed state a new state save is required. This method allows modification of
+ * the state saving object prior to actually doing the save, including addition or
+ * other state properties or modification. Note that for plug-in authors, you should
+ * use the `stateSaveParams` event to save parameters for a plug-in.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateSaveParams
+ *
+ * @example
+ * // Remove a saved filter, so filtering is never saved
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateSaveParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
+ * }
+ * } );
+ * } );
+ */
+ "fnStateSaveParams": null,
+
+
+ /**
+ * Duration for which the saved state information is considered valid. After this period
+ * has elapsed the state will be returned to the default.
+ * Value is given in seconds.
+ * @type int
+ * @default 7200 <i>(2 hours)</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.stateDuration
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateDuration": 60*60*24; // 1 day
+ * } );
+ * } )
+ */
+ "iStateDuration": 7200,
+
+
+ /**
+ * When enabled DataTables will not make a request to the server for the first
+ * page draw - rather it will use the data already on the page (no sorting etc
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
+ * is used to indicate that deferred loading is required, but it is also used
+ * to tell DataTables how many records there are in the full table (allowing
+ * the information element and pagination to be displayed correctly). In the case
+ * where a filtering is applied to the table on initial load, this can be
+ * indicated by giving the parameter as an array, where the first element is
+ * the number of records available after filtering and the second element is the
+ * number of records without filtering (allowing the table information element
+ * to be shown correctly).
+ * @type int | array
+ * @default null
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.deferLoading
+ *
+ * @example
+ * // 57 records available in the table, no filtering applied
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": 57
+ * } );
+ * } );
+ *
+ * @example
+ * // 57 records after filtering, 100 without filtering (an initial filter applied)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": [ 57, 100 ],
+ * "search": {
+ * "search": "my_filter"
+ * }
+ * } );
+ * } );
+ */
+ "iDeferLoading": null,
+
+
+ /**
+ * Number of rows to display on a single page when using pagination. If
+ * feature enabled (`lengthChange`) then the end user will be able to override
+ * this to a custom setting using a pop-up menu.
+ * @type int
+ * @default 10
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.pageLength
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "pageLength": 50
+ * } );
+ * } )
+ */
+ "iDisplayLength": 10,
+
+
+ /**
+ * Define the starting point for data display when using DataTables with
+ * pagination. Note that this parameter is the number of records, rather than
+ * the page number, so if you have 10 records per page and want to start on
+ * the third page, it should be "20".
+ * @type int
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.displayStart
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "displayStart": 20
+ * } );
+ * } )
+ */
+ "iDisplayStart": 0,
+
+
+ /**
+ * By default DataTables allows keyboard navigation of the table (sorting, paging,
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
+ * allows you to tab through the controls and press the enter key to activate them.
+ * The tabindex is default 0, meaning that the tab follows the flow of the document.
+ * You can overrule this using this parameter if you wish. Use a value of -1 to
+ * disable built-in keyboard navigation.
+ * @type int
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.tabIndex
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "tabIndex": 1
+ * } );
+ * } );
+ */
+ "iTabIndex": 0,
+
+
+ /**
+ * Classes that DataTables assigns to the various components and features
+ * that it adds to the HTML table. This allows classes to be configured
+ * during initialisation in addition to through the static
+ * {@link DataTable.ext.oStdClasses} object).
+ * @namespace
+ * @name DataTable.defaults.classes
+ */
+ "oClasses": {},
+
+
+ /**
+ * All strings that DataTables uses in the user interface that it creates
+ * are defined in this object, allowing you to modified them individually or
+ * completely replace them all as required.
+ * @namespace
+ * @name DataTable.defaults.language
+ */
+ "oLanguage": {
+ /**
+ * Strings that are used for WAI-ARIA labels and controls only (these are not
+ * actually visible on the page, but will be read by screenreaders, and thus
+ * must be internationalised as well).
+ * @namespace
+ * @name DataTable.defaults.language.aria
+ */
+ "oAria": {
+ /**
+ * ARIA label that is added to the table headers when the column may be
+ * sorted ascending by activing the column (click or return when focused).
+ * Note that the column header is prefixed to this string.
+ * @type string
+ * @default : activate to sort column ascending
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.aria.sortAscending
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "aria": {
+ * "sortAscending": " - click/return to sort ascending"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sSortAscending": ": activate to sort column ascending",
+
+ /**
+ * ARIA label that is added to the table headers when the column may be
+ * sorted descending by activing the column (click or return when focused).
+ * Note that the column header is prefixed to this string.
+ * @type string
+ * @default : activate to sort column ascending
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.aria.sortDescending
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "aria": {
+ * "sortDescending": " - click/return to sort descending"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sSortDescending": ": activate to sort column descending"
+ },
+
+ /**
+ * Pagination string used by DataTables for the built-in pagination
+ * control types.
+ * @namespace
+ * @name DataTable.defaults.language.paginate
+ */
+ "oPaginate": {
+ /**
+ * Text to use when using the 'full_numbers' type of pagination for the
+ * button to take the user to the first page.
+ * @type string
+ * @default First
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.paginate.first
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "paginate": {
+ * "first": "First page"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sFirst": "First",
+
+
+ /**
+ * Text to use when using the 'full_numbers' type of pagination for the
+ * button to take the user to the last page.
+ * @type string
+ * @default Last
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.paginate.last
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "paginate": {
+ * "last": "Last page"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sLast": "Last",
+
+
+ /**
+ * Text to use for the 'next' pagination button (to take the user to the
+ * next page).
+ * @type string
+ * @default Next
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.paginate.next
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "paginate": {
+ * "next": "Next page"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sNext": "Next",
+
+
+ /**
+ * Text to use for the 'previous' pagination button (to take the user to
+ * the previous page).
+ * @type string
+ * @default Previous
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.paginate.previous
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "paginate": {
+ * "previous": "Previous page"
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "sPrevious": "Previous"
+ },
+
+ /**
+ * This string is shown in preference to `zeroRecords` when the table is
+ * empty of data (regardless of filtering). Note that this is an optional
+ * parameter - if it is not given, the value of `zeroRecords` will be used
+ * instead (either the default or given value).
+ * @type string
+ * @default No data available in table
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.emptyTable
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "emptyTable": "No data available in table"
+ * }
+ * } );
+ * } );
+ */
+ "sEmptyTable": "No data available in table",
+
+
+ /**
+ * This string gives information to the end user about the information
+ * that is current on display on the page. The following tokens can be
+ * used in the string and will be dynamically replaced as the table
+ * display updates. This tokens can be placed anywhere in the string, or
+ * removed as needed by the language requires:
+ *
+ * * `\_START\_` - Display index of the first record on the current page
+ * * `\_END\_` - Display index of the last record on the current page
+ * * `\_TOTAL\_` - Number of records in the table after filtering
+ * * `\_MAX\_` - Number of records in the table without filtering
+ * * `\_PAGE\_` - Current page number
+ * * `\_PAGES\_` - Total number of pages of data in the table
+ *
+ * @type string
+ * @default Showing _START_ to _END_ of _TOTAL_ entries
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.info
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "info": "Showing page _PAGE_ of _PAGES_"
+ * }
+ * } );
+ * } );
+ */
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
+
+
+ /**
+ * Display information string for when the table is empty. Typically the
+ * format of this string should match `info`.
+ * @type string
+ * @default Showing 0 to 0 of 0 entries
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoEmpty
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "infoEmpty": "No entries to show"
+ * }
+ * } );
+ * } );
+ */
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
+
+
+ /**
+ * When a user filters the information in a table, this string is appended
+ * to the information (`info`) to give an idea of how strong the filtering
+ * is. The variable _MAX_ is dynamically updated.
+ * @type string
+ * @default (filtered from _MAX_ total entries)
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoFiltered
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "infoFiltered": " - filtering from _MAX_ records"
+ * }
+ * } );
+ * } );
+ */
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
+
+
+ /**
+ * If can be useful to append extra information to the info string at times,
+ * and this variable does exactly that. This information will be appended to
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
+ * being used) at all times.
+ * @type string
+ * @default <i>Empty string</i>
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoPostFix
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "infoPostFix": "All records shown are derived from real information."
+ * }
+ * } );
+ * } );
+ */
+ "sInfoPostFix": "",
+
+
+ /**
+ * This decimal place operator is a little different from the other
+ * language options since DataTables doesn't output floating point
+ * numbers, so it won't ever use this for display of a number. Rather,
+ * what this parameter does is modify the sort methods of the table so
+ * that numbers which are in a format which has a character other than
+ * a period (`.`) as a decimal place will be sorted numerically.
+ *
+ * Note that numbers with different decimal places cannot be shown in
+ * the same table and still be sortable, the table must be consistent.
+ * However, multiple different tables on the page can use different
+ * decimal place characters.
+ * @type string
+ * @default
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.decimal
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "decimal": ","
+ * "thousands": "."
+ * }
+ * } );
+ * } );
+ */
+ "sDecimal": "",
+
+
+ /**
+ * DataTables has a build in number formatter (`formatNumber`) which is
+ * used to format large numbers that are used in the table information.
+ * By default a comma is used, but this can be trivially changed to any
+ * character you wish with this parameter.
+ * @type string
+ * @default ,
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.thousands
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "thousands": "'"
+ * }
+ * } );
+ * } );
+ */
+ "sThousands": ",",
+
+
+ /**
+ * Detail the action that will be taken when the drop down menu for the
+ * pagination length option is changed. The '_MENU_' variable is replaced
+ * with a default select list of 10, 25, 50 and 100, and can be replaced
+ * with a custom select box if required.
+ * @type string
+ * @default Show _MENU_ entries
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.lengthMenu
+ *
+ * @example
+ * // Language change only
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "lengthMenu": "Display _MENU_ records"
+ * }
+ * } );
+ * } );
+ *
+ * @example
+ * // Language and options change
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "lengthMenu": 'Display <select>'+
+ * '<option value="10">10</option>'+
+ * '<option value="20">20</option>'+
+ * '<option value="30">30</option>'+
+ * '<option value="40">40</option>'+
+ * '<option value="50">50</option>'+
+ * '<option value="-1">All</option>'+
+ * '</select> records'
+ * }
+ * } );
+ * } );
+ */
+ "sLengthMenu": "Show _MENU_ entries",
+
+
+ /**
+ * When using Ajax sourced data and during the first draw when DataTables is
+ * gathering the data, this message is shown in an empty row in the table to
+ * indicate to the end user the the data is being loaded. Note that this
+ * parameter is not used when loading data by server-side processing, just
+ * Ajax sourced data with client-side processing.
+ * @type string
+ * @default Loading...
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.loadingRecords
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "loadingRecords": "Please wait - loading..."
+ * }
+ * } );
+ * } );
+ */
+ "sLoadingRecords": "Loading...",
+
+
+ /**
+ * Text which is displayed when the table is processing a user action
+ * (usually a sort command or similar).
+ * @type string
+ * @default Processing...
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.processing
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "processing": "DataTables is currently busy"
+ * }
+ * } );
+ * } );
+ */
+ "sProcessing": "Processing...",
+
+
+ /**
+ * Details the actions that will be taken when the user types into the
+ * filtering input text box. The variable "_INPUT_", if used in the string,
+ * is replaced with the HTML text box for the filtering input allowing
+ * control over where it appears in the string. If "_INPUT_" is not given
+ * then the input box is appended to the string automatically.
+ * @type string
+ * @default Search:
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.search
+ *
+ * @example
+ * // Input text box will be appended at the end automatically
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "search": "Filter records:"
+ * }
+ * } );
+ * } );
+ *
+ * @example
+ * // Specify where the filter should appear
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "search": "Apply filter _INPUT_ to table"
+ * }
+ * } );
+ * } );
+ */
+ "sSearch": "Search:",
+
+
+ /**
+ * Assign a `placeholder` attribute to the search `input` element
+ * @type string
+ * @default
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.searchPlaceholder
+ */
+ "sSearchPlaceholder": "",
+
+
+ /**
+ * All of the language information can be stored in a file on the
+ * server-side, which DataTables will look up if this parameter is passed.
+ * It must store the URL of the language file, which is in a JSON format,
+ * and the object has the same properties as the oLanguage object in the
+ * initialiser object (i.e. the above parameters). Please refer to one of
+ * the example language files to see how this works in action.
+ * @type string
+ * @default <i>Empty string - i.e. disabled</i>
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.url
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
+ * }
+ * } );
+ * } );
+ */
+ "sUrl": "",
+
+
+ /**
+ * Text shown inside the table records when the is no information to be
+ * displayed after filtering. `emptyTable` is shown when there is simply no
+ * information in the table at all (regardless of filtering).
+ * @type string
+ * @default No matching records found
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.zeroRecords
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "zeroRecords": "No records to display"
+ * }
+ * } );
+ * } );
+ */
+ "sZeroRecords": "No matching records found"
+ },
+
+
+ /**
+ * This parameter allows you to have define the global filtering state at
+ * initialisation time. As an object the `search` parameter must be
+ * defined, but all other parameters are optional. When `regex` is true,
+ * the search string will be treated as a regular expression, when false
+ * (default) it will be treated as a straight string. When `smart`
+ * DataTables will use it's smart filtering methods (to word match at
+ * any point in the data), when false this will not be done.
+ * @namespace
+ * @extends DataTable.models.oSearch
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.search
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "search": {"search": "Initial search"}
+ * } );
+ * } )
+ */
+ "oSearch": $.extend({}, DataTable.models.oSearch),
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * By default DataTables will look for the property `data` (or `aaData` for
+ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
+ * source or for server-side processing - this parameter allows that
+ * property to be changed. You can use Javascript dotted object notation to
+ * get a data source for multiple levels of nesting.
+ * @type string
+ * @default data
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.ajaxDataProp
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sAjaxDataProp": "data",
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * You can instruct DataTables to load data from an external
+ * source using this parameter (use aData if you want to pass data in you
+ * already have). Simply provide a url a JSON object can be obtained from.
+ * @type string
+ * @default null
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.ajaxSource
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sAjaxSource": null,
+
+
+ /**
+ * This initialisation variable allows you to specify exactly where in the
+ * DOM you want DataTables to inject the various controls it adds to the page
+ * (for example you might want the pagination controls at the top of the
+ * table). DIV elements (with or without a custom class) can also be added to
+ * aid styling. The follow syntax is used:
+ * <ul>
+ * <li>The following options are allowed:
+ * <ul>
+ * <li>'l' - Length changing</li>
+ * <li>'f' - Filtering input</li>
+ * <li>'t' - The table!</li>
+ * <li>'i' - Information</li>
+ * <li>'p' - Pagination</li>
+ * <li>'r' - pRocessing</li>
+ * </ul>
+ * </li>
+ * <li>The following constants are allowed:
+ * <ul>
+ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
+ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
+ * </ul>
+ * </li>
+ * <li>The following syntax is expected:
+ * <ul>
+ * <li>'&lt;' and '&gt;' - div elements</li>
+ * <li>'&lt;"class" and '&gt;' - div with a class</li>
+ * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
+ * </ul>
+ * </li>
+ * <li>Examples:
+ * <ul>
+ * <li>'&lt;"wrapper"flipt&gt;'</li>
+ * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @type string
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.dom
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
+ * } );
+ * } );
+ */
+ "sDom": "lfrtip",
+
+
+ /**
+ * Search delay option. This will throttle full table searches that use the
+ * DataTables provided search input element (it does not effect calls to
+ * `dt-api search()`, providing a delay before the search is made.
+ * @type integer
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.searchDelay
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "searchDelay": 200
+ * } );
+ * } )
+ */
+ "searchDelay": null,
+
+
+ /**
+ * DataTables features four different built-in options for the buttons to
+ * display for pagination control:
+ *
+ * * `simple` - 'Previous' and 'Next' buttons only
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
+ * page numbers
+ *
+ * Further methods can be added using {@link DataTable.ext.oPagination}.
+ * @type string
+ * @default simple_numbers
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.pagingType
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "pagingType": "full_numbers"
+ * } );
+ * } )
+ */
+ "sPaginationType": "simple_numbers",
+
+
+ /**
+ * Enable horizontal scrolling. When a table is too wide to fit into a
+ * certain layout, or you have a large number of columns in the table, you
+ * can enable x-scrolling to show the table in a viewport, which can be
+ * scrolled. This property can be `true` which will allow the table to
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
+ * case it will be treated as a pixel measurement). Setting as simply `true`
+ * is recommended.
+ * @type boolean|string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.scrollX
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollX": true,
+ * "scrollCollapse": true
+ * } );
+ * } );
+ */
+ "sScrollX": "",
+
+
+ /**
+ * This property can be used to force a DataTable to use more width than it
+ * might otherwise do when x-scrolling is enabled. For example if you have a
+ * table which requires to be well spaced, this parameter is useful for
+ * "over-sizing" the table, and thus forcing scrolling. This property can by
+ * any CSS unit, or a number (in which case it will be treated as a pixel
+ * measurement).
+ * @type string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.scrollXInner
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollX": "100%",
+ * "scrollXInner": "110%"
+ * } );
+ * } );
+ */
+ "sScrollXInner": "",
+
+
+ /**
+ * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
+ * to the given height, and enable scrolling for any data which overflows the
+ * current viewport. This can be used as an alternative to paging to display
+ * a lot of data in a small area (although paging and scrolling can both be
+ * enabled at the same time). This property can be any CSS unit, or a number
+ * (in which case it will be treated as a pixel measurement).
+ * @type string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.scrollY
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollY": "200px",
+ * "paginate": false
+ * } );
+ * } );
+ */
+ "sScrollY": "",
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * Set the HTTP method that is used to make the Ajax call for server-side
+ * processing or Ajax sourced data.
+ * @type string
+ * @default GET
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverMethod
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sServerMethod": "GET",
+
+
+ /**
+ * DataTables makes use of renderers when displaying HTML elements for
+ * a table. These renderers can be added or modified by plug-ins to
+ * generate suitable mark-up for a site. For example the Bootstrap
+ * integration plug-in for DataTables uses a paging button renderer to
+ * display pagination buttons in the mark-up required by Bootstrap.
+ *
+ * For further information about the renderers available see
+ * DataTable.ext.renderer
+ * @type string|object
+ * @default null
+ *
+ * @name DataTable.defaults.renderer
+ *
+ */
+ "renderer": null,
+
+
+ /**
+ * Set the data property name that DataTables should use to get a row's id
+ * to set as the `id` property in the node.
+ * @type string
+ * @default DT_RowId
+ *
+ * @name DataTable.defaults.rowId
+ */
+ "rowId": "DT_RowId"
+ };
+
+ _fnHungarianMap(DataTable.defaults);
+
+
+ /*
+ * Developer note - See note in model.defaults.js about the use of Hungarian
+ * notation and camel case.
+ */
+
+ /**
+ * Column options that can be given to DataTables at initialisation time.
+ * @namespace
+ */
+ DataTable.defaults.column = {
+ /**
+ * Define which column(s) an order will occur on for this column. This
+ * allows a column's ordering to take multiple columns into account when
+ * doing a sort or use the data from a different column. For example first
+ * name / last name columns make sense to do a multi-column sort over the
+ * two columns.
+ * @type array|int
+ * @default null <i>Takes the value of the column index automatically</i>
+ *
+ * @name DataTable.defaults.column.orderData
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
+ * { "orderData": 2, "targets": [ 2 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "orderData": [ 0, 1 ] },
+ * { "orderData": [ 1, 0 ] },
+ * { "orderData": 2 },
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "aDataSort": null,
+ "iDataSort": -1,
+
+
+ /**
+ * You can control the default ordering direction, and even alter the
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
+ * using this parameter.
+ * @type array
+ * @default [ 'asc', 'desc' ]
+ *
+ * @name DataTable.defaults.column.orderSequence
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * { "orderSequence": [ "asc" ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
+ * { "orderSequence": [ "desc" ] },
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "asSorting": ['asc', 'desc'],
+
+
+ /**
+ * Enable or disable filtering on the data in this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.searchable
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "searchable": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "searchable": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bSearchable": true,
+
+
+ /**
+ * Enable or disable ordering on this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.orderable
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderable": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "orderable": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bSortable": true,
+
+
+ /**
+ * Enable or disable the display of this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.visible
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "visible": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "visible": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bVisible": true,
+
+
+ /**
+ * Developer definable function that is called whenever a cell is created (Ajax source,
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
+ * allowing you to modify the DOM element (add background colour for example) when the
+ * element is available.
+ * @type function
+ * @param {element} td The TD node that has been created
+ * @param {*} cellData The Data for the cell
+ * @param {array|object} rowData The data for the whole row
+ * @param {int} row The row index for the aoData data store
+ * @param {int} col The column index for aoColumns
+ *
+ * @name DataTable.defaults.column.createdCell
+ * @dtopt Columns
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [3],
+ * "createdCell": function (td, cellData, rowData, row, col) {
+ * if ( cellData == "1.7" ) {
+ * $(td).css('color', 'blue')
+ * }
+ * }
+ * } ]
+ * });
+ * } );
+ */
+ "fnCreatedCell": null,
+
+
+ /**
+ * This parameter has been replaced by `data` in DataTables to ensure naming
+ * consistency. `dataProp` can still be used, as there is backwards
+ * compatibility in DataTables for this option, but it is strongly
+ * recommended that you use `data` in preference to `dataProp`.
+ * @name DataTable.defaults.column.dataProp
+ */
+
+
+ /**
+ * This property can be used to read data from any data source property,
+ * including deeply nested objects / properties. `data` can be given in a
+ * number of different ways which effect its behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object. Note that
+ * function notation is recommended for use in `render` rather than
+ * `data` as it is much simpler to use as a renderer.
+ * * `null` - use the original data source for the row rather than plucking
+ * data directly from it. This action has effects on two other
+ * initialisation options:
+ * * `defaultContent` - When null is given as the `data` option and
+ * `defaultContent` is specified for the column, the value defined by
+ * `defaultContent` will be used for the cell.
+ * * `render` - When null is used for the `data` option and the `render`
+ * option is specified for the column, the whole data source for the
+ * row is used for the renderer.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * `{array|object}` The data source for the row
+ * * `{string}` The type call data requested - this will be 'set' when
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
+ * when gathering data. Note that when `undefined` is given for the
+ * type DataTables expects to get the raw data for the object back<
+ * * `{*}` Data to set when the second parameter is 'set'.
+ * * Return:
+ * * The return value from the function is not required when 'set' is
+ * the type of call, but otherwise the return is what will be used
+ * for the data requested.
+ *
+ * Note that `data` is a getter and setter option. If you just require
+ * formatting of data for output, you will likely want to use `render` which
+ * is simply a getter and thus simpler to use.
+ *
+ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
+ * name change reflects the flexibility of this property and is consistent
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
+ * be used by DataTables, as it automatically maps the old name to the new
+ * if required.
+ *
+ * @type string|int|function|null
+ * @default null <i>Use automatically calculated column index</i>
+ *
+ * @name DataTable.defaults.column.data
+ * @dtopt Columns
+ *
+ * @example
+ * // Read table data from objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {value},
+ * // "version": {value},
+ * // "grade": {value}
+ * // }
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/objects.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform" },
+ * { "data": "version" },
+ * { "data": "grade" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Read information from deeply nested objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {
+ * // "inner": {value}
+ * // },
+ * // "details": [
+ * // {value}, {value}
+ * // ]
+ * // }
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform.inner" },
+ * { "data": "platform.details.0" },
+ * { "data": "platform.details.1" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `data` as a function to provide different information for
+ * // sorting, filtering and display. In this case, currency (price)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": function ( source, type, val ) {
+ * if (type === 'set') {
+ * source.price = val;
+ * // Store the computed dislay and filter values for efficiency
+ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
+ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
+ * return;
+ * }
+ * else if (type === 'display') {
+ * return source.price_display;
+ * }
+ * else if (type === 'filter') {
+ * return source.price_filter;
+ * }
+ * // 'sort', 'type' and undefined all just use the integer
+ * return source.price;
+ * }
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using default content
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null,
+ * "defaultContent": "Click to edit"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using array notation - outputting a list from an array
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "name[, ]"
+ * } ]
+ * } );
+ * } );
+ *
+ */
+ "mData": null,
+
+
+ /**
+ * This property is the rendering partner to `data` and it is suggested that
+ * when you want to manipulate data for display (including filtering,
+ * sorting etc) without altering the underlying data for the table, use this
+ * property. `render` can be considered to be the the read only companion to
+ * `data` which is read / write (then as such more complex). Like `data`
+ * this option can be given in a number of different ways to effect its
+ * behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object.
+ * * `object` - use different data for the different data types requested by
+ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
+ * of the object is the data type the property refers to and the value can
+ * defined using an integer, string or function using the same rules as
+ * `render` normally does. Note that an `_` option _must_ be specified.
+ * This is the default value to use if you haven't specified a value for
+ * the data type requested by DataTables.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * {array|object} The data source for the row (based on `data`)
+ * * {string} The type call data requested - this will be 'filter',
+ * 'display', 'type' or 'sort'.
+ * * {array|object} The full data source for the row (not based on
+ * `data`)
+ * * Return:
+ * * The return value from the function is what will be used for the
+ * data requested.
+ *
+ * @type string|int|function|object|null
+ * @default null Use the data source value.
+ *
+ * @name DataTable.defaults.column.render
+ * @dtopt Columns
+ *
+ * @example
+ * // Create a comma separated list from an array of objects
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * {
+ * "data": "platform",
+ * "render": "[, ].name"
+ * }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Execute a function to obtain data
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": "browserName()"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // As an object, extracting different data for the different types
+ * // This would be used with a data source such as:
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
+ * // the formatted phone number is the one that is shown in the table.
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": {
+ * "_": "phone",
+ * "filter": "phone_filter",
+ * "display": "phone_display"
+ * }
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Use as a function to create a link from the data source
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "download_link",
+ * "render": function ( data, type, full ) {
+ * return '<a href="'+data+'">Download</a>';
+ * }
+ * } ]
+ * } );
+ * } );
+ */
+ "mRender": null,
+
+
+ /**
+ * Change the cell type created for the column - either TD cells or TH cells. This
+ * can be useful as TH cells have semantic meaning in the table body, allowing them
+ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
+ * @type string
+ * @default td
+ *
+ * @name DataTable.defaults.column.cellType
+ * @dtopt Columns
+ *
+ * @example
+ * // Make the first column use TH cells
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "cellType": "th"
+ * } ]
+ * } );
+ * } );
+ */
+ "sCellType": "td",
+
+
+ /**
+ * Class to give to each cell in this column.
+ * @type string
+ * @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.class
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "class": "my_class", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "class": "my_class" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sClass": "",
+
+ /**
+ * When DataTables calculates the column widths to assign to each column,
+ * it finds the longest string in each column and then constructs a
+ * temporary table and reads the widths from that. The problem with this
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * string - thus the calculation can go wrong (doing it properly and putting
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
+ * a "work around" we provide this option. It will append its value to the
+ * text that is found to be the longest string for the column - i.e. padding.
+ * Generally you shouldn't need this!
+ * @type string
+ * @default <i>Empty string<i>
+ *
+ * @name DataTable.defaults.column.contentPadding
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * null,
+ * {
+ * "contentPadding": "mmm"
+ * }
+ * ]
+ * } );
+ * } );
+ */
+ "sContentPadding": "",
+
+
+ /**
+ * Allows a default value to be given for a column's data, and will be used
+ * whenever a null data source is encountered (this can be because `data`
+ * is set to null, or because the data source itself is null).
+ * @type string
+ * @default null
+ *
+ * @name DataTable.defaults.column.defaultContent
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * {
+ * "data": null,
+ * "defaultContent": "Edit",
+ * "targets": [ -1 ]
+ * }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * null,
+ * {
+ * "data": null,
+ * "defaultContent": "Edit"
+ * }
+ * ]
+ * } );
+ * } );
+ */
+ "sDefaultContent": null,
+
+
+ /**
+ * This parameter is only used in DataTables' server-side processing. It can
+ * be exceptionally useful to know what columns are being displayed on the
+ * client side, and to map these to database fields. When defined, the names
+ * also allow DataTables to reorder information from the server if it comes
+ * back in an unexpected order (i.e. if you switch your columns around on the
+ * client-side, your server-side code does not also need updating).
+ * @type string
+ * @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.name
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "name": "engine", "targets": [ 0 ] },
+ * { "name": "browser", "targets": [ 1 ] },
+ * { "name": "platform", "targets": [ 2 ] },
+ * { "name": "version", "targets": [ 3 ] },
+ * { "name": "grade", "targets": [ 4 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "name": "engine" },
+ * { "name": "browser" },
+ * { "name": "platform" },
+ * { "name": "version" },
+ * { "name": "grade" }
+ * ]
+ * } );
+ * } );
+ */
+ "sName": "",
+
+
+ /**
+ * Defines a data source type for the ordering which can be used to read
+ * real-time information from the table (updating the internally cached
+ * version) prior to ordering. This allows ordering to occur on user
+ * editable elements such as form inputs.
+ * @type string
+ * @default std
+ *
+ * @name DataTable.defaults.column.orderDataType
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
+ * { "type": "numeric", "targets": [ 3 ] },
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * { "orderDataType": "dom-text" },
+ * { "orderDataType": "dom-text", "type": "numeric" },
+ * { "orderDataType": "dom-select" },
+ * { "orderDataType": "dom-checkbox" }
+ * ]
+ * } );
+ * } );
+ */
+ "sSortDataType": "std",
+
+
+ /**
+ * The title of this column.
+ * @type string
+ * @default null <i>Derived from the 'TH' value for this column in the
+ * original HTML table.</i>
+ *
+ * @name DataTable.defaults.column.title
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "title": "My column title", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "title": "My column title" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sTitle": null,
+
+
+ /**
+ * The type allows you to specify how the data for this column will be
+ * ordered. Four types (string, numeric, date and html (which will strip
+ * HTML tags before ordering)) are currently available. Note that only date
+ * formats understood by Javascript's Date() object will be accepted as type
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
+ * through plug-ins.
+ * @type string
+ * @default null <i>Auto-detected from raw data</i>
+ *
+ * @name DataTable.defaults.column.type
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "type": "html", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "type": "html" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sType": null,
+
+
+ /**
+ * Defining the width of the column, this parameter may take any CSS value
+ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
+ * been given a specific width through this interface ensuring that the table
+ * remains readable.
+ * @type string
+ * @default null <i>Automatic</i>
+ *
+ * @name DataTable.defaults.column.width
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "width": "20%", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "width": "20%" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sWidth": null
+ };
+
+ _fnHungarianMap(DataTable.defaults.column);
+
+
+ /**
+ * DataTables settings object - this holds all the information needed for a
+ * given table, including configuration, data and current application of the
+ * table options. DataTables does not have a single instance for each DataTable
+ * with the settings attached to that instance, but rather instances of the
+ * DataTable "class" are created on-the-fly as needed (typically by a
+ * $().dataTable() call) and the settings object is then applied to that
+ * instance.
+ *
+ * Note that this object is related to {@link DataTable.defaults} but this
+ * one is the internal data store for DataTables's cache of columns. It should
+ * NOT be manipulated outside of DataTables. Any configuration should be done
+ * through the initialisation options.
+ * @namespace
+ * @todo Really should attach the settings object to individual instances so we
+ * don't need to create new instances on each $().dataTable() call (if the
+ * table already exists). It would also save passing oSettings around and
+ * into every single function. However, this is a very significant
+ * architecture change for DataTables and will almost certainly break
+ * backwards compatibility with older installations. This is something that
+ * will be done in 2.0.
+ */
+ DataTable.models.oSettings = {
+ /**
+ * Primary features of DataTables and their enablement state.
+ * @namespace
+ */
+ "oFeatures": {
+
+ /**
+ * Flag to say if DataTables should automatically try to calculate the
+ * optimum table and columns widths (true) or not (false).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bAutoWidth": null,
+
+ /**
+ * Delay the creation of TR and TD elements until they are actually
+ * needed by a driven page draw. This can give a significant speed
+ * increase for Ajax source and Javascript source data, but makes no
+ * difference at all fro DOM and server-side processing tables.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bDeferRender": null,
+
+ /**
+ * Enable filtering on the table or not. Note that if this is disabled
+ * then there is no filtering at all on the table, including fnFilter.
+ * To just remove the filtering input use sDom and remove the 'f' option.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bFilter": null,
+
+ /**
+ * Table information element (the 'Showing x of y records' div) enable
+ * flag.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bInfo": null,
+
+ /**
+ * Present a user control allowing the end user to change the page size
+ * when pagination is enabled.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bLengthChange": null,
+
+ /**
+ * Pagination enabled or not. Note that if this is disabled then length
+ * changing must also be disabled.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bPaginate": null,
+
+ /**
+ * Processing indicator enable flag whenever DataTables is enacting a
+ * user request - typically an Ajax request for server-side processing.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bProcessing": null,
+
+ /**
+ * Server-side processing enabled flag - when enabled DataTables will
+ * get all data from the server for every draw - there is no filtering,
+ * sorting or paging done on the client-side.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bServerSide": null,
+
+ /**
+ * Sorting enablement flag.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSort": null,
+
+ /**
+ * Multi-column sorting
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSortMulti": null,
+
+ /**
+ * Apply a class to the columns which are being sorted to provide a
+ * visual highlight or not. This can slow things down when enabled since
+ * there is a lot of DOM interaction.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSortClasses": null,
+
+ /**
+ * State saving enablement flag.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bStateSave": null
+ },
+
+
+ /**
+ * Scrolling settings for a table.
+ * @namespace
+ */
+ "oScroll": {
+ /**
+ * When the table is shorter in height than sScrollY, collapse the
+ * table container down to the height of the table (when true).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bCollapse": null,
+
+ /**
+ * Width of the scrollbar for the web-browser's platform. Calculated
+ * during table initialisation.
+ * @type int
+ * @default 0
+ */
+ "iBarWidth": 0,
+
+ /**
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
+ * disabled if an empty string.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sX": null,
+
+ /**
+ * Width to expand the table to when using x-scrolling. Typically you
+ * should not need to use this.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @deprecated
+ */
+ "sXInner": null,
+
+ /**
+ * Viewport height for vertical scrolling. Vertical scrolling is disabled
+ * if an empty string.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sY": null
+ },
+
+ /**
+ * Language information for the table.
+ * @namespace
+ * @extends DataTable.defaults.oLanguage
+ */
+ "oLanguage": {
+ /**
+ * Information callback function. See
+ * {@link DataTable.defaults.fnInfoCallback}
+ * @type function
+ * @default null
+ */
+ "fnInfoCallback": null
+ },
+
+ /**
+ * Browser support parameters
+ * @namespace
+ */
+ "oBrowser": {
+ /**
+ * Indicate if the browser incorrectly calculates width:100% inside a
+ * scrolling element (IE6/7)
+ * @type boolean
+ * @default false
+ */
+ "bScrollOversize": false,
+
+ /**
+ * Determine if the vertical scrollbar is on the right or left of the
+ * scrolling container - needed for rtl language layout, although not
+ * all browsers move the scrollbar (Safari).
+ * @type boolean
+ * @default false
+ */
+ "bScrollbarLeft": false,
+
+ /**
+ * Flag for if `getBoundingClientRect` is fully supported or not
+ * @type boolean
+ * @default false
+ */
+ "bBounding": false,
+
+ /**
+ * Browser scrollbar width
+ * @type integer
+ * @default 0
+ */
+ "barWidth": 0
+ },
+
+
+ "ajax": null,
+
+
+ /**
+ * Array referencing the nodes which are used for the features. The
+ * parameters of this object match what is allowed by sDom - i.e.
+ * <ul>
+ * <li>'l' - Length changing</li>
+ * <li>'f' - Filtering input</li>
+ * <li>'t' - The table!</li>
+ * <li>'i' - Information</li>
+ * <li>'p' - Pagination</li>
+ * <li>'r' - pRocessing</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aanFeatures": [],
+
+ /**
+ * Store data information - see {@link DataTable.models.oRow} for detailed
+ * information.
+ * @type array
+ * @default []
+ */
+ "aoData": [],
+
+ /**
+ * Array of indexes which are in the current display (after filtering etc)
+ * @type array
+ * @default []
+ */
+ "aiDisplay": [],
+
+ /**
+ * Array of indexes for display - no filtering
+ * @type array
+ * @default []
+ */
+ "aiDisplayMaster": [],
+
+ /**
+ * Map of row ids to data indexes
+ * @type object
+ * @default {}
+ */
+ "aIds": {},
+
+ /**
+ * Store information about each column that is in use
+ * @type array
+ * @default []
+ */
+ "aoColumns": [],
+
+ /**
+ * Store information about the table's header
+ * @type array
+ * @default []
+ */
+ "aoHeader": [],
+
+ /**
+ * Store information about the table's footer
+ * @type array
+ * @default []
+ */
+ "aoFooter": [],
+
+ /**
+ * Store the applied global search information in case we want to force a
+ * research or compare the old search to a new one.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @namespace
+ * @extends DataTable.models.oSearch
+ */
+ "oPreviousSearch": {},
+
+ /**
+ * Store the applied search for each column - see
+ * {@link DataTable.models.oSearch} for the format that is used for the
+ * filtering information for each column.
+ * @type array
+ * @default []
+ */
+ "aoPreSearchCols": [],
+
+ /**
+ * Sorting that is applied to the table. Note that the inner arrays are
+ * used in the following manner:
+ * <ul>
+ * <li>Index 0 - column number</li>
+ * <li>Index 1 - current sorting direction</li>
+ * </ul>
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @todo These inner arrays should really be objects
+ */
+ "aaSorting": null,
+
+ /**
+ * Sorting that is always applied to the table (i.e. prefixed in front of
+ * aaSorting).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "aaSortingFixed": [],
+
+ /**
+ * Classes to use for the striping of a table.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "asStripeClasses": null,
+
+ /**
+ * If restoring a table - we should restore its striping classes as well
+ * @type array
+ * @default []
+ */
+ "asDestroyStripes": [],
+
+ /**
+ * If restoring a table - we should restore its width
+ * @type int
+ * @default 0
+ */
+ "sDestroyWidth": 0,
+
+ /**
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
+ * @type array
+ * @default []
+ */
+ "aoRowCallback": [],
+
+ /**
+ * Callback functions for the header on each draw.
+ * @type array
+ * @default []
+ */
+ "aoHeaderCallback": [],
+
+ /**
+ * Callback function for the footer on each draw.
+ * @type array
+ * @default []
+ */
+ "aoFooterCallback": [],
+
+ /**
+ * Array of callback functions for draw callback functions
+ * @type array
+ * @default []
+ */
+ "aoDrawCallback": [],
+
+ /**
+ * Array of callback functions for row created function
+ * @type array
+ * @default []
+ */
+ "aoRowCreatedCallback": [],
+
+ /**
+ * Callback functions for just before the table is redrawn. A return of
+ * false will be used to cancel the draw.
+ * @type array
+ * @default []
+ */
+ "aoPreDrawCallback": [],
+
+ /**
+ * Callback functions for when the table has been initialised.
+ * @type array
+ * @default []
+ */
+ "aoInitComplete": [],
+
+
+ /**
+ * Callbacks for modifying the settings to be stored for state saving, prior to
+ * saving state.
+ * @type array
+ * @default []
+ */
+ "aoStateSaveParams": [],
+
+ /**
+ * Callbacks for modifying the settings that have been stored for state saving
+ * prior to using the stored values to restore the state.
+ * @type array
+ * @default []
+ */
+ "aoStateLoadParams": [],
+
+ /**
+ * Callbacks for operating on the settings object once the saved state has been
+ * loaded
+ * @type array
+ * @default []
+ */
+ "aoStateLoaded": [],
+
+ /**
+ * Cache the table ID for quick access
+ * @type string
+ * @default <i>Empty string</i>
+ */
+ "sTableId": "",
+
+ /**
+ * The TABLE node for the main table
+ * @type node
+ * @default null
+ */
+ "nTable": null,
+
+ /**
+ * Permanent ref to the thead element
+ * @type node
+ * @default null
+ */
+ "nTHead": null,
+
+ /**
+ * Permanent ref to the tfoot element - if it exists
+ * @type node
+ * @default null
+ */
+ "nTFoot": null,
+
+ /**
+ * Permanent ref to the tbody element
+ * @type node
+ * @default null
+ */
+ "nTBody": null,
+
+ /**
+ * Cache the wrapper node (contains all DataTables controlled elements)
+ * @type node
+ * @default null
+ */
+ "nTableWrapper": null,
+
+ /**
+ * Indicate if when using server-side processing the loading of data
+ * should be deferred until the second draw.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ * @default false
+ */
+ "bDeferLoading": false,
+
+ /**
+ * Indicate if all required information has been read in
+ * @type boolean
+ * @default false
+ */
+ "bInitialised": false,
+
+ /**
+ * Information about open rows. Each object in the array has the parameters
+ * 'nTr' and 'nParent'
+ * @type array
+ * @default []
+ */
+ "aoOpenRows": [],
+
+ /**
+ * Dictate the positioning of DataTables' control elements - see
+ * {@link DataTable.model.oInit.sDom}.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default null
+ */
+ "sDom": null,
+
+ /**
+ * Search delay (in mS)
+ * @type integer
+ * @default null
+ */
+ "searchDelay": null,
+
+ /**
+ * Which type of pagination should be used.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default two_button
+ */
+ "sPaginationType": "two_button",
+
+ /**
+ * The state duration (for `stateSave`) in seconds.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type int
+ * @default 0
+ */
+ "iStateDuration": 0,
+
+ /**
+ * Array of callback functions for state saving. Each array element is an
+ * object with the following parameters:
+ * <ul>
+ * <li>function:fn - function to call. Takes two parameters, oSettings
+ * and the JSON string to save that has been thus far created. Returns
+ * a JSON string to be inserted into a json object
+ * (i.e. '"param": [ 0, 1, 2]')</li>
+ * <li>string:sName - name of callback</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aoStateSave": [],
+
+ /**
+ * Array of callback functions for state loading. Each array element is an
+ * object with the following parameters:
+ * <ul>
+ * <li>function:fn - function to call. Takes two parameters, oSettings
+ * and the object stored. May return false to cancel state loading</li>
+ * <li>string:sName - name of callback</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aoStateLoad": [],
+
+ /**
+ * State that was saved. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oSavedState": null,
+
+ /**
+ * State that was loaded. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oLoadedState": null,
+
+ /**
+ * Source url for AJAX data for the table.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default null
+ */
+ "sAjaxSource": null,
+
+ /**
+ * Property from a given object from which to read the table data from. This
+ * can be an empty string (when not server-side processing), in which case
+ * it is assumed an an array is given directly.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sAjaxDataProp": null,
+
+ /**
+ * Note if draw should be blocked while getting data
+ * @type boolean
+ * @default true
+ */
+ "bAjaxDataGet": true,
+
+ /**
+ * The last jQuery XHR object that was used for server-side data gathering.
+ * This can be used for working with the XHR information in one of the
+ * callbacks
+ * @type object
+ * @default null
+ */
+ "jqXHR": null,
+
+ /**
+ * JSON returned from the server in the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "json": undefined,
+
+ /**
+ * Data submitted as part of the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "oAjaxData": undefined,
+
+ /**
+ * Function to get the server-side data.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type function
+ */
+ "fnServerData": null,
+
+ /**
+ * Functions which are called prior to sending an Ajax request so extra
+ * parameters can easily be sent to the server
+ * @type array
+ * @default []
+ */
+ "aoServerParams": [],
+
+ /**
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
+ * required).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sServerMethod": null,
+
+ /**
+ * Format numbers for display.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type function
+ */
+ "fnFormatNumber": null,
+
+ /**
+ * List of options that can be used for the user selectable length menu.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "aLengthMenu": null,
+
+ /**
+ * Counter for the draws that the table does. Also used as a tracker for
+ * server-side processing
+ * @type int
+ * @default 0
+ */
+ "iDraw": 0,
+
+ /**
+ * Indicate if a redraw is being done - useful for Ajax
+ * @type boolean
+ * @default false
+ */
+ "bDrawing": false,
+
+ /**
+ * Draw index (iDraw) of the last error when parsing the returned data
+ * @type int
+ * @default -1
+ */
+ "iDrawError": -1,
+
+ /**
+ * Paging display length
+ * @type int
+ * @default 10
+ */
+ "_iDisplayLength": 10,
+
+ /**
+ * Paging start point - aiDisplay index
+ * @type int
+ * @default 0
+ */
+ "_iDisplayStart": 0,
+
+ /**
+ * Server-side processing - number of records in the result set
+ * (i.e. before filtering), Use fnRecordsTotal rather than
+ * this property to get the value of the number of records, regardless of
+ * the server-side processing setting.
+ * @type int
+ * @default 0
+ * @private
+ */
+ "_iRecordsTotal": 0,
+
+ /**
+ * Server-side processing - number of records in the current display set
+ * (i.e. after filtering). Use fnRecordsDisplay rather than
+ * this property to get the value of the number of records, regardless of
+ * the server-side processing setting.
+ * @type boolean
+ * @default 0
+ * @private
+ */
+ "_iRecordsDisplay": 0,
+
+ /**
+ * Flag to indicate if jQuery UI marking and classes should be used.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bJUI": null,
+
+ /**
+ * The classes to use for the table
+ * @type object
+ * @default {}
+ */
+ "oClasses": {},
+
+ /**
+ * Flag attached to the settings object so you can check in the draw
+ * callback if filtering has been done in the draw. Deprecated in favour of
+ * events.
+ * @type boolean
+ * @default false
+ * @deprecated
+ */
+ "bFiltered": false,
+
+ /**
+ * Flag attached to the settings object so you can check in the draw
+ * callback if sorting has been done in the draw. Deprecated in favour of
+ * events.
+ * @type boolean
+ * @default false
+ * @deprecated
+ */
+ "bSorted": false,
+
+ /**
+ * Indicate that if multiple rows are in the header and there is more than
+ * one unique cell per column, if the top one (true) or bottom one (false)
+ * should be used for sorting / title by DataTables.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSortCellsTop": null,
+
+ /**
+ * Initialisation object that is used for the table
+ * @type object
+ * @default null
+ */
+ "oInit": null,
+
+ /**
+ * Destroy callback functions - for plug-ins to attach themselves to the
+ * destroy so they can clean up markup and events.
+ * @type array
+ * @default []
+ */
+ "aoDestroyCallback": [],
+
+
+ /**
+ * Get the number of records in the current record set, before filtering
+ * @type function
+ */
+ "fnRecordsTotal": function () {
+ return _fnDataSource(this) == 'ssp' ?
+ this._iRecordsTotal * 1 :
+ this.aiDisplayMaster.length;
+ },
+
+ /**
+ * Get the number of records in the current record set, after filtering
+ * @type function
+ */
+ "fnRecordsDisplay": function () {
+ return _fnDataSource(this) == 'ssp' ?
+ this._iRecordsDisplay * 1 :
+ this.aiDisplay.length;
+ },
+
+ /**
+ * Get the display end point - aiDisplay index
+ * @type function
+ */
+ "fnDisplayEnd": function () {
+ var
+ len = this._iDisplayLength,
+ start = this._iDisplayStart,
+ calc = start + len,
+ records = this.aiDisplay.length,
+ features = this.oFeatures,
+ paginate = features.bPaginate;
+
+ if (features.bServerSide) {
+ return paginate === false || len === -1 ?
+ start + records :
+ Math.min(start + len, this._iRecordsDisplay);
+ } else {
+ return !paginate || calc > records || len === -1 ?
+ records :
+ calc;
+ }
+ },
+
+ /**
+ * The DataTables object for this table
+ * @type object
+ * @default null
+ */
+ "oInstance": null,
+
+ /**
+ * Unique identifier for each instance of the DataTables object. If there
+ * is an ID on the table node, then it takes that value, otherwise an
+ * incrementing internal counter is used.
+ * @type string
+ * @default null
+ */
+ "sInstance": null,
+
+ /**
+ * tabindex attribute value that is added to DataTables control elements, allowing
+ * keyboard navigation of the table and its controls.
+ */
+ "iTabIndex": 0,
+
+ /**
+ * DIV container for the footer scrolling table if scrolling
+ */
+ "nScrollHead": null,
+
+ /**
+ * DIV container for the footer scrolling table if scrolling
+ */
+ "nScrollFoot": null,
+
+ /**
+ * Last applied sort
+ * @type array
+ * @default []
+ */
+ "aLastSort": [],
+
+ /**
+ * Stored plug-in instances
+ * @type object
+ * @default {}
+ */
+ "oPlugins": {},
+
+ /**
+ * Function used to get a row's id from the row's data
+ * @type function
+ * @default null
+ */
+ "rowIdFn": null,
+
+ /**
+ * Data location where to store a row's id
+ * @type string
+ * @default null
+ */
+ "rowId": null
+ };
+
+ /**
+ * Extension object for DataTables that is used to provide all extension
+ * options.
+ *
+ * Note that the `DataTable.ext` object is available through
+ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
+ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
+ * @namespace
+ * @extends DataTable.models.ext
+ */
+
+
+ /**
+ * DataTables extensions
+ *
+ * This namespace acts as a collection area for plug-ins that can be used to
+ * extend DataTables capabilities. Indeed many of the build in methods
+ * use this method to provide their own capabilities (sorting methods for
+ * example).
+ *
+ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
+ * reasons
+ *
+ * @namespace
+ */
+ DataTable.ext = _ext = {
+ /**
+ * Buttons. For use with the Buttons extension for DataTables. This is
+ * defined here so other extensions can define buttons regardless of load
+ * order. It is _not_ used by DataTables core.
+ *
+ * @type object
+ * @default {}
+ */
+ buttons: {},
+
+
+ /**
+ * Element class names
+ *
+ * @type object
+ * @default {}
+ */
+ classes: {},
+
+
+ /**
+ * DataTables build type (expanded by the download builder)
+ *
+ * @type string
+ */
+ builder: "-source-",
+
+
+ /**
+ * Error reporting.
+ *
+ * How should DataTables report an error. Can take the value 'alert',
+ * 'throw', 'none' or a function.
+ *
+ * @type string|function
+ * @default alert
+ */
+ errMode: "alert",
+
+
+ /**
+ * Feature plug-ins.
+ *
+ * This is an array of objects which describe the feature plug-ins that are
+ * available to DataTables. These feature plug-ins are then available for
+ * use through the `dom` initialisation option.
+ *
+ * Each feature plug-in is described by an object which must have the
+ * following properties:
+ *
+ * * `fnInit` - function that is used to initialise the plug-in,
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
+ * instillation option. This is case sensitive.
+ *
+ * The `fnInit` function has the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ *
+ * And the following return is expected:
+ *
+ * * {node|null} The element which contains your feature. Note that the
+ * return may also be void if your plug-in does not require to inject any
+ * DOM elements into DataTables control (`dom`) - for example this might
+ * be useful when developing a plug-in which allows table control via
+ * keyboard entry
+ *
+ * @type array
+ *
+ * @example
+ * $.fn.dataTable.ext.features.push( {
+ * "fnInit": function( oSettings ) {
+ * return new TableTools( { "oDTSettings": oSettings } );
+ * },
+ * "cFeature": "T"
+ * } );
+ */
+ feature: [],
+
+
+ /**
+ * Row searching.
+ *
+ * This method of searching is complimentary to the default type based
+ * searching, and a lot more comprehensive as it allows you complete control
+ * over the searching logic. Each element in this array is a function
+ * (parameters described below) that is called for every row in the table,
+ * and your logic decides if it should be included in the searching data set
+ * or not.
+ *
+ * Searching functions have the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{array|object}` Data for the row to be processed (same as the
+ * original format that was passed in as the data source, or an array
+ * from a DOM data source
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
+ *
+ * And the following return is expected:
+ *
+ * * {boolean} Include the row in the searched result set (true) or not
+ * (false)
+ *
+ * Note that as with the main search ability in DataTables, technically this
+ * is "filtering", since it is subtractive. However, for consistency in
+ * naming we call it searching here.
+ *
+ * @type array
+ * @default []
+ *
+ * @example
+ * // The following example shows custom search being applied to the
+ * // fourth column (i.e. the data[3] index) based on two input values
+ * // from the end-user, matching the data in a certain range.
+ * $.fn.dataTable.ext.search.push(
+ * function( settings, data, dataIndex ) {
+ * var min = document.getElementById('min').value * 1;
+ * var max = document.getElementById('max').value * 1;
+ * var version = data[3] == "-" ? 0 : data[3]*1;
+ *
+ * if ( min == "" && max == "" ) {
+ * return true;
+ * }
+ * else if ( min == "" && version < max ) {
+ * return true;
+ * }
+ * else if ( min < version && "" == max ) {
+ * return true;
+ * }
+ * else if ( min < version && version < max ) {
+ * return true;
+ * }
+ * return false;
+ * }
+ * );
+ */
+ search: [],
+
+
+ /**
+ * Selector extensions
+ *
+ * The `selector` option can be used to extend the options available for the
+ * selector modifier options (`selector-modifier` object data type) that
+ * each of the three built in selector types offer (row, column and cell +
+ * their plural counterparts). For example the Select extension uses this
+ * mechanism to provide an option to select only rows, columns and cells
+ * that have been marked as selected by the end user (`{selected: true}`),
+ * which can be used in conjunction with the existing built in selector
+ * options.
+ *
+ * Each property is an array to which functions can be pushed. The functions
+ * take three attributes:
+ *
+ * * Settings object for the host table
+ * * Options object (`selector-modifier` object type)
+ * * Array of selected item indexes
+ *
+ * The return is an array of the resulting item indexes after the custom
+ * selector has been applied.
+ *
+ * @type object
+ */
+ selector: {
+ cell: [],
+ column: [],
+ row: []
+ },
+
+
+ /**
+ * Internal functions, exposed for used in plug-ins.
+ *
+ * Please note that you should not need to use the internal methods for
+ * anything other than a plug-in (and even then, try to avoid if possible).
+ * The internal function may change between releases.
+ *
+ * @type object
+ * @default {}
+ */
+ internal: {},
+
+
+ /**
+ * Legacy configuration options. Enable and disable legacy options that
+ * are available in DataTables.
+ *
+ * @type object
+ */
+ legacy: {
+ /**
+ * Enable / disable DataTables 1.9 compatible server-side processing
+ * requests
+ *
+ * @type boolean
+ * @default null
+ */
+ ajax: null
+ },
+
+
+ /**
+ * Pagination plug-in methods.
+ *
+ * Each entry in this object is a function and defines which buttons should
+ * be shown by the pagination rendering method that is used for the table:
+ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
+ * buttons are displayed in the document, while the functions here tell it
+ * what buttons to display. This is done by returning an array of button
+ * descriptions (what each button will do).
+ *
+ * Pagination types (the four built in options and any additional plug-in
+ * options defined here) can be used through the `paginationType`
+ * initialisation parameter.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{int} page` The current page index
+ * 2. `{int} pages` The number of pages in the table
+ *
+ * Each function is expected to return an array where each element of the
+ * array can be one of:
+ *
+ * * `first` - Jump to first page when activated
+ * * `last` - Jump to last page when activated
+ * * `previous` - Show previous page when activated
+ * * `next` - Show next page when activated
+ * * `{int}` - Show page of the index given
+ * * `{array}` - A nested array containing the above elements to add a
+ * containing 'DIV' element (might be useful for styling).
+ *
+ * Note that DataTables v1.9- used this object slightly differently whereby
+ * an object with two functions would be defined for each plug-in. That
+ * ability is still supported by DataTables 1.10+ to provide backwards
+ * compatibility, but this option of use is now decremented and no longer
+ * documented in DataTables 1.10+.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * // Show previous, next and current page buttons only
+ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
+ * return [ 'previous', page, 'next' ];
+ * };
+ */
+ pager: {},
+
+
+ renderer: {
+ pageButton: {},
+ header: {}
+ },
+
+
+ /**
+ * Ordering plug-ins - custom data source
+ *
+ * The extension options for ordering of data available here is complimentary
+ * to the default type based ordering that DataTables typically uses. It
+ * allows much greater control over the the data that is being used to
+ * order a column, but is necessarily therefore more complex.
+ *
+ * This type of ordering is useful if you want to do ordering based on data
+ * live from the DOM (for example the contents of an 'input' element) rather
+ * than just the static string that DataTables knows of.
+ *
+ * The way these plug-ins work is that you create an array of the values you
+ * wish to be ordering for the column in question and then return that
+ * array. The data in the array much be in the index order of the rows in
+ * the table (not the currently ordering order!). Which order data gathering
+ * function is run here depends on the `dt-init columns.orderDataType`
+ * parameter that is used for the column (if any).
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{int}` Target column index
+ *
+ * Each function is expected to return an array:
+ *
+ * * `{array}` Data for the column to be ordering upon
+ *
+ * @type array
+ *
+ * @example
+ * // Ordering using `input` node values
+ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
+ * {
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
+ * return $('input', td).val();
+ * } );
+ * }
+ */
+ order: {},
+
+
+ /**
+ * Type based plug-ins.
+ *
+ * Each column in DataTables has a type assigned to it, either by automatic
+ * detection or by direct assignment using the `type` option for the column.
+ * The type of a column will effect how it is ordering and search (plug-ins
+ * can also make use of the column type if required).
+ *
+ * @namespace
+ */
+ type: {
+ /**
+ * Type detection functions.
+ *
+ * The functions defined in this object are used to automatically detect
+ * a column's type, making initialisation of DataTables super easy, even
+ * when complex data is in the table.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{*}` Data from the column cell to be analysed
+ * 2. `{settings}` DataTables settings object. This can be used to
+ * perform context specific type detection - for example detection
+ * based on language settings such as using a comma for a decimal
+ * place. Generally speaking the options from the settings will not
+ * be required
+ *
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Data type detected, or null if unknown (and thus
+ * pass it on to the other type detection functions.
+ *
+ * @type array
+ *
+ * @example
+ * // Currency type detection plug-in:
+ * $.fn.dataTable.ext.type.detect.push(
+ * function ( data, settings ) {
+ * // Check the numeric part
+ * if ( ! $.isNumeric( data.substring(1) ) ) {
+ * return null;
+ * }
+ *
+ * // Check prefixed by currency
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
+ * return 'currency';
+ * }
+ * return null;
+ * }
+ * );
+ */
+ detect: [],
+
+
+ /**
+ * Type based search formatting.
+ *
+ * The type based searching functions can be used to pre-format the
+ * data to be search on. For example, it can be used to strip HTML
+ * tags or to de-format telephone numbers for numeric only searching.
+ *
+ * Note that is a search is not defined for a column of a given type,
+ * no search formatting will be performed.
+ *
+ * Pre-processing of searching data plug-ins - When you assign the sType
+ * for a column (or have it automatically detected for you by DataTables
+ * or a type detection plug-in), you will typically be using this for
+ * custom sorting, but it can also be used to provide custom searching
+ * by allowing you to pre-processing the data and returning the data in
+ * the format that should be searched upon. This is done by adding
+ * functions this object with a parameter name which matches the sType
+ * for that target column. This is the corollary of <i>afnSortData</i>
+ * for searching data.
+ *
+ * The functions defined take a single parameter:
+ *
+ * 1. `{*}` Data from the column cell to be prepared for searching
+ *
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Formatted string that will be used for the searching.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
+ * }
+ */
+ search: {},
+
+
+ /**
+ * Type based ordering.
+ *
+ * The column type tells DataTables what ordering to apply to the table
+ * when a column is sorted upon. The order for each type that is defined,
+ * is defined by the functions available in this object.
+ *
+ * Each ordering option can be described by three properties added to
+ * this object:
+ *
+ * * `{type}-pre` - Pre-formatting function
+ * * `{type}-asc` - Ascending order function
+ * * `{type}-desc` - Descending order function
+ *
+ * All three can be used together, only `{type}-pre` or only
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
+ * that only `{type}-pre` is used, as this provides the optimal
+ * implementation in terms of speed, although the others are provided
+ * for compatibility with existing Javascript sort functions.
+ *
+ * `{type}-pre`: Functions defined take a single parameter:
+ *
+ * 1. `{*}` Data from the column cell to be prepared for ordering
+ *
+ * And return:
+ *
+ * * `{*}` Data to be sorted upon
+ *
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
+ * functions, taking two parameters:
+ *
+ * 1. `{*}` Data to compare to the second parameter
+ * 2. `{*}` Data to compare to the first parameter
+ *
+ * And returning:
+ *
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
+ * than the second parameter, ===0 if the two parameters are equal and
+ * >0 if the first parameter should be sorted height than the second
+ * parameter.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * // Numeric ordering of formatted numbers with a pre-formatter
+ * $.extend( $.fn.dataTable.ext.type.order, {
+ * "string-pre": function(x) {
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
+ * return parseFloat( a );
+ * }
+ * } );
+ *
+ * @example
+ * // Case-sensitive string ordering, with no pre-formatting method
+ * $.extend( $.fn.dataTable.ext.order, {
+ * "string-case-asc": function(x,y) {
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ * },
+ * "string-case-desc": function(x,y) {
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ * }
+ * } );
+ */
+ order: {}
+ },
+
+ /**
+ * Unique DataTables instance counter
+ *
+ * @type int
+ * @private
+ */
+ _unique: 0,
+
+
+ //
+ // Depreciated
+ // The following properties are retained for backwards compatiblity only.
+ // The should not be used in new projects and will be removed in a future
+ // version
+ //
+
+ /**
+ * Version check function.
+ * @type function
+ * @depreciated Since 1.10
+ */
+ fnVersionCheck: DataTable.fnVersionCheck,
+
+
+ /**
+ * Index for what 'this' index API functions should use
+ * @type int
+ * @deprecated Since v1.10
+ */
+ iApiIndex: 0,
+
+
+ /**
+ * jQuery UI class container
+ * @type object
+ * @deprecated Since v1.10
+ */
+ oJUIClasses: {},
+
+
+ /**
+ * Software version
+ * @type string
+ * @deprecated Since v1.10
+ */
+ sVersion: DataTable.version
+ };
+
+
+ //
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
+ //
+ $.extend(_ext, {
+ afnFiltering: _ext.search,
+ aTypes: _ext.type.detect,
+ ofnSearch: _ext.type.search,
+ oSort: _ext.type.order,
+ afnSortData: _ext.order,
+ aoFeatures: _ext.feature,
+ oApi: _ext.internal,
+ oStdClasses: _ext.classes,
+ oPagination: _ext.pager
+ });
+
+
+ $.extend(DataTable.ext.classes, {
+ "sTable": "dataTable",
+ "sNoFooter": "no-footer",
+
+ /* Paging buttons */
+ "sPageButton": "paginate_button",
+ "sPageButtonActive": "current",
+ "sPageButtonDisabled": "disabled",
+
+ /* Striping classes */
+ "sStripeOdd": "odd",
+ "sStripeEven": "even",
+
+ /* Empty row */
+ "sRowEmpty": "dataTables_empty",
+
+ /* Features */
+ "sWrapper": "dataTables_wrapper",
+ "sFilter": "dataTables_filter",
+ "sInfo": "dataTables_info",
+ "sPaging": "dataTables_paginate paging_",
+ /* Note that the type is postfixed */
+ "sLength": "dataTables_length",
+ "sProcessing": "dataTables_processing",
+
+ /* Sorting */
+ "sSortAsc": "sorting_asc",
+ "sSortDesc": "sorting_desc",
+ "sSortable": "sorting",
+ /* Sortable in both directions */
+ "sSortableAsc": "sorting_asc_disabled",
+ "sSortableDesc": "sorting_desc_disabled",
+ "sSortableNone": "sorting_disabled",
+ "sSortColumn": "sorting_",
+ /* Note that an int is postfixed for the sorting order */
+
+ /* Filtering */
+ "sFilterInput": "",
+
+ /* Page length */
+ "sLengthSelect": "",
+
+ /* Scrolling */
+ "sScrollWrapper": "dataTables_scroll",
+ "sScrollHead": "dataTables_scrollHead",
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
+ "sScrollBody": "dataTables_scrollBody",
+ "sScrollFoot": "dataTables_scrollFoot",
+ "sScrollFootInner": "dataTables_scrollFootInner",
+
+ /* Misc */
+ "sHeaderTH": "",
+ "sFooterTH": "",
+
+ // Deprecated
+ "sSortJUIAsc": "",
+ "sSortJUIDesc": "",
+ "sSortJUI": "",
+ "sSortJUIAscAllowed": "",
+ "sSortJUIDescAllowed": "",
+ "sSortJUIWrapper": "",
+ "sSortIcon": "",
+ "sJUIHeader": "",
+ "sJUIFooter": ""
+ });
+
+
+ (function () {
+
+ // Reused strings for better compression. Closure compiler appears to have a
+ // weird edge case where it is trying to expand strings rather than use the
+ // variable version. This results in about 200 bytes being added, for very
+ // little preference benefit since it this run on script load only.
+ var _empty = '';
+ _empty = '';
+
+ var _stateDefault = _empty + 'ui-state-default';
+ var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
+ var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
+
+ $.extend(DataTable.ext.oJUIClasses, DataTable.ext.classes, {
+ /* Full numbers paging buttons */
+ "sPageButton": "fg-button ui-button " + _stateDefault,
+ "sPageButtonActive": "ui-state-disabled",
+ "sPageButtonDisabled": "ui-state-disabled",
+
+ /* Features */
+ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi " +
+ "ui-buttonset-multi paging_",
+ /* Note that the type is postfixed */
+
+ /* Sorting */
+ "sSortAsc": _stateDefault + " sorting_asc",
+ "sSortDesc": _stateDefault + " sorting_desc",
+ "sSortable": _stateDefault + " sorting",
+ "sSortableAsc": _stateDefault + " sorting_asc_disabled",
+ "sSortableDesc": _stateDefault + " sorting_desc_disabled",
+ "sSortableNone": _stateDefault + " sorting_disabled",
+ "sSortJUIAsc": _sortIcon + "triangle-1-n",
+ "sSortJUIDesc": _sortIcon + "triangle-1-s",
+ "sSortJUI": _sortIcon + "carat-2-n-s",
+ "sSortJUIAscAllowed": _sortIcon + "carat-1-n",
+ "sSortJUIDescAllowed": _sortIcon + "carat-1-s",
+ "sSortJUIWrapper": "DataTables_sort_wrapper",
+ "sSortIcon": "DataTables_sort_icon",
+
+ /* Scrolling */
+ "sScrollHead": "dataTables_scrollHead " + _stateDefault,
+ "sScrollFoot": "dataTables_scrollFoot " + _stateDefault,
+
+ /* Misc */
+ "sHeaderTH": _stateDefault,
+ "sFooterTH": _stateDefault,
+ "sJUIHeader": _headerFooter + " ui-corner-tl ui-corner-tr",
+ "sJUIFooter": _headerFooter + " ui-corner-bl ui-corner-br"
+ });
+
+ }());
+
+
+ var extPagination = DataTable.ext.pager;
+
+ function _numbers(page, pages) {
+ var
+ numbers = [],
+ buttons = extPagination.numbers_length,
+ half = Math.floor(buttons / 2),
+ i = 1;
+
+ if (pages <= buttons) {
+ numbers = _range(0, pages);
+ } else if (page <= half) {
+ numbers = _range(0, buttons - 2);
+ numbers.push('ellipsis');
+ numbers.push(pages - 1);
+ } else if (page >= pages - 1 - half) {
+ numbers = _range(pages - (buttons - 2), pages);
+ numbers.splice(0, 0, 'ellipsis'); // no unshift in ie6
+ numbers.splice(0, 0, 0);
+ } else {
+ numbers = _range(page - half + 2, page + half - 1);
+ numbers.push('ellipsis');
+ numbers.push(pages - 1);
+ numbers.splice(0, 0, 'ellipsis');
+ numbers.splice(0, 0, 0);
+ }
+
+ numbers.DT_el = 'span';
+ return numbers;
+ }
+
+
+ $.extend(extPagination, {
+ simple: function (page, pages) {
+ return ['previous', 'next'];
+ },
+
+ full: function (page, pages) {
+ return ['first', 'previous', 'next', 'last'];
+ },
+
+ numbers: function (page, pages) {
+ return [_numbers(page, pages)];
+ },
+
+ simple_numbers: function (page, pages) {
+ return ['previous', _numbers(page, pages), 'next'];
+ },
+
+ full_numbers: function (page, pages) {
+ return ['first', 'previous', _numbers(page, pages), 'next', 'last'];
+ },
+
+ // For testing and plug-ins to use
+ _numbers: _numbers,
+
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
+ numbers_length: 7
+ });
+
+
+ $.extend(true, DataTable.ext.renderer, {
+ pageButton: {
+ _: function (settings, host, idx, buttons, page, pages) {
+ var classes = settings.oClasses;
+ var lang = settings.oLanguage.oPaginate;
+ var aria = settings.oLanguage.oAria.paginate || {};
+ var btnDisplay, btnClass, counter = 0;
+
+ var attach = function (container, buttons) {
+ var i, ien, node, button;
+ var clickHandler = function (e) {
+ _fnPageChange(settings, e.data.action, true);
+ };
+
+ for (i = 0, ien = buttons.length; i < ien; i++) {
+ button = buttons[i];
+
+ if ($.isArray(button)) {
+ var inner = $('<' + (button.DT_el || 'div') + '/>')
+ .appendTo(container);
+ attach(inner, button);
+ } else {
+ btnDisplay = null;
+ btnClass = '';
+
+ switch (button) {
+ case 'ellipsis':
+ container.append('<span class="ellipsis">&#x2026;</span>');
+ break;
+
+ case 'first':
+ btnDisplay = lang.sFirst;
+ btnClass = button + (page > 0 ?
+ '' : ' ' + classes.sPageButtonDisabled);
+ break;
+
+ case 'previous':
+ btnDisplay = lang.sPrevious;
+ btnClass = button + (page > 0 ?
+ '' : ' ' + classes.sPageButtonDisabled);
+ break;
+
+ case 'next':
+ btnDisplay = lang.sNext;
+ btnClass = button + (page < pages - 1 ?
+ '' : ' ' + classes.sPageButtonDisabled);
+ break;
+
+ case 'last':
+ btnDisplay = lang.sLast;
+ btnClass = button + (page < pages - 1 ?
+ '' : ' ' + classes.sPageButtonDisabled);
+ break;
+
+ default:
+ btnDisplay = button + 1;
+ btnClass = page === button ?
+ classes.sPageButtonActive : '';
+ break;
+ }
+
+ if (btnDisplay !== null) {
+ node = $('<a>', {
+ 'class': classes.sPageButton + ' ' + btnClass,
+ 'aria-controls': settings.sTableId,
+ 'aria-label': aria[button],
+ 'data-dt-idx': counter,
+ 'tabindex': settings.iTabIndex,
+ 'id': idx === 0 && typeof button === 'string' ?
+ settings.sTableId + '_' + button : null
+ })
+ .html(btnDisplay)
+ .appendTo(container);
+
+ _fnBindAction(
+ node, {
+ action: button
+ }, clickHandler
+ );
+
+ counter++;
+ }
+ }
+ }
+ };
+
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame. Try / catch the error. Not good for
+ // accessibility, but neither are frames.
+ var activeEl;
+
+ try {
+ // Because this approach is destroying and recreating the paging
+ // elements, focus is lost on the select button which is bad for
+ // accessibility. So we want to restore focus once the draw has
+ // completed
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
+ } catch (e) {}
+
+ attach($(host).empty(), buttons);
+
+ if (activeEl) {
+ $(host).find('[data-dt-idx=' + activeEl + ']').focus();
+ }
+ }
+ }
+ });
+
+
+ // Built in type detection. See model.ext.aTypes for information about
+ // what is required from this methods.
+ $.extend(DataTable.ext.type.detect, [
+ // Plain numbers - first since V8 detects some plain numbers as dates
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
+ function (d, settings) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber(d, decimal) ? 'num' + decimal : null;
+ },
+
+ // Dates (only those recognised by the browser's Date.parse)
+ function (d, settings) {
+ // V8 will remove any unknown characters at the start and end of the
+ // expression, leading to false matches such as `$245.12` or `10%` being
+ // a valid date. See forum thread 18941 for detail.
+ if (d && !(d instanceof Date) && (!_re_date_start.test(d) || !_re_date_end.test(d))) {
+ return null;
+ }
+ var parsed = Date.parse(d);
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
+ },
+
+ // Formatted numbers
+ function (d, settings) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber(d, decimal, true) ? 'num-fmt' + decimal : null;
+ },
+
+ // HTML numeric
+ function (d, settings) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric(d, decimal) ? 'html-num' + decimal : null;
+ },
+
+ // HTML numeric, formatted
+ function (d, settings) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric(d, decimal, true) ? 'html-num-fmt' + decimal : null;
+ },
+
+ // HTML (this is strict checking - there must be html)
+ function (d, settings) {
+ return _empty(d) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
+ 'html' : null;
+ }
+ ]);
+
+
+ // Filter formatting functions. See model.ext.ofnSearch for information about
+ // what is required from these methods.
+ //
+ // Note that additional search methods are added for the html numbers and
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
+ // place is
+
+
+ $.extend(DataTable.ext.type.search, {
+ html: function (data) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data
+ .replace(_re_new_lines, " ")
+ .replace(_re_html, "") :
+ '';
+ },
+
+ string: function (data) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data.replace(_re_new_lines, " ") :
+ data;
+ }
+ });
+
+
+ var __numericReplace = function (d, decimalPlace, re1, re2) {
+ if (d !== 0 && (!d || d === '-')) {
+ return -Infinity;
+ }
+
+ // If a decimal place other than `.` is used, it needs to be given to the
+ // function so we can detect it and replace with a `.` which is the only
+ // decimal place Javascript recognises - it is not locale aware.
+ if (decimalPlace) {
+ d = _numToDecimal(d, decimalPlace);
+ }
+
+ if (d.replace) {
+ if (re1) {
+ d = d.replace(re1, '');
+ }
+
+ if (re2) {
+ d = d.replace(re2, '');
+ }
+ }
+
+ return d * 1;
+ };
+
+
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
+ // in a function to provide an easy ability for the language options to add
+ // additional methods if a non-period decimal place is used.
+ function _addNumericSort(decimalPlace) {
+ $.each({
+ // Plain numbers
+ "num": function (d) {
+ return __numericReplace(d, decimalPlace);
+ },
+
+ // Formatted numbers
+ "num-fmt": function (d) {
+ return __numericReplace(d, decimalPlace, _re_formatted_numeric);
+ },
+
+ // HTML numeric
+ "html-num": function (d) {
+ return __numericReplace(d, decimalPlace, _re_html);
+ },
+
+ // HTML numeric, formatted
+ "html-num-fmt": function (d) {
+ return __numericReplace(d, decimalPlace, _re_html, _re_formatted_numeric);
+ }
+ },
+ function (key, fn) {
+ // Add the ordering method
+ _ext.type.order[key + decimalPlace + '-pre'] = fn;
+
+ // For HTML types add a search formatter that will strip the HTML
+ if (key.match(/^html\-/)) {
+ _ext.type.search[key + decimalPlace] = _ext.type.search.html;
+ }
+ }
+ );
+ }
+
+
+ // Default sort methods
+ $.extend(_ext.type.order, {
+ // Dates
+ "date-pre": function (d) {
+ return Date.parse(d) || 0;
+ },
+
+ // html
+ "html-pre": function (a) {
+ return _empty(a) ?
+ '' :
+ a.replace ?
+ a.replace(/<.*?>/g, "").toLowerCase() :
+ a + '';
+ },
+
+ // string
+ "string-pre": function (a) {
+ // This is a little complex, but faster than always calling toString,
+ // http://jsperf.com/tostring-v-check
+ return _empty(a) ?
+ '' :
+ typeof a === 'string' ?
+ a.toLowerCase() :
+ !a.toString ?
+ '' :
+ a.toString();
+ },
+
+ // string-asc and -desc are retained only for compatibility with the old
+ // sort methods
+ "string-asc": function (x, y) {
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ },
+
+ "string-desc": function (x, y) {
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ }
+ });
+
+
+ // Numeric sorting types - order doesn't matter here
+ _addNumericSort('');
+
+
+ $.extend(true, DataTable.ext.renderer, {
+ header: {
+ _: function (settings, cell, column, classes) {
+ // No additional mark-up required
+ // Attach a sort listener to update on sort - note that using the
+ // `DT` namespace will allow the event to be removed automatically
+ // on destroy, while the `dt` namespaced event is the one we are
+ // listening for
+ $(settings.nTable).on('order.dt.DT', function (e, ctx, sorting, columns) {
+ if (settings !== ctx) { // need to check this this is the host
+ return; // table, not a nested one
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass(
+ column.sSortingClass + ' ' +
+ classes.sSortAsc + ' ' +
+ classes.sSortDesc
+ )
+ .addClass(columns[colIdx] == 'asc' ?
+ classes.sSortAsc : columns[colIdx] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+ });
+ },
+
+ jqueryui: function (settings, cell, column, classes) {
+ $('<div/>')
+ .addClass(classes.sSortJUIWrapper)
+ .append(cell.contents())
+ .append($('<span/>')
+ .addClass(classes.sSortIcon + ' ' + column.sSortingClassJUI)
+ )
+ .appendTo(cell);
+
+ // Attach a sort listener to update on sort
+ $(settings.nTable).on('order.dt.DT', function (e, ctx, sorting, columns) {
+ if (settings !== ctx) {
+ return;
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass(classes.sSortAsc + " " + classes.sSortDesc)
+ .addClass(columns[colIdx] == 'asc' ?
+ classes.sSortAsc : columns[colIdx] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+
+ cell
+ .find('span.' + classes.sSortIcon)
+ .removeClass(
+ classes.sSortJUIAsc + " " +
+ classes.sSortJUIDesc + " " +
+ classes.sSortJUI + " " +
+ classes.sSortJUIAscAllowed + " " +
+ classes.sSortJUIDescAllowed
+ )
+ .addClass(columns[colIdx] == 'asc' ?
+ classes.sSortJUIAsc : columns[colIdx] == 'desc' ?
+ classes.sSortJUIDesc :
+ column.sSortingClassJUI
+ );
+ });
+ }
+ }
+ });
+
+ /*
+ * Public helper functions. These aren't used internally by DataTables, or
+ * called by any of the options passed into DataTables, but they can be used
+ * externally by developers working with DataTables. They are helper functions
+ * to make working with DataTables a little bit easier.
+ */
+
+ var __htmlEscapeEntities = function (d) {
+ return typeof d === 'string' ?
+ d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
+ d;
+ };
+
+ /**
+ * Helpers for `columns.render`.
+ *
+ * The options defined here can be used with the `columns.render` initialisation
+ * option to provide a display renderer. The following functions are defined:
+ *
+ * * `number` - Will format numeric data (defined by `columns.data`) for
+ * display, retaining the original unformatted data for sorting and filtering.
+ * It takes 5 parameters:
+ * * `string` - Thousands grouping separator
+ * * `string` - Decimal point indicator
+ * * `integer` - Number of decimal points to show
+ * * `string` (optional) - Prefix.
+ * * `string` (optional) - Postfix (/suffix).
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
+ * parameters.
+ *
+ * @example
+ * // Column definition using the number renderer
+ * {
+ * data: "salary",
+ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
+ * }
+ *
+ * @namespace
+ */
+ DataTable.render = {
+ number: function (thousands, decimal, precision, prefix, postfix) {
+ return {
+ display: function (d) {
+ if (typeof d !== 'number' && typeof d !== 'string') {
+ return d;
+ }
+
+ var negative = d < 0 ? '-' : '';
+ var flo = parseFloat(d);
+
+ // If NaN then there isn't much formatting that we can do - just
+ // return immediately, escaping any HTML (this was supposed to
+ // be a number after all)
+ if (isNaN(flo)) {
+ return __htmlEscapeEntities(d);
+ }
+
+ d = Math.abs(flo);
+
+ var intPart = parseInt(d, 10);
+ var floatPart = precision ?
+ decimal + (d - intPart).toFixed(precision).substring(2) :
+ '';
+
+ return negative + (prefix || '') +
+ intPart.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g, thousands
+ ) +
+ floatPart +
+ (postfix || '');
+ }
+ };
+ },
+
+ text: function () {
+ return {
+ display: __htmlEscapeEntities
+ };
+ }
+ };
+
+
+ /*
+ * This is really a good bit rubbish this method of exposing the internal methods
+ * publicly... - To be fixed in 2.0 using methods on the prototype
+ */
+
+
+ /**
+ * Create a wrapper function for exporting an internal functions to an external API.
+ * @param {string} fn API function name
+ * @returns {function} wrapped function
+ * @memberof DataTable#internal
+ */
+ function _fnExternApiFunc(fn) {
+ return function () {
+ var args = [_fnSettingsFromNode(this[DataTable.ext.iApiIndex])].concat(
+ Array.prototype.slice.call(arguments)
+ );
+ return DataTable.ext.internal[fn].apply(this, args);
+ };
+ }
+
+
+ /**
+ * Reference to internal functions for use by plug-in developers. Note that
+ * these methods are references to internal functions and are considered to be
+ * private. If you use these methods, be aware that they are liable to change
+ * between versions.
+ * @namespace
+ */
+ $.extend(DataTable.ext.internal, {
+ _fnExternApiFunc: _fnExternApiFunc,
+ _fnBuildAjax: _fnBuildAjax,
+ _fnAjaxUpdate: _fnAjaxUpdate,
+ _fnAjaxParameters: _fnAjaxParameters,
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
+ _fnAjaxDataSrc: _fnAjaxDataSrc,
+ _fnAddColumn: _fnAddColumn,
+ _fnColumnOptions: _fnColumnOptions,
+ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
+ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
+ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
+ _fnVisbleColumns: _fnVisbleColumns,
+ _fnGetColumns: _fnGetColumns,
+ _fnColumnTypes: _fnColumnTypes,
+ _fnApplyColumnDefs: _fnApplyColumnDefs,
+ _fnHungarianMap: _fnHungarianMap,
+ _fnCamelToHungarian: _fnCamelToHungarian,
+ _fnLanguageCompat: _fnLanguageCompat,
+ _fnBrowserDetect: _fnBrowserDetect,
+ _fnAddData: _fnAddData,
+ _fnAddTr: _fnAddTr,
+ _fnNodeToDataIndex: _fnNodeToDataIndex,
+ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
+ _fnGetCellData: _fnGetCellData,
+ _fnSetCellData: _fnSetCellData,
+ _fnSplitObjNotation: _fnSplitObjNotation,
+ _fnGetObjectDataFn: _fnGetObjectDataFn,
+ _fnSetObjectDataFn: _fnSetObjectDataFn,
+ _fnGetDataMaster: _fnGetDataMaster,
+ _fnClearTable: _fnClearTable,
+ _fnDeleteIndex: _fnDeleteIndex,
+ _fnInvalidate: _fnInvalidate,
+ _fnGetRowElements: _fnGetRowElements,
+ _fnCreateTr: _fnCreateTr,
+ _fnBuildHead: _fnBuildHead,
+ _fnDrawHead: _fnDrawHead,
+ _fnDraw: _fnDraw,
+ _fnReDraw: _fnReDraw,
+ _fnAddOptionsHtml: _fnAddOptionsHtml,
+ _fnDetectHeader: _fnDetectHeader,
+ _fnGetUniqueThs: _fnGetUniqueThs,
+ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
+ _fnFilterComplete: _fnFilterComplete,
+ _fnFilterCustom: _fnFilterCustom,
+ _fnFilterColumn: _fnFilterColumn,
+ _fnFilter: _fnFilter,
+ _fnFilterCreateSearch: _fnFilterCreateSearch,
+ _fnEscapeRegex: _fnEscapeRegex,
+ _fnFilterData: _fnFilterData,
+ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
+ _fnUpdateInfo: _fnUpdateInfo,
+ _fnInfoMacros: _fnInfoMacros,
+ _fnInitialise: _fnInitialise,
+ _fnInitComplete: _fnInitComplete,
+ _fnLengthChange: _fnLengthChange,
+ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
+ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
+ _fnPageChange: _fnPageChange,
+ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
+ _fnProcessingDisplay: _fnProcessingDisplay,
+ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
+ _fnScrollDraw: _fnScrollDraw,
+ _fnApplyToChildren: _fnApplyToChildren,
+ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
+ _fnThrottle: _fnThrottle,
+ _fnConvertToWidth: _fnConvertToWidth,
+ _fnGetWidestNode: _fnGetWidestNode,
+ _fnGetMaxLenString: _fnGetMaxLenString,
+ _fnStringToCss: _fnStringToCss,
+ _fnSortFlatten: _fnSortFlatten,
+ _fnSort: _fnSort,
+ _fnSortAria: _fnSortAria,
+ _fnSortListener: _fnSortListener,
+ _fnSortAttachListener: _fnSortAttachListener,
+ _fnSortingClasses: _fnSortingClasses,
+ _fnSortData: _fnSortData,
+ _fnSaveState: _fnSaveState,
+ _fnLoadState: _fnLoadState,
+ _fnSettingsFromNode: _fnSettingsFromNode,
+ _fnLog: _fnLog,
+ _fnMap: _fnMap,
+ _fnBindAction: _fnBindAction,
+ _fnCallbackReg: _fnCallbackReg,
+ _fnCallbackFire: _fnCallbackFire,
+ _fnLengthOverflow: _fnLengthOverflow,
+ _fnRenderer: _fnRenderer,
+ _fnDataSource: _fnDataSource,
+ _fnRowAttributes: _fnRowAttributes,
+ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
+ // in 1.10, so this dead-end function is
+ // added to prevent errors
+ });
+
+
+ // jQuery access
+ $.fn.dataTable = DataTable;
+
+ // Provide access to the host jQuery object (circular reference)
+ DataTable.$ = $;
+
+ // Legacy aliases
+ $.fn.dataTableSettings = DataTable.settings;
+ $.fn.dataTableExt = DataTable.ext;
+
+ // With a capital `D` we return a DataTables API instance rather than a
+ // jQuery object
+ $.fn.DataTable = function (opts) {
+ return $(this).dataTable(opts).api();
+ };
+
+ // All properties that are available to $.fn.dataTable should also be
+ // available on $.fn.DataTable
+ $.each(DataTable, function (prop, val) {
+ $.fn.DataTable[prop] = val;
+ });
+
+
+ // Information about events fired by DataTables - for documentation.
+ /**
+ * Draw event, fired whenever the table is redrawn on the page, at the same
+ * point as fnDrawCallback. This may be useful for binding events or
+ * performing calculations when the table is altered at all.
+ * @name DataTable#draw.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Search event, fired when the searching applied to the table (using the
+ * built-in global search, or column filters) is altered.
+ * @name DataTable#search.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Page change event, fired when the paging of the table is altered.
+ * @name DataTable#page.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Order event, fired when the ordering applied to the table is altered.
+ * @name DataTable#order.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * DataTables initialisation complete event, fired when the table is fully
+ * drawn, including Ajax data loaded, if Ajax data is required.
+ * @name DataTable#init.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} oSettings DataTables settings object
+ * @param {object} json The JSON object request from the server - only
+ * present if client-side Ajax sourced data is used</li></ol>
+ */
+
+ /**
+ * State save event, fired when the table has changed state a new state save
+ * is required. This event allows modification of the state saving object
+ * prior to actually doing the save, including addition or other state
+ * properties (for plug-ins) or modification of a DataTables core property.
+ * @name DataTable#stateSaveParams.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} oSettings DataTables settings object
+ * @param {object} json The state information to be saved
+ */
+
+ /**
+ * State load event, fired when the table is loading state from the stored
+ * data, but prior to the settings object being modified by the saved state
+ * - allowing modification of the saved state is required or loading of
+ * state for a plug-in.
+ * @name DataTable#stateLoadParams.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} oSettings DataTables settings object
+ * @param {object} json The saved state information
+ */
+
+ /**
+ * State loaded event, fired when state has been loaded from stored data and
+ * the settings object has been modified by the loaded data.
+ * @name DataTable#stateLoaded.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} oSettings DataTables settings object
+ * @param {object} json The saved state information
+ */
+
+ /**
+ * Processing event, fired when DataTables is doing some kind of processing
+ * (be it, order, searcg or anything else). It can be used to indicate to
+ * the end user that there is something happening, or that something has
+ * finished.
+ * @name DataTable#processing.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} oSettings DataTables settings object
+ * @param {boolean} bShow Flag for if DataTables is doing processing or not
+ */
+
+ /**
+ * Ajax (XHR) event, fired whenever an Ajax request is completed from a
+ * request to made to the server for new data. This event is called before
+ * DataTables processed the returned data, so it can also be used to pre-
+ * process the data returned from the server, if needed.
+ *
+ * Note that this trigger is called in `fnServerData`, if you override
+ * `fnServerData` and which to use this event, you need to trigger it in you
+ * success function.
+ * @name DataTable#xhr.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ * @param {object} json JSON returned from the server
+ *
+ * @example
+ * // Use a custom property returned from the server in another DOM element
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
+ * $('#status').html( json.status );
+ * } );
+ *
+ * @example
+ * // Pre-process the data returned from the server
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
+ * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
+ * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
+ * }
+ * // Note no return - manipulate the data directly in the JSON object.
+ * } );
+ */
+
+ /**
+ * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
+ * or passing the bDestroy:true parameter in the initialisation object. This
+ * can be used to remove bound events, added DOM nodes, etc.
+ * @name DataTable#destroy.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Page length change event, fired when number of records to show on each
+ * page (the length) is changed.
+ * @name DataTable#length.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ * @param {integer} len New length
+ */
+
+ /**
+ * Column sizing has changed.
+ * @name DataTable#column-sizing.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Column visibility has changed.
+ * @name DataTable#column-visibility.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ * @param {int} column Column index
+ * @param {bool} vis `false` if column now hidden, or `true` if visible
+ */
+
+ return $.fn.dataTable;
+ }));
diff --git a/js/xloadtree/xtree2.js b/js/xloadtree/xtree2.js
index c7feecbc..ebac7e14 100644
--- a/js/xloadtree/xtree2.js
+++ b/js/xloadtree/xtree2.js
@@ -54,11 +54,12 @@
// WebFXTreePersisitance
function WebFXTreePersistence() {}
var _p = WebFXTreePersistence.prototype;
-_p.getExpanded = function (oNode) { return false; };
+_p.getExpanded = function (oNode) {
+ return false;
+};
_p.setExpanded = function (oNode, bOpen) {};
-
// Cookie handling
function WebFXCookie() {}
@@ -128,7 +129,6 @@ _p.setExpanded = function (oNode, bOpen) {
};
-
// this object provides a few useful methods when working with arrays
var arrayHelper = {
indexOf: function (a, o) {
@@ -161,25 +161,25 @@ var arrayHelper = {
// WebFX Tree Config object //
///////////////////////////////////////////////////////////////////////////////
var webFXTreeConfig = {
- rootIcon : "images/folder.png",
- openRootIcon : "images/openfolder.png",
- folderIcon : "images/folder.png",
- openFolderIcon : "images/openfolder.png",
- fileIcon : "images/file.png",
- iIcon : "images/I.png",
- lIcon : "images/L.png",
- lMinusIcon : "images/Lminus.png",
- lPlusIcon : "images/Lplus.png",
- tIcon : "images/T.png",
- tMinusIcon : "images/Tminus.png",
- tPlusIcon : "images/Tplus.png",
- plusIcon : "images/plus.png",
- minusIcon : "images/minus.png",
- blankIcon : "images/blank.png",
- defaultText : "Tree Item",
- defaultAction : null,
- defaultBehavior : "classic",
- usePersistence : true
+ rootIcon: "images/folder.png",
+ openRootIcon: "images/openfolder.png",
+ folderIcon: "images/folder.png",
+ openFolderIcon: "images/openfolder.png",
+ fileIcon: "images/file.png",
+ iIcon: "images/I.png",
+ lIcon: "images/L.png",
+ lMinusIcon: "images/Lminus.png",
+ lPlusIcon: "images/Lplus.png",
+ tIcon: "images/T.png",
+ tMinusIcon: "images/Tminus.png",
+ tPlusIcon: "images/Tplus.png",
+ plusIcon: "images/plus.png",
+ minusIcon: "images/minus.png",
+ blankIcon: "images/blank.png",
+ defaultText: "Tree Item",
+ defaultAction: null,
+ defaultBehavior: "classic",
+ usePersistence: true
};
///////////////////////////////////////////////////////////////////////////////
@@ -201,7 +201,7 @@ var webFXTreeHandler = {
addNode: function (oNode) {
this.all[oNode.id] = oNode;
},
- removeNode: function (oNode) {
+ removeNode: function (oNode) {
delete this.all[oNode.id];
},
@@ -235,24 +235,24 @@ var webFXTreeHandler = {
_htmlToText: function (s) {
switch (s) {
- case "&amp;":
- return "&";
- case "&lt;":
- return "<";
- case "&gt;":
- return ">";
- case "&quot;":
- return "\"";
- case "&nbsp;":
- return String.fromCharCode(160);
- default:
- if (/\s+/.test(s)) {
- return " ";
- }
- if (/^<BR/gi.test(s)) {
- return "\n";
- }
- return "";
+ case "&amp;":
+ return "&";
+ case "&lt;":
+ return "<";
+ case "&gt;":
+ return ">";
+ case "&quot;":
+ return "\"";
+ case "&nbsp;":
+ return String.fromCharCode(160);
+ default:
+ if (/\s+/.test(s)) {
+ return " ";
+ }
+ if (/^<BR/gi.test(s)) {
+ return "\n";
+ }
+ return "";
}
},
@@ -262,18 +262,18 @@ var webFXTreeHandler = {
_textToHtml: function (s) {
switch (s) {
- case "&":
- return "&amp;";
- case "<":
- return "&lt;";
- case ">":
- return "&gt;";
- case "\n":
- return "<BR>";
- case "\"":
- return "&quot;"; // so we can use this in attributes
- default:
- return "&nbsp;";
+ case "&":
+ return "&amp;";
+ case "<":
+ return "&lt;";
+ case ">":
+ return "&gt;";
+ case "\n":
+ return "<BR>";
+ case "\"":
+ return "&quot;"; // so we can use this in attributes
+ default:
+ return "&nbsp;";
}
},
@@ -300,7 +300,7 @@ function WebFXTreeAbstractNode(sText, oAction, oIconAction) {
_p = WebFXTreeAbstractNode.prototype;
_p._selected = false;
-_p.indentWidth = 19;
+_p.indentWidth = 15;
_p.open = false;
_p.text = webFXTreeConfig.defaultText;
_p.action = null;
@@ -381,7 +381,6 @@ _p.add = function (oChild, oBefore) {
};
-
_p.remove = function (oChild) {
// backwards compatible. If no argument remove the node
if (arguments.length == 0) {
@@ -586,7 +585,7 @@ _p._setSelected = function (b) {
if (this._selected != b) {
this._selected = b;
- var wasFocused = false; // used to keep focus state
+ var wasFocused = false; // used to keep focus state
var si = t.getSelected();
if (b && si != null && si != this) {
var oldFireChange = t._fireChange;
@@ -893,24 +892,24 @@ _p.getExpandIconSrc = function () {
}
switch (bits) {
- case 1:
- return webFXTreeConfig.plusIcon;
- case 2:
- return webFXTreeConfig.minusIcon;
- case 4:
- return webFXTreeConfig.lIcon;
- case 5:
- return webFXTreeConfig.lPlusIcon;
- case 6:
- return webFXTreeConfig.lMinusIcon;
- case 8:
- return webFXTreeConfig.tIcon;
- case 9:
- return webFXTreeConfig.tPlusIcon;
- case 10:
- return webFXTreeConfig.tMinusIcon;
- default: // 0
- return webFXTreeConfig.blankIcon;
+ case 1:
+ return webFXTreeConfig.plusIcon;
+ case 2:
+ return webFXTreeConfig.minusIcon;
+ case 4:
+ return webFXTreeConfig.lIcon;
+ case 5:
+ return webFXTreeConfig.lPlusIcon;
+ case 6:
+ return webFXTreeConfig.lMinusIcon;
+ case 8:
+ return webFXTreeConfig.tIcon;
+ case 9:
+ return webFXTreeConfig.tPlusIcon;
+ case 10:
+ return webFXTreeConfig.tMinusIcon;
+ default: // 0
+ return webFXTreeConfig.blankIcon;
}
} else {
if (t && hideLines) {
@@ -1079,8 +1078,8 @@ _p.setIconAction = function (oAction) {
var el = this.getIconElement();
if (el) {
el.href = this._getIconHref();
- }
-}
+ }
+}
_p.getIconAction = function () {
if (this.iconAction)
@@ -1144,8 +1143,8 @@ _p._onmousedown = function (e) {
}
this.select();
- if (/*!/webfx-tree-item-label/.test(el.className) && */!webFXTreeHandler.opera) { // opera cancels the click if focus is called
-
+ if ( /*!/webfx-tree-item-label/.test(el.className) && */ !webFXTreeHandler.opera) { // opera cancels the click if focus is called
+
// in case we are not clicking on the label
if (webFXTreeHandler.ie) {
window.setTimeout("WebFXTreeAbstractNode._onTimeoutFocus(\"" + this.id + "\")", 10);
@@ -1170,7 +1169,7 @@ _p._onclick = function (e) {
var doAction = null;
if (/webfx-tree-icon/.test(el.className) && this.iconAction) {
- doAction = this.iconAction;
+ doAction = this.iconAction;
} else {
doAction = this.action;
}
@@ -1207,52 +1206,52 @@ _p._onkeydown = function (e) {
var n;
var rv = true;
switch (e.keyCode) {
- case 39: // RIGHT
- if (e.altKey) {
- rv = true;
- break;
- }
- if (this.hasChildren()) {
- if (!this.getExpanded()) {
- this.setExpanded(true);
- } else {
- this.getFirstChild().focus();
- }
- }
- rv = false;
+ case 39: // RIGHT
+ if (e.altKey) {
+ rv = true;
break;
- case 37: // LEFT
- if (e.altKey) {
- rv = true;
- break;
- }
- if (this.hasChildren() && this.getExpanded()) {
- this.setExpanded(false);
+ }
+ if (this.hasChildren()) {
+ if (!this.getExpanded()) {
+ this.setExpanded(true);
} else {
- var p = this.getParent();
- var t = this.getTree();
- // don't go to root if hidden
- if (p && (t.showRootNode || p != t)) {
- p.focus();
- }
+ this.getFirstChild().focus();
}
- rv = false;
- break;
-
- case 40: // DOWN
- n = this.getNextShownNode();
- if (n) {
- n.focus();
- }
- rv = false;
+ }
+ rv = false;
+ break;
+ case 37: // LEFT
+ if (e.altKey) {
+ rv = true;
break;
- case 38: // UP
- n = this.getPreviousShownNode()
- if (n) {
- n.focus();
+ }
+ if (this.hasChildren() && this.getExpanded()) {
+ this.setExpanded(false);
+ } else {
+ var p = this.getParent();
+ var t = this.getTree();
+ // don't go to root if hidden
+ if (p && (t.showRootNode || p != t)) {
+ p.focus();
}
- rv = false;
- break;
+ }
+ rv = false;
+ break;
+
+ case 40: // DOWN
+ n = this.getNextShownNode();
+ if (n) {
+ n.focus();
+ }
+ rv = false;
+ break;
+ case 38: // UP
+ n = this.getPreviousShownNode()
+ if (n) {
+ n.focus();
+ }
+ rv = false;
+ break;
}
if (!rv && e.preventDefault) {
@@ -1325,11 +1324,6 @@ _p.getPreviousShownNode = function () {
};
-
-
-
-
-
///////////////////////////////////////////////////////////////////////////////
// WebFXTree
///////////////////////////////////////////////////////////////////////////////
@@ -1412,7 +1406,7 @@ _p.getRowClassName = function () {
_p.getIconSrc = function () {
var behavior = this.getTree() ? this.getTree().getBehavior() : webFXTreeConfig.defaultBehavior;
var open = behavior == "classic" && this.getExpanded() ||
- behavior != "classic" && this.isSelected();
+ behavior != "classic" && this.isSelected();
if (open && this.openIcon) {
return this.openIcon;
}
@@ -1454,7 +1448,7 @@ _p.setTabIndex = function (i) {
this.tabIndex = i;
if (n) {
n._setTabIndex(i);
- }
+ }
};
_p.getTabIndex = function () {
@@ -1545,7 +1539,6 @@ _p.getSuspendRedraw = function () {
};
-
///////////////////////////////////////////////////////////////////////////////
// WebFXTreeItem
///////////////////////////////////////////////////////////////////////////////
@@ -1593,7 +1586,7 @@ _p.getCreated = function () {
_p.getIconSrc = function () {
var behavior = this.getTree() ? this.getTree().getBehavior() : webFXTreeConfig.defaultBehavior;
var open = behavior == "classic" && this.getExpanded() ||
- behavior != "classic" && this.isSelected();
+ behavior != "classic" && this.isSelected();
if (open && this.openIcon) {
return this.openIcon;
}
@@ -1611,8 +1604,6 @@ _p.getIconSrc = function () {
/* end tree model */
-
-
if (window.attachEvent) {
window.attachEvent("onunload", function () {
for (var id in webFXTreeHandler.all)
diff --git a/logout.php b/logout.php
deleted file mode 100644
index 7e73ebda..00000000
--- a/logout.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-/**
- * Logs a user out of the app
- *
- * $Id: logout.php,v 1.3 2003/09/10 01:55:52 chriskl Exp $
- */
-
-if (!ini_get('session.auto_start')) {
- session_name('PPA_ID');
- session_start();
-}
-unset($_SESSION);
-session_destroy();
-
-header('Location: index.php');
diff --git a/src/classes/Highlight.php b/src/classes/Highlight.php
new file mode 100644
index 00000000..1cbb0d2a
--- /dev/null
+++ b/src/classes/Highlight.php
@@ -0,0 +1,1112 @@
+<?php
+namespace PHPPgAdmin;
+
+/* This software is licensed through a BSD-style License.
+ * http://www.opensource.org/licenses/bsd-license.php
+
+Copyright (c) 2003, 2004, Jacob D. Cohen
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+Neither the name of Jacob D. Cohen nor the names of his contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+class Highlight {
+
+ const NORMAL_TEXT = 1;
+ const DQ_LITERAL = 2;
+ const DQ_ESCAPE = 3;
+ const SQ_LITERAL = 4;
+ const SQ_ESCAPE = 5;
+ const SLASH_BEGIN = 6;
+ const STAR_COMMENT = 7;
+ const STAR_END = 8;
+ const LINE_COMMENT = 9;
+ const HTML_ENTITY = 10;
+ const LC_ESCAPE = 11;
+ const BLOCK_COMMENT = 12;
+ const PAREN_BEGIN = 13;
+ const DASH_BEGIN = 14;
+ const BT_LITERAL = 15;
+ const BT_ESCAPE = 16;
+ const XML_TAG_BEGIN = 17;
+ const XML_TAG = 18;
+ const XML_PI = 19;
+ const SCH_NORMAL = 20;
+ const SCH_STRESC = 21;
+ const SCH_IDEXPR = 22;
+ const SCH_NUMLIT = 23;
+ const SCH_CHRLIT = 24;
+ const SCH_STRLIT = 25;
+ public $initial_state = ["Scheme" => self::SCH_NORMAL];
+ public $sch = [];
+ public $c89 = [];
+ public $c99 = [];
+ public $cpp = [];
+ public $cs = [];
+ public $edges = [];
+ public $java = [];
+ public $mirc = [];
+ public $pascal = [];
+ public $perl = [];
+ public $php = [];
+ public $pli = [];
+ public $process = [];
+ public $process_end = [];
+ public $python = [];
+ public $ruby = [];
+ public $sql = [];
+ public $states = [];
+ public $vb = [];
+ public $xml = [];
+
+ /* Constructor */
+
+ function __construct() {
+
+ $keyword_replace = function ($keywords, $text, $ncs = false) {
+ $cm = ($ncs) ? "i" : "";
+ foreach ($keywords as $keyword) {
+ $search[] = "/(\\b$keyword\\b)/" . $cm;
+ $replace[] = '<span class="keyword">\\0</span>';
+ }
+
+ $search[] = "/(\\bclass\s)/";
+ $replace[] = '<span class="keyword">\\0</span>';
+
+ return preg_replace($search, $replace, $text);
+ };
+
+ $preproc_replace = function ($preproc, $text) {
+ foreach ($preproc as $proc) {
+ $search[] = "/(\\s*#\s*$proc\\b)/";
+ $replace[] = '<span class="keyword">\\0</span>';
+ }
+
+ return preg_replace($search, $replace, $text);
+ };
+
+ $sch_syntax_helper = function ($text) {
+ return $text;
+ };
+
+ $syntax_highlight_helper = function ($text, $language) use ($keyword_replace, $preproc_replac) {
+ $preproc = [];
+ $preproc["C++"] = [
+ "if", "ifdef", "ifndef", "elif", "else",
+ "endif", "include", "define", "undef", "line",
+ "error", "pragma"];
+ $preproc["C89"] = &$preproc["C++"];
+ $preproc["C"] = &$preproc["C89"];
+
+ $keywords = [
+ "C++" => [
+ "asm", "auto", "bool", "break", "case",
+ "catch", "char", /*class*/"const", "const_cast",
+ "continue", "default", "delete", "do", "double",
+ "dynamic_cast", "else", "enum", "explicit", "export",
+ "extern", "false", "float", "for", "friend",
+ "goto", "if", "inline", "int", "long",
+ "mutable", "namespace", "new", "operator", "private",
+ "protected", "public", "register", "reinterpret_cast", "return",
+ "short", "signed", "sizeof", "static", "static_cast",
+ "struct", "switch", "template", "this", "throw",
+ "true", "try", "typedef", "typeid", "typename",
+ "union", "unsigned", "using", "virtual", "void",
+ "volatile", "wchar_t", "while"],
+
+ "C89" => [
+ "auto", "break", "case", "char", "const",
+ "continue", "default", "do", "double", "else",
+ "enum", "extern", "float", "for", "goto",
+ "if", "int", "long", "register", "return",
+ "short", "signed", "sizeof", "static", "struct",
+ "switch", "typedef", "union", "unsigned", "void",
+ "volatile", "while"],
+
+ "C" => [
+ "auto", "break", "case", "char", "const",
+ "continue", "default", "do", "double", "else",
+ "enum", "extern", "float", "for", "goto",
+ "if", "int", "long", "register", "return",
+ "short", "signed", "sizeof", "static", "struct",
+ "switch", "typedef", "union", "unsigned", "void",
+ "volatile", "while", "__restrict", "_Bool"],
+
+ "PHP" => [
+ "and", "or", "xor", "__FILE__", "__LINE__",
+ "array", "as", "break", "case", "cfunction",
+ /*class*/"const", "continue", "declare", "default",
+ "die", "do", "echo", "else", "elseif",
+ "empty", "enddeclare", "endfor", "endforeach", "endif",
+ "endswitch", "endwhile", "eval", "exit", "extends",
+ "for", "foreach", "function", "global", "if",
+ "include", "include_once", "isset", "list", "new",
+ "old_function", "print", "require", "require_once", "return",
+ "static", "switch", "unset", "use", "var",
+ "while", "__FUNCTION__", "__CLASS__"],
+
+ "Perl" => [
+ "-A", "-B", "-C", "-M", "-O",
+ "-R", "-S", "-T", "-W", "-X",
+ "-b", "-c", "-d", "-e", "-f",
+ "-g", "-k", "-l", "-o", "-p",
+ "-r", "-s", "-t", "-u", "-w",
+ "-x", "-z", "ARGV", "DATA", "ENV",
+ "SIG", "STDERR", "STDIN", "STDOUT", "atan2",
+ "bind", "binmode", "bless", "caller", "chdir",
+ "chmod", "chomp", "chop", "chown", "chr",
+ "chroot", "close", "closedir", "cmp", "connect",
+ "continue", "cos", "crypt", "dbmclose", "dbmopen",
+ "defined", "delete", "die", "do", "dump",
+ "each", "else", "elsif", "endgrent", "endhostent",
+ "endnetent", "endprotent", "endpwent", "endservent", "eof",
+ "eq", "eval", "exec", "exists", "exit",
+ "exp", "fcntl", "fileno", "flock", "for",
+ "foreach", "fork", "format", "formline", "ge",
+ "getc", "getgrent", "getgrid", "getgrnam", "gethostbyaddr",
+ "gethostbyname", "gethostent", "getlogin", "getnetbyaddr", "getnetbyname",
+ "getnetent", "getpeername", "getpgrp", "getppid", "getpriority",
+ "getprotobyname", "getprotobynumber", "getprotoent", "getpwent", "getpwnam",
+ "getpwuid", "getservbyname", "getservbyport", "getservent", "getsockname",
+ "getsockopt", "glob", "gmtime", "goto", "grep",
+ /*gt*/"hex", "if", "import", "index",
+ "int", "ioctl", "join", "keys", "kill",
+ "last", "lc", "lcfirst", "le", "length",
+ "link", "listen", "local", "localtime", "log",
+ "lstat", /*lt*/"m", "map", "mkdir",
+ "msgctl", "msgget", "msgrcv", "msgsnd", "my",
+ "ne", "next", "no", "oct", "open",
+ "opendir", "ord", "pack", "package", "pipe",
+ "pop", "pos", "print", "printf", "push",
+ "q", "qq", "quotemeta", "qw", "qx",
+ "rand", "read", "readdir", "readlink", "recv",
+ "redo", "ref", "refname", "require", "reset",
+ "return", "reverse", "rewinddir", "rindex", "rmdir",
+ "s", "scalar", "seek", "seekdir", "select",
+ "semctl", "semget", "semop", "send", "setgrent",
+ "sethostent", "setnetent", "setpgrp", "setpriority", "setprotoent",
+ "setpwent", "setservent", "setsockopt", "shift", "shmctl",
+ "shmget", "shmread", "shmwrite", "shutdown", "sin",
+ "sleep", "socket", "socketpair", "sort", "splice",
+ "split", "sprintf", "sqrt", "srand", "stat",
+ "study", "sub", "substr", "symlink", "syscall",
+ "sysopen", "sysread", "system", "syswrite", "tell",
+ "telldir", "tie", "tied", "time", "times",
+ "tr", "truncate", "uc", "ucfirst", "umask",
+ "undef", "unless", "unlink", "unpack", "unshift",
+ "untie", "until", "use", "utime", "values",
+ "vec", "wait", "waitpid", "wantarray", "warn",
+ "while", "write", "y", "or", "and",
+ "not"],
+
+ "Java" => [
+ "abstract", "boolean", "break", "byte", "case",
+ "catch", "char", /*class*/"const", "continue",
+ "default", "do", "double", "else", "extends",
+ "final", "finally", "float", "for", "goto",
+ "if", "implements", "import", "instanceof", "int",
+ "interface", "long", "native", "new", "package",
+ "private", "protected", "public", "return", "short",
+ "static", "strictfp", "super", "switch", "synchronized",
+ "this", "throw", "throws", "transient", "try",
+ "void", "volatile", "while"],
+
+ "VB" => [
+ "AddressOf", "Alias", "And", "Any", "As",
+ "Binary", "Boolean", "ByRef", "Byte", "ByVal",
+ "Call", "Case", "CBool", "CByte", "CCur",
+ "CDate", "CDbl", "CInt", "CLng", "Close",
+ "Const", "CSng", "CStr", "Currency", "CVar",
+ "CVErr", "Date", "Debug", "Declare", "DefBool",
+ "DefByte", "DefCur", "DefDate", "DefDbl", "DefInt",
+ "DefLng", "DefObj", "DefSng", "DefStr", "DefVar",
+ "Dim", "Do", "Double", "Each", "Else",
+ "End", "Enum", "Eqv", "Erase", "Error",
+ "Event", "Exit", "For", "Friend", "Function",
+ "Get", "Get", "Global", "GoSub", "GoTo",
+ "If", "Imp", "Implements", "In", "Input",
+ "Integer", "Is", "LBound", "Len", "Let",
+ "Lib", "Like", "Line", "Lock", "Long",
+ "Loop", "LSet", "Mod", "Name", "Next",
+ "Not", "Nothing", "Null", "Object", "On",
+ "Open", "Option Base 1", "Option Compare Binary",
+ "Option Compare Database", "Option Compare Text", "Option Explicit",
+ "Option Private Module", "Optional", "Or", "Output",
+ "ParamArray", "Preserve", "Print", "Private", "Property",
+ "Public", "Put", "RaiseEvent", "Random", "Read",
+ "ReDim", "Resume", "Return", "RSet", "Seek",
+ "Select", "Set", "Single", "Spc", "Static",
+ "Step", "Stop", "String", "Sub", "Tab",
+ "Then", "To", "Type", "UBound", "Unlock",
+ "Variant", "Wend", "While", "With", "WithEvents",
+ "Write", "Xor"],
+
+ "C#" => [
+ "abstract", "as", "base", "bool", "break",
+ "byte", "case", "catch", "char", "checked",
+ /*class*/"const", "continue", "decimal", "default",
+ "delegate", "do", "double", "else", "enum",
+ "event", "explicit", "extern", "false", "finally",
+ "fixed", "float", "for", "foreach", "goto",
+ "if", "implicit", "in", "int", "interface",
+ "internal", "is", "lock", "long", "namespace",
+ "new", "null", "object", "operator", "out",
+ "override", "params", "private", "protected", "public",
+ "readonly", "ref", "return", "sbyte", "sealed",
+ "short", "sizeof", "stackalloc", "static", "string",
+ "struct", "switch", "this", "throw", "true",
+ "try", "typeof", "uint", "ulong", "unchecked",
+ "unsafe", "ushort", "using", "virtual", "volatile",
+ "void", "while"],
+
+ "Ruby" => [
+ "alias", "and", "begin", "break", "case",
+ /*class*/"def", "defined", "do", "else",
+ "elsif", "end", "ensure", "false", "for",
+ "if", "in", "module", "next", "module",
+ "next", "nil", "not", "or", "redo",
+ "rescue", "retry", "return", "self", "super",
+ "then", "true", "undef", "unless", "until",
+ "when", "while", "yield"],
+
+ "Python" => [
+ "and", "assert", "break", /*"class",*/"continue",
+ "def", "del", "elif", "else", "except",
+ "exec", "finally", "for", "from", "global",
+ "if", "import", "in", "is", "lambda",
+ "not", "or", "pass", "print", "raise",
+ "return", "try", "while", "yield"],
+
+ "Pascal" => [
+ "Absolute", "Abstract", "All", "And", "And_then",
+ "Array", "Asm", "Begin", "Bindable", "Case",
+ /*"Class",*/"Const", "Constructor", "Destructor", "Div",
+ "Do", "Downto", "Else", "End", "Export",
+ "File", "For", "Function", "Goto", "If",
+ "Import", "Implementation", "Inherited", "In", "Inline",
+ "Interface", "Is", "Label", "Mod", "Module",
+ "Nil", "Not", "Object", "Of", "Only",
+ "Operator", "Or", "Or_else", "Otherwise", "Packed",
+ "Pow", "Procedure", "Program", "Property", "Protected",
+ "Qualified", "Record", "Repeat", "Restricted", "Set",
+ "Shl", "Shr", "Then", "To", "Type",
+ "Unit", "Until", "Uses", "Value", "Var",
+ "View", "Virtual", "While", "With", "Xor"],
+
+ "mIRC" => [
+ ],
+
+ "PL/I" => [
+ "A", "ABS", "ACOS", "%ACTIVATE", "ACTUALCOUNT",
+ "ADD", "ADDR", "ADDREL", "ALIGNED", "ALLOCATE",
+ "ALLOC", "ALLOCATION", "ALLOCN", "ANY", "ANYCONDITION",
+ "APPEND", "AREA", "ASIN", "ATAN", "ATAND",
+ "ATANH", "AUTOMATIC", "AUTO", "B", "B1",
+ "B2", "B3", "B4", "BACKUP_DATE", "BASED",
+ "BATCH", "BEGIN", "BINARY", "BIN", "BIT",
+ "BLOCK_BOUNDARY_FORMAT", "BLOCK_IO", "BLOCK_SIZE", "BOOL",
+ "BUCKET_SIZE", "BUILTIN", "BY", "BYTE", "BYTESIZE",
+ "CALL", "CANCEL_CONTROL_O", "CARRIAGE_RETURN_FORMAT",
+ "CEIL", "CHAR", "CHARACTER", "CLOSE", "COLLATE", "COLUMN",
+ "CONDITION", "CONTIGUOUS", "CONTIGUOUS_BEST_TRY", "CONTROLLED",
+ "CONVERSION", "COPY", "COS", "COSD", "COSH",
+ "CREATION_DATE", "CURRENT_POSITION", "DATE",
+ "DATETIME", "%DEACTIVATE", "DECIMAL", "DEC", "%DECLARE",
+ "%DCL", "DECLARE", "DCL", "DECODE", "DEFAULT_FILE_NAME",
+ "DEFERRED_WRITE", "DEFINED", "DEF", "DELETE",
+ "DESCRIPTOR", "%DICTIONARY", "DIMENSION", "DIM", "DIRECT",
+ "DISPLAY", "DIVIDE", "%DO", "DO", "E",
+ "EDIT", "%ELSE", "ELSE", "EMPTY", "ENCODE",
+ "%END", "END", "ENDFILE", "ENDPAGE", "ENTRY",
+ "ENVIRONMENT", "ENV", "%ERROR", "ERROR", "EVERY",
+ "EXP", "EXPIRATION_DATE", "EXTEND", "EXTENSION_SIZE",
+ "EXTERNAL", "EXT", "F", "FAST_DELETE", "%FATAL",
+ "FILE", "FILE_ID", "FILE_ID_TO", "FILE_SIZE",
+ "FINISH", "FIXED", "FIXEDOVERFLOW", "FOFL",
+ "FIXED_CONTROL_FROM", "FIXED_CONTROL_SIZE", "FIXED_CONTROL_SIZE_TO",
+ "FIXED_CONTROL_TO", "FIXED_LENGTH_RECORDS", "FLOAT",
+ "FLOOR", "FLUSH", "FORMAT", "FREE", "FROM",
+ "GET", "GLOBALDEF", "GLOBALREF", "%GOTO",
+ "GOTO", "GO", "TO", "GROUP_PROTETION", "HBOUND",
+ "HIGH", "INDENT", "%IF", "IF", "IGNORE_LINE_MARKS",
+ "IN", "%INCLUDE", "INDEX", "INDEXED", "INDEX_NUMBER",
+ "%INFORM", "INFORM", "INITIAL", "INIT", "INITIAL_FILL",
+ "INPUT", "INT", "INTERNAL", "INTO", "KEY",
+ "KEYED", "KEYFROM", "KEYTO", "LABEL", "LBOUND",
+ "LEAVE", "LENGTH", "LIKE", "LINE", "LINENO",
+ "LINESIZE", "%LIST", "LIST", "LOCK_ON_READ", "LOCK_ON_WRITE",
+ "LOG", "LOG10", "LOG2", "LOW", "LTRIM",
+ "MAIN", "MANUAL_UNLOCKING", "MATCH_GREATER",
+ "MATCH_GREATER_EQUAL", "MATCH_NEXT", "MATCH_NEXT_EQUAL",
+ "MAX", "MAXIMUM_RECORD_NUMBER", "MAXIMUM_RECORD_SIZE",
+ "MAXLENGTH", "MEMBER", "MIN", "MOD", "MULTIBLOCK_COUNT",
+ "MULTIBUFFER_COUNT", "MULTIPLY", "NEXT_VOLUME", "%NOLIST",
+ "NOLOCK", "NONEXISTENT_RECORD", "NONRECURSIVE", "NONVARYING",
+ "NONVAR", "NORESCAN", "NO_ECHO", "NO_FILTER", "NO_SHARE",
+ "NULL", "OFFSET", "ON", "ONARGSLIST", "ONCHAR",
+ "ONCODE", "ONFILE", "ONKEY", "ONSOURCE", "OPEN",
+ "OPTIONAL", "OPTIONS", "OTHERWISE", "OTHER", "OUTPUT",
+ "OVERFLOW", "OFL", "OWNER_GROUP", "OWNER_ID",
+ "OWNER_MEMBER", "OWNER_PROTECTION", "P", "%PAGE",
+ "PAGE", "PAGENO", "PAGESIZE", "PARAMETER", "PARM",
+ "PICTURE", "PIC", "POINTER", "PTR", "POSINT",
+ "POSITION", "POS", "PRECISION", "PREC", "PRESENT",
+ "PRINT", "PRINTER_FORMAT", "%PROCEDURE", "%PROC",
+ "PROCEDURE", "PROC", "PROD", "PROMPT", "PURGE_TYPE_AHEAD",
+ "PUT", "R", "RANK", "READ", "READONLY",
+ "READ_AHEAD", "READ_CHECK", "READ_REGARDLESS", "RECORD",
+ "RECORD_ID", "RECORD_ID_ACCESS", "RECORD_ID_TO", "RECURSIVE",
+ "REFER", "REFERENCE", "RELEASE", "REPEAT", "%REPLACE",
+ "RESCAN", "RESIGNAL", "RETRIEVAL_POINTERS", "%RETURN",
+ "RETURN", "RETURNS", "REVERSE", "REVERT", "REVISION_DATE",
+ "REWIND", "REWIND_ON_CLOSE", "REWIND_ON_OPEN",
+ "REWRITE", "ROUND", "RTRIM", "%SBTTL", "SCALARVARYING",
+ "SEARCH", "SELECT", "SEQUENTIAL", "SEQL",
+ "SET", "SHARED_READ", "SHARED_WRITE", "SIGN",
+ "SIGNAL", "SIN", "SIND", "SINH", "SIZE",
+ "SKIP", "SNAP", "SOME", "SPACEBLOCK", "SPOOL",
+ "SQRT", "STATEMENT", "STATIC", "STOP", "STORAGE",
+ "STREAM", "STRING", "STRINGRANGE", "STRG",
+ "STRUCTURE", "SUBSCRIPTRANGE", "SUBRG", "SUBSTR",
+ "SUBTRACT", "SUM", "SUPERCEDE", "SYSIN", "SYSPRINT",
+ "SYSTEM", "SYSTEM_PROTECTION", "TAB", "TAN",
+ "TAND", "TANH", "TEMPORARY", "%THEN", "THEN",
+ "TIME", "TIMEOUT_PERIOD", "%TITLE", "TITLE",
+ "TO", "TRANSLATE", "TRIM", "TRUNC", "TRUNCATE",
+ "UNALIGNED", "UNAL", "UNDEFINED", "UNDF", "UNDERFLOW",
+ "UFL", "UNION", "UNSPEC", "UNTIL", "UPDATE",
+ "USER_OPEN", "VALID", "VALUE", "VAL", "VARIABLE",
+ "VARIANT", "VARYING", "VAR", "VAXCONDITION", "VERIFY",
+ "WAIT_FOR_RECORD", "%WARN", "WARN", "WHEN",
+ "WHILE", "WORLD_PROTECTION", "WRITE", "WRITE_BEHIND",
+ "WRITE_CHECK", "X", "ZERODIVIDE"],
+
+ "SQL" => [
+ "abort", "abs", "absolute", "access",
+ "action", "ada", "add", "admin",
+ "after", "aggregate", "alias", "all",
+ "allocate", "alter", "analyse", "analyze",
+ "and", "any", "are", "array",
+ "as", "asc", "asensitive", "assertion",
+ "assignment", "asymmetric", "at", "atomic",
+ "authorization", "avg", "backward", "before",
+ "begin", "between", "bigint", "binary",
+ "bit", "bitvar", "bit_length", "blob",
+ "boolean", "both", "breadth", "by",
+ "c", "cache", "call", "called",
+ "cardinality", "cascade", "cascaded", "case",
+ "cast", "catalog", "catalog_name", "chain",
+ "char", "character", "characteristics", "character_length",
+ "character_set_catalog", "character_set_name", "character_set_schema", "char_length",
+ "check", "checked", "checkpoint", /* "class", */
+ "class_origin", "clob", "close", "cluster",
+ "coalesce", "cobol", "collate", "collation",
+ "collation_catalog", "collation_name", "collation_schema", "column",
+ "column_name", "command_function", "command_function_code", "comment",
+ "commit", "committed", "completion", "condition_number",
+ "connect", "connection", "connection_name", "constraint",
+ "constraints", "constraint_catalog", "constraint_name", "constraint_schema",
+ "constructor", "contains", "continue", "conversion",
+ "convert", "copy", "corresponding", "count",
+ "create", "createdb", "createuser", "cross",
+ "cube", "current", "current_date", "current_path",
+ "current_role", "current_time", "current_timestamp", "current_user",
+ "cursor", "cursor_name", "cycle", "data",
+ "database", "date", "datetime_interval_code", "datetime_interval_precision",
+ "day", "deallocate", "dec", "decimal",
+ "declare", "default", "defaults", "deferrable",
+ "deferred", "defined", "definer", "delete",
+ "delimiter", "delimiters", "depth", "deref",
+ "desc", "describe", "descriptor", "destroy",
+ "destructor", "deterministic", "diagnostics", "dictionary",
+ "disconnect", "dispatch", "distinct", "do",
+ "domain", "double", "drop", "dynamic",
+ "dynamic_function", "dynamic_function_code", "each", "else",
+ "encoding", "encrypted", "end", "end-exec",
+ "equals", "escape", "every", "except",
+ "exception", "excluding", "exclusive", "exec",
+ "execute", "existing", "exists", "explain",
+ "external", "extract", "false", "fetch",
+ "final", "first", "float", "for",
+ "force", "foreign", "fortran", "forward",
+ "found", "free", "freeze", "from",
+ "full", "function", "g", "general",
+ "generated", "get", "global", "go",
+ "goto", "grant", "granted", "group",
+ "grouping", "handler", "having", "hierarchy",
+ "hold", "host", "hour", "identity",
+ "ignore", "ilike", "immediate", "immutable",
+ "implementation", "implicit", "in", "including",
+ "increment", "index", "indicator", "infix",
+ "inherits", "initialize", "initially", "inner",
+ "inout", "input", "insensitive", "insert",
+ "instance", "instantiable", "instead", "int",
+ "integer", "intersect", "interval", "into",
+ "invoker", "is", "isnull", "isolation",
+ "iterate", "join", "k", "key",
+ "key_member", "key_type", "lancompiler", "language",
+ "large", "last", "lateral", "leading",
+ "left", "length", "less", "level",
+ "like", "limit", "listen", "load",
+ "local", "localtime", "localtimestamp", "location",
+ "locator", "lock", "lower", "m",
+ "map", "match", "max", "maxvalue",
+ "message_length", "message_octet_length", "message_text", "method",
+ "min", "minute", "minvalue", "mod",
+ "mode", "modifies", "modify", "module",
+ "month", "more", "move", "mumps",
+ "name", "names", "national", "natural",
+ "nchar", "nclob", "new", "next",
+ "no", "nocreatedb", "nocreateuser", "none",
+ "not", "nothing", "notify", "notnull",
+ "null", "nullable", "nullif", "number",
+ "numeric", "object", "octet_length", "of",
+ "off", "offset", "oids", "old",
+ "on", "only", "open", "operation",
+ "operator", "option", "options", "or",
+ "order", "ordinality", "out", "outer",
+ "output", "overlaps", "overlay", "overriding",
+ "owner", "pad", "parameter", "parameters",
+ "parameter_mode", "parameter_name", "parameter_ordinal_position", "parameter_specific_catalog",
+ "parameter_specific_name", "parameter_specific_schema", "partial", "pascal",
+ "password", "path", "pendant", "placing",
+ "pli", "position", "postfix", "precision",
+ "prefix", "preorder", "prepare", "preserve",
+ "primary", "prior", "privileges", "procedural",
+ "procedure", "public", "read", "reads",
+ "real", "recheck", "recursive", "ref",
+ "references", "referencing", "reindex", "relative",
+ "rename", "repeatable", "replace", "reset",
+ "restart", "restrict", "result", "return",
+ "returned_length", "returned_octet_length", "returned_sqlstate", "returns",
+ "revoke", "right", "role", "rollback",
+ "rollup", "routine", "routine_catalog", "routine_name",
+ "routine_schema", "row", "rows", "row_count",
+ "rule", "savepoint", "scale", "schema",
+ "schema_name", "scope", "scroll", "search",
+ "second", "section", "security", "select",
+ "self", "sensitive", "sequence", "serializable",
+ "server_name", "session", "session_user", "set",
+ "setof", "sets", "share", "show",
+ "similar", "simple", "size", "smallint",
+ "some", "source", "space", "specific",
+ "specifictype", "specific_name", "sql", "sqlcode",
+ "sqlerror", "sqlexception", "sqlstate", "sqlwarning",
+ "stable", "start", "state", "statement",
+ "static", "statistics", "stdin", "stdout",
+ "storage", "strict", "structure", "style",
+ "subclass_origin", "sublist", "substring", "sum",
+ "symmetric", "sysid", "system", "system_user",
+ "table", "table_name", "temp", "template",
+ "temporary", "terminate", "text", "than", "then",
+ "time", "timestamp", "timezone_hour", "timezone_minute",
+ "to", "toast", "trailing", "transaction",
+ "transactions_committed", "transactions_rolled_back", "transaction_active", "transform",
+ "transforms", "translate", "translation", "treat",
+ "trigger", "trigger_catalog", "trigger_name", "trigger_schema",
+ "trim", "true", "truncate", "trusted",
+ "type", "uncommitted", "under", "unencrypted",
+ "union", "unique", "unknown", "unlisten",
+ "unnamed", "unnest", "until", "update",
+ "upper", "usage", "user", "user_defined_type_catalog",
+ "user_defined_type_name", "user_defined_type_schema", "using", "vacuum",
+ "valid", "validator", "value", "values",
+ "varchar", "variable", "varying", "verbose",
+ "version", "view", "volatile", "when",
+ "whenever", "where", "with", "without",
+ "work", "write", "year", "zone"],
+
+ ];
+
+ $case_insensitive = [
+ "VB" => true,
+ "Pascal" => true,
+ "PL/I" => true,
+ "SQL" => true,
+ ];
+ $ncs = false;
+ if (array_key_exists($language, $case_insensitive)) {
+ $ncs = true;
+ }
+
+ $text = (array_key_exists($language, $preproc)) ?
+ $preproc_replace($preproc[$language], $text) :
+ $text;
+ $text = (array_key_exists($language, $keywords)) ?
+ $keyword_replace($keywords[$language], $text, $ncs) :
+ $text;
+
+ return $text;
+ };
+
+ $rtrim1 = function ($span, $lang, $ch) use ($syntax_highlight_helper) {
+ return $syntax_highlight_helper(substr($span, 0, -1), $lang);
+ };
+
+ $rtrim1_htmlesc = function ($span, $lang, $ch) {
+ return htmlspecialchars(substr($span, 0, -1));
+ };
+
+ $sch_rtrim1 = function ($span, $lang, $ch) use ($sch_syntax_helper) {
+ return $sch_syntax_helper(substr($span, 0, -1));
+ };
+
+ $rtrim2 = function ($span, $lang, $ch) {
+ return substr($span, 0, -2);
+ };
+
+ $syn_proc = function ($span, $lang, $ch) use ($syntax_highlight_helper) {
+ return $syntax_highlight_helper($span, $lang);
+ };
+
+ $dash_putback = function ($span, $lang, $ch) use ($syntax_highlight_helper) {
+ return $syntax_highlight_helper('-' . $span, $lang);
+ };
+
+ $slash_putback = function ($span, $lang, $ch) use ($syntax_highlight_helper) {
+ return $syntax_highlight_helper('/' . $span, $lang);
+ };
+
+ $slash_putback_rtrim1 = function ($span, $lang, $ch) use ($rtrim1) {
+ return $rtrim1('/' . $span, $lang, $ch);
+ };
+
+ $lparen_putback = function ($span, $lang, $ch) use ($syntax_highlight_helper) {
+ return $syntax_highlight_helper('(' . $span, $lang);
+ };
+
+ $lparen_putback_rtrim1 = function ($span, $lang, $ch) use ($rtrim1) {
+ return $rtrim1('(' . $span, $lang, $ch);
+ };
+
+ $prepend_xml_opentag = function ($span, $lang, $ch) {
+ return '<span class="xml_tag">&lt;' . $span;
+ };
+
+ $proc_void = function ($span, $lang, $ch) {
+ return $span;
+ };
+
+ $this->sch[self::SCH_NORMAL][0] = self::SCH_NORMAL;
+ $this->sch[self::SCH_NORMAL]['"'] = self::SCH_STRLIT;
+ $this->sch[self::SCH_NORMAL]["#"] = self::SCH_CHRLIT;
+ $this->sch[self::SCH_NORMAL]["0"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["1"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["2"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["3"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["4"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["5"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["6"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["7"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["8"] = self::SCH_NUMLIT;
+ $this->sch[self::SCH_NORMAL]["9"] = self::SCH_NUMLIT;
+
+ $this->sch[self::SCH_STRLIT]['"'] = self::SCH_NORMAL;
+ $this->sch[self::SCH_STRLIT]["\n"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_STRLIT]["\\"] = self::SCH_STRESC;
+ $this->sch[self::SCH_STRLIT][0] = self::SCH_STRLIT;
+
+ $this->sch[self::SCH_CHRLIT][" "] = self::SCH_NORMAL;
+ $this->sch[self::SCH_CHRLIT]["\t"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_CHRLIT]["\n"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_CHRLIT]["\r"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_CHRLIT][0] = self::SCH_CHRLIT;
+
+ $this->sch[self::SCH_NUMLIT][" "] = self::SCH_NORMAL;
+ $this->sch[self::SCH_NUMLIT]["\t"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_NUMLIT]["\n"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_NUMLIT]["\r"] = self::SCH_NORMAL;
+ $this->sch[self::SCH_NUMLIT][0] = self::SCH_NUMLIT;
+
+ //
+ // State transitions for C
+ //
+ $this->c89[self::NORMAL_TEXT]["\""] = self::DQ_LITERAL;
+ $this->c89[self::NORMAL_TEXT]["'"] = self::SQ_LITERAL;
+ $this->c89[self::NORMAL_TEXT]["/"] = self::SLASH_BEGIN;
+ $this->c89[self::NORMAL_TEXT][0] = self::NORMAL_TEXT;
+
+ $this->c89[self::DQ_LITERAL]["\""] = self::NORMAL_TEXT;
+ $this->c89[self::DQ_LITERAL]["\n"] = self::NORMAL_TEXT;
+ $this->c89[self::DQ_LITERAL]["\\"] = self::DQ_ESCAPE;
+ $this->c89[self::DQ_LITERAL][0] = self::DQ_LITERAL;
+
+ $this->c89[self::DQ_ESCAPE][0] = self::DQ_LITERAL;
+
+ $this->c89[self::SQ_LITERAL]["'"] = self::NORMAL_TEXT;
+ $this->c89[self::SQ_LITERAL]["\n"] = self::NORMAL_TEXT;
+ $this->c89[self::SQ_LITERAL]["\\"] = self::SQ_ESCAPE;
+ $this->c89[self::SQ_LITERAL][0] = self::SQ_LITERAL;
+
+ $this->c89[self::SQ_ESCAPE][0] = self::SQ_LITERAL;
+
+ $this->c89[self::SLASH_BEGIN]["*"] = self::STAR_COMMENT;
+ $this->c89[self::SLASH_BEGIN][0] = self::NORMAL_TEXT;
+
+ $this->c89[self::STAR_COMMENT]["*"] = self::STAR_END;
+ $this->c89[self::STAR_COMMENT][0] = self::STAR_COMMENT;
+
+ $this->c89[self::STAR_END]["/"] = self::NORMAL_TEXT;
+ $this->c89[self::STAR_END]["*"] = self::STAR_END;
+ $this->c89[self::STAR_END][0] = self::STAR_COMMENT;
+
+ //
+ // State transitions for C++
+ // Inherit transitions from C, and add line comment support
+ //
+ $this->cpp = $this->c89;
+ $this->cpp[self::SLASH_BEGIN]["/"] = self::LINE_COMMENT;
+ $this->cpp[self::LINE_COMMENT]["\n"] = self::NORMAL_TEXT;
+ $this->cpp[self::LINE_COMMENT]["\\"] = self::LC_ESCAPE;
+ $this->cpp[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+
+ $this->cpp[self::LC_ESCAPE]["\r"] = self::LC_ESCAPE;
+ $this->cpp[self::LC_ESCAPE][0] = self::LINE_COMMENT;
+
+ //
+ // State transitions for C99.
+ // C99 supports line comments like C++
+ //
+ $this->c99 = $this->cpp;
+
+ // State transitions for PL/I
+ // Kinda like C
+ $this->pli = $this->c89;
+
+ //
+ // State transitions for PHP
+ // Inherit transitions from C++, and add perl-style line comment support
+ $this->php = $this->cpp;
+ $this->php[self::NORMAL_TEXT]["#"] = self::LINE_COMMENT;
+ $this->php[self::SQ_LITERAL]["\n"] = self::SQ_LITERAL;
+ $this->php[self::DQ_LITERAL]["\n"] = self::DQ_LITERAL;
+
+ //
+ // State transitions for Perl
+ $this->perl[self::NORMAL_TEXT]["#"] = self::LINE_COMMENT;
+ $this->perl[self::NORMAL_TEXT]["\""] = self::DQ_LITERAL;
+ $this->perl[self::NORMAL_TEXT]["'"] = self::SQ_LITERAL;
+ $this->perl[self::NORMAL_TEXT][0] = self::NORMAL_TEXT;
+
+ $this->perl[self::DQ_LITERAL]["\""] = self::NORMAL_TEXT;
+ $this->perl[self::DQ_LITERAL]["\\"] = self::DQ_ESCAPE;
+ $this->perl[self::DQ_LITERAL][0] = self::DQ_LITERAL;
+
+ $this->perl[self::DQ_ESCAPE][0] = self::DQ_LITERAL;
+
+ $this->perl[self::SQ_LITERAL]["'"] = self::NORMAL_TEXT;
+ $this->perl[self::SQ_LITERAL]["\\"] = self::SQ_ESCAPE;
+ $this->perl[self::SQ_LITERAL][0] = self::SQ_LITERAL;
+
+ $this->perl[self::SQ_ESCAPE][0] = self::SQ_LITERAL;
+
+ $this->perl[self::LINE_COMMENT]["\n"] = self::NORMAL_TEXT;
+ $this->perl[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+
+ $this->mirc[self::NORMAL_TEXT]["\""] = self::DQ_LITERAL;
+ $this->mirc[self::NORMAL_TEXT][";"] = self::LINE_COMMENT;
+ $this->mirc[self::NORMAL_TEXT][0] = self::NORMAL_TEXT;
+
+ $this->mirc[self::DQ_LITERAL]["\""] = self::NORMAL_TEXT;
+ $this->mirc[self::DQ_LITERAL]["\\"] = self::DQ_ESCAPE;
+ $this->mirc[self::DQ_LITERAL][0] = self::DQ_LITERAL;
+
+ $this->mirc[self::DQ_ESCAPE][0] = self::DQ_LITERAL;
+
+ $this->mirc[self::LINE_COMMENT]["\n"] = self::NORMAL_TEXT;
+ $this->mirc[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+
+ $this->ruby = $this->perl;
+
+ $this->python = $this->perl;
+
+ $this->java = $this->cpp;
+
+ $this->vb = $this->perl;
+ $this->vb[self::NORMAL_TEXT]["#"] = self::NORMAL_TEXT;
+ $this->vb[self::NORMAL_TEXT]["'"] = self::LINE_COMMENT;
+
+ $this->cs = $this->java;
+
+ $this->pascal = $this->c89;
+ $this->pascal[self::NORMAL_TEXT]["("] = self::PAREN_BEGIN;
+ $this->pascal[self::NORMAL_TEXT]["/"] = self::SLASH_BEGIN;
+ $this->pascal[self::NORMAL_TEXT]["{"] = self::BLOCK_COMMENT;
+
+ $this->pascal[self::PAREN_BEGIN]["*"] = self::STAR_COMMENT;
+ $this->pascal[self::PAREN_BEGIN]["'"] = self::SQ_LITERAL;
+ $this->pascal[self::PAREN_BEGIN]['"'] = self::DQ_LITERAL;
+ $this->pascal[self::PAREN_BEGIN][0] = self::NORMAL_TEXT;
+
+ $this->pascal[self::SLASH_BEGIN]["'"] = self::SQ_LITERAL;
+ $this->pascal[self::SLASH_BEGIN]['"'] = self::DQ_LITERAL;
+ $this->pascal[self::SLASH_BEGIN]['/'] = self::LINE_COMMENT;
+ $this->pascal[self::SLASH_BEGIN][0] = self::NORMAL_TEXT;
+
+ $this->pascal[self::STAR_COMMENT]["*"] = self::STAR_END;
+ $this->pascal[self::STAR_COMMENT][0] = self::STAR_COMMENT;
+
+ $this->pascal[self::BLOCK_COMMENT]["}"] = self::NORMAL_TEXT;
+ $this->pascal[self::BLOCK_COMMENT][0] = self::BLOCK_COMMENT;
+
+ $this->pascal[self::LINE_COMMENT]["\n"] = self::NORMAL_TEXT;
+ $this->pascal[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+
+ $this->pascal[self::STAR_END][")"] = self::NORMAL_TEXT;
+ $this->pascal[self::STAR_END]["*"] = self::STAR_END;
+ $this->pascal[self::STAR_END][0] = self::STAR_COMMENT;
+
+ $this->sql[self::NORMAL_TEXT]['"'] = self::DQ_LITERAL;
+ $this->sql[self::NORMAL_TEXT]["'"] = self::SQ_LITERAL;
+ $this->sql[self::NORMAL_TEXT]['`'] = self::BT_LITERAL;
+ $this->sql[self::NORMAL_TEXT]['-'] = self::DASH_BEGIN;
+ $this->sql[self::NORMAL_TEXT][0] = self::NORMAL_TEXT;
+
+ $this->sql[self::DQ_LITERAL]['"'] = self::NORMAL_TEXT;
+ $this->sql[self::DQ_LITERAL]['\\'] = self::DQ_ESCAPE;
+ $this->sql[self::DQ_LITERAL][0] = self::DQ_LITERAL;
+
+ $this->sql[self::SQ_LITERAL]["'"] = self::NORMAL_TEXT;
+ $this->sql[self::SQ_LITERAL]['\\'] = self::SQ_ESCAPE;
+ $this->sql[self::SQ_LITERAL][0] = self::SQ_LITERAL;
+
+ $this->sql[self::BT_LITERAL]['`'] = self::NORMAL_TEXT;
+ $this->sql[self::BT_LITERAL]['\\'] = self::BT_ESCAPE;
+ $this->sql[self::BT_LITERAL][0] = self::BT_LITERAL;
+
+ $this->sql[self::DQ_ESCAPE][0] = self::DQ_LITERAL;
+ $this->sql[self::SQ_ESCAPE][0] = self::SQ_LITERAL;
+ $this->sql[self::BT_ESCAPE][0] = self::BT_LITERAL;
+
+ $this->sql[self::DASH_BEGIN]["-"] = self::LINE_COMMENT;
+ $this->sql[self::DASH_BEGIN][0] = self::NORMAL_TEXT;
+
+ $this->sql[self::LINE_COMMENT]["\n"] = self::NORMAL_TEXT;
+ $this->sql[self::LINE_COMMENT]["\\"] = self::LC_ESCAPE;
+ $this->sql[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+
+ $this->sql[self::LC_ESCAPE]["\r"] = self::LC_ESCAPE;
+ $this->sql[self::LC_ESCAPE][0] = self::LINE_COMMENT;
+
+ $this->xml[self::NORMAL_TEXT]["<"] = self::XML_TAG_BEGIN;
+ $this->xml[self::NORMAL_TEXT]["&"] = self::HTML_ENTITY;
+ $this->xml[self::NORMAL_TEXT][0] = self::NORMAL_TEXT;
+ $this->xml[self::HTML_ENTITY][";"] = self::NORMAL_TEXT;
+ $this->xml[self::HTML_ENTITY]["<"] = self::XML_TAG_BEGIN;
+ $this->xml[self::HTML_ENTITY][0] = self::HTML_ENTITY;
+ $this->xml[self::XML_TAG_BEGIN]["?"] = self::XML_PI;
+ $this->xml[self::XML_TAG_BEGIN]["!"] = self::LINE_COMMENT;
+ $this->xml[self::XML_TAG_BEGIN][0] = self::XML_TAG;
+ $this->xml[self::XML_TAG][">"] = self::NORMAL_TEXT;
+ $this->xml[self::XML_TAG]["\""] = self::DQ_LITERAL;
+ $this->xml[self::XML_TAG]["'"] = self::SQ_LITERAL;
+ $this->xml[self::XML_TAG][0] = self::XML_TAG;
+ $this->xml[self::XML_PI][">"] = self::NORMAL_TEXT;
+ $this->xml[self::XML_PI][0] = self::XML_TAG;
+ $this->xml[self::LINE_COMMENT][">"] = self::NORMAL_TEXT;
+ $this->xml[self::LINE_COMMENT][0] = self::LINE_COMMENT;
+ $this->xml[self::DQ_LITERAL]["\""] = self::XML_TAG;
+ $this->xml[self::DQ_LITERAL]["&"] = self::DQ_ESCAPE;
+ $this->xml[self::DQ_LITERAL][0] = self::DQ_LITERAL;
+ $this->xml[self::SQ_LITERAL]["'"] = self::XML_TAG;
+ $this->xml[self::SQ_LITERAL]["&"] = self::SQ_ESCAPE;
+ $this->xml[self::SQ_LITERAL][0] = self::SQ_LITERAL;
+ $this->xml[self::DQ_ESCAPE][";"] = self::DQ_LITERAL;
+ $this->xml[self::DQ_ESCAPE][0] = self::DQ_ESCAPE;
+
+ //
+ // Main state transition table
+ //
+ $this->states = [
+ "C89" => $this->c89,
+ "C" => $this->c99,
+ "C++" => $this->cpp,
+ "PHP" => $this->php,
+ "Perl" => $this->perl,
+ "Java" => $this->java,
+ "VB" => $this->vb,
+ "C#" => $this->cs,
+ "Ruby" => $this->ruby,
+ "Python" => $this->python,
+ "Pascal" => $this->pascal,
+ "mIRC" => $this->mirc,
+ "PL/I" => $this->pli,
+ "SQL" => $this->sql,
+ "XML" => $this->xml,
+ "Scheme" => $this->sch,
+ ];
+
+ //
+ // Process functions
+ //
+ $this->process["C89"][self::NORMAL_TEXT][self::SQ_LITERAL] = $rtrim1;
+ $this->process["C89"][self::NORMAL_TEXT][self::DQ_LITERAL] = $rtrim1;
+ $this->process["C89"][self::NORMAL_TEXT][self::SLASH_BEGIN] = $rtrim1;
+ $this->process["C89"][self::NORMAL_TEXT][0] = $syn_proc;
+
+ $this->process["C89"][self::SLASH_BEGIN][self::STAR_COMMENT] = $rtrim1;
+ $this->process["C89"][self::SLASH_BEGIN][0] = $slash_putback;
+
+ $this->process["Scheme"][self::SCH_NORMAL][self::SCH_STRLIT] = $$this;
+ $this->process["Scheme"][self::SCH_NORMAL][self::SCH_CHRLIT] = $$this;
+ $this->process["Scheme"][self::SCH_NORMAL][self::SCH_NUMLIT] = $$this;
+
+ $this->process["SQL"][self::NORMAL_TEXT][self::SQ_LITERAL] = $rtrim1;
+ $this->process["SQL"][self::NORMAL_TEXT][self::DQ_LITERAL] = $rtrim1;
+ $this->process["SQL"][self::NORMAL_TEXT][self::BT_LITERAL] = $rtrim1;
+ $this->process["SQL"][self::NORMAL_TEXT][self::DASH_BEGIN] = $rtrim1;
+ $this->process["SQL"][self::NORMAL_TEXT][0] = $syn_proc;
+
+ $this->process["SQL"][self::DASH_BEGIN][self::LINE_COMMENT] = $rtrim1;
+ $this->process["SQL"][self::DASH_BEGIN][0] = $dash_putback;
+
+ $this->process["PL/I"] = $this->process["C89"];
+
+ $this->process["C++"] = $this->process["C89"];
+ $this->process["C++"][self::SLASH_BEGIN][self::LINE_COMMENT] = $rtrim1;
+
+ $this->process["C"] = $this->process["C++"];
+
+ $this->process["PHP"] = $this->process["C++"];
+ $this->process["PHP"][self::NORMAL_TEXT][self::LINE_COMMENT] = $rtrim1;
+
+ $this->process["Perl"][self::NORMAL_TEXT][self::SQ_LITERAL] = $rtrim1;
+ $this->process["Perl"][self::NORMAL_TEXT][self::DQ_LITERAL] = $rtrim1;
+ $this->process["Perl"][self::NORMAL_TEXT][self::LINE_COMMENT] = $rtrim1;
+ $this->process["Perl"][self::NORMAL_TEXT][0] = $syn_proc;
+
+ $this->process["Ruby"] = $this->process["Perl"];
+ $this->process["Python"] = $this->process["Perl"];
+
+ $this->process["mIRC"][self::NORMAL_TEXT][self::DQ_LITERAL] = $rtrim1;
+ $this->process["mIRC"][self::NORMAL_TEXT][self::LINE_COMMENT] = $rtrim1;
+ $this->process["mIRC"][self::NORMAL_TEXT][0] = $syn_proc;
+
+ $this->process["VB"] = $this->process["Perl"];
+
+ $this->process["Java"] = $this->process["C++"];
+
+ $this->process["C#"] = $this->process["Java"];
+
+ $this->process["Pascal"] = $this->process["C++"];
+ $this->process["Pascal"][self::NORMAL_TEXT][self::LINE_COMMENT] = $rtrim1;
+ $this->process["Pascal"][self::NORMAL_TEXT][self::BLOCK_COMMENT] = $rtrim1;
+ $this->process["Pascal"][self::NORMAL_TEXT][self::PAREN_BEGIN] = $rtrim1;
+ $this->process["Pascal"][self::SLASH_BEGIN][self::SQ_LITERAL] = $slash_putback_rtrim1;
+ $this->process["Pascal"][self::SLASH_BEGIN][self::DQ_LITERAL] = $slash_putback_rtrim1;
+ $this->process["Pascal"][self::SLASH_BEGIN][0] = $slash_putback;
+ $this->process["Pascal"][self::PAREN_BEGIN][self::SQ_LITERAL] = $lparen_putback_rtrim1;
+ $this->process["Pascal"][self::PAREN_BEGIN][self::DQ_LITERAL] = $lparen_putback_rtrim1;
+ $this->process["Pascal"][self::PAREN_BEGIN][self::STAR_COMMENT] = $rtrim1;
+ $this->process["Pascal"][self::PAREN_BEGIN][0] = $lparen_putback;
+
+ $this->process["XML"][self::NORMAL_TEXT][self::XML_TAG_BEGIN] = $rtrim1;
+ $this->process["XML"][self::NORMAL_TEXT][self::HTML_ENTITY] = $rtrim1;
+ $this->process["XML"][self::HTML_ENTITY][self::XML_TAG_BEGIN] = $rtrim1;
+ $this->process["XML"][self::HTML_ENTITY][0] = $proc_void;
+ $this->process["XML"][self::XML_TAG_BEGIN][self::XML_TAG] = $prepend_xml_opentag;
+ $this->process["XML"][self::XML_TAG_BEGIN][self::XML_PI] = $rtrim1;
+ $this->process["XML"][self::XML_TAG_BEGIN][self::LINE_COMMENT] = $rtrim1;
+ $this->process["XML"][self::LINE_COMMENT][self::NORMAL_TEXT] = $rtrim1_htmlesc;
+ $this->process["XML"][self::XML_TAG][self::NORMAL_TEXT] = $rtrim1;
+ $this->process["XML"][self::XML_TAG][self::DQ_LITERAL] = $rtrim1;
+ $this->process["XML"][self::DQ_LITERAL][self::XML_TAG] = $rtrim1;
+ $this->process["XML"][self::DQ_LITERAL][self::DQ_ESCAPE] = $rtrim1;
+
+ $this->process_end["C89"] = $syntax_highlight_helper;
+ $this->process_end["C++"] = $this->process_end["C89"];
+ $this->process_end["C"] = $this->process_end["C89"];
+ $this->process_end["PHP"] = $this->process_end["C89"];
+ $this->process_end["Perl"] = $this->process_end["C89"];
+ $this->process_end["Java"] = $this->process_end["C89"];
+ $this->process_end["VB"] = $this->process_end["C89"];
+ $this->process_end["C#"] = $this->process_end["C89"];
+ $this->process_end["Ruby"] = $this->process_end["C89"];
+ $this->process_end["Python"] = $this->process_end["C89"];
+ $this->process_end["Pascal"] = $this->process_end["C89"];
+ $this->process_end["mIRC"] = $this->process_end["C89"];
+ $this->process_end["PL/I"] = $this->process_end["C89"];
+ $this->process_end["SQL"] = $this->process_end["C89"];
+ $this->process_end["Scheme"] = $sch_syntax_helper;
+
+ $this->edges["C89"][self::NORMAL_TEXT . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["C89"][self::NORMAL_TEXT . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["C89"][self::SLASH_BEGIN . "," . self::STAR_COMMENT] = '<span class="comment">/*';
+ $this->edges["C89"][self::DQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["C89"][self::SQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["C89"][self::STAR_END . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["Scheme"][self::SCH_NORMAL . "," . self::SCH_STRLIT] = '<span class="sch_str">"';
+ $this->edges["Scheme"][self::SCH_NORMAL . "," . self::SCH_NUMLIT] = '<span class="sch_num">';
+ $this->edges["Scheme"][self::SCH_NORMAL . "," . self::SCH_CHRLIT] = '<span class="sch_chr">#';
+ $this->edges["Scheme"][self::SCH_STRLIT . "," . self::SCH_NORMAL] = '</span>';
+ $this->edges["Scheme"][self::SCH_NUMLIT . "," . self::SCH_NORMAL] = '</span>';
+ $this->edges["Scheme"][self::SCH_CHRLIT . "," . self::SCH_NORMAL] = '</span>';
+
+ $this->edges["SQL"][self::NORMAL_TEXT . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["SQL"][self::NORMAL_TEXT . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["SQL"][self::DASH_BEGIN . "," . self::LINE_COMMENT] = '<span class="comment">--';
+ $this->edges["SQL"][self::NORMAL_TEXT . "," . self::BT_LITERAL] = '`';
+ $this->edges["SQL"][self::DQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["SQL"][self::SQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["SQL"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["PL/I"] = $this->edges["C89"];
+
+ $this->edges["C++"] = $this->edges["C89"];
+ $this->edges["C++"][self::SLASH_BEGIN . "," . self::LINE_COMMENT] = '<span class="comment">//';
+ $this->edges["C++"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["C"] = $this->edges["C++"];
+
+ $this->edges["PHP"] = $this->edges["C++"];
+ $this->edges["PHP"][self::NORMAL_TEXT . "," . self::LINE_COMMENT] = '<span class="comment">#';
+
+ $this->edges["Perl"][self::NORMAL_TEXT . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["Perl"][self::NORMAL_TEXT . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["Perl"][self::DQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["Perl"][self::SQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["Perl"][self::NORMAL_TEXT . "," . self::LINE_COMMENT] = '<span class="comment">#';
+ $this->edges["Perl"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["Ruby"] = $this->edges["Perl"];
+
+ $this->edges["Python"] = $this->edges["Perl"];
+
+ $this->edges["mIRC"][self::NORMAL_TEXT . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["mIRC"][self::NORMAL_TEXT . "," . self::LINE_COMMENT] = '<span class="comment">;';
+ $this->edges["mIRC"][self::DQ_LITERAL . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["mIRC"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["VB"] = $this->edges["Perl"];
+ $this->edges["VB"][self::NORMAL_TEXT . "," . self::LINE_COMMENT] = '<span class="comment">\'';
+
+ $this->edges["Java"] = $this->edges["C++"];
+
+ $this->edges["C#"] = $this->edges["Java"];
+
+ $this->edges["Pascal"] = $this->edges["C89"];
+ $this->edges["Pascal"][self::PAREN_BEGIN . "," . self::STAR_COMMENT] = '<span class="comment">(*';
+ $this->edges["Pascal"][self::PAREN_BEGIN . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["Pascal"][self::PAREN_BEGIN . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["Pascal"][self::SLASH_BEGIN . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["Pascal"][self::SLASH_BEGIN . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["Pascal"][self::SLASH_BEGIN . "," . self::LINE_COMMENT] = '<span class="comment">//';
+ $this->edges["Pascal"][self::NORMAL_TEXT . "," . self::BLOCK_COMMENT] = '<span class="comment">{';
+ $this->edges["Pascal"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["Pascal"][self::BLOCK_COMMENT . "," . self::NORMAL_TEXT] = '</span>';
+
+ $this->edges["XML"][self::NORMAL_TEXT . "," . self::HTML_ENTITY] = '<span class="html_entity">&amp;';
+ $this->edges["XML"][self::HTML_ENTITY . "," . self::NORMAL_TEXT] = '</span>';
+ $this->edges["XML"][self::HTML_ENTITY . "," . self::XML_TAG_BEGIN] = '</span>';
+ $this->edges["XML"][self::XML_TAG . "," . self::NORMAL_TEXT] = '&gt;</span>';
+ $this->edges["XML"][self::XML_TAG_BEGIN . "," . self::XML_PI] = '<span class="xml_pi">&lt;?';
+ $this->edges["XML"][self::XML_TAG_BEGIN . "," . self::LINE_COMMENT] = '<span class="comment">&lt;!';
+ $this->edges["XML"][self::LINE_COMMENT . "," . self::NORMAL_TEXT] = '&gt;</span>';
+ $this->edges["XML"][self::XML_TAG . "," . self::DQ_LITERAL] = '<span class="literal">"';
+ $this->edges["XML"][self::DQ_LITERAL . "," . self::XML_TAG] = '"</span>';
+ $this->edges["XML"][self::DQ_LITERAL . "," . self::DQ_ESCAPE] = '<span class="html_entity">&amp;';
+ $this->edges["XML"][self::DQ_ESCAPE . "," . self::DQ_LITERAL] = '</span>';
+ $this->edges["XML"][self::XML_TAG . "," . self::SQ_LITERAL] = '<span class="literal">\'';
+ $this->edges["XML"][self::SQ_LITERAL . "," . self::XML_TAG] = '\'</span>';
+ $this->edges["XML"][self::SQ_LITERAL . "," . self::SQ_ESCAPE] = '<span class="html_entity">&amp;';
+ $this->edges["XML"][self::SQ_ESCAPE . "," . self::SQ_LITERAL] = '</span>';
+
+ }
+
+ /**
+ * Syntax highlight function
+ * Does the bulk of the syntax highlighting by lexing the input
+ * string, then calling the helper function to highlight keywords.
+ */
+ function syntax_highlight($text, $language) {
+ if ($language == "Plain Text") {
+ return $text;
+ }
+
+ //
+ // The State Machine
+ //
+ if (array_key_exists($language, $this->initial_state)) {
+ $state = $this->initial_state[$language];
+ } else {
+ $state = self::NORMAL_TEXT;
+ }
+
+ $output = "";
+ $span = "";
+ while (strlen($text) > 0) {
+ $ch = substr($text, 0, 1);
+ $text = substr($text, 1);
+
+ $oldstate = $state;
+ $state = (array_key_exists($ch, $this->states[$language][$state])) ?
+ $this->states[$language][$state][$ch] :
+ $this->states[$language][$state][0];
+
+ $span .= $ch;
+
+ if ($oldstate != $state) {
+ if (array_key_exists($language, $this->process) &&
+ array_key_exists($oldstate, $this->process[$language])) {
+ if (array_key_exists($state, $this->process[$language][$oldstate])) {
+ $pf = $this->process[$language][$oldstate][$state];
+ $output .= $pf($span, $language, $ch);
+ } else {
+ $pf = $this->process[$language][$oldstate][0];
+ $output .= $pf($span, $language, $ch);
+ }
+ } else {
+ $output .= $span;
+ }
+
+ if (array_key_exists($language, $this->edges) &&
+ array_key_exists("$oldstate,$state", $this->edges[$language])) {
+ $output .= $this->edges[$language]["$oldstate,$state"];
+ }
+
+ $span = "";
+ }
+ }
+
+ if (array_key_exists($language, $this->process_end) && $state == self::NORMAL_TEXT) {
+ $output .= $this->process_end[$language]($span, $language);
+ } else {
+ $output .= $span;
+ }
+
+ if ($state != self::NORMAL_TEXT) {
+ if (array_key_exists($language, $this->edges) &&
+ array_key_exists("$state," . self::NORMAL_TEXT, $this->edges[$language])) {
+ $output .= $this->edges[$language]["$state," . self::NORMAL_TEXT];
+ }
+
+ }
+
+ return $output;
+ }
+}
diff --git a/src/classes/Misc.php b/src/classes/Misc.php
index 50f06d26..17d1cc49 100644
--- a/src/classes/Misc.php
+++ b/src/classes/Misc.php
@@ -2,6 +2,7 @@
namespace PHPPgAdmin;
+use \PHPPgAdmin\Controller\LoginController;
use \PHPPgAdmin\Decorators\Decorator;
/**
@@ -16,6 +17,7 @@ class Misc {
private $_no_db_connection = false;
private $_reload_drop_database = false;
private $_reload_browser = false;
+ private $_no_bottom_link = false;
private $app = null;
private $data = null;
private $database = null;
@@ -26,6 +28,7 @@ class Misc {
public $form = '';
public $href = '';
public $lang = [];
+ private $server_info = null;
private $_no_output = false;
/* Constructor */
@@ -38,10 +41,18 @@ class Misc {
$this->conf = $container->get('conf');
$this->view = $container->get('view');
$this->plugin_manager = $container->get('plugin_manager');
- $this->appName = $container->get('settings')['appName'];
- $this->appVersion = $container->get('settings')['appVersion'];
$this->appLangFiles = $container->get('appLangFiles');
+ $this->appName = $container->get('settings')['appName'];
+ $this->appVersion = $container->get('settings')['appVersion'];
+ $this->postgresqlMinVer = $container->get('settings')['postgresqlMinVer'];
+ $this->phpMinVer = $container->get('settings')['phpMinVer'];
+
+ // Check the version of PHP
+ if (version_compare(phpversion(), $this->phpMinVer, '<')) {
+ exit(sprintf('Version of PHP not supported. Please upgrade to version %s or later.', $this->phpMinVer));
+ }
+
if (count($this->conf['servers']) === 1) {
$info = $this->conf['servers'][0];
$this->server_id = $info['host'] . ':' . $info['port'] . ':' . $info['sslmode'];
@@ -50,35 +61,53 @@ class Misc {
} else if (isset($_SESSION['webdbLogin']) && count($_SESSION['webdbLogin']) > 0) {
$this->server_id = array_keys($_SESSION['webdbLogin'])[0];
}
+
+ $_server_info = $this->getServerInfo();
+ /* starting with PostgreSQL 9.0, we can set the application name */
+ if (isset($_server_info['pgVersion']) && $_server_info['pgVersion'] >= 9) {
+ putenv("PGAPPNAME=" . $this->appName . '_' . $this->appVersion);
+ }
+
//\PC::debug($this->conf, 'conf');
//\PC::debug($this->server_id, 'server_id');
}
function getConnection($database = '', $server_id = null) {
+ $lang = $this->lang;
- if ($server_id !== null) {
- $this->server_id = $server_id;
- }
- $server_info = $this->getServerInfo($this->server_id);
-
- $database_to_use = $this->getDatabase($database);
- // Perform extra security checks if this config option is set
- if ($this->conf['extra_login_security']) {
- // Disallowed logins if extra_login_security is enabled.
- // These must be lowercase.
- $bad_usernames = ['pgsql', 'postgres', 'root', 'administrator'];
+ if ($this->_connection === null) {
+ if ($server_id !== null) {
+ $this->server_id = $server_id;
+ }
+ $server_info = $this->getServerInfo($this->server_id);
+
+ $database_to_use = $this->getDatabase($database);
+ // Perform extra security checks if this config option is set
+ if ($this->conf['extra_login_security']) {
+ // Disallowed logins if extra_login_security is enabled.
+ // These must be lowercase.
+ $bad_usernames = [
+ 'pgsql' => 'pgsql',
+ 'postgres' => 'postgres',
+ 'root' => 'root',
+ 'administrator' => 'administrator',
+ ];
- $username = strtolower($server_info['username']);
+ if (isset($server_info['username']) && array_key_exists(strtolower($server_info['username']), $bad_usernames)) {
+ unset($_SESSION['webdbLogin'][$this->server_id]);
+ $msg = $lang['strlogindisallowed'];
+ $login_controller = new LoginController($this->app->getContainer());
+ return $login_controller->render();
+ }
- if ($server_info['password'] == '' || in_array($username, $bad_usernames)) {
- unset($_SESSION['webdbLogin'][$this->server_id]);
- $msg = $lang['strlogindisallowed'];
- include '../views/login.php';
- exit;
+ if (!isset($server_info['password']) || $server_info['password'] == '') {
+ unset($_SESSION['webdbLogin'][$this->server_id]);
+ $msg = $lang['strlogindisallowed'];
+ $login_controller = new LoginController($this->app->getContainer());
+ return $login_controller->render();
+ }
}
- }
- if ($this->_connection === null) {
// Create the connection object and make the connection
$this->_connection = new \PHPPgAdmin\Database\Connection(
$server_info['host'],
@@ -93,6 +122,15 @@ class Misc {
}
/**
+ * sets $_no_bottom_link boolean value
+ * @param boolean $flag [description]
+ */
+ function setNoBottomLink($flag) {
+ $this->_no_bottom_link = boolval($flag);
+ return $this;
+ }
+
+ /**
* sets $_no_db_connection boolean value, allows to render scripts that do not need an active session
* @param boolean $flag [description]
*/
@@ -102,9 +140,7 @@ class Misc {
}
function setNoOutput($flag) {
- global $_no_output;
$this->_no_output = boolval($flag);
- $_no_output = $this->_no_output;
return $this;
}
@@ -141,18 +177,14 @@ class Misc {
* @param boolean $flag sets internal $_reload_browser var which will be passed to the footer methods
*/
function setReloadBrowser($flag) {
- global $_reload_browser;
- $_reload_browser = $flag;
$this->_reload_browser = boolval($flag);
return $this;
}
/**
* [setReloadBrowser description]
- * @param boolean $flag sets internal $_reload_browser var which will be passed to the footer methods
+ * @param boolean $flag sets internal $_reload_drop_database var which will be passed to the footer methods
*/
function setReloadDropDatabase($flag) {
- global $_reload_drop_database;
- $_reload_drop_database = $flag;
$this->_reload_drop_database = boolval($flag);
return $this;
}
@@ -167,6 +199,12 @@ class Misc {
$this->server_id = $server_id;
}
+ $server_info = $this->getServerInfo($this->server_id);
+
+ if ($this->_no_db_connection || !isset($server_info['username'])) {
+ return null;
+ }
+
if ($this->data === null) {
$_connection = $this->getConnection($database, $this->server_id);
@@ -174,7 +212,7 @@ class Misc {
// The description of the server is returned in $platform.
$_type = $_connection->getDriver($platform);
if ($_type === null) {
- printf($lang['strpostgresqlversionnotsupported'], $postgresqlMinVer);
+ printf($lang['strpostgresqlversionnotsupported'], $this->postgresqlMinVer);
exit;
}
$_type = '\PHPPgAdmin\Database\\' . $_type;
@@ -220,6 +258,7 @@ class Misc {
*/
function isDumpEnabled($all = false) {
$info = $this->getServerInfo();
+
return !empty($info[$all ? 'pg_dumpall_path' : 'pg_dump_path']);
}
@@ -257,7 +296,7 @@ class Misc {
}
function getSubjectParams($subject) {
- global $plugin_manager;
+ $plugin_manager = $this->plugin_manager;
$vars = [];
@@ -403,7 +442,7 @@ class Misc {
if (!isset($vars['url'])) {
$vars['url'] = '/redirect';
}
- \PC::debug($vars, 'getSubjectParams');
+ //\PC::debug($vars, 'getSubjectParams');
if ($vars['url'] == '/redirect' && isset($vars['params']['subject'])) {
$vars['url'] = '/redirect/' . $vars['params']['subject'];
unset($vars['params']['subject']);
@@ -417,12 +456,6 @@ class Misc {
return "{$vars['url']}?" . http_build_query($vars['params'], '', '&amp;');
}
- function getForm() {
- if (!$this->form) {
- $this->form = $this->setForm();
- }
- return $this->form;
- }
/**
* Sets the form tracking variable
*/
@@ -715,13 +748,13 @@ class Misc {
}
/**
- * Prints the page header. If global variable $_no_output is
+ * Prints the page header. If member variable $this->_no_output is
* set then no header is drawn.
* @param $title The title of the page
* @param $script script tag
* @param $do_print boolean if false, the function will return the header content
*/
- function printHeader($title = '', $script = null, $do_print = true) {
+ function printHeader($title = '', $script = null, $do_print = true, $template = 'header.twig') {
if (function_exists('newrelic_disable_autorum')) {
newrelic_disable_autorum();
@@ -735,7 +768,7 @@ class Misc {
$viewVars['appName'] = htmlspecialchars($this->appName) . ($title != '') ? htmlspecialchars(" - {$title}") : '';
- $header_html = $this->view->fetch('header.twig', $viewVars);
+ $header_html = $this->view->fetch($template, $viewVars);
if ($script) {
$header_html .= "{$script}\n";
@@ -770,14 +803,15 @@ class Misc {
$lang = $this->lang;
$footer_html = '';
- \PC::debug($this->_reload_browser, '$_reload_browser');
+ //\PC::debug($this->_reload_browser, '$_reload_browser');
if ($this->_reload_browser) {
$footer_html .= $this->printReload(false, false);
} elseif ($this->_reload_drop_database) {
$footer_html .= $this->printReload(true, false);
}
-
- $footer_html .= "<a href=\"#\" class=\"bottom_link\">" . $lang['strgotoppage'] . "</a>";
+ if (!$this->_no_bottom_link) {
+ $footer_html .= "<a href=\"#\" class=\"bottom_link\">" . $lang['strgotoppage'] . "</a>";
+ }
$footer_html .= "</body>\n";
$footer_html .= "</html>\n";
@@ -798,7 +832,8 @@ class Misc {
function printBody($doBody = true, $bodyClass = '') {
$bodyClass = htmlspecialchars($bodyClass);
- $bodyHtml = "<body " . ($bodyClass == '' ? '' : " class=\"{$bodyClass}\"") . ">\n";
+ $bodyHtml = '<body class="detailbody ' . ($bodyClass == '' ? '' : $bodyClass) . '">';
+ $bodyHtml .= "\n";
if (!$this->_no_output && $doBody) {
echo $bodyHtml;
@@ -833,128 +868,6 @@ class Misc {
}
/**
- * Display a link
- * @param $link An associative array of link parameters to print
- * link = array(
- * 'attr' => array( // list of A tag attribute
- * 'attrname' => attribute value
- * ...
- * ),
- * 'content' => The link text
- * 'fields' => (optionnal) the data from which content and attr's values are obtained
- * );
- * the special attribute 'href' might be a string or an array. If href is an array it
- * will be generated by getActionUrl. See getActionUrl comment for array format.
- */
- function printLink($link, $do_print = true) {
-
- if (!isset($link['fields'])) {
- $link['fields'] = $_REQUEST;
- }
-
- $tag = "<a ";
- foreach ($link['attr'] as $attr => $value) {
- if ($attr == 'href' and is_array($value)) {
- $tag .= 'href="' . htmlentities($this->getActionUrl($value, $link['fields'])) . '" ';
- } else {
- $tag .= htmlentities($attr) . '="' . value($value, $link['fields'], 'html') . '" ';
- }
- }
- $tag .= ">" . value($link['content'], $link['fields'], 'html') . "</a>\n";
-
- if ($do_print) {
- echo $tag;
- } else {
- return $tag;
- }
- }
-
- /**
- * Display a list of links
- * @param $links An associative array of links to print. See printLink function for
- * the links array format.
- * @param $class An optional class or list of classes seprated by a space
- * WARNING: This field is NOT escaped! No user should be able to inject something here, use with care.
- * @param boolean $do_print true to echo, false to return
- */
- function printLinksList($links, $class = '', $do_print = true) {
-
- $list_html = "<ul class=\"{$class}\">\n";
- foreach ($links as $link) {
- $list_html .= "\t<li>";
- $list_html .= $this->printLink($link, false);
- $list_html .= "</li>\n";
- }
- $list_html .= "</ul>\n";
- if ($do_print) {
- echo $list_html;
- } else {
- return $list_html;
- }
- }
-
- /**
- * Display navigation tabs
- * @param $tabs The name of current section (Ex: intro, server, ...), or an array with tabs (Ex: sqledit.php doFind function)
- * @param $activetab The name of the tab to be highlighted.
- * @param $print if false, return html
- */
- function printTabs($tabs, $activetab, $do_print = true) {
- global $misc, $data, $lang;
-
- if (is_string($tabs)) {
- $_SESSION['webdbLastTab'][$tabs] = $activetab;
- $tabs = $this->getNavTabs($tabs);
- }
- $tabs_html = '';
- if (count($tabs) > 0) {
-
- $tabs_html .= "<table class=\"tabs\"><tr>\n";
-
- # FIXME: don't count hidden tabs
- $width = (int) (100 / count($tabs)) . '%';
- foreach ($tabs as $tab_id => $tab) {
-
- $tabs[$tab_id]['active'] = $active = ($tab_id == $activetab) ? ' active' : '';
-
- $tabs[$tab_id]['width'] = $width;
-
- if (!isset($tab['hide']) || $tab['hide'] !== true) {
-
- $tabs[$tab_id]['tablink'] = htmlentities($this->getActionUrl($tab, $_REQUEST));
-
- $tablink = '<a href="' . $tabs[$tab_id]['tablink'] . '">';
-
- if (isset($tab['icon']) && $icon = $this->icon($tab['icon'])) {
- $tabs[$tab_id]['iconurl'] = $icon;
- $tablink .= "<span class=\"icon\"><img src=\"{$icon}\" alt=\"{$tab['title']}\" /></span>";
- }
-
- $tablink .= "<span class=\"label\">{$tab['title']}</span></a>";
-
- $tabs_html .= "<td style=\"width: {$width}\" class=\"tab{$active}\">";
-
- if (isset($tab['help'])) {
- $tabs_html .= $this->printHelp($tablink, $tab['help'], false);
- } else {
- $tabs_html .= $tablink;
- }
-
- $tabs_html .= "</td>\n";
- }
- }
- $tabs_html .= "</tr></table>\n";
- }
-
- if ($do_print) {
- echo $tabs_html;
- } else {
- return $tabs_html;
- }
-
- }
-
- /**
* Retrieve the tab info for a specific tab bar.
* @param $section The name of the tab bar.
*/
@@ -972,12 +885,12 @@ class Misc {
$tabs = [
'intro' => [
'title' => $lang['strintroduction'],
- 'url' => "intro",
+ 'url' => "intro.php",
'icon' => 'Introduction',
],
'servers' => [
'title' => $lang['strservers'],
- 'url' => "servers",
+ 'url' => "servers.php",
'icon' => 'Servers',
],
];
@@ -1159,6 +1072,13 @@ class Misc {
'help' => 'pg.view',
'icon' => 'Views',
],
+ 'matviews' => [
+ 'title' => 'M ' . $lang['strviews'],
+ 'url' => 'materialized_views.php',
+ 'urlvars' => ['subject' => 'schema'],
+ 'help' => 'pg.matview',
+ 'icon' => 'MViews',
+ ],
'sequences' => [
'title' => $lang['strsequences'],
'url' => 'sequences.php',
@@ -1412,6 +1332,65 @@ class Misc {
];
break;
+ case 'matview':
+ $tabs = [
+ 'columns' => [
+ 'title' => $lang['strcolumns'],
+ 'url' => 'viewproperties.php',
+ 'urlvars' => ['subject' => 'matview', 'matview' => Decorator::field('matview')],
+ 'icon' => 'Columns',
+ 'branch' => true,
+ ],
+ 'browse' => [
+ 'title' => $lang['strbrowse'],
+ 'icon' => 'Columns',
+ 'url' => 'display.php',
+ 'urlvars' => [
+ 'action' => 'confselectrows',
+ 'return' => 'schema',
+ 'subject' => 'matview',
+ 'matview' => Decorator::field('matview'),
+ ],
+ 'branch' => true,
+ ],
+ 'select' => [
+ 'title' => $lang['strselect'],
+ 'icon' => 'Search',
+ 'url' => 'views.php',
+ 'urlvars' => ['action' => 'confselectrows', 'matview' => Decorator::field('matview')],
+ 'help' => 'pg.sql.select',
+ ],
+ 'definition' => [
+ 'title' => $lang['strdefinition'],
+ 'url' => 'viewproperties.php',
+ 'urlvars' => ['subject' => 'matview', 'matview' => Decorator::field('matview'), 'action' => 'definition'],
+ 'icon' => 'Definition',
+ ],
+ 'rules' => [
+ 'title' => $lang['strrules'],
+ 'url' => 'rules.php',
+ 'urlvars' => ['subject' => 'matview', 'matview' => Decorator::field('matview')],
+ 'help' => 'pg.rule',
+ 'icon' => 'Rules',
+ 'branch' => true,
+ ],
+ 'privileges' => [
+ 'title' => $lang['strprivileges'],
+ 'url' => 'privileges.php',
+ 'urlvars' => ['subject' => 'matview', 'matview' => Decorator::field('matview')],
+ 'help' => 'pg.privilege',
+ 'icon' => 'Privileges',
+ ],
+ 'export' => [
+ 'title' => $lang['strexport'],
+ 'url' => 'viewproperties.php',
+ 'urlvars' => ['subject' => 'matview', 'matview' => Decorator::field('matview'), 'action' => 'export'],
+ 'icon' => 'Export',
+ 'hide' => false,
+ ],
+ ];
+ break;
+
case 'function':
$tabs = [
'definition' => [
@@ -1473,15 +1452,15 @@ class Misc {
$tabs = [
'sql' => [
'title' => $lang['strsql'],
- 'url' => '/sqledit/sql',
- 'urlvars' => ['subject' => 'schema'],
+ 'url' => '/src/views/sqledit.php',
+ 'urlvars' => ['action' => 'sql', 'subject' => 'schema'],
'help' => 'pg.sql',
'icon' => 'SqlEditor',
],
'find' => [
'title' => $lang['strfind'],
- 'url' => '/sqledit/find',
- 'urlvars' => ['subject' => 'schema'],
+ 'url' => '/src/views/sqledit.php',
+ 'urlvars' => ['action' => 'find', 'subject' => 'schema'],
'icon' => 'Search',
],
];
@@ -1574,410 +1553,6 @@ class Misc {
}
/**
- * [printTopbar description]
- * @param bool $do_print true to print, false to return html
- * @return string
- */
- function printTopbar($do_print = true) {
-
- $lang = $this->lang;
- $plugin_manager = $this->plugin_manager;
- $appName = $this->appName;
- $appVersion = $this->appVersion;
- $appLangFiles = $this->appLangFiles;
-
- $server_info = $this->getServerInfo();
- $reqvars = $this->getRequestVars('table');
-
- $topbar_html = "<div class=\"topbar\"><table style=\"width: 100%\"><tr><td>";
-
- if ($server_info && isset($server_info['platform']) && isset($server_info['username'])) {
- /* top left informations when connected */
- $topbar_html .= sprintf($lang['strtopbar'],
- '<span class="platform">' . htmlspecialchars($server_info['platform']) . '</span>',
- '<span class="host">' . htmlspecialchars((empty($server_info['host'])) ? 'localhost' : $server_info['host']) . '</span>',
- '<span class="port">' . htmlspecialchars($server_info['port']) . '</span>',
- '<span class="username">' . htmlspecialchars($server_info['username']) . '</span>');
-
- $topbar_html .= "</td>";
-
- /* top right informations when connected */
-
- $toplinks = [
- 'sql' => [
- 'attr' => [
- 'href' => [
- 'url' => '/sqledit/sql',
- 'urlvars' => $reqvars,
- ],
- 'target' => "sqledit",
- 'id' => 'toplink_sql',
- ],
- 'content' => $lang['strsql'],
- ],
- 'history' => [
- 'attr' => [
- 'href' => [
- 'url' => '/history.php',
- 'urlvars' => array_merge($reqvars, [
- 'action' => 'pophistory',
- ]),
- ],
- 'id' => 'toplink_history',
- ],
- 'content' => $lang['strhistory'],
- ],
- 'find' => [
- 'attr' => [
- 'href' => [
- 'url' => '/sqledit/find',
- 'urlvars' => $reqvars,
- ],
- 'target' => "sqledit",
- 'id' => 'toplink_find',
- ],
- 'content' => $lang['strfind'],
- ],
- 'logout' => [
- 'attr' => [
- 'href' => [
- 'url' => '/src/views/servers/logout',
- 'urlvars' => [
- 'logoutServer' => "{$server_info['host']}:{$server_info['port']}:{$server_info['sslmode']}",
- ],
- ],
- 'id' => 'toplink_logout',
- ],
- 'content' => $lang['strlogout'],
- ],
- ];
-
- // Toplink hook's place
- $plugin_functions_parameters = [
- 'toplinks' => &$toplinks,
- ];
-
- $plugin_manager->do_hook('toplinks', $plugin_functions_parameters);
-
- $topbar_html .= "<td style=\"text-align: right\">";
-
- $topbar_html .= $this->printLinksList($toplinks, 'toplink', [], false);
-
- $topbar_html .= "</td>";
-
- $sql_window_id = htmlentities('sqledit:' . $this->server_id);
- $history_window_id = htmlentities('history:' . $this->server_id);
-
- $topbar_html .= "<script type=\"text/javascript\">
- $('#toplink_sql').click(function() {
- window.open($(this).attr('href'),'{$sql_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
- return false;
- });
-
- $('#toplink_history').click(function() {
- window.open($(this).attr('href'),'{$history_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
- return false;
- });
-
- $('#toplink_find').click(function() {
- window.open($(this).attr('href'),'{$sql_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
- return false;
- });
- ";
-
- if (isset($_SESSION['sharedUsername'])) {
- $topbar_html .= sprintf("
- $('#toplink_logout').click(function() {
- return confirm('%s');
- });", str_replace("'", "\'", $lang['strconfdropcred']));
- }
-
- $topbar_html .= "
- </script>";
- } else {
- $topbar_html .= "<span class=\"appname\">{$appName}</span> <span class=\"version\">{$appVersion}</span>";
- }
- /*
- echo "<td style=\"text-align: right; width: 1%\">";
-
- echo "<form method=\"get\"><select name=\"language\" onchange=\"this.form.submit()\">\n";
- $language = isset($_SESSION['webdbLanguage']) ? $_SESSION['webdbLanguage'] : 'english';
- foreach ($appLangFiles as $k => $v) {
- echo "<option value=\"{$k}\"",
- ($k == $language) ? ' selected="selected"' : '',
- ">{$v}</option>\n";
- }
- echo "</select>\n";
- echo "<noscript><input type=\"submit\" value=\"Set Language\"></noscript>\n";
- foreach ($_GET as $key => $val) {
- if ($key == 'language') continue;
- echo "<input type=\"hidden\" name=\"$key\" value=\"", htmlspecialchars($val), "\" />\n";
- }
- echo "</form>\n";
-
- echo "</td>";
- */
- $topbar_html .= "</tr></table></div>\n";
-
- if ($do_print) {
- echo $topbar_html;
- } else {
- return $topbar_html;
- }
- }
-
- /**
- * Display a bread crumb trail.
- * @param $do_print true to echo, false to return html
- */
- function printTrail($trail = [], $do_print = true) {
- $lang = $this->lang;
-
- $trail_html = $this->printTopbar(false);
-
- if (is_string($trail)) {
- $trail = $this->getTrail($trail);
- }
-
- $trail_html .= "<div class=\"trail\"><table><tr>";
-
- foreach ($trail as $crumb) {
- $trail_html .= "<td class=\"crumb\">";
- $crumblink = "<a";
-
- if (isset($crumb['url'])) {
- $crumblink .= " href=\"{$crumb['url']}\"";
- }
-
- if (isset($crumb['title'])) {
- $crumblink .= " title=\"{$crumb['title']}\"";
- }
-
- $crumblink .= ">";
-
- if (isset($crumb['title'])) {
- $iconalt = $crumb['title'];
- } else {
- $iconalt = 'Database Root';
- }
-
- if (isset($crumb['icon']) && $icon = $this->icon($crumb['icon'])) {
- $crumblink .= "<span class=\"icon\"><img src=\"{$icon}\" alt=\"{$iconalt}\" /></span>";
- }
-
- $crumblink .= "<span class=\"label\">" . htmlspecialchars($crumb['text']) . "</span></a>";
-
- if (isset($crumb['help'])) {
- $trail_html .= $this->printHelp($crumblink, $crumb['help'], false);
- } else {
- $trail_html .= $crumblink;
- }
-
- $trail_html .= "{$lang['strseparator']}";
- $trail_html .= "</td>";
- }
-
- $trail_html .= "</tr></table></div>\n";
- if ($do_print) {
- echo $trail_html;
- } else {
- return $trail_html;
- }
- }
-
- /**
- * Create a bread crumb trail of the object hierarchy.
- * @param $object The type of object at the end of the trail.
- */
- function getTrail($subject = null) {
- global $lang, $data, $appName, $plugin_manager;
-
- $trail = [];
- $vars = '';
- $done = false;
-
- $trail['root'] = [
- 'text' => $appName,
- 'url' => '/redirect/root',
- 'icon' => 'Introduction',
- ];
-
- if ($subject == 'root') {
- $done = true;
- }
-
- if (!$done) {
- $server_info = $this->getServerInfo();
- $trail['server'] = [
- 'title' => $lang['strserver'],
- 'text' => $server_info['desc'],
- 'url' => $this->getHREFSubject('server'),
- 'help' => 'pg.server',
- 'icon' => 'Server',
- ];
- }
- if ($subject == 'server') {
- $done = true;
- }
-
- if (isset($_REQUEST['database']) && !$done) {
- $trail['database'] = [
- 'title' => $lang['strdatabase'],
- 'text' => $_REQUEST['database'],
- 'url' => $this->getHREFSubject('database'),
- 'help' => 'pg.database',
- 'icon' => 'Database',
- ];
- } elseif (isset($_REQUEST['rolename']) && !$done) {
- $trail['role'] = [
- 'title' => $lang['strrole'],
- 'text' => $_REQUEST['rolename'],
- 'url' => $this->getHREFSubject('role'),
- 'help' => 'pg.role',
- 'icon' => 'Roles',
- ];
- }
- if ($subject == 'database' || $subject == 'role') {
- $done = true;
- }
-
- if (isset($_REQUEST['schema']) && !$done) {
- $trail['schema'] = [
- 'title' => $lang['strschema'],
- 'text' => $_REQUEST['schema'],
- 'url' => $this->getHREFSubject('schema'),
- 'help' => 'pg.schema',
- 'icon' => 'Schema',
- ];
- }
- if ($subject == 'schema') {
- $done = true;
- }
-
- if (isset($_REQUEST['table']) && !$done) {
- $trail['table'] = [
- 'title' => $lang['strtable'],
- 'text' => $_REQUEST['table'],
- 'url' => $this->getHREFSubject('table'),
- 'help' => 'pg.table',
- 'icon' => 'Table',
- ];
- } elseif (isset($_REQUEST['view']) && !$done) {
- $trail['view'] = [
- 'title' => $lang['strview'],
- 'text' => $_REQUEST['view'],
- 'url' => $this->getHREFSubject('view'),
- 'help' => 'pg.view',
- 'icon' => 'View',
- ];
- } elseif (isset($_REQUEST['ftscfg']) && !$done) {
- $trail['ftscfg'] = [
- 'title' => $lang['strftsconfig'],
- 'text' => $_REQUEST['ftscfg'],
- 'url' => $this->getHREFSubject('ftscfg'),
- 'help' => 'pg.ftscfg.example',
- 'icon' => 'Fts',
- ];
- }
- if ($subject == 'table' || $subject == 'view' || $subject == 'ftscfg') {
- $done = true;
- }
-
- if (!$done && !is_null($subject)) {
- switch ($subject) {
- case 'function':
- $trail[$subject] = [
- 'title' => $lang['str' . $subject],
- 'text' => $_REQUEST[$subject],
- 'url' => $this->getHREFSubject('function'),
- 'help' => 'pg.function',
- 'icon' => 'Function',
- ];
- break;
- case 'aggregate':
- $trail[$subject] = [
- 'title' => $lang['straggregate'],
- 'text' => $_REQUEST['aggrname'],
- 'url' => $this->getHREFSubject('aggregate'),
- 'help' => 'pg.aggregate',
- 'icon' => 'Aggregate',
- ];
- break;
- case 'column':
- $trail['column'] = [
- 'title' => $lang['strcolumn'],
- 'text' => $_REQUEST['column'],
- 'icon' => 'Column',
- 'url' => $this->getHREFSubject('column'),
- ];
- break;
- default:
- if (isset($_REQUEST[$subject])) {
- switch ($subject) {
- case 'domain':$icon = 'Domain';
- break;
- case 'sequence':$icon = 'Sequence';
- break;
- case 'type':$icon = 'Type';
- break;
- case 'operator':$icon = 'Operator';
- break;
- default:$icon = null;
- break;
- }
- $trail[$subject] = [
- 'title' => $lang['str' . $subject],
- 'text' => $_REQUEST[$subject],
- 'help' => 'pg.' . $subject,
- 'icon' => $icon,
- ];
- }
- }
- }
-
- // Trail hook's place
- $plugin_functions_parameters = [
- 'trail' => &$trail,
- 'section' => $subject,
- ];
-
- $plugin_manager->do_hook('trail', $plugin_functions_parameters);
-
- return $trail;
- }
-
- /**
- * Display the navlinks
- *
- * @param $navlinks - An array with the the attributes and values that will be shown. See printLinksList for array format.
- * @param $place - Place where the $navlinks are displayed. Like 'display-browse', where 'display' is the file (display.php)
- * @param $env - Associative array of defined variables in the scope of the caller.
- * Allows to give some environnement details to plugins.
- * and 'browse' is the place inside that code (doBrowse).
- * @param bool $do_print if true, print html, if false, return html
- */
- function printNavLinks($navlinks, $place, $env = [], $do_print = true) {
- $plugin_manager = $this->plugin_manager;
-
- // Navlinks hook's place
- $plugin_functions_parameters = [
- 'navlinks' => &$navlinks,
- 'place' => $place,
- 'env' => $env,
- ];
- $plugin_manager->do_hook('navlinks', $plugin_functions_parameters);
-
- if (count($navlinks) > 0) {
- if ($do_print) {
- $this->printLinksList($navlinks, 'navlink');
- } else {
- return $this->printLinksList($navlinks, 'navlink', false);
- }
-
- }
- }
-
- /**
* Do multi-page navigation. Displays the prev, next and page options.
* @param $page - the page currently viewed
* @param $pages - the maximum number of pages
@@ -1985,7 +1560,7 @@ class Misc {
* @param $max_width - the number of pages to make available at any one time (default = 20)
*/
function printPages($page, $pages, $gets, $max_width = 20) {
- global $lang;
+ $lang = $this->lang;
$window = 10;
@@ -2129,54 +1704,6 @@ class Misc {
}
}
- /**
- * Returns URL given an action associative array.
- * NOTE: this function does not html-escape, only url-escape
- * @param $action An associative array of the follow properties:
- * 'url' => The first part of the URL (before the ?)
- * 'urlvars' => Associative array of (URL variable => field name)
- * these are appended to the URL
- * @param $fields Field data from which 'urlfield' and 'vars' are obtained.
- */
- function getActionUrl(&$action, &$fields) {
- $url = value($action['url'], $fields);
-
- if ($url === false) {
- return '';
- }
-
- if (!empty($action['urlvars'])) {
- $urlvars = value($action['urlvars'], $fields);
- } else {
- $urlvars = [];
- }
-
- /* set server, database and schema parameter if not presents */
- if (isset($urlvars['subject'])) {
- $subject = value($urlvars['subject'], $fields);
- } else {
- $subject = '';
- }
-
- if (isset($_REQUEST['server']) and !isset($urlvars['server']) and $subject != 'root') {
- $urlvars['server'] = $_REQUEST['server'];
- if (isset($_REQUEST['database']) and !isset($urlvars['database']) and $subject != 'server') {
- $urlvars['database'] = $_REQUEST['database'];
- if (isset($_REQUEST['schema']) and !isset($urlvars['schema']) and $subject != 'database') {
- $urlvars['schema'] = $_REQUEST['schema'];
- }
- }
- }
-
- $sep = '?';
- foreach ($urlvars as $var => $varfield) {
- $url .= $sep . value_url($var, $fields) . '=' . value_url($varfield, $fields);
- $sep = '&';
- }
- //return '/src/views/' . $url;
- return $url;
- }
-
function getRequestVars($subject = '') {
$v = [];
if (!empty($subject)) {
@@ -2195,273 +1722,6 @@ class Misc {
return $v;
}
- function printUrlVars(&$vars, &$fields, $do_print = true) {
- $url_vars_html = '';
- foreach ($vars as $var => $varfield) {
- $url_vars_html .= "{$var}=" . urlencode($fields[$varfield]) . "&amp;";
- }
- if ($do_print) {
- echo $url_vars_html;
- } else {
- return $url_vars_html;
- }
- }
-
- /**
- * Display a table of data.
- * @param $tabledata A set of data to be formatted, as returned by $data->getDatabases() etc.
- * @param $columns An associative array of columns to be displayed:
- * $columns = array(
- * column_id => array(
- * 'title' => Column heading,
- * 'class' => The class to apply on the column cells,
- * 'field' => Field name for $tabledata->fields[...],
- * 'help' => Help page for this column,
- * ), ...
- * );
- * @param $actions Actions that can be performed on each object:
- * $actions = array(
- * * multi action support
- * * parameters are serialized for each entries and given in $_REQUEST['ma']
- * 'multiactions' => array(
- * 'keycols' => Associative array of (URL variable => field name), // fields included in the form
- * 'url' => URL submission,
- * 'default' => Default selected action in the form.
- * if null, an empty action is added & selected
- * ),
- * * actions *
- * action_id => array(
- * 'title' => Action heading,
- * 'url' => Static part of URL. Often we rely
- * relative urls, usually the page itself (not '' !), or just a query string,
- * 'vars' => Associative array of (URL variable => field name),
- * 'multiaction' => Name of the action to execute.
- * Add this action to the multi action form
- * ), ...
- * );
- * @param $place Place where the $actions are displayed. Like 'display-browse', where 'display' is the file (display.php)
- * and 'browse' is the place inside that code (doBrowse).
- * @param $nodata (optional) Message to display if data set is empty.
- * @param $pre_fn (optional) Name of a function to call for each row,
- * it will be passed two params: $rowdata and $actions,
- * it may be used to derive new fields or modify actions.
- * It can return an array of actions specific to the row,
- * or if nothing is returned then the standard actions are used.
- * (see tblproperties.php and constraints.php for examples)
- * The function must not must not store urls because
- * they are relative and won't work out of context.
- */
- function printTable(&$tabledata, &$columns, &$actions, $place, $nodata = null, $pre_fn = null) {
-
- $data = $this->data;
- $misc = $this;
- $lang = $this->lang;
- $plugin_manager = $this->plugin_manager;
-
- // Action buttons hook's place
- $plugin_functions_parameters = [
- 'actionbuttons' => &$actions,
- 'place' => $place,
- ];
- $plugin_manager->do_hook('actionbuttons', $plugin_functions_parameters);
-
- if ($has_ma = isset($actions['multiactions'])) {
- $ma = $actions['multiactions'];
- }
- $tablehtml = '';
-
- unset($actions['multiactions']);
-
- if ($tabledata->recordCount() > 0) {
-
- // Remove the 'comment' column if they have been disabled
- if (!$this->conf['show_comments']) {
- unset($columns['comment']);
- }
-
- if (isset($columns['comment'])) {
- // Uncomment this for clipped comments.
- // TODO: This should be a user option.
- //$columns['comment']['params']['clip'] = true;
- }
-
- if ($has_ma) {
- $tablehtml .= "<script src=\"/js/multiactionform.js\" type=\"text/javascript\"></script>\n";
- $tablehtml .= "<form id=\"multi_form\" action=\"{$ma['url']}\" method=\"post\" enctype=\"multipart/form-data\">\n";
- if (isset($ma['vars'])) {
- foreach ($ma['vars'] as $k => $v) {
- $tablehtml .= "<input type=\"hidden\" name=\"$k\" value=\"$v\" />";
- }
- }
-
- }
-
- $tablehtml .= "<table>\n";
- $tablehtml .= "<tr>\n";
-
- // Handle cases where no class has been passed
- if (isset($column['class'])) {
- $class = $column['class'] !== '' ? " class=\"{$column['class']}\"" : '';
- } else {
- $class = '';
- }
-
- // Display column headings
- if ($has_ma) {
- $tablehtml .= "<th></th>";
- }
-
- foreach ($columns as $column_id => $column) {
- switch ($column_id) {
- case 'actions':
- if (sizeof($actions) > 0) {
- $tablehtml .= "<th class=\"data\" colspan=\"" . count($actions) . "\">{$column['title']}</th>\n";
- }
-
- break;
- default:
- $tablehtml .= "<th class=\"data{$class}\">";
- if (isset($column['help'])) {
- $tablehtml .= $this->printHelp($column['title'], $column['help'], false);
- } else {
- $tablehtml .= $column['title'];
- }
-
- $tablehtml .= "</th>\n";
- break;
- }
- }
- $tablehtml .= "</tr>\n";
-
- // Display table rows
- $i = 0;
- while (!$tabledata->EOF) {
- $id = ($i % 2) + 1;
-
- unset($alt_actions);
- if (!is_null($pre_fn)) {
- $alt_actions = $pre_fn($tabledata, $actions);
- }
-
- if (!isset($alt_actions)) {
- $alt_actions = &$actions;
- }
-
- $tablehtml .= "<tr class=\"data{$id}\">\n";
- if ($has_ma) {
- foreach ($ma['keycols'] as $k => $v) {
- $a[$k] = $tabledata->fields[$v];
- }
-
- $tablehtml .= "<td>";
- $tablehtml .= "<input type=\"checkbox\" name=\"ma[]\" value=\"" . htmlentities(serialize($a), ENT_COMPAT, 'UTF-8') . "\" />";
- $tablehtml .= "</td>\n";
- }
-
- foreach ($columns as $column_id => $column) {
-
- // Apply default values for missing parameters
- if (isset($column['url']) && !isset($column['vars'])) {
- $column['vars'] = [];
- }
-
- switch ($column_id) {
- case 'actions':
- foreach ($alt_actions as $action) {
- if (isset($action['disable']) && $action['disable'] === true) {
- $tablehtml .= "<td></td>\n";
- } else {
- $tablehtml .= "<td class=\"opbutton{$id} {$class}\">";
- $action['fields'] = $tabledata->fields;
- $tablehtml .= $this->printLink($action, false);
- $tablehtml .= "</td>\n";
- }
- }
- break;
- case 'comment':
- $tablehtml .= "<td class='comment_cell'>";
- $val = value($column['field'], $tabledata->fields);
- if (!is_null($val)) {
- $tablehtml .= htmlentities($val);
- }
- $tablehtml .= "</td>";
- break;
- default:
- $tablehtml .= "<td{$class}>";
- $val = value($column['field'], $tabledata->fields);
- if (!is_null($val)) {
- if (isset($column['url'])) {
- $tablehtml .= "<a href=\"{$column['url']}";
- $tablehtml .= $this->printUrlVars($column['vars'], $tabledata->fields, false);
- $tablehtml .= "\">";
- }
- $type = isset($column['type']) ? $column['type'] : null;
- $params = isset($column['params']) ? $column['params'] : [];
- $tablehtml .= $this->printVal($val, $type, $params);
- if (isset($column['url'])) {
- $tablehtml .= "</a>";
- }
-
- }
-
- $tablehtml .= "</td>\n";
- break;
- }
- }
- $tablehtml .= "</tr>\n";
-
- $tabledata->moveNext();
- $i++;
- }
- $tablehtml .= "</table>\n";
-
- // Multi action table footer w/ options & [un]check'em all
- if ($has_ma) {
- // if default is not set or doesn't exist, set it to null
- if (!isset($ma['default']) || !isset($actions[$ma['default']])) {
- $ma['default'] = null;
- }
-
- $tablehtml .= "<br />\n";
- $tablehtml .= "<table>\n";
- $tablehtml .= "<tr>\n";
- $tablehtml .= "<th class=\"data\" style=\"text-align: left\" colspan=\"3\">{$lang['stractionsonmultiplelines']}</th>\n";
- $tablehtml .= "</tr>\n";
- $tablehtml .= "<tr class=\"row1\">\n";
- $tablehtml .= "<td>";
- $tablehtml .= "<a href=\"#\" onclick=\"javascript:checkAll(true);\">{$lang['strselectall']}</a> / ";
- $tablehtml .= "<a href=\"#\" onclick=\"javascript:checkAll(false);\">{$lang['strunselectall']}</a></td>\n";
- $tablehtml .= "<td>&nbsp;--->&nbsp;</td>\n";
- $tablehtml .= "<td>\n";
- $tablehtml .= "\t<select name=\"action\">\n";
- if ($ma['default'] == null) {
- $tablehtml .= "\t\t<option value=\"\">--</option>\n";
- }
-
- foreach ($actions as $k => $a) {
- if (isset($a['multiaction'])) {
- $tablehtml .= "\t\t<option value=\"{$a['multiaction']}\"" . ($ma['default'] == $k ? ' selected="selected"' : '') . ">{$a['content']}</option>\n";
- }
- }
-
- $tablehtml .= "\t</select>\n";
- $tablehtml .= "<input type=\"submit\" value=\"{$lang['strexecute']}\" />\n";
- $tablehtml .= $this->getForm();
- $tablehtml .= "</td>\n";
- $tablehtml .= "</tr>\n";
- $tablehtml .= "</table>\n";
- $tablehtml .= '</form>';
- };
-
- } else {
- if (!is_null($nodata)) {
- $tablehtml .= "<p>{$nodata}</p>\n";
- }
-
- }
- return $tablehtml;
- }
-
/** Produce XML data for the browser tree
* @param $treedata A set of records to populate the tree.
* @param $attrs Attributes for tree items
@@ -2525,20 +1785,20 @@ class Misc {
foreach ($treedata as $rec) {
echo "<tree";
- echo value_xml_attr('text', $attrs['text'], $rec);
- echo value_xml_attr('action', $attrs['action'], $rec);
- echo value_xml_attr('src', $attrs['branch'], $rec);
+ echo Decorator::value_xml_attr('text', $attrs['text'], $rec);
+ echo Decorator::value_xml_attr('action', $attrs['action'], $rec);
+ echo Decorator::value_xml_attr('src', $attrs['branch'], $rec);
- $icon = $this->icon(value($attrs['icon'], $rec));
- echo value_xml_attr('icon', $icon, $rec);
- echo value_xml_attr('iconaction', $attrs['iconAction'], $rec);
+ $icon = $this->icon(Decorator::get_sanitized_value($attrs['icon'], $rec));
+ echo Decorator::value_xml_attr('icon', $icon, $rec);
+ echo Decorator::value_xml_attr('iconaction', $attrs['iconAction'], $rec);
if (!empty($attrs['openicon'])) {
- $icon = $this->icon(value($attrs['openIcon'], $rec));
+ $icon = $this->icon(Decorator::get_sanitized_value($attrs['openIcon'], $rec));
}
- echo value_xml_attr('openicon', $icon, $rec);
+ echo Decorator::value_xml_attr('openicon', $icon, $rec);
- echo value_xml_attr('tooltip', $attrs['toolTip'], $rec);
+ echo Decorator::value_xml_attr('tooltip', $attrs['toolTip'], $rec);
echo " />\n";
}
@@ -2601,7 +1861,8 @@ class Misc {
* @return The escaped string
*/
function escapeShellArg($str) {
- global $data, $lang;
+ $data = $this->getDatabaseAccessor();
+ $lang = $this->lang;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// Due to annoying PHP bugs, shell arguments cannot be escaped
@@ -2625,7 +1886,7 @@ class Misc {
* @return The escaped string
*/
function escapeShellCmd($str) {
- global $data;
+ $data = $this->getDatabaseAccessor();
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$data->fieldClean($str);
@@ -2660,12 +1921,12 @@ class Misc {
'id' => $i,
'desc' => $group['desc'],
'icon' => 'Servers',
- 'action' => url('/views/servers',
+ 'action' => Decorator::url('/views/servers',
[
'group' => Decorator::field('id'),
]
),
- 'branch' => url('/tree/servers',
+ 'branch' => Decorator::url('/tree/servers',
[
'group' => $i,
]
@@ -2680,12 +1941,12 @@ class Misc {
'id' => 'all',
'desc' => $lang['strallservers'],
'icon' => 'Servers',
- 'action' => url('/views/servers',
+ 'action' => Decorator::url('/views/servers',
[
'group' => Decorator::field('id'),
]
),
- 'branch' => url('/tree/servers',
+ 'branch' => Decorator::url('/tree/servers',
[
'group' => 'all',
]
@@ -2744,9 +2005,9 @@ class Misc {
);
if (isset($srvs[$server_id]['username'])) {
$srvs[$server_id]['icon'] = 'Server';
- $srvs[$server_id]['branch'] = Decorator::branchurl('all_db.php',
+ $srvs[$server_id]['branch'] = Decorator::url('all_db.php',
[
-
+ 'action' => 'tree',
'subject' => 'server',
'server' => Decorator::field('id'),
]
@@ -2780,11 +2041,14 @@ class Misc {
if ($server_id !== null) {
$this->server_id = $server_id;
+ } else if ($this->server_info !== null) {
+ return $this->server_info;
}
// Check for the server in the logged-in list
if (isset($_SESSION['webdbLogin'][$this->server_id])) {
- return $_SESSION['webdbLogin'][$this->server_id];
+ $this->server_info = $_SESSION['webdbLogin'][$this->server_id];
+ return $this->server_info;
}
// Otherwise, look for it in the conf file
@@ -2797,18 +2061,21 @@ class Misc {
$this->setReloadBrowser(true);
$this->setServerInfo(null, $info, $this->server_id);
}
+ $this->server_info = $info;
+ return $this->server_info;
- return $info;
}
}
if ($server_id === null) {
-
- return null;
+ $this->server_info = null;
+ return $this->server_info;
} else {
+ $this->server_info = null;
// Unable to find a matching server, are we being hacked?
echo $this->lang['strinvalidserverparam'];
+
exit;
}
}
@@ -2822,7 +2089,7 @@ class Misc {
* server.
*/
function setServerInfo($key, $value, $server_id = null) {
- \PC::debug('setsetverinfo');
+ //\PC::debug('setsetverinfo');
if ($server_id === null && isset($_REQUEST['server'])) {
$server_id = $_REQUEST['server'];
}
@@ -2831,7 +2098,7 @@ class Misc {
if ($value === null) {
unset($_SESSION['webdbLogin'][$server_id]);
} else {
- \PC::debug(['server_id' => $server_id, 'value' => $value], 'webdbLogin');
+ //\PC::debug(['server_id' => $server_id, 'value' => $value], 'webdbLogin null key');
$_SESSION['webdbLogin'][$server_id] = $value;
}
@@ -2839,7 +2106,7 @@ class Misc {
if ($value === null) {
unset($_SESSION['webdbLogin'][$server_id][$key]);
} else {
- \PC::debug(['server_id' => $server_id, 'key' => $key, 'value' => $value], 'webdbLogin');
+ //\PC::debug(['server_id' => $server_id, 'key' => $key, 'value' => $value], __FILE__ . ' ' . __LINE__ . ' webdbLogin key ' . $key);
$_SESSION['webdbLogin'][$server_id][$key] = $value;
}
@@ -2853,7 +2120,7 @@ class Misc {
* @return $data->seSchema() on error
*/
function setCurrentSchema($schema) {
- global $data;
+ $data = $this->getDatabaseAccessor();
$status = $data->setSchema($schema);
if ($status != 0) {
@@ -2982,7 +2249,7 @@ class Misc {
* )
**/
function getAutocompleteFKProperties($table) {
- global $data;
+ $data = $this->getDatabaseAccessor();
$fksprops = [
'byconstr' => [],
diff --git a/src/controllers/ACInsertController.php b/src/controllers/ACInsertController.php
new file mode 100644
index 00000000..085130ed
--- /dev/null
+++ b/src/controllers/ACInsertController.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class ACInsertController extends BaseController {
+ public $_name = 'ACInsertController';
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ if (isset($_POST['offset'])) {
+ $offset = " OFFSET {$_POST['offset']}";
+ } else {
+ $_POST['offset'] = 0;
+ $offset = " OFFSET 0";
+ }
+
+ $keynames = [];
+ foreach ($_POST['fkeynames'] as $k => $v) {
+ $fkeynames[$k] = html_entity_decode($v, ENT_QUOTES);
+ }
+
+ $keyspos = array_combine($fkeynames, $_POST['keys']);
+
+ $f_schema = html_entity_decode($_POST['f_schema'], ENT_QUOTES);
+ $data->fieldClean($f_schema);
+ $f_table = html_entity_decode($_POST['f_table'], ENT_QUOTES);
+ $data->fieldClean($f_table);
+ $f_attname = $fkeynames[$_POST['fattpos'][0]];
+ $data->fieldClean($f_attname);
+
+ $q = "SELECT *
+ FROM \"{$f_schema}\".\"{$f_table}\"
+ WHERE \"{$f_attname}\"::text LIKE '{$_POST['fvalue']}%'
+ ORDER BY \"{$f_attname}\" LIMIT 12 {$offset};";
+
+ $res = $data->selectSet($q);
+
+ if (!$res->EOF) {
+ echo "<table class=\"ac_values\">";
+ echo '<tr>';
+ foreach (array_keys($res->fields) as $h) {
+ echo '<th>';
+
+ if (in_array($h, $fkeynames)) {
+ echo '<img src="' . $misc->icon('ForeignKey') . '" alt="[referenced key]" />';
+ }
+
+ echo htmlentities($h, ENT_QUOTES, 'UTF-8'), '</th>';
+
+ }
+ echo "</tr>\n";
+ $i = 0;
+ while ((!$res->EOF) && ($i < 11)) {
+ $j = 0;
+ echo "<tr class=\"acline\">";
+ foreach ($res->fields as $n => $v) {
+ $finfo = $res->fetchField($j++);
+ if (in_array($n, $fkeynames)) {
+ echo "<td><a href=\"javascript:void(0)\" class=\"fkval\" name=\"{$keyspos[$n]}\">",
+ $misc->printVal($v, $finfo->type, ['clip' => 'collapsed']),
+ "</a></td>";
+ } else {
+ echo "<td><a href=\"javascript:void(0)\">",
+ $misc->printVal($v, $finfo->type, ['clip' => 'collapsed']),
+ "</a></td>";
+ }
+
+ }
+ echo "</tr>\n";
+ $i++;
+ $res->moveNext();
+ }
+ echo "</table>\n";
+
+ $page_tests = '';
+
+ $js = "<script type=\"text/javascript\">\n";
+
+ if ($_POST['offset']) {
+ echo "<a href=\"javascript:void(0)\" id=\"fkprev\">&lt;&lt; Prev</a>";
+ $js .= "fkl_hasprev=true;\n";
+ } else {
+ $js .= "fkl_hasprev=false;\n";
+ }
+
+ if ($res->recordCount() == 12) {
+ $js .= "fkl_hasnext=true;\n";
+ echo "&nbsp;&nbsp;&nbsp;<a href=\"javascript:void(0)\" id=\"fknext\">Next &gt;&gt;</a>";
+ } else {
+ $js .= "fkl_hasnext=false;\n";
+ }
+
+ echo $js . "</script>";
+ } else {
+ printf("<p>{$lang['strnofkref']}</p>", "\"{$_POST['f_schema']}\".\"{$_POST['f_table']}\".\"{$fkeynames[$_POST['fattpos']]}\"");
+
+ if ($_POST['offset']) {
+ echo "<a href=\"javascript:void(0)\" class=\"fkprev\">Prev &lt;&lt;</a>";
+ }
+ }
+
+ }
+}
diff --git a/src/controllers/AdminTrait.php b/src/controllers/AdminTrait.php
index b9cbf1b0..f35df87c 100644
--- a/src/controllers/AdminTrait.php
+++ b/src/controllers/AdminTrait.php
@@ -1,5 +1,6 @@
<?php
namespace PHPPgAdmin\Controller;
+use \PHPPgAdmin\Decorators\Decorator;
trait AdminTrait {
@@ -13,7 +14,7 @@ trait AdminTrait {
$script = $this->script;
$misc = $this->misc;
$lang = $this->lang;
- $data = $this->getDatabaseAccessor();
+ $data = $misc->getDatabaseAccessor();
if (($type == 'table') && empty($_REQUEST['table']) && empty($_REQUEST['ma'])) {
$this->doDefault($lang['strspecifytabletocluster']);
@@ -22,7 +23,7 @@ trait AdminTrait {
if ($confirm) {
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strclusterindex'], 'pg.index.cluster');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -33,7 +34,7 @@ trait AdminTrait {
}
} // END if multi cluster
else {
- $misc->printTrail($type);
+ $this->printTrail($type);
$misc->printTitle($lang['strclusterindex'], 'pg.index.cluster');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -102,7 +103,7 @@ trait AdminTrait {
$script = $this->script;
$misc = $this->misc;
$lang = $this->lang;
- $data = $this->getDatabaseAccessor();
+ $data = $misc->getDatabaseAccessor();
if (($type == 'table') && empty($_REQUEST['table']) && empty($_REQUEST['ma'])) {
$this->doDefault($lang['strspecifytabletoreindex']);
@@ -111,7 +112,7 @@ trait AdminTrait {
if ($confirm) {
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strreindex'], 'pg.reindex');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -122,7 +123,7 @@ trait AdminTrait {
}
} // END if multi reindex
else {
- $misc->printTrail($type);
+ $this->printTrail($type);
$misc->printTitle($lang['strreindex'], 'pg.reindex');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -181,10 +182,11 @@ trait AdminTrait {
*/
public function doAnalyze($type, $confirm = false) {
$this->script = ($type == 'database') ? 'database.php' : 'tables.php';
- $script = $this->script;
- $data = $this->data;
- $misc = $this->misc;
- $lang = $this->lang;
+
+ $script = $this->script;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if (($type == 'table') && empty($_REQUEST['table']) && empty($_REQUEST['ma'])) {
$this->doDefault($lang['strspecifytabletoanalyze']);
@@ -192,19 +194,21 @@ trait AdminTrait {
}
if ($confirm) {
+
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['stranalyze'], 'pg.analyze');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
foreach ($_REQUEST['ma'] as $v) {
$a = unserialize(htmlspecialchars_decode($v, ENT_QUOTES));
+ \Kint::dump($a);
echo "<p>", sprintf($lang['strconfanalyzetable'], $misc->printVal($a['table'])), "</p>\n";
echo "<input type=\"hidden\" name=\"table[]\" value=\"", htmlspecialchars($a['table']), "\" />\n";
}
} // END if multi analyze
else {
- $misc->printTrail($type);
+ $this->printTrail($type);
$misc->printTitle($lang['stranalyze'], 'pg.analyze');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -272,7 +276,7 @@ trait AdminTrait {
if ($confirm) {
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strvacuum'], 'pg.vacuum');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -283,7 +287,7 @@ trait AdminTrait {
}
} else {
// END if multi vacuum
- $misc->printTrail($type);
+ $this->printTrail($type);
$misc->printTitle($lang['strvacuum'], 'pg.vacuum');
echo "<form action=\"/src/views/{$script}\" method=\"post\">\n";
@@ -344,7 +348,7 @@ trait AdminTrait {
$misc = $this->misc;
$lang = $this->lang;
- $data = $this->getDatabaseAccessor();
+ $data = $misc->getDatabaseAccessor();
if (empty($_REQUEST['table'])) {
$this->doAdmin($type, '', $lang['strspecifyeditvacuumtable']);
@@ -354,7 +358,7 @@ trait AdminTrait {
$script = ($type == 'database') ? 'database.php' : 'tables.php';
if ($confirm) {
- $misc->printTrail($type);
+ $this->printTrail($type);
$misc->printTitle(sprintf($lang['streditvacuumtable'], $misc->printVal($_REQUEST['table'])));
$misc->printMsg(sprintf($msg, $misc->printVal($_REQUEST['table'])));
@@ -460,7 +464,7 @@ trait AdminTrait {
$misc = $this->misc;
$lang = $this->lang;
- $data = $this->getDatabaseAccessor();
+ $data = $misc->getDatabaseAccessor();
if (empty($_REQUEST['table'])) {
$this->doAdmin($type, '', $lang['strspecifydelvacuumtable']);
@@ -468,8 +472,8 @@ trait AdminTrait {
}
if ($confirm) {
- $misc->printTrail($type);
- $misc->printTabs($type, 'admin');
+ $this->printTrail($type);
+ $this->printTabs($type, 'admin');
$script = ($type == 'database') ? 'database.php' : 'tables.php';
@@ -517,10 +521,10 @@ trait AdminTrait {
$misc = $this->misc;
$lang = $this->lang;
- $data = $this->getDatabaseAccessor();
+ $data = $misc->getDatabaseAccessor();
- $misc->printTrail($type);
- $misc->printTabs($type, 'admin');
+ $this->printTrail($type);
+ $this->printTabs($type, 'admin');
$misc->printMsg($msg);
if ($type == 'database') {
@@ -648,37 +652,37 @@ trait AdminTrait {
],
'autovacuum_enabled' => [
'title' => $lang['strenabled'],
- 'field' => callback('enlight', ['autovacuum_enabled', $defaults['autovacuum']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_enabled', $defaults['autovacuum']]),
'type' => 'verbatim',
],
'autovacuum_vacuum_threshold' => [
'title' => $lang['strvacuumbasethreshold'],
- 'field' => callback('enlight', ['autovacuum_vacuum_threshold', $defaults['autovacuum_vacuum_threshold']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_vacuum_threshold', $defaults['autovacuum_vacuum_threshold']]),
'type' => 'verbatim',
],
'autovacuum_vacuum_scale_factor' => [
'title' => $lang['strvacuumscalefactor'],
- 'field' => callback('enlight', ['autovacuum_vacuum_scale_factor', $defaults['autovacuum_vacuum_scale_factor']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_vacuum_scale_factor', $defaults['autovacuum_vacuum_scale_factor']]),
'type' => 'verbatim',
],
'autovacuum_analyze_threshold' => [
'title' => $lang['stranalybasethreshold'],
- 'field' => callback('enlight', ['autovacuum_analyze_threshold', $defaults['autovacuum_analyze_threshold']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_analyze_threshold', $defaults['autovacuum_analyze_threshold']]),
'type' => 'verbatim',
],
'autovacuum_analyze_scale_factor' => [
'title' => $lang['stranalyzescalefactor'],
- 'field' => callback('enlight', ['autovacuum_analyze_scale_factor', $defaults['autovacuum_analyze_scale_factor']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_analyze_scale_factor', $defaults['autovacuum_analyze_scale_factor']]),
'type' => 'verbatim',
],
'autovacuum_vacuum_cost_delay' => [
'title' => $lang['strvacuumcostdelay'],
- 'field' => concat(callback('enlight', ['autovacuum_vacuum_cost_delay', $defaults['autovacuum_vacuum_cost_delay']]), 'ms'),
+ 'field' => Decorator::concat(Decorator::callback('enlight', ['autovacuum_vacuum_cost_delay', $defaults['autovacuum_vacuum_cost_delay']]), 'ms'),
'type' => 'verbatim',
],
'autovacuum_vacuum_cost_limit' => [
'title' => $lang['strvacuumcostlimit'],
- 'field' => callback('enlight', ['autovacuum_vacuum_cost_limit', $defaults['autovacuum_vacuum_cost_limit']]),
+ 'field' => Decorator::callback('enlight', ['autovacuum_vacuum_cost_limit', $defaults['autovacuum_vacuum_cost_limit']]),
'type' => 'verbatim',
],
];
@@ -725,7 +729,7 @@ trait AdminTrait {
);
}
- echo $misc->printTable($autovac, $columns, $actions, 'admin-admin', $lang['strnovacuumconf']);
+ echo $this->printTable($autovac, $columns, $actions, 'admin-admin', $lang['strnovacuumconf']);
if (($type == 'table') and ($autovac->recordCount() == 0)) {
echo "<br />";
diff --git a/src/controllers/AggregateController.php b/src/controllers/AggregateController.php
index 9246bbd3..f852e498 100644
--- a/src/controllers/AggregateController.php
+++ b/src/controllers/AggregateController.php
@@ -9,9 +9,97 @@ use \PHPPgAdmin\Decorators\Decorator;
class AggregateController extends BaseController {
public $_name = 'AggregateController';
-/**
- * Actually creates the new aggregate in the database
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['straggregates']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'alter':
+ $this->doAlter();
+ break;
+ case 'save_alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doProperties();
+ }
+
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $aggregates = $data->getAggregates();
+
+ $proto = Decorator::concat(Decorator::field('proname'), ' (', Decorator::field('proargtypes'), ')');
+ $reqvars = $misc->getRequestVars('aggregate');
+
+ $attrs = [
+ 'text' => $proto,
+ 'icon' => 'Aggregate',
+ 'toolTip' => Decorator::field('aggcomment'),
+ 'action' => Decorator::redirecturl('redirect.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'aggrname' => Decorator::field('proname'),
+ 'aggrtype' => Decorator::field('proargtypes'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($aggregates, $attrs, 'aggregates');
+ }
+
+ /**
+ * Actually creates the new aggregate in the database
+ */
public function doSaveCreate() {
$conf = $this->conf;
$misc = $this->misc;
@@ -84,7 +172,7 @@ class AggregateController extends BaseController {
$_REQUEST['aggrcomment'] = '';
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreateaggregate'], 'pg.aggregate.create');
$misc->printMsg($msg);
@@ -158,7 +246,7 @@ class AggregateController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('aggregate');
+ $this->printTrail('aggregate');
$misc->printTitle($lang['stralter'], 'pg.aggregate.alter');
$misc->printMsg($msg);
@@ -205,7 +293,7 @@ class AggregateController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('aggregate');
+ $this->printTrail('aggregate');
$misc->printTitle($lang['strdrop'], 'pg.aggregate.drop');
echo "<p>", sprintf($lang['strconfdropaggregate'], htmlspecialchars($_REQUEST['aggrname'])), "</p>\n";
@@ -240,7 +328,7 @@ class AggregateController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('aggregate');
+ $this->printTrail('aggregate');
$misc->printTitle($lang['strproperties'], 'pg.aggregate');
$misc->printMsg($msg);
@@ -326,7 +414,7 @@ class AggregateController extends BaseController {
'content' => $lang['strdrop'],
];
- $misc->printNavLinks($navlinks, 'aggregates-properties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'aggregates-properties', get_defined_vars());
}
/**
@@ -337,8 +425,8 @@ class AggregateController extends BaseController {
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'aggregates');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'aggregates');
$misc->printMsg($msg);
$aggregates = $data->getAggregates();
@@ -404,7 +492,7 @@ class AggregateController extends BaseController {
unset($actions['alter']);
}
- echo $misc->printTable($aggregates, $columns, $actions, 'aggregates-aggregates', $lang['strnoaggregates']);
+ echo $this->printTable($aggregates, $columns, $actions, 'aggregates-aggregates', $lang['strnoaggregates']);
$navlinks = [
'create' => [
@@ -422,7 +510,7 @@ class AggregateController extends BaseController {
'content' => $lang['strcreateaggregate'],
],
];
- $misc->printNavLinks($navlinks, 'aggregates-aggregates', get_defined_vars());
+ $this->printNavLinks($navlinks, 'aggregates-aggregates', get_defined_vars());
}
}
diff --git a/src/controllers/AllDBController.php b/src/controllers/AllDBController.php
index f1237d84..bc3fecbc 100644
--- a/src/controllers/AllDBController.php
+++ b/src/controllers/AllDBController.php
@@ -7,10 +7,94 @@ use \PHPPgAdmin\Decorators\Decorator;
* Base controller class
*/
class AllDBController extends BaseController {
- public $_name = 'AllDBController';
-/**
- * Display a form for alter and perform actual alter
- */
+ public $_name = 'AllDBController';
+ public $table_place = 'all_db-databases';
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strdatabases']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'export':
+ $this->doExport();
+ break;
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_REQUEST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ doDrop(true);
+ break;
+ case 'alter':
+ if (isset($_POST['oldname']) && isset($_POST['newname']) && !isset($_POST['cancel'])) {
+ $this->doAlter(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_alter':
+ $this->doAlter(true);
+ break;
+ default:
+ $this->doDefault();
+
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $databases = $data->getDatabases();
+
+ $reqvars = $misc->getRequestVars('database');
+
+ $attrs = [
+ 'text' => Decorator::field('datname'),
+ 'icon' => 'Database',
+ 'toolTip' => Decorator::field('datcomment'),
+ 'action' => Decorator::redirecturl('redirect.php', $reqvars, ['database' => Decorator::field('datname')]),
+ 'branch' => Decorator::url('database.php', $reqvars, ['action' => 'tree', 'database' => Decorator::field('datname')]),
+ ];
+
+ return $misc->printTree($databases, $attrs, 'databases');
+ }
+
+ /**
+ * Display a form for alter and perform actual alter
+ */
public function doAlter($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -18,7 +102,7 @@ class AllDBController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('database');
+ $this->printTrail('database');
$misc->printTitle($lang['stralter'], 'pg.database.alter');
echo "<form action=\"/src/views/all_db.php\" method=\"post\">\n";
@@ -80,9 +164,9 @@ class AllDBController extends BaseController {
}
}
-/**
- * Show confirmation of drop and perform actual drop
- */
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
public function doDrop($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -96,7 +180,7 @@ class AllDBController extends BaseController {
if ($confirm) {
- $misc->printTrail('database');
+ $this->printTrail('database');
$misc->printTitle($lang['strdrop'], 'pg.database.drop');
echo "<form action=\"/src/views/all_db.php\" method=\"post\">\n";
@@ -148,16 +232,16 @@ class AllDBController extends BaseController {
} //END DROP
} // END FUNCTION
-/**
- * Displays a screen where they can enter a new database
- */
+ /**
+ * Displays a screen where they can enter a new database
+ */
public function doCreate($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('server');
+ $this->printTrail('server');
$misc->printTitle($lang['strcreatedatabase'], 'pg.database.create');
$misc->printMsg($msg);
@@ -282,9 +366,9 @@ class AllDBController extends BaseController {
echo "</form>\n";
}
-/**
- * Actually creates the new view in the database
- */
+ /**
+ * Actually creates the new view in the database
+ */
public function doSaveCreate() {
$conf = $this->conf;
$misc = $this->misc;
@@ -327,17 +411,17 @@ class AllDBController extends BaseController {
}
}
-/**
- * Displays options for cluster download
- */
+ /**
+ * Displays options for cluster download
+ */
public function doExport($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('server');
- $misc->printTabs('server', 'export');
+ $this->printTrail('server');
+ $this->printTabs('server', 'export');
$misc->printMsg($msg);
echo "<form action=\"/src/views/dbexport.php\" method=\"post\">\n";
@@ -386,8 +470,8 @@ class AllDBController extends BaseController {
$misc = $this->misc;
$lang = $this->lang;
- $misc->printTrail('server');
- $misc->printTabs('server', 'databases');
+ $this->printTrail('server');
+ $this->printTabs('server', 'databases');
$misc->printMsg($msg);
$data = $misc->getDatabaseAccessor();
$databases = $data->getDatabases();
@@ -498,7 +582,7 @@ class AllDBController extends BaseController {
unset($actions['privileges']);
}
- echo $misc->printTable($databases, $columns, $actions, 'all_db-databases', $lang['strnodatabases']);
+ echo $this->printTable($databases, $columns, $actions, $this->table_place, $lang['strnodatabases']);
$navlinks = [
'create' => [
@@ -514,7 +598,7 @@ class AllDBController extends BaseController {
'content' => $lang['strcreatedatabase'],
],
];
- $misc->printNavLinks($navlinks, 'all_db-databases', get_defined_vars());
+ $this->printNavLinks($navlinks, $this->table_place, get_defined_vars());
}
diff --git a/src/controllers/BaseController.php b/src/controllers/BaseController.php
index 6c5de796..7326fd7f 100644
--- a/src/controllers/BaseController.php
+++ b/src/controllers/BaseController.php
@@ -6,40 +6,136 @@ namespace PHPPgAdmin\Controller;
* Base controller class
*/
class BaseController {
-
- private $_connection = null;
- private $_no_db_connection = false;
- private $_reload_browser = false;
- private $app = null;
- private $data = null;
- private $database = null;
- private $server_id = null;
- public $appLangFiles = [];
- public $appThemes = [];
- public $appName = '';
- public $appVersion = '';
- public $form = '';
- public $href = '';
- public $lang = [];
- public $_name = 'BaseController';
+ private $container = null;
+ private $_connection = null;
+ private $app = null;
+ private $data = null;
+ private $database = null;
+ private $server_id = null;
+ public $appLangFiles = [];
+ public $appThemes = [];
+ public $appName = '';
+ public $appVersion = '';
+ public $form = '';
+ public $href = '';
+ public $lang = [];
+ public $action = '';
+ public $_name = 'BaseController';
+ public $_title = 'base';
+ private $table_controller = null;
+ private $trail_controller = null;
+ public $msg = '';
/* Constructor */
function __construct(\Slim\Container $container) {
-
+ $this->container = $container;
$this->lang = $container->get('lang');
$this->conf = $container->get('conf');
$this->view = $container->get('view');
$this->plugin_manager = $container->get('plugin_manager');
- $this->appName = $container->get('settings')['appName'];
- $this->appVersion = $container->get('settings')['appVersion'];
+ $this->msg = $container->get('msg');
$this->appLangFiles = $container->get('appLangFiles');
$this->misc = $container->get('misc');
$this->appThemes = $container->get('appThemes');
+ $this->action = $container->get('action');
+
+ $msg = $container->get('msg');
+ if ($this->misc->getNoDBConnection() === false) {
+ if ($this->misc->getServerId() === null) {
+ echo $lang['strnoserversupplied'];
+ exit;
+ }
+ $_server_info = $this->misc->getServerInfo();
+ // Redirect to the login form if not logged in
+ if (!isset($_server_info['username'])) {
+
+ $login_controller = new \PHPPgAdmin\Controller\LoginController($container);
+ echo $login_controller->doLoginForm($msg);
+
+ exit;
+ }
+ }
+
+ //\PC::debug(['name' => $this->_name, 'no_db_connection' => $this->misc->getNoDBConnection()], 'instanced controller');
+ }
+
+ public function getContainer() {
+ return $this->container;
+ }
+
+ private function getTableController() {
+ if ($this->table_controller === null) {
+ $this->table_controller = new \PHPPgAdmin\XHtml\HTMLTableController($this->getContainer());
+ }
+ return $this->table_controller;
+ }
+
+ private function getNavbarController() {
+ if ($this->trail_controller === null) {
+ $this->trail_controller = new \PHPPgAdmin\XHtml\HTMLNavbarController($this->getContainer());
+ }
+
+ return $this->trail_controller;
+ }
+ /**
+ * Instances an HTMLTable and returns its html content
+ * @param [type] &$tabledata [description]
+ * @param [type] &$columns [description]
+ * @param [type] &$actions [description]
+ * @param [type] $place [description]
+ * @param [type] $nodata [description]
+ * @param [type] $pre_fn [description]
+ * @return [type] [description]
+ */
+ function printTable(&$tabledata, &$columns, &$actions, $place, $nodata = null, $pre_fn = null) {
+ $html_table = $this->getTableController();
+ return $html_table->printTable($tabledata, $columns, $actions, $place, $nodata, $pre_fn);
+ }
+
+ function printTrail($trail = [], $do_print = true) {
+ $html_trail = $this->getNavbarController();
+ return $html_trail->printTrail($trail, $do_print);
+ }
+
+ function printNavLinks($navlinks, $place, $env = [], $do_print = true) {
+ $html_trail = $this->getNavbarController();
+ return $html_trail->printNavLinks($navlinks, $place, $env, $do_print);
+ }
+
+ function printTabs($tabs, $activetab, $do_print = true) {
+ $html_trail = $this->getNavbarController();
+ return $html_trail->printTabs($tabs, $activetab, $do_print);
+ }
+ function getLastTabURL($section) {
+ $html_trail = $this->getNavbarController();
+ return $html_trail->getLastTabURL($section);
+ }
+
+ function printLink($link, $do_print = true) {
+ $html_trail = $this->getNavbarController();
+ return $html_trail->printLink($link, $do_print);
+ }
+
+ public function render() {
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ $misc->printHeader($lang[$this->_title]);
+ $misc->printBody();
+
+ switch ($action) {
+ default:
+ $this->doDefault();
+ break;
+ }
- \PC::debug($this->_name, 'instanced controller');
+ $misc->printFooter();
}
public function doDefault() {
- return $this;
+ $html = '<div><h2>Section title</h2> <p>Main content</p></div>';
+ echo $html;
+ return $html;
}
} \ No newline at end of file
diff --git a/src/controllers/BrowserController.php b/src/controllers/BrowserController.php
new file mode 100644
index 00000000..8b6ec841
--- /dev/null
+++ b/src/controllers/BrowserController.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class BrowserController extends BaseController {
+ public $_name = 'BrowserController';
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ $this->misc = $container->get('misc');
+
+ $this->misc->setNoDBConnection(true);
+ $this->misc->setNoBottomLink(true);
+
+ parent::__construct($container);
+ }
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $viewVars = $this->lang;
+ $viewVars['appName'] = $this->misc->appName;
+ $viewVars['icon'] = [
+ 'blank' => $this->misc->icon('blank'),
+ 'I' => $this->misc->icon('I'),
+ 'L' => $this->misc->icon('L'),
+ 'Lminus' => $this->misc->icon('Lminus'),
+ 'Loading' => $this->misc->icon('Loading'),
+ 'Lplus' => $this->misc->icon('Lplus'),
+ 'ObjectNotFound' => $this->misc->icon('ObjectNotFound'),
+ 'Refresh' => $this->misc->icon('Refresh'),
+ 'Servers' => $this->misc->icon('Servers'),
+ 'T' => $this->misc->icon('T'),
+ 'Tminus' => $this->misc->icon('Tminus'),
+ 'Tplus' => $this->misc->icon('Tplus'),
+
+ ];
+
+ echo $this->view->fetch('browser.twig', $viewVars);
+
+ }
+}
diff --git a/src/controllers/CastController.php b/src/controllers/CastController.php
index bed5d1ff..e54453f0 100644
--- a/src/controllers/CastController.php
+++ b/src/controllers/CastController.php
@@ -18,17 +18,17 @@ class CastController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- function renderCastContext($val) {
- global $lang;
+ $renderCastContext = function ($val) use ($lang) {
+
switch ($val) {
case 'e':return $lang['strno'];
case 'a':return $lang['strinassignment'];
default:return $lang['stryes'];
}
- }
+ };
- $misc->printTrail('database');
- $misc->printTabs('database', 'casts');
+ $this->printTrail('database');
+ $this->printTabs('database', 'casts');
$misc->printMsg($msg);
$casts = $data->getCasts();
@@ -51,7 +51,7 @@ class CastController extends BaseController {
'title' => $lang['strimplicit'],
'field' => Decorator::field('castcontext'),
'type' => 'callback',
- 'params' => ['function' => 'renderCastContext', 'align' => 'center'],
+ 'params' => ['function' => $renderCastContext, 'align' => 'center'],
],
'comment' => [
'title' => $lang['strcomment'],
@@ -61,6 +61,54 @@ class CastController extends BaseController {
$actions = [];
- echo $misc->printTable($casts, $columns, $actions, 'casts-casts', $lang['strnocasts']);
+ echo $this->printTable($casts, $columns, $actions, 'casts-casts', $lang['strnocasts']);
}
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strcasts']);
+ $misc->printBody();
+
+ switch ($action) {
+
+ default:
+ $cast_controller->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+/**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $casts = $data->getCasts();
+
+ $proto = Decorator::concat(Decorator::field('castsource'), ' AS ', Decorator::field('casttarget'));
+
+ $attrs = [
+ 'text' => $proto,
+ 'icon' => 'Cast',
+ ];
+
+ return $misc->printTree($casts, $attrs, 'casts');
+
+ }
+
}
diff --git a/src/controllers/ColPropertyController.php b/src/controllers/ColPropertyController.php
index c83c2a53..fe18f8e8 100644
--- a/src/controllers/ColPropertyController.php
+++ b/src/controllers/ColPropertyController.php
@@ -7,8 +7,48 @@ use \PHPPgAdmin\Decorators\Decorator;
* Base controller class
*/
class ColPropertyController extends BaseController {
- public $_name = 'ColPropertyController';
+ public $_name = 'ColPropertyController';
+ public $tableName = '';
+ public $table_place = 'colproperties-colproperties';
+
+ public function render() {
+ if (isset($_REQUEST['table'])) {
+ $this->tableName = &$_REQUEST['table'];
+ } elseif (isset($_REQUEST['view'])) {
+ $this->tableName = &$_REQUEST['view'];
+ } else {
+ die($lang['strnotableprovided']);
+ }
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strtables'] . ' - ' . $this->tableName);
+ $misc->printBody();
+
+ if (isset($_REQUEST['view'])) {
+ $this->doDefault(null, false);
+ } else {
+ switch ($action) {
+ case 'properties':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doAlter();
+ }
+
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+ }
+
+ $misc->printFooter();
+ }
/**
* Displays a screen where they can alter a column
*/
@@ -24,7 +64,7 @@ class ColPropertyController extends BaseController {
switch ($_REQUEST['stage']) {
case 1:
- $misc->printTrail('column');
+ $this->printTrail('column');
$misc->printTitle($lang['stralter'], 'pg.column.alter');
$misc->printMsg($msg);
@@ -162,7 +202,7 @@ class ColPropertyController extends BaseController {
if ($status == 0) {
if ($_REQUEST['column'] != $_REQUEST['field']) {
$_REQUEST['column'] = $_REQUEST['field'];
- $_reload_browser = true;
+ $misc->setReloadBrowser(true);
}
$this->doDefault($lang['strcolumnaltered']);
} else {
@@ -185,8 +225,6 @@ class ColPropertyController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- global $tableName;
-
$attPre = function (&$rowdata) use ($data) {
$rowdata->fields['+type'] = $data->formatType($rowdata->fields['type'], $rowdata->fields['atttypmod']);
@@ -196,16 +234,16 @@ class ColPropertyController extends BaseController {
$msg .= "<br/>{$lang['strnoobjects']}";
}
- $misc->printTrail('column');
+ $this->printTrail('column');
//$misc->printTitle($lang['strcolprop']);
- $misc->printTabs('column', 'properties');
+ $this->printTabs('column', 'properties');
$misc->printMsg($msg);
if (!empty($_REQUEST['column'])) {
// Get table
- $tdata = $data->getTable($tableName);
+ $tdata = $data->getTable($this->tableName);
// Get columns
- $attrs = $data->getTableAttributes($tableName, $_REQUEST['column']);
+ $attrs = $data->getTableAttributes($this->tableName, $_REQUEST['column']);
// Show comment if any
if ($attrs->fields['comment'] !== null) {
@@ -237,12 +275,12 @@ class ColPropertyController extends BaseController {
}
$actions = [];
- echo $misc->printTable($attrs, $column, $actions, 'colproperties-colproperties', null, $attPre);
+ echo $this->printTable($attrs, $column, $actions, $this->table_place, null, $attPre);
echo "<br />\n";
$f_attname = $_REQUEST['column'];
- $f_table = $tableName;
+ $f_table = $this->tableName;
$f_schema = $data->_schema;
$data->fieldClean($f_attname);
$data->fieldClean($f_table);
@@ -265,7 +303,7 @@ class ColPropertyController extends BaseController {
'server' => $_REQUEST['server'],
'database' => $_REQUEST['database'],
'schema' => $_REQUEST['schema'],
- 'table' => $tableName,
+ 'table' => $this->tableName,
'column' => $_REQUEST['column'],
'return' => 'column',
'query' => $query,
@@ -283,7 +321,7 @@ class ColPropertyController extends BaseController {
'server' => $_REQUEST['server'],
'database' => $_REQUEST['database'],
'schema' => $_REQUEST['schema'],
- 'table' => $tableName,
+ 'table' => $this->tableName,
'column' => $_REQUEST['column'],
],
],
@@ -299,7 +337,7 @@ class ColPropertyController extends BaseController {
'server' => $_REQUEST['server'],
'database' => $_REQUEST['database'],
'schema' => $_REQUEST['schema'],
- 'table' => $tableName,
+ 'table' => $this->tableName,
'column' => $_REQUEST['column'],
],
],
@@ -319,7 +357,7 @@ class ColPropertyController extends BaseController {
'server' => $_REQUEST['server'],
'database' => $_REQUEST['database'],
'schema' => $_REQUEST['schema'],
- 'view' => $tableName,
+ 'view' => $this->tableName,
'column' => $_REQUEST['column'],
'return' => 'column',
'query' => $query,
@@ -331,7 +369,7 @@ class ColPropertyController extends BaseController {
];
}
- $misc->printNavLinks($navlinks, 'colproperties-colproperties', get_defined_vars());
+ $this->printNavLinks($navlinks, $this->table_place, get_defined_vars());
}
}
diff --git a/src/controllers/ConstraintController.php b/src/controllers/ConstraintController.php
index dd02702f..99addc7f 100644
--- a/src/controllers/ConstraintController.php
+++ b/src/controllers/ConstraintController.php
@@ -9,9 +9,100 @@ use \PHPPgAdmin\Decorators\Decorator;
class ConstraintController extends BaseController {
public $_name = 'ConstraintController';
-/**
- * Confirm and then actually add a FOREIGN KEY constraint
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table'] . ' - ' . $lang['strconstraints'],
+ "<script src=\"/js/indexes.js\" type=\"text/javascript\"></script>");
+
+ if ($action == 'add_unique_key' || $action == 'save_add_unique_key'
+ || $action == 'add_primary_key' || $action == 'save_add_primary_key'
+ || $action == 'add_foreign_key' || $action == 'save_add_foreign_key') {
+ echo "<body onload=\"init();\">";
+ } else {
+ $misc->printBody();
+ }
+
+ switch ($action) {
+ case 'add_foreign_key':
+ $this->addForeignKey(1);
+ break;
+ case 'save_add_foreign_key':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->addForeignKey($_REQUEST['stage']);
+ }
+
+ break;
+ case 'add_unique_key':
+ $this->addPrimaryOrUniqueKey('unique', true);
+ break;
+ case 'save_add_unique_key':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->addPrimaryOrUniqueKey('unique', false);
+ }
+
+ break;
+ case 'add_primary_key':
+ $this->addPrimaryOrUniqueKey('primary', true);
+ break;
+ case 'save_add_primary_key':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->addPrimaryOrUniqueKey('primary', false);
+ }
+
+ break;
+ case 'add_check':
+ $this->addCheck(true);
+ break;
+ case 'save_add_check':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->addCheck(false);
+ }
+
+ break;
+ case 'save_create':
+ $this->doSaveCreate();
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+ /**
+ * Confirm and then actually add a FOREIGN KEY constraint
+ */
function addForeignKey($stage, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -60,7 +151,7 @@ class ConstraintController extends BaseController {
$_REQUEST['target'] = unserialize($_REQUEST['target']);
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['straddfk'], 'pg.constraint.foreign_key');
$misc->printMsg($msg);
@@ -182,7 +273,7 @@ class ConstraintController extends BaseController {
}
break;
default:
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['straddfk'], 'pg.constraint.foreign_key');
$misc->printMsg($msg);
@@ -270,7 +361,7 @@ class ConstraintController extends BaseController {
$_POST['tablespace'] = '';
}
- $misc->printTrail('table');
+ $this->printTrail('table');
switch ($type) {
case 'primary':
@@ -410,7 +501,7 @@ class ConstraintController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['straddcheck'], 'pg.constraint.check');
$misc->printMsg($msg);
@@ -459,7 +550,7 @@ class ConstraintController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('constraint');
+ $this->printTrail('constraint');
$misc->printTitle($lang['strdrop'], 'pg.constraint.drop');
echo "<p>", sprintf($lang['strconfdropconstraint'], $misc->printVal($_REQUEST['constraint']),
@@ -505,8 +596,8 @@ class ConstraintController extends BaseController {
}
};
- $misc->printTrail('table');
- $misc->printTabs('table', 'constraints');
+ $this->printTrail('table');
+ $this->printTabs('table', 'constraints');
$misc->printMsg($msg);
$constraints = $data->getConstraints($_REQUEST['table']);
@@ -547,7 +638,7 @@ class ConstraintController extends BaseController {
],
];
- echo $misc->printTable($constraints, $columns, $actions, 'constraints-constraints', $lang['strnoconstraints'], $cnPre);
+ echo $this->printTable($constraints, $columns, $actions, 'constraints-constraints', $lang['strnoconstraints'], $cnPre);
$navlinks = [
'addcheck' => [
@@ -611,6 +702,6 @@ class ConstraintController extends BaseController {
'content' => $lang['straddfk'],
],
];
- $misc->printNavLinks($navlinks, 'constraints-constraints', get_defined_vars());
+ $this->printNavLinks($navlinks, 'constraints-constraints', get_defined_vars());
}
}
diff --git a/src/controllers/ConversionController.php b/src/controllers/ConversionController.php
index a33243a8..bfad2dcb 100644
--- a/src/controllers/ConversionController.php
+++ b/src/controllers/ConversionController.php
@@ -8,9 +8,67 @@ use \PHPPgAdmin\Decorators\Decorator;
*/
class ConversionController extends BaseController {
public $_name = 'ConversionController';
-/**
- * Show default list of conversions in the database
- */
+
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strconversions']);
+ $misc->printBody();
+
+ switch ($action) {
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $constraints = $data->getConstraints($_REQUEST['table']);
+
+ $reqvars = $misc->getRequestVars('schema');
+
+ function getIcon($f) {
+ switch ($f['contype']) {
+ case 'u':
+ return 'UniqueConstraint';
+ case 'c':
+ return 'CheckConstraint';
+ case 'f':
+ return 'ForeignKey';
+ case 'p':
+ return 'PrimaryKey';
+
+ }
+ }
+
+ $attrs = [
+ 'text' => Decorator::field('conname'),
+ 'icon' => Decorator::callback('getIcon'),
+ ];
+
+ return $misc->printTree($constraints, $attrs, 'constraints');
+
+ }
+
+ /**
+ * Show default list of conversions in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
@@ -18,8 +76,8 @@ class ConversionController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'conversions');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'conversions');
$misc->printMsg($msg);
$conversions = $data->getconversions();
@@ -50,6 +108,6 @@ class ConversionController extends BaseController {
$actions = [];
- echo $misc->printTable($conversions, $columns, $actions, 'conversions-conversions', $lang['strnoconversions']);
+ echo $this->printTable($conversions, $columns, $actions, 'conversions-conversions', $lang['strnoconversions']);
}
}
diff --git a/src/controllers/DBExportController.php b/src/controllers/DBExportController.php
new file mode 100644
index 00000000..68d342af
--- /dev/null
+++ b/src/controllers/DBExportController.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class DBExportController extends BaseController {
+ public $_name = 'DBExportController';
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ // Include application functions
+ $f_schema = $f_object = '';
+ $misc->setNoOutput(true);
+
+ // Are we doing a cluster-wide dump or just a per-database dump
+ $dumpall = ($_REQUEST['subject'] == 'server');
+
+ // Check that database dumps are enabled.
+ if ($misc->isDumpEnabled($dumpall)) {
+
+ $server_info = $misc->getServerInfo();
+
+ // Get the path of the pg_dump/pg_dumpall executable
+ $exe = $misc->escapeShellCmd($server_info[$dumpall ? 'pg_dumpall_path' : 'pg_dump_path']);
+
+ // Obtain the pg_dump version number and check if the path is good
+ $version = [];
+ preg_match("/(\d+(?:\.\d+)?)(?:\.\d+)?.*$/", exec($exe . " --version"), $version);
+
+ if (empty($version)) {
+ if ($dumpall) {
+ printf($lang['strbadpgdumpallpath'], $server_info['pg_dumpall_path']);
+ } else {
+ printf($lang['strbadpgdumppath'], $server_info['pg_dump_path']);
+ }
+
+ return;
+ }
+
+ // Make it do a download, if necessary
+ switch ($_REQUEST['output']) {
+ case 'show':
+ header('Content-Type: text/plain');
+ break;
+ case 'download':
+ // Set headers. MSIE is totally broken for SSL downloading, so
+ // we need to have it download in-place as plain text
+ if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && isset($_SERVER['HTTPS'])) {
+ header('Content-Type: text/plain');
+ } else {
+ header('Content-Type: application/download');
+ header('Content-Disposition: attachment; filename=dump.sql');
+ }
+ break;
+ case 'gzipped':
+ // MSIE in SSL mode cannot do this - it should never get to this point
+ header('Content-Type: application/download');
+ header('Content-Disposition: attachment; filename=dump.sql.gz');
+ break;
+ }
+
+ // Set environmental variables that pg_dump uses
+ putenv('PGPASSWORD=' . $server_info['password']);
+ putenv('PGUSER=' . $server_info['username']);
+ $hostname = $server_info['host'];
+ if ($hostname !== null && $hostname != '') {
+ putenv('PGHOST=' . $hostname);
+ }
+ $port = $server_info['port'];
+ if ($port !== null && $port != '') {
+ putenv('PGPORT=' . $port);
+ }
+
+ // Build command for executing pg_dump. '-i' means ignore version differences.
+ $cmd = $exe . " -i";
+
+ // we are PG 7.4+, so we always have a schema
+ if (isset($_REQUEST['schema'])) {
+ $f_schema = $_REQUEST['schema'];
+ $data->fieldClean($f_schema);
+ }
+
+ // Check for a specified table/view
+ switch ($_REQUEST['subject']) {
+ case 'schema':
+ // This currently works for 8.2+ (due to the orthoganl -t -n issue introduced then)
+ $cmd .= " -n " . $misc->escapeShellArg("\"{$f_schema}\"");
+ break;
+ case 'table':
+ case 'view':
+ $f_object = $_REQUEST[$_REQUEST['subject']];
+ $data->fieldClean($f_object);
+
+ // Starting in 8.2, -n and -t are orthagonal, so we now schema qualify
+ // the table name in the -t argument and quote both identifiers
+ if (((float) $version[1]) >= 8.2) {
+ $cmd .= " -t " . $misc->escapeShellArg("\"{$f_schema}\".\"{$f_object}\"");
+ } else {
+ // If we are 7.4 or higher, assume they are using 7.4 pg_dump and
+ // set dump schema as well. Also, mixed case dumping has been fixed
+ // then..
+ $cmd .= " -t " . $misc->escapeShellArg($f_object)
+ . " -n " . $misc->escapeShellArg($f_schema);
+ }
+ }
+
+ // Check for GZIP compression specified
+ if ($_REQUEST['output'] == 'gzipped' && !$dumpall) {
+ $cmd .= " -Z 9";
+ }
+
+ switch ($_REQUEST['what']) {
+ case 'dataonly':
+ $cmd .= ' -a';
+ if ($_REQUEST['d_format'] == 'sql') {
+ $cmd .= ' --inserts';
+ } elseif (isset($_REQUEST['d_oids'])) {
+ $cmd .= ' -o';
+ }
+
+ break;
+ case 'structureonly':
+ $cmd .= ' -s';
+ if (isset($_REQUEST['s_clean'])) {
+ $cmd .= ' -c';
+ }
+
+ break;
+ case 'structureanddata':
+ if ($_REQUEST['sd_format'] == 'sql') {
+ $cmd .= ' --inserts';
+ } elseif (isset($_REQUEST['sd_oids'])) {
+ $cmd .= ' -o';
+ }
+
+ if (isset($_REQUEST['sd_clean'])) {
+ $cmd .= ' -c';
+ }
+
+ break;
+ }
+
+ if (!$dumpall) {
+ putenv('PGDATABASE=' . $_REQUEST['database']);
+ }
+
+ // Execute command and return the output to the screen
+ passthru($cmd);
+ }
+
+ }
+
+}
diff --git a/src/controllers/DataExportController.php b/src/controllers/DataExportController.php
new file mode 100644
index 00000000..d414c517
--- /dev/null
+++ b/src/controllers/DataExportController.php
@@ -0,0 +1,399 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class DataExportController extends BaseController {
+ public $_name = 'DataExportController';
+ public $extensions = [
+ 'sql' => 'sql',
+ 'copy' => 'sql',
+ 'csv' => 'csv',
+ 'tab' => 'txt',
+ 'html' => 'html',
+ 'xml' => 'xml',
+ ];
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+ }
+
+ public function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ // if (!isset($_REQUEST['table']) && !isset($_REQUEST['query']))
+ // What must we do in this case? Maybe redirect to the homepage?
+
+ // If format is set, then perform the export
+ if (isset($_REQUEST['what'])) {
+
+ // Include application functions
+ $misc->setNoOutput(true);
+
+ switch ($_REQUEST['what']) {
+ case 'dataonly':
+ // Check to see if they have pg_dump set up and if they do, use that
+ // instead of custom dump code
+ if ($misc->isDumpEnabled()
+ && ($_REQUEST['d_format'] == 'copy' || $_REQUEST['d_format'] == 'sql')) {
+
+ $dbexport_controller = new \PHPPgAdmin\Controller\DBExportController($this->getContainer());
+ return $dbexport_controller->render();
+
+ } else {
+ $format = $_REQUEST['d_format'];
+ $oids = isset($_REQUEST['d_oids']);
+ }
+ break;
+ case 'structureonly':
+ // Check to see if they have pg_dump set up and if they do, use that
+ // instead of custom dump code
+ if ($misc->isDumpEnabled()) {
+ $dbexport_controller = new \PHPPgAdmin\Controller\DBExportController($this->getContainer());
+ return $dbexport_controller->render();
+ } else {
+ $clean = isset($_REQUEST['s_clean']);
+ }
+
+ break;
+ case 'structureanddata':
+ // Check to see if they have pg_dump set up and if they do, use that
+ // instead of custom dump code
+ if ($misc->isDumpEnabled()) {
+ $dbexport_controller = new \PHPPgAdmin\Controller\DBExportController($this->getContainer());
+ return $dbexport_controller->render();
+ } else {
+ $format = $_REQUEST['sd_format'];
+ $clean = isset($_REQUEST['sd_clean']);
+ $oids = isset($_REQUEST['sd_oids']);
+ }
+ break;
+ }
+
+ // Make it do a download, if necessary
+ if ($_REQUEST['output'] == 'download') {
+ // Set headers. MSIE is totally broken for SSL downloading, so
+ // we need to have it download in-place as plain text
+ if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && isset($_SERVER['HTTPS'])) {
+ header('Content-Type: text/plain');
+ } else {
+ header('Content-Type: application/download');
+
+ if (isset($extensions[$format])) {
+ $ext = $extensions[$format];
+ } else {
+ $ext = 'txt';
+ }
+
+ header('Content-Disposition: attachment; filename=dump.' . $ext);
+ }
+ } else {
+ header('Content-Type: text/plain');
+ }
+
+ if (isset($_REQUEST['query'])) {
+ $_REQUEST['query'] = trim(urldecode($_REQUEST['query']));
+ }
+
+ // Set the schema search path
+ if (isset($_REQUEST['search_path'])) {
+ $data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path'])));
+ }
+
+ // Set up the dump transaction
+ $status = $data->beginDump();
+
+ // If the dump is not dataonly then dump the structure prefix
+ if ($_REQUEST['what'] != 'dataonly') {
+ echo $data->getTableDefPrefix($_REQUEST['table'], $clean);
+ }
+
+ // If the dump is not structureonly then dump the actual data
+ if ($_REQUEST['what'] != 'structureonly') {
+ // Get database encoding
+ $dbEncoding = $data->getDatabaseEncoding();
+
+ // Set fetch mode to NUM so that duplicate field names are properly returned
+ $data->conn->setFetchMode(ADODB_FETCH_NUM);
+
+ // Execute the query, if set, otherwise grab all rows from the table
+ if (isset($_REQUEST['table'])) {
+ $rs = $data->dumpRelation($_REQUEST['table'], $oids);
+ } else {
+ $rs = $data->conn->Execute($_REQUEST['query']);
+ }
+
+ if ($format == 'copy') {
+ $data->fieldClean($_REQUEST['table']);
+ echo "COPY \"{$_REQUEST['table']}\"";
+ if ($oids) {
+ echo " WITH OIDS";
+ }
+
+ echo " FROM stdin;\n";
+ while (!$rs->EOF) {
+ $first = true;
+ while (list($k, $v) = each($rs->fields)) {
+ // Escape value
+ $v = $data->escapeBytea($v);
+
+ // We add an extra escaping slash onto octal encoded characters
+ $v = preg_replace('/\\\\([0-7]{3})/', '\\\\\1', $v);
+ if ($first) {
+ echo (is_null($v)) ? '\\N' : $v;
+ $first = false;
+ } else {
+ echo "\t", (is_null($v)) ? '\\N' : $v;
+ }
+
+ }
+ echo "\n";
+ $rs->moveNext();
+ }
+ echo "\\.\n";
+ } elseif ($format == 'html') {
+ echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n";
+ echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n";
+ echo "<head>\r\n";
+ echo "\t<title></title>\r\n";
+ echo "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n";
+ echo "</head>\r\n";
+ echo "<body>\r\n";
+ echo "<table class=\"phppgadmin\">\r\n";
+ echo "\t<tr>\r\n";
+ if (!$rs->EOF) {
+ // Output header row
+ $j = 0;
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+ if ($finfo->name == $data->id && !$oids) {
+ continue;
+ }
+
+ echo "\t\t<th>", $misc->printVal($finfo->name, true), "</th>\r\n";
+ }
+ }
+ echo "\t</tr>\r\n";
+ while (!$rs->EOF) {
+ echo "\t<tr>\r\n";
+ $j = 0;
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+ if ($finfo->name == $data->id && !$oids) {
+ continue;
+ }
+
+ echo "\t\t<td>", $misc->printVal($v, true, $finfo->type), "</td>\r\n";
+ }
+ echo "\t</tr>\r\n";
+ $rs->moveNext();
+ }
+ echo "</table>\r\n";
+ echo "</body>\r\n";
+ echo "</html>\r\n";
+ } elseif ($format == 'xml') {
+ echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
+ echo "<data>\n";
+ if (!$rs->EOF) {
+ // Output header row
+ $j = 0;
+ echo "\t<header>\n";
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+ $name = htmlspecialchars($finfo->name);
+ $type = htmlspecialchars($finfo->type);
+ echo "\t\t<column name=\"{$name}\" type=\"{$type}\" />\n";
+ }
+ echo "\t</header>\n";
+ }
+ echo "\t<records>\n";
+ while (!$rs->EOF) {
+ $j = 0;
+ echo "\t\t<row>\n";
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+ $name = htmlspecialchars($finfo->name);
+ if (!is_null($v)) {
+ $v = htmlspecialchars($v);
+ }
+
+ echo "\t\t\t<column name=\"{$name}\"", (is_null($v) ? ' null="null"' : ''), ">{$v}</column>\n";
+ }
+ echo "\t\t</row>\n";
+ $rs->moveNext();
+ }
+ echo "\t</records>\n";
+ echo "</data>\n";
+ } elseif ($format == 'sql') {
+ $data->fieldClean($_REQUEST['table']);
+ while (!$rs->EOF) {
+ echo "INSERT INTO \"{$_REQUEST['table']}\" (";
+ $first = true;
+ $j = 0;
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+ $k = $finfo->name;
+ // SQL (INSERT) format cannot handle oids
+ // if ($k == $data->id) continue;
+ // Output field
+ $data->fieldClean($k);
+ if ($first) {
+ echo "\"{$k}\"";
+ } else {
+ echo ", \"{$k}\"";
+ }
+
+ if (!is_null($v)) {
+ // Output value
+ // addCSlashes converts all weird ASCII characters to octal representation,
+ // EXCEPT the 'special' ones like \r \n \t, etc.
+ $v = addCSlashes($v, "\0..\37\177..\377");
+ // We add an extra escaping slash onto octal encoded characters
+ $v = preg_replace('/\\\\([0-7]{3})/', '\\\1', $v);
+ // Finally, escape all apostrophes
+ $v = str_replace("'", "''", $v);
+ }
+ if ($first) {
+ $values = (is_null($v) ? 'NULL' : "'{$v}'");
+ $first = false;
+ } else {
+ $values .= ', ' . ((is_null($v) ? 'NULL' : "'{$v}'"));
+ }
+
+ }
+ echo ") VALUES ({$values});\n";
+ $rs->moveNext();
+ }
+ } else {
+ switch ($format) {
+ case 'tab':
+ $sep = "\t";
+ break;
+ case 'csv':
+ default:
+ $sep = ',';
+ break;
+ }
+ if (!$rs->EOF) {
+ // Output header row
+ $first = true;
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($k);
+ $v = $finfo->name;
+ if (!is_null($v)) {
+ $v = str_replace('"', '""', $v);
+ }
+
+ if ($first) {
+ echo "\"{$v}\"";
+ $first = false;
+ } else {
+ echo "{$sep}\"{$v}\"";
+ }
+
+ }
+ echo "\r\n";
+ }
+ while (!$rs->EOF) {
+ $first = true;
+ foreach ($rs->fields as $k => $v) {
+ if (!is_null($v)) {
+ $v = str_replace('"', '""', $v);
+ }
+
+ if ($first) {
+ echo (is_null($v)) ? "\"\\N\"" : "\"{$v}\"";
+ $first = false;
+ } else {
+ echo is_null($v) ? "{$sep}\"\\N\"" : "{$sep}\"{$v}\"";
+ }
+
+ }
+ echo "\r\n";
+ $rs->moveNext();
+ }
+ }
+ }
+
+ // If the dump is not dataonly then dump the structure suffix
+ if ($_REQUEST['what'] != 'dataonly') {
+ // Set fetch mode back to ASSOC for the table suffix to work
+ $data->conn->setFetchMode(ADODB_FETCH_ASSOC);
+ echo $data->getTableDefSuffix($_REQUEST['table']);
+ }
+
+ // Finish the dump transaction
+ $status = $data->endDump();
+ } else {
+ return $this->doDefault();
+ }
+ }
+
+ public function doDefault($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ if (!isset($_REQUEST['query']) or empty($_REQUEST['query'])) {
+ $_REQUEST['query'] = $_SESSION['sqlquery'];
+ }
+
+ $misc->printHeader($lang['strexport']);
+ $misc->printBody();
+ $misc->printTrail(isset($_REQUEST['subject']) ? $_REQUEST['subject'] : 'database');
+ $misc->printTitle($lang['strexport']);
+ if (isset($msg)) {
+ $misc->printMsg($msg);
+ }
+
+ echo "<form action=\"/src/views/dataexport.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strformat']}:</th><td><select name=\"d_format\">\n";
+ // COPY and SQL require a table
+ if (isset($_REQUEST['table'])) {
+ echo "<option value=\"copy\">COPY</option>\n";
+ echo "<option value=\"sql\">SQL</option>\n";
+ }
+ echo "<option value=\"csv\">CSV</option>\n";
+ echo "<option value=\"tab\">{$lang['strtabbed']}</option>\n";
+ echo "<option value=\"html\">XHTML</option>\n";
+ echo "<option value=\"xml\">XML</option>\n";
+ echo "</select></td></tr>";
+ echo "</table>\n";
+
+ echo "<h3>{$lang['stroptions']}</h3>\n";
+ echo "<p><input type=\"radio\" id=\"output1\" name=\"output\" value=\"show\" checked=\"checked\" /><label for=\"output1\">{$lang['strshow']}</label>\n";
+ echo "<br/><input type=\"radio\" id=\"output2\" name=\"output\" value=\"download\" /><label for=\"output2\">{$lang['strdownload']}</label></p>\n";
+
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"export\" />\n";
+ echo "<input type=\"hidden\" name=\"what\" value=\"dataonly\" />\n";
+ if (isset($_REQUEST['table'])) {
+ echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
+ }
+ echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars(urlencode($_REQUEST['query'])), "\" />\n";
+ if (isset($_REQUEST['search_path'])) {
+ echo "<input type=\"hidden\" name=\"search_path\" value=\"", htmlspecialchars($_REQUEST['search_path']), "\" />\n";
+ }
+ echo $misc->form;
+ echo "<input type=\"submit\" value=\"{$lang['strexport']}\" /></p>\n";
+ echo "</form>\n";
+
+ $misc->printFooter();
+
+ }
+}
diff --git a/src/controllers/DataImportController.php b/src/controllers/DataImportController.php
new file mode 100644
index 00000000..35422112
--- /dev/null
+++ b/src/controllers/DataImportController.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class DataImportController extends BaseController {
+ public $_name = 'DataImportController';
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+ }
+
+ public function render() {
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strimport']);
+ $misc->printTrail('table');
+ $misc->printTabs('table', 'import');
+
+ // Default state for XML parser
+ $state = 'XML';
+ $curr_col_name = null;
+ $curr_col_val = null;
+ $curr_col_null = false;
+ $curr_row = [];
+
+ /**
+ * Character data handler for XML import feature
+ */
+ $_charHandler = function ($parser, $cdata) use (&$state, &$curr_col_val) {
+ if ($state == 'COLUMN') {
+ $curr_col_val .= $cdata;
+ }
+ };
+
+ /**
+ * Open tag handler for XML import feature
+ */
+ $_startElement = function ($parser, $name, $attrs) use ($data, $misc, &$state, &$curr_col_name, &$curr_col_null) {
+
+ switch ($name) {
+ case 'DATA':
+ if ($state != 'XML') {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ $state = 'DATA';
+ break;
+ case 'HEADER':
+ if ($state != 'DATA') {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ $state = 'HEADER';
+ break;
+ case 'RECORDS':
+ if ($state != 'READ_HEADER') {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ $state = 'RECORDS';
+ break;
+ case 'ROW':
+ if ($state != 'RECORDS') {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ $state = 'ROW';
+ $curr_row = [];
+ break;
+ case 'COLUMN':
+ // We handle columns in rows
+ if ($state == 'ROW') {
+ $state = 'COLUMN';
+ $curr_col_name = $attrs['NAME'];
+ $curr_col_null = isset($attrs['NULL']);
+ }
+ // And we ignore columns in headers and fail in any other context
+ elseif ($state != 'HEADER') {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ break;
+ default:
+ // An unrecognised tag means failure
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ };
+
+ /**
+ * Close tag handler for XML import feature
+ */
+ $_endElement = function ($parser, $name) use ($data, $misc, &$state, &$curr_col_name, &$curr_col_null, &$curr_col_val) {
+
+ switch ($name) {
+ case 'DATA':
+ $state = 'READ_DATA';
+ break;
+ case 'HEADER':
+ $state = 'READ_HEADER';
+ break;
+ case 'RECORDS':
+ $state = 'READ_RECORDS';
+ break;
+ case 'ROW':
+ // Build value map in order to insert row into table
+ $fields = [];
+ $vars = [];
+ $nulls = [];
+ $format = [];
+ $types = [];
+ $i = 0;
+ foreach ($curr_row as $k => $v) {
+ $fields[$i] = $k;
+ // Check for nulls
+ if ($v === null) {
+ $nulls[$i] = 'on';
+ }
+
+ // Add to value array
+ $vars[$i] = $v;
+ // Format is always VALUE
+ $format[$i] = 'VALUE';
+ // Type is always text
+ $types[$i] = 'text';
+ $i++;
+ }
+ $status = $data->insertRow($_REQUEST['table'], $fields, $vars, $nulls, $format, $types);
+ if ($status != 0) {
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ $curr_row = [];
+ $state = 'RECORDS';
+ break;
+ case 'COLUMN':
+ $curr_row[$curr_col_name] = ($curr_col_null ? null : $curr_col_val);
+ $curr_col_name = null;
+ $curr_col_val = null;
+ $curr_col_null = false;
+ $state = 'ROW';
+ break;
+ default:
+ // An unrecognised tag means failure
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ };
+
+ // Check that file is specified and is an uploaded file
+ if (isset($_FILES['source']) && is_uploaded_file($_FILES['source']['tmp_name']) && is_readable($_FILES['source']['tmp_name'])) {
+
+ $fd = fopen($_FILES['source']['tmp_name'], 'r');
+ // Check that file was opened successfully
+ if ($fd !== false) {
+ $null_array = self::loadNULLArray();
+ $status = $data->beginTransaction();
+ if ($status != 0) {
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+
+ // If format is set to 'auto', then determine format automatically from file name
+ if ($_REQUEST['format'] == 'auto') {
+ $extension = substr(strrchr($_FILES['source']['name'], '.'), 1);
+ switch ($extension) {
+ case 'csv':
+ $_REQUEST['format'] = 'csv';
+ break;
+ case 'txt':
+ $_REQUEST['format'] = 'tab';
+ break;
+ case 'xml':
+ $_REQUEST['format'] = 'xml';
+ break;
+ default:
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strimporterror-fileformat']);
+ exit;
+ }
+ }
+
+ // Do different import technique depending on file format
+ switch ($_REQUEST['format']) {
+ case 'csv':
+ case 'tab':
+ // XXX: Length of CSV lines limited to 100k
+ $csv_max_line = 100000;
+ // Set delimiter to tabs or commas
+ if ($_REQUEST['format'] == 'csv') {
+ $csv_delimiter = ',';
+ } else {
+ $csv_delimiter = "\t";
+ }
+
+ // Get first line of field names
+ $fields = fgetcsv($fd, $csv_max_line, $csv_delimiter);
+ $row = 2; //We start on the line AFTER the field names
+ while ($line = fgetcsv($fd, $csv_max_line, $csv_delimiter)) {
+ // Build value map
+ $t_fields = [];
+ $vars = [];
+ $nulls = [];
+ $format = [];
+ $types = [];
+ $i = 0;
+ foreach ($fields as $f) {
+ // Check that there is a column
+ if (!isset($line[$i])) {
+ $misc->printMsg(sprintf($lang['strimporterrorline-badcolumnnum'], $row));
+ exit;
+ }
+ $t_fields[$i] = $f;
+
+ // Check for nulls
+ if (self::determineNull($line[$i], $null_array)) {
+ $nulls[$i] = 'on';
+ }
+ // Add to value array
+ $vars[$i] = $line[$i];
+ // Format is always VALUE
+ $format[$i] = 'VALUE';
+ // Type is always text
+ $types[$i] = 'text';
+ $i++;
+ }
+
+ $status = $data->insertRow($_REQUEST['table'], $t_fields, $vars, $nulls, $format, $types);
+ if ($status != 0) {
+ $data->rollbackTransaction();
+ $misc->printMsg(sprintf($lang['strimporterrorline'], $row));
+ exit;
+ }
+ $row++;
+ }
+ break;
+ case 'xml':
+ $parser = xml_parser_create();
+ xml_set_element_handler($parser, $_startElement, $_endElement);
+ xml_set_character_data_handler($parser, $_charHandler);
+
+ while (!feof($fd)) {
+ $line = fgets($fd, 4096);
+ xml_parse($parser, $line);
+ }
+
+ xml_parser_free($parser);
+ break;
+ default:
+ // Unknown type
+ $data->rollbackTransaction();
+ $misc->printMsg($lang['strinvalidparam']);
+ exit;
+ }
+
+ $status = $data->endTransaction();
+ if ($status != 0) {
+ $misc->printMsg($lang['strimporterror']);
+ exit;
+ }
+ fclose($fd);
+
+ $misc->printMsg($lang['strfileimported']);
+ } else {
+ // File could not be opened
+ $misc->printMsg($lang['strimporterror']);
+ }
+ } else {
+ // Upload went wrong
+ $misc->printMsg($lang['strimporterror-uploadedfile']);
+ }
+
+ $misc->printFooter();
+ }
+
+ public static function loadNULLArray() {
+ $array = [];
+ if (isset($_POST['allowednulls'])) {
+ foreach ($_POST['allowednulls'] as $null_char) {
+ $array[] = $null_char;
+ }
+
+ }
+ return $array;
+ }
+
+ public static function determineNull($field, $null_array) {
+ return in_array($field, $null_array);
+ }
+}
diff --git a/src/controllers/DatabaseController.php b/src/controllers/DatabaseController.php
index b3f47e40..9500153b 100644
--- a/src/controllers/DatabaseController.php
+++ b/src/controllers/DatabaseController.php
@@ -8,8 +8,9 @@ use \PHPPgAdmin\Decorators\Decorator;
*/
class DatabaseController extends BaseController {
use AdminTrait;
- public $script = 'database.php';
- public $_name = 'DatabaseController';
+ public $script = 'database.php';
+ public $_name = 'DatabaseController';
+ public $table_place = 'database-variables';
function _highlight($string, $term) {
return str_replace($term, "<b>{$term}</b>", $string);
@@ -50,8 +51,8 @@ class DatabaseController extends BaseController {
$_REQUEST['filter'] = '';
}
- $misc->printTrail('database');
- $misc->printTabs('database', 'find');
+ $this->printTrail('database');
+ $this->printTabs('database', 'find');
$misc->printMsg($msg);
echo "<form action=\"/src/views/database.php\" method=\"post\">\n";
@@ -319,8 +320,8 @@ class DatabaseController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('database');
- $misc->printTabs('database', 'export');
+ $this->printTrail('database');
+ $this->printTabs('database', 'export');
$misc->printMsg($msg);
echo "<form action=\"/src/views/dbexport.php\" method=\"post\">\n";
@@ -376,8 +377,8 @@ class DatabaseController extends BaseController {
// Fetch the variables from the database
$variables = $data->getVariables();
- $misc->printTrail('database');
- $misc->printTabs('database', 'variables');
+ $this->printTrail('database');
+ $this->printTabs('database', 'variables');
$columns = [
'variable' => [
@@ -392,7 +393,7 @@ class DatabaseController extends BaseController {
$actions = [];
- echo $misc->printTable($variables, $columns, $actions, 'database-variables', $lang['strnodata']);
+ echo $this->printTable($variables, $columns, $actions, $this->table_place, $lang['strnodata']);
}
/**
@@ -405,8 +406,8 @@ class DatabaseController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('database');
- $misc->printTabs('database', 'processes');
+ $this->printTrail('database');
+ $this->printTabs('database', 'processes');
$misc->printMsg($msg);
if (strlen($msg) === 0) {
@@ -450,7 +451,7 @@ class DatabaseController extends BaseController {
$actions = [];
- echo $misc->printTable($prep_xacts, $columns, $actions, 'database-processes-preparedxacts', $lang['strnodata']);
+ echo $this->printTable($prep_xacts, $columns, $actions, 'database-processes-preparedxacts', $lang['strnodata']);
}
// Fetch the processes from the database
@@ -529,7 +530,7 @@ class DatabaseController extends BaseController {
unset($columns['actions']);
}
- echo $misc->printTable($processes, $columns, $actions, 'database-processes', $lang['strnodata']);
+ echo $this->printTable($processes, $columns, $actions, 'database-processes', $lang['strnodata']);
if ($isAjax) {
exit;
@@ -583,7 +584,7 @@ class DatabaseController extends BaseController {
}
$actions = [];
- echo $misc->printTable($variables, $columns, $actions, 'database-locks', $lang['strnodata']);
+ echo $this->printTable($variables, $columns, $actions, 'database-locks', $lang['strnodata']);
if ($isAjax) {
exit;
@@ -600,8 +601,8 @@ class DatabaseController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('database');
- $misc->printTabs('database', 'locks');
+ $this->printTrail('database');
+ $this->printTabs('database', 'locks');
echo "<br /><a id=\"control\" href=\"\"><img src=\"" . $misc->icon('Refresh') . "\" alt=\"{$lang['strrefresh']}\" title=\"{$lang['strrefresh']}\"/>&nbsp;{$lang['strrefresh']}</a>";
@@ -624,8 +625,8 @@ class DatabaseController extends BaseController {
$_REQUEST['paginate'] = 'on';
}
- $misc->printTrail('database');
- $misc->printTabs('database', 'sql');
+ $this->printTrail('database');
+ $this->printTabs('database', 'sql');
echo "<p>{$lang['strentersql']}</p>\n";
echo "<form action=\"/src/views/sql.php\" method=\"post\" enctype=\"multipart/form-data\">\n";
echo "<p>{$lang['strsql']}<br />\n";
@@ -651,4 +652,110 @@ class DatabaseController extends BaseController {
// Default focus
$misc->setFocus('forms[0].query');
}
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $data = $misc->getDatabaseAccessor();
+
+ $reqvars = $misc->getRequestVars('database');
+
+ $tabs = $misc->getNavTabs('database');
+
+ $items = $misc->adjustTabsForTree($tabs);
+ //\PC::debug($reqvars, 'reqvars');
+ $attrs = [
+ 'text' => Decorator::field('title'),
+ 'icon' => Decorator::field('icon'),
+ 'action' => Decorator::actionurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars', [])),
+ 'branch' => Decorator::url(Decorator::field('url'), $reqvars, Decorator::field('urlvars'), ['action' => 'tree']),
+ ];
+
+ return $misc->printTree($items, $attrs, 'database');
+
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ if ($action == 'refresh_locks') {
+ return $this->currentLocks(true);
+ }
+
+ if ($action == 'refresh_processes') {
+ return $this->currentProcesses(true);
+ }
+ $scripts = '';
+ /* normal flow */
+ if ($action == 'locks' || $action == 'processes') {
+ $scripts .= "<script src=\"/js/database.js\" type=\"text/javascript\"></script>";
+
+ $refreshTime = $conf['ajax_refresh'] * 1000;
+
+ $scripts .= "<script type=\"text/javascript\">\n";
+ $scripts .= "var Database = {\n";
+ $scripts .= "ajax_time_refresh: {$refreshTime},\n";
+ $scripts .= "str_start: {text:'{$lang['strstart']}',icon: '" . $misc->icon('Execute') . "'},\n";
+ $scripts .= "str_stop: {text:'{$lang['strstop']}',icon: '" . $misc->icon('Stop') . "'},\n";
+ $scripts .= "load_icon: '" . $misc->icon('Loading') . "',\n";
+ $scripts .= "server:'{$_REQUEST['server']}',\n";
+ $scripts .= "dbname:'{$_REQUEST['database']}',\n";
+ $scripts .= "action:'refresh_{$action}',\n";
+ $scripts .= "errmsg: '" . str_replace("'", "\'", $lang['strconnectionfail']) . "'\n";
+ $scripts .= "};\n";
+ $scripts .= "</script>\n";
+ }
+
+ $misc->printHeader($lang['strdatabase'], $scripts);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'find':
+ if (isset($_REQUEST['term'])) {
+ $this->doFind(false);
+ } else {
+ $this->doFind(true);
+ }
+
+ break;
+ case 'sql':
+ $this->doSQL();
+ break;
+ case 'variables':
+ $this->doVariables();
+ break;
+ case 'processes':
+ $this->doProcesses();
+ break;
+ case 'locks':
+ $this->doLocks();
+ break;
+ case 'export':
+ $this->doExport();
+ break;
+ case 'signal':
+ $this->doSignal();
+ break;
+ default:
+ if ($this->adminActions($action, 'database') === false) {
+ $this->doSQL();
+ }
+
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
}
diff --git a/src/controllers/DisplayController.php b/src/controllers/DisplayController.php
new file mode 100644
index 00000000..11d3292e
--- /dev/null
+++ b/src/controllers/DisplayController.php
@@ -0,0 +1,1007 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class DisplayController extends BaseController {
+ public $_name = 'DisplayController';
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+ }
+
+ /**
+ * Show confirmation of edit and perform actual update
+ */
+ function doEditRow($confirm, $msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (is_array($_REQUEST['key'])) {
+ $key = $_REQUEST['key'];
+ } else {
+ $key = unserialize(urldecode($_REQUEST['key']));
+ }
+
+ if ($confirm) {
+ $this->printTrail($_REQUEST['subject']);
+ $misc->printTitle($lang['streditrow']);
+ $misc->printMsg($msg);
+
+ $attrs = $data->getTableAttributes($_REQUEST['table']);
+ $rs = $data->browseRow($_REQUEST['table'], $key);
+
+ if (($conf['autocomplete'] != 'disable')) {
+ $fksprops = $misc->getAutocompleteFKProperties($_REQUEST['table']);
+ if ($fksprops !== false) {
+ echo $fksprops['code'];
+ }
+
+ } else {
+ $fksprops = false;
+ }
+
+ echo "<form action=\"/src/views/display.php\" method=\"post\" id=\"ac_form\">\n";
+ $elements = 0;
+ $error = true;
+ if ($rs->recordCount() == 1 && $attrs->recordCount() > 0) {
+ echo "<table>\n";
+
+ // Output table header
+ echo "<tr><th class=\"data\">{$lang['strcolumn']}</th><th class=\"data\">{$lang['strtype']}</th>";
+ echo "<th class=\"data\">{$lang['strformat']}</th>\n";
+ echo "<th class=\"data\">{$lang['strnull']}</th><th class=\"data\">{$lang['strvalue']}</th></tr>";
+
+ $i = 0;
+ while (!$attrs->EOF) {
+
+ $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
+ $id = (($i % 2) == 0 ? '1' : '2');
+
+ // Initialise variables
+ if (!isset($_REQUEST['format'][$attrs->fields['attname']])) {
+ $_REQUEST['format'][$attrs->fields['attname']] = 'VALUE';
+ }
+
+ echo "<tr class=\"data{$id}\">\n";
+ echo "<td style=\"white-space:nowrap;\">", $misc->printVal($attrs->fields['attname']), "</td>";
+ echo "<td style=\"white-space:nowrap;\">\n";
+ echo $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
+ echo "<input type=\"hidden\" name=\"types[", htmlspecialchars($attrs->fields['attname']), "]\" value=\"",
+ htmlspecialchars($attrs->fields['type']), "\" /></td>";
+ $elements++;
+ echo "<td style=\"white-space:nowrap;\">\n";
+ echo "<select name=\"format[", htmlspecialchars($attrs->fields['attname']), "]\">\n";
+ echo "<option value=\"VALUE\"", ($_REQUEST['format'][$attrs->fields['attname']] == 'VALUE') ? ' selected="selected"' : '', ">{$lang['strvalue']}</option>\n";
+ echo "<option value=\"EXPRESSION\"", ($_REQUEST['format'][$attrs->fields['attname']] == 'EXPRESSION') ? ' selected="selected"' : '', ">{$lang['strexpression']}</option>\n";
+ echo "</select>\n</td>\n";
+ $elements++;
+ echo "<td style=\"white-space:nowrap;\">";
+ // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
+ if (!$attrs->fields['attnotnull']) {
+ // Set initial null values
+ if ($_REQUEST['action'] == 'confeditrow' && $rs->fields[$attrs->fields['attname']] === null) {
+ $_REQUEST['nulls'][$attrs->fields['attname']] = 'on';
+ }
+ echo "<label><span><input type=\"checkbox\" name=\"nulls[{$attrs->fields['attname']}]\"",
+ isset($_REQUEST['nulls'][$attrs->fields['attname']]) ? ' checked="checked"' : '', " /></span></label></td>\n";
+ $elements++;
+ } else {
+ echo "&nbsp;</td>";
+ }
+
+ echo "<td id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
+
+ $extras = [];
+
+ // If the column allows nulls, then we put a JavaScript action on the data field to unset the
+ // NULL checkbox as soon as anything is entered in the field. We use the $elements variable to
+ // keep track of which element offset we're up to. We can't refer to the null checkbox by name
+ // as it contains '[' and ']' characters.
+ if (!$attrs->fields['attnotnull']) {
+ $extras['onChange'] = 'elements[' . ($elements - 1) . '].checked = false;';
+ }
+
+ if (($fksprops !== false) && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
+ $extras['id'] = "attr_{$attrs->fields['attnum']}";
+ $extras['autocomplete'] = 'off';
+ }
+
+ echo $data->printField("values[{$attrs->fields['attname']}]", $rs->fields[$attrs->fields['attname']], $attrs->fields['type'], $extras);
+
+ echo "</td>";
+ $elements++;
+ echo "</tr>\n";
+ $i++;
+ $attrs->moveNext();
+ }
+ echo "</table>\n";
+
+ $error = false;
+ } elseif ($rs->recordCount() != 1) {
+ echo "<p>{$lang['strrownotunique']}</p>\n";
+ } else {
+ echo "<p>{$lang['strinvalidparam']}</p>\n";
+ }
+
+ echo "<input type=\"hidden\" name=\"action\" value=\"editrow\" />\n";
+ echo $misc->form;
+ if (isset($_REQUEST['table'])) {
+ echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['subject'])) {
+ echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['query'])) {
+ echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars($_REQUEST['query']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['count'])) {
+ echo "<input type=\"hidden\" name=\"count\" value=\"", htmlspecialchars($_REQUEST['count']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['return'])) {
+ echo "<input type=\"hidden\" name=\"return\" value=\"", htmlspecialchars($_REQUEST['return']), "\" />\n";
+ }
+
+ echo "<input type=\"hidden\" name=\"page\" value=\"", htmlspecialchars($_REQUEST['page']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"sortkey\" value=\"", htmlspecialchars($_REQUEST['sortkey']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"sortdir\" value=\"", htmlspecialchars($_REQUEST['sortdir']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"strings\" value=\"", htmlspecialchars($_REQUEST['strings']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"key\" value=\"", htmlspecialchars(urlencode(serialize($key))), "\" />\n";
+ echo "<p>";
+ if (!$error) {
+ echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$lang['strsave']}\" />\n";
+ }
+
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+
+ if ($fksprops !== false) {
+ if ($conf['autocomplete'] != 'default off') {
+ echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
+ } else {
+ echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
+ }
+
+ }
+
+ echo "</p>\n";
+ echo "</form>\n";
+ } else {
+ if (!isset($_POST['values'])) {
+ $_POST['values'] = [];
+ }
+
+ if (!isset($_POST['nulls'])) {
+ $_POST['nulls'] = [];
+ }
+
+ $status = $data->editRow($_POST['table'], $_POST['values'], $_POST['nulls'],
+ $_POST['format'], $_POST['types'], $key);
+ if ($status == 0) {
+ $this->doBrowse($lang['strrowupdated']);
+ } elseif ($status == -2) {
+ $this->doEditRow(true, $lang['strrownotunique']);
+ } else {
+ $this->doEditRow(true, $lang['strrowupdatedbad']);
+ }
+
+ }
+
+ }
+
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
+ function doDelRow($confirm) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if ($confirm) {
+ $this->printTrail($_REQUEST['subject']);
+ $misc->printTitle($lang['strdeleterow']);
+
+ $rs = $data->browseRow($_REQUEST['table'], $_REQUEST['key']);
+
+ echo "<form action=\"/src/views/display.php\" method=\"post\">\n";
+ echo $misc->form;
+
+ if ($rs->recordCount() == 1) {
+ echo "<p>{$lang['strconfdeleterow']}</p>\n";
+
+ $fkinfo = [];
+ echo "<table><tr>";
+ $this->printTableHeaderCells($rs, false, true);
+ echo "</tr>";
+ echo "<tr class=\"data1\">\n";
+ $this->printTableRowCells($rs, $fkinfo, true);
+ echo "</tr>\n";
+ echo "</table>\n";
+ echo "<br />\n";
+
+ echo "<input type=\"hidden\" name=\"action\" value=\"delrow\" />\n";
+ echo "<input type=\"submit\" name=\"yes\" value=\"{$lang['stryes']}\" />\n";
+ echo "<input type=\"submit\" name=\"no\" value=\"{$lang['strno']}\" />\n";
+ } elseif ($rs->recordCount() != 1) {
+ echo "<p>{$lang['strrownotunique']}</p>\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+ } else {
+ echo "<p>{$lang['strinvalidparam']}</p>\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+ }
+ if (isset($_REQUEST['table'])) {
+ echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['subject'])) {
+ echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['query'])) {
+ echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars($_REQUEST['query']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['count'])) {
+ echo "<input type=\"hidden\" name=\"count\" value=\"", htmlspecialchars($_REQUEST['count']), "\" />\n";
+ }
+
+ if (isset($_REQUEST['return'])) {
+ echo "<input type=\"hidden\" name=\"return\" value=\"", htmlspecialchars($_REQUEST['return']), "\" />\n";
+ }
+
+ echo "<input type=\"hidden\" name=\"page\" value=\"", htmlspecialchars($_REQUEST['page']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"sortkey\" value=\"", htmlspecialchars($_REQUEST['sortkey']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"sortdir\" value=\"", htmlspecialchars($_REQUEST['sortdir']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"strings\" value=\"", htmlspecialchars($_REQUEST['strings']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"key\" value=\"", htmlspecialchars(urlencode(serialize($_REQUEST['key']))), "\" />\n";
+ echo "</form>\n";
+ } else {
+ $status = $data->deleteRow($_POST['table'], unserialize(urldecode($_POST['key'])));
+ if ($status == 0) {
+ $this->doBrowse($lang['strrowdeleted']);
+ } elseif ($status == -2) {
+ $this->doBrowse($lang['strrownotunique']);
+ } else {
+ $this->doBrowse($lang['strrowdeletedbad']);
+ }
+
+ }
+
+ }
+
+ /**
+ * build & return the FK information data structure
+ * used when deciding if a field should have a FK link or not
+ * @return [type] [description]
+ */
+ function &getFKInfo() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Get the foreign key(s) information from the current table
+ $fkey_information = ['byconstr' => [], 'byfield' => []];
+
+ if (isset($_REQUEST['table'])) {
+ $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
+ if ($constraints->recordCount() > 0) {
+
+ $fkey_information['common_url'] = $misc->getHREF('schema') . '&amp;subject=table';
+
+ /* build the FK constraints data structure */
+ while (!$constraints->EOF) {
+ $constr = &$constraints->fields;
+ if ($constr['contype'] == 'f') {
+
+ if (!isset($fkey_information['byconstr'][$constr['conid']])) {
+ $fkey_information['byconstr'][$constr['conid']] = [
+ 'url_data' => 'table=' . urlencode($constr['f_table']) . '&amp;schema=' . urlencode($constr['f_schema']),
+ 'fkeys' => [],
+ 'consrc' => $constr['consrc'],
+ ];
+ }
+
+ $fkey_information['byconstr'][$constr['conid']]['fkeys'][$constr['p_field']] = $constr['f_field'];
+
+ if (!isset($fkey_information['byfield'][$constr['p_field']])) {
+ $fkey_information['byfield'][$constr['p_field']] = [];
+ }
+
+ $fkey_information['byfield'][$constr['p_field']][] = $constr['conid'];
+ }
+ $constraints->moveNext();
+ }
+ }
+ }
+
+ return $fkey_information;
+ }
+
+ /**
+ * Print table header cells
+ * @param $args - associative array for sort link parameters
+ *
+ */
+ function printTableHeaderCells(&$rs, $args, $withOid) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $j = 0;
+
+ foreach ($rs->fields as $k => $v) {
+
+ if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
+ $j++;
+ continue;
+ }
+ $finfo = $rs->fetchField($j);
+
+ if ($args === false) {
+ echo "<th class=\"data\">", $misc->printVal($finfo->name), "</th>\n";
+ } else {
+ $args['page'] = $_REQUEST['page'];
+ $args['sortkey'] = $j + 1;
+ // Sort direction opposite to current direction, unless it's currently ''
+ $args['sortdir'] = (
+ $_REQUEST['sortdir'] == 'asc'
+ and $_REQUEST['sortkey'] == ($j + 1)
+ ) ? 'desc' : 'asc';
+
+ $sortLink = http_build_query($args);
+
+ echo "<th class=\"data\"><a href=\"?{$sortLink}\">"
+ , $misc->printVal($finfo->name);
+ if ($_REQUEST['sortkey'] == ($j + 1)) {
+ if ($_REQUEST['sortdir'] == 'asc') {
+ echo '<img src="' . $misc->icon('RaiseArgument') . '" alt="asc">';
+ } else {
+ echo '<img src="' . $misc->icon('LowerArgument') . '" alt="desc">';
+ }
+
+ }
+ echo "</a></th>\n";
+ }
+ $j++;
+ }
+
+ reset($rs->fields);
+ }
+
+ /* Print data-row cells */
+ function printTableRowCells(&$rs, &$fkey_information, $withOid) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $j = 0;
+
+ if (!isset($_REQUEST['strings'])) {
+ $_REQUEST['strings'] = 'collapsed';
+ }
+
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($j++);
+
+ if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
+ continue;
+ } elseif ($v !== null && $v == '') {
+ echo "<td>&nbsp;</td>";
+ } else {
+ echo "<td style=\"white-space:nowrap;\">";
+
+ if (($v !== null) && isset($fkey_information['byfield'][$k])) {
+ foreach ($fkey_information['byfield'][$k] as $conid) {
+
+ $query_params = $fkey_information['byconstr'][$conid]['url_data'];
+
+ foreach ($fkey_information['byconstr'][$conid]['fkeys'] as $p_field => $f_field) {
+ $query_params .= '&amp;' . urlencode("fkey[{$f_field}]") . '=' . urlencode($rs->fields[$p_field]);
+ }
+
+ /* $fkey_information['common_url'] is already urlencoded */
+ $query_params .= '&amp;' . $fkey_information['common_url'];
+ echo "<div style=\"display:inline-block;\">";
+ echo "<a class=\"fk fk_" . htmlentities($conid, ENT_QUOTES, 'UTF-8') . "\" href=\"display.php?{$query_params}\">";
+ echo "<img src=\"" . $misc->icon('ForeignKey') . "\" style=\"vertical-align:middle;\" alt=\"[fk]\" title=\""
+ . htmlentities($fkey_information['byconstr'][$conid]['consrc'], ENT_QUOTES, 'UTF-8')
+ . "\" />";
+ echo "</a>";
+ echo "</div>";
+ }
+ echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed'), 'class' => 'fk_value']);
+ } else {
+ echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed')]);
+ }
+ echo "</td>";
+ }
+ }
+ }
+
+ /* Print the FK row, used in ajax requests */
+ function doBrowseFK() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $ops = [];
+ foreach ($_REQUEST['fkey'] as $x => $y) {
+ $ops[$x] = '=';
+ }
+ $query = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
+ $_REQUEST['query'] = $query;
+
+ $fkinfo = $this->getFKInfo();
+
+ $max_pages = 1;
+ // Retrieve page from query. $max_pages is returned by reference.
+ $rs = $data->browseQuery('SELECT', $_REQUEST['table'], $_REQUEST['query'],
+ null, null, 1, 1, $max_pages);
+
+ echo "<a href=\"\" style=\"display:table-cell;\" class=\"fk_delete\"><img alt=\"[delete]\" src=\"" . $misc->icon('Delete') . "\" /></a>\n";
+ echo "<div style=\"display:table-cell;\">";
+
+ if (is_object($rs) && $rs->recordCount() > 0) {
+ /* we are browsing a referenced table here
+ * we should show OID if show_oids is true
+ * so we give true to withOid in functions bellow
+ */
+
+ echo "<table><tr>";
+ $this->printTableHeaderCells($rs, false, true);
+ echo "</tr>";
+ echo "<tr class=\"data1\">\n";
+ $this->printTableRowCells($rs, $fkinfo, true);
+ echo "</tr>\n";
+ echo "</table>\n";
+ } else {
+ echo $lang['strnodata'];
+ }
+
+ echo "</div>";
+
+ exit;
+ }
+
+ /**
+ * Displays requested data
+ */
+ function doBrowse($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $plugin_manager = $this->plugin_manager;
+ $data = $misc->getDatabaseAccessor();
+
+ $save_history = false;
+ // If current page is not set, default to first page
+ if (!isset($_REQUEST['page'])) {
+ $_REQUEST['page'] = 1;
+ }
+
+ if (!isset($_REQUEST['nohistory'])) {
+ $save_history = true;
+ }
+
+ if (isset($_REQUEST['subject'])) {
+ $subject = $_REQUEST['subject'];
+ if (isset($_REQUEST[$subject])) {
+ $object = $_REQUEST[$subject];
+ }
+
+ } else {
+ $subject = '';
+ }
+
+ $this->printTrail(isset($subject) ? $subject : 'database');
+ $this->printTabs($subject, 'browse');
+
+ /* This code is used when browsing FK in pure-xHTML (without js) */
+ if (isset($_REQUEST['fkey'])) {
+ $ops = [];
+ foreach ($_REQUEST['fkey'] as $x => $y) {
+ $ops[$x] = '=';
+ }
+ $query = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
+ $_REQUEST['query'] = $query;
+ }
+
+ if (isset($object)) {
+ if (isset($_REQUEST['query'])) {
+ $_SESSION['sqlquery'] = $_REQUEST['query'];
+ $misc->printTitle($lang['strselect']);
+ $type = 'SELECT';
+ } else {
+ $type = 'TABLE';
+ }
+ } else {
+ $misc->printTitle($lang['strqueryresults']);
+ /*we comes from sql.php, $_SESSION['sqlquery'] has been set there */
+ $type = 'QUERY';
+ }
+
+ $misc->printMsg($msg);
+
+ // If 'sortkey' is not set, default to ''
+ if (!isset($_REQUEST['sortkey'])) {
+ $_REQUEST['sortkey'] = '';
+ }
+
+ // If 'sortdir' is not set, default to ''
+ if (!isset($_REQUEST['sortdir'])) {
+ $_REQUEST['sortdir'] = '';
+ }
+
+ // If 'strings' is not set, default to collapsed
+ if (!isset($_REQUEST['strings'])) {
+ $_REQUEST['strings'] = 'collapsed';
+ }
+
+ // Fetch unique row identifier, if this is a table browse request.
+ if (isset($object)) {
+ $key = $data->getRowIdentifier($object);
+ } else {
+ $key = [];
+ }
+
+ // Set the schema search path
+ if (isset($_REQUEST['search_path'])) {
+ if ($data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path']))) != 0) {
+ return;
+ }
+ }
+
+ // Retrieve page from query. $max_pages is returned by reference.
+ $rs = $data->browseQuery($type,
+ isset($object) ? $object : null,
+ isset($_SESSION['sqlquery']) ? $_SESSION['sqlquery'] : null,
+ $_REQUEST['sortkey'], $_REQUEST['sortdir'], $_REQUEST['page'],
+ $conf['max_rows'], $max_pages);
+
+ $fkey_information = $this->getFKInfo();
+
+ // Build strings for GETs in array
+ $_gets = [
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ ];
+
+ if (isset($_REQUEST['schema'])) {
+ $_gets['schema'] = $_REQUEST['schema'];
+ }
+
+ if (isset($object)) {
+ $_gets[$subject] = $object;
+ }
+
+ if (isset($subject)) {
+ $_gets['subject'] = $subject;
+ }
+
+ if (isset($_REQUEST['query'])) {
+ $_gets['query'] = $_REQUEST['query'];
+ }
+
+ if (isset($_REQUEST['count'])) {
+ $_gets['count'] = $_REQUEST['count'];
+ }
+
+ if (isset($_REQUEST['return'])) {
+ $_gets['return'] = $_REQUEST['return'];
+ }
+
+ if (isset($_REQUEST['search_path'])) {
+ $_gets['search_path'] = $_REQUEST['search_path'];
+ }
+
+ if (isset($_REQUEST['table'])) {
+ $_gets['table'] = $_REQUEST['table'];
+ }
+
+ if (isset($_REQUEST['sortkey'])) {
+ $_gets['sortkey'] = $_REQUEST['sortkey'];
+ }
+
+ if (isset($_REQUEST['sortdir'])) {
+ $_gets['sortdir'] = $_REQUEST['sortdir'];
+ }
+
+ if (isset($_REQUEST['nohistory'])) {
+ $_gets['nohistory'] = $_REQUEST['nohistory'];
+ }
+
+ $_gets['strings'] = $_REQUEST['strings'];
+
+ if ($save_history && is_object($rs) && ($type == 'QUERY')) //{
+ {
+ $misc->saveScriptHistory($_REQUEST['query']);
+ }
+
+ echo '<form method="POST" action="' . $_SERVER['REQUEST_URI'] . '"><textarea width="90%" name="query" rows="5" cols="100" resizable="true">';
+ if (isset($_REQUEST['query'])) {
+ $query = $_REQUEST['query'];
+ } else {
+ $query = "SELECT * FROM {$_REQUEST['schema']}";
+ if ($_REQUEST['subject'] == 'view') {
+ $query = "{$query}.{$_REQUEST['view']};";
+ } else {
+ $query = "{$query}.{$_REQUEST['table']};";
+ }
+ }
+ //$query = isset($_REQUEST['query'])? $_REQUEST['query'] : "select * from {$_REQUEST['schema']}.{$_REQUEST['table']};";
+ echo $query;
+ echo '</textarea><br><input type="submit"/></form>';
+
+ if (is_object($rs) && $rs->recordCount() > 0) {
+ // Show page navigation
+ $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
+
+ echo "<table id=\"data\">\n<tr>";
+
+ // Check that the key is actually in the result set. This can occur for select
+ // operations where the key fields aren't part of the select. XXX: We should
+ // be able to support this, somehow.
+ foreach ($key as $v) {
+ // If a key column is not found in the record set, then we
+ // can't use the key.
+ if (!in_array($v, array_keys($rs->fields))) {
+ $key = [];
+ break;
+ }
+ }
+
+ $buttons = [
+ 'edit' => [
+ 'content' => $lang['stredit'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => array_merge([
+ 'action' => 'confeditrow',
+ 'strings' => $_REQUEST['strings'],
+ 'page' => $_REQUEST['page'],
+ ], $_gets),
+ ],
+ ],
+ ],
+ 'delete' => [
+ 'content' => $lang['strdelete'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => array_merge([
+ 'action' => 'confdelrow',
+ 'strings' => $_REQUEST['strings'],
+ 'page' => $_REQUEST['page'],
+ ], $_gets),
+ ],
+ ],
+ ],
+ ];
+ $actions = [
+ 'actionbuttons' => &$buttons,
+ 'place' => 'display-browse',
+ ];
+ $plugin_manager->do_hook('actionbuttons', $actions);
+
+ foreach (array_keys($actions['actionbuttons']) as $action) {
+ $actions['actionbuttons'][$action]['attr']['href']['urlvars'] = array_merge(
+ $actions['actionbuttons'][$action]['attr']['href']['urlvars'],
+ $_gets
+ );
+ }
+
+ $edit_params = isset($actions['actionbuttons']['edit']) ?
+ $actions['actionbuttons']['edit'] : [];
+ $delete_params = isset($actions['actionbuttons']['delete']) ?
+ $actions['actionbuttons']['delete'] : [];
+
+ // Display edit and delete actions if we have a key
+ $colspan = count($buttons);
+ if ($colspan > 0 and count($key) > 0) {
+ echo "<th colspan=\"{$colspan}\" class=\"data\">{$lang['stractions']}</th>\n";
+ }
+
+ /* we show OIDs only if we are in TABLE or SELECT type browsing */
+ $this->printTableHeaderCells($rs, $_gets, isset($object));
+
+ echo "</tr>\n";
+
+ $i = 0;
+ reset($rs->fields);
+ while (!$rs->EOF) {
+ $id = (($i % 2) == 0 ? '1' : '2');
+ echo "<tr class=\"data{$id}\">\n";
+ // Display edit and delete links if we have a key
+ if ($colspan > 0 and count($key) > 0) {
+ $keys_array = [];
+ $has_nulls = false;
+ foreach ($key as $v) {
+ if ($rs->fields[$v] === null) {
+ $has_nulls = true;
+ break;
+ }
+ $keys_array["key[{$v}]"] = $rs->fields[$v];
+ }
+ if ($has_nulls) {
+ echo "<td colspan=\"{$colspan}\">&nbsp;</td>\n";
+ } else {
+
+ if (isset($actions['actionbuttons']['edit'])) {
+ $actions['actionbuttons']['edit'] = $edit_params;
+ $actions['actionbuttons']['edit']['attr']['href']['urlvars'] = array_merge(
+ $actions['actionbuttons']['edit']['attr']['href']['urlvars'],
+ $keys_array
+ );
+ }
+
+ if (isset($actions['actionbuttons']['delete'])) {
+ $actions['actionbuttons']['delete'] = $delete_params;
+ $actions['actionbuttons']['delete']['attr']['href']['urlvars'] = array_merge(
+ $actions['actionbuttons']['delete']['attr']['href']['urlvars'],
+ $keys_array
+ );
+ }
+
+ foreach ($actions['actionbuttons'] as $action) {
+ echo "<td class=\"opbutton{$id}\">";
+ $this->printLink($action);
+ echo "</td>\n";
+ }
+ }
+ }
+
+ $this->printTableRowCells($rs, $fkey_information, isset($object));
+
+ echo "</tr>\n";
+ $rs->moveNext();
+ $i++;
+ }
+ echo "</table>\n";
+
+ echo "<p>", $rs->recordCount(), " {$lang['strrows']}</p>\n";
+ // Show page navigation
+ $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
+ } else {
+ echo "<p>{$lang['strnodata']}</p>\n";
+ }
+
+ // Navigation links
+ $navlinks = [];
+
+ $fields = [
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ ];
+
+ if (isset($_REQUEST['schema'])) {
+ $fields['schema'] = $_REQUEST['schema'];
+ }
+
+ // Return
+ if (isset($_REQUEST['return'])) {
+ $urlvars = $misc->getSubjectParams($_REQUEST['return']);
+
+ $navlinks['back'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => $urlvars['url'],
+ 'urlvars' => $urlvars['params'],
+ ],
+ ],
+ 'content' => $lang['strback'],
+ ];
+ }
+
+ // Edit SQL link
+ if ($type == 'QUERY') {
+ $navlinks['edit'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'database.php',
+ 'urlvars' => array_merge($fields, [
+ 'action' => 'sql',
+ 'paginate' => 'on',
+ ]),
+ ],
+ ],
+ 'content' => $lang['streditsql'],
+ ];
+ }
+
+ // Expand/Collapse
+ if ($_REQUEST['strings'] == 'expanded') {
+ $navlinks['collapse'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => array_merge(
+ $_gets,
+ [
+ 'strings' => 'collapsed',
+ 'page' => $_REQUEST['page'],
+ ]),
+ ],
+ ],
+ 'content' => $lang['strcollapse'],
+ ];
+ } else {
+ $navlinks['collapse'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => array_merge(
+ $_gets,
+ [
+ 'strings' => 'expanded',
+ 'page' => $_REQUEST['page'],
+ ]),
+ ],
+ ],
+ 'content' => $lang['strexpand'],
+ ];
+ }
+
+ // Create view and download
+ if (isset($_REQUEST['query']) && isset($rs) && is_object($rs) && $rs->recordCount() > 0) {
+
+ // Report views don't set a schema, so we need to disable create view in that case
+ if (isset($_REQUEST['schema'])) {
+
+ $navlinks['createview'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'views.php',
+ 'urlvars' => array_merge($fields, [
+ 'action' => 'create',
+ 'formDefinition' => $_REQUEST['query'],
+ ]),
+ ],
+ ],
+ 'content' => $lang['strcreateview'],
+ ];
+ }
+
+ $urlvars = [];
+ if (isset($_REQUEST['search_path'])) {
+ $urlvars['search_path'] = $_REQUEST['search_path'];
+ }
+
+ $navlinks['download'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'dataexport.php',
+ 'urlvars' => array_merge($fields, $urlvars),
+ ],
+ ],
+ 'content' => $lang['strdownload'],
+ ];
+ }
+
+ // Insert
+ if (isset($object) && (isset($subject) && $subject == 'table')) {
+ $navlinks['insert'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => array_merge($fields, [
+ 'action' => 'confinsertrow',
+ 'table' => $object,
+ ]),
+ ],
+ ],
+ 'content' => $lang['strinsert'],
+ ];
+ }
+
+ // Refresh
+ $navlinks['refresh'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => array_merge(
+ $_gets,
+ [
+ 'strings' => $_REQUEST['strings'],
+ 'page' => $_REQUEST['page'],
+ ]),
+ ],
+ ],
+ 'content' => $lang['strrefresh'],
+ ];
+
+ $this->printNavLinks($navlinks, 'display-browse', get_defined_vars());
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $plugin_manager = $this->plugin_manager;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ /* shortcuts: this function exit the script for ajax purpose */
+ if ($action == 'dobrowsefk') {
+ $this->doBrowseFK();
+ }
+
+ $scripts = "<script src=\"/js/display.js\" type=\"text/javascript\"></script>";
+
+ $scripts .= "<script type=\"text/javascript\">\n";
+ $scripts .= "var Display = {\n";
+ $scripts .= "errmsg: '" . str_replace("'", "\'", $lang['strconnectionfail']) . "'\n";
+ $scripts .= "};\n";
+ $scripts .= "</script>\n";
+
+ // Set the title based on the subject of the request
+ if (isset($_REQUEST['subject']) && isset($_REQUEST[$_REQUEST['subject']])) {
+ if ($_REQUEST['subject'] == 'table') {
+ $misc->printHeader(
+ $lang['strtables'] . ': ' . $_REQUEST[$_REQUEST['subject']],
+ $scripts
+ );
+ } else if ($_REQUEST['subject'] == 'view') {
+ $misc->printHeader(
+ $lang['strviews'] . ': ' . $_REQUEST[$_REQUEST['subject']],
+ $scripts
+ );
+ } else if ($_REQUEST['subject'] == 'column') {
+ $misc->printHeader(
+ $lang['strcolumn'] . ': ' . $_REQUEST[$_REQUEST['subject']],
+ $scripts
+ );
+ }
+ } else {
+ $misc->printHeader($lang['strqueryresults']);
+ }
+
+ $misc->printBody();
+
+ switch ($action) {
+ case 'editrow':
+ if (isset($_POST['save'])) {
+ $this->doEditRow(false);
+ } else {
+ $this->doBrowse();
+ }
+
+ break;
+ case 'confeditrow':
+ $this->doEditRow(true);
+ break;
+ case 'delrow':
+ if (isset($_POST['yes'])) {
+ $this->doDelRow(false);
+ } else {
+ $this->doBrowse();
+ }
+
+ break;
+ case 'confdelrow':
+ $this->doDelRow(true);
+ break;
+ default:
+ $this->doBrowse();
+ break;
+ }
+
+ $misc->printFooter();
+ }
+}
diff --git a/src/controllers/DomainController.php b/src/controllers/DomainController.php
index 22f8a841..5da54b4b 100644
--- a/src/controllers/DomainController.php
+++ b/src/controllers/DomainController.php
@@ -9,6 +9,116 @@ use \PHPPgAdmin\Decorators\Decorator;
class DomainController extends BaseController {
public $_name = 'DomainController';
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strdomains']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'add_check':
+ $this->addCheck(true);
+ break;
+ case 'save_add_check':
+ if (isset($_POST['cancel'])) {
+ $this->doProperties();
+ } else {
+ $this->addCheck(false);
+ }
+
+ break;
+ case 'drop_con':
+ if (isset($_POST['drop'])) {
+ $this->doDropConstraint(false);
+ } else {
+ $this->doProperties();
+ }
+
+ break;
+ case 'confirm_drop_con':
+ $this->doDropConstraint(true);
+ break;
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'save_alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doProperties();
+ }
+
+ break;
+ case 'alter':
+ $this->doAlter();
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+ }
+
+/**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $domains = $data->getDomains();
+
+ $reqvars = $misc->getRequestVars('domain');
+
+ $attrs = [
+ 'text' => Decorator::field('domname'),
+ 'icon' => 'Domain',
+ 'toolTip' => Decorator::field('domcomment'),
+ 'action' => Decorator::actionurl('domains.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'domain' => Decorator::field('domname'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($domains, $attrs, 'domains');
+ }
+
/**
* Function to save after altering a domain
*/
@@ -37,7 +147,7 @@ class DomainController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('domain');
+ $this->printTrail('domain');
$misc->printTitle($lang['stralter'], 'pg.domain.alter');
$misc->printMsg($msg);
@@ -110,7 +220,7 @@ class DomainController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('domain');
+ $this->printTrail('domain');
$misc->printTitle($lang['straddcheck'], 'pg.constraint.check');
$misc->printMsg($msg);
@@ -159,7 +269,7 @@ class DomainController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('domain');
+ $this->printTrail('domain');
$misc->printTitle($lang['strdrop'], 'pg.constraint.drop');
$misc->printMsg($msg);
@@ -195,7 +305,7 @@ class DomainController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('domain');
+ $this->printTrail('domain');
$misc->printTitle($lang['strproperties'], 'pg.domain');
$misc->printMsg($msg);
@@ -258,7 +368,7 @@ class DomainController extends BaseController {
],
];
- echo $misc->printTable($domaincons, $columns, $actions, 'domains-properties', $lang['strnodata']);
+ echo $this->printTable($domaincons, $columns, $actions, 'domains-properties', $lang['strnodata']);
}
} else {
echo "<p>{$lang['strnodata']}</p>\n";
@@ -314,7 +424,7 @@ class DomainController extends BaseController {
];
}
- $misc->printNavLinks($navlinks, 'domains-properties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'domains-properties', get_defined_vars());
}
/**
@@ -327,7 +437,7 @@ class DomainController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('domain');
+ $this->printTrail('domain');
$misc->printTitle($lang['strdrop'], 'pg.domain.drop');
echo "<p>", sprintf($lang['strconfdropdomain'], $misc->printVal($_REQUEST['domain'])), "</p>\n";
@@ -386,7 +496,7 @@ class DomainController extends BaseController {
$types = $data->getTypes(true);
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatedomain'], 'pg.domain.create');
$misc->printMsg($msg);
@@ -472,8 +582,8 @@ class DomainController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'domains');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'domains');
$misc->printMsg($msg);
$domains = $data->getDomains();
@@ -543,7 +653,7 @@ class DomainController extends BaseController {
unset($actions['alter']);
}
- echo $misc->printTable($domains, $columns, $actions, 'domains-domains', $lang['strnodomains']);
+ echo $this->printTable($domains, $columns, $actions, 'domains-domains', $lang['strnodomains']);
$navlinks = [
'create' => [
@@ -561,6 +671,6 @@ class DomainController extends BaseController {
'content' => $lang['strcreatedomain'],
],
];
- $misc->printNavLinks($navlinks, 'domains-domains', get_defined_vars());
+ $this->printNavLinks($navlinks, 'domains-domains', get_defined_vars());
}
}
diff --git a/src/controllers/FulltextController.php b/src/controllers/FulltextController.php
index 0649166f..d3c74b9d 100644
--- a/src/controllers/FulltextController.php
+++ b/src/controllers/FulltextController.php
@@ -9,15 +9,214 @@ use \PHPPgAdmin\Decorators\Decorator;
class FulltextController extends BaseController {
public $_name = 'FulltextController';
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ } else if ($action == 'subtree') {
+ return $this->doSubTree($_REQUEST['what']);
+ }
+
+ $misc->printHeader($lang['strschemas']);
+ $misc->printBody();
+
+ if (isset($_POST['cancel'])) {
+ if (isset($_POST['prev_action'])) {
+ $action = $_POST['prev_action'];
+ } else {
+ $action = '';
+ }
+ }
+
+ switch ($action) {
+ case 'createconfig':
+ if (isset($_POST['create'])) {
+ $this->doSaveCreateConfig();
+ } else {
+ $this->doCreateConfig();
+ }
+
+ break;
+ case 'alterconfig':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlterConfig();
+ } else {
+ $this->doAlterConfig();
+ }
+
+ break;
+ case 'dropconfig':
+ if (isset($_POST['drop'])) {
+ $this->doDropConfig(false);
+ } else {
+ $this->doDropConfig(true);
+ }
+
+ break;
+ case 'viewconfig':
+ $this->doViewConfig($_REQUEST['ftscfg']);
+ break;
+ case 'viewparsers':
+ $this->doViewParsers();
+ break;
+ case 'viewdicts':
+ $this->doViewDicts();
+ break;
+ case 'createdict':
+ if (isset($_POST['create'])) {
+ $this->doSaveCreateDict();
+ } else {
+ doCreateDict();
+ }
+
+ break;
+ case 'alterdict':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlterDict();
+ } else {
+ $this->doAlterDict();
+ }
+
+ break;
+ case 'dropdict':
+ if (isset($_POST['drop'])) {
+ $this->doDropDict(false);
+ } else {
+ $this->doDropDict(true);
+ }
+
+ break;
+ case 'dropmapping':
+ if (isset($_POST['drop'])) {
+ $this->doDropMapping(false);
+ } else {
+ $this->doDropMapping(true);
+ }
+
+ break;
+ case 'altermapping':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlterMapping();
+ } else {
+ $this->doAlterMapping();
+ }
+
+ break;
+ case 'addmapping':
+ if (isset($_POST['add'])) {
+ $this->doSaveAddMapping();
+ } else {
+ $this->doAddMapping();
+ }
+
+ break;
+
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ /**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $tabs = $misc->getNavTabs('fulltext');
+ $items = $misc->adjustTabsForTree($tabs);
+
+ $reqvars = $misc->getRequestVars('ftscfg');
+
+ $attrs = [
+ 'text' => Decorator::field('title'),
+ 'icon' => Decorator::field('icon'),
+ 'action' => Decorator::actionurl('fulltext.php',
+ $reqvars,
+ field('urlvars')
+ ),
+ 'branch' => Decorator::url('fulltext.php',
+ $reqvars,
+ [
+ 'action' => 'subtree',
+ 'what' => Decorator::field('icon'), // IZ: yeah, it's ugly, but I do not want to change navigation tabs arrays
+ ]
+ ),
+ ];
+
+ return $misc->printTree($items, $attrs, 'fts');
+ }
+
+ function doSubTree($what) {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ switch ($what) {
+ case 'FtsCfg':
+ $items = $data->getFtsConfigurations(false);
+ $urlvars = ['action' => 'viewconfig', 'ftscfg' => Decorator::field('name')];
+ break;
+ case 'FtsDict':
+ $items = $data->getFtsDictionaries(false);
+ $urlvars = ['action' => 'viewdicts'];
+ break;
+ case 'FtsParser':
+ $items = $data->getFtsParsers(false);
+ $urlvars = ['action' => 'viewparsers'];
+ break;
+ default:
+ return;
+ }
+
+ $reqvars = $misc->getRequestVars('ftscfg');
+
+ $attrs = [
+ 'text' => Decorator::field('name'),
+ 'icon' => $what,
+ 'toolTip' => Decorator::field('comment'),
+ 'action' => Decorator::actionurl('fulltext.php',
+ $reqvars,
+ $urlvars
+ ),
+ 'branch' => Decorator::ifempty(Decorator::field('branch'),
+ '',
+ url('fulltext.php',
+ $reqvars,
+ [
+ 'action' => 'subtree',
+ 'ftscfg' => Decorator::field('name'),
+ ]
+ )
+ ),
+ ];
+
+ return $misc->printTree($items, $attrs, strtolower($what));
+
+ }
+
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'fulltext');
- $misc->printTabs('fulltext', 'ftsconfigs');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'fulltext');
+ $this->printTabs('fulltext', 'ftsconfigs');
$misc->printMsg($msg);
$cfgs = $data->getFtsConfigurations(false);
@@ -69,7 +268,7 @@ class FulltextController extends BaseController {
],
];
- echo $misc->printTable($cfgs, $columns, $actions, 'fulltext-fulltext', $lang['strftsnoconfigs']);
+ echo $this->printTable($cfgs, $columns, $actions, 'fulltext-fulltext', $lang['strftsnoconfigs']);
$navlinks = [
'createconf' => [
@@ -88,7 +287,7 @@ class FulltextController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'fulltext-fulltext', get_defined_vars());
+ $this->printNavLinks($navlinks, 'fulltext-fulltext', get_defined_vars());
}
public function doDropConfig($confirm) {
@@ -98,7 +297,7 @@ class FulltextController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('ftscfg');
+ $this->printTrail('ftscfg');
$misc->printTitle($lang['strdrop'], 'pg.ftscfg.drop');
echo "<p>", sprintf($lang['strconfdropftsconfig'], $misc->printVal($_REQUEST['ftscfg'])), "</p>\n";
@@ -132,7 +331,7 @@ class FulltextController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('ftscfg'); // TODO: change to smth related to dictionary
+ $this->printTrail('ftscfg'); // TODO: change to smth related to dictionary
$misc->printTitle($lang['strdrop'], 'pg.ftsdict.drop');
echo "<p>", sprintf($lang['strconfdropftsdict'], $misc->printVal($_REQUEST['ftsdict'])), "</p>\n";
@@ -199,7 +398,7 @@ class FulltextController extends BaseController {
// Fetch all FTS parsers from the database
$ftsparsers = $data->getFtsParsers();
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strftscreateconfig'], 'pg.ftscfg.create');
$misc->printMsg($msg);
@@ -324,7 +523,7 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('ftscfg');
+ $this->printTrail('ftscfg');
$misc->printTitle($lang['stralter'], 'pg.ftscfg.alter');
$misc->printMsg($msg);
@@ -403,9 +602,9 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'fulltext');
- $misc->printTabs('fulltext', 'ftsparsers');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'fulltext');
+ $this->printTabs('fulltext', 'ftsparsers');
$misc->printMsg($msg);
$parsers = $data->getFtsParsers(false);
@@ -427,7 +626,7 @@ class FulltextController extends BaseController {
$actions = [];
- echo $misc->printTable($parsers, $columns, $actions, 'fulltext-viewparsers', $lang['strftsnoparsers']);
+ echo $this->printTable($parsers, $columns, $actions, 'fulltext-viewparsers', $lang['strftsnoparsers']);
//TODO: navlink to "create parser"
}
@@ -441,9 +640,9 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'fulltext');
- $misc->printTabs('fulltext', 'ftsdicts');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'fulltext');
+ $this->printTabs('fulltext', 'ftsdicts');
$misc->printMsg($msg);
$dicts = $data->getFtsDictionaries(false);
@@ -493,7 +692,7 @@ class FulltextController extends BaseController {
],
];
- echo $misc->printTable($dicts, $columns, $actions, 'fulltext-viewdicts', $lang['strftsnodicts']);
+ echo $this->printTable($dicts, $columns, $actions, 'fulltext-viewdicts', $lang['strftsnodicts']);
$navlinks = [
'createdict' => [
@@ -512,7 +711,7 @@ class FulltextController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'fulltext-viewdicts', get_defined_vars());
+ $this->printNavLinks($navlinks, 'fulltext-viewdicts', get_defined_vars());
}
/**
@@ -524,9 +723,9 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('ftscfg');
- $misc->printTabs('schema', 'fulltext');
- $misc->printTabs('fulltext', 'ftsconfigs');
+ $this->printTrail('ftscfg');
+ $this->printTabs('schema', 'fulltext');
+ $this->printTabs('fulltext', 'ftsconfigs');
$misc->printMsg($msg);
echo "<h3>{$lang['strftsconfigmap']}</h3>\n";
@@ -588,7 +787,7 @@ class FulltextController extends BaseController {
];
- echo $misc->printTable($map, $columns, $actions, 'fulltext-viewconfig', $lang['strftsemptymap']);
+ echo $this->printTable($map, $columns, $actions, 'fulltext-viewconfig', $lang['strftsemptymap']);
$navlinks = [
'addmapping' => [
@@ -608,7 +807,7 @@ class FulltextController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'fulltext-viewconfig', get_defined_vars());
+ $this->printNavLinks($navlinks, 'fulltext-viewconfig', get_defined_vars());
}
/**
@@ -655,7 +854,7 @@ class FulltextController extends BaseController {
// Fetch all FTS dictionaries from the database
$ftstpls = $data->getFtsDictionaryTemplates();
- $misc->printTrail('schema');
+ $this->printTrail('schema');
// TODO: create doc links
$misc->printTitle($lang['strftscreatedict'], 'pg.ftsdict.create');
$misc->printMsg($msg);
@@ -796,7 +995,7 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('ftscfg'); // TODO: change to smth related to dictionary
+ $this->printTrail('ftscfg'); // TODO: change to smth related to dictionary
$misc->printTitle($lang['stralter'], 'pg.ftsdict.alter');
$misc->printMsg($msg);
@@ -881,7 +1080,7 @@ class FulltextController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('ftscfg'); // TODO: proper breadcrumbs
+ $this->printTrail('ftscfg'); // TODO: proper breadcrumbs
$misc->printTitle($lang['strdrop'], 'pg.ftscfg.alter');
echo "<form action=\"/src/views/fulltext.php\" method=\"post\">\n";
@@ -932,7 +1131,7 @@ class FulltextController extends BaseController {
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('ftscfg');
+ $this->printTrail('ftscfg');
$misc->printTitle($lang['stralter'], 'pg.ftscfg.alter');
$misc->printMsg($msg);
@@ -1034,7 +1233,7 @@ class FulltextController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('ftscfg');
+ $this->printTrail('ftscfg');
$misc->printTitle($lang['stralter'], 'pg.ftscfg.alter');
$misc->printMsg($msg);
diff --git a/src/controllers/FunctionController.php b/src/controllers/FunctionController.php
index 0100aef6..be32e03d 100644
--- a/src/controllers/FunctionController.php
+++ b/src/controllers/FunctionController.php
@@ -7,11 +7,105 @@ use \PHPPgAdmin\Decorators\Decorator;
* Base controller class
*/
class FunctionController extends BaseController {
- public $_name = 'FunctionController';
+ public $_name = 'FunctionController';
+ public $table_place = 'functions-functions';
-/**
- * Function to save after editing a function
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strfunctions'], null, true, 'datatables_header.twig');
+ $misc->printBody();
+
+ switch ($action) {
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'save_edit':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveEdit();
+ }
+
+ break;
+ case 'edit':
+ $this->doEdit();
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
+ /**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $funcs = $data->getFunctions();
+
+ $proto = Decorator::concat(Decorator::field('proname'), ' (', Decorator::field('proarguments'), ')');
+
+ $reqvars = $misc->getRequestVars('function');
+
+ $attrs = [
+ 'text' => $proto,
+ 'icon' => 'Function',
+ 'toolTip' => Decorator::field('procomment'),
+ 'action' => Decorator::redirecturl('redirect.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'function' => $proto,
+ 'function_oid' => Decorator::field('prooid'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($funcs, $attrs, 'functions');
+ }
+
+ /**
+ * Function to save after editing a function
+ */
public function doSaveEdit() {
$conf = $this->conf;
@@ -54,16 +148,16 @@ class FunctionController extends BaseController {
}
}
-/**
- * Function to allow editing of a Function
- */
+ /**
+ * Function to allow editing of a Function
+ */
public function doEdit($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('function');
+ $this->printTrail('function');
$misc->printTitle($lang['stralter'], 'pg.function.alter');
$misc->printMsg($msg);
@@ -290,22 +384,23 @@ class FunctionController extends BaseController {
}
-/**
- * Show read only properties of a function
- */
+ /**
+ * Show read only properties of a function
+ */
public function doProperties($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('function');
+ $this->printTrail('function');
$misc->printTitle($lang['strproperties'], 'pg.function');
$misc->printMsg($msg);
$funcdata = $data->getFunction($_REQUEST['function_oid']);
if ($funcdata->recordCount() > 0) {
+
// Deal with named parameters
if ($data->hasNamedParams()) {
if (isset($funcdata->fields['proallarguments'])) {
@@ -324,15 +419,20 @@ class FunctionController extends BaseController {
if (isset($modes_arr[$i])) {
switch ($modes_arr[$i]) {
- case 'i':$args .= " IN ";
+ case 'i':
+ $args .= " IN ";
break;
- case 'o':$args .= " OUT ";
+ case 'o':
+ $args .= " OUT ";
break;
- case 'b':$args .= " INOUT ";
+ case 'b':
+ $args .= " INOUT ";
break;
- case 'v':$args .= " VARIADIC ";
+ case 'v':
+ $args .= " VARIADIC ";
break;
- case 't':$args .= " TABLE ";
+ case 't':
+ $args .= " TABLE ";
break;
}
}
@@ -378,11 +478,13 @@ class FunctionController extends BaseController {
echo "<tr><th class=\"data\" colspan=\"4\">{$lang['strlinksymbol']}</th></tr>\n";
echo "<tr><td class=\"data1\" colspan=\"4\">", $misc->printVal($funcdata->fields['prosrc']), "</td></tr>\n";
} else {
- include_once BASE_PATH . '/src/highlight.php';
+ $highlight = new \PHPPgAdmin\Highlight();
+
echo "<tr><th class=\"data\" colspan=\"4\">{$lang['strdefinition']}</th></tr>\n";
// Check to see if we have syntax highlighting for this language
- if (isset($data->langmap[$funcdata->fields['prolanguage']])) {
- $temp = syntax_highlight(htmlspecialchars($funcdata->fields['prosrc']), $data->langmap[$funcdata->fields['prolanguage']]);
+ if (array_key_exists($fnlang, $data->langmap)) {
+
+ $temp = $highlight->syntax_highlight(htmlspecialchars($funcdata->fields['prosrc']), $data->langmap[$fnlang]);
$tag = 'prenoescape';
} else {
$temp = $funcdata->fields['prosrc'];
@@ -465,12 +567,12 @@ class FunctionController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'functions-properties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'functions-properties', get_defined_vars());
}
-/**
- * Show confirmation of drop and perform actual drop
- */
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
public function doDrop($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -483,7 +585,7 @@ class FunctionController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strdrop'], 'pg.function.drop');
echo "<form action=\"/src/views/functions.php\" method=\"post\">\n";
@@ -546,16 +648,16 @@ class FunctionController extends BaseController {
}
-/**
- * Displays a screen where they can enter a new function
- */
+ /**
+ * Displays a screen where they can enter a new function
+ */
public function doCreate($msg = '', $szJS = "") {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
+ $this->printTrail('schema');
if (!isset($_POST['formFunction'])) {
$_POST['formFunction'] = '';
}
@@ -813,9 +915,9 @@ class FunctionController extends BaseController {
echo $szJS;
}
-/**
- * Actually creates the new function in the database
- */
+ /**
+ * Actually creates the new function in the database
+ */
public function doSaveCreate() {
$conf = $this->conf;
$misc = $this->misc;
@@ -941,17 +1043,17 @@ class FunctionController extends BaseController {
return $szTypes . $szModes;
}
-/**
- * Show default list of functions in the database
- */
+ /**
+ * Show default list of functions in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'functions');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'functions');
$misc->printMsg($msg);
$funcs = $data->getFunctions();
@@ -1031,7 +1133,7 @@ class FunctionController extends BaseController {
],
];
- echo $misc->printTable($funcs, $columns, $actions, 'functions-functions', $lang['strnofunctions']);
+ echo $this->printTable($funcs, $columns, $actions, $this->table_place, $lang['strnofunctions']);
$navlinks = [
'createpl' => [
@@ -1080,6 +1182,9 @@ class FunctionController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'functions-functions', get_defined_vars());
+ $this->printNavLinks($navlinks, 'functions-functions', get_defined_vars());
+
+ echo $this->view->fetch('table_list_footer.twig', ['table_class' => $this->table_place]);
}
+
}
diff --git a/src/controllers/GroupController.php b/src/controllers/GroupController.php
index 9895fca0..b91f4a55 100644
--- a/src/controllers/GroupController.php
+++ b/src/controllers/GroupController.php
@@ -37,7 +37,7 @@ class GroupController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('group');
+ $this->printTrail('group');
$misc->printTitle($lang['strdropmember'], 'pg.group.alter');
echo "<p>", sprintf($lang['strconfdropmember'], $misc->printVal($_REQUEST['user']), $misc->printVal($_REQUEST['group'])), "</p>\n";
@@ -74,7 +74,7 @@ class GroupController extends BaseController {
$_POST['user'] = '';
}
- $misc->printTrail('group');
+ $this->printTrail('group');
$misc->printTitle($lang['strproperties'], 'pg.group');
$misc->printMsg($msg);
@@ -108,7 +108,7 @@ class GroupController extends BaseController {
],
];
- echo $misc->printTable($groupdata, $columns, $actions, 'groups-members', $lang['strnousers']);
+ echo $this->printTable($groupdata, $columns, $actions, 'groups-members', $lang['strnousers']);
}
// Display form for adding a user to the group
@@ -127,7 +127,7 @@ class GroupController extends BaseController {
echo "<input type=\"hidden\" name=\"action\" value=\"add_member\" />\n";
echo "</form>\n";
- $misc->printNavLinks(['showall' => [
+ $this->printNavLinks(['showall' => [
'attr' => [
'href' => [
'url' => 'groups.php',
@@ -150,7 +150,7 @@ class GroupController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('group');
+ $this->printTrail('group');
$misc->printTitle($lang['strdrop'], 'pg.group.drop');
echo "<p>", sprintf($lang['strconfdropgroup'], $misc->printVal($_REQUEST['group'])), "</p>\n";
@@ -192,7 +192,7 @@ class GroupController extends BaseController {
// Fetch a list of all users in the cluster
$users = $data->getUsers();
- $misc->printTrail('server');
+ $this->printTrail('server');
$misc->printTitle($lang['strcreategroup'], 'pg.group.create');
$misc->printMsg($msg);
@@ -258,8 +258,8 @@ class GroupController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('server');
- $misc->printTabs('server', 'groups');
+ $this->printTrail('server');
+ $this->printTabs('server', 'groups');
$misc->printMsg($msg);
$groups = $data->getGroups();
@@ -291,9 +291,9 @@ class GroupController extends BaseController {
],
];
- echo $misc->printTable($groups, $columns, $actions, 'groups-properties', $lang['strnogroups']);
+ echo $this->printTable($groups, $columns, $actions, 'groups-properties', $lang['strnogroups']);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'groups.php',
diff --git a/src/controllers/HistoryController.php b/src/controllers/HistoryController.php
index ecd5d6e3..57761063 100644
--- a/src/controllers/HistoryController.php
+++ b/src/controllers/HistoryController.php
@@ -80,7 +80,7 @@ class HistoryController extends BaseController {
],
];
- echo $misc->printTable($history, $columns, $actions, 'history-history', $lang['strnohistory']);
+ echo $this->printTable($history, $columns, $actions, 'history-history', $lang['strnohistory']);
} else {
echo "<p>{$lang['strnohistory']}</p>\n";
}
@@ -131,7 +131,7 @@ class HistoryController extends BaseController {
];
}
- $misc->printNavLinks($navlinks, 'history-history', get_defined_vars());
+ $this->printNavLinks($navlinks, 'history-history', get_defined_vars());
}
public function doDelHistory($qid, $confirm) {
diff --git a/src/controllers/IndexController.php b/src/controllers/IndexController.php
index 69cc0655..9d521479 100644
--- a/src/controllers/IndexController.php
+++ b/src/controllers/IndexController.php
@@ -9,6 +9,101 @@ use \PHPPgAdmin\Decorators\Decorator;
class IndexController extends BaseController {
public $_name = 'IndexController';
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strindexes'], "<script src=\"/js/indexes.js\" type=\"text/javascript\"></script>");
+
+ if ($action == 'create_index' || $action == 'save_create_index') {
+ echo "<body onload=\"init();\">";
+ } else {
+ $misc->printBody();
+ }
+
+ switch ($action) {
+ case 'cluster_index':
+ if (isset($_POST['cluster'])) {
+ $this->doClusterIndex(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_cluster_index':
+ $this->doClusterIndex(true);
+ break;
+ case 'reindex':
+ $this->doReindex();
+ break;
+ case 'save_create_index':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreateIndex();
+ }
+
+ break;
+ case 'create_index':
+ $this->doCreateIndex();
+ break;
+ case 'drop_index':
+ if (isset($_POST['drop'])) {
+ $this->doDropIndex(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop_index':
+ $this->doDropIndex(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $indexes = $data->getIndexes($_REQUEST['table']);
+
+ $reqvars = $misc->getRequestVars('table');
+
+ function getIcon($f) {
+ if ($f['indisprimary'] == 't') {
+ return 'PrimaryKey';
+ }
+
+ if ($f['indisunique'] == 't') {
+ return 'UniqueConstraint';
+ }
+
+ return 'Index';
+ }
+
+ $attrs = [
+ 'text' => Decorator::field('indname'),
+ 'icon' => Decorator::callback('getIcon'),
+ ];
+
+ return $misc->printTree($indexes, $attrs, 'indexes');
+ }
+
/**
* Show confirmation of cluster index and perform actual cluster
*/
@@ -22,7 +117,7 @@ class IndexController extends BaseController {
// Default analyze to on
$_REQUEST['analyze'] = true;
- $misc->printTrail('index');
+ $this->printTrail('index');
$misc->printTitle($lang['strclusterindex'], 'pg.index.cluster');
echo "<p>", sprintf($lang['strconfcluster'], $misc->printVal($_REQUEST['index'])), "</p>\n";
@@ -107,7 +202,7 @@ class IndexController extends BaseController {
$tablespaces = $data->getTablespaces();
}
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['strcreateindex'], 'pg.index.create');
$misc->printMsg($msg);
@@ -247,7 +342,7 @@ class IndexController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('index');
+ $this->printTrail('index');
$misc->printTitle($lang['strdrop'], 'pg.index.drop');
echo "<p>", sprintf($lang['strconfdropindex'], $misc->printVal($_REQUEST['index'])), "</p>\n";
@@ -293,8 +388,8 @@ class IndexController extends BaseController {
return $actions;
};
- $misc->printTrail('table');
- $misc->printTabs('table', 'indexes');
+ $this->printTrail('table');
+ $this->printTabs('table', 'indexes');
$misc->printMsg($msg);
$indexes = $data->getIndexes($_REQUEST['table']);
@@ -370,9 +465,9 @@ class IndexController extends BaseController {
],
];
- echo $misc->printTable($indexes, $columns, $actions, 'indexes-indexes', $lang['strnoindexes'], $indPre);
+ echo $this->printTable($indexes, $columns, $actions, 'indexes-indexes', $lang['strnoindexes'], $indPre);
- $misc->printNavLinks([
+ $this->printNavLinks([
'create' => [
'attr' => [
'href' => [
diff --git a/src/controllers/InfoController.php b/src/controllers/InfoController.php
index 899f0d35..7598d21e 100644
--- a/src/controllers/InfoController.php
+++ b/src/controllers/InfoController.php
@@ -9,17 +9,17 @@ use \PHPPgAdmin\Decorators\Decorator;
class InfoController extends BaseController {
public $_name = 'InfoController';
-/**
- * List all the information on the table
- */
+ /**
+ * List all the information on the table
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('table');
- $misc->printTabs('table', 'info');
+ $this->printTrail('table');
+ $this->printTabs('table', 'info');
$misc->printMsg($msg);
// common params for printVal
@@ -82,7 +82,7 @@ class InfoController extends BaseController {
],
];
- echo $misc->printTable($referrers, $columns, $actions, 'info-referrers', $lang['strnodata']);
+ echo $this->printTable($referrers, $columns, $actions, 'info-referrers', $lang['strnodata']);
}
// Parent tables
@@ -118,7 +118,7 @@ class InfoController extends BaseController {
],
];
- echo $misc->printTable($parents, $columns, $actions, 'info-parents', $lang['strnodata']);
+ echo $this->printTable($parents, $columns, $actions, 'info-parents', $lang['strnodata']);
}
// Child tables
@@ -154,7 +154,7 @@ class InfoController extends BaseController {
],
];
- echo $misc->printTable($children, $columns, $actions, 'info-children', $lang['strnodata']);
+ echo $this->printTable($children, $columns, $actions, 'info-children', $lang['strnodata']);
}
@@ -343,4 +343,5 @@ class InfoController extends BaseController {
}
}
}
+
}
diff --git a/src/controllers/IntroController.php b/src/controllers/IntroController.php
index 77143ee3..607bdb0e 100644
--- a/src/controllers/IntroController.php
+++ b/src/controllers/IntroController.php
@@ -8,6 +8,14 @@ namespace PHPPgAdmin\Controller;
class IntroController extends BaseController {
public $_name = 'IntroController';
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ $this->misc = $container->get('misc');
+
+ $this->misc->setNoDBConnection(true);
+ parent::__construct($container);
+
+ }
/**
* Intro screen
*
@@ -27,9 +35,9 @@ class IntroController extends BaseController {
$misc->setNoDBConnection(true);
- $intro_html = $misc->printTrail('root', false);
+ $intro_html = $this->printTrail('root', false);
- $intro_html .= $misc->printTabs('root', 'intro', false);
+ $intro_html .= $this->printTabs('root', 'intro', false);
$intro_html .= "<h1> $appName $appVersion (PHP " . phpversion() . ')</h1>';
@@ -83,4 +91,26 @@ class IntroController extends BaseController {
echo $intro_html;
}
+
+ public function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ $misc->setNoDBConnection(true);
+ $misc->printHeader($lang['strintro']);
+ $misc->printBody();
+
+ switch ($action) {
+
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
} \ No newline at end of file
diff --git a/src/controllers/LangController.php b/src/controllers/LangController.php
index f3b09c98..56a76b33 100644
--- a/src/controllers/LangController.php
+++ b/src/controllers/LangController.php
@@ -9,17 +9,39 @@ use \PHPPgAdmin\Decorators\Decorator;
class LangController extends BaseController {
public $_name = 'LangController';
-/**
- * Show default list of languages in the database
- */
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strlanguages']);
+ $misc->printBody();
+
+ switch ($action) {
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
+ /**
+ * Show default list of languages in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('database');
- $misc->printTabs('database', 'languages');
+ $this->printTrail('database');
+ $this->printTabs('database', 'languages');
$misc->printMsg($msg);
$languages = $data->getLanguages();
@@ -42,6 +64,28 @@ class LangController extends BaseController {
$actions = [];
- echo $misc->printTable($languages, $columns, $actions, 'languages-languages', $lang['strnolanguages']);
+ echo $this->printTable($languages, $columns, $actions, 'languages-languages', $lang['strnolanguages']);
+ }
+
+ /**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $languages = $data->getLanguages();
+
+ $attrs = [
+ 'text' => Decorator::field('lanname'),
+ 'icon' => 'Language',
+ ];
+
+ return $misc->printTree($languages, $attrs, 'languages');
+
}
+
}
diff --git a/src/controllers/LoginController.php b/src/controllers/LoginController.php
new file mode 100644
index 00000000..55d02949
--- /dev/null
+++ b/src/controllers/LoginController.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Login controller class
+ */
+class LoginController extends BaseController {
+
+ private $container = null;
+ private $_connection = null;
+ private $app = null;
+ private $data = null;
+ private $database = null;
+ private $server_id = null;
+ public $appLangFiles = [];
+ public $appThemes = [];
+ public $appName = '';
+ public $appVersion = '';
+ public $form = '';
+ public $href = '';
+ public $lang = [];
+ public $action = '';
+ public $_name = 'LoginController';
+ public $_title = 'strlogin';
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ $this->misc = $container->get('misc');
+
+ $this->misc->setNoDBConnection(true);
+ parent::__construct($container);
+
+ }
+
+ function doLoginForm($msg = '') {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $misc->setNoDBConnection(true);
+
+ $login_html = $misc->printHeader($lang[$this->_title], null, false);
+ $login_html .= $misc->printBody(false);
+ $login_html .= $this->printTrail('root', false);
+
+ if (!empty($_POST)) {
+ $vars = &$_POST;
+ } else {
+ $vars = &$_GET;
+ }
+ foreach ($_REQUEST as $key => $val) {
+ if (strpos($key, '?') !== FALSE) {
+ $namexploded = explode('?', $key);
+ $_REQUEST[$namexploded[1]] = htmlspecialchars($val);
+ }
+ }
+
+ $server_info = $misc->getServerInfo($_REQUEST['server']);
+ $title = sprintf($lang['strlogintitle'], $server_info['desc']);
+ \PC::debug($title, 'title');
+ $printTitle = $misc->printTitle($title, null, false);
+ \PC::debug($printTitle, 'printTitle');
+
+ $login_html .= $printTitle;
+
+ if (isset($msg)) {
+ $login_html .= $misc->printMsg($msg, false);
+ }
+
+ $login_html .= '<form id="login_form" method="post" name="login_form">';
+
+ $md5_server = md5($_REQUEST['server']);
+ // Pass request vars through form (is this a security risk???)
+ foreach ($vars as $key => $val) {
+ if (substr($key, 0, 5) == 'login') {
+ continue;
+ }
+ if (strpos($key, '?') !== FALSE) {
+ $key = explode('?', $key)[1];
+ }
+
+ $login_html .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($val) . '" />' . "\n";
+ }
+
+ $login_html .= '<input type="hidden" name="loginServer" value="' . htmlspecialchars($_REQUEST['server']) . '" />';
+ $login_html .= '<table class="navbar" border="0" cellpadding="5" cellspacing="3">';
+ $login_html .= '<tr>';
+ $login_html .= '<td>' . $lang['strusername'] . '</td>';
+ $loginusername = isset($_POST['loginUsername']) ? htmlspecialchars($_POST['loginUsername']) : '';
+
+ $login_html .= '<td><input type="text" name="loginUsername" value="' . $loginusername . '" size="24" /></td>';
+ $login_html .= '</tr>';
+ $login_html .= '<tr>';
+ $login_html .= '<td>' . $lang['strpassword'] . '</td>';
+ $login_html .= '<td><input id="loginPassword" type="password" name="loginPassword_' . $md5_server . '" size="24" /></td>';
+ $login_html .= '</tr>';
+ $login_html .= '</table>';
+ if (sizeof($conf['servers']) > 1) {
+ $checked = isset($_POST['loginShared']) ? 'checked="checked"' : '';
+ $login_html .= '<p><input type="checkbox" id="loginShared" name="loginShared" ' . $checked . ' />';
+ $login_html .= '<label for="loginShared">' . $lang['strtrycred'] . '</label></p>';
+ }
+ $login_html .= '<p><input type="submit" name="loginSubmit" value="' . $lang['strlogin'] . '" /></p>';
+ $login_html .= '</form>';
+
+ $login_html .= '<script type="text/javascript">';
+ $login_html .= ' var uname = document.login_form.loginUsername;';
+ $login_html .= ' var pword = document.login_form.loginPassword_' . $md5_server . ';';
+ $login_html .= ' if (uname.value == "") {';
+ $login_html .= ' uname.focus();';
+ $login_html .= ' } else {';
+ $login_html .= ' pword.focus();';
+ $login_html .= ' }';
+ $login_html .= '</script>';
+
+ // Output footer
+ $login_html .= $misc->printFooter(false);
+ return $login_html;
+
+ }
+
+ public function render() {
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ $misc->setNoDBConnection(true);
+
+ switch ($action) {
+ default:
+ echo $this->doLoginForm();
+ break;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/controllers/MaterializedViewController.php b/src/controllers/MaterializedViewController.php
new file mode 100644
index 00000000..34904c10
--- /dev/null
+++ b/src/controllers/MaterializedViewController.php
@@ -0,0 +1,833 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Base controller class
+ */
+class MaterializedViewController extends BaseController {
+ public $script = 'materialized_materialized_views.php';
+ public $_name = 'MaterializedViewController';
+ public $table_place = 'matviews-matviews';
+ /**
+ * Ask for select parameters and perform select
+ */
+ public function doSelectRows($confirm, $msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if ($confirm) {
+ $this->printTrail('view');
+ $this->printTabs('view', 'select');
+ $misc->printMsg($msg);
+
+ $attrs = $data->getTableAttributes($_REQUEST['view']);
+
+ echo '<form action="/src/views/' . $this->script . '" method="post" id="selectform">';
+ echo "\n";
+
+ if ($attrs->recordCount() > 0) {
+ // JavaScript for select all feature
+ echo "<script type=\"text/javascript\">\n";
+ echo "//<![CDATA[\n";
+ echo " function selectAll() {\n";
+ echo " for (var i=0; i<document.getElementById('selectform').elements.length; i++) {\n";
+ echo " var e = document.getElementById('selectform').elements[i];\n";
+ echo " if (e.name.indexOf('show') == 0) { \n ";
+ echo " e.checked = document.getElementById('selectform').selectall.checked;\n";
+ echo " }\n";
+ echo " }\n";
+ echo " }\n";
+ echo "//]]>\n";
+ echo "</script>\n";
+
+ echo "<table>\n";
+
+ // Output table header
+ echo "<tr><th class=\"data\">{$lang['strshow']}</th><th class=\"data\">{$lang['strcolumn']}</th>";
+ echo "<th class=\"data\">{$lang['strtype']}</th><th class=\"data\">{$lang['stroperator']}</th>";
+ echo "<th class=\"data\">{$lang['strvalue']}</th></tr>";
+
+ $i = 0;
+ while (!$attrs->EOF) {
+ $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
+ // Set up default value if there isn't one already
+ if (!isset($_REQUEST['values'][$attrs->fields['attname']])) {
+ $_REQUEST['values'][$attrs->fields['attname']] = null;
+ }
+
+ if (!isset($_REQUEST['ops'][$attrs->fields['attname']])) {
+ $_REQUEST['ops'][$attrs->fields['attname']] = null;
+ }
+
+ // Continue drawing row
+ $id = (($i % 2) == 0 ? '1' : '2');
+ echo "<tr class=\"data{$id}\">\n";
+ echo "<td style=\"white-space:nowrap;\">";
+ echo "<input type=\"checkbox\" name=\"show[", htmlspecialchars($attrs->fields['attname']), "]\"",
+ isset($_REQUEST['show'][$attrs->fields['attname']]) ? ' checked="checked"' : '', " /></td>";
+ echo "<td style=\"white-space:nowrap;\">", $misc->printVal($attrs->fields['attname']), "</td>";
+ echo "<td style=\"white-space:nowrap;\">", $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod'])), "</td>";
+ echo "<td style=\"white-space:nowrap;\">";
+ echo "<select name=\"ops[{$attrs->fields['attname']}]\">\n";
+ foreach (array_keys($data->selectOps) as $v) {
+ echo "<option value=\"", htmlspecialchars($v), "\"", ($v == $_REQUEST['ops'][$attrs->fields['attname']]) ? ' selected="selected"' : '',
+ ">", htmlspecialchars($v), "</option>\n";
+ }
+ echo "</select></td>\n";
+ echo "<td style=\"white-space:nowrap;\">", $data->printField("values[{$attrs->fields['attname']}]",
+ $_REQUEST['values'][$attrs->fields['attname']], $attrs->fields['type']), "</td>";
+ echo "</tr>\n";
+ $i++;
+ $attrs->moveNext();
+ }
+ // Select all checkbox
+ echo "<tr><td colspan=\"5\"><input type=\"checkbox\" id=\"selectall\" name=\"selectall\" accesskey=\"a\" onclick=\"javascript:selectAll()\" /><label for=\"selectall\">{$lang['strselectallfields']}</label></td></tr>";
+ echo "</table>\n";
+ } else {
+ echo "<p>{$lang['strinvalidparam']}</p>\n";
+ }
+
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"selectrows\" />\n";
+ echo "<input type=\"hidden\" name=\"view\" value=\"", htmlspecialchars($_REQUEST['view']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"subject\" value=\"view\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" name=\"select\" accesskey=\"r\" value=\"{$lang['strselect']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ return;
+ } else {
+ if (!isset($_POST['show'])) {
+ $_POST['show'] = [];
+ }
+
+ if (!isset($_POST['values'])) {
+ $_POST['values'] = [];
+ }
+
+ if (!isset($_POST['nulls'])) {
+ $_POST['nulls'] = [];
+ }
+
+ // Verify that they haven't supplied a value for unary operators
+ foreach ($_POST['ops'] as $k => $v) {
+ if ($data->selectOps[$v] == 'p' && $_POST['values'][$k] != '') {
+ $this->doSelectRows(true, $lang['strselectunary']);
+ return;
+ }
+ }
+
+ if (sizeof($_POST['show']) == 0) {
+ return $this->doSelectRows(true, $lang['strselectneedscol']);
+ } else {
+ // Generate query SQL
+ $query = $data->getSelectSQL($_REQUEST['view'], array_keys($_POST['show']), $_POST['values'], $_POST['ops']);
+
+ $_REQUEST['query'] = $query;
+ $_REQUEST['return'] = "schema";
+
+ $misc->setNoOutput(true);
+
+ $display_controller = new DisplayController($this->getContainer());
+
+ return $display_controller->render();
+ }
+ }
+
+ }
+
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
+ public function doDrop($confirm) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (empty($_REQUEST['view']) && empty($_REQUEST['ma'])) {
+ $this->doDefault($lang['strspecifyviewtodrop']);
+ exit();
+ }
+
+ if ($confirm) {
+ $this->printTrail('view');
+ $misc->printTitle($lang['strdrop'], 'pg.view.drop');
+
+ echo "<form action=\"/src/views/materialized_views.php\" method=\"post\">\n";
+
+ //If multi drop
+ if (isset($_REQUEST['ma'])) {
+ foreach ($_REQUEST['ma'] as $v) {
+ $a = unserialize(htmlspecialchars_decode($v, ENT_QUOTES));
+ echo "<p>", sprintf($lang['strconfdropview'], $misc->printVal($a['view'])), "</p>\n";
+ echo '<input type="hidden" name="view[]" value="', htmlspecialchars($a['view']), "\" />\n";
+ }
+ } else {
+ echo "<p>", sprintf($lang['strconfdropview'], $misc->printVal($_REQUEST['view'])), "</p>\n";
+ echo "<input type=\"hidden\" name=\"view\" value=\"", htmlspecialchars($_REQUEST['view']), "\" />\n";
+ }
+
+ echo "<input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
+
+ echo $misc->form;
+ echo "<p><input type=\"checkbox\" id=\"cascade\" name=\"cascade\" /> <label for=\"cascade\">{$lang['strcascade']}</label></p>\n";
+ echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+ echo "</form>\n";
+ } else {
+ if (is_array($_POST['view'])) {
+ $msg = '';
+ $status = $data->beginTransaction();
+ if ($status == 0) {
+ foreach ($_POST['view'] as $s) {
+ $status = $data->dropView($s, isset($_POST['cascade']));
+ if ($status == 0) {
+ $msg .= sprintf('%s: %s<br />', htmlentities($s, ENT_QUOTES, 'UTF-8'), $lang['strviewdropped']);
+ } else {
+ $data->endTransaction();
+ $this->doDefault(sprintf('%s%s: %s<br />', $msg, htmlentities($s, ENT_QUOTES, 'UTF-8'), $lang['strviewdroppedbad']));
+ return;
+ }
+ }
+ }
+ if ($data->endTransaction() == 0) {
+ // Everything went fine, back to the Default page....
+ $this->misc->setReloadBrowser(true);
+ $this->doDefault($msg);
+ } else {
+ $this->doDefault($lang['strviewdroppedbad']);
+ }
+
+ } else {
+ $status = $data->dropView($_POST['view'], isset($_POST['cascade']));
+ if ($status == 0) {
+ $this->misc->setReloadBrowser(true);
+ $this->doDefault($lang['strviewdropped']);
+ } else {
+ $this->doDefault($lang['strviewdroppedbad']);
+ }
+
+ }
+ }
+
+ }
+
+ /**
+ * Sets up choices for table linkage, and which fields to select for the view we're creating
+ */
+ public function doSetParamsCreate($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check that they've chosen tables for the view definition
+ if (!isset($_POST['formTables'])) {
+ $this->doWizardCreate($lang['strviewneedsdef']);
+ } else {
+ // Initialise variables
+ if (!isset($_REQUEST['formView'])) {
+ $_REQUEST['formView'] = '';
+ }
+
+ if (!isset($_REQUEST['formComment'])) {
+ $_REQUEST['formComment'] = '';
+ }
+
+ $this->printTrail('schema');
+ $misc->printTitle($lang['strcreateviewwiz'], 'pg.view.create');
+ $misc->printMsg($msg);
+
+ $tblCount = sizeof($_POST['formTables']);
+ //unserialize our schema/table information and store in arrSelTables
+ for ($i = 0; $i < $tblCount; $i++) {
+ $arrSelTables[] = unserialize($_POST['formTables'][$i]);
+ }
+
+ $linkCount = $tblCount;
+
+ //get linking keys
+ $rsLinkKeys = $data->getLinkingKeys($arrSelTables);
+ $linkCount = $rsLinkKeys->recordCount() > $tblCount ? $rsLinkKeys->recordCount() : $tblCount;
+
+ $arrFields = []; //array that will hold all our table/field names
+
+ //if we have schemas we need to specify the correct schema for each table we're retrieiving
+ //with getTableAttributes
+ $curSchema = $data->_schema;
+ for ($i = 0; $i < $tblCount; $i++) {
+ if ($data->_schema != $arrSelTables[$i]['schemaname']) {
+ $data->setSchema($arrSelTables[$i]['schemaname']);
+ }
+
+ $attrs = $data->getTableAttributes($arrSelTables[$i]['tablename']);
+ while (!$attrs->EOF) {
+ $arrFields["{$arrSelTables[$i]['schemaname']}.{$arrSelTables[$i]['tablename']}.{$attrs->fields['attname']}"] = serialize([
+ 'schemaname' => $arrSelTables[$i]['schemaname'],
+ 'tablename' => $arrSelTables[$i]['tablename'],
+ 'fieldname' => $attrs->fields['attname']]
+ );
+ $attrs->moveNext();
+ }
+
+ $data->setSchema($curSchema);
+ }
+ asort($arrFields);
+
+ echo "<form action=\"/src/views/materialized_views.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strviewname']}</th></tr>";
+ echo "<tr>\n<td class=\"data1\">\n";
+ // View name
+ echo "<input name=\"formView\" value=\"", htmlspecialchars($_REQUEST['formView']), "\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" />\n";
+ echo "</td>\n</tr>\n";
+ echo "<tr><th class=\"data\">{$lang['strcomment']}</th></tr>";
+ echo "<tr>\n<td class=\"data1\">\n";
+ // View comments
+ echo "<textarea name=\"formComment\" rows=\"3\" cols=\"32\">",
+ htmlspecialchars($_REQUEST['formComment']), "</textarea>\n";
+ echo "</td>\n</tr>\n";
+ echo "</table>\n";
+
+ // Output selector for fields to be retrieved from view
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strcolumns']}</th></tr>";
+ echo "<tr>\n<td class=\"data1\">\n";
+ echo \PHPPgAdmin\GUI::printCombo($arrFields, 'formFields[]', false, '', true);
+ echo "</td>\n</tr>";
+ echo "<tr><td><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth1\" value=\"rename\" /><label for=\"dblFldMeth1\">{$lang['strrenamedupfields']}</label>";
+ echo "<br /><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth2\" value=\"drop\" /><label for=\"dblFldMeth2\">{$lang['strdropdupfields']}</label>";
+ echo "<br /><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth3\" value=\"\" checked=\"checked\" /><label for=\"dblFldMeth3\">{$lang['strerrordupfields']}</label></td></tr></table><br />";
+
+ // Output the Linking keys combo boxes
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strviewlink']}</th></tr>";
+ $rowClass = 'data1';
+ for ($i = 0; $i < $linkCount; $i++) {
+ // Initialise variables
+ if (!isset($formLink[$i]['operator'])) {
+ $formLink[$i]['operator'] = 'INNER JOIN';
+ }
+
+ echo "<tr>\n<td class=\"$rowClass\">\n";
+
+ if (!$rsLinkKeys->EOF) {
+ $curLeftLink = htmlspecialchars(serialize(['schemaname' => $rsLinkKeys->fields['p_schema'], 'tablename' => $rsLinkKeys->fields['p_table'], 'fieldname' => $rsLinkKeys->fields['p_field']]));
+ $curRightLink = htmlspecialchars(serialize(['schemaname' => $rsLinkKeys->fields['f_schema'], 'tablename' => $rsLinkKeys->fields['f_table'], 'fieldname' => $rsLinkKeys->fields['f_field']]));
+ $rsLinkKeys->moveNext();
+ } else {
+ $curLeftLink = '';
+ $curRightLink = '';
+ }
+
+ echo \PHPPgAdmin\GUI::printCombo($arrFields, "formLink[$i][leftlink]", true, $curLeftLink, false);
+ echo \PHPPgAdmin\GUI::printCombo($data->joinOps, "formLink[$i][operator]", true, $formLink[$i]['operator']);
+ echo \PHPPgAdmin\GUI::printCombo($arrFields, "formLink[$i][rightlink]", true, $curRightLink, false);
+ echo "</td>\n</tr>\n";
+ $rowClass = $rowClass == 'data1' ? 'data2' : 'data1';
+ }
+ echo "</table>\n<br />\n";
+
+ // Build list of available operators (infix only)
+ $arrOperators = [];
+ foreach ($data->selectOps as $k => $v) {
+ if ($v == 'i') {
+ $arrOperators[$k] = $k;
+ }
+
+ }
+
+ // Output additional conditions, note that this portion of the wizard treats the right hand side as literal values
+ //(not as database objects) so field names will be treated as strings, use the above linking keys section to perform joins
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strviewconditions']}</th></tr>";
+ $rowClass = 'data1';
+ for ($i = 0; $i < $linkCount; $i++) {
+ echo "<tr>\n<td class=\"$rowClass\">\n";
+ echo \PHPPgAdmin\GUI::printCombo($arrFields, "formCondition[$i][field]");
+ echo \PHPPgAdmin\GUI::printCombo($arrOperators, "formCondition[$i][operator]", false, false);
+ echo "<input type=\"text\" name=\"formCondition[$i][txt]\" />\n";
+ echo "</td>\n</tr>\n";
+ $rowClass = $rowClass == 'data1' ? 'data2' : 'data1';
+ }
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create_wiz\" />\n";
+
+ foreach ($arrSelTables as $curTable) {
+ echo "<input type=\"hidden\" name=\"formTables[]\" value=\"" . htmlspecialchars(serialize($curTable)) . "\" />\n";
+ }
+
+ echo $misc->form;
+ echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ }
+ }
+
+ /**
+ * Display a wizard where they can enter a new view
+ */
+ public function doWizardCreate($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $tables = $data->getTables(true);
+
+ $this->printTrail('schema');
+ $misc->printTitle($lang['strcreateviewwiz'], 'pg.view.create');
+ $misc->printMsg($msg);
+
+ echo "<form action=\"/src/views/materialized_views.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "<tr><th class=\"data\">{$lang['strtables']}</th></tr>";
+ echo "<tr>\n<td class=\"data1\">\n";
+
+ $arrTables = [];
+ while (!$tables->EOF) {
+ $arrTmp = [];
+ $arrTmp['schemaname'] = $tables->fields['nspname'];
+ $arrTmp['tablename'] = $tables->fields['relname'];
+ $arrTables[$tables->fields['nspname'] . '.' . $tables->fields['relname']] = serialize($arrTmp);
+ $tables->moveNext();
+ }
+ echo \PHPPgAdmin\GUI::printCombo($arrTables, 'formTables[]', false, '', true);
+
+ echo "</td>\n</tr>\n";
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"set_params_create\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" value=\"{$lang['strnext']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ }
+
+ /**
+ * Displays a screen where they can enter a new view
+ */
+ public function doCreate($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_REQUEST['formView'])) {
+ $_REQUEST['formView'] = '';
+ }
+
+ if (!isset($_REQUEST['formDefinition'])) {
+ if (isset($_SESSION['sqlquery'])) {
+ $_REQUEST['formDefinition'] = $_SESSION['sqlquery'];
+ } else {
+ $_REQUEST['formDefinition'] = 'SELECT ';
+ }
+
+ }
+ if (!isset($_REQUEST['formComment'])) {
+ $_REQUEST['formComment'] = '';
+ }
+
+ $this->printTrail('schema');
+ $misc->printTitle($lang['strcreateview'], 'pg.view.create');
+ $misc->printMsg($msg);
+
+ echo "<form action=\"/src/views/materialized_views.php\" method=\"post\">\n";
+ echo "<table style=\"width: 100%\">\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strname']}</th>\n";
+ echo "\t<td class=\"data1\"><input name=\"formView\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" value=\"",
+ htmlspecialchars($_REQUEST['formView']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strdefinition']}</th>\n";
+ echo "\t<td class=\"data1\"><textarea style=\"width:100%;\" rows=\"10\" cols=\"50\" name=\"formDefinition\">",
+ htmlspecialchars($_REQUEST['formDefinition']), "</textarea></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strcomment']}</th>\n";
+ echo "\t\t<td class=\"data1\"><textarea name=\"formComment\" rows=\"3\" cols=\"32\">",
+ htmlspecialchars($_REQUEST['formComment']), "</textarea></td>\n\t</tr>\n";
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ }
+
+ /**
+ * Actually creates the new view in the database
+ */
+ public function doSaveCreate() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check that they've given a name and a definition
+ if ($_POST['formView'] == '') {
+ $this->doCreate($lang['strviewneedsname']);
+ } elseif ($_POST['formDefinition'] == '') {
+ $this->doCreate($lang['strviewneedsdef']);
+ } else {
+ $status = $data->createView($_POST['formView'], $_POST['formDefinition'], false, $_POST['formComment']);
+ if ($status == 0) {
+ $this->misc->setReloadBrowser(true);
+ $this->doDefault($lang['strviewcreated']);
+ } else {
+ $this->doCreate($lang['strviewcreatedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Actually creates the new wizard view in the database
+ */
+ public function doSaveCreateWiz() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check that they've given a name and fields they want to select
+
+ if (!strlen($_POST['formView'])) {
+ $this->doSetParamsCreate($lang['strviewneedsname']);
+ } else if (!isset($_POST['formFields']) || !count($_POST['formFields'])) {
+ $this->doSetParamsCreate($lang['strviewneedsfields']);
+ } else {
+ $selFields = '';
+
+ if (!empty($_POST['dblFldMeth'])) {
+ $tmpHsh = [];
+ }
+
+ foreach ($_POST['formFields'] as $curField) {
+ $arrTmp = unserialize($curField);
+ $data->fieldArrayClean($arrTmp);
+ if (!empty($_POST['dblFldMeth'])) {
+ // doublon control
+ if (empty($tmpHsh[$arrTmp['fieldname']])) {
+ // field does not exist
+ $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\", ";
+ $tmpHsh[$arrTmp['fieldname']] = 1;
+ } else if ($_POST['dblFldMeth'] == 'rename') {
+ // field exist and must be renamed
+ $tmpHsh[$arrTmp['fieldname']]++;
+ $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" AS \"{$arrTmp['schemaname']}_{$arrTmp['tablename']}_{$arrTmp['fieldname']}{$tmpHsh[$arrTmp['fieldname']]}\", ";
+ }
+ /* field already exist, just ignore this one */
+ } else {
+ // no doublon control
+ $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\", ";
+ }
+ }
+
+ $selFields = substr($selFields, 0, -2);
+ unset($arrTmp, $tmpHsh);
+ $linkFields = '';
+
+ // If we have links, out put the JOIN ... ON statements
+ if (is_array($_POST['formLink'])) {
+ // Filter out invalid/blank entries for our links
+ $arrLinks = [];
+ foreach ($_POST['formLink'] as $curLink) {
+ if (strlen($curLink['leftlink']) && strlen($curLink['rightlink']) && strlen($curLink['operator'])) {
+ $arrLinks[] = $curLink;
+ }
+ }
+ // We must perform some magic to make sure that we have a valid join order
+ $count = sizeof($arrLinks);
+ $arrJoined = [];
+ $arrUsedTbls = [];
+
+ // If we have at least one join condition, output it
+ if ($count > 0) {
+ $j = 0;
+ while ($j < $count) {
+ foreach ($arrLinks as $curLink) {
+
+ $arrLeftLink = unserialize($curLink['leftlink']);
+ $arrRightLink = unserialize($curLink['rightlink']);
+ $data->fieldArrayClean($arrLeftLink);
+ $data->fieldArrayClean($arrRightLink);
+
+ $tbl1 = "\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\"";
+ $tbl2 = "\"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\"";
+
+ if ((!in_array($curLink, $arrJoined) && in_array($tbl1, $arrUsedTbls)) || !count($arrJoined)) {
+
+ // Make sure for multi-column foreign keys that we use a table alias tables joined to more than once
+ // This can (and should be) more optimized for multi-column foreign keys
+ $adj_tbl2 = in_array($tbl2, $arrUsedTbls) ? "$tbl2 AS alias_ppa_" . mktime() : $tbl2;
+
+ $linkFields .= strlen($linkFields) ? "{$curLink['operator']} $adj_tbl2 ON (\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\".\"{$arrLeftLink['fieldname']}\" = \"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\".\"{$arrRightLink['fieldname']}\") "
+ : "$tbl1 {$curLink['operator']} $adj_tbl2 ON (\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\".\"{$arrLeftLink['fieldname']}\" = \"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\".\"{$arrRightLink['fieldname']}\") ";
+
+ $arrJoined[] = $curLink;
+ if (!in_array($tbl1, $arrUsedTbls)) {
+ $arrUsedTbls[] = $tbl1;
+ }
+
+ if (!in_array($tbl2, $arrUsedTbls)) {
+ $arrUsedTbls[] = $tbl2;
+ }
+
+ }
+ }
+ $j++;
+ }
+ }
+ }
+
+ //if linkfields has no length then either _POST['formLink'] was not set, or there were no join conditions
+ //just select from all seleted tables - a cartesian join do a
+ if (!strlen($linkFields)) {
+ foreach ($_POST['formTables'] as $curTable) {
+ $arrTmp = unserialize($curTable);
+ $data->fieldArrayClean($arrTmp);
+ $linkFields .= strlen($linkFields) ? ", \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\"" : "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\"";
+ }
+ }
+
+ $addConditions = '';
+ if (is_array($_POST['formCondition'])) {
+ foreach ($_POST['formCondition'] as $curCondition) {
+ if (strlen($curCondition['field']) && strlen($curCondition['txt'])) {
+ $arrTmp = unserialize($curCondition['field']);
+ $data->fieldArrayClean($arrTmp);
+ $addConditions .= strlen($addConditions) ? " AND \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" {$curCondition['operator']} '{$curCondition['txt']}' "
+ : " \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" {$curCondition['operator']} '{$curCondition['txt']}' ";
+ }
+ }
+ }
+
+ $viewQuery = "SELECT $selFields FROM $linkFields ";
+
+ //add where from additional conditions
+ if (strlen($addConditions)) {
+ $viewQuery .= ' WHERE ' . $addConditions;
+ }
+
+ $status = $data->createView($_POST['formView'], $viewQuery, false, $_POST['formComment']);
+ if ($status == 0) {
+ $this->misc->setReloadBrowser(true);
+ $this->doDefault($lang['strviewcreated']);
+ } else {
+ $this->doSetParamsCreate($lang['strviewcreatedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Show default list of views in the database
+ */
+ public function doDefault($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'matviews');
+ $misc->printMsg($msg);
+
+ //$matviews = $data->getViews();
+ $matviews = $data->getMaterializedViews();
+
+ $columns = [
+ 'matview' => [
+ 'title' => 'M ' . $lang['strview'],
+ 'field' => Decorator::field('relname'),
+ 'url' => "/redirect/matview?{$misc->href}&amp;",
+ 'vars' => ['matview' => 'relname'],
+ ],
+ 'owner' => [
+ 'title' => $lang['strowner'],
+ 'field' => Decorator::field('relowner'),
+ ],
+ 'actions' => [
+ 'title' => $lang['stractions'],
+ ],
+ 'comment' => [
+ 'title' => $lang['strcomment'],
+ 'field' => Decorator::field('relcomment'),
+ ],
+ ];
+
+ $actions = [
+ 'multiactions' => [
+ 'keycols' => ['matview' => 'relname'],
+ 'url' => 'materialized_materialized_views.php',
+ ],
+ 'browse' => [
+ 'content' => $lang['strbrowse'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => [
+ 'action' => 'confselectrows',
+ 'subject' => 'matview',
+ 'return' => 'schema',
+ 'matview' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'select' => [
+ 'content' => $lang['strselect'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'materialized_views.php',
+ 'urlvars' => [
+ 'action' => 'confselectrows',
+ 'matview' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+
+ // Insert is possible if the relevant rule for the view has been created.
+ // 'insert' => array(
+ // 'title' => $lang['strinsert'],
+ // 'url' => "materialized_views.php?action=confinsertrow&amp;{$misc->href}&amp;",
+ // 'vars' => array('view' => 'relname'),
+ // ),
+
+ 'alter' => [
+ 'content' => $lang['stralter'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'viewproperties.php',
+ 'urlvars' => [
+ 'action' => 'confirm_alter',
+ 'matview' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'drop' => [
+ 'multiaction' => 'confirm_drop',
+ 'content' => $lang['strdrop'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'materialized_views.php',
+ 'urlvars' => [
+ 'action' => 'confirm_drop',
+ 'matview' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ echo $this->printTable($matviews, $columns, $actions, $this->table_place, $lang['strnoviews']);
+
+ $navlinks = [
+ 'create' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'materialized_views.php',
+ 'urlvars' => [
+ 'action' => 'create',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreateview'],
+ ],
+ 'createwiz' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'materialized_views.php',
+ 'urlvars' => [
+ 'action' => 'wiz_create',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreateviewwiz'],
+ ],
+ ];
+ $this->printNavLinks($navlinks, $this->table_place, get_defined_vars());
+
+ }
+
+ public function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader('M ' . $lang['strviews']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'selectrows':
+ if (!isset($_REQUEST['cancel'])) {
+ $this->doSelectRows(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confselectrows':
+ $this->doSelectRows(true);
+ break;
+ case 'save_create_wiz':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreateWiz();
+ }
+
+ break;
+ case 'wiz_create':
+ doWizardCreate();
+ break;
+ case 'set_params_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSetParamsCreate();
+ }
+
+ break;
+ case 'save_create':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+}
diff --git a/src/controllers/OpClassesController.php b/src/controllers/OpClassesController.php
index 18a0a066..550c7a31 100644
--- a/src/controllers/OpClassesController.php
+++ b/src/controllers/OpClassesController.php
@@ -9,17 +9,64 @@ use \PHPPgAdmin\Decorators\Decorator;
class OpClassesController extends BaseController {
public $_name = 'OpClassesController';
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['stropclasses']);
+ $misc->printBody();
+
+ switch ($action) {
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
/**
- * Show default list of opclasss in the database
+ * Generate XML for the browser tree.
*/
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $opclasses = $data->getOpClasses();
+
+ // OpClass prototype: "op_class/access_method"
+ $proto = Decorator::concat(Decorator::field('opcname'), '/', Decorator::field('amname'));
+
+ $attrs = [
+ 'text' => $proto,
+ 'icon' => 'OperatorClass',
+ 'toolTip' => Decorator::field('opccomment'),
+ ];
+
+ return $misc->printTree($opclasses, $attrs, 'opclasses');
+ }
+
+ /**
+ * Show default list of opclasss in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'opclasses');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'opclasses');
$misc->printMsg($msg);
$opclasses = $data->getOpClasses();
@@ -50,7 +97,7 @@ class OpClassesController extends BaseController {
$actions = [];
- echo $misc->printTable($opclasses, $columns, $actions, 'opclasses-opclasses', $lang['strnoopclasses']);
+ echo $this->printTable($opclasses, $columns, $actions, 'opclasses-opclasses', $lang['strnoopclasses']);
}
}
diff --git a/src/controllers/OperatorController.php b/src/controllers/OperatorController.php
index 2791d945..ff4b6323 100644
--- a/src/controllers/OperatorController.php
+++ b/src/controllers/OperatorController.php
@@ -9,16 +9,98 @@ use \PHPPgAdmin\Decorators\Decorator;
class OperatorController extends BaseController {
public $_name = 'OperatorController';
-/**
- * Show read only properties for an operator
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['stroperators']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doDrop(false);
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
+ /**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $operators = $data->getOperators();
+
+ // Operator prototype: "type operator type"
+ $proto = Decorator::concat(Decorator::field('oprleftname'), ' ', Decorator::field('oprname'), ' ', Decorator::field('oprrightname'));
+
+ $reqvars = $misc->getRequestVars('operator');
+
+ $attrs = [
+ 'text' => $proto,
+ 'icon' => 'Operator',
+ 'toolTip' => Decorator::field('oprcomment'),
+ 'action' => Decorator::actionurl('operators.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'operator' => $proto,
+ 'operator_oid' => Decorator::field('oid'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($operators, $attrs, 'operators');
+ }
+
+ /**
+ * Show read only properties for an operator
+ */
public function doProperties($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('operator');
+ $this->printTrail('operator');
$misc->printTitle($lang['strproperties'], 'pg.operator');
$misc->printMsg($msg);
@@ -62,7 +144,7 @@ class OperatorController extends BaseController {
}
echo "</table>\n";
- $misc->printNavLinks([
+ $this->printNavLinks([
'showall' => [
'attr' => [
'href' => [
@@ -93,7 +175,7 @@ class OperatorController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('operator');
+ $this->printTrail('operator');
$misc->printTitle($lang['strdrop'], 'pg.operator.drop');
echo "<p>", sprintf($lang['strconfdropoperator'], $misc->printVal($_REQUEST['operator'])), "</p>\n";
@@ -128,8 +210,8 @@ class OperatorController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'operators');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'operators');
$misc->printMsg($msg);
$operators = $data->getOperators();
@@ -181,7 +263,7 @@ class OperatorController extends BaseController {
],
];
- echo $misc->printTable($operators, $columns, $actions, 'operators-operators', $lang['strnooperators']);
+ echo $this->printTable($operators, $columns, $actions, 'operators-operators', $lang['strnooperators']);
// TODO operators.php action=create $lang['strcreateoperator']
}
diff --git a/src/controllers/PrivilegeController.php b/src/controllers/PrivilegeController.php
new file mode 100644
index 00000000..f5ec29b8
--- /dev/null
+++ b/src/controllers/PrivilegeController.php
@@ -0,0 +1,405 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * PrivilegeController controller class
+ */
+class PrivilegeController extends BaseController {
+ public $_name = 'PrivilegeController';
+ public $table_place = 'privileges-privileges';
+
+ function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strprivileges']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'save':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doAlter(false, $_REQUEST['mode']);
+ }
+
+ break;
+ case 'alter':
+ $this->doAlter(true, $_REQUEST['mode']);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+ }
+ /**
+ * Grant permissions on an object to a user
+ * @param $confirm To show entry screen
+ * @param $mode 'grant' or 'revoke'
+ * @param $msg (optional) A message to show
+ */
+ function doAlter($confirm, $mode, $msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_REQUEST['username'])) {
+ $_REQUEST['username'] = [];
+ }
+
+ if (!isset($_REQUEST['groupname'])) {
+ $_REQUEST['groupname'] = [];
+ }
+
+ if (!isset($_REQUEST['privilege'])) {
+ $_REQUEST['privilege'] = [];
+ }
+
+ if ($confirm) {
+ // Get users from the database
+ $users = $data->getUsers();
+ // Get groups from the database
+ $groups = $data->getGroups();
+
+ $misc->printTrail($_REQUEST['subject']);
+
+ switch ($mode) {
+ case 'grant':
+ $misc->printTitle($lang['strgrant'], 'pg.privilege.grant');
+ break;
+ case 'revoke':
+ $misc->printTitle($lang['strrevoke'], 'pg.privilege.revoke');
+ break;
+ }
+ $misc->printMsg($msg);
+
+ echo "<form action=\"/src/views/privileges.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "<tr><th class=\"data left\">{$lang['strusers']}</th>\n";
+ echo "<td class=\"data1\"><select name=\"username[]\" multiple=\"multiple\" size=\"", min(6, $users->recordCount()), "\">\n";
+ while (!$users->EOF) {
+ $uname = htmlspecialchars($users->fields['usename']);
+ echo "<option value=\"{$uname}\"",
+ in_array($users->fields['usename'], $_REQUEST['username']) ? ' selected="selected"' : '', ">{$uname}</option>\n";
+ $users->moveNext();
+ }
+ echo "</select></td></tr>\n";
+ echo "<tr><th class=\"data left\">{$lang['strgroups']}</th>\n";
+ echo "<td class=\"data1\">\n";
+ echo "<input type=\"checkbox\" id=\"public\" name=\"public\"", (isset($_REQUEST['public']) ? ' checked="checked"' : ''), " /><label for=\"public\">PUBLIC</label>\n";
+ // Only show groups if there are groups!
+ if ($groups->recordCount() > 0) {
+ echo "<br /><select name=\"groupname[]\" multiple=\"multiple\" size=\"", min(6, $groups->recordCount()), "\">\n";
+ while (!$groups->EOF) {
+ $gname = htmlspecialchars($groups->fields['groname']);
+ echo "<option value=\"{$gname}\"",
+ in_array($groups->fields['groname'], $_REQUEST['groupname']) ? ' selected="selected"' : '', ">{$gname}</option>\n";
+ $groups->moveNext();
+ }
+ echo "</select>\n";
+ }
+ echo "</td></tr>\n";
+ echo "<tr><th class=\"data left required\">{$lang['strprivileges']}</th>\n";
+ echo "<td class=\"data1\">\n";
+ foreach ($data->privlist[$_REQUEST['subject']] as $v) {
+ $v = htmlspecialchars($v);
+ echo "<input type=\"checkbox\" id=\"privilege[$v]\" name=\"privilege[$v]\"",
+ isset($_REQUEST['privilege'][$v]) ? ' checked="checked"' : '', " /><label for=\"privilege[$v]\">{$v}</label><br />\n";
+ }
+ echo "</td></tr>\n";
+ // Grant option
+ if ($data->hasGrantOption()) {
+ echo "<tr><th class=\"data left\">{$lang['stroptions']}</th>\n";
+ echo "<td class=\"data1\">\n";
+ if ($mode == 'grant') {
+ echo "<input type=\"checkbox\" id=\"grantoption\" name=\"grantoption\"",
+ isset($_REQUEST['grantoption']) ? ' checked="checked"' : '', " /><label for=\"grantoption\">GRANT OPTION</label>\n";
+ } elseif ($mode == 'revoke') {
+ echo "<input type=\"checkbox\" id=\"grantoption\" name=\"grantoption\"",
+ isset($_REQUEST['grantoption']) ? ' checked="checked"' : '', " /><label for=\"grantoption\">GRANT OPTION FOR</label><br />\n";
+ echo "<input type=\"checkbox\" id=\"cascade\" name=\"cascade\"",
+ isset($_REQUEST['cascade']) ? ' checked="checked"' : '', " /><label for=\"cascade\">CASCADE</label><br />\n";
+ }
+ echo "</td></tr>\n";
+ }
+ echo "</table>\n";
+
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save\" />\n";
+ echo "<input type=\"hidden\" name=\"mode\" value=\"", htmlspecialchars($mode), "\" />\n";
+ echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
+ if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
+ echo "<input type=\"hidden\" name=\"", htmlspecialchars($_REQUEST['subject'] . '_oid'),
+ "\" value=\"", htmlspecialchars($_REQUEST[$_REQUEST['subject'] . '_oid']), "\" />\n";
+ }
+
+ echo "<input type=\"hidden\" name=\"", htmlspecialchars($_REQUEST['subject']),
+ "\" value=\"", htmlspecialchars($_REQUEST[$_REQUEST['subject']]), "\" />\n";
+ if ($_REQUEST['subject'] == 'column') {
+ echo "<input type=\"hidden\" name=\"table\" value=\"",
+ htmlspecialchars($_REQUEST['table']), "\" />\n";
+ }
+
+ echo $misc->form;
+ if ($mode == 'grant') {
+ echo "<input type=\"submit\" name=\"grant\" value=\"{$lang['strgrant']}\" />\n";
+ } elseif ($mode == 'revoke') {
+ echo "<input type=\"submit\" name=\"revoke\" value=\"{$lang['strrevoke']}\" />\n";
+ }
+
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>";
+ echo "</form>\n";
+ } else {
+ // Determine whether object should be ref'd by name or oid.
+ if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
+ $object = $_REQUEST[$_REQUEST['subject'] . '_oid'];
+ } else {
+ $object = $_REQUEST[$_REQUEST['subject']];
+ }
+
+ if (isset($_REQUEST['table'])) {
+ $table = $_REQUEST['table'];
+ } else {
+ $table = null;
+ }
+
+ $status = $data->setPrivileges(($mode == 'grant') ? 'GRANT' : 'REVOKE', $_REQUEST['subject'], $object,
+ isset($_REQUEST['public']), $_REQUEST['username'], $_REQUEST['groupname'], array_keys($_REQUEST['privilege']),
+ isset($_REQUEST['grantoption']), isset($_REQUEST['cascade']), $table);
+
+ if ($status == 0) {
+ doDefault($lang['strgranted']);
+ } elseif ($status == -3 || $status == -4) {
+ doAlter(true, $_REQUEST['mode'], $lang['strgrantbad']);
+ } else {
+ doAlter(true, $_REQUEST['mode'], $lang['strgrantfailed']);
+ }
+
+ }
+ }
+
+ /**
+ * Show permissions on a database, namespace, relation, language or function
+ */
+ function doDefault($msg = '') {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+ $database = $misc->getDatabase();
+
+ $misc->printTrail($_REQUEST['subject']);
+
+ # @@@FIXME: This switch is just a temporary solution,
+ # need a better way, maybe every type of object should
+ # have a tab bar???
+ switch ($_REQUEST['subject']) {
+ case 'server':
+ case 'database':
+ case 'schema':
+ case 'table':
+ case 'column':
+ case 'view':
+ $misc->printTabs($_REQUEST['subject'], 'privileges');
+ break;
+ default:
+ $misc->printTitle($lang['strprivileges'], 'pg.privilege');
+ }
+ $misc->printMsg($msg);
+
+ // Determine whether object should be ref'd by name or oid.
+ if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
+ $object = $_REQUEST[$_REQUEST['subject'] . '_oid'];
+ } else {
+ $object = $_REQUEST[$_REQUEST['subject']];
+ }
+
+ // Get the privileges on the object, given its type
+ if ($_REQUEST['subject'] == 'column') {
+ $privileges = $data->getPrivileges($object, 'column', $_REQUEST['table']);
+ } else {
+ $privileges = $data->getPrivileges($object, $_REQUEST['subject']);
+ }
+
+ if (sizeof($privileges) > 0) {
+ echo "<table>\n";
+ if ($data->hasRoles()) {
+ echo "<tr><th class=\"data\">{$lang['strrole']}</th>";
+ } else {
+ echo "<tr><th class=\"data\">{$lang['strtype']}</th><th class=\"data\">{$lang['struser']}/{$lang['strgroup']}</th>";
+ }
+
+ foreach ($data->privlist[$_REQUEST['subject']] as $v2) {
+ // Skip over ALL PRIVILEGES
+ if ($v2 == 'ALL PRIVILEGES') {
+ continue;
+ }
+
+ echo "<th class=\"data\">{$v2}</th>\n";
+ }
+ if ($data->hasGrantOption()) {
+ echo "<th class=\"data\">{$lang['strgrantor']}</th>";
+ }
+ echo "</tr>\n";
+
+ // Loop over privileges, outputting them
+ $i = 0;
+ foreach ($privileges as $v) {
+ $id = (($i % 2) == 0 ? '1' : '2');
+ echo "<tr class=\"data{$id}\">\n";
+ if (!$data->hasRoles()) {
+ echo "<td>", $misc->printVal($v[0]), "</td>\n";
+ }
+
+ echo "<td>", $misc->printVal($v[1]), "</td>\n";
+ foreach ($data->privlist[$_REQUEST['subject']] as $v2) {
+ // Skip over ALL PRIVILEGES
+ if ($v2 == 'ALL PRIVILEGES') {
+ continue;
+ }
+
+ echo "<td>";
+ if (in_array($v2, $v[2])) {
+ echo $lang['stryes'];
+ } else {
+ echo $lang['strno'];
+ }
+
+ // If we have grant option for this, end mark
+ if ($data->hasGrantOption() && in_array($v2, $v[4])) {
+ echo $lang['strasterisk'];
+ }
+
+ echo "</td>\n";
+ }
+ if ($data->hasGrantOption()) {
+ echo "<td>", $misc->printVal($v[3]), "</td>\n";
+ }
+ echo "</tr>\n";
+ $i++;
+ }
+
+ echo "</table>";
+ } else {
+ echo "<p>{$lang['strnoprivileges']}</p>\n";
+ }
+
+ // Links for granting to a user or group
+ switch ($_REQUEST['subject']) {
+ case 'table':
+ case 'view':
+ case 'sequence':
+ case 'function':
+ case 'tablespace':
+ $alllabel = "showall{$_REQUEST['subject']}s";
+ $allurl = "{$_REQUEST['subject']}s.php";
+ $alltxt = $lang["strshowall{$_REQUEST['subject']}s"];
+ break;
+ case 'schema':
+ $alllabel = "showallschemas";
+ $allurl = "schemas.php";
+ $alltxt = $lang["strshowallschemas"];
+ break;
+ case 'database':
+ $alllabel = "showalldatabases";
+ $allurl = 'all_db.php';
+ $alltxt = $lang['strshowalldatabases'];
+ break;
+ }
+
+ $subject = $_REQUEST['subject'];
+ $object = $_REQUEST[$_REQUEST['subject']];
+
+ if ($_REQUEST['subject'] == 'function') {
+ $objectoid = $_REQUEST[$_REQUEST['subject'] . '_oid'];
+ $urlvars = [
+ 'action' => 'alter',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ $subject => $object,
+ "{$subject}_oid" => $objectoid,
+ 'subject' => $subject,
+ ];
+ } else if ($_REQUEST['subject'] == 'column') {
+ $urlvars = [
+ 'action' => 'alter',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ $subject => $object,
+ 'subject' => $subject,
+ ];
+
+ if (isset($_REQUEST['table'])) {
+ $urlvars['table'] = $_REQUEST['table'];
+ } else {
+ $urlvars['view'] = $_REQUEST['view'];
+ }
+
+ } else {
+ $urlvars = [
+ 'action' => 'alter',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ $subject => $object,
+ 'subject' => $subject,
+ ];
+ if (isset($_REQUEST['schema'])) {
+ $urlvars['schema'] = $_REQUEST['schema'];
+ }
+ }
+
+ $navlinks = [
+ 'grant' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'privileges.php',
+ 'urlvars' => array_merge($urlvars, ['mode' => 'grant']),
+ ],
+ ],
+ 'content' => $lang['strgrant'],
+ ],
+ 'revoke' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'privileges.php',
+ 'urlvars' => array_merge($urlvars, ['mode' => 'revoke']),
+ ],
+ ],
+ 'content' => $lang['strrevoke'],
+ ],
+ ];
+
+ if (isset($allurl)) {
+ $navlinks[$alllabel] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => $allurl,
+ 'urlvars' => [
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ ],
+ ],
+ ],
+ 'content' => $alltxt,
+ ];
+ if (isset($_REQUEST['schema'])) {
+ $navlinks[$alllabel]['attr']['href']['urlvars']['schema'] = $_REQUEST['schema'];
+ }
+ }
+
+ $misc->printNavLinks($navlinks, $this->table_place, get_defined_vars());
+ }
+
+}
diff --git a/src/controllers/RolesController.php b/src/controllers/RolesController.php
new file mode 100644
index 00000000..58596345
--- /dev/null
+++ b/src/controllers/RolesController.php
@@ -0,0 +1,860 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Base controller class
+ */
+class RolesController extends BaseController {
+ public $_name = 'RolesController';
+
+ /**
+ * Show default list of roles in the database
+ */
+ function doDefault($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $renderRoleConnLimit = function ($val) use ($lang) {
+ return $val == '-1' ? $lang['strnolimit'] : htmlspecialchars($val);
+ };
+
+ $renderRoleExpires = function ($val) use ($lang) {
+ return $val == 'infinity' ? $lang['strnever'] : htmlspecialchars($val);
+ };
+
+ $this->printTrail('server');
+ $this->printTabs('server', 'roles');
+ $misc->printMsg($msg);
+
+ $roles = $data->getRoles();
+
+ $columns = [
+ 'role' => [
+ 'title' => $lang['strrole'],
+ 'field' => Decorator::field('rolname'),
+ 'url' => "/redirect/role?action=properties&amp;{$misc->href}&amp;",
+ 'vars' => ['rolename' => 'rolname'],
+ ],
+ 'superuser' => [
+ 'title' => $lang['strsuper'],
+ 'field' => Decorator::field('rolsuper'),
+ 'type' => 'yesno',
+ ],
+ 'createdb' => [
+ 'title' => $lang['strcreatedb'],
+ 'field' => Decorator::field('rolcreatedb'),
+ 'type' => 'yesno',
+ ],
+ 'createrole' => [
+ 'title' => $lang['strcancreaterole'],
+ 'field' => Decorator::field('rolcreaterole'),
+ 'type' => 'yesno',
+ ],
+ 'inherits' => [
+ 'title' => $lang['strinheritsprivs'],
+ 'field' => Decorator::field('rolinherit'),
+ 'type' => 'yesno',
+ ],
+ 'canloging' => [
+ 'title' => $lang['strcanlogin'],
+ 'field' => Decorator::field('rolcanlogin'),
+ 'type' => 'yesno',
+ ],
+ 'connlimit' => [
+ 'title' => $lang['strconnlimit'],
+ 'field' => Decorator::field('rolconnlimit'),
+ 'type' => 'callback',
+ 'params' => ['function' => $renderRoleConnLimit],
+ ],
+ 'expires' => [
+ 'title' => $lang['strexpires'],
+ 'field' => Decorator::field('rolvaliduntil'),
+ 'type' => 'callback',
+ 'params' => ['function' => $renderRoleExpires, 'null' => $lang['strnever']],
+ ],
+ 'actions' => [
+ 'title' => $lang['stractions'],
+ ],
+ ];
+
+ $actions = [
+ 'alter' => [
+ 'content' => $lang['stralter'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'alter',
+ 'rolename' => Decorator::field('rolname'),
+ ],
+ ],
+ ],
+ ],
+ 'drop' => [
+ 'content' => $lang['strdrop'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'confirm_drop',
+ 'rolename' => Decorator::field('rolname'),
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ echo $this->printTable($roles, $columns, $actions, 'roles-roles', $lang['strnoroles']);
+
+ $navlinks = [
+ 'create' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'create',
+ 'server' => $_REQUEST['server'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreaterole'],
+ ],
+ ];
+ $this->printNavLinks($navlinks, 'roles-roles', get_defined_vars());
+ }
+
+ function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action = $this->action;
+
+ $misc->printHeader($lang['strroles']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'save_create':
+ if (isset($_POST['create'])) {
+ $this->doSaveCreate();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'alter':
+ $this->doAlter();
+ break;
+ case 'save_alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ case 'confchangepassword':
+ $this->doChangePassword(true);
+ break;
+ case 'changepassword':
+ if (isset($_REQUEST['ok'])) {
+ $this->doChangePassword(false);
+ } else {
+ $this->doAccount();
+ }
+
+ break;
+ case 'account':
+ $this->doAccount();
+ break;
+ default:
+ $this->doDefault();
+ }
+
+ $misc->printFooter();
+ }
+
+ /**
+ * Displays a screen for create a new role
+ */
+ function doCreate($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_POST['formRolename'])) {
+ $_POST['formRolename'] = '';
+ }
+
+ if (!isset($_POST['formPassword'])) {
+ $_POST['formPassword'] = '';
+ }
+
+ if (!isset($_POST['formConfirm'])) {
+ $_POST['formConfirm'] = '';
+ }
+
+ if (!isset($_POST['formConnLimit'])) {
+ $_POST['formConnLimit'] = '';
+ }
+
+ if (!isset($_POST['formExpires'])) {
+ $_POST['formExpires'] = '';
+ }
+
+ if (!isset($_POST['memberof'])) {
+ $_POST['memberof'] = [];
+ }
+
+ if (!isset($_POST['members'])) {
+ $_POST['members'] = [];
+ }
+
+ if (!isset($_POST['adminmembers'])) {
+ $_POST['adminmembers'] = [];
+ }
+
+ $this->printTrail('role');
+ $misc->printTitle($lang['strcreaterole'], 'pg.role.create');
+ $misc->printMsg($msg);
+
+ echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\" style=\"width: 130px\">{$lang['strname']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"15\" maxlength=\"{$data->_maxNameLen}\" name=\"formRolename\" value=\"", htmlspecialchars($_POST['formRolename']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strpassword']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formPassword\" value=\"", htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconfirm']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formConfirm\" value=\"", htmlspecialchars($_POST['formConfirm']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$lang['strsuper']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
+ (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$lang['strcreatedb']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
+ (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateRole\">{$lang['strcancreaterole']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateRole\" name=\"formCreateRole\"",
+ (isset($_POST['formCreateRole'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formInherits\">{$lang['strinheritsprivs']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formInherits\" name=\"formInherits\"",
+ (isset($_POST['formInherits'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCanLogin\">{$lang['strcanlogin']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCanLogin\" name=\"formCanLogin\"",
+ (isset($_POST['formCanLogin'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconnlimit']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"4\" name=\"formConnLimit\" value=\"", htmlspecialchars($_POST['formConnLimit']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strexpires']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"23\" name=\"formExpires\" value=\"", htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>\n";
+
+ $roles = $data->getRoles();
+ if ($roles->recordCount() > 0) {
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmemberof']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"memberof[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['memberof']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+
+ $roles->moveFirst();
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmembers']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"members[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['members']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+
+ $roles->moveFirst();
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['stradminmembers']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"adminmembers[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['adminmembers']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+ }
+
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" name=\"create\" value=\"{$lang['strcreate']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ }
+
+ /**
+ * Actually creates the new role in the database
+ */
+ function doSaveCreate() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_POST['memberof'])) {
+ $_POST['memberof'] = [];
+ }
+
+ if (!isset($_POST['members'])) {
+ $_POST['members'] = [];
+ }
+
+ if (!isset($_POST['adminmembers'])) {
+ $_POST['adminmembers'] = [];
+ }
+
+ // Check data
+ if ($_POST['formRolename'] == '') {
+ $this->doCreate($lang['strroleneedsname']);
+ } else if ($_POST['formPassword'] != $_POST['formConfirm']) {
+ $this->doCreate($lang['strpasswordconfirm']);
+ } else {
+ $status = $data->createRole($_POST['formRolename'], $_POST['formPassword'], isset($_POST['formSuper']),
+ isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']),
+ isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'],
+ $_POST['adminmembers']);
+ if ($status == 0) {
+ $this->doDefault($lang['strrolecreated']);
+ } else {
+ $this->doCreate($lang['strrolecreatedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Function to allow alter a role
+ */
+ function doAlter($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('role');
+ $misc->printTitle($lang['stralter'], 'pg.role.alter');
+ $misc->printMsg($msg);
+
+ $roledata = $data->getRole($_REQUEST['rolename']);
+
+ if ($roledata->recordCount() > 0) {
+ $server_info = $misc->getServerInfo();
+ $canRename = $data->hasUserRename() && ($_REQUEST['rolename'] != $server_info['username']);
+ $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
+ $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
+ $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
+ $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
+ $roledata->fields['rolcanlogin'] = $data->phpBool($roledata->fields['rolcanlogin']);
+
+ if (!isset($_POST['formExpires'])) {
+ if ($canRename) {
+ $_POST['formNewRoleName'] = $roledata->fields['rolname'];
+ }
+
+ if ($roledata->fields['rolsuper']) {
+ $_POST['formSuper'] = '';
+ }
+
+ if ($roledata->fields['rolcreatedb']) {
+ $_POST['formCreateDB'] = '';
+ }
+
+ if ($roledata->fields['rolcreaterole']) {
+ $_POST['formCreateRole'] = '';
+ }
+
+ if ($roledata->fields['rolinherit']) {
+ $_POST['formInherits'] = '';
+ }
+
+ if ($roledata->fields['rolcanlogin']) {
+ $_POST['formCanLogin'] = '';
+ }
+
+ $_POST['formConnLimit'] = $roledata->fields['rolconnlimit'] == '-1' ? '' : $roledata->fields['rolconnlimit'];
+ $_POST['formExpires'] = $roledata->fields['rolvaliduntil'] == 'infinity' ? '' : $roledata->fields['rolvaliduntil'];
+ $_POST['formPassword'] = '';
+ }
+
+ echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\" style=\"width: 130px\">{$lang['strname']}</th>\n";
+ echo "\t\t<td class=\"data1\">", ($canRename ? "<input name=\"formNewRoleName\" size=\"15\" maxlength=\"{$data->_maxNameLen}\" value=\"" . htmlspecialchars($_POST['formNewRoleName']) . "\" />" : $misc->printVal($roledata->fields['rolname'])), "</td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strpassword']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"15\" name=\"formPassword\" value=\"", htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconfirm']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"15\" name=\"formConfirm\" value=\"\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$lang['strsuper']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
+ (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$lang['strcreatedb']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
+ (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateRole\">{$lang['strcancreaterole']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateRole\" name=\"formCreateRole\"",
+ (isset($_POST['formCreateRole'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formInherits\">{$lang['strinheritsprivs']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formInherits\" name=\"formInherits\"",
+ (isset($_POST['formInherits'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCanLogin\">{$lang['strcanlogin']}</label></th>\n";
+ echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCanLogin\" name=\"formCanLogin\"",
+ (isset($_POST['formCanLogin'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconnlimit']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"4\" name=\"formConnLimit\" value=\"", htmlspecialchars($_POST['formConnLimit']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strexpires']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"23\" name=\"formExpires\" value=\"", htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>\n";
+
+ if (!isset($_POST['memberof'])) {
+ $memberof = $data->getMemberOf($_REQUEST['rolename']);
+ if ($memberof->recordCount() > 0) {
+ $i = 0;
+ while (!$memberof->EOF) {
+ $_POST['memberof'][$i++] = $memberof->fields['rolname'];
+ $memberof->moveNext();
+ }
+ } else {
+ $_POST['memberof'] = [];
+ }
+
+ $memberofold = implode(',', $_POST['memberof']);
+ }
+ if (!isset($_POST['members'])) {
+ $members = $data->getMembers($_REQUEST['rolename']);
+ if ($members->recordCount() > 0) {
+ $i = 0;
+ while (!$members->EOF) {
+ $_POST['members'][$i++] = $members->fields['rolname'];
+ $members->moveNext();
+ }
+ } else {
+ $_POST['members'] = [];
+ }
+
+ $membersold = implode(',', $_POST['members']);
+ }
+ if (!isset($_POST['adminmembers'])) {
+ $adminmembers = $data->getMembers($_REQUEST['rolename'], 't');
+ if ($adminmembers->recordCount() > 0) {
+ $i = 0;
+ while (!$adminmembers->EOF) {
+ $_POST['adminmembers'][$i++] = $adminmembers->fields['rolname'];
+ $adminmembers->moveNext();
+ }
+ } else {
+ $_POST['adminmembers'] = [];
+ }
+
+ $adminmembersold = implode(',', $_POST['adminmembers']);
+ }
+
+ $roles = $data->getRoles($_REQUEST['rolename']);
+ if ($roles->recordCount() > 0) {
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmemberof']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"memberof[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['memberof']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+
+ $roles->moveFirst();
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmembers']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"members[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['members']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+
+ $roles->moveFirst();
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['stradminmembers']}</th>\n";
+ echo "\t\t<td class=\"data\">\n";
+ echo "\t\t\t<select name=\"adminmembers[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
+ while (!$roles->EOF) {
+ $rolename = $roles->fields['rolname'];
+ echo "\t\t\t\t<option value=\"{$rolename}\"",
+ (in_array($rolename, $_POST['adminmembers']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
+ $roles->moveNext();
+ }
+ echo "\t\t\t</select>\n";
+ echo "\t\t</td>\n\t</tr>\n";
+ }
+ echo "</table>\n";
+
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_alter\" />\n";
+ echo "<input type=\"hidden\" name=\"rolename\" value=\"", htmlspecialchars($_REQUEST['rolename']), "\" />\n";
+ echo "<input type=\"hidden\" name=\"memberofold\" value=\"", isset($_POST['memberofold']) ? $_POST['memberofold'] : htmlspecialchars($memberofold), "\" />\n";
+ echo "<input type=\"hidden\" name=\"membersold\" value=\"", isset($_POST['membersold']) ? $_POST['membersold'] : htmlspecialchars($membersold), "\" />\n";
+ echo "<input type=\"hidden\" name=\"adminmembersold\" value=\"", isset($_POST['adminmembersold']) ? $_POST['adminmembersold'] : htmlspecialchars($adminmembersold), "\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" name=\"alter\" value=\"{$lang['stralter']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ } else {
+ echo "<p>{$lang['strnodata']}</p>\n";
+ }
+
+ }
+
+ /**
+ * Function to save after editing a role
+ */
+ function doSaveAlter() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_POST['memberof'])) {
+ $_POST['memberof'] = [];
+ }
+
+ if (!isset($_POST['members'])) {
+ $_POST['members'] = [];
+ }
+
+ if (!isset($_POST['adminmembers'])) {
+ $_POST['adminmembers'] = [];
+ }
+
+ // Check name and password
+ if (isset($_POST['formNewRoleName']) && $_POST['formNewRoleName'] == '') {
+ $this->doAlter($lang['strroleneedsname']);
+ } else if ($_POST['formPassword'] != $_POST['formConfirm']) {
+ $this->doAlter($lang['strpasswordconfirm']);
+ } else {
+ if (isset($_POST['formNewRoleName'])) {
+ $status = $data->setRenameRole($_POST['rolename'], $_POST['formPassword'], isset($_POST['formSuper']), isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']), isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'], $_POST['adminmembers'], $_POST['memberofold'], $_POST['membersold'], $_POST['adminmembersold'], $_POST['formNewRoleName']);
+ } else {
+ $status = $data->setRole($_POST['rolename'], $_POST['formPassword'], isset($_POST['formSuper']), isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']), isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'], $_POST['adminmembers'], $_POST['memberofold'], $_POST['membersold'], $_POST['adminmembersold']);
+ }
+
+ if ($status == 0) {
+ $this->doDefault($lang['strrolealtered']);
+ } else {
+ $this->doAlter($lang['strrolealteredbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Show confirmation of drop a role and perform actual drop
+ */
+ function doDrop($confirm) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if ($confirm) {
+ $this->printTrail('role');
+ $misc->printTitle($lang['strdroprole'], 'pg.role.drop');
+
+ echo "<p>", sprintf($lang['strconfdroprole'], $misc->printVal($_REQUEST['rolename'])), "</p>\n";
+
+ echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
+ echo "<input type=\"hidden\" name=\"rolename\" value=\"", htmlspecialchars($_REQUEST['rolename']), "\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ } else {
+ $status = $data->dropRole($_REQUEST['rolename']);
+ if ($status == 0) {
+ $this->doDefault($lang['strroledropped']);
+ } else {
+ $this->doDefault($lang['strroledroppedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Show the properties of a role
+ */
+ function doProperties($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('role');
+ $misc->printTitle($lang['strproperties'], 'pg.role');
+ $misc->printMsg($msg);
+
+ $roledata = $data->getRole($_REQUEST['rolename']);
+ if ($roledata->recordCount() > 0) {
+ $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
+ $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
+ $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
+ $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
+ $roledata->fields['rolcanlogin'] = $data->phpBool($roledata->fields['rolcanlogin']);
+
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data\" style=\"width: 130px\">Description</th>\n";
+ echo "\t\t<th class=\"data\" style=\"width: 120\">Value</th>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strname']}</td>\n";
+ echo "\t\t<td class=\"data1\">", htmlspecialchars($_REQUEST['rolename']), "</td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strsuper']}</td>\n";
+ echo "\t\t<td class=\"data2\">", (($roledata->fields['rolsuper']) ? $lang['stryes'] : $lang['strno']), "</td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strcreatedb']}</td>\n";
+ echo "\t\t<td class=\"data1\">", (($roledata->fields['rolcreatedb']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strcancreaterole']}</td>\n";
+ echo "\t\t<td class=\"data2\">", (($roledata->fields['rolcreaterole']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strinheritsprivs']}</td>\n";
+ echo "\t\t<td class=\"data1\">", (($roledata->fields['rolinherit']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strcanlogin']}</td>\n";
+ echo "\t\t<td class=\"data2\">", (($roledata->fields['rolcanlogin']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strconnlimit']}</td>\n";
+ echo "\t\t<td class=\"data1\">", ($roledata->fields['rolconnlimit'] == '-1' ? $lang['strnolimit'] : $misc->printVal($roledata->fields['rolconnlimit'])), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strexpires']}</td>\n";
+ echo "\t\t<td class=\"data2\">", ($roledata->fields['rolvaliduntil'] == 'infinity' || is_null($roledata->fields['rolvaliduntil']) ? $lang['strnever'] : $misc->printVal($roledata->fields['rolvaliduntil'])), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strsessiondefaults']}</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolconfig']), "</td>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strmemberof']}</td>\n";
+ echo "\t\t<td class=\"data2\">";
+ $memberof = $data->getMemberOf($_REQUEST['rolename']);
+ if ($memberof->recordCount() > 0) {
+ while (!$memberof->EOF) {
+ echo $misc->printVal($memberof->fields['rolname']), "<br />\n";
+ $memberof->moveNext();
+ }
+ }
+ echo "</td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strmembers']}</td>\n";
+ echo "\t\t<td class=\"data1\">";
+ $members = $data->getMembers($_REQUEST['rolename']);
+ if ($members->recordCount() > 0) {
+ while (!$members->EOF) {
+ echo $misc->printVal($members->fields['rolname']), "<br />\n";
+ $members->moveNext();
+ }
+ }
+ echo "</td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['stradminmembers']}</td>\n";
+ echo "\t\t<td class=\"data2\">";
+ $adminmembers = $data->getMembers($_REQUEST['rolename'], 't');
+ if ($adminmembers->recordCount() > 0) {
+ while (!$adminmembers->EOF) {
+ echo $misc->printVal($adminmembers->fields['rolname']), "<br />\n";
+ $adminmembers->moveNext();
+ }
+ }
+ echo "</td>\n\t</tr>\n";
+ echo "</table>\n";
+ } else {
+ echo "<p>{$lang['strnodata']}</p>\n";
+ }
+
+ $navlinks = [
+ 'showall' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'server' => $_REQUEST['server'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strshowallroles'],
+ ],
+ 'alter' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'alter',
+ 'server' => $_REQUEST['server'],
+ 'rolename' => $_REQUEST['rolename'],
+ ],
+ ],
+ ],
+ 'content' => $lang['stralter'],
+ ],
+ 'drop' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'confirm_drop',
+ 'server' => $_REQUEST['server'],
+ 'rolename' => $_REQUEST['rolename'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strdrop'],
+ ],
+ ];
+
+ $this->printNavLinks($navlinks, 'roles-properties', get_defined_vars());
+ }
+
+ /**
+ * If a role is not a superuser role, then we have an 'account management'
+ * page for change his password, etc. We don't prevent them from
+ * messing with the URL to gain access to other role admin stuff, because
+ * the PostgreSQL permissions will prevent them changing anything anyway.
+ */
+ function doAccount($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $server_info = $misc->getServerInfo();
+
+ $roledata = $data->getRole($server_info['username']);
+ $_REQUEST['rolename'] = $server_info['username'];
+
+ $this->printTrail('role');
+ $this->printTabs('server', 'account');
+ $misc->printMsg($msg);
+
+ if ($roledata->recordCount() > 0) {
+ $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
+ $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
+ $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
+ $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data\">{$lang['strname']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strsuper']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strcreatedb']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strcancreaterole']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strinheritsprivs']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strconnlimit']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strexpires']}</th>\n";
+ echo "\t\t<th class=\"data\">{$lang['strsessiondefaults']}</th>\n";
+ echo "\t</tr>\n";
+ echo "\t<tr>\n\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolname']), "</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolsuper'], 'yesno'), "</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolcreatedb'], 'yesno'), "</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolcreaterole'], 'yesno'), "</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolinherit'], 'yesno'), "</td>\n";
+ echo "\t\t<td class=\"data1\">", ($roledata->fields['rolconnlimit'] == '-1' ? $lang['strnolimit'] : $misc->printVal($roledata->fields['rolconnlimit'])), "</td>\n";
+ echo "\t\t<td class=\"data1\">", ($roledata->fields['rolvaliduntil'] == 'infinity' || is_null($roledata->fields['rolvaliduntil']) ? $lang['strnever'] : $misc->printVal($roledata->fields['rolvaliduntil'])), "</td>\n";
+ echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolconfig']), "</td>\n";
+ echo "\t</tr>\n</table>\n";
+ } else {
+ echo "<p>{$lang['strnodata']}</p>\n";
+ }
+
+ $this->printNavLinks(['changepassword' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'roles.php',
+ 'urlvars' => [
+ 'action' => 'confchangepassword',
+ 'server' => $_REQUEST['server'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strchangepassword'],
+ ]], 'roles-account', get_defined_vars());
+ }
+
+ /**
+ * Show confirmation of change password and actually change password
+ */
+ function doChangePassword($confirm, $msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $server_info = $misc->getServerInfo();
+
+ if ($confirm) {
+ $_REQUEST['rolename'] = $server_info['username'];
+ $this->printTrail('role');
+ $misc->printTitle($lang['strchangepassword'], 'pg.role.alter');
+ $misc->printMsg($msg);
+
+ if (!isset($_POST['password'])) {
+ $_POST['password'] = '';
+ }
+
+ if (!isset($_POST['confirm'])) {
+ $_POST['confirm'] = '';
+ }
+
+ echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strpassword']}</th>\n";
+ echo "\t\t<td><input type=\"password\" name=\"password\" size=\"32\" value=\"",
+ htmlspecialchars($_POST['password']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strconfirm']}</th>\n";
+ echo "\t\t<td><input type=\"password\" name=\"confirm\" size=\"32\" value=\"\" /></td>\n\t</tr>\n";
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"changepassword\" />\n";
+ echo $misc->form;
+ echo "<input type=\"submit\" name=\"ok\" value=\"{$lang['strok']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+ echo "</p></form>\n";
+ } else {
+ // Check that password is minimum length
+ if (strlen($_POST['password']) < $conf['min_password_length']) {
+ $this->doChangePassword(true, $lang['strpasswordshort']);
+ }
+
+ // Check that password matches confirmation password
+ elseif ($_POST['password'] != $_POST['confirm']) {
+ $this->doChangePassword(true, $lang['strpasswordconfirm']);
+ } else {
+ $status = $data->changePassword($server_info['username'], $_POST['password']);
+ if ($status == 0) {
+ $this->doAccount($lang['strpasswordchanged']);
+ } else {
+ $this->doAccount($lang['strpasswordchangedbad']);
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/src/controllers/RuleController.php b/src/controllers/RuleController.php
index a19be99e..84aadf82 100644
--- a/src/controllers/RuleController.php
+++ b/src/controllers/RuleController.php
@@ -9,9 +9,74 @@ use \PHPPgAdmin\Decorators\Decorator;
class RuleController extends BaseController {
public $_name = 'RuleController';
-/**
- * Confirm and then actually create a rule
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ // Different header if we're view rules or table rules
+ $misc->printHeader($_REQUEST[$_REQUEST['subject']] . ' - ' . $lang['strrules']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create_rule':
+ $this->createRule(true);
+ break;
+ case 'save_create_rule':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->createRule(false);
+ }
+
+ break;
+ case 'drop':
+ if (isset($_POST['yes'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $rules = $data->getRules($_REQUEST[$_REQUEST['subject']]);
+
+ $reqvars = $misc->getRequestVars($_REQUEST['subject']);
+
+ $attrs = [
+ 'text' => Decorator::field('rulename'),
+ 'icon' => 'Rule',
+ ];
+
+ return $misc->printTree($rules, $attrs, 'rules');
+ }
+
+ /**
+ * Confirm and then actually create a rule
+ */
function createRule($confirm, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -39,7 +104,7 @@ class RuleController extends BaseController {
}
if ($confirm) {
- $misc->printTrail($_REQUEST['subject']);
+ $this->printTrail($_REQUEST['subject']);
$misc->printTitle($lang['strcreaterule'], 'pg.rule.create');
$misc->printMsg($msg);
@@ -106,7 +171,7 @@ class RuleController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail($_REQUEST['subject']);
+ $this->printTrail($_REQUEST['subject']);
$misc->printTitle($lang['strdrop'], 'pg.rule.drop');
echo "<p>", sprintf($lang['strconfdroprule'], $misc->printVal($_REQUEST['rule']),
@@ -144,8 +209,8 @@ class RuleController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail($_REQUEST['subject']);
- $misc->printTabs($_REQUEST['subject'], 'rules');
+ $this->printTrail($_REQUEST['subject']);
+ $this->printTabs($_REQUEST['subject'], 'rules');
$misc->printMsg($msg);
$rules = $data->getRules($_REQUEST[$_REQUEST['subject']]);
@@ -185,9 +250,9 @@ class RuleController extends BaseController {
],
];
- echo $misc->printTable($rules, $columns, $actions, 'rules-rules', $lang['strnorules']);
+ echo $this->printTable($rules, $columns, $actions, 'rules-rules', $lang['strnorules']);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'rules.php',
diff --git a/src/controllers/SQLEditController.php b/src/controllers/SQLEditController.php
new file mode 100644
index 00000000..ecc54880
--- /dev/null
+++ b/src/controllers/SQLEditController.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class SQLEditController extends BaseController {
+ public $_name = 'SQLEditController';
+ public $query = '';
+ public $subject = '';
+ public $start_time = null;
+ public $duration = null;
+
+/**
+ * Private function to display server and list of databases
+ */
+ function _printConnection($action) {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // The javascript action on the select box reloads the
+ // popup whenever the server or database is changed.
+ // This ensures that the correct page encoding is used.
+ $onchange = "onchange=\"location.href='/sqledit/" .
+ urlencode($action) . "?server=' + encodeURI(server.options[server.selectedIndex].value) + '&amp;database=' + encodeURI(database.options[database.selectedIndex].value) + ";
+
+ // The exact URL to reload to is different between SQL and Find mode, however.
+ if ($action == 'find') {
+ $onchange .= "'&amp;term=' + encodeURI(term.value) + '&amp;filter=' + encodeURI(filter.value) + '&amp;'\"";
+ } else {
+ $onchange .= "'&amp;query=' + encodeURI(query.value) + '&amp;search_path=' + encodeURI(search_path.value) + (paginate.checked ? '&amp;paginate=on' : '') + '&amp;'\"";
+ }
+
+ return $misc->printConnection($onchange, false);
+ }
+
+ /**
+ * Searches for a named database object
+ */
+ function doFind() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_REQUEST['term'])) {
+ $_REQUEST['term'] = '';
+ }
+
+ if (!isset($_REQUEST['filter'])) {
+ $_REQUEST['filter'] = '';
+ }
+
+ $default_html = $this->printTabs($misc->getNavTabs('popup'), 'find', false);
+
+ $default_html .= "<form action=\"database.php\" method=\"post\" target=\"detail\">\n";
+ $default_html .= $this->_printConnection('find');
+ $default_html .= "<p><input class=\"focusme\" name=\"term\" value=\"" . htmlspecialchars($_REQUEST['term']) . "\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" />\n";
+
+ // Output list of filters. This is complex due to all the 'has' and 'conf' feature possibilities
+ $default_html .= "<select name=\"filter\">\n";
+ $default_html .= "\t<option value=\"\"" . ($_REQUEST['filter'] == '' ? ' selected="selected" ' : '') . ">{$lang['strallobjects']}</option>\n";
+ $default_html .= "\t<option value=\"SCHEMA\"" . ($_REQUEST['filter'] == 'SCHEMA' ? ' selected="selected" ' : '') . ">{$lang['strschemas']}</option>\n";
+ $default_html .= "\t<option value=\"TABLE\"" . ($_REQUEST['filter'] == 'TABLE' ? ' selected="selected" ' : '') . ">{$lang['strtables']}</option>\n";
+ $default_html .= "\t<option value=\"VIEW\"" . ($_REQUEST['filter'] == 'VIEW' ? ' selected="selected" ' : '') . ">{$lang['strviews']}</option>\n";
+ $default_html .= "\t<option value=\"SEQUENCE\"" . ($_REQUEST['filter'] == 'SEQUENCE' ? ' selected="selected" ' : '') . ">{$lang['strsequences']}</option>\n";
+ $default_html .= "\t<option value=\"COLUMN\"" . ($_REQUEST['filter'] == 'COLUMN' ? ' selected="selected" ' : '') . ">{$lang['strcolumns']}</option>\n";
+ $default_html .= "\t<option value=\"RULE\"" . ($_REQUEST['filter'] == 'RULE' ? ' selected="selected" ' : '') . ">{$lang['strrules']}</option>\n";
+ $default_html .= "\t<option value=\"INDEX\"" . ($_REQUEST['filter'] == 'INDEX' ? ' selected="selected" ' : '') . ">{$lang['strindexes']}</option>\n";
+ $default_html .= "\t<option value=\"TRIGGER\"" . ($_REQUEST['filter'] == 'TRIGGER' ? ' selected="selected" ' : '') . ">{$lang['strtriggers']}</option>\n";
+ $default_html .= "\t<option value=\"CONSTRAINT\"" . ($_REQUEST['filter'] == 'CONSTRAINT' ? ' selected="selected" ' : '') . ">{$lang['strconstraints']}</option>\n";
+ $default_html .= "\t<option value=\"FUNCTION\"" . ($_REQUEST['filter'] == 'FUNCTION' ? ' selected="selected" ' : '') . ">{$lang['strfunctions']}</option>\n";
+ $default_html .= "\t<option value=\"DOMAIN\"" . ($_REQUEST['filter'] == 'DOMAIN' ? ' selected="selected" ' : '') . ">{$lang['strdomains']}</option>\n";
+ if ($conf['show_advanced']) {
+ $default_html .= "\t<option value=\"AGGREGATE\"" . ($_REQUEST['filter'] == 'AGGREGATE' ? ' selected="selected" ' : '') . ">{$lang['straggregates']}</option>\n";
+ $default_html .= "\t<option value=\"TYPE\"" . ($_REQUEST['filter'] == 'TYPE' ? ' selected="selected" ' : '') . ">{$lang['strtypes']}</option>\n";
+ $default_html .= "\t<option value=\"OPERATOR\"" . ($_REQUEST['filter'] == 'OPERATOR' ? ' selected="selected" ' : '') . ">{$lang['stroperators']}</option>\n";
+ $default_html .= "\t<option value=\"OPCLASS\"" . ($_REQUEST['filter'] == 'OPCLASS' ? ' selected="selected" ' : '') . ">{$lang['stropclasses']}</option>\n";
+ $default_html .= "\t<option value=\"CONVERSION\"" . ($_REQUEST['filter'] == 'CONVERSION' ? ' selected="selected" ' : '') . ">{$lang['strconversions']}</option>\n";
+ $default_html .= "\t<option value=\"LANGUAGE\"" . ($_REQUEST['filter'] == 'LANGUAGE' ? ' selected="selected" ' : '') . ">{$lang['strlanguages']}</option>\n";
+ }
+ $default_html .= "</select>\n";
+
+ $default_html .= "<input type=\"submit\" value=\"{$lang['strfind']}\" />\n";
+ $default_html .= "<input type=\"hidden\" name=\"action\" value=\"find\" /></p>\n";
+ $default_html .= "</form>\n";
+
+ // Default focus
+ //$misc->setFocus('forms[0].term');
+ return $default_html;
+ }
+
+ /**
+ * Allow execution of arbitrary SQL statements on a database
+ */
+ function doDefault() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if (!isset($_SESSION['sqlquery'])) {
+ $_SESSION['sqlquery'] = '';
+ }
+
+ $default_html = $this->printTabs($misc->getNavTabs('popup'), 'sql', false);
+
+ $default_html .= '<form action="/src/views/sql.php" method="post" enctype="multipart/form-data" class="sqlform" id="sqlform" target="detail">';
+ $default_html .= "\n";
+ $default_html .= $this->_printConnection('sql');
+
+ $default_html .= "\n";
+
+ if (!isset($_REQUEST['search_path'])) {
+ $_REQUEST['search_path'] = implode(',', $data->getSearchPath());
+ }
+ $search_path = htmlspecialchars($_REQUEST['search_path']);
+ $sqlquery = htmlspecialchars($_SESSION['sqlquery']);
+
+ $default_html .= ' <div class="searchpath">';
+ $default_html .= "<label>";
+ $default_html .= $misc->printHelp($lang['strsearchpath'], 'pg.schema.search_path', false);
+
+ $default_html .= ': <input type="text" name="search_path" size="50" value="' . $search_path . '" />';
+ $default_html .= "</label>\n";
+ $default_html .= "</div>\n";
+
+ $default_html .= '<div id="queryedition" style="padding:1%;width:98%;float:left;">';
+ $default_html .= "\n";
+ $default_html .= '<textarea style="width:98%;" rows="10" cols="50" name="query" id="query" resizable="true">' . $sqlquery . "</textarea>";
+ $default_html .= "\n";
+ $default_html .= "</div>\n";
+
+ // Check that file uploads are enabled
+ if (ini_get('file_uploads')) {
+ // Don't show upload option if max size of uploads is zero
+ $max_size = $misc->inisizeToBytes(ini_get('upload_max_filesize'));
+ if (is_double($max_size) && $max_size > 0) {
+ $default_html .= "<p>";
+ $default_html .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . $max_size . '" />';
+ $default_html .= "\n";
+ $default_html .= '<label for="script">' . $lang['struploadscript'] . '</label>';
+ $default_html .= ' <input id="script" name="script" type="file" /></p>';
+ $default_html .= "</p>\n";
+ }
+ }
+ $checked = (isset($_REQUEST['paginate']) ? ' checked="checked"' : '');
+ $default_html .= '<p>';
+ $default_html .= '<label for="paginate"><input type="checkbox" id="paginate" name="paginate"' . $checked . ' />&nbsp;' . $lang['strpaginate'] . '</label>';
+ $default_html .= "</p>\n";
+ $default_html .= '<p><input type="submit" name="execute" accesskey="r" value="' . $lang['strexecute'] . '" />';
+ $default_html .= "\n";
+ $default_html .= '<input type="reset" accesskey="q" value="' . $lang['strreset'] . '" /></p>';
+ $default_html .= "\n";
+ $default_html .= "</form>";
+ $default_html .= "\n";
+
+ // Default focus
+ //$misc->setFocus('forms[0].query');
+ return $default_html;
+
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $lang = $this->lang;
+ $misc = $this->misc;
+ $action = $this->action;
+ $data = $misc->getDatabaseAccessor();
+
+ switch ($action) {
+ case 'find':
+ $view_param = ['title' => $this->lang['strfind']];
+ $body_text = $this->doFind();
+ break;
+ case 'sql':
+ default:
+ $view_param = ['title' => $this->lang['strsql']];
+ $body_text = $this->doDefault();
+
+ break;
+ }
+
+ echo $this->view->fetch('sqledit_header.twig', $view_param);
+ echo $body_text;
+ echo $this->view->fetch('sqledit.twig');
+
+ $misc->setWindowName('sqledit');
+
+ }
+
+}
diff --git a/src/controllers/SQLQueryController.php b/src/controllers/SQLQueryController.php
new file mode 100644
index 00000000..68980db9
--- /dev/null
+++ b/src/controllers/SQLQueryController.php
@@ -0,0 +1,324 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+
+/**
+ * Base controller class
+ */
+class SQLQueryController extends BaseController {
+ public $_name = 'SQLQueryController';
+ public $query = '';
+ public $subject = '';
+ public $start_time = null;
+ public $duration = null;
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+
+ // We need to store the query in a session for editing purposes
+ // We avoid GPC vars to avoid truncating long queries
+ if (isset($_REQUEST['subject']) && $_REQUEST['subject'] == 'history') {
+ // Or maybe we came from the history popup
+ $_SESSION['sqlquery'] = $_SESSION['history'][$_REQUEST['server']][$_REQUEST['database']][$_GET['queryid']]['query'];
+ $this->query = $_SESSION['sqlquery'];
+ } elseif (isset($_POST['query'])) {
+ // Or maybe we came from an sql form
+ $_SESSION['sqlquery'] = $_POST['query'];
+ $this->query = $_SESSION['sqlquery'];
+ } else {
+ echo "could not find the query!!";
+ }
+
+ // Pagination maybe set by a get link that has it as FALSE,
+ // if that's the case, unset the variable.
+ if (isset($_REQUEST['paginate']) && $_REQUEST['paginate'] == 'f') {
+ unset($_REQUEST['paginate']);
+ unset($_POST['paginate']);
+ unset($_GET['paginate']);
+ }
+
+ if (isset($_REQUEST['subject'])) {
+ $this->subject = $_REQUEST['subject'];
+ }
+
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $lang = $this->lang;
+ $misc = $this->misc;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check to see if pagination has been specified. In that case, send to display
+ // script for pagination
+ /* if a file is given or the request is an explain, do not paginate */
+ if (isset($_REQUEST['paginate']) &&
+ !(isset($_FILES['script']) &&
+ $_FILES['script']['size'] > 0) &&
+ (preg_match('/^\s*explain/i', $this->query) == 0)) {
+
+ $display_controller = new DisplayController($this->getContainer());
+
+ return $display_controller->render();
+ }
+
+ $misc->printHeader($lang['strqueryresults']);
+ $misc->printBody();
+ $this->printTrail('database');
+ $misc->printTitle($lang['strqueryresults']);
+
+ // Set the schema search path
+ if (isset($_REQUEST['search_path'])) {
+ if ($data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path']))) != 0) {
+ return $misc->printFooter();
+ }
+ }
+
+ // May as well try to time the query
+ if (function_exists('microtime')) {
+ list($usec, $sec) = explode(' ', microtime());
+ $this->start_time = ((float) $usec + (float) $sec);
+ }
+
+ $this->doDefault();
+
+ $this->printFooter();
+ }
+
+ private function execute_script() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $_connection = $this->getConnection();
+
+ /**
+ * This is a callback function to display the result of each separate query
+ * @param ADORecordSet $rs The recordset returned by the script execetor
+ */
+ $sqlCallback = function ($query, $rs, $lineno) use ($data, $misc, $lang, $_connection) {
+
+ // Check if $rs is false, if so then there was a fatal error
+ if ($rs === false) {
+ echo htmlspecialchars($_FILES['script']['name']), ':', $lineno, ': ', nl2br(htmlspecialchars($_connection->getLastError())), "<br/>\n";
+ } else {
+ // Print query results
+ switch (pg_result_status($rs)) {
+ case PGSQL_TUPLES_OK:
+ // If rows returned, then display the results
+ $num_fields = pg_numfields($rs);
+ echo "<p><table>\n<tr>";
+ for ($k = 0; $k < $num_fields; $k++) {
+ echo "<th class=\"data\">", $misc->printVal(pg_fieldname($rs, $k)), "</th>";
+ }
+
+ $i = 0;
+ $row = pg_fetch_row($rs);
+ while ($row !== false) {
+ $id = (($i % 2) == 0 ? '1' : '2');
+ echo "<tr class=\"data{$id}\">\n";
+ foreach ($row as $k => $v) {
+ echo "<td style=\"white-space:nowrap;\">", $misc->printVal($v, pg_fieldtype($rs, $k), ['null' => true]), "</td>";
+ }
+ echo "</tr>\n";
+ $row = pg_fetch_row($rs);
+ $i++;
+ }
+ ;
+ echo "</table><br/>\n";
+ echo $i, " {$lang['strrows']}</p>\n";
+ break;
+ case PGSQL_COMMAND_OK:
+ // If we have the command completion tag
+ if (version_compare(phpversion(), '4.3', '>=')) {
+ echo htmlspecialchars(pg_result_status($rs, PGSQL_STATUS_STRING)), "<br/>\n";
+ }
+ // Otherwise if any rows have been affected
+ elseif ($data->conn->Affected_Rows() > 0) {
+ echo $data->conn->Affected_Rows(), " {$lang['strrowsaff']}<br/>\n";
+ }
+ // Otherwise output nothing...
+ break;
+ case PGSQL_EMPTY_QUERY:
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ return $data->executeScript('script', $sqlCallback);
+ }
+
+ private function execute_query() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Set fetch mode to NUM so that duplicate field names are properly returned
+ $data->conn->setFetchMode(ADODB_FETCH_NUM);
+ $rs = $data->conn->Execute($this->query);
+
+ // $rs will only be an object if there is no error
+ if (is_object($rs)) {
+ // Request was run, saving it in history
+ if (!isset($_REQUEST['nohistory'])) {
+ $misc->saveScriptHistory($this->query);
+ }
+
+ // Now, depending on what happened do various things
+
+ // First, if rows returned, then display the results
+ if ($rs->recordCount() > 0) {
+ echo "<table>\n<tr>";
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($k);
+ echo "<th class=\"data\">", $misc->printVal($finfo->name), "</th>";
+ }
+ echo "</tr>\n";
+ $i = 0;
+ while (!$rs->EOF) {
+ $id = (($i % 2) == 0 ? '1' : '2');
+ echo "<tr class=\"data{$id}\">\n";
+ foreach ($rs->fields as $k => $v) {
+ $finfo = $rs->fetchField($k);
+ echo "<td style=\"white-space:nowrap;\">", $misc->printVal($v, $finfo->type, ['null' => true]), "</td>";
+ }
+ echo "</tr>\n";
+ $rs->moveNext();
+ $i++;
+ }
+ echo "</table>\n";
+ echo "<p>", $rs->recordCount(), " {$lang['strrows']}</p>\n";
+ } else if ($data->conn->Affected_Rows() > 0) {
+ // Otherwise if any rows have been affected
+ echo "<p>", $data->conn->Affected_Rows(), " {$lang['strrowsaff']}</p>\n";
+ } else {
+ // Otherwise nodata to print
+ echo '<p>', $lang['strnodata'], "</p>\n";
+ }
+
+ }
+ }
+
+ public function doDefault() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Execute the query. If it's a script upload, special handling is necessary
+ if (isset($_FILES['script']) && $_FILES['script']['size'] > 0) {
+ return $this->execute_script();
+ } else {
+ return $this->execute_query();
+
+ }
+
+ }
+
+ public function printFooter() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // May as well try to time the query
+ if ($this->start_time !== null) {
+ list($usec, $sec) = explode(' ', microtime());
+ $end_time = ((float) $usec + (float) $sec);
+ // Get duration in milliseconds, round to 3dp's
+ $this->duration = number_format(($end_time - $this->start_time) * 1000, 3);
+ }
+
+ // Reload the browser as we may have made schema changes
+ $misc->setReloadBrowser(true);
+
+ // Display duration if we know it
+ if ($this->duration !== null) {
+ echo "<p>", sprintf($lang['strruntime'], $this->duration), "</p>\n";
+ }
+
+ echo "<p>{$lang['strsqlexecuted']}</p>\n";
+
+ $navlinks = [];
+ $fields = [
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ ];
+
+ if (isset($_REQUEST['schema'])) {
+ $fields['schema'] = $_REQUEST['schema'];
+ }
+
+ // Return
+ if (isset($_REQUEST['return'])) {
+ $urlvars = $misc->getSubjectParams($_REQUEST['return']);
+ $navlinks['back'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => $urlvars['url'],
+ 'urlvars' => $urlvars['params'],
+ ],
+ ],
+ 'content' => $lang['strback'],
+ ];
+ }
+
+ // Edit
+ $navlinks['alter'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'database.php',
+ 'urlvars' => array_merge($fields, [
+ 'action' => 'sql',
+ ]),
+ ],
+ ],
+ 'content' => $lang['streditsql'],
+ ];
+
+ // Create view and download
+ if ($this->query !== '' && isset($rs) && is_object($rs) && $rs->recordCount() > 0) {
+ // Report views don't set a schema, so we need to disable create view in that case
+ if (isset($_REQUEST['schema'])) {
+ $navlinks['createview'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'views.php',
+ 'urlvars' => array_merge($fields, [
+ 'action' => 'create',
+ ]),
+ ],
+ ],
+ 'content' => $lang['strcreateview'],
+ ];
+ }
+
+ if (isset($_REQUEST['search_path'])) {
+ $fields['search_path'] = $_REQUEST['search_path'];
+ }
+
+ $navlinks['download'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'dataexport.php',
+ 'urlvars' => $fields,
+ ],
+ ],
+ 'content' => $lang['strdownload'],
+ ];
+ }
+
+ $this->printNavLinks($navlinks, 'sql-form', get_defined_vars());
+
+ return $misc->printFooter();
+ }
+}
diff --git a/src/controllers/SchemaController.php b/src/controllers/SchemaController.php
index e1d81cfa..e879d5b3 100644
--- a/src/controllers/SchemaController.php
+++ b/src/controllers/SchemaController.php
@@ -9,17 +9,142 @@ use \PHPPgAdmin\Decorators\Decorator;
class SchemaController extends BaseController {
public $_name = 'SchemaController';
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ } else if ($action == 'subtree') {
+ return $this->doSubTree();
+ }
+
+ $misc->printHeader($lang['strschemas']);
+ $misc->printBody();
+
+ if (isset($_POST['cancel'])) {
+ $action = '';
+ }
+
+ switch ($action) {
+ case 'create':
+ if (isset($_POST['create'])) {
+ $this->doSaveCreate();
+ } else {
+ $this->doCreate();
+ }
+
+ break;
+ case 'alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doAlter();
+ }
+
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDrop(true);
+ }
+
+ break;
+ case 'export':
+ $this->doExport();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
/**
- * Show default list of schemas in the database
+ * Generate XML for the browser tree.
*/
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $schemas = $data->getSchemas();
+
+ $reqvars = $misc->getRequestVars('schema');
+
+ $attrs = [
+ 'text' => Decorator::field('nspname'),
+ 'icon' => 'Schema',
+ 'toolTip' => Decorator::field('nspcomment'),
+ 'action' => Decorator::redirecturl('redirect.php',
+ $reqvars,
+ [
+ 'subject' => 'schema',
+ 'schema' => Decorator::field('nspname'),
+ ]
+ ),
+ 'branch' => Decorator::url('schemas.php',
+ $reqvars,
+ [
+ 'action' => 'subtree',
+ 'schema' => Decorator::field('nspname'),
+ ]
+ ),
+ ];
+
+ $misc->printTree($schemas, $attrs, 'schemas');
+
+ }
+
+ function doSubTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $tabs = $misc->getNavTabs('schema');
+
+ $items = $misc->adjustTabsForTree($tabs);
+
+ $reqvars = $misc->getRequestVars('schema');
+
+ $attrs = [
+ 'text' => Decorator::field('title'),
+ 'icon' => Decorator::field('icon'),
+ 'action' => Decorator::actionurl(Decorator::field('url'),
+ $reqvars,
+ Decorator::field('urlvars', [])
+ ),
+ 'branch' => Decorator::url(Decorator::field('url'),
+ $reqvars,
+ Decorator::field('urlvars'),
+ ['action' => 'tree']
+ ),
+ ];
+
+ $misc->printTree($items, $attrs, 'schema');
+
+ }
+
+ /**
+ * Show default list of schemas in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('database');
- $misc->printTabs('database', 'schemas');
+ $this->printTrail('database');
+ $this->printTabs('database', 'schemas');
$misc->printMsg($msg);
// Check that the DB actually supports schemas
@@ -93,9 +218,9 @@ class SchemaController extends BaseController {
unset($actions['alter']);
}
- echo $misc->printTable($schemas, $columns, $actions, 'schemas-schemas', $lang['strnoschemas']);
+ echo $this->printTable($schemas, $columns, $actions, 'schemas-schemas', $lang['strnoschemas']);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'schemas.php',
@@ -110,12 +235,13 @@ class SchemaController extends BaseController {
]], 'schemas-schemas', get_defined_vars());
}
-/**
- * Displays a screen where they can enter a new schema
- */
+ /**
+ * Displays a screen where they can enter a new schema
+ */
public function doCreate($msg = '') {
- global $data, $misc;
- global $lang;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
$server_info = $misc->getServerInfo();
@@ -138,7 +264,7 @@ class SchemaController extends BaseController {
// Fetch all users from the database
$users = $data->getUsers();
- $misc->printTrail('database');
+ $this->printTrail('database');
$misc->printTitle($lang['strcreateschema'], 'pg.schema.create');
$misc->printMsg($msg);
@@ -172,11 +298,13 @@ class SchemaController extends BaseController {
echo "</form>\n";
}
-/**
- * Actually creates the new schema in the database
- */
+ /**
+ * Actually creates the new schema in the database
+ */
public function doSaveCreate() {
- global $data, $lang, $_reload_browser;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
// Check that they've given a name
if ($_POST['formName'] == '') {
@@ -193,14 +321,17 @@ class SchemaController extends BaseController {
}
}
-/**
- * Display a form to permit editing schema properies.
- * TODO: permit changing owner
- */
+ /**
+ * Display a form to permit editing schema properies.
+ * TODO: permit changing owner
+ */
public function doAlter($msg = '') {
- global $data, $misc, $lang;
- $misc->printTrail('schema');
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('schema');
$misc->printTitle($lang['stralter'], 'pg.schema.alter');
$misc->printMsg($msg);
@@ -264,11 +395,13 @@ class SchemaController extends BaseController {
}
}
-/**
- * Save the form submission containing changes to a schema
- */
+ /**
+ * Save the form submission containing changes to a schema
+ */
public function doSaveAlter($msg = '') {
- global $data, $misc, $lang, $_reload_browser;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
$status = $data->updateSchema($_POST['schema'], $_POST['comment'], $_POST['name'], $_POST['owner']);
if ($status == 0) {
@@ -280,12 +413,13 @@ class SchemaController extends BaseController {
}
-/**
- * Show confirmation of drop and perform actual drop
- */
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
public function doDrop($confirm) {
- global $data, $misc;
- global $lang, $_reload_browser;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if (empty($_REQUEST['nsp']) && empty($_REQUEST['ma'])) {
$this->doDefault($lang['strspecifyschematodrop']);
@@ -293,7 +427,7 @@ class SchemaController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strdrop'], 'pg.schema.drop');
echo '<form action="/src/views/schemas.php" method="post">' . "\n";
@@ -353,15 +487,16 @@ class SchemaController extends BaseController {
}
}
-/**
- * Displays options for database download
- */
+ /**
+ * Displays options for database download
+ */
public function doExport($msg = '') {
- global $data, $misc;
- global $lang;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'export');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'export');
$misc->printMsg($msg);
echo '<form action="/src/views/dbexport.php" method="post">' . "\n";
diff --git a/src/controllers/SequenceController.php b/src/controllers/SequenceController.php
index db988c60..94e78340 100644
--- a/src/controllers/SequenceController.php
+++ b/src/controllers/SequenceController.php
@@ -9,17 +9,128 @@ use \PHPPgAdmin\Decorators\Decorator;
class SequenceController extends BaseController {
public $_name = 'SequenceController';
-/**
- * Display list of all sequences in the database/schema
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ // Print header
+ $misc->printHeader($lang['strsequences']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create':
+ $this->doCreateSequence();
+ break;
+ case 'save_create_sequence':
+ if (isset($_POST['create'])) {
+ $this->doSaveCreateSequence();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'restart':
+ $this->doRestart();
+ break;
+ case 'reset':
+ $this->doReset();
+ break;
+ case 'nextval':
+ $this->doNextval();
+ break;
+ case 'setval':
+ if (isset($_POST['setval'])) {
+ $this->doSaveSetval();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_setval':
+ $this->doSetval();
+ break;
+ case 'alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_alter':
+ $this->doAlter();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ // Print footer
+ return $misc->printFooter();
+
+ }
+
+ /**
+ * Generate XML for the browser tree.
+ */
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $sequences = $data->getSequences();
+
+ $reqvars = $misc->getRequestVars('sequence');
+
+ $attrs = [
+ 'text' => Decorator::field('seqname'),
+ 'icon' => 'Sequence',
+ 'toolTip' => Decorator::field('seqcomment'),
+ 'action' => Decorator::actionurl('sequences.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'sequence' => Decorator::field('seqname'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($sequences, $attrs, 'sequences');
+ }
+
+ /**
+ * Display list of all sequences in the database/schema
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'sequences');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'sequences');
$misc->printMsg($msg);
// Get all sequences
@@ -90,9 +201,9 @@ class SequenceController extends BaseController {
],
];
- echo $misc->printTable($sequences, $columns, $actions, 'sequences-sequences', $lang['strnosequences']);
+ echo $this->printTable($sequences, $columns, $actions, 'sequences-sequences', $lang['strnosequences']);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'sequences.php',
@@ -116,7 +227,7 @@ class SequenceController extends BaseController {
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('sequence');
+ $this->printTrail('sequence');
$misc->printTitle($lang['strproperties'], 'pg.sequence');
$misc->printMsg($msg);
@@ -256,7 +367,7 @@ class SequenceController extends BaseController {
unset($navlinks['restart']);
}
- $misc->printNavLinks($navlinks, 'sequences-properties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'sequences-properties', get_defined_vars());
} else {
echo "<p>{$lang['strnodata']}</p>\n";
}
@@ -278,7 +389,7 @@ class SequenceController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('sequence');
+ $this->printTrail('sequence');
$misc->printTitle($lang['strdrop'], 'pg.sequence.drop');
$misc->printMsg($msg);
@@ -372,7 +483,7 @@ class SequenceController extends BaseController {
$_POST['formCacheValue'] = '';
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatesequence'], 'pg.sequence.create');
$misc->printMsg($msg);
@@ -521,7 +632,7 @@ class SequenceController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('sequence');
+ $this->printTrail('sequence');
$misc->printTitle($lang['strsetval'], 'pg.sequence');
$misc->printMsg($msg);
@@ -625,7 +736,7 @@ class SequenceController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('sequence');
+ $this->printTrail('sequence');
$misc->printTitle($lang['stralter'], 'pg.sequence.alter');
$misc->printMsg($msg);
diff --git a/src/controllers/ServerController.php b/src/controllers/ServerController.php
new file mode 100644
index 00000000..994fe602
--- /dev/null
+++ b/src/controllers/ServerController.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Base controller class
+ */
+class ServerController extends BaseController {
+ public $_name = 'ServerController';
+ public $table_place = 'servers-servers';
+ public $query = '';
+ public $subject = '';
+ public $start_time = null;
+ public $duration = null;
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ parent::__construct($container);
+
+ // Prevent timeouts on large exports (non-safe mode only)
+ if (!ini_get('safe_mode')) {
+ set_time_limit(0);
+ }
+ }
+
+ function doLogout() {
+
+ $plugin_manager = $this->plugin_manager;
+ $lang = $this->lang;
+ $misc = $this->misc;
+ $conf = $this->conf;
+ $data = $misc->getDatabaseAccessor();
+
+ $plugin_manager->do_hook('logout', $_REQUEST['logoutServer']);
+
+ $server_info = $misc->getServerInfo($_REQUEST['logoutServer']);
+ $misc->setServerInfo(null, null, $_REQUEST['logoutServer']);
+
+ unset($_SESSION['sharedUsername'], $_SESSION['sharedPassword']);
+
+ $misc->setReloadBrowser(true);
+
+ echo sprintf($lang['strlogoutmsg'], $server_info['desc']);
+
+ }
+
+ function doDefault($msg = '') {
+
+ $lang = $this->lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTabs('root', 'servers');
+ $misc->printMsg($msg);
+ $group = isset($_GET['group']) ? $_GET['group'] : false;
+
+ $groups = $misc->getServersGroups(true, $group);
+ $columns = [
+ 'group' => [
+ 'title' => $lang['strgroup'],
+ 'field' => Decorator::field('desc'),
+ 'url' => 'servers.php?',
+ 'vars' => ['group' => 'id'],
+ ],
+ ];
+ $actions = [];
+ if (($group !== false) and (isset($conf['srv_groups'][$group])) and ($groups->recordCount() > 0)) {
+ $misc->printTitle(sprintf($lang['strgroupgroups'], htmlentities($conf['srv_groups'][$group]['desc'], ENT_QUOTES, 'UTF-8')));
+ }
+ $this->printTable($groups, $columns, $actions, $this->table_place);
+ $servers = $misc->getServers(true, $group);
+
+ $svPre = function (&$rowdata) use ($actions) {
+ $actions['logout']['disable'] = empty($rowdata->fields['username']);
+ return $actions;
+ };
+
+ $columns = [
+ 'server' => [
+ 'title' => $lang['strserver'],
+ 'field' => Decorator::field('desc'),
+ 'url' => "/redirect/server?",
+ 'vars' => ['server' => 'id'],
+ ],
+ 'host' => [
+ 'title' => $lang['strhost'],
+ 'field' => Decorator::field('host'),
+ ],
+ 'port' => [
+ 'title' => $lang['strport'],
+ 'field' => Decorator::field('port'),
+ ],
+ 'username' => [
+ 'title' => $lang['strusername'],
+ 'field' => Decorator::field('username'),
+ ],
+ 'actions' => [
+ 'title' => $lang['stractions'],
+ ],
+ ];
+
+ $actions = [
+ 'logout' => [
+ 'content' => $lang['strlogout'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'servers.php',
+ 'urlvars' => [
+ 'action' => 'logout',
+ 'logoutServer' => Decorator::field('id'),
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ if (($group !== false) and isset($conf['srv_groups'][$group])) {
+ $misc->printTitle(sprintf($lang['strgroupservers'], htmlentities($conf['srv_groups'][$group]['desc'], ENT_QUOTES, 'UTF-8')), null);
+ $actions['logout']['attr']['href']['urlvars']['group'] = $group;
+ }
+ echo $this->printTable($servers, $columns, $actions, $this->table_place, $lang['strnoobjects'], $svPre);
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+
+ $nodes = [];
+ $group_id = isset($_GET['group']) ? $_GET['group'] : false;
+
+ /* root with srv_groups */
+ if (isset($conf['srv_groups']) and count($conf['srv_groups']) > 0
+ and $group_id === false) {
+ $nodes = $misc->getServersGroups(true);
+ } else if (isset($conf['srv_groups']) and $group_id !== false) {
+ /* group subtree */
+ if ($group_id !== 'all') {
+ $nodes = $misc->getServersGroups(false, $group_id);
+ }
+
+ $nodes = array_merge($nodes, $misc->getServers(false, $group_id));
+ $nodes = new \PHPPgAdmin\ArrayRecordSet($nodes);
+ } else {
+ /* no srv_group */
+ $nodes = $misc->getServers(true, false);
+ }
+
+ $reqvars = $misc->getRequestVars('server');
+
+ $attrs = [
+ 'text' => Decorator::field('desc'),
+
+ // Show different icons for logged in/out
+ 'icon' => Decorator::field('icon'),
+
+ 'toolTip' => Decorator::field('id'),
+
+ 'action' => Decorator::field('action'),
+
+ // Only create a branch url if the user has
+ // logged into the server.
+ 'branch' => Decorator::field('branch'),
+ ];
+ \PC::debug(['attrs' => $attrs, 'nodes' => $nodes], __CLASS__ . '::' . __METHOD__);
+ return $misc->printTree($nodes, $attrs, 'servers');
+
+ }
+
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $msg = $this->msg;
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($this->lang['strservers'], null);
+ $misc->printBody();
+ $this->printTrail('root');
+
+ switch ($action) {
+ case 'logout':
+ $this->doLogout();
+
+ break;
+ default:
+ $this->doDefault($msg);
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+}
diff --git a/src/controllers/TableController.php b/src/controllers/TableController.php
index 7ed7498e..9103d582 100644
--- a/src/controllers/TableController.php
+++ b/src/controllers/TableController.php
@@ -8,12 +8,392 @@ use \PHPPgAdmin\Decorators\Decorator;
*/
class TableController extends BaseController {
use AdminTrait;
- public $script = 'table.php';
- public $_name = 'TableController';
+ public $script = 'tables.php';
+ public $_name = 'TableController';
+ public $table_place = 'tables-tables';
+
+ public function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ } else if ($action == 'subtree') {
+ return $this->doSubTree();
+ }
+
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strtables'], null, true, 'datatables_header.twig');
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doCreate();
+ }
+
+ break;
+ case 'createlike':
+ $this->doCreateLike(false);
+ break;
+ case 'confcreatelike':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doCreateLike(true);
+ }
+
+ break;
+ case 'selectrows':
+ if (!isset($_POST['cancel'])) {
+ $this->doSelectRows(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confselectrows':
+ $this->doSelectRows(true);
+ break;
+ case 'insertrow':
+ if (!isset($_POST['cancel'])) {
+ $this->doInsertRow(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confinsertrow':
+ $this->doInsertRow(true);
+ break;
+ case 'empty':
+ if (isset($_POST['empty'])) {
+ $this->doEmpty(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_empty':
+ $this->doEmpty(true);
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ if ($this->adminActions($action, 'table') === false) {
+ $this->doDefault();
+ }
+
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
/**
- * Displays a screen where they can enter a new table
+ * Generate XML for the browser tree.
*/
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ //\PC::debug($misc->getDatabase(), 'getDatabase');
+
+ $tables = $data->getTables();
+
+ $reqvars = $misc->getRequestVars('table');
+
+ $attrs = [
+ 'text' => Decorator::field('relname'),
+ 'icon' => 'Table',
+ 'iconAction' => Decorator::url('display.php',
+ $reqvars,
+ ['table' => Decorator::field('relname')]
+ ),
+ 'toolTip' => Decorator::field('relcomment'),
+ 'action' => Decorator::redirecturl('redirect.php',
+ $reqvars,
+ ['table' => Decorator::field('relname')]
+ ),
+ 'branch' => Decorator::url('tables.php',
+ $reqvars,
+ [
+ 'action' => 'subtree',
+ 'table' => Decorator::field('relname'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($tables, $attrs, 'tables');
+ }
+
+ function doSubTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $tabs = $misc->getNavTabs('table');
+ $items = $misc->adjustTabsForTree($tabs);
+ $reqvars = $misc->getRequestVars('table');
+
+ $attrs = [
+ 'text' => Decorator::field('title'),
+ 'icon' => Decorator::field('icon'),
+ 'action' => Decorator::actionurl(
+ Decorator::field('url'),
+ $reqvars,
+ Decorator::field('urlvars'),
+ ['table' => $_REQUEST['table']]
+ ),
+ 'branch' => Decorator::ifempty(
+ Decorator::field('branch'), '', Decorator::url(Decorator::field('url'), $reqvars, [
+ 'action' => 'tree',
+ 'table' => $_REQUEST['table'],
+ ]
+ )
+ ),
+ ];
+
+ return $misc->printTree($items, $attrs, 'table');
+ }
+
+ /**
+ * Show default list of tables in the database
+ */
+ public function doDefault($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'tables');
+ $misc->printMsg($msg);
+
+ $tables = $data->getTables();
+
+ $columns = [
+ 'table' => [
+ 'title' => $lang['strtable'],
+ 'field' => Decorator::field('relname'),
+ 'url' => "/redirect/table?{$misc->href}&amp;",
+ 'vars' => ['table' => 'relname'],
+ ],
+ 'owner' => [
+ 'title' => $lang['strowner'],
+ 'field' => Decorator::field('relowner'),
+ ],
+ 'tablespace' => [
+ 'title' => $lang['strtablespace'],
+ 'field' => Decorator::field('tablespace'),
+ ],
+ 'tuples' => [
+ 'title' => $lang['strestimatedrowcount'],
+ 'field' => Decorator::field('reltuples'),
+ 'type' => 'numeric',
+ ],
+ 'actions' => [
+ 'title' => $lang['stractions'],
+ ],
+ 'comment' => [
+ 'title' => $lang['strcomment'],
+ 'field' => Decorator::field('relcomment'),
+ ],
+ ];
+
+ $actions = [
+ 'multiactions' => [
+ 'keycols' => ['table' => 'relname'],
+ 'url' => 'tables.php',
+ 'default' => 'analyze',
+ ],
+ 'browse' => [
+ 'content' => $lang['strbrowse'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'display.php',
+ 'urlvars' => [
+ 'subject' => 'table',
+ 'return' => 'table',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'select' => [
+ 'content' => $lang['strselect'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confselectrows',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'insert' => [
+ 'content' => $lang['strinsert'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confinsertrow',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'empty' => [
+ 'multiaction' => 'confirm_empty',
+ 'content' => $lang['strempty'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confirm_empty',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'alter' => [
+ 'content' => $lang['stralter'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tblproperties.php',
+ 'urlvars' => [
+ 'action' => 'confirm_alter',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'drop' => [
+ 'multiaction' => 'confirm_drop',
+ 'content' => $lang['strdrop'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confirm_drop',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'vacuum' => [
+ 'multiaction' => 'confirm_vacuum',
+ 'content' => $lang['strvacuum'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confirm_vacuum',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'analyze' => [
+ 'multiaction' => 'confirm_analyze',
+ 'content' => $lang['stranalyze'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confirm_analyze',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ 'reindex' => [
+ 'multiaction' => 'confirm_reindex',
+ 'content' => $lang['strreindex'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'confirm_reindex',
+ 'table' => Decorator::field('relname'),
+ ],
+ ],
+ ],
+ ],
+ //'cluster' TODO ?
+ ];
+
+ if (!$data->hasTablespaces()) {
+ unset($columns['tablespace']);
+ }
+
+ //\Kint::dump($tables);
+
+ echo $this->printTable($tables, $columns, $actions, $this->table_place, $lang['strnotables']);
+
+ $navlinks = [
+ 'create' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'create',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreatetable'],
+ ],
+ ];
+
+ if (($tables->recordCount() > 0) && $data->hasCreateTableLike()) {
+ $navlinks['createlike'] = [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tables.php',
+ 'urlvars' => [
+ 'action' => 'createlike',
+ 'server' => $_REQUEST['server'],
+ 'database' => $_REQUEST['database'],
+ 'schema' => $_REQUEST['schema'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreatetablelike'],
+ ];
+ }
+ $this->printNavLinks($navlinks, 'tables-tables', get_defined_vars());
+
+ echo $this->view->fetch('table_list_footer.twig', ['table_class' => $this->table_place]);
+
+ }
+ /**
+ * Displays a screen where they can enter a new table
+ */
public function doCreate($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -52,11 +432,12 @@ class TableController extends BaseController {
$tablespaces = $data->getTablespaces();
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatetable'], 'pg.table.create');
$misc->printMsg($msg);
- echo "<form action=\"/src/views/tables.php\" method=\"post\">\n";
+ echo '<form action="/src/views/' . $this->script . '" method="post">';
+ echo "\n";
echo "<table>\n";
echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strname']}</th>\n";
echo "\t\t<td class=\"data\"><input name=\"name\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" value=\"",
@@ -113,7 +494,7 @@ class TableController extends BaseController {
$types = $data->getTypes(true, false, true);
$types_for_js = [];
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatetable'], 'pg.table.create');
$misc->printMsg($msg);
@@ -272,11 +653,11 @@ class TableController extends BaseController {
}
}
-/**
- * Dsiplay a screen where user can create a table from an existing one.
- * We don't have to check if pg supports schema cause create table like
- * is available under pg 7.4+ which has schema.
- */
+ /**
+ * Dsiplay a screen where user can create a table from an existing one.
+ * We don't have to check if pg supports schema cause create table like
+ * is available under pg 7.4+ which has schema.
+ */
public function doCreateLike($confirm, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -299,7 +680,7 @@ class TableController extends BaseController {
$_REQUEST['tablespace'] = '';
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatetable'], 'pg.table.create');
$misc->printMsg($msg);
@@ -392,9 +773,9 @@ class TableController extends BaseController {
}
}
-/**
- * Ask for select parameters and perform select
- */
+ /**
+ * Ask for select parameters and perform select
+ */
public function doSelectRows($confirm, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -402,8 +783,8 @@ class TableController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('table');
- $misc->printTabs('table', 'select');
+ $this->printTrail('table');
+ $this->printTabs('table', 'select');
$misc->printMsg($msg);
$attrs = $data->getTableAttributes($_REQUEST['table']);
@@ -476,6 +857,8 @@ class TableController extends BaseController {
echo "<input type=\"submit\" name=\"select\" accesskey=\"r\" value=\"{$lang['strselect']}\" />\n";
echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
echo "</form>\n";
+
+ return;
} else {
if (!isset($_POST['show'])) {
$_POST['show'] = [];
@@ -507,15 +890,18 @@ class TableController extends BaseController {
$_REQUEST['return'] = 'selectrows';
$misc->setNoOutput(true);
- include './display.php';
- exit;
+
+ $display_controller = new DisplayController($this->getContainer());
+
+ return $display_controller->render();
+
}
}
}
-/**
- * Ask for insert parameters and then actually insert row
- */
+ /**
+ * Ask for insert parameters and then actually insert row
+ */
public function doInsertRow($confirm, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -523,8 +909,8 @@ class TableController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('table');
- $misc->printTabs('table', 'insert');
+ $this->printTrail('table');
+ $this->printTabs('table', 'insert');
$misc->printMsg($msg);
@@ -662,9 +1048,9 @@ class TableController extends BaseController {
}
-/**
- * Show confirmation of empty and perform actual empty
- */
+ /**
+ * Show confirmation of empty and perform actual empty
+ */
public function doEmpty($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -678,7 +1064,7 @@ class TableController extends BaseController {
if ($confirm) {
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strempty'], 'pg.table.empty');
echo "<form action=\"/src/views/tables.php\" method=\"post\">\n";
@@ -689,7 +1075,7 @@ class TableController extends BaseController {
}
} // END mutli empty
else {
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['strempty'], 'pg.table.empty');
echo "<p>", sprintf($lang['strconfemptytable'], $misc->printVal($_REQUEST['table'])), "</p>\n";
@@ -730,9 +1116,9 @@ class TableController extends BaseController {
} // END do Empty
}
-/**
- * Show confirmation of drop and perform actual drop
- */
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
public function doDrop($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -748,7 +1134,7 @@ class TableController extends BaseController {
//If multi drop
if (isset($_REQUEST['ma'])) {
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strdrop'], 'pg.table.drop');
echo "<form action=\"/src/views/tables.php\" method=\"post\">\n";
@@ -759,7 +1145,7 @@ class TableController extends BaseController {
}
} else {
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['strdrop'], 'pg.table.drop');
echo "<p>", sprintf($lang['strconfdroptable'], $misc->printVal($_REQUEST['table'])), "</p>\n";
@@ -813,213 +1199,4 @@ class TableController extends BaseController {
} // END DROP
} // END Function
-/**
- * Show default list of tables in the database
- */
- public function doDefault($msg = '') {
- $conf = $this->conf;
- $misc = $this->misc;
- $lang = $this->lang;
- $data = $misc->getDatabaseAccessor();
-
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'tables');
- $misc->printMsg($msg);
-
- $tables = $data->getTables();
-
- $columns = [
- 'table' => [
- 'title' => $lang['strtable'],
- 'field' => Decorator::field('relname'),
- 'url' => "/redirect/table?{$misc->href}&amp;",
- 'vars' => ['table' => 'relname'],
- ],
- 'owner' => [
- 'title' => $lang['strowner'],
- 'field' => Decorator::field('relowner'),
- ],
- 'tablespace' => [
- 'title' => $lang['strtablespace'],
- 'field' => Decorator::field('tablespace'),
- ],
- 'tuples' => [
- 'title' => $lang['strestimatedrowcount'],
- 'field' => Decorator::field('reltuples'),
- 'type' => 'numeric',
- ],
- 'actions' => [
- 'title' => $lang['stractions'],
- ],
- 'comment' => [
- 'title' => $lang['strcomment'],
- 'field' => Decorator::field('relcomment'),
- ],
- ];
-
- $actions = [
- 'multiactions' => [
- 'keycols' => ['table' => 'relname'],
- 'url' => 'tables.php',
- 'default' => 'analyze',
- ],
- 'browse' => [
- 'content' => $lang['strbrowse'],
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => [
- 'subject' => 'table',
- 'return' => 'table',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'select' => [
- 'content' => $lang['strselect'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confselectrows',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'insert' => [
- 'content' => $lang['strinsert'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confinsertrow',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'empty' => [
- 'multiaction' => 'confirm_empty',
- 'content' => $lang['strempty'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confirm_empty',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'alter' => [
- 'content' => $lang['stralter'],
- 'attr' => [
- 'href' => [
- 'url' => 'tblproperties.php',
- 'urlvars' => [
- 'action' => 'confirm_alter',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'drop' => [
- 'multiaction' => 'confirm_drop',
- 'content' => $lang['strdrop'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confirm_drop',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'vacuum' => [
- 'multiaction' => 'confirm_vacuum',
- 'content' => $lang['strvacuum'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confirm_vacuum',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'analyze' => [
- 'multiaction' => 'confirm_analyze',
- 'content' => $lang['stranalyze'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confirm_analyze',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- 'reindex' => [
- 'multiaction' => 'confirm_reindex',
- 'content' => $lang['strreindex'],
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'confirm_reindex',
- 'table' => Decorator::field('relname'),
- ],
- ],
- ],
- ],
- //'cluster' TODO ?
- ];
-
- if (!$data->hasTablespaces()) {
- unset($columns['tablespace']);
- }
-
- echo $misc->printTable($tables, $columns, $actions, 'tables-tables', $lang['strnotables']);
-
- $navlinks = [
- 'create' => [
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'create',
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- 'schema' => $_REQUEST['schema'],
- ],
- ],
- ],
- 'content' => $lang['strcreatetable'],
- ],
- ];
-
- if (($tables->recordCount() > 0) && $data->hasCreateTableLike()) {
- $navlinks['createlike'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => [
- 'action' => 'createlike',
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- 'schema' => $_REQUEST['schema'],
- ],
- ],
- ],
- 'content' => $lang['strcreatetablelike'],
- ];
- }
- $misc->printNavLinks($navlinks, 'tables-tables', get_defined_vars());
- }
-
}
diff --git a/src/controllers/TablePropertyController.php b/src/controllers/TablePropertyController.php
index 96deb1bd..2301da45 100644
--- a/src/controllers/TablePropertyController.php
+++ b/src/controllers/TablePropertyController.php
@@ -9,6 +9,112 @@ use \PHPPgAdmin\Decorators\Decorator;
class TablePropertyController extends BaseController {
public $_name = 'TablePropertyController';
+ public function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+ $data = $misc->getDatabaseAccessor();
+ $misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_alter':
+ $this->doAlter();
+ break;
+ case 'import':
+ $this->doImport();
+ break;
+ case 'export':
+ $this->doExport();
+ break;
+ case 'add_column':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doAddColumn();
+ }
+
+ break;
+ case 'properties':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doProperties();
+ }
+
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $columns = $data->getTableAttributes($_REQUEST['table']);
+ $reqvars = $misc->getRequestVars('column');
+
+ $attrs = [
+ 'text' => Decorator::field('attname'),
+ 'action' => Decorator::actionurl('colproperties.php',
+ $reqvars,
+ [
+ 'table' => $_REQUEST['table'],
+ 'column' => Decorator::field('attname'),
+ ]
+ ),
+ 'icon' => 'Column',
+ 'iconAction' => Decorator::url('display.php',
+ $reqvars,
+ [
+ 'table' => $_REQUEST['table'],
+ 'column' => Decorator::field('attname'),
+ 'query' => Decorator::replace(
+ 'SELECT "%column%", count(*) AS "count" FROM "%table%" GROUP BY "%column%" ORDER BY "%column%"',
+ [
+ '%column%' => Decorator::field('attname'),
+ '%table%' => $_REQUEST['table'],
+ ]
+ ),
+ ]
+ ),
+ 'toolTip' => Decorator::field('comment'),
+ ];
+
+ return $misc->printTree($columns, $attrs, 'tblcolumns');
+ }
public function doSaveAlter() {
$conf = $this->conf;
$misc = $this->misc;
@@ -37,13 +143,13 @@ class TablePropertyController extends BaseController {
// Jump them to the new table name
$_REQUEST['table'] = $_POST['name'];
// Force a browser reload
- $_reload_browser = true;
+ $misc->setReloadBrowser(true);
}
// If schema has changed, need to change to the new schema and reload the browser
if (!empty($_POST['newschema']) && ($_POST['newschema'] != $data->_schema)) {
// Jump them to the new sequence schema
$misc->setCurrentSchema($_POST['newschema']);
- $_reload_browser = true;
+ $misc->setReloadBrowser(true);
}
$this->doDefault($lang['strtablealtered']);
} else {
@@ -61,7 +167,7 @@ class TablePropertyController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['stralter'], 'pg.table.alter');
$misc->printMsg($msg);
@@ -171,8 +277,8 @@ class TablePropertyController extends BaseController {
// Determine whether or not the table has an object ID
$hasID = $data->hasObjectID($_REQUEST['table']);
- $misc->printTrail('table');
- $misc->printTabs('table', 'export');
+ $this->printTrail('table');
+ $this->printTabs('table', 'export');
$misc->printMsg($msg);
echo "<form action=\"/src/views/dataexport.php\" method=\"post\">\n";
@@ -228,8 +334,8 @@ class TablePropertyController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('table');
- $misc->printTabs('table', 'import');
+ $this->printTrail('table');
+ $this->printTabs('table', 'import');
$misc->printMsg($msg);
// Check that file uploads are enabled
@@ -312,7 +418,7 @@ class TablePropertyController extends BaseController {
$types = $data->getTypes(true, false, true);
$types_for_js = [];
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['straddcolumn'], 'pg.column.add');
$misc->printMsg($msg);
@@ -400,7 +506,7 @@ class TablePropertyController extends BaseController {
$_POST['type'], $_POST['array'] != '', $_POST['length'], isset($_POST['notnull']),
$_POST['default'], $_POST['comment']);
if ($status == 0) {
- $_reload_browser = true;
+ $misc->setReloadBrowser(true);
$this->doDefault($lang['strcolumnadded']);
} else {
$_REQUEST['stage'] = 1;
@@ -423,7 +529,7 @@ class TablePropertyController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('column');
+ $this->printTrail('column');
$misc->printTitle($lang['strdrop'], 'pg.column.drop');
echo "<p>", sprintf($lang['strconfdropcolumn'], $misc->printVal($_REQUEST['column']),
@@ -441,7 +547,7 @@ class TablePropertyController extends BaseController {
} else {
$status = $data->dropColumn($_POST['table'], $_POST['column'], isset($_POST['cascade']));
if ($status == 0) {
- $_reload_browser = true;
+ $misc->setReloadBrowser(true);
$this->doDefault($lang['strcolumndropped']);
} else {
$this->doDefault($lang['strcolumndroppedbad']);
@@ -451,9 +557,9 @@ class TablePropertyController extends BaseController {
}
-/**
- * Show default list of columns in the table
- */
+ /**
+ * Show default list of columns in the table
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -509,8 +615,8 @@ class TablePropertyController extends BaseController {
return $str;
};
- $misc->printTrail('table');
- $misc->printTabs('table', 'columns');
+ $this->printTrail('table');
+ $this->printTabs('table', 'columns');
$misc->printMsg($msg);
// Get table
@@ -646,7 +752,7 @@ class TablePropertyController extends BaseController {
],
];
- echo $misc->printTable($attrs, $columns, $actions, 'tblproperties-tblproperties', null, $attPre);
+ echo $this->printTable($attrs, $columns, $actions, 'tblproperties-tblproperties', null, $attPre);
$navlinks = [
'browse' => [
@@ -756,7 +862,7 @@ class TablePropertyController extends BaseController {
'content' => $lang['stralter'],
],
];
- $misc->printNavLinks($navlinks, 'tblproperties-tblproperties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'tblproperties-tblproperties', get_defined_vars());
}
diff --git a/src/controllers/TableSpacesController.php b/src/controllers/TableSpacesController.php
new file mode 100644
index 00000000..42c8ce57
--- /dev/null
+++ b/src/controllers/TableSpacesController.php
@@ -0,0 +1,379 @@
+<?php
+
+namespace PHPPgAdmin\Controller;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Base controller class
+ */
+class TableSpacesController extends BaseController {
+ public $_name = 'TableSpacesController';
+
+ /**
+ * Function to allow altering of a tablespace
+ */
+ function doAlter($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('tablespace');
+ $misc->printTitle($lang['stralter'], 'pg.tablespace.alter');
+ $misc->printMsg($msg);
+
+ // Fetch tablespace info
+ $tablespace = $data->getTablespace($_REQUEST['tablespace']);
+ // Fetch all users
+ $users = $data->getUsers();
+
+ if ($tablespace->recordCount() > 0) {
+
+ if (!isset($_POST['name'])) {
+ $_POST['name'] = $tablespace->fields['spcname'];
+ }
+
+ if (!isset($_POST['owner'])) {
+ $_POST['owner'] = $tablespace->fields['spcowner'];
+ }
+
+ if (!isset($_POST['comment'])) {
+ $_POST['comment'] = ($data->hasSharedComments()) ? $tablespace->fields['spccomment'] : '';
+ }
+
+ echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
+ echo $misc->form;
+ echo "<table>\n";
+ echo "<tr><th class=\"data left required\">{$lang['strname']}</th>\n";
+ echo "<td class=\"data1\">";
+ echo "<input name=\"name\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" value=\"",
+ htmlspecialchars($_POST['name']), "\" /></td></tr>\n";
+ echo "<tr><th class=\"data left required\">{$lang['strowner']}</th>\n";
+ echo "<td class=\"data1\"><select name=\"owner\">";
+ while (!$users->EOF) {
+ $uname = $users->fields['usename'];
+ echo "<option value=\"", htmlspecialchars($uname), "\"",
+ ($uname == $_POST['owner']) ? ' selected="selected"' : '', ">", htmlspecialchars($uname), "</option>\n";
+ $users->moveNext();
+ }
+ echo "</select></td></tr>\n";
+ if ($data->hasSharedComments()) {
+ echo "<tr><th class=\"data left\">{$lang['strcomment']}</th>\n";
+ echo "<td class=\"data1\">";
+ echo "<textarea rows=\"3\" cols=\"32\" name=\"comment\">",
+ htmlspecialchars($_POST['comment']), "</textarea></td></tr>\n";
+ }
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_edit\" />\n";
+ echo "<input type=\"hidden\" name=\"tablespace\" value=\"", htmlspecialchars($_REQUEST['tablespace']), "\" />\n";
+ echo "<input type=\"submit\" name=\"alter\" value=\"{$lang['stralter']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ } else {
+ echo "<p>{$lang['strnodata']}</p>\n";
+ }
+
+ }
+
+ /**
+ * Function to save after altering a tablespace
+ */
+ function doSaveAlter() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check data
+ if (trim($_POST['name']) == '') {
+ $this->doAlter($lang['strtablespaceneedsname']);
+ } else {
+ $status = $data->alterTablespace($_POST['tablespace'], $_POST['name'], $_POST['owner'], $_POST['comment']);
+ if ($status == 0) {
+ // If tablespace has been renamed, need to change to the new name
+ if ($_POST['tablespace'] != $_POST['name']) {
+ // Jump them to the new table name
+ $_REQUEST['tablespace'] = $_POST['name'];
+ }
+ $this->doDefault($lang['strtablespacealtered']);
+ } else {
+ $this->doAlter($lang['strtablespacealteredbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
+ function doDrop($confirm) {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ if ($confirm) {
+ $this->printTrail('tablespace');
+ $misc->printTitle($lang['strdrop'], 'pg.tablespace.drop');
+
+ echo "<p>", sprintf($lang['strconfdroptablespace'], $misc->printVal($_REQUEST['tablespace'])), "</p>\n";
+
+ echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
+ echo $misc->form;
+ echo "<input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
+ echo "<input type=\"hidden\" name=\"tablespace\" value=\"", htmlspecialchars($_REQUEST['tablespace']), "\" />\n";
+ echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
+ echo "</form>\n";
+ } else {
+ $status = $data->droptablespace($_REQUEST['tablespace']);
+ if ($status == 0) {
+ $this->doDefault($lang['strtablespacedropped']);
+ } else {
+ $this->doDefault($lang['strtablespacedroppedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Displays a screen where they can enter a new tablespace
+ */
+ function doCreate($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $server_info = $misc->getServerInfo();
+
+ if (!isset($_POST['formSpcname'])) {
+ $_POST['formSpcname'] = '';
+ }
+
+ if (!isset($_POST['formOwner'])) {
+ $_POST['formOwner'] = $server_info['username'];
+ }
+
+ if (!isset($_POST['formLoc'])) {
+ $_POST['formLoc'] = '';
+ }
+
+ if (!isset($_POST['formComment'])) {
+ $_POST['formComment'] = '';
+ }
+
+ // Fetch all users
+ $users = $data->getUsers();
+
+ $this->printTrail('server');
+ $misc->printTitle($lang['strcreatetablespace'], 'pg.tablespace.create');
+ $misc->printMsg($msg);
+
+ echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
+ echo $misc->form;
+ echo "<table>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strname']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"32\" name=\"formSpcname\" maxlength=\"{$data->_maxNameLen}\" value=\"", htmlspecialchars($_POST['formSpcname']), "\" /></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strowner']}</th>\n";
+ echo "\t\t<td class=\"data1\"><select name=\"formOwner\">\n";
+ while (!$users->EOF) {
+ $uname = $users->fields['usename'];
+ echo "\t\t\t<option value=\"", htmlspecialchars($uname), "\"",
+ ($uname == $_POST['formOwner']) ? ' selected="selected"' : '', ">", htmlspecialchars($uname), "</option>\n";
+ $users->moveNext();
+ }
+ echo "\t\t</select></td>\n\t</tr>\n";
+ echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strlocation']}</th>\n";
+ echo "\t\t<td class=\"data1\"><input size=\"32\" name=\"formLoc\" value=\"", htmlspecialchars($_POST['formLoc']), "\" /></td>\n\t</tr>\n";
+ // Comments (if available)
+ if ($data->hasSharedComments()) {
+ echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strcomment']}</th>\n";
+ echo "\t\t<td><textarea name=\"formComment\" rows=\"3\" cols=\"32\">",
+ htmlspecialchars($_POST['formComment']), "</textarea></td>\n\t</tr>\n";
+ }
+ echo "</table>\n";
+ echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
+ echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
+ echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
+ echo "</form>\n";
+ }
+
+ /**
+ * Actually creates the new tablespace in the cluster
+ */
+ function doSaveCreate() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ // Check data
+ if (trim($_POST['formSpcname']) == '') {
+ $this->doCreate($lang['strtablespaceneedsname']);
+ } elseif (trim($_POST['formLoc']) == '') {
+ $this->doCreate($lang['strtablespaceneedsloc']);
+ } else {
+ // Default comment to blank if it isn't set
+ if (!isset($_POST['formComment'])) {
+ $_POST['formComment'] = null;
+ }
+
+ $status = $data->createTablespace($_POST['formSpcname'], $_POST['formOwner'], $_POST['formLoc'], $_POST['formComment']);
+ if ($status == 0) {
+ $this->doDefault($lang['strtablespacecreated']);
+ } else {
+ $this->doCreate($lang['strtablespacecreatedbad']);
+ }
+
+ }
+ }
+
+ /**
+ * Show default list of tablespaces in the cluster
+ */
+ function doDefault($msg = '') {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $this->printTrail('server');
+ $this->printTabs('server', 'tablespaces');
+ $misc->printMsg($msg);
+
+ $tablespaces = $data->getTablespaces();
+
+ $columns = [
+ 'database' => [
+ 'title' => $lang['strname'],
+ 'field' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
+ ],
+ 'owner' => [
+ 'title' => $lang['strowner'],
+ 'field' => \PHPPgAdmin\Decorators\Decorator::field('spcowner'),
+ ],
+ 'location' => [
+ 'title' => $lang['strlocation'],
+ 'field' => \PHPPgAdmin\Decorators\Decorator::field('spclocation'),
+ ],
+ 'actions' => [
+ 'title' => $lang['stractions'],
+ ],
+ ];
+
+ if ($data->hasSharedComments()) {
+ $columns['comment'] = [
+ 'title' => $lang['strcomment'],
+ 'field' => \PHPPgAdmin\Decorators\Decorator::field('spccomment'),
+ ];
+ }
+
+ $actions = [
+ 'alter' => [
+ 'content' => $lang['stralter'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tablespaces.php',
+ 'urlvars' => [
+ 'action' => 'edit',
+ 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
+ ],
+ ],
+ ],
+ ],
+ 'drop' => [
+ 'content' => $lang['strdrop'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tablespaces.php',
+ 'urlvars' => [
+ 'action' => 'confirm_drop',
+ 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
+ ],
+ ],
+ ],
+ ],
+ 'privileges' => [
+ 'content' => $lang['strprivileges'],
+ 'attr' => [
+ 'href' => [
+ 'url' => 'privileges.php',
+ 'urlvars' => [
+ 'subject' => 'tablespace',
+ 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ echo $this->printTable($tablespaces, $columns, $actions, 'tablespaces-tablespaces', $lang['strnotablespaces']);
+
+ $this->printNavLinks(['create' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'tablespaces.php',
+ 'urlvars' => [
+ 'action' => 'create',
+ 'server' => $_REQUEST['server'],
+ ],
+ ],
+ ],
+ 'content' => $lang['strcreatetablespace'],
+ ]], 'tablespaces-tablespaces', get_defined_vars());
+ }
+
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+ $action->$this->action;
+
+ $misc->printHeader($lang['strtablespaces']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'save_create':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doDrop(false);
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'save_edit':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveAlter();
+ }
+
+ break;
+ case 'edit':
+ $this->doAlter();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+ }
+
+}
diff --git a/src/controllers/TriggerController.php b/src/controllers/TriggerController.php
index 16e22338..a2927362 100644
--- a/src/controllers/TriggerController.php
+++ b/src/controllers/TriggerController.php
@@ -9,9 +9,107 @@ use \PHPPgAdmin\Decorators\Decorator;
class TriggerController extends BaseController {
public $_name = 'TriggerController';
-/**
- * Function to save after altering a trigger
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table'] . ' - ' . $lang['strtriggers']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'alter':
+ if (isset($_POST['alter'])) {
+ $this->doSaveAlter();
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_alter':
+ $this->doAlter();
+ break;
+ case 'confirm_enable':
+ $this->doEnable(true);
+ break;
+ case 'confirm_disable':
+ $this->doDisable(true);
+ break;
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['yes'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'enable':
+ if (isset($_POST['yes'])) {
+ $this->doEnable(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'disable':
+ if (isset($_POST['yes'])) {
+ $this->doDisable(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $triggers = $data->getTriggers($_REQUEST['table']);
+
+ $reqvars = $misc->getRequestVars('table');
+
+ $attrs = [
+ 'text' => Decorator::field('tgname'),
+ 'icon' => 'Trigger',
+ ];
+
+ return $misc->printTree($triggers, $attrs, 'triggers');
+
+ }
+
+ /**
+ * Function to save after altering a trigger
+ */
public function doSaveAlter() {
$conf = $this->conf;
$misc = $this->misc;
@@ -36,7 +134,7 @@ class TriggerController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('trigger');
+ $this->printTrail('trigger');
$misc->printTitle($lang['stralter'], 'pg.trigger.alter');
$misc->printMsg($msg);
@@ -78,7 +176,7 @@ class TriggerController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('trigger');
+ $this->printTrail('trigger');
$misc->printTitle($lang['strdrop'], 'pg.trigger.drop');
echo "<p>", sprintf($lang['strconfdroptrigger'], $misc->printVal($_REQUEST['trigger']),
@@ -115,7 +213,7 @@ class TriggerController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('trigger');
+ $this->printTrail('trigger');
$misc->printTitle($lang['strenable'], 'pg.table.alter');
echo "<p>", sprintf($lang['strconfenabletrigger'], $misc->printVal($_REQUEST['trigger']),
@@ -151,7 +249,7 @@ class TriggerController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('trigger');
+ $this->printTrail('trigger');
$misc->printTitle($lang['strdisable'], 'pg.table.alter');
echo "<p>", sprintf($lang['strconfdisabletrigger'], $misc->printVal($_REQUEST['trigger']),
@@ -186,7 +284,7 @@ class TriggerController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('table');
+ $this->printTrail('table');
$misc->printTitle($lang['strcreatetrigger'], 'pg.trigger.create');
$misc->printMsg($msg);
@@ -296,8 +394,8 @@ class TriggerController extends BaseController {
return $actions;
};
- $misc->printTrail('table');
- $misc->printTabs('table', 'triggers');
+ $this->printTrail('table');
+ $this->printTabs('table', 'triggers');
$misc->printMsg($msg);
$triggers = $data->getTriggers($_REQUEST['table']);
@@ -383,9 +481,9 @@ class TriggerController extends BaseController {
];
}
- echo $misc->printTable($triggers, $columns, $actions, 'triggers-triggers', $lang['strnotriggers'], $tgPre);
+ echo $this->printTable($triggers, $columns, $actions, 'triggers-triggers', $lang['strnotriggers'], $tgPre);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'triggers.php',
diff --git a/src/controllers/TypeController.php b/src/controllers/TypeController.php
index 7ffe5a1b..23bc2fb6 100644
--- a/src/controllers/TypeController.php
+++ b/src/controllers/TypeController.php
@@ -9,9 +9,103 @@ use \PHPPgAdmin\Decorators\Decorator;
class TypeController extends BaseController {
public $_name = 'TypeController';
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strtypes']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'create_comp':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doCreateComposite();
+ }
+
+ break;
+ case 'create_enum':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doCreateEnum();
+ }
+
+ break;
+ case 'save_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ $this->doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doDrop(false);
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ case 'properties':
+ $this->doProperties();
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
/**
- * Show read only properties for a type
+ * Generate XML for the browser tree.
*/
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $types = $data->getTypes();
+
+ $reqvars = $misc->getRequestVars('type');
+
+ $attrs = [
+ 'text' => Decorator::field('typname'),
+ 'icon' => 'Type',
+ 'toolTip' => Decorator::field('typcomment'),
+ 'action' => Decorator::actionurl('types.php',
+ $reqvars,
+ [
+ 'action' => 'properties',
+ 'type' => Decorator::field('basename'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($types, $attrs, 'types');
+
+ }
+
+ /**
+ * Show read only properties for a type
+ */
public function doProperties($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -20,7 +114,7 @@ class TypeController extends BaseController {
// Get type (using base name)
$typedata = $data->getType($_REQUEST['type']);
- $misc->printTrail('type');
+ $this->printTrail('type');
$misc->printTitle($lang['strproperties'], 'pg.type');
$misc->printMsg($msg);
@@ -51,7 +145,7 @@ class TypeController extends BaseController {
$actions = [];
- echo $misc->printTable($attrs, $columns, $actions, 'types-properties', null, $attPre);
+ echo $this->printTable($attrs, $columns, $actions, 'types-properties', null, $attPre);
break;
case 'e':
@@ -84,7 +178,7 @@ class TypeController extends BaseController {
echo "</table>\n";
}
- $misc->printNavLinks(['showall' => [
+ $this->printNavLinks(['showall' => [
'attr' => [
'href' => [
'url' => 'types.php',
@@ -107,11 +201,13 @@ class TypeController extends BaseController {
* Show confirmation of drop and perform actual drop
*/
public function doDrop($confirm) {
- global $data, $misc;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('type');
+ $this->printTrail('type');
$misc->printTitle($lang['strdrop'], 'pg.type.drop');
echo "<p>", sprintf($lang['strconfdroptype'], $misc->printVal($_REQUEST['type'])), "</p>\n";
@@ -140,8 +236,10 @@ class TypeController extends BaseController {
* Displays a screen where they can enter a new composite type
*/
public function doCreateComposite($msg = '') {
- global $data, $misc;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if (!isset($_REQUEST['stage'])) {
$_REQUEST['stage'] = 1;
@@ -161,7 +259,7 @@ class TypeController extends BaseController {
switch ($_REQUEST['stage']) {
case 1:
- $misc->printTrail('type');
+ $this->printTrail('type');
$misc->printTitle($lang['strcreatecomptype'], 'pg.type.create');
$misc->printMsg($msg);
@@ -187,7 +285,6 @@ class TypeController extends BaseController {
echo "</form>\n";
break;
case 2:
- global $lang;
// Check inputs
$fields = trim($_REQUEST['fields']);
@@ -203,7 +300,7 @@ class TypeController extends BaseController {
$types = $data->getTypes(true, false, true);
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatecomptype'], 'pg.type.create');
$misc->printMsg($msg);
@@ -265,7 +362,6 @@ class TypeController extends BaseController {
break;
case 3:
- global $data, $lang;
// Check inputs
$fields = trim($_REQUEST['fields']);
@@ -304,8 +400,10 @@ class TypeController extends BaseController {
* Displays a screen where they can enter a new enum type
*/
public function doCreateEnum($msg = '') {
- global $data, $misc;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if (!isset($_REQUEST['stage'])) {
$_REQUEST['stage'] = 1;
@@ -325,7 +423,7 @@ class TypeController extends BaseController {
switch ($_REQUEST['stage']) {
case 1:
- $misc->printTrail('type');
+ $this->printTrail('type');
$misc->printTitle($lang['strcreateenumtype'], 'pg.type.create');
$misc->printMsg($msg);
@@ -351,7 +449,6 @@ class TypeController extends BaseController {
echo "</form>\n";
break;
case 2:
- global $lang;
// Check inputs
$values = trim($_REQUEST['values']);
@@ -365,7 +462,7 @@ class TypeController extends BaseController {
return;
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreateenumtype'], 'pg.type.create');
$misc->printMsg($msg);
@@ -397,7 +494,6 @@ class TypeController extends BaseController {
break;
case 3:
- global $data, $lang;
// Check inputs
$values = trim($_REQUEST['values']);
@@ -434,8 +530,10 @@ class TypeController extends BaseController {
* Displays a screen where they can enter a new type
*/
public function doCreate($msg = '') {
- global $data, $misc;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
if (!isset($_POST['typname'])) {
$_POST['typname'] = '';
@@ -477,7 +575,7 @@ class TypeController extends BaseController {
$funcs = $data->getFunctions(true);
$types = $data->getTypes(true);
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreatetype'], 'pg.type.create');
$misc->printMsg($msg);
@@ -553,8 +651,10 @@ class TypeController extends BaseController {
* Actually creates the new type in the database
*/
public function doSaveCreate() {
- global $data;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
// Check that they've given a name and a length.
// Note: We're assuming they've given in and out functions here
@@ -589,11 +689,13 @@ class TypeController extends BaseController {
* Show default list of types in the database
*/
public function doDefault($msg = '') {
- global $data, $conf, $misc;
- global $lang;
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'types');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'types');
$misc->printMsg($msg);
$types = $data->getTypes();
@@ -652,7 +754,7 @@ class TypeController extends BaseController {
],
];
- echo $misc->printTable($types, $columns, $actions, 'types-types', $lang['strnotypes']);
+ echo $this->printTable($types, $columns, $actions, 'types-types', $lang['strnotypes']);
$navlinks = [
'create' => [
@@ -703,7 +805,7 @@ class TypeController extends BaseController {
unset($navlinks['enum']);
}
- $misc->printNavLinks($navlinks, 'types-types', get_defined_vars());
+ $this->printNavLinks($navlinks, 'types-types', get_defined_vars());
}
}
diff --git a/src/controllers/UserController.php b/src/controllers/UserController.php
index 59a9d742..01644ccd 100644
--- a/src/controllers/UserController.php
+++ b/src/controllers/UserController.php
@@ -26,8 +26,8 @@ class UserController extends BaseController {
$userdata = $data->getUser($server_info['username']);
$_REQUEST['user'] = $server_info['username'];
- $misc->printTrail('user');
- $misc->printTabs('server', 'account');
+ $this->printTrail('user');
+ $this->printTabs('server', 'account');
$misc->printMsg($msg);
if ($userdata->recordCount() > 0) {
@@ -47,7 +47,7 @@ class UserController extends BaseController {
echo "<p>{$lang['strnodata']}</p>\n";
}
- $misc->printNavLinks(['changepassword' => [
+ $this->printNavLinks(['changepassword' => [
'attr' => [
'href' => [
'url' => 'users.php',
@@ -74,7 +74,7 @@ class UserController extends BaseController {
if ($confirm) {
$_REQUEST['user'] = $server_info['username'];
- $misc->printTrail('user');
+ $this->printTrail('user');
$misc->printTitle($lang['strchangepassword'], 'pg.user.alter');
$misc->printMsg($msg);
@@ -130,7 +130,7 @@ class UserController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('user');
+ $this->printTrail('user');
$misc->printTitle($lang['stralter'], 'pg.user.alter');
$misc->printMsg($msg);
@@ -228,7 +228,7 @@ class UserController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('user');
+ $this->printTrail('user');
$misc->printTitle($lang['strdrop'], 'pg.user.drop');
echo "<p>", sprintf($lang['strconfdropuser'], $misc->printVal($_REQUEST['username'])), "</p>\n";
@@ -276,7 +276,7 @@ class UserController extends BaseController {
$_POST['formExpires'] = '';
}
- $misc->printTrail('server');
+ $this->printTrail('server');
$misc->printTitle($lang['strcreateuser'], 'pg.user.create');
$misc->printMsg($msg);
@@ -339,13 +339,12 @@ class UserController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- function renderUseExpires($val) {
- global $lang;
+ $renderUseExpires = function ($val) use ($lang) {
return $val == 'infinity' ? $lang['strnever'] : htmlspecialchars($val);
- }
+ };
- $misc->printTrail('server');
- $misc->printTabs('server', 'users');
+ $this->printTrail('server');
+ $this->printTabs('server', 'users');
$misc->printMsg($msg);
$users = $data->getUsers();
@@ -369,7 +368,7 @@ class UserController extends BaseController {
'title' => $lang['strexpires'],
'field' => Decorator::field('useexpires'),
'type' => 'callback',
- 'params' => ['function' => 'renderUseExpires', 'null' => $lang['strnever']],
+ 'params' => ['function' => $renderUseExpires, 'null' => $lang['strnever']],
],
'defaults' => [
'title' => $lang['strsessiondefaults'],
@@ -407,9 +406,9 @@ class UserController extends BaseController {
],
];
- echo $misc->printTable($users, $columns, $actions, 'users-users', $lang['strnousers']);
+ echo $this->printTable($users, $columns, $actions, 'users-users', $lang['strnousers']);
- $misc->printNavLinks(['create' => [
+ $this->printNavLinks(['create' => [
'attr' => [
'href' => [
'url' => 'users.php',
diff --git a/src/controllers/ViewController.php b/src/controllers/ViewController.php
index 8273fd6f..3f84997f 100644
--- a/src/controllers/ViewController.php
+++ b/src/controllers/ViewController.php
@@ -7,12 +7,152 @@ use \PHPPgAdmin\Decorators\Decorator;
* Base controller class
*/
class ViewController extends BaseController {
- public $script = 'view.php';
- public $_name = 'ViewController';
+ public $script = 'views.php';
+ public $_name = 'ViewController';
+ public $table_place = 'views-views';
+
+ public function render() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $action = $this->action;
+
+ if ($action == 'tree') {
+ return $this->doTree();
+ } else if ($action == 'subtree') {
+ return $this->doSubTree();
+ }
+
+ $data = $misc->getDatabaseAccessor();
+
+ $misc->printHeader($lang['strviews']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'selectrows':
+ if (!isset($_REQUEST['cancel'])) {
+ $this->doSelectRows(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confselectrows':
+ $this->doSelectRows(true);
+ break;
+ case 'save_create_wiz':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreateWiz();
+ }
+
+ break;
+ case 'wiz_create':
+ doWizardCreate();
+ break;
+ case 'set_params_create':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSetParamsCreate();
+ }
+
+ break;
+ case 'save_create':
+ if (isset($_REQUEST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doSaveCreate();
+ }
+
+ break;
+ case 'create':
+ doCreate();
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ return $misc->printFooter();
+
+ }
/**
- * Ask for select parameters and perform select
+ * Generate XML for the browser tree.
*/
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $views = $data->getViews();
+
+ $reqvars = $misc->getRequestVars('view');
+
+ $attrs = [
+ 'text' => Decorator::field('relname'),
+ 'icon' => 'View',
+ 'iconAction' => Decorator::url('display.php', $reqvars, ['view' => Decorator::field('relname')]),
+ 'toolTip' => Decorator::field('relcomment'),
+ 'action' => Decorator::redirecturl('redirect.php', $reqvars, ['view' => Decorator::field('relname')]),
+ 'branch' => Decorator::url('views.php', $reqvars,
+ [
+ 'action' => 'subtree',
+ 'view' => Decorator::field('relname'),
+ ]
+ ),
+ ];
+
+ return $misc->printTree($views, $attrs, 'views');
+ }
+
+ function doSubTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $tabs = $misc->getNavTabs('view');
+ $items = $misc->adjustTabsForTree($tabs);
+ $reqvars = $misc->getRequestVars('view');
+
+ $attrs = [
+ 'text' => Decorator::field('title'),
+ 'icon' => Decorator::field('icon'),
+ 'action' => Decorator::actionurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars'), ['view' => $_REQUEST['view']]),
+ 'branch' => Decorator::ifempty(
+ Decorator::field('branch'), '', Decorator::url(Decorator::field('url'), Decorator::field('urlvars'), $reqvars,
+ [
+ 'action' => 'tree',
+ 'view' => $_REQUEST['view'],
+ ]
+ )
+ ),
+ ];
+
+ return $misc->printTree($items, $attrs, 'view');
+ }
+
+ /**
+ * Ask for select parameters and perform select
+ */
public function doSelectRows($confirm, $msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -20,13 +160,15 @@ class ViewController extends BaseController {
$data = $misc->getDatabaseAccessor();
if ($confirm) {
- $misc->printTrail('view');
- $misc->printTabs('view', 'select');
+ $this->printTrail('view');
+ $this->printTabs('view', 'select');
$misc->printMsg($msg);
$attrs = $data->getTableAttributes($_REQUEST['view']);
- echo "<form action=\"/src/views/views.php\" method=\"post\" id=\"selectform\">\n";
+ echo '<form action="/src/views/' . $this->script . '" method="post" id="selectform">';
+ echo "\n";
+
if ($attrs->recordCount() > 0) {
// JavaScript for select all feature
echo "<script type=\"text/javascript\">\n";
@@ -96,6 +238,7 @@ class ViewController extends BaseController {
echo "<input type=\"submit\" name=\"select\" accesskey=\"r\" value=\"{$lang['strselect']}\" />\n";
echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
echo "</form>\n";
+ return;
} else {
if (!isset($_POST['show'])) {
$_POST['show'] = [];
@@ -118,24 +261,27 @@ class ViewController extends BaseController {
}
if (sizeof($_POST['show']) == 0) {
- $this->doSelectRows(true, $lang['strselectneedscol']);
+ return $this->doSelectRows(true, $lang['strselectneedscol']);
} else {
// Generate query SQL
- $query = $data->getSelectSQL($_REQUEST['view'], array_keys($_POST['show']),
- $_POST['values'], $_POST['ops']);
+ $query = $data->getSelectSQL($_REQUEST['view'], array_keys($_POST['show']), $_POST['values'], $_POST['ops']);
+
$_REQUEST['query'] = $query;
$_REQUEST['return'] = "schema";
+
$misc->setNoOutput(true);
- include './display.php';
- exit;
+
+ $display_controller = new DisplayController($this->getContainer());
+
+ return $display_controller->render();
}
}
}
-/**
- * Show confirmation of drop and perform actual drop
- */
+ /**
+ * Show confirmation of drop and perform actual drop
+ */
public function doDrop($confirm) {
$conf = $this->conf;
$misc = $this->misc;
@@ -148,7 +294,7 @@ class ViewController extends BaseController {
}
if ($confirm) {
- $misc->printTrail('view');
+ $this->printTrail('view');
$misc->printTitle($lang['strdrop'], 'pg.view.drop');
echo "<form action=\"/src/views/views.php\" method=\"post\">\n";
@@ -210,9 +356,9 @@ class ViewController extends BaseController {
}
-/**
- * Sets up choices for table linkage, and which fields to select for the view we're creating
- */
+ /**
+ * Sets up choices for table linkage, and which fields to select for the view we're creating
+ */
public function doSetParamsCreate($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -232,7 +378,7 @@ class ViewController extends BaseController {
$_REQUEST['formComment'] = '';
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreateviewwiz'], 'pg.view.create');
$misc->printMsg($msg);
@@ -362,9 +508,9 @@ class ViewController extends BaseController {
}
}
-/**
- * Display a wizard where they can enter a new view
- */
+ /**
+ * Display a wizard where they can enter a new view
+ */
public function doWizardCreate($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -373,7 +519,7 @@ class ViewController extends BaseController {
$tables = $data->getTables(true);
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreateviewwiz'], 'pg.view.create');
$misc->printMsg($msg);
@@ -401,9 +547,9 @@ class ViewController extends BaseController {
echo "</form>\n";
}
-/**
- * Displays a screen where they can enter a new view
- */
+ /**
+ * Displays a screen where they can enter a new view
+ */
public function doCreate($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
@@ -426,7 +572,7 @@ class ViewController extends BaseController {
$_REQUEST['formComment'] = '';
}
- $misc->printTrail('schema');
+ $this->printTrail('schema');
$misc->printTitle($lang['strcreateview'], 'pg.view.create');
$misc->printMsg($msg);
@@ -449,9 +595,9 @@ class ViewController extends BaseController {
echo "</form>\n";
}
-/**
- * Actually creates the new view in the database
- */
+ /**
+ * Actually creates the new view in the database
+ */
public function doSaveCreate() {
$conf = $this->conf;
$misc = $this->misc;
@@ -475,9 +621,9 @@ class ViewController extends BaseController {
}
}
-/**
- * Actually creates the new wizard view in the database
- */
+ /**
+ * Actually creates the new wizard view in the database
+ */
public function doSaveCreateWiz() {
$conf = $this->conf;
$misc = $this->misc;
@@ -615,17 +761,17 @@ class ViewController extends BaseController {
}
}
-/**
- * Show default list of views in the database
- */
+ /**
+ * Show default list of views in the database
+ */
public function doDefault($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('schema');
- $misc->printTabs('schema', 'views');
+ $this->printTrail('schema');
+ $this->printTabs('schema', 'views');
$misc->printMsg($msg);
$views = $data->getViews();
@@ -716,7 +862,7 @@ class ViewController extends BaseController {
],
];
- echo $misc->printTable($views, $columns, $actions, 'views-views', $lang['strnoviews']);
+ echo $this->printTable($views, $columns, $actions, $this->table_place, $lang['strnoviews']);
$navlinks = [
'create' => [
@@ -748,7 +894,7 @@ class ViewController extends BaseController {
'content' => $lang['strcreateviewwiz'],
],
];
- $misc->printNavLinks($navlinks, 'views-views', get_defined_vars());
+ $this->printNavLinks($navlinks, $this->table_place, get_defined_vars());
}
diff --git a/src/controllers/ViewPropertyController.php b/src/controllers/ViewPropertyController.php
index 2c67d166..48f6a85e 100644
--- a/src/controllers/ViewPropertyController.php
+++ b/src/controllers/ViewPropertyController.php
@@ -9,9 +9,120 @@ use \PHPPgAdmin\Decorators\Decorator;
class ViewPropertyController extends BaseController {
public $_name = 'ViewPropertyController';
-/**
- * Function to save after editing a view
- */
+ function render() {
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+
+ $action = $this->action;
+ if ($action == 'tree') {
+ return $this->doTree();
+ }
+
+ $misc->printHeader($lang['strviews'] . ' - ' . $_REQUEST['view']);
+ $misc->printBody();
+
+ switch ($action) {
+ case 'save_edit':
+ if (isset($_POST['cancel'])) {
+ $this->doDefinition();
+ } else {
+ $this->doSaveEdit();
+ }
+
+ break;
+ case 'edit':
+ $this->doEdit();
+ break;
+ case 'export':
+ $this->doExport();
+ break;
+ case 'definition':
+ $this->doDefinition();
+ break;
+ case 'properties':
+ if (isset($_POST['cancel'])) {
+ $this->doDefault();
+ } else {
+ $this->doProperties();
+ }
+
+ break;
+ case 'alter':
+ if (isset($_POST['alter'])) {
+ $this->doAlter(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_alter':
+ doAlter(true);
+ break;
+ case 'drop':
+ if (isset($_POST['drop'])) {
+ $this->doDrop(false);
+ } else {
+ $this->doDefault();
+ }
+
+ break;
+ case 'confirm_drop':
+ $this->doDrop(true);
+ break;
+ default:
+ $this->doDefault();
+ break;
+ }
+
+ $misc->printFooter();
+
+ }
+
+ function doTree() {
+
+ $conf = $this->conf;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $data = $misc->getDatabaseAccessor();
+
+ $reqvars = $misc->getRequestVars('column');
+ $columns = $data->getTableAttributes($_REQUEST['view']);
+
+ $attrs = [
+ 'text' => Decorator::field('attname'),
+ 'action' => Decorator::actionurl('colproperties.php',
+ $reqvars,
+ [
+ 'view' => $_REQUEST['view'],
+ 'column' => Decorator::field('attname'),
+ ]
+ ),
+ 'icon' => 'Column',
+ 'iconAction' => Decorator::url('display.php',
+ $reqvars,
+ [
+ 'view' => $_REQUEST['view'],
+ 'column' => Decorator::field('attname'),
+ 'query' => Decorator::replace(
+ 'SELECT "%column%", count(*) AS "count" FROM %view% GROUP BY "%column%" ORDER BY "%column%"',
+ [
+ '%column%' => Decorator::field('attname'),
+ '%view%' => $_REQUEST['view'],
+ ]
+ ),
+ ]
+ ),
+ 'toolTip' => Decorator::field('comment'),
+ ];
+
+ return $misc->printTree($columns, $attrs, 'viewcolumns');
+
+ }
+
+ /**
+ * Function to save after editing a view
+ */
public function doSaveEdit() {
$conf = $this->conf;
$misc = $this->misc;
@@ -27,16 +138,16 @@ class ViewPropertyController extends BaseController {
}
-/**
- * Function to allow editing of a view
- */
+ /**
+ * Function to allow editing of a view
+ */
public function doEdit($msg = '') {
$conf = $this->conf;
$misc = $this->misc;
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('view');
+ $this->printTrail('view');
$misc->printTitle($lang['stredit'], 'pg.view.alter');
$misc->printMsg($msg);
@@ -83,8 +194,8 @@ class ViewPropertyController extends BaseController {
$lang = $this->lang;
$data = $misc->getDatabaseAccessor();
- $misc->printTrail('view');
- $misc->printTabs('view', 'export');
+ $this->printTrail('view');
+ $this->printTabs('view', 'export');
$misc->printMsg($msg);
echo "<form action=\"/src/views/dataexport.php\" method=\"post\">\n";
@@ -145,8 +256,8 @@ class ViewPropertyController extends BaseController {
// Get view
$vdata = $data->getView($_REQUEST['view']);
- $misc->printTrail('view');
- $misc->printTabs('view', 'definition');
+ $this->printTrail('view');
+ $this->printTabs('view', 'definition');
$misc->printMsg($msg);
if ($vdata->recordCount() > 0) {
@@ -163,7 +274,7 @@ class ViewPropertyController extends BaseController {
echo "<p>{$lang['strnodata']}</p>\n";
}
- $misc->printNavLinks(['alter' => [
+ $this->printNavLinks(['alter' => [
'attr' => [
'href' => [
'url' => 'viewproperties.php',
@@ -195,9 +306,8 @@ class ViewPropertyController extends BaseController {
switch ($_REQUEST['stage']) {
case 1:
- global $lang;
- $misc->printTrail('column');
+ $this->printTrail('column');
$misc->printTitle($lang['stralter'], 'pg.column.alter');
$misc->printMsg($msg);
@@ -238,7 +348,6 @@ class ViewPropertyController extends BaseController {
break;
case 2:
- global $data, $lang;
// Check inputs
if (trim($_REQUEST['field']) == '') {
@@ -272,7 +381,7 @@ class ViewPropertyController extends BaseController {
if ($confirm) {
- $misc->printTrail('view');
+ $this->printTrail('view');
$misc->printTitle($lang['stralter'], 'pg.view.alter');
$misc->printMsg($msg);
@@ -348,7 +457,6 @@ class ViewPropertyController extends BaseController {
}
} else {
- global $data, $lang, $_reload_browser, $misc;
// For databases that don't allow owner change
if (!isset($_POST['owner'])) {
@@ -396,8 +504,8 @@ class ViewPropertyController extends BaseController {
$rowdata->fields['+type'] = $data->formatType($rowdata->fields['type'], $rowdata->fields['atttypmod']);
};
- $misc->printTrail('view');
- $misc->printTabs('view', 'columns');
+ $this->printTrail('view');
+ $this->printTabs('view', 'columns');
$misc->printMsg($msg);
// Get view
@@ -450,7 +558,7 @@ class ViewPropertyController extends BaseController {
],
];
- echo $misc->printTable($attrs, $columns, $actions, 'viewproperties-viewproperties', null, $attPre);
+ echo $this->printTable($attrs, $columns, $actions, 'viewproperties-viewproperties', null, $attPre);
echo "<br />\n";
@@ -518,7 +626,7 @@ class ViewPropertyController extends BaseController {
],
];
- $misc->printNavLinks($navlinks, 'viewproperties-viewproperties', get_defined_vars());
+ $this->printNavLinks($navlinks, 'viewproperties-viewproperties', get_defined_vars());
}
}
diff --git a/src/classes/database/ADODB_base.php b/src/database/ADODB_base.php
index fe052a75..fe052a75 100644
--- a/src/classes/database/ADODB_base.php
+++ b/src/database/ADODB_base.php
diff --git a/src/classes/database/Connection.php b/src/database/Connection.php
index 93a61df0..93a61df0 100755
--- a/src/classes/database/Connection.php
+++ b/src/database/Connection.php
diff --git a/src/classes/database/Postgres.php b/src/database/Postgres.php
index 2bf3ba59..c6df059a 100755
--- a/src/classes/database/Postgres.php
+++ b/src/database/Postgres.php
@@ -9,7 +9,7 @@ namespace PHPPgAdmin\Database;
class Postgres extends ADODB_base {
- var $major_version = 9.4;
+ var $major_version = 9.5;
// Max object name length
var $_maxNameLen = 63;
// Store the current schema
@@ -434,7 +434,7 @@ class Postgres extends ADODB_base {
}
function getHelpPages() {
- include_once BASE_PATH . '/help/PostgresDoc94.php';
+ include_once BASE_PATH . '/help/PostgresDoc95.php';
return $this->help_page;
}
@@ -3269,6 +3269,23 @@ class Postgres extends ADODB_base {
}
/**
+ * Returns a list of all views in the database
+ * @return All views
+ */
+ function getMaterializedViews() {
+ $c_schema = $this->_schema;
+ $this->clean($c_schema);
+ $sql = "
+ SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
+ WHERE (n.nspname='{$c_schema}') AND (c.relkind = 'm'::\"char\")
+ ORDER BY relname";
+
+ return $this->selectSet($sql);
+ }
+ /**
* Updates a view.
* @param $viewname The name fo the view to update
* @param $definition The new definition for the view
diff --git a/src/classes/database/Postgres74.php b/src/database/Postgres74.php
index 0af4d449..0af4d449 100644
--- a/src/classes/database/Postgres74.php
+++ b/src/database/Postgres74.php
diff --git a/src/classes/database/Postgres80.php b/src/database/Postgres80.php
index 37451b11..37451b11 100644
--- a/src/classes/database/Postgres80.php
+++ b/src/database/Postgres80.php
diff --git a/src/classes/database/Postgres81.php b/src/database/Postgres81.php
index ddc81730..ddc81730 100644
--- a/src/classes/database/Postgres81.php
+++ b/src/database/Postgres81.php
diff --git a/src/classes/database/Postgres82.php b/src/database/Postgres82.php
index a0a93c53..a0a93c53 100644
--- a/src/classes/database/Postgres82.php
+++ b/src/database/Postgres82.php
diff --git a/src/classes/database/Postgres83.php b/src/database/Postgres83.php
index 63b22fb0..63b22fb0 100644
--- a/src/classes/database/Postgres83.php
+++ b/src/database/Postgres83.php
diff --git a/src/classes/database/Postgres84.php b/src/database/Postgres84.php
index bbdb3a09..bbdb3a09 100755
--- a/src/classes/database/Postgres84.php
+++ b/src/database/Postgres84.php
diff --git a/src/classes/database/Postgres90.php b/src/database/Postgres90.php
index 58e66828..58e66828 100755
--- a/src/classes/database/Postgres90.php
+++ b/src/database/Postgres90.php
diff --git a/src/classes/database/Postgres91.php b/src/database/Postgres91.php
index e4b48114..e4b48114 100755
--- a/src/classes/database/Postgres91.php
+++ b/src/database/Postgres91.php
diff --git a/src/classes/database/Postgres92.php b/src/database/Postgres92.php
index 8110d4a7..8110d4a7 100644
--- a/src/classes/database/Postgres92.php
+++ b/src/database/Postgres92.php
diff --git a/src/classes/database/Postgres93.php b/src/database/Postgres93.php
index 63c506c2..63c506c2 100644
--- a/src/classes/database/Postgres93.php
+++ b/src/database/Postgres93.php
diff --git a/src/decorator.inc.php b/src/decorator.inc.php
deleted file mode 100644
index d3309e6d..00000000
--- a/src/decorator.inc.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-// $Id: decorator.inc.php,v 1.8 2007/04/05 11:09:38 mr-russ Exp $
-
-// This group of functions and classes provides support for
-// resolving values in a lazy manner (ie, as and when required)
-// using the Decorator pattern.
-
-###TODO: Better documentation!!!
-
-// Construction functions:
-
-function merge( /* ... */) {
- return new \PHPPgAdmin\Decorators\ArrayMergeDecorator(func_get_args());
-}
-
-function concat( /* ... */) {
- return new \PHPPgAdmin\Decorators\ConcatDecorator(func_get_args());
-}
-
-function callback($callback, $params = null) {
- return new \PHPPgAdmin\Decorators\CallbackDecorator($callback, $params);
-}
-
-function ifempty($value, $empty, $full = null) {
- return new \PHPPgAdmin\Decorators\IfEmptyDecorator($value, $empty, $full);
-}
-
-function url($base, $vars = null/* ... */) {
- // If more than one array of vars is given,
- // use an ArrayMergeDecorator to have them merged
- // at value evaluation time.
- if (func_num_args() > 2) {
- $v = func_get_args();
- array_shift($v);
- return new \PHPPgAdmin\Decorators\UrlDecorator($base, new \PHPPgAdmin\Decorators\ArrayMergeDecorator($v));
- }
- return new \PHPPgAdmin\Decorators\UrlDecorator($base, $vars);
-}
-
-function replace($str, $params) {
- return new \PHPPgAdmin\Decorators\replaceDecorator($str, $params);
-}
-
-// Resolving functions:
-
-function value(&$var, &$fields, $esc = null) {
- if (is_a($var, 'PHPPgAdmin\Decorators\Decorator')) {
- $val = $var->value($fields);
- } else {
- $val = &$var;
- }
-
- if (is_string($val)) {
- switch ($esc) {
- case 'xml':
- return strtr($val, [
- '&' => '&amp;',
- "'" => '&apos;', '"' => '&quot;',
- '<' => '&lt;', '>' => '&gt;',
- ]);
- case 'html':
- return htmlentities($val, ENT_COMPAT, 'UTF-8');
- case 'url':
- return urlencode($val);
- }
- }
- return $val;
-}
-
-function value_xml(&$var, &$fields) {
- return value($var, $fields, 'xml');
-}
-
-function value_xml_attr($attr, &$var, &$fields) {
- $val = value($var, $fields, 'xml');
- if (!empty($val)) {
- return " {$attr}=\"{$val}\"";
- } else {
- return '';
- }
-
-}
-
-function value_url(&$var, &$fields) {
- return value($var, $fields, 'url');
-}
diff --git a/src/classes/decorators/ActionUrlDecorator.php b/src/decorators/ActionUrlDecorator.php
index 5176e02a..01378bad 100644
--- a/src/classes/decorators/ActionUrlDecorator.php
+++ b/src/decorators/ActionUrlDecorator.php
@@ -12,18 +12,18 @@ class ActionUrlDecorator extends Decorator {
}
function value($fields) {
- $url = value($this->b, $fields);
+ $url = Decorator::get_sanitized_value($this->b, $fields);
if ($url === false) {
return '';
}
if (!empty($this->q)) {
- $queryVars = value($this->q, $fields);
+ $queryVars = Decorator::get_sanitized_value($this->q, $fields);
$sep = '?';
foreach ($queryVars as $var => $value) {
- $url .= $sep . value_url($var, $fields) . '=' . value_url($value, $fields);
+ $url .= $sep . Decorator::value_url($var, $fields) . '=' . Decorator::value_url($value, $fields);
$sep = '&';
}
}
diff --git a/src/classes/decorators/ArrayMergeDecorator.php b/src/decorators/ArrayMergeDecorator.php
index 1a5e5a2f..7c1f0489 100644
--- a/src/classes/decorators/ArrayMergeDecorator.php
+++ b/src/decorators/ArrayMergeDecorator.php
@@ -7,9 +7,9 @@ class ArrayMergeDecorator extends Decorator {
}
function value($fields) {
- $accum = array();
+ $accum = [];
foreach ($this->m as $var) {
- $accum = array_merge($accum, value($var, $fields));
+ $accum = array_merge($accum, Decorator::get_sanitized_value($var, $fields));
}
return $accum;
}
diff --git a/src/classes/decorators/BranchUrlDecorator.php b/src/decorators/BranchUrlDecorator.php
index 592f3a0b..93a86530 100644
--- a/src/classes/decorators/BranchUrlDecorator.php
+++ b/src/decorators/BranchUrlDecorator.php
@@ -4,7 +4,7 @@ namespace PHPPgAdmin\Decorators;
class BranchUrlDecorator extends Decorator {
function __construct($base, $queryVars = null) {
- \PC::debug($base, 'BranchUrlDecorator');
+ //\PC::debug($base, 'BranchUrlDecorator');
$this->b = $base;
if ($queryVars !== null) {
@@ -14,19 +14,19 @@ class BranchUrlDecorator extends Decorator {
}
function value($fields) {
- $url = value($this->b, $fields);
+ $url = Decorator::get_sanitized_value($this->b, $fields);
if ($url === false) {
return '';
}
if (!empty($this->q)) {
- $queryVars = value($this->q, $fields);
+ $queryVars = Decorator::get_sanitized_value($this->q, $fields);
$sep = '?';
foreach ($queryVars as $var => $value) {
- $varname = value_url($var, $fields);
- $varvalue = value_url($value, $fields);
+ $varname = Decorator::value_url($var, $fields);
+ $varvalue = Decorator::value_url($value, $fields);
if ($varname == 'action') {
if ($varvalue == 'subtree') {
$url = '/tree/' . str_replace('.php', '/subtree', $url);
diff --git a/src/classes/decorators/CallbackDecorator.php b/src/decorators/CallbackDecorator.php
index 4207fc0f..a83a063e 100644
--- a/src/classes/decorators/CallbackDecorator.php
+++ b/src/decorators/CallbackDecorator.php
@@ -5,7 +5,7 @@ class CallbackDecorator extends Decorator {
function __construct($callback, $param = null) {
$this->fn = $callback;
- $this->p = $param;
+ $this->p = $param;
}
function value($fields) {
diff --git a/src/classes/decorators/ConcatDecorator.php b/src/decorators/ConcatDecorator.php
index 6c438e6c..7d242749 100644
--- a/src/classes/decorators/ConcatDecorator.php
+++ b/src/decorators/ConcatDecorator.php
@@ -9,7 +9,7 @@ class ConcatDecorator extends Decorator {
function value($fields) {
$accum = '';
foreach ($this->c as $var) {
- $accum .= value($var, $fields);
+ $accum .= Decorator::get_sanitized_value($var, $fields);
}
return trim($accum);
}
diff --git a/src/classes/decorators/Decorator.php b/src/decorators/Decorator.php
index d1c0bab8..9c6ae694 100644
--- a/src/classes/decorators/Decorator.php
+++ b/src/decorators/Decorator.php
@@ -10,6 +10,51 @@ class Decorator {
return $this->v;
}
+ public static function get_sanitized_value(&$var, &$fields, $esc = null) {
+ if (is_a($var, 'PHPPgAdmin\Decorators\Decorator')) {
+ $val = $var->value($fields);
+ } else {
+ $val = &$var;
+ }
+
+ if (is_string($val)) {
+ switch ($esc) {
+ case 'xml':
+ return strtr($val, [
+ '&' => '&amp;',
+ "'" => '&apos;', '"' => '&quot;',
+ '<' => '&lt;', '>' => '&gt;',
+ ]);
+ case 'html':
+ return htmlentities($val, ENT_COMPAT, 'UTF-8');
+ case 'url':
+ return urlencode($val);
+ }
+ }
+ return $val;
+ }
+
+ public static function value_xml_attr($attr, &$var, &$fields) {
+ $val = self::get_sanitized_value($var, $fields, 'xml');
+ if (!empty($val)) {
+ return " {$attr}=\"{$val}\"";
+ } else {
+ return '';
+ }
+
+ }
+
+ public static function value_url(&$var, &$fields) {
+ return self::get_sanitized_value($var, $fields, 'url');
+ }
+
+ public static function concat( /* ... */) {
+ return new \PHPPgAdmin\Decorators\ConcatDecorator(func_get_args());
+ }
+
+ public static function replace($str, $params) {
+ return new \PHPPgAdmin\Decorators\replaceDecorator($str, $params);
+ }
public static function field($fieldName, $default = null) {
return new FieldDecorator($fieldName, $default);
}
diff --git a/src/classes/decorators/FieldDecorator.php b/src/decorators/FieldDecorator.php
index f97cf860..0f9e36c5 100644
--- a/src/classes/decorators/FieldDecorator.php
+++ b/src/decorators/FieldDecorator.php
@@ -11,7 +11,7 @@ class FieldDecorator extends Decorator {
}
function value($fields) {
- return isset($fields[$this->f]) ? value($fields[$this->f], $fields) : (isset($this->d) ? $this->d : null);
+ return isset($fields[$this->f]) ? Decorator::get_sanitized_value($fields[$this->f], $fields) : (isset($this->d) ? $this->d : null);
}
} \ No newline at end of file
diff --git a/src/classes/decorators/IfEmptyDecorator.php b/src/decorators/IfEmptyDecorator.php
index f80754b7..4a690563 100644
--- a/src/classes/decorators/IfEmptyDecorator.php
+++ b/src/decorators/IfEmptyDecorator.php
@@ -12,11 +12,11 @@ class IfEmptyDecorator extends Decorator {
}
function value($fields) {
- $val = value($this->v, $fields);
+ $val = Decorator::get_sanitized_value($this->v, $fields);
if (empty($val)) {
- return value($this->e, $fields);
+ return Decorator::get_sanitized_value($this->e, $fields);
} else {
- return isset($this->f) ? value($this->f, $fields) : $val;
+ return isset($this->f) ? Decorator::get_sanitized_value($this->f, $fields) : $val;
}
}
diff --git a/src/classes/decorators/RedirectUrlDecorator.php b/src/decorators/RedirectUrlDecorator.php
index 8f125f90..144a1bff 100644
--- a/src/classes/decorators/RedirectUrlDecorator.php
+++ b/src/decorators/RedirectUrlDecorator.php
@@ -4,7 +4,7 @@ namespace PHPPgAdmin\Decorators;
class RedirectUrlDecorator extends Decorator {
function __construct($base, $queryVars = null) {
- \PC::debug($base, 'RedirectUrlDecorator');
+ //\PC::debug($base, 'RedirectUrlDecorator');
$this->b = $base;
if ($queryVars !== null) {
@@ -14,19 +14,19 @@ class RedirectUrlDecorator extends Decorator {
}
function value($fields) {
- $url = value($this->b, $fields);
+ $url = Decorator::get_sanitized_value($this->b, $fields);
if ($url === false) {
return '';
}
if (!empty($this->q)) {
- $queryVars = value($this->q, $fields);
+ $queryVars = Decorator::get_sanitized_value($this->q, $fields);
$sep = '?';
foreach ($queryVars as $var => $value) {
- $varname = value_url($var, $fields);
- $varvalue = value_url($value, $fields);
+ $varname = Decorator::value_url($var, $fields);
+ $varvalue = Decorator::value_url($value, $fields);
if ($varname == 'subject') {
$url = '/' . str_replace('.php', '/' . $varvalue, $url);
}
diff --git a/src/classes/decorators/UrlDecorator.php b/src/decorators/UrlDecorator.php
index 95148615..db5f688e 100644
--- a/src/classes/decorators/UrlDecorator.php
+++ b/src/decorators/UrlDecorator.php
@@ -12,18 +12,18 @@ class UrlDecorator extends Decorator {
}
function value($fields) {
- $url = value($this->b, $fields);
+ $url = Decorator::get_sanitized_value($this->b, $fields);
if ($url === false) {
return '';
}
if (!empty($this->q)) {
- $queryVars = value($this->q, $fields);
+ $queryVars = Decorator::get_sanitized_value($this->q, $fields);
$sep = '?';
foreach ($queryVars as $var => $value) {
- $url .= $sep . value_url($var, $fields) . '=' . value_url($value, $fields);
+ $url .= $sep . Decorator::value_url($var, $fields) . '=' . Decorator::value_url($value, $fields);
$sep = '&';
}
}
diff --git a/src/classes/decorators/replaceDecorator.php b/src/decorators/replaceDecorator.php
index 4909faef..9cf11909 100644
--- a/src/classes/decorators/replaceDecorator.php
+++ b/src/decorators/replaceDecorator.php
@@ -10,7 +10,7 @@ class replaceDecorator extends Decorator {
function value($fields) {
$str = $this->s;
foreach ($this->p as $k => $v) {
- $str = str_replace($k, value($v, $fields), $str);
+ $str = str_replace($k, Decorator::get_sanitized_value($v, $fields), $str);
}
return $str;
}
diff --git a/src/errorhandler.inc.php b/src/errorhandler.inc.php
index dee49c70..6e796628 100644
--- a/src/errorhandler.inc.php
+++ b/src/errorhandler.inc.php
@@ -6,10 +6,6 @@
* $Id: errorhandler.inc.php,v 1.20 2005/11/13 08:39:49 chriskl Exp $
*/
-if (!defined('ADODB_ERROR_HANDLER')) {
- define('ADODB_ERROR_HANDLER', 'Error_Handler');
-}
-
/**
* Default Error Handler. This will be called with the following params
*
@@ -25,33 +21,33 @@ function Error_Handler($dbms, $fn, $errno, $errmsg, $p1 = false, $p2 = false) {
global $misc, $appName, $appVersion, $appLangFiles;
switch ($fn) {
- case 'EXECUTE':
- $sql = $p1;
- $inputparams = $p2;
+ case 'EXECUTE':
+ $sql = $p1;
+ $inputparams = $p2;
- $s = "<p><b>{$lang['strsqlerror']}</b><br />" . $misc->printVal($errmsg, 'errormsg') . "</p>
+ $s = "<p><b>{$lang['strsqlerror']}</b><br />" . $misc->printVal($errmsg, 'errormsg') . "</p>
<p><b>{$lang['strinstatement']}</b><br />" . $misc->printVal($sql) . "</p>
";
- echo "<table class=\"error\" cellpadding=\"5\"><tr><td>{$s}</td></tr></table><br />\n";
+ echo "<table class=\"error\" cellpadding=\"5\"><tr><td>{$s}</td></tr></table><br />\n";
- break;
+ break;
- case 'PCONNECT':
- case 'CONNECT':
- $_failed = true;
- global $_reload_browser;
- $_reload_browser = true;
- unset($_SESSION['sharedUsername']);
- unset($_SESSION['sharedPassword']);
- unset($_SESSION['webdbLogin'][$_REQUEST['server']]);
- $msg = $lang['strloginfailed'];
- include './login.php';
- exit;
- break;
- default:
- $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n";
- echo "<table class=\"error\" cellpadding=\"5\"><tr><td>{$s}</td></tr></table><br />\n";
- break;
+ case 'PCONNECT':
+ case 'CONNECT':
+ $_failed = true;
+ global $_reload_browser;
+ $_reload_browser = true;
+ unset($_SESSION['sharedUsername']);
+ unset($_SESSION['sharedPassword']);
+ unset($_SESSION['webdbLogin'][$_REQUEST['server']]);
+ $msg = $lang['strloginfailed'];
+ $login_controller = new \PHPPgAdmin\Controller\LoginController($container);
+ return $login_controller->render();
+ break;
+ default:
+ $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n";
+ echo "<table class=\"error\" cellpadding=\"5\"><tr><td>{$s}</td></tr></table><br />\n";
+ break;
}
/*
* Log connection error somewhere
diff --git a/src/highlight.php b/src/highlight.php
deleted file mode 100644
index 7c5e234a..00000000
--- a/src/highlight.php
+++ /dev/null
@@ -1,1082 +0,0 @@
-<?php
-/* This software is licensed through a BSD-style License.
- * http://www.opensource.org/licenses/bsd-license.php
-
-Copyright (c) 2003, 2004, Jacob D. Cohen
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
-Neither the name of Jacob D. Cohen nor the names of his contributors
-may be used to endorse or promote products derived from this software
-without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- */
-
-function keyword_replace($keywords, $text, $ncs = false) {
- $cm = ($ncs) ? "i" : "";
- foreach ($keywords as $keyword) {
- $search[] = "/(\\b$keyword\\b)/" . $cm;
- $replace[] = '<span class="keyword">\\0</span>';
- }
-
- $search[] = "/(\\bclass\s)/";
- $replace[] = '<span class="keyword">\\0</span>';
-
- return preg_replace($search, $replace, $text);
-}
-
-function preproc_replace($preproc, $text) {
- foreach ($preproc as $proc) {
- $search[] = "/(\\s*#\s*$proc\\b)/";
- $replace[] = '<span class="keyword">\\0</span>';
- }
-
- return preg_replace($search, $replace, $text);
-}
-
-function sch_syntax_helper($text) {
- return $text;
-}
-
-function syntax_highlight_helper($text, $language) {
- $preproc = array();
- $preproc["C++"] = array(
- "if", "ifdef", "ifndef", "elif", "else",
- "endif", "include", "define", "undef", "line",
- "error", "pragma");
- $preproc["C89"] = &$preproc["C++"];
- $preproc["C"] = &$preproc["C89"];
-
- $keywords = array(
- "C++" => array(
- "asm", "auto", "bool", "break", "case",
- "catch", "char", /*class*/"const", "const_cast",
- "continue", "default", "delete", "do", "double",
- "dynamic_cast", "else", "enum", "explicit", "export",
- "extern", "false", "float", "for", "friend",
- "goto", "if", "inline", "int", "long",
- "mutable", "namespace", "new", "operator", "private",
- "protected", "public", "register", "reinterpret_cast", "return",
- "short", "signed", "sizeof", "static", "static_cast",
- "struct", "switch", "template", "this", "throw",
- "true", "try", "typedef", "typeid", "typename",
- "union", "unsigned", "using", "virtual", "void",
- "volatile", "wchar_t", "while"),
-
- "C89" => array(
- "auto", "break", "case", "char", "const",
- "continue", "default", "do", "double", "else",
- "enum", "extern", "float", "for", "goto",
- "if", "int", "long", "register", "return",
- "short", "signed", "sizeof", "static", "struct",
- "switch", "typedef", "union", "unsigned", "void",
- "volatile", "while"),
-
- "C" => array(
- "auto", "break", "case", "char", "const",
- "continue", "default", "do", "double", "else",
- "enum", "extern", "float", "for", "goto",
- "if", "int", "long", "register", "return",
- "short", "signed", "sizeof", "static", "struct",
- "switch", "typedef", "union", "unsigned", "void",
- "volatile", "while", "__restrict", "_Bool"),
-
- "PHP" => array(
- "and", "or", "xor", "__FILE__", "__LINE__",
- "array", "as", "break", "case", "cfunction",
- /*class*/"const", "continue", "declare", "default",
- "die", "do", "echo", "else", "elseif",
- "empty", "enddeclare", "endfor", "endforeach", "endif",
- "endswitch", "endwhile", "eval", "exit", "extends",
- "for", "foreach", "function", "global", "if",
- "include", "include_once", "isset", "list", "new",
- "old_function", "print", "require", "require_once", "return",
- "static", "switch", "unset", "use", "var",
- "while", "__FUNCTION__", "__CLASS__"),
-
- "Perl" => array(
- "-A", "-B", "-C", "-M", "-O",
- "-R", "-S", "-T", "-W", "-X",
- "-b", "-c", "-d", "-e", "-f",
- "-g", "-k", "-l", "-o", "-p",
- "-r", "-s", "-t", "-u", "-w",
- "-x", "-z", "ARGV", "DATA", "ENV",
- "SIG", "STDERR", "STDIN", "STDOUT", "atan2",
- "bind", "binmode", "bless", "caller", "chdir",
- "chmod", "chomp", "chop", "chown", "chr",
- "chroot", "close", "closedir", "cmp", "connect",
- "continue", "cos", "crypt", "dbmclose", "dbmopen",
- "defined", "delete", "die", "do", "dump",
- "each", "else", "elsif", "endgrent", "endhostent",
- "endnetent", "endprotent", "endpwent", "endservent", "eof",
- "eq", "eval", "exec", "exists", "exit",
- "exp", "fcntl", "fileno", "flock", "for",
- "foreach", "fork", "format", "formline", "ge",
- "getc", "getgrent", "getgrid", "getgrnam", "gethostbyaddr",
- "gethostbyname", "gethostent", "getlogin", "getnetbyaddr", "getnetbyname",
- "getnetent", "getpeername", "getpgrp", "getppid", "getpriority",
- "getprotobyname", "getprotobynumber", "getprotoent", "getpwent", "getpwnam",
- "getpwuid", "getservbyname", "getservbyport", "getservent", "getsockname",
- "getsockopt", "glob", "gmtime", "goto", "grep",
- /*gt*/"hex", "if", "import", "index",
- "int", "ioctl", "join", "keys", "kill",
- "last", "lc", "lcfirst", "le", "length",
- "link", "listen", "local", "localtime", "log",
- "lstat", /*lt*/"m", "map", "mkdir",
- "msgctl", "msgget", "msgrcv", "msgsnd", "my",
- "ne", "next", "no", "oct", "open",
- "opendir", "ord", "pack", "package", "pipe",
- "pop", "pos", "print", "printf", "push",
- "q", "qq", "quotemeta", "qw", "qx",
- "rand", "read", "readdir", "readlink", "recv",
- "redo", "ref", "refname", "require", "reset",
- "return", "reverse", "rewinddir", "rindex", "rmdir",
- "s", "scalar", "seek", "seekdir", "select",
- "semctl", "semget", "semop", "send", "setgrent",
- "sethostent", "setnetent", "setpgrp", "setpriority", "setprotoent",
- "setpwent", "setservent", "setsockopt", "shift", "shmctl",
- "shmget", "shmread", "shmwrite", "shutdown", "sin",
- "sleep", "socket", "socketpair", "sort", "splice",
- "split", "sprintf", "sqrt", "srand", "stat",
- "study", "sub", "substr", "symlink", "syscall",
- "sysopen", "sysread", "system", "syswrite", "tell",
- "telldir", "tie", "tied", "time", "times",
- "tr", "truncate", "uc", "ucfirst", "umask",
- "undef", "unless", "unlink", "unpack", "unshift",
- "untie", "until", "use", "utime", "values",
- "vec", "wait", "waitpid", "wantarray", "warn",
- "while", "write", "y", "or", "and",
- "not"),
-
- "Java" => array(
- "abstract", "boolean", "break", "byte", "case",
- "catch", "char", /*class*/"const", "continue",
- "default", "do", "double", "else", "extends",
- "final", "finally", "float", "for", "goto",
- "if", "implements", "import", "instanceof", "int",
- "interface", "long", "native", "new", "package",
- "private", "protected", "public", "return", "short",
- "static", "strictfp", "super", "switch", "synchronized",
- "this", "throw", "throws", "transient", "try",
- "void", "volatile", "while"),
-
- "VB" => array(
- "AddressOf", "Alias", "And", "Any", "As",
- "Binary", "Boolean", "ByRef", "Byte", "ByVal",
- "Call", "Case", "CBool", "CByte", "CCur",
- "CDate", "CDbl", "CInt", "CLng", "Close",
- "Const", "CSng", "CStr", "Currency", "CVar",
- "CVErr", "Date", "Debug", "Declare", "DefBool",
- "DefByte", "DefCur", "DefDate", "DefDbl", "DefInt",
- "DefLng", "DefObj", "DefSng", "DefStr", "DefVar",
- "Dim", "Do", "Double", "Each", "Else",
- "End", "Enum", "Eqv", "Erase", "Error",
- "Event", "Exit", "For", "Friend", "Function",
- "Get", "Get", "Global", "GoSub", "GoTo",
- "If", "Imp", "Implements", "In", "Input",
- "Integer", "Is", "LBound", "Len", "Let",
- "Lib", "Like", "Line", "Lock", "Long",
- "Loop", "LSet", "Mod", "Name", "Next",
- "Not", "Nothing", "Null", "Object", "On",
- "Open", "Option Base 1", "Option Compare Binary",
- "Option Compare Database", "Option Compare Text", "Option Explicit",
- "Option Private Module", "Optional", "Or", "Output",
- "ParamArray", "Preserve", "Print", "Private", "Property",
- "Public", "Put", "RaiseEvent", "Random", "Read",
- "ReDim", "Resume", "Return", "RSet", "Seek",
- "Select", "Set", "Single", "Spc", "Static",
- "Step", "Stop", "String", "Sub", "Tab",
- "Then", "To", "Type", "UBound", "Unlock",
- "Variant", "Wend", "While", "With", "WithEvents",
- "Write", "Xor"),
-
- "C#" => array(
- "abstract", "as", "base", "bool", "break",
- "byte", "case", "catch", "char", "checked",
- /*class*/"const", "continue", "decimal", "default",
- "delegate", "do", "double", "else", "enum",
- "event", "explicit", "extern", "false", "finally",
- "fixed", "float", "for", "foreach", "goto",
- "if", "implicit", "in", "int", "interface",
- "internal", "is", "lock", "long", "namespace",
- "new", "null", "object", "operator", "out",
- "override", "params", "private", "protected", "public",
- "readonly", "ref", "return", "sbyte", "sealed",
- "short", "sizeof", "stackalloc", "static", "string",
- "struct", "switch", "this", "throw", "true",
- "try", "typeof", "uint", "ulong", "unchecked",
- "unsafe", "ushort", "using", "virtual", "volatile",
- "void", "while"),
-
- "Ruby" => array(
- "alias", "and", "begin", "break", "case",
- /*class*/"def", "defined", "do", "else",
- "elsif", "end", "ensure", "false", "for",
- "if", "in", "module", "next", "module",
- "next", "nil", "not", "or", "redo",
- "rescue", "retry", "return", "self", "super",
- "then", "true", "undef", "unless", "until",
- "when", "while", "yield"),
-
- "Python" => array(
- "and", "assert", "break", /*"class",*/"continue",
- "def", "del", "elif", "else", "except",
- "exec", "finally", "for", "from", "global",
- "if", "import", "in", "is", "lambda",
- "not", "or", "pass", "print", "raise",
- "return", "try", "while", "yield"),
-
- "Pascal" => array(
- "Absolute", "Abstract", "All", "And", "And_then",
- "Array", "Asm", "Begin", "Bindable", "Case",
- /*"Class",*/"Const", "Constructor", "Destructor", "Div",
- "Do", "Downto", "Else", "End", "Export",
- "File", "For", "Function", "Goto", "If",
- "Import", "Implementation", "Inherited", "In", "Inline",
- "Interface", "Is", "Label", "Mod", "Module",
- "Nil", "Not", "Object", "Of", "Only",
- "Operator", "Or", "Or_else", "Otherwise", "Packed",
- "Pow", "Procedure", "Program", "Property", "Protected",
- "Qualified", "Record", "Repeat", "Restricted", "Set",
- "Shl", "Shr", "Then", "To", "Type",
- "Unit", "Until", "Uses", "Value", "Var",
- "View", "Virtual", "While", "With", "Xor"),
-
- "mIRC" => array(
- ),
-
- "PL/I" => array(
- "A", "ABS", "ACOS", "%ACTIVATE", "ACTUALCOUNT",
- "ADD", "ADDR", "ADDREL", "ALIGNED", "ALLOCATE",
- "ALLOC", "ALLOCATION", "ALLOCN", "ANY", "ANYCONDITION",
- "APPEND", "AREA", "ASIN", "ATAN", "ATAND",
- "ATANH", "AUTOMATIC", "AUTO", "B", "B1",
- "B2", "B3", "B4", "BACKUP_DATE", "BASED",
- "BATCH", "BEGIN", "BINARY", "BIN", "BIT",
- "BLOCK_BOUNDARY_FORMAT", "BLOCK_IO", "BLOCK_SIZE", "BOOL",
- "BUCKET_SIZE", "BUILTIN", "BY", "BYTE", "BYTESIZE",
- "CALL", "CANCEL_CONTROL_O", "CARRIAGE_RETURN_FORMAT",
- "CEIL", "CHAR", "CHARACTER", "CLOSE", "COLLATE", "COLUMN",
- "CONDITION", "CONTIGUOUS", "CONTIGUOUS_BEST_TRY", "CONTROLLED",
- "CONVERSION", "COPY", "COS", "COSD", "COSH",
- "CREATION_DATE", "CURRENT_POSITION", "DATE",
- "DATETIME", "%DEACTIVATE", "DECIMAL", "DEC", "%DECLARE",
- "%DCL", "DECLARE", "DCL", "DECODE", "DEFAULT_FILE_NAME",
- "DEFERRED_WRITE", "DEFINED", "DEF", "DELETE",
- "DESCRIPTOR", "%DICTIONARY", "DIMENSION", "DIM", "DIRECT",
- "DISPLAY", "DIVIDE", "%DO", "DO", "E",
- "EDIT", "%ELSE", "ELSE", "EMPTY", "ENCODE",
- "%END", "END", "ENDFILE", "ENDPAGE", "ENTRY",
- "ENVIRONMENT", "ENV", "%ERROR", "ERROR", "EVERY",
- "EXP", "EXPIRATION_DATE", "EXTEND", "EXTENSION_SIZE",
- "EXTERNAL", "EXT", "F", "FAST_DELETE", "%FATAL",
- "FILE", "FILE_ID", "FILE_ID_TO", "FILE_SIZE",
- "FINISH", "FIXED", "FIXEDOVERFLOW", "FOFL",
- "FIXED_CONTROL_FROM", "FIXED_CONTROL_SIZE", "FIXED_CONTROL_SIZE_TO",
- "FIXED_CONTROL_TO", "FIXED_LENGTH_RECORDS", "FLOAT",
- "FLOOR", "FLUSH", "FORMAT", "FREE", "FROM",
- "GET", "GLOBALDEF", "GLOBALREF", "%GOTO",
- "GOTO", "GO", "TO", "GROUP_PROTETION", "HBOUND",
- "HIGH", "INDENT", "%IF", "IF", "IGNORE_LINE_MARKS",
- "IN", "%INCLUDE", "INDEX", "INDEXED", "INDEX_NUMBER",
- "%INFORM", "INFORM", "INITIAL", "INIT", "INITIAL_FILL",
- "INPUT", "INT", "INTERNAL", "INTO", "KEY",
- "KEYED", "KEYFROM", "KEYTO", "LABEL", "LBOUND",
- "LEAVE", "LENGTH", "LIKE", "LINE", "LINENO",
- "LINESIZE", "%LIST", "LIST", "LOCK_ON_READ", "LOCK_ON_WRITE",
- "LOG", "LOG10", "LOG2", "LOW", "LTRIM",
- "MAIN", "MANUAL_UNLOCKING", "MATCH_GREATER",
- "MATCH_GREATER_EQUAL", "MATCH_NEXT", "MATCH_NEXT_EQUAL",
- "MAX", "MAXIMUM_RECORD_NUMBER", "MAXIMUM_RECORD_SIZE",
- "MAXLENGTH", "MEMBER", "MIN", "MOD", "MULTIBLOCK_COUNT",
- "MULTIBUFFER_COUNT", "MULTIPLY", "NEXT_VOLUME", "%NOLIST",
- "NOLOCK", "NONEXISTENT_RECORD", "NONRECURSIVE", "NONVARYING",
- "NONVAR", "NORESCAN", "NO_ECHO", "NO_FILTER", "NO_SHARE",
- "NULL", "OFFSET", "ON", "ONARGSLIST", "ONCHAR",
- "ONCODE", "ONFILE", "ONKEY", "ONSOURCE", "OPEN",
- "OPTIONAL", "OPTIONS", "OTHERWISE", "OTHER", "OUTPUT",
- "OVERFLOW", "OFL", "OWNER_GROUP", "OWNER_ID",
- "OWNER_MEMBER", "OWNER_PROTECTION", "P", "%PAGE",
- "PAGE", "PAGENO", "PAGESIZE", "PARAMETER", "PARM",
- "PICTURE", "PIC", "POINTER", "PTR", "POSINT",
- "POSITION", "POS", "PRECISION", "PREC", "PRESENT",
- "PRINT", "PRINTER_FORMAT", "%PROCEDURE", "%PROC",
- "PROCEDURE", "PROC", "PROD", "PROMPT", "PURGE_TYPE_AHEAD",
- "PUT", "R", "RANK", "READ", "READONLY",
- "READ_AHEAD", "READ_CHECK", "READ_REGARDLESS", "RECORD",
- "RECORD_ID", "RECORD_ID_ACCESS", "RECORD_ID_TO", "RECURSIVE",
- "REFER", "REFERENCE", "RELEASE", "REPEAT", "%REPLACE",
- "RESCAN", "RESIGNAL", "RETRIEVAL_POINTERS", "%RETURN",
- "RETURN", "RETURNS", "REVERSE", "REVERT", "REVISION_DATE",
- "REWIND", "REWIND_ON_CLOSE", "REWIND_ON_OPEN",
- "REWRITE", "ROUND", "RTRIM", "%SBTTL", "SCALARVARYING",
- "SEARCH", "SELECT", "SEQUENTIAL", "SEQL",
- "SET", "SHARED_READ", "SHARED_WRITE", "SIGN",
- "SIGNAL", "SIN", "SIND", "SINH", "SIZE",
- "SKIP", "SNAP", "SOME", "SPACEBLOCK", "SPOOL",
- "SQRT", "STATEMENT", "STATIC", "STOP", "STORAGE",
- "STREAM", "STRING", "STRINGRANGE", "STRG",
- "STRUCTURE", "SUBSCRIPTRANGE", "SUBRG", "SUBSTR",
- "SUBTRACT", "SUM", "SUPERCEDE", "SYSIN", "SYSPRINT",
- "SYSTEM", "SYSTEM_PROTECTION", "TAB", "TAN",
- "TAND", "TANH", "TEMPORARY", "%THEN", "THEN",
- "TIME", "TIMEOUT_PERIOD", "%TITLE", "TITLE",
- "TO", "TRANSLATE", "TRIM", "TRUNC", "TRUNCATE",
- "UNALIGNED", "UNAL", "UNDEFINED", "UNDF", "UNDERFLOW",
- "UFL", "UNION", "UNSPEC", "UNTIL", "UPDATE",
- "USER_OPEN", "VALID", "VALUE", "VAL", "VARIABLE",
- "VARIANT", "VARYING", "VAR", "VAXCONDITION", "VERIFY",
- "WAIT_FOR_RECORD", "%WARN", "WARN", "WHEN",
- "WHILE", "WORLD_PROTECTION", "WRITE", "WRITE_BEHIND",
- "WRITE_CHECK", "X", "ZERODIVIDE"),
-
- "SQL" => array(
- "abort", "abs", "absolute", "access",
- "action", "ada", "add", "admin",
- "after", "aggregate", "alias", "all",
- "allocate", "alter", "analyse", "analyze",
- "and", "any", "are", "array",
- "as", "asc", "asensitive", "assertion",
- "assignment", "asymmetric", "at", "atomic",
- "authorization", "avg", "backward", "before",
- "begin", "between", "bigint", "binary",
- "bit", "bitvar", "bit_length", "blob",
- "boolean", "both", "breadth", "by",
- "c", "cache", "call", "called",
- "cardinality", "cascade", "cascaded", "case",
- "cast", "catalog", "catalog_name", "chain",
- "char", "character", "characteristics", "character_length",
- "character_set_catalog", "character_set_name", "character_set_schema", "char_length",
- "check", "checked", "checkpoint", /* "class", */
- "class_origin", "clob", "close", "cluster",
- "coalesce", "cobol", "collate", "collation",
- "collation_catalog", "collation_name", "collation_schema", "column",
- "column_name", "command_function", "command_function_code", "comment",
- "commit", "committed", "completion", "condition_number",
- "connect", "connection", "connection_name", "constraint",
- "constraints", "constraint_catalog", "constraint_name", "constraint_schema",
- "constructor", "contains", "continue", "conversion",
- "convert", "copy", "corresponding", "count",
- "create", "createdb", "createuser", "cross",
- "cube", "current", "current_date", "current_path",
- "current_role", "current_time", "current_timestamp", "current_user",
- "cursor", "cursor_name", "cycle", "data",
- "database", "date", "datetime_interval_code", "datetime_interval_precision",
- "day", "deallocate", "dec", "decimal",
- "declare", "default", "defaults", "deferrable",
- "deferred", "defined", "definer", "delete",
- "delimiter", "delimiters", "depth", "deref",
- "desc", "describe", "descriptor", "destroy",
- "destructor", "deterministic", "diagnostics", "dictionary",
- "disconnect", "dispatch", "distinct", "do",
- "domain", "double", "drop", "dynamic",
- "dynamic_function", "dynamic_function_code", "each", "else",
- "encoding", "encrypted", "end", "end-exec",
- "equals", "escape", "every", "except",
- "exception", "excluding", "exclusive", "exec",
- "execute", "existing", "exists", "explain",
- "external", "extract", "false", "fetch",
- "final", "first", "float", "for",
- "force", "foreign", "fortran", "forward",
- "found", "free", "freeze", "from",
- "full", "function", "g", "general",
- "generated", "get", "global", "go",
- "goto", "grant", "granted", "group",
- "grouping", "handler", "having", "hierarchy",
- "hold", "host", "hour", "identity",
- "ignore", "ilike", "immediate", "immutable",
- "implementation", "implicit", "in", "including",
- "increment", "index", "indicator", "infix",
- "inherits", "initialize", "initially", "inner",
- "inout", "input", "insensitive", "insert",
- "instance", "instantiable", "instead", "int",
- "integer", "intersect", "interval", "into",
- "invoker", "is", "isnull", "isolation",
- "iterate", "join", "k", "key",
- "key_member", "key_type", "lancompiler", "language",
- "large", "last", "lateral", "leading",
- "left", "length", "less", "level",
- "like", "limit", "listen", "load",
- "local", "localtime", "localtimestamp", "location",
- "locator", "lock", "lower", "m",
- "map", "match", "max", "maxvalue",
- "message_length", "message_octet_length", "message_text", "method",
- "min", "minute", "minvalue", "mod",
- "mode", "modifies", "modify", "module",
- "month", "more", "move", "mumps",
- "name", "names", "national", "natural",
- "nchar", "nclob", "new", "next",
- "no", "nocreatedb", "nocreateuser", "none",
- "not", "nothing", "notify", "notnull",
- "null", "nullable", "nullif", "number",
- "numeric", "object", "octet_length", "of",
- "off", "offset", "oids", "old",
- "on", "only", "open", "operation",
- "operator", "option", "options", "or",
- "order", "ordinality", "out", "outer",
- "output", "overlaps", "overlay", "overriding",
- "owner", "pad", "parameter", "parameters",
- "parameter_mode", "parameter_name", "parameter_ordinal_position", "parameter_specific_catalog",
- "parameter_specific_name", "parameter_specific_schema", "partial", "pascal",
- "password", "path", "pendant", "placing",
- "pli", "position", "postfix", "precision",
- "prefix", "preorder", "prepare", "preserve",
- "primary", "prior", "privileges", "procedural",
- "procedure", "public", "read", "reads",
- "real", "recheck", "recursive", "ref",
- "references", "referencing", "reindex", "relative",
- "rename", "repeatable", "replace", "reset",
- "restart", "restrict", "result", "return",
- "returned_length", "returned_octet_length", "returned_sqlstate", "returns",
- "revoke", "right", "role", "rollback",
- "rollup", "routine", "routine_catalog", "routine_name",
- "routine_schema", "row", "rows", "row_count",
- "rule", "savepoint", "scale", "schema",
- "schema_name", "scope", "scroll", "search",
- "second", "section", "security", "select",
- "self", "sensitive", "sequence", "serializable",
- "server_name", "session", "session_user", "set",
- "setof", "sets", "share", "show",
- "similar", "simple", "size", "smallint",
- "some", "source", "space", "specific",
- "specifictype", "specific_name", "sql", "sqlcode",
- "sqlerror", "sqlexception", "sqlstate", "sqlwarning",
- "stable", "start", "state", "statement",
- "static", "statistics", "stdin", "stdout",
- "storage", "strict", "structure", "style",
- "subclass_origin", "sublist", "substring", "sum",
- "symmetric", "sysid", "system", "system_user",
- "table", "table_name", "temp", "template",
- "temporary", "terminate", "text", "than", "then",
- "time", "timestamp", "timezone_hour", "timezone_minute",
- "to", "toast", "trailing", "transaction",
- "transactions_committed", "transactions_rolled_back", "transaction_active", "transform",
- "transforms", "translate", "translation", "treat",
- "trigger", "trigger_catalog", "trigger_name", "trigger_schema",
- "trim", "true", "truncate", "trusted",
- "type", "uncommitted", "under", "unencrypted",
- "union", "unique", "unknown", "unlisten",
- "unnamed", "unnest", "until", "update",
- "upper", "usage", "user", "user_defined_type_catalog",
- "user_defined_type_name", "user_defined_type_schema", "using", "vacuum",
- "valid", "validator", "value", "values",
- "varchar", "variable", "varying", "verbose",
- "version", "view", "volatile", "when",
- "whenever", "where", "with", "without",
- "work", "write", "year", "zone"),
-
- );
-
- $case_insensitive = array(
- "VB" => true,
- "Pascal" => true,
- "PL/I" => true,
- "SQL" => true,
- );
- $ncs = false;
- if (array_key_exists($language, $case_insensitive)) {
- $ncs = true;
- }
-
- $text = (array_key_exists($language, $preproc)) ?
- preproc_replace($preproc[$language], $text) :
- $text;
- $text = (array_key_exists($language, $keywords)) ?
- keyword_replace($keywords[$language], $text, $ncs) :
- $text;
-
- return $text;
-}
-
-function rtrim1($span, $lang, $ch) {
- return syntax_highlight_helper(substr($span, 0, -1), $lang);
-}
-
-function rtrim1_htmlesc($span, $lang, $ch) {
- return htmlspecialchars(substr($span, 0, -1));
-}
-
-function sch_rtrim1($span, $lang, $ch) {
- return sch_syntax_helper(substr($span, 0, -1));
-}
-
-function rtrim2($span, $lang, $ch) {
- return substr($span, 0, -2);
-}
-
-function syn_proc($span, $lang, $ch) {
- return syntax_highlight_helper($span, $lang);
-}
-
-function dash_putback($span, $lang, $ch) {
- return syntax_highlight_helper('-' . $span, $lang);
-}
-
-function slash_putback($span, $lang, $ch) {
- return syntax_highlight_helper('/' . $span, $lang);
-}
-
-function slash_putback_rtrim1($span, $lang, $ch) {
- return rtrim1('/' . $span, $lang, $ch);
-}
-
-function lparen_putback($span, $lang, $ch) {
- return syntax_highlight_helper('(' . $span, $lang);
-}
-
-function lparen_putback_rtrim1($span, $lang, $ch) {
- return rtrim1('(' . $span, $lang, $ch);
-}
-
-function prepend_xml_opentag($span, $lang, $ch) {
- return '<span class="xml_tag">&lt;' . $span;
-}
-
-function proc_void($span, $lang, $ch) {
- return $span;
-}
-
-/**
- * Syntax highlight function
- * Does the bulk of the syntax highlighting by lexing the input
- * string, then calling the helper function to highlight keywords.
- */
-function syntax_highlight($text, $language) {
- if ($language == "Plain Text") {
- return $text;
- }
-
- define("normal_text", 1, true);
- define("dq_literal", 2, true);
- define("dq_escape", 3, true);
- define("sq_literal", 4, true);
- define("sq_escape", 5, true);
- define("slash_begin", 6, true);
- define("star_comment", 7, true);
- define("star_end", 8, true);
- define("line_comment", 9, true);
- define("html_entity", 10, true);
- define("lc_escape", 11, true);
- define("block_comment", 12, true);
- define("paren_begin", 13, true);
- define("dash_begin", 14, true);
- define("bt_literal", 15, true);
- define("bt_escape", 16, true);
- define("xml_tag_begin", 17, true);
- define("xml_tag", 18, true);
- define("xml_pi", 19, true);
- define("sch_normal", 20, true);
- define("sch_stresc", 21, true);
- define("sch_idexpr", 22, true);
- define("sch_numlit", 23, true);
- define("sch_chrlit", 24, true);
- define("sch_strlit", 25, true);
-
- $initial_state["Scheme"] = sch_normal;
-
- $sch[sch_normal][0] = sch_normal;
- $sch[sch_normal]['"'] = sch_strlit;
- $sch[sch_normal]["#"] = sch_chrlit;
- $sch[sch_normal]["0"] = sch_numlit;
- $sch[sch_normal]["1"] = sch_numlit;
- $sch[sch_normal]["2"] = sch_numlit;
- $sch[sch_normal]["3"] = sch_numlit;
- $sch[sch_normal]["4"] = sch_numlit;
- $sch[sch_normal]["5"] = sch_numlit;
- $sch[sch_normal]["6"] = sch_numlit;
- $sch[sch_normal]["7"] = sch_numlit;
- $sch[sch_normal]["8"] = sch_numlit;
- $sch[sch_normal]["9"] = sch_numlit;
-
- $sch[sch_strlit]['"'] = sch_normal;
- $sch[sch_strlit]["\n"] = sch_normal;
- $sch[sch_strlit]["\\"] = sch_stresc;
- $sch[sch_strlit][0] = sch_strlit;
-
- $sch[sch_chrlit][" "] = sch_normal;
- $sch[sch_chrlit]["\t"] = sch_normal;
- $sch[sch_chrlit]["\n"] = sch_normal;
- $sch[sch_chrlit]["\r"] = sch_normal;
- $sch[sch_chrlit][0] = sch_chrlit;
-
- $sch[sch_numlit][" "] = sch_normal;
- $sch[sch_numlit]["\t"] = sch_normal;
- $sch[sch_numlit]["\n"] = sch_normal;
- $sch[sch_numlit]["\r"] = sch_normal;
- $sch[sch_numlit][0] = sch_numlit;
-
- //
- // State transitions for C
- //
- $c89[normal_text]["\""] = dq_literal;
- $c89[normal_text]["'"] = sq_literal;
- $c89[normal_text]["/"] = slash_begin;
- $c89[normal_text][0] = normal_text;
-
- $c89[dq_literal]["\""] = normal_text;
- $c89[dq_literal]["\n"] = normal_text;
- $c89[dq_literal]["\\"] = dq_escape;
- $c89[dq_literal][0] = dq_literal;
-
- $c89[dq_escape][0] = dq_literal;
-
- $c89[sq_literal]["'"] = normal_text;
- $c89[sq_literal]["\n"] = normal_text;
- $c89[sq_literal]["\\"] = sq_escape;
- $c89[sq_literal][0] = sq_literal;
-
- $c89[sq_escape][0] = sq_literal;
-
- $c89[slash_begin]["*"] = star_comment;
- $c89[slash_begin][0] = normal_text;
-
- $c89[star_comment]["*"] = star_end;
- $c89[star_comment][0] = star_comment;
-
- $c89[star_end]["/"] = normal_text;
- $c89[star_end]["*"] = star_end;
- $c89[star_end][0] = star_comment;
-
- //
- // State transitions for C++
- // Inherit transitions from C, and add line comment support
- //
- $cpp = $c89;
- $cpp[slash_begin]["/"] = line_comment;
- $cpp[line_comment]["\n"] = normal_text;
- $cpp[line_comment]["\\"] = lc_escape;
- $cpp[line_comment][0] = line_comment;
-
- $cpp[lc_escape]["\r"] = lc_escape;
- $cpp[lc_escape][0] = line_comment;
-
- //
- // State transitions for C99.
- // C99 supports line comments like C++
- //
- $c99 = $cpp;
-
- // State transitions for PL/I
- // Kinda like C
- $pli = $c89;
-
- //
- // State transitions for PHP
- // Inherit transitions from C++, and add perl-style line comment support
- $php = $cpp;
- $php[normal_text]["#"] = line_comment;
- $php[sq_literal]["\n"] = sq_literal;
- $php[dq_literal]["\n"] = dq_literal;
-
- //
- // State transitions for Perl
- $perl[normal_text]["#"] = line_comment;
- $perl[normal_text]["\""] = dq_literal;
- $perl[normal_text]["'"] = sq_literal;
- $perl[normal_text][0] = normal_text;
-
- $perl[dq_literal]["\""] = normal_text;
- $perl[dq_literal]["\\"] = dq_escape;
- $perl[dq_literal][0] = dq_literal;
-
- $perl[dq_escape][0] = dq_literal;
-
- $perl[sq_literal]["'"] = normal_text;
- $perl[sq_literal]["\\"] = sq_escape;
- $perl[sq_literal][0] = sq_literal;
-
- $perl[sq_escape][0] = sq_literal;
-
- $perl[line_comment]["\n"] = normal_text;
- $perl[line_comment][0] = line_comment;
-
- $mirc[normal_text]["\""] = dq_literal;
- $mirc[normal_text][";"] = line_comment;
- $mirc[normal_text][0] = normal_text;
-
- $mirc[dq_literal]["\""] = normal_text;
- $mirc[dq_literal]["\\"] = dq_escape;
- $mirc[dq_literal][0] = dq_literal;
-
- $mirc[dq_escape][0] = dq_literal;
-
- $mirc[line_comment]["\n"] = normal_text;
- $mirc[line_comment][0] = line_comment;
-
- $ruby = $perl;
-
- $python = $perl;
-
- $java = $cpp;
-
- $vb = $perl;
- $vb[normal_text]["#"] = normal_text;
- $vb[normal_text]["'"] = line_comment;
-
- $cs = $java;
-
- $pascal = $c89;
- $pascal[normal_text]["("] = paren_begin;
- $pascal[normal_text]["/"] = slash_begin;
- $pascal[normal_text]["{"] = block_comment;
-
- $pascal[paren_begin]["*"] = star_comment;
- $pascal[paren_begin]["'"] = sq_literal;
- $pascal[paren_begin]['"'] = dq_literal;
- $pascal[paren_begin][0] = normal_text;
-
- $pascal[slash_begin]["'"] = sq_literal;
- $pascal[slash_begin]['"'] = dq_literal;
- $pascal[slash_begin]['/'] = line_comment;
- $pascal[slash_begin][0] = normal_text;
-
- $pascal[star_comment]["*"] = star_end;
- $pascal[star_comment][0] = star_comment;
-
- $pascal[block_comment]["}"] = normal_text;
- $pascal[block_comment][0] = block_comment;
-
- $pascal[line_comment]["\n"] = normal_text;
- $pascal[line_comment][0] = line_comment;
-
- $pascal[star_end][")"] = normal_text;
- $pascal[star_end]["*"] = star_end;
- $pascal[star_end][0] = star_comment;
-
- $sql[normal_text]['"'] = dq_literal;
- $sql[normal_text]["'"] = sq_literal;
- $sql[normal_text]['`'] = bt_literal;
- $sql[normal_text]['-'] = dash_begin;
- $sql[normal_text][0] = normal_text;
-
- $sql[dq_literal]['"'] = normal_text;
- $sql[dq_literal]['\\'] = dq_escape;
- $sql[dq_literal][0] = dq_literal;
-
- $sql[sq_literal]["'"] = normal_text;
- $sql[sq_literal]['\\'] = sq_escape;
- $sql[sq_literal][0] = sq_literal;
-
- $sql[bt_literal]['`'] = normal_text;
- $sql[bt_literal]['\\'] = bt_escape;
- $sql[bt_literal][0] = bt_literal;
-
- $sql[dq_escape][0] = dq_literal;
- $sql[sq_escape][0] = sq_literal;
- $sql[bt_escape][0] = bt_literal;
-
- $sql[dash_begin]["-"] = line_comment;
- $sql[dash_begin][0] = normal_text;
-
- $sql[line_comment]["\n"] = normal_text;
- $sql[line_comment]["\\"] = lc_escape;
- $sql[line_comment][0] = line_comment;
-
- $sql[lc_escape]["\r"] = lc_escape;
- $sql[lc_escape][0] = line_comment;
-
- $xml[normal_text]["<"] = xml_tag_begin;
- $xml[normal_text]["&"] = html_entity;
- $xml[normal_text][0] = normal_text;
- $xml[html_entity][";"] = normal_text;
- $xml[html_entity]["<"] = xml_tag_begin;
- $xml[html_entity][0] = html_entity;
- $xml[xml_tag_begin]["?"] = xml_pi;
- $xml[xml_tag_begin]["!"] = line_comment;
- $xml[xml_tag_begin][0] = xml_tag;
- $xml[xml_tag][">"] = normal_text;
- $xml[xml_tag]["\""] = dq_literal;
- $xml[xml_tag]["'"] = sq_literal;
- $xml[xml_tag][0] = xml_tag;
- $xml[xml_pi][">"] = normal_text;
- $xml[xml_pi][0] = xml_tag;
- $xml[line_comment][">"] = normal_text;
- $xml[line_comment][0] = line_comment;
- $xml[dq_literal]["\""] = xml_tag;
- $xml[dq_literal]["&"] = dq_escape;
- $xml[dq_literal][0] = dq_literal;
- $xml[sq_literal]["'"] = xml_tag;
- $xml[sq_literal]["&"] = sq_escape;
- $xml[sq_literal][0] = sq_literal;
- $xml[dq_escape][";"] = dq_literal;
- $xml[dq_escape][0] = dq_escape;
-
- //
- // Main state transition table
- //
- $states = array(
- "C89" => $c89,
- "C" => $c99,
- "C++" => $cpp,
- "PHP" => $php,
- "Perl" => $perl,
- "Java" => $java,
- "VB" => $vb,
- "C#" => $cs,
- "Ruby" => $ruby,
- "Python" => $python,
- "Pascal" => $pascal,
- "mIRC" => $mirc,
- "PL/I" => $pli,
- "SQL" => $sql,
- "XML" => $xml,
- "Scheme" => $sch,
- );
-
- //
- // Process functions
- //
- $process["C89"][normal_text][sq_literal] = "rtrim1";
- $process["C89"][normal_text][dq_literal] = "rtrim1";
- $process["C89"][normal_text][slash_begin] = "rtrim1";
- $process["C89"][normal_text][0] = "syn_proc";
-
- $process["C89"][slash_begin][star_comment] = "rtrim1";
- $process["C89"][slash_begin][0] = "slash_putback";
-
- $process["Scheme"][sch_normal][sch_strlit] = "sch_rtrim1";
- $process["Scheme"][sch_normal][sch_chrlit] = "sch_rtrim1";
- $process["Scheme"][sch_normal][sch_numlit] = "sch_rtrim1";
-
- $process["SQL"][normal_text][sq_literal] = "rtrim1";
- $process["SQL"][normal_text][dq_literal] = "rtrim1";
- $process["SQL"][normal_text][bt_literal] = "rtrim1";
- $process["SQL"][normal_text][dash_begin] = "rtrim1";
- $process["SQL"][normal_text][0] = "syn_proc";
-
- $process["SQL"][dash_begin][line_comment] = "rtrim1";
- $process["SQL"][dash_begin][0] = "dash_putback";
-
- $process["PL/I"] = $process["C89"];
-
- $process["C++"] = $process["C89"];
- $process["C++"][slash_begin][line_comment] = "rtrim1";
-
- $process["C"] = $process["C++"];
-
- $process["PHP"] = $process["C++"];
- $process["PHP"][normal_text][line_comment] = "rtrim1";
-
- $process["Perl"][normal_text][sq_literal] = "rtrim1";
- $process["Perl"][normal_text][dq_literal] = "rtrim1";
- $process["Perl"][normal_text][line_comment] = "rtrim1";
- $process["Perl"][normal_text][0] = "syn_proc";
-
- $process["Ruby"] = $process["Perl"];
- $process["Python"] = $process["Perl"];
-
- $process["mIRC"][normal_text][dq_literal] = "rtrim1";
- $process["mIRC"][normal_text][line_comment] = "rtrim1";
- $process["mIRC"][normal_text][0] = "syn_proc";
-
- $process["VB"] = $process["Perl"];
-
- $process["Java"] = $process["C++"];
-
- $process["C#"] = $process["Java"];
-
- $process["Pascal"] = $process["C++"];
- $process["Pascal"][normal_text][line_comment] = "rtrim1";
- $process["Pascal"][normal_text][block_comment] = "rtrim1";
- $process["Pascal"][normal_text][paren_begin] = "rtrim1";
- $process["Pascal"][slash_begin][sq_literal] = "slash_putback_rtrim1";
- $process["Pascal"][slash_begin][dq_literal] = "slash_putback_rtrim1";
- $process["Pascal"][slash_begin][0] = "slash_putback";
- $process["Pascal"][paren_begin][sq_literal] = "lparen_putback_rtrim1";
- $process["Pascal"][paren_begin][dq_literal] = "lparen_putback_rtrim1";
- $process["Pascal"][paren_begin][star_comment] = "rtrim1";
- $process["Pascal"][paren_begin][0] = "lparen_putback";
-
- $process["XML"][normal_text][xml_tag_begin] = "rtrim1";
- $process["XML"][normal_text][html_entity] = "rtrim1";
- $process["XML"][html_entity][xml_tag_begin] = "rtrim1";
- $process["XML"][html_entity][0] = "proc_void";
- $process["XML"][xml_tag_begin][xml_tag] = "prepend_xml_opentag";
- $process["XML"][xml_tag_begin][xml_pi] = "rtrim1";
- $process["XML"][xml_tag_begin][line_comment] = "rtrim1";
- $process["XML"][line_comment][normal_text] = "rtrim1_htmlesc";
- $process["XML"][xml_tag][normal_text] = "rtrim1";
- $process["XML"][xml_tag][dq_literal] = "rtrim1";
- $process["XML"][dq_literal][xml_tag] = "rtrim1";
- $process["XML"][dq_literal][dq_escape] = "rtrim1";
-
- $process_end["C89"] = "syntax_highlight_helper";
- $process_end["C++"] = $process_end["C89"];
- $process_end["C"] = $process_end["C89"];
- $process_end["PHP"] = $process_end["C89"];
- $process_end["Perl"] = $process_end["C89"];
- $process_end["Java"] = $process_end["C89"];
- $process_end["VB"] = $process_end["C89"];
- $process_end["C#"] = $process_end["C89"];
- $process_end["Ruby"] = $process_end["C89"];
- $process_end["Python"] = $process_end["C89"];
- $process_end["Pascal"] = $process_end["C89"];
- $process_end["mIRC"] = $process_end["C89"];
- $process_end["PL/I"] = $process_end["C89"];
- $process_end["SQL"] = $process_end["C89"];
- $process_end["Scheme"] = "sch_syntax_helper";
-
- $edges["C89"][normal_text . "," . dq_literal] = '<span class="literal">"';
- $edges["C89"][normal_text . "," . sq_literal] = '<span class="literal">\'';
- $edges["C89"][slash_begin . "," . star_comment] = '<span class="comment">/*';
- $edges["C89"][dq_literal . "," . normal_text] = '</span>';
- $edges["C89"][sq_literal . "," . normal_text] = '</span>';
- $edges["C89"][star_end . "," . normal_text] = '</span>';
-
- $edges["Scheme"][sch_normal . "," . sch_strlit] = '<span class="sch_str">"';
- $edges["Scheme"][sch_normal . "," . sch_numlit] = '<span class="sch_num">';
- $edges["Scheme"][sch_normal . "," . sch_chrlit] = '<span class="sch_chr">#';
- $edges["Scheme"][sch_strlit . "," . sch_normal] = '</span>';
- $edges["Scheme"][sch_numlit . "," . sch_normal] = '</span>';
- $edges["Scheme"][sch_chrlit . "," . sch_normal] = '</span>';
-
- $edges["SQL"][normal_text . "," . dq_literal] = '<span class="literal">"';
- $edges["SQL"][normal_text . "," . sq_literal] = '<span class="literal">\'';
- $edges["SQL"][dash_begin . "," . line_comment] = '<span class="comment">--';
- $edges["SQL"][normal_text . "," . bt_literal] = '`';
- $edges["SQL"][dq_literal . "," . normal_text] = '</span>';
- $edges["SQL"][sq_literal . "," . normal_text] = '</span>';
- $edges["SQL"][line_comment . "," . normal_text] = '</span>';
-
- $edges["PL/I"] = $edges["C89"];
-
- $edges["C++"] = $edges["C89"];
- $edges["C++"][slash_begin . "," . line_comment] = '<span class="comment">//';
- $edges["C++"][line_comment . "," . normal_text] = '</span>';
-
- $edges["C"] = $edges["C++"];
-
- $edges["PHP"] = $edges["C++"];
- $edges["PHP"][normal_text . "," . line_comment] = '<span class="comment">#';
-
- $edges["Perl"][normal_text . "," . dq_literal] = '<span class="literal">"';
- $edges["Perl"][normal_text . "," . sq_literal] = '<span class="literal">\'';
- $edges["Perl"][dq_literal . "," . normal_text] = '</span>';
- $edges["Perl"][sq_literal . "," . normal_text] = '</span>';
- $edges["Perl"][normal_text . "," . line_comment] = '<span class="comment">#';
- $edges["Perl"][line_comment . "," . normal_text] = '</span>';
-
- $edges["Ruby"] = $edges["Perl"];
-
- $edges["Python"] = $edges["Perl"];
-
- $edges["mIRC"][normal_text . "," . dq_literal] = '<span class="literal">"';
- $edges["mIRC"][normal_text . "," . line_comment] = '<span class="comment">;';
- $edges["mIRC"][dq_literal . "," . normal_text] = '</span>';
- $edges["mIRC"][line_comment . "," . normal_text] = '</span>';
-
- $edges["VB"] = $edges["Perl"];
- $edges["VB"][normal_text . "," . line_comment] = '<span class="comment">\'';
-
- $edges["Java"] = $edges["C++"];
-
- $edges["C#"] = $edges["Java"];
-
- $edges["Pascal"] = $edges["C89"];
- $edges["Pascal"][paren_begin . "," . star_comment] = '<span class="comment">(*';
- $edges["Pascal"][paren_begin . "," . dq_literal] = '<span class="literal">"';
- $edges["Pascal"][paren_begin . "," . sq_literal] = '<span class="literal">\'';
- $edges["Pascal"][slash_begin . "," . dq_literal] = '<span class="literal">"';
- $edges["Pascal"][slash_begin . "," . sq_literal] = '<span class="literal">\'';
- $edges["Pascal"][slash_begin . "," . line_comment] = '<span class="comment">//';
- $edges["Pascal"][normal_text . "," . block_comment] = '<span class="comment">{';
- $edges["Pascal"][line_comment . "," . normal_text] = '</span>';
- $edges["Pascal"][block_comment . "," . normal_text] = '</span>';
-
- $edges["XML"][normal_text . "," . html_entity] = '<span class="html_entity">&amp;';
- $edges["XML"][html_entity . "," . normal_text] = '</span>';
- $edges["XML"][html_entity . "," . xml_tag_begin] = '</span>';
- $edges["XML"][xml_tag . "," . normal_text] = '&gt;</span>';
- $edges["XML"][xml_tag_begin . "," . xml_pi] = '<span class="xml_pi">&lt;?';
- $edges["XML"][xml_tag_begin . "," . line_comment] = '<span class="comment">&lt;!';
- $edges["XML"][line_comment . "," . normal_text] = '&gt;</span>';
- $edges["XML"][xml_tag . "," . dq_literal] = '<span class="literal">"';
- $edges["XML"][dq_literal . "," . xml_tag] = '"</span>';
- $edges["XML"][dq_literal . "," . dq_escape] = '<span class="html_entity">&amp;';
- $edges["XML"][dq_escape . "," . dq_literal] = '</span>';
- $edges["XML"][xml_tag . "," . sq_literal] = '<span class="literal">\'';
- $edges["XML"][sq_literal . "," . xml_tag] = '\'</span>';
- $edges["XML"][sq_literal . "," . sq_escape] = '<span class="html_entity">&amp;';
- $edges["XML"][sq_escape . "," . sq_literal] = '</span>';
-
- //
- // The State Machine
- //
- if (array_key_exists($language, $initial_state)) {
- $state = $initial_state[$language];
- } else {
- $state = normal_text;
- }
-
- $output = "";
- $span = "";
- while (strlen($text) > 0) {
- $ch = substr($text, 0, 1);
- $text = substr($text, 1);
-
- $oldstate = $state;
- $state = (array_key_exists($ch, $states[$language][$state])) ?
- $states[$language][$state][$ch] :
- $states[$language][$state][0];
-
- $span .= $ch;
-
- if ($oldstate != $state) {
- if (array_key_exists($language, $process) &&
- array_key_exists($oldstate, $process[$language])) {
- if (array_key_exists($state, $process[$language][$oldstate])) {
- $pf = $process[$language][$oldstate][$state];
- $output .= $pf($span, $language, $ch);
- } else {
- $pf = $process[$language][$oldstate][0];
- $output .= $pf($span, $language, $ch);
- }
- } else {
- $output .= $span;
- }
-
- if (array_key_exists($language, $edges) &&
- array_key_exists("$oldstate,$state", $edges[$language])) {
- $output .= $edges[$language]["$oldstate,$state"];
- }
-
- $span = "";
- }
- }
-
- if (array_key_exists($language, $process_end) && $state == normal_text) {
- $output .= $process_end[$language]($span, $language);
- } else {
- $output .= $span;
- }
-
- if ($state != normal_text) {
- if (array_key_exists($language, $edges) &&
- array_key_exists("$state," . normal_text, $edges[$language])) {
- $output .= $edges[$language]["$state," . normal_text];
- }
-
- }
-
- return $output;
-}
diff --git a/src/lib.inc.php b/src/lib.inc.php
index 5d3b2655..eefef9c0 100644
--- a/src/lib.inc.php
+++ b/src/lib.inc.php
@@ -5,44 +5,34 @@
*
* $Id: lib.inc.php,v 1.123 2008/04/06 01:10:35 xzilla Exp $
*/
-DEFINE('BASE_PATH', dirname(__DIR__));
+DEFINE('BASE_PATH', dirname(__DIR__));
ini_set('error_log', BASE_PATH . '/temp/logs/phppga.php_error.log');
+$debugmode = true;
+
+if ($debugmode) {
+ ini_set('display_errors', 1);
+ ini_set('display_startup_errors', 1);
+ error_reporting(E_ALL);
+}
+
+require_once BASE_PATH . '/src/errorhandler.inc.php';
+
+if (!defined('ADODB_ERROR_HANDLER_TYPE')) {
+ define('ADODB_ERROR_HANDLER_TYPE', E_USER_ERROR);
+}
+if (!defined('ADODB_ERROR_HANDLER')) {
+ define('ADODB_ERROR_HANDLER', 'Error_Handler');
+}
require_once BASE_PATH . '/vendor/autoload.php';
-include_once BASE_PATH . '/src/errorhandler.inc.php';
-include_once BASE_PATH . '/src/decorator.inc.php';
Kint::enabled(true);
$handler = PhpConsole\Handler::getInstance();
-/* You can override default Handler behavior:
-$handler->setHandleErrors(false); // disable errors handling
-$handler->setHandleExceptions(false); // disable exceptions handling
-$handler->setCallOldHandlers(false); // disable passing errors & exceptions to prviously defined handlers
- */
-$handler->start(); // initialize handlers
+$handler->start(); // initialize handlers*/
PhpConsole\Helper::register(); // it will register global PC class
-// Set error reporting level to max
-error_reporting(E_ALL);
-
-// Application name
-$appName = 'phpPgAdmin';
-
-// Application version
-$appVersion = '6.0.0-alpha';
-
-// PostgreSQL and PHP minimum version
-$postgresqlMinVer = '9.3';
-$phpMinVer = '5.5';
-$debugmode = true;
-
-// Check the version of PHP
-if (version_compare(phpversion(), $phpMinVer, '<')) {
- exit(sprintf('Version of PHP not supported. Please upgrade to version %s or later.', $phpMinVer));
-}
-
// Check to see if the configuration file exists, if not, explain
if (file_exists(BASE_PATH . '/config.inc.php')) {
$conf = [];
@@ -77,7 +67,6 @@ if (!ini_get('session.auto_start')) {
session_name('PPA_ID');
session_start();
}
-//Kint::dump($_SERVER);
$config = [
'msg' => '',
@@ -85,10 +74,18 @@ $config = [
'conf' => $conf,
'lang' => $lang,
'language' => $_language,
+
'settings' => [
+ 'base_path' => BASE_PATH,
'debug' => $debugmode,
- 'appVersion' => $appVersion,
- 'appName' => htmlspecialchars($appName),
+ // Application version
+ 'appVersion' => '6.0.0-alpha',
+ // Application name
+ 'appName' => 'phpPgAdmin',
+
+ // PostgreSQL and PHP minimum version
+ 'postgresqlMinVer' => '9.3',
+ 'phpMinVer' => '5.5',
'displayErrorDetails' => true,
'addContentLengthHeader' => false,
],
@@ -99,8 +96,7 @@ $app = new \Slim\App($config);
// Fetch DI Container
$container = $app->getContainer();
-$plugin_manager = new \PHPPgAdmin\PluginManager($app);
-$container['plugin_manager'] = $plugin_manager;
+$container['plugin_manager'] = new \PHPPgAdmin\PluginManager($app);
$container['serializer'] = function ($c) {
$serializerbuilder = \JMS\Serializer\SerializerBuilder::create();
@@ -132,6 +128,7 @@ $container['misc'] = $misc;
// 4. Check for theme by server/db/user
$_server_info = $misc->getServerInfo();
+
include_once BASE_PATH . '/src/themes.php';
$container['appThemes'] = $appThemes;
@@ -157,31 +154,9 @@ if (!function_exists('pg_connect')) {
exit;
}
-//PC::debug($_server_info, 'server_info');
+$container['action'] = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
-// Create data accessor object, if necessary
-if (!isset($_no_db_connection)) {
- if ($misc->getServerId() === null) {
- echo $lang['strnoserversupplied'];
- exit;
- }
-
- /* starting with PostgreSQL 9.0, we can set the application name */
- if (isset($_server_info['pgVersion']) && $_server_info['pgVersion'] >= 9) {
- putenv("PGAPPNAME={$appName}_{$appVersion}");
- }
-
- // Redirect to the login form if not logged in
- if (!isset($_server_info['username'])) {
- include BASE_PATH . '/src/views/login.php';
- exit;
- }
-
- // Connect to database and set the global $data variable
- $data = $misc->getDatabaseAccessor();
-
-}
-$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
if (!isset($msg)) {
$msg = '';
-} \ No newline at end of file
+}
+$container['msg'] = $msg; \ No newline at end of file
diff --git a/src/themes/datatables.min.css b/src/themes/datatables.min.css
new file mode 100644
index 00000000..0592cc87
--- /dev/null
+++ b/src/themes/datatables.min.css
@@ -0,0 +1,625 @@
+/*
+ * This combined file was created by the DataTables downloader builder:
+ * https://datatables.net/download
+ *
+ * To rebuild or modify this file with the latest versions of the included
+ * software please visit:
+ * https://datatables.net/download/#ju/dt-1.10.12/fh-3.1.2
+ *
+ * Included libraries:
+ * DataTables 1.10.12, FixedHeader 3.1.2
+ */
+
+table.dataTable {
+ width: auto;
+ clear: both;
+ min-width: 100%;
+}
+
+table.dataTable thead th,
+table.dataTable tfoot th {
+ font-weight: bold
+}
+
+table.dataTable thead th {
+ padding: 2px 4px
+}
+
+table.dataTable thead td {
+ padding: 10px 18px
+}
+
+table.dataTable thead th:active,
+table.dataTable thead td:active {
+ outline: none
+}
+
+table.dataTable tbody tr.selected {
+ background-color: #B0BED9
+}
+
+table.dataTable.row-border tbody th,
+table.dataTable.row-border tbody td,
+table.dataTable.display tbody th,
+table.dataTable.display tbody td {
+ border-top: 1px solid #ddd
+}
+
+table.dataTable.row-border tbody tr:first-child th,
+table.dataTable.row-border tbody tr:first-child td,
+table.dataTable.display tbody tr:first-child th,
+table.dataTable.display tbody tr:first-child td {
+ border-top: none
+}
+
+table.dataTable.cell-border tbody th,
+table.dataTable.cell-border tbody td {
+ border-top: 1px solid #ddd;
+ border-right: 1px solid #ddd
+}
+
+table.dataTable.cell-border tbody tr th:first-child,
+table.dataTable.cell-border tbody tr td:first-child {
+ border-left: 1px solid #ddd
+}
+
+table.dataTable.cell-border tbody tr:first-child th,
+table.dataTable.cell-border tbody tr:first-child td {
+ border-top: none
+}
+
+table.dataTable.stripe tbody tr.odd,
+table.dataTable.display tbody tr.odd {
+ background-color: #f9f9f9
+}
+
+table.dataTable.stripe tbody tr.odd.selected,
+table.dataTable.display tbody tr.odd.selected {
+ background-color: #acbad4
+}
+
+table.dataTable.hover tbody tr:hover,
+table.dataTable.display tbody tr:hover {
+ background-color: #f6f6f6
+}
+
+table.dataTable.hover tbody tr:hover.selected,
+table.dataTable.display tbody tr:hover.selected {
+ background-color: #aab7d1
+}
+
+table.dataTable thead .sorting,
+table.dataTable thead .sorting_asc,
+table.dataTable thead .sorting_desc {
+ cursor: pointer;
+ *cursor: hand
+}
+
+table.dataTable thead .sorting,
+table.dataTable thead .sorting_asc,
+table.dataTable thead .sorting_desc,
+table.dataTable thead .sorting_asc_disabled,
+table.dataTable thead .sorting_desc_disabled {
+ background-repeat: no-repeat;
+ background-position: center right
+}
+
+tab table.dataTable thead .sorting {
+ background-image: url("/images/datatables/sort_both.png")
+}
+
+table.dataTable thead .sorting_asc {
+ background-image: url("/images/datatables/sort_asc.png")
+}
+
+table.dataTable thead .sorting_desc {
+ background-image: url("/images/datatables/sort_desc.png")
+}
+
+table.dataTable thead .sorting_asc_disabled {
+ background-image: url("/images/datatables/sort_asc_disabled.png")
+}
+
+table.dataTable thead .sorting_desc_disabled {
+ background-image: url("/images/datatables/sort_desc_disabled.png")
+}
+
+table.dataTable.order-column tbody tr>.sorting_1,
+table.dataTable.order-column tbody tr>.sorting_2,
+table.dataTable.order-column tbody tr>.sorting_3,
+table.dataTable.display tbody tr>.sorting_1,
+table.dataTable.display tbody tr>.sorting_2,
+table.dataTable.display tbody tr>.sorting_3 {
+ background-color: #fafafa
+}
+
+table.dataTable.order-column tbody tr.selected>.sorting_1,
+table.dataTable.order-column tbody tr.selected>.sorting_2,
+table.dataTable.order-column tbody tr.selected>.sorting_3,
+table.dataTable.display tbody tr.selected>.sorting_1,
+table.dataTable.display tbody tr.selected>.sorting_2,
+table.dataTable.display tbody tr.selected>.sorting_3 {
+ background-color: #acbad5
+}
+
+table.dataTable.display tbody tr.odd>.sorting_1,
+table.dataTable.order-column.stripe tbody tr.odd>.sorting_1 {
+ background-color: #f1f1f1
+}
+
+table.dataTable.display tbody tr.odd>.sorting_2,
+table.dataTable.order-column.stripe tbody tr.odd>.sorting_2 {
+ background-color: #f3f3f3
+}
+
+table.dataTable.display tbody tr.odd>.sorting_3,
+table.dataTable.order-column.stripe tbody tr.odd>.sorting_3 {
+ background-color: whitesmoke
+}
+
+table.dataTable.display tbody tr.odd.selected>.sorting_1,
+table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1 {
+ background-color: #a6b4cd
+}
+
+table.dataTable.display tbody tr.odd.selected>.sorting_2,
+table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2 {
+ background-color: #a8b5cf
+}
+
+table.dataTable.display tbody tr.odd.selected>.sorting_3,
+table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3 {
+ background-color: #a9b7d1
+}
+
+table.dataTable.display tbody tr.even>.sorting_1,
+table.dataTable.order-column.stripe tbody tr.even>.sorting_1 {
+ background-color: #fafafa
+}
+
+table.dataTable.display tbody tr.even>.sorting_2,
+table.dataTable.order-column.stripe tbody tr.even>.sorting_2 {
+ background-color: #fcfcfc
+}
+
+table.dataTable.display tbody tr.even>.sorting_3,
+table.dataTable.order-column.stripe tbody tr.even>.sorting_3 {
+ background-color: #fefefe
+}
+
+table.dataTable.display tbody tr.even.selected>.sorting_1,
+table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1 {
+ background-color: #acbad5
+}
+
+table.dataTable.display tbody tr.even.selected>.sorting_2,
+table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2 {
+ background-color: #aebcd6
+}
+
+table.dataTable.display tbody tr.even.selected>.sorting_3,
+table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3 {
+ background-color: #afbdd8
+}
+
+table.dataTable.display tbody tr:hover>.sorting_1,
+table.dataTable.order-column.hover tbody tr:hover>.sorting_1 {
+ background-color: #eaeaea
+}
+
+table.dataTable.display tbody tr:hover>.sorting_2,
+table.dataTable.order-column.hover tbody tr:hover>.sorting_2 {
+ background-color: #ececec
+}
+
+table.dataTable.display tbody tr:hover>.sorting_3,
+table.dataTable.order-column.hover tbody tr:hover>.sorting_3 {
+ background-color: #efefef
+}
+
+table.dataTable.display tbody tr:hover.selected>.sorting_1,
+table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1 {
+ background-color: #a2aec7
+}
+
+table.dataTable.display tbody tr:hover.selected>.sorting_2,
+table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2 {
+ background-color: #a3b0c9
+}
+
+table.dataTable.display tbody tr:hover.selected>.sorting_3,
+table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3 {
+ background-color: #a5b2cb
+}
+
+table.dataTable.no-footer {
+ border-bottom: 1px solid #111
+}
+
+table.dataTable.nowrap th,
+table.dataTable.nowrap td {
+ white-space: nowrap
+}
+
+table.dataTable.compact thead th,
+table.dataTable.compact thead td {
+ padding: 4px 17px 4px 4px
+}
+
+table.dataTable.compact tfoot th,
+table.dataTable.compact tfoot td {
+ padding: 4px
+}
+
+table.dataTable.compact tbody th,
+table.dataTable.compact tbody td {
+ padding: 4px
+}
+
+table.dataTable th.dt-left,
+table.dataTable td.dt-left {
+ text-align: left
+}
+
+table.dataTable th.dt-center,
+table.dataTable td.dt-center,
+table.dataTable td.dataTables_empty {
+ text-align: center
+}
+
+table.dataTable th.dt-right,
+table.dataTable td.dt-right {
+ text-align: right
+}
+
+table.dataTable th.dt-justify,
+table.dataTable td.dt-justify {
+ text-align: justify
+}
+
+table.dataTable th.dt-nowrap,
+table.dataTable td.dt-nowrap {
+ white-space: nowrap
+}
+
+table.dataTable thead th.dt-head-left,
+table.dataTable thead td.dt-head-left,
+table.dataTable tfoot th.dt-head-left,
+table.dataTable tfoot td.dt-head-left {
+ text-align: left
+}
+
+table.dataTable thead th.dt-head-center,
+table.dataTable thead td.dt-head-center,
+table.dataTable tfoot th.dt-head-center,
+table.dataTable tfoot td.dt-head-center {
+ text-align: center
+}
+
+table.dataTable thead th.dt-head-right,
+table.dataTable thead td.dt-head-right,
+table.dataTable tfoot th.dt-head-right,
+table.dataTable tfoot td.dt-head-right {
+ text-align: right
+}
+
+table.dataTable thead th.dt-head-justify,
+table.dataTable thead td.dt-head-justify,
+table.dataTable tfoot th.dt-head-justify,
+table.dataTable tfoot td.dt-head-justify {
+ text-align: justify
+}
+
+table.dataTable thead th.dt-head-nowrap,
+table.dataTable thead td.dt-head-nowrap,
+table.dataTable tfoot th.dt-head-nowrap,
+table.dataTable tfoot td.dt-head-nowrap {
+ white-space: nowrap
+}
+
+table.dataTable tbody th.dt-body-left,
+table.dataTable tbody td.dt-body-left {
+ text-align: left
+}
+
+table.dataTable tbody th.dt-body-center,
+table.dataTable tbody td.dt-body-center {
+ text-align: center
+}
+
+table.dataTable tbody th.dt-body-right,
+table.dataTable tbody td.dt-body-right {
+ text-align: right
+}
+
+table.dataTable tbody th.dt-body-justify,
+table.dataTable tbody td.dt-body-justify {
+ text-align: justify
+}
+
+table.dataTable tbody th.dt-body-nowrap,
+table.dataTable tbody td.dt-body-nowrap {
+ white-space: nowrap
+}
+
+table.dataTable,
+table.dataTable th,
+table.dataTable td {
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box
+}
+
+.dataTables_wrapper {
+ margin-top: 10px;
+ position: relative;
+ clear: both;
+ *zoom: 1;
+ zoom: 1
+}
+
+.dataTables_wrapper .dataTables_length {
+ margin: 5px 5px;
+ padding: 1px 2px 1px 5px;
+ float: left
+}
+
+.dataTables_wrapper .dataTables_filter {
+ margin: 5px 15px;
+ float: right;
+ text-align: right
+}
+
+.dataTables_wrapper .dataTables_filter input {
+ margin-left: 0.5em
+}
+
+.dataTables_wrapper .dataTables_info {
+ clear: both;
+ float: left;
+ padding-top: 0.755em
+}
+
+.dataTables_wrapper .dataTables_paginate {
+ float: right;
+ text-align: right;
+ padding-top: 0.25em
+}
+
+.dataTables_wrapper .dataTables_paginate .paginate_button {
+ box-sizing: border-box;
+ display: inline-block;
+ min-width: 1.5em;
+ padding: 0.5em 1em;
+ margin-left: 2px;
+ text-align: center;
+ text-decoration: none !important;
+ cursor: pointer;
+ *cursor: hand;
+ color: #333 !important;
+ border: 1px solid transparent;
+ border-radius: 2px
+}
+
+.dataTables_wrapper .dataTables_paginate .paginate_button.current,
+.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
+ color: #333 !important;
+ border: 1px solid #979797;
+ background-color: white;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));
+ background: -webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);
+ background: -moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);
+ background: -ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);
+ background: -o-linear-gradient(top, #fff 0%, #dcdcdc 100%);
+ background: linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)
+}
+
+.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
+.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
+.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
+ cursor: default;
+ color: #666 !important;
+ border: 1px solid transparent;
+ background: transparent;
+ box-shadow: none
+}
+
+.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
+ color: white !important;
+ border: 1px solid #111;
+ background-color: #585858;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
+ background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
+ background: -moz-linear-gradient(top, #585858 0%, #111 100%);
+ background: -ms-linear-gradient(top, #585858 0%, #111 100%);
+ background: -o-linear-gradient(top, #585858 0%, #111 100%);
+ background: linear-gradient(to bottom, #585858 0%, #111 100%)
+}
+
+.dataTables_wrapper .dataTables_paginate .paginate_button:active {
+ outline: none;
+ background-color: #2b2b2b;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
+ background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
+ box-shadow: inset 0 0 3px #111
+}
+
+.dataTables_wrapper .dataTables_paginate .ellipsis {
+ padding: 0 1em
+}
+
+.dataTables_wrapper .dataTables_processing {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ height: 40px;
+ margin-left: -50%;
+ margin-top: -25px;
+ padding-top: 20px;
+ text-align: center;
+ font-size: 1.2em;
+ background-color: white;
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%)
+}
+
+.dataTables_wrapper .dataTables_length,
+.dataTables_wrapper .dataTables_filter,
+.dataTables_wrapper .dataTables_info,
+.dataTables_wrapper .dataTables_processing,
+.dataTables_wrapper .dataTables_paginate {
+ color: #333
+}
+
+.dataTables_wrapper .dataTables_scroll {
+ clear: both
+}
+
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
+ *margin-top: -1px;
+ -webkit-overflow-scrolling: touch
+}
+
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td {
+ vertical-align: middle
+}
+
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing {
+ height: 0;
+ overflow: hidden;
+ margin: 0 !important;
+ padding: 0 !important
+}
+
+.dataTables_wrapper.no-footer .dataTables_scrollBody {
+ border-bottom: 1px solid #111
+}
+
+.dataTables_wrapper.no-footer div.dataTables_scrollHead table,
+.dataTables_wrapper.no-footer div.dataTables_scrollBody table {
+ border-bottom: none
+}
+
+.dataTables_wrapper:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0
+}
+
+@media screen and (max-width: 767px) {
+ .dataTables_wrapper .dataTables_info,
+ .dataTables_wrapper .dataTables_paginate {
+ float: none;
+ text-align: center
+ }
+ .dataTables_wrapper .dataTables_paginate {
+ margin-top: 0.5em
+ }
+}
+
+@media screen and (max-width: 640px) {
+ .dataTables_wrapper .dataTables_length,
+ .dataTables_wrapper .dataTables_filter {
+ float: none;
+ text-align: center
+ }
+ .dataTables_wrapper .dataTables_filter {
+ margin-top: 0.5em
+ }
+}
+
+table.dataTable thead th div.DataTables_sort_wrapper {
+ position: relative
+}
+
+table.dataTable thead th div.DataTables_sort_wrapper span {
+ position: absolute;
+ top: 50%;
+ margin-top: -8px;
+ right: -18px
+}
+
+table.dataTable thead th.ui-state-default,
+table.dataTable tfoot th.ui-state-default {
+ border-left-width: 0
+}
+
+table.dataTable thead th.ui-state-default:first-child,
+table.dataTable tfoot th.ui-state-default:first-child {
+ border-left-width: 1px
+}
+
+.dataTables_wrapper .dataTables_paginate .fg-button {
+ box-sizing: border-box;
+ display: inline-block;
+ min-width: 1.5em;
+ padding: 0.5em;
+ margin-left: 2px;
+ text-align: center;
+ text-decoration: none !important;
+ cursor: pointer;
+ *cursor: hand;
+ border: 1px solid transparent
+}
+
+.dataTables_wrapper .dataTables_paginate .fg-button:active {
+ outline: none
+}
+
+.dataTables_wrapper .dataTables_paginate .fg-button:first-child {
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+
+.dataTables_wrapper .dataTables_paginate .fg-button:last-child {
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px
+}
+
+.dataTables_wrapper .ui-widget-header {
+ font-weight: normal
+}
+
+.dataTables_wrapper .ui-toolbar {
+ padding: 8px
+}
+
+.dataTables_wrapper.no-footer .dataTables_scrollBody {
+ border-bottom: none
+}
+
+.dataTables_wrapper .dataTables_length,
+.dataTables_wrapper .dataTables_filter,
+.dataTables_wrapper .dataTables_info,
+.dataTables_wrapper .dataTables_processing,
+.dataTables_wrapper .dataTables_paginate {
+ color: inherit
+}
+
+table.fixedHeader-floating {
+ position: fixed !important;
+ background-color: white
+}
+
+table.fixedHeader-locked {
+ position: absolute !important;
+ background-color: white
+}
+
+@media print {
+ table.fixedHeader-floating {
+ display: none
+ }
+}
diff --git a/src/themes/default/global.css b/src/themes/default/global.css
index 4407be23..4c80f79b 100644
--- a/src/themes/default/global.css
+++ b/src/themes/default/global.css
@@ -10,9 +10,9 @@
body {
background-color: #FFFFFF;
- margin: 4px;
+ margin: 0;
font-family: roboto;
- padding: 0px;
+ padding: 0;
font-size: smaller;
/*0.8em;*/
}
@@ -36,7 +36,7 @@ body.bottombar {
}
h2 {
- color: #666633;
+ color: #336699;
font-size: medium;
/*1.3em;*/
font-family: roboto;
@@ -48,7 +48,7 @@ h2 {
}
h3 {
- color: #666633;
+ color: #336699;
font-size: small;
font-family: roboto;
font-weight: bold;
@@ -85,7 +85,7 @@ th.data {
color: #000000;
background-color: #cfd8dc;
font-family: roboto;
- font-size: 1.2em;
+ font-size: 1em;
padding: 2px 4px;
}
@@ -115,6 +115,10 @@ td.topbar {
text-align: left;
}
+.topbar .toplink {
+ margin-right: 20px;
+}
+
ul.toplink,
ul.navlink {
list-style: none;
diff --git a/src/themes/global.css b/src/themes/global.css
index bd8c90a3..2a5a4160 100644
--- a/src/themes/global.css
+++ b/src/themes/global.css
@@ -27,6 +27,55 @@ p.comment {
padding-left: 5pt;
}
+.flexbox_wrapper,
+html,
+body.flexbox_body {
+ height: 100%;
+ margin: 0;
+}
+
+.flexbox_wrapper {
+ display: flex;
+}
+
+.flexbox_wrapper > .browser_container {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+ text-align: left;
+ float: left;
+ flex: 0 0 300px;
+}
+
+.flexbox_wrapper >.detail_container {
+ height: 100%;
+ width: 100%;
+ margin: 0px;
+ padding: 0px;
+ text-align: left;
+ float: left;
+ flex: 1 1;
+}
+
+.topbar {
+ position: fixed;
+ z-index: 100;
+ width: 100%;
+ top: 0;
+}
+
+.trail {
+ position: fixed;
+ z-index: 100;
+ width: 100%;
+ top: 27px;
+}
+
+.detailbody {
+ margin-top: 58px;
+ margin-bottom: -58px;
+}
+
/** Browser Tree using XLoadTree 2 **/
diff --git a/src/tree/aggregates.php b/src/tree/aggregates.php
deleted file mode 100644
index fb318b49..00000000
--- a/src/tree/aggregates.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage aggregates in a database
- *
- * $Id: aggregates.php,v 1.27 2008/01/19 13:46:15 ioguix Exp $
- */
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $aggregates = $data->getAggregates();
-
- $proto = concat(Decorator::field('proname'), ' (', Decorator::field('proargtypes'), ')');
- $reqvars = $misc->getRequestVars('aggregate');
-
- $attrs = [
- 'text' => $proto,
- 'icon' => 'Aggregate',
- 'toolTip' => Decorator::field('aggcomment'),
- 'action' => Decorator::redirecturl('redirect.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'aggrname' => Decorator::field('proname'),
- 'aggrtype' => Decorator::field('proargtypes'),
- ]
- ),
- ];
-
- return $misc->printTree($aggregates, $attrs, 'aggregates', false);
-}
diff --git a/src/tree/all_db.php b/src/tree/all_db.php
deleted file mode 100644
index f713f784..00000000
--- a/src/tree/all_db.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $databases = $data->getDatabases();
-
- $reqvars = $misc->getRequestVars('database');
-
- $attrs = [
- 'text' => Decorator::field('datname'),
- 'icon' => 'Database',
- 'toolTip' => Decorator::field('datcomment'),
- 'action' => Decorator::redirecturl('redirect.php', $reqvars, ['database' => Decorator::field('datname')]),
- 'branch' => Decorator::branchurl('database.php', $reqvars, ['action' => 'tree', 'database' => Decorator::field('datname')]),
- ];
-
- return $misc->printTree($databases, $attrs, 'databases', false);
-}
diff --git a/src/tree/casts.php b/src/tree/casts.php
deleted file mode 100644
index c1d0137c..00000000
--- a/src/tree/casts.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage casts in a database
- *
- * $Id: casts.php,v 1.16 2007/09/25 16:08:05 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $casts = $data->getCasts();
-
- $proto = concat(Decorator::field('castsource'), ' AS ', Decorator::field('casttarget'));
-
- $attrs = [
- 'text' => $proto,
- 'icon' => 'Cast',
- ];
-
- $misc->printTree($casts, $attrs, 'casts');
- exit;
-}
diff --git a/src/tree/constraints.php b/src/tree/constraints.php
deleted file mode 100644
index 702aca28..00000000
--- a/src/tree/constraints.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * List constraints on a table
- *
- * $Id: constraints.php,v 1.56 2007/12/31 16:46:07 xzilla Exp $
- */
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $constraints = $data->getConstraints($_REQUEST['table']);
-
- $reqvars = $misc->getRequestVars('schema');
-
- function getIcon($f) {
- switch ($f['contype']) {
- case 'u':
- return 'UniqueConstraint';
- case 'c':
- return 'CheckConstraint';
- case 'f':
- return 'ForeignKey';
- case 'p':
- return 'PrimaryKey';
-
- }
- }
-
- $attrs = [
- 'text' => Decorator::field('conname'),
- 'icon' => callback('getIcon'),
- ];
-
- $misc->printTree($constraints, $attrs, 'constraints');
- exit;
-}
diff --git a/src/tree/conversions.php b/src/tree/conversions.php
deleted file mode 100644
index b1afc6bb..00000000
--- a/src/tree/conversions.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage conversions in a database
- *
- * $Id: conversions.php,v 1.15 2007/08/31 18:30:10 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $conversions = $data->getconversions();
-
- $attrs = [
- 'text' => Decorator::field('conname'),
- 'icon' => 'Conversion',
- 'toolTip' => Decorator::field('concomment'),
- ];
-
- $misc->printTree($conversions, $attrs, 'conversions');
- exit;
-}
diff --git a/src/tree/database.php b/src/tree/database.php
deleted file mode 100755
index 3eeca0ee..00000000
--- a/src/tree/database.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage schemas within a database
- *
- * $Id: database.php,v 1.104 2007/11/30 06:04:43 xzilla Exp $
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $reqvars = $misc->getRequestVars('database');
-
- $tabs = $misc->getNavTabs('database');
-
- $items = $misc->adjustTabsForTree($tabs);
- \PC::debug($reqvars, 'reqvars');
- $attrs = [
- 'text' => Decorator::field('title'),
- 'icon' => Decorator::field('icon'),
- 'action' => Decorator::actionurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars', [])),
- 'branch' => Decorator::branchurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars'), ['action' => 'tree']),
- ];
-
- return $misc->printTree($items, $attrs, 'database', false);
-
-}
diff --git a/src/tree/domains.php b/src/tree/domains.php
deleted file mode 100644
index f3faa1b4..00000000
--- a/src/tree/domains.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage domains in a database
- *
- * $Id: domains.php,v 1.34 2007/09/13 13:41:01 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $domains = $data->getDomains();
-
- $reqvars = $misc->getRequestVars('domain');
-
- $attrs = [
- 'text' => Decorator::field('domname'),
- 'icon' => 'Domain',
- 'toolTip' => Decorator::field('domcomment'),
- 'action' => Decorator::actionurl('domains.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'domain' => Decorator::field('domname'),
- ]
- ),
- ];
-
- return $misc->printTree($domains, $attrs, 'domains', false);
-}
diff --git a/src/tree/fulltext.php b/src/tree/fulltext.php
deleted file mode 100644
index b8d4eb43..00000000
--- a/src/tree/fulltext.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage fulltext configurations, dictionaries and mappings
- *
- * $Id: fulltext.php,v 1.6 2008/03/17 21:35:48 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $tabs = $misc->getNavTabs('fulltext');
- $items = $misc->adjustTabsForTree($tabs);
-
- $reqvars = $misc->getRequestVars('ftscfg');
-
- $attrs = [
- 'text' => Decorator::field('title'),
- 'icon' => Decorator::field('icon'),
- 'action' => Decorator::actionurl('fulltext.php',
- $reqvars,
- field('urlvars')
- ),
- 'branch' => Decorator::branchurl('fulltext.php',
- $reqvars,
- [
- 'action' => 'subtree',
- 'what' => Decorator::field('icon'), // IZ: yeah, it's ugly, but I do not want to change navigation tabs arrays
- ]
- ),
- ];
-
- return $misc->printTree($items, $attrs, 'fts', false);
-}
-
-function doSubTree($container, $what) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- switch ($what) {
- case 'FtsCfg':
- $items = $data->getFtsConfigurations(false);
- $urlvars = ['action' => 'viewconfig', 'ftscfg' => Decorator::field('name')];
- break;
- case 'FtsDict':
- $items = $data->getFtsDictionaries(false);
- $urlvars = ['action' => 'viewdicts'];
- break;
- case 'FtsParser':
- $items = $data->getFtsParsers(false);
- $urlvars = ['action' => 'viewparsers'];
- break;
- default:
- exit;
- }
-
- $reqvars = $misc->getRequestVars('ftscfg');
-
- $attrs = [
- 'text' => Decorator::field('name'),
- 'icon' => $what,
- 'toolTip' => Decorator::field('comment'),
- 'action' => Decorator::actionurl('fulltext.php',
- $reqvars,
- $urlvars
- ),
- 'branch' => Decorator::ifempty(Decorator::field('branch'),
- '',
- url('fulltext.php',
- $reqvars,
- [
- 'action' => 'subtree',
- 'ftscfg' => Decorator::field('name'),
- ]
- )
- ),
- ];
-
- return $misc->printTree($items, $attrs, strtolower($what), false);
-
-}
diff --git a/src/tree/functions.php b/src/tree/functions.php
deleted file mode 100644
index 2768cabf..00000000
--- a/src/tree/functions.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage functions in a database
- *
- * $Id: functions.php,v 1.78 2008/01/08 22:50:29 xzilla Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $funcs = $data->getFunctions();
-
- $proto = concat(Decorator::field('proname'), ' (', Decorator::field('proarguments'), ')');
-
- $reqvars = $misc->getRequestVars('function');
-
- $attrs = [
- 'text' => $proto,
- 'icon' => 'Function',
- 'toolTip' => Decorator::field('procomment'),
- 'action' => Decorator::redirecturl('redirect.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'function' => $proto,
- 'function_oid' => Decorator::field('prooid'),
- ]
- ),
- ];
-
- return $misc->printTree($funcs, $attrs, 'functions', false);
-}
diff --git a/src/tree/indexes.php b/src/tree/indexes.php
deleted file mode 100644
index 673cf5a2..00000000
--- a/src/tree/indexes.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * List indexes on a table
- *
- * $Id: indexes.php,v 1.46 2008/01/08 22:50:29 xzilla Exp $
- */
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $indexes = $data->getIndexes($_REQUEST['table']);
-
- $reqvars = $misc->getRequestVars('table');
-
- function getIcon($f) {
- if ($f['indisprimary'] == 't') {
- return 'PrimaryKey';
- }
-
- if ($f['indisunique'] == 't') {
- return 'UniqueConstraint';
- }
-
- return 'Index';
- }
-
- $attrs = [
- 'text' => Decorator::field('indname'),
- 'icon' => callback('getIcon'),
- ];
-
- return $misc->printTree($indexes, $attrs, 'indexes', false);
-}
diff --git a/src/tree/languages.php b/src/tree/languages.php
deleted file mode 100644
index 5b03dad2..00000000
--- a/src/tree/languages.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage languages in a database
- *
- * $Id: languages.php,v 1.13 2007/08/31 18:30:11 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $languages = $data->getLanguages();
-
- $attrs = [
- 'text' => Decorator::field('lanname'),
- 'icon' => 'Language',
- ];
-
- return $misc->printTree($languages, $attrs, 'languages', false);
-
-}
diff --git a/src/tree/opclasses.php b/src/tree/opclasses.php
deleted file mode 100644
index 4a0c2907..00000000
--- a/src/tree/opclasses.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage opclasss in a database
- *
- * $Id: opclasses.php,v 1.10 2007/08/31 18:30:11 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $opclasses = $data->getOpClasses();
-
- // OpClass prototype: "op_class/access_method"
- $proto = concat(Decorator::field('opcname'), '/', Decorator::field('amname'));
-
- $attrs = [
- 'text' => $proto,
- 'icon' => 'OperatorClass',
- 'toolTip' => Decorator::field('opccomment'),
- ];
-
- return $misc->printTree($opclasses, $attrs, 'opclasses', false);
-}
diff --git a/src/tree/operators.php b/src/tree/operators.php
deleted file mode 100644
index 47b6db64..00000000
--- a/src/tree/operators.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage operators in a database
- *
- * $Id: operators.php,v 1.29 2007/08/31 18:30:11 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $operators = $data->getOperators();
-
- // Operator prototype: "type operator type"
- $proto = concat(Decorator::field('oprleftname'), ' ', Decorator::field('oprname'), ' ', Decorator::field('oprrightname'));
-
- $reqvars = $misc->getRequestVars('operator');
-
- $attrs = [
- 'text' => $proto,
- 'icon' => 'Operator',
- 'toolTip' => Decorator::field('oprcomment'),
- 'action' => Decorator::actionurl('operators.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'operator' => $proto,
- 'operator_oid' => Decorator::field('oid'),
- ]
- ),
- ];
-
- return $misc->printTree($operators, $attrs, 'operators', false);
-}
diff --git a/src/tree/rules.php b/src/tree/rules.php
deleted file mode 100644
index 17cebdbe..00000000
--- a/src/tree/rules.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * List rules on a table OR view
- *
- * $Id: rules.php,v 1.33 2007/08/31 18:30:11 ioguix Exp $
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $rules = $data->getRules($_REQUEST[$_REQUEST['subject']]);
-
- $reqvars = $misc->getRequestVars($_REQUEST['subject']);
-
- $attrs = [
- 'text' => Decorator::field('rulename'),
- 'icon' => 'Rule',
- ];
-
- return $misc->printTree($rules, $attrs, 'rules', false);
-}
diff --git a/src/tree/schemas.php b/src/tree/schemas.php
deleted file mode 100755
index 7f607ab3..00000000
--- a/src/tree/schemas.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage schemas in a database
- *
- * $Id: schemas.php,v 1.22 2007/12/15 22:57:43 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $schemas = $data->getSchemas();
-
- $reqvars = $misc->getRequestVars('schema');
-
- $attrs = [
- 'text' => Decorator::field('nspname'),
- 'icon' => 'Schema',
- 'toolTip' => Decorator::field('nspcomment'),
- 'action' => Decorator::redirecturl('redirect.php',
- $reqvars,
- [
- 'subject' => 'schema',
- 'schema' => Decorator::field('nspname'),
- ]
- ),
- 'branch' => Decorator::branchurl('schemas.php',
- $reqvars,
- [
- 'action' => 'subtree',
- 'schema' => Decorator::field('nspname'),
- ]
- ),
- ];
-
- return $misc->printTree($schemas, $attrs, 'schemas', false);
-
-}
-
-function doSubTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $tabs = $misc->getNavTabs('schema');
-
- $items = $misc->adjustTabsForTree($tabs);
-
- $reqvars = $misc->getRequestVars('schema');
-
- $attrs = [
- 'text' => Decorator::field('title'),
- 'icon' => Decorator::field('icon'),
- 'action' => Decorator::actionurl(Decorator::field('url'),
- $reqvars,
- Decorator::field('urlvars', [])
- ),
- 'branch' => Decorator::branchurl(Decorator::field('url'),
- $reqvars,
- Decorator::field('urlvars'),
- ['action' => 'tree']
- ),
- ];
-
- return $misc->printTree($items, $attrs, 'schema', false);
-
-}
diff --git a/src/tree/sequences.php b/src/tree/sequences.php
deleted file mode 100644
index 23a067ac..00000000
--- a/src/tree/sequences.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-/**
- * Manage sequences in a database
- *
- * $Id: sequences.php,v 1.49 2007/12/15 22:21:54 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $sequences = $data->getSequences();
-
- $reqvars = $misc->getRequestVars('sequence');
-
- $attrs = [
- 'text' => Decorator::field('seqname'),
- 'icon' => 'Sequence',
- 'toolTip' => Decorator::field('seqcomment'),
- 'action' => Decorator::actionurl('sequences.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'sequence' => Decorator::field('seqname'),
- ]
- ),
- ];
-
- return $misc->printTree($sequences, $attrs, 'sequences', false);
-}
diff --git a/src/tree/servers.php b/src/tree/servers.php
deleted file mode 100644
index a99956a1..00000000
--- a/src/tree/servers.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
-
- $nodes = [];
- $group_id = isset($_GET['group']) ? $_GET['group'] : false;
-
- /* root with srv_groups */
- if (isset($conf['srv_groups']) and count($conf['srv_groups']) > 0
- and $group_id === false) {
- $nodes = $misc->getServersGroups(true);
- } else if (isset($conf['srv_groups']) and $group_id !== false) {
- /* group subtree */
- if ($group_id !== 'all') {
- $nodes = $misc->getServersGroups(false, $group_id);
- }
-
- $nodes = array_merge($nodes, $misc->getServers(false, $group_id));
- $nodes = new \PHPPgAdmin\ArrayRecordSet($nodes);
- } else {
- /* no srv_group */
- $nodes = $misc->getServers(true, false);
- }
-
- $reqvars = $misc->getRequestVars('server');
-
- $attrs = [
- 'text' => Decorator::field('desc'),
-
- // Show different icons for logged in/out
- 'icon' => Decorator::field('icon'),
-
- 'toolTip' => Decorator::field('id'),
-
- 'action' => Decorator::field('action'),
-
- // Only create a branch url if the user has
- // logged into the server.
- 'branch' => Decorator::field('branch'),
- ];
- PC::debug($nodes, 'printTree');
- return $misc->printTree($nodes, $attrs, 'servers', false);
-
-}
diff --git a/src/tree/tables.php b/src/tree/tables.php
deleted file mode 100644
index 546fa96d..00000000
--- a/src/tree/tables.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * List tables in a database
- *
- * $Id: tables.php,v 1.112 2008/06/16 22:38:46 ioguix Exp $
- */
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- \PC::debug($misc->getDatabase(), 'getDatabase');
-
- $tables = $data->getTables();
-
- $reqvars = $misc->getRequestVars('table');
-
- $attrs = [
- 'text' => Decorator::field('relname'),
- 'icon' => 'Table',
- 'iconAction' => Decorator::url('display.php',
- $reqvars,
- ['table' => Decorator::field('relname')]
- ),
- 'toolTip' => Decorator::field('relcomment'),
- 'action' => Decorator::redirecturl('redirect.php',
- $reqvars,
- ['table' => Decorator::field('relname')]
- ),
- 'branch' => Decorator::branchurl('tables.php',
- $reqvars,
- [
- 'action' => 'subtree',
- 'table' => Decorator::field('relname'),
- ]
- ),
- ];
-
- return $misc->printTree($tables, $attrs, 'tables', false);
-}
-
-function doSubTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $tabs = $misc->getNavTabs('table');
- $items = $misc->adjustTabsForTree($tabs);
- $reqvars = $misc->getRequestVars('table');
-
- $attrs = [
- 'text' => Decorator::field('title'),
- 'icon' => Decorator::field('icon'),
- 'action' => Decorator::actionurl(
- Decorator::field('url'),
- $reqvars,
- Decorator::field('urlvars'),
- ['table' => $_REQUEST['table']]
- ),
- 'branch' => Decorator::ifempty(
- Decorator::field('branch'), '', Decorator::branchurl(Decorator::field('url'), $reqvars, [
- 'action' => 'tree',
- 'table' => $_REQUEST['table'],
- ]
- )
- ),
- ];
-
- return $misc->printTree($items, $attrs, 'table', false);
-}
diff --git a/src/tree/tblproperties.php b/src/tree/tblproperties.php
deleted file mode 100644
index f3f5d852..00000000
--- a/src/tree/tblproperties.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $columns = $data->getTableAttributes($_REQUEST['table']);
- $reqvars = $misc->getRequestVars('column');
-
- $attrs = [
- 'text' => Decorator::field('attname'),
- 'action' => Decorator::actionurl('colproperties.php',
- $reqvars,
- [
- 'table' => $_REQUEST['table'],
- 'column' => Decorator::field('attname'),
- ]
- ),
- 'icon' => 'Column',
- 'iconAction' => Decorator::url('display.php',
- $reqvars,
- [
- 'table' => $_REQUEST['table'],
- 'column' => Decorator::field('attname'),
- 'query' => replace(
- 'SELECT "%column%", count(*) AS "count" FROM "%table%" GROUP BY "%column%" ORDER BY "%column%"',
- [
- '%column%' => Decorator::field('attname'),
- '%table%' => $_REQUEST['table'],
- ]
- ),
- ]
- ),
- 'toolTip' => Decorator::field('comment'),
- ];
-
- return $misc->printTree($columns, $attrs, 'tblcolumns', false);
-}
diff --git a/src/tree/triggers.php b/src/tree/triggers.php
deleted file mode 100644
index 775f44aa..00000000
--- a/src/tree/triggers.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * List triggers on a table
- *
- * $Id: triggers.php,v 1.37 2007/09/19 14:42:12 ioguix Exp $
- */
-
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
- $triggers = $data->getTriggers($_REQUEST['table']);
-
- $reqvars = $misc->getRequestVars('table');
-
- $attrs = [
- 'text' => Decorator::field('tgname'),
- 'icon' => 'Trigger',
- ];
-
- $misc->printTree($triggers, $attrs, 'triggers');
- exit;
-}
diff --git a/src/tree/types.php b/src/tree/types.php
deleted file mode 100644
index f4235955..00000000
--- a/src/tree/types.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage types in a database
- *
- * $Id: types.php,v 1.42 2007/11/30 15:25:23 soranzo Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree() {
- global $misc, $data;
-
- $types = $data->getTypes();
-
- $reqvars = $misc->getRequestVars('type');
-
- $attrs = [
- 'text' => Decorator::field('typname'),
- 'icon' => 'Type',
- 'toolTip' => Decorator::field('typcomment'),
- 'action' => Decorator::actionurl('types.php',
- $reqvars,
- [
- 'action' => 'properties',
- 'type' => Decorator::field('basename'),
- ]
- ),
- ];
-
- $misc->printTree($types, $attrs, 'types');
- exit;
-}
diff --git a/src/tree/viewproperties.php b/src/tree/viewproperties.php
deleted file mode 100755
index 3cb2acce..00000000
--- a/src/tree/viewproperties.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * List views in a database
- *
- * $Id: viewproperties.php,v 1.34 2007/12/11 14:17:17 ioguix Exp $
- */
-
-function doTree() {
- global $misc, $data;
-
- $reqvars = $misc->getRequestVars('column');
- $columns = $data->getTableAttributes($_REQUEST['view']);
-
- $attrs = [
- 'text' => Decorator::field('attname'),
- 'action' => Decorator::actionurl('colproperties.php',
- $reqvars,
- [
- 'view' => $_REQUEST['view'],
- 'column' => Decorator::field('attname'),
- ]
- ),
- 'icon' => 'Column',
- 'iconAction' => Decorator::url('display.php',
- $reqvars,
- [
- 'view' => $_REQUEST['view'],
- 'column' => Decorator::field('attname'),
- 'query' => replace(
- 'SELECT "%column%", count(*) AS "count" FROM %view% GROUP BY "%column%" ORDER BY "%column%"',
- [
- '%column%' => Decorator::field('attname'),
- '%view%' => $_REQUEST['view'],
- ]
- ),
- ]
- ),
- 'toolTip' => Decorator::field('comment'),
- ];
-
- $misc->printTree($columns, $attrs, 'viewcolumns');
-
- exit;
-}
diff --git a/src/tree/views.php b/src/tree/views.php
deleted file mode 100644
index 08e7caec..00000000
--- a/src/tree/views.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-use \PHPPgAdmin\Decorators\Decorator;
-/**
- * Manage views in a database
- *
- * $Id: views.php,v 1.75 2007/12/15 22:57:43 ioguix Exp $
- */
-
-/**
- * Generate XML for the browser tree.
- */
-function doTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $views = $data->getViews();
-
- $reqvars = $misc->getRequestVars('view');
-
- $attrs = [
- 'text' => Decorator::field('relname'),
- 'icon' => 'View',
- 'iconAction' => Decorator::url('display.php', $reqvars, ['view' => Decorator::field('relname')]),
- 'toolTip' => Decorator::field('relcomment'),
- 'action' => Decorator::redirecturl('redirect.php', $reqvars, ['view' => Decorator::field('relname')]),
- 'branch' => Decorator::branchurl('views/subtree', $reqvars,
- [
- 'view' => Decorator::field('relname'),
- ]
- ),
- ];
-
- return $misc->printTree($views, $attrs, 'views', false);
-}
-
-function doSubTree($container) {
-
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- $lang = $container->get('lang');
- $data = $misc->getDatabaseAccessor();
-
- $tabs = $misc->getNavTabs('view');
- $items = $misc->adjustTabsForTree($tabs);
- $reqvars = $misc->getRequestVars('view');
-
- $attrs = [
- 'text' => Decorator::field('title'),
- 'icon' => Decorator::field('icon'),
- 'action' => Decorator::actionurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars'), ['view' => $_REQUEST['view']]),
- 'branch' => Decorator::ifempty(
- Decorator::field('branch'), '', Decorator::branchurl(Decorator::field('url'), Decorator::field('urlvars'), $reqvars,
- [
- 'action' => 'tree',
- 'view' => $_REQUEST['view'],
- ]
- )
- ),
- ];
-
- return $misc->printTree($items, $attrs, 'view', false);
-}
diff --git a/src/views/aggregates.php b/src/views/aggregates.php
index f6c51c9f..aee82b44 100644
--- a/src/views/aggregates.php
+++ b/src/views/aggregates.php
@@ -9,51 +9,5 @@
// Include application functions
require_once '../lib.inc.php';
-$misc->printHeader($lang['straggregates']);
-$misc->printBody();
-
$aggregate_controller = new \PHPPgAdmin\Controller\AggregateController($container);
-
-switch ($action) {
- case 'create':
- $aggregate_controller->doCreate();
- break;
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $aggregate_controller->doDefault();
- } else {
- $aggregate_controller->doSaveCreate();
- }
-
- break;
- case 'alter':
- $aggregate_controller->doAlter();
- break;
- case 'save_alter':
- if (isset($_POST['alter'])) {
- $aggregate_controller->doSaveAlter();
- } else {
- $aggregate_controller->doProperties();
- }
-
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $aggregate_controller->doDrop(false);
- } else {
- $aggregate_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $aggregate_controller->doDrop(true);
- break;
- default:
- $aggregate_controller->doDefault();
- break;
- case 'properties':
- $aggregate_controller->doProperties();
- break;
-}
-
-$misc->printFooter();
+$aggregate_controller->render();
diff --git a/src/views/ajax-ac-insert.php b/src/views/ajax-ac-insert.php
index 6367c760..a0436500 100644
--- a/src/views/ajax-ac-insert.php
+++ b/src/views/ajax-ac-insert.php
@@ -1,94 +1,7 @@
<?php
-require_once '../lib.inc.php';
-
-if (isset($_POST['offset'])) {
- $offset = " OFFSET {$_POST['offset']}";
-} else {
- $_POST['offset'] = 0;
- $offset = " OFFSET 0";
-}
-
-$keynames = [];
-foreach ($_POST['fkeynames'] as $k => $v) {
- $fkeynames[$k] = html_entity_decode($v, ENT_QUOTES);
-}
-
-$keyspos = array_combine($fkeynames, $_POST['keys']);
-
-$f_schema = html_entity_decode($_POST['f_schema'], ENT_QUOTES);
-$data->fieldClean($f_schema);
-$f_table = html_entity_decode($_POST['f_table'], ENT_QUOTES);
-$data->fieldClean($f_table);
-$f_attname = $fkeynames[$_POST['fattpos'][0]];
-$data->fieldClean($f_attname);
-
-$q = "SELECT *
- FROM \"{$f_schema}\".\"{$f_table}\"
- WHERE \"{$f_attname}\"::text LIKE '{$_POST['fvalue']}%'
- ORDER BY \"{$f_attname}\" LIMIT 12 {$offset};";
-
-$res = $data->selectSet($q);
-
-if (!$res->EOF) {
- echo "<table class=\"ac_values\">";
- echo '<tr>';
- foreach (array_keys($res->fields) as $h) {
- echo '<th>';
- if (in_array($h, $fkeynames)) {
- echo '<img src="' . $misc->icon('ForeignKey') . '" alt="[referenced key]" />';
- }
-
- echo htmlentities($h, ENT_QUOTES, 'UTF-8'), '</th>';
-
- }
- echo "</tr>\n";
- $i = 0;
- while ((!$res->EOF) && ($i < 11)) {
- $j = 0;
- echo "<tr class=\"acline\">";
- foreach ($res->fields as $n => $v) {
- $finfo = $res->fetchField($j++);
- if (in_array($n, $fkeynames)) {
- echo "<td><a href=\"javascript:void(0)\" class=\"fkval\" name=\"{$keyspos[$n]}\">",
- $misc->printVal($v, $finfo->type, ['clip' => 'collapsed']),
- "</a></td>";
- } else {
- echo "<td><a href=\"javascript:void(0)\">",
- $misc->printVal($v, $finfo->type, ['clip' => 'collapsed']),
- "</a></td>";
- }
-
- }
- echo "</tr>\n";
- $i++;
- $res->moveNext();
- }
- echo "</table>\n";
-
- $page_tests = '';
-
- $js = "<script type=\"text/javascript\">\n";
-
- if ($_POST['offset']) {
- echo "<a href=\"javascript:void(0)\" id=\"fkprev\">&lt;&lt; Prev</a>";
- $js .= "fkl_hasprev=true;\n";
- } else {
- $js .= "fkl_hasprev=false;\n";
- }
-
- if ($res->recordCount() == 12) {
- $js .= "fkl_hasnext=true;\n";
- echo "&nbsp;&nbsp;&nbsp;<a href=\"javascript:void(0)\" id=\"fknext\">Next &gt;&gt;</a>";
- } else {
- $js .= "fkl_hasnext=false;\n";
- }
+require_once '../lib.inc.php';
- echo $js . "</script>";
-} else {
- printf("<p>{$lang['strnofkref']}</p>", "\"{$_POST['f_schema']}\".\"{$_POST['f_table']}\".\"{$fkeynames[$_POST['fattpos']]}\"");
+$acinsert_controller = new \PHPPgAdmin\Controller\ACInsertController($container);
- if ($_POST['offset']) {
- echo "<a href=\"javascript:void(0)\" class=\"fkprev\">Prev &lt;&lt;</a>";
- }
-}
+$acinsert_controller->render();
diff --git a/src/views/all_db.php b/src/views/all_db.php
index 1e75659b..6ccb3ade 100644
--- a/src/views/all_db.php
+++ b/src/views/all_db.php
@@ -13,50 +13,4 @@ if (!defined('BASE_PATH')) {
$all_db_controller = new \PHPPgAdmin\Controller\AllDBController($container);
-$misc->printHeader($lang['strdatabases']);
-$misc->printBody();
-
-switch ($action) {
- case 'export':
- $all_db_controller->doExport();
- break;
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $all_db_controller->doDefault();
- } else {
- $all_db_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- $all_db_controller->doCreate();
- break;
- case 'drop':
- if (isset($_REQUEST['drop'])) {
- $all_db_controller->doDrop(false);
- } else {
- $all_db_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- doDrop(true);
- break;
- case 'alter':
- if (isset($_POST['oldname']) && isset($_POST['newname']) && !isset($_POST['cancel'])) {
- $all_db_controller->doAlter(false);
- } else {
- $all_db_controller->doDefault();
- }
-
- break;
- case 'confirm_alter':
- $all_db_controller->doAlter(true);
- break;
- default:
- $all_db_controller->doDefault();
-
- break;
-}
-
-$misc->printFooter();
+$all_db_controller->render();
diff --git a/src/views/browser.php b/src/views/browser.php
new file mode 100644
index 00000000..142e9a9f
--- /dev/null
+++ b/src/views/browser.php
@@ -0,0 +1,9 @@
+<?php
+
+if (!defined('BASE_PATH')) {
+ require_once '../lib.inc.php';
+}
+
+$browser_controller = new \PHPPgAdmin\Controller\BrowserController($container);
+
+$browser_controller->render();
diff --git a/src/views/casts.php b/src/views/casts.php
index b46b7d56..201c0332 100644
--- a/src/views/casts.php
+++ b/src/views/casts.php
@@ -11,14 +11,4 @@ require_once '../lib.inc.php';
$cast_controller = new \PHPPgAdmin\Controller\CastController($container);
-$misc->printHeader($lang['strcasts']);
-$misc->printBody();
-
-switch ($action) {
-
- default:
- $cast_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$cast_controller->render(); \ No newline at end of file
diff --git a/src/views/colproperties.php b/src/views/colproperties.php
index 1b504422..c45fd01c 100644
--- a/src/views/colproperties.php
+++ b/src/views/colproperties.php
@@ -9,35 +9,6 @@
// Include application functions
require_once '../lib.inc.php';
-if (isset($_REQUEST['table'])) {
- $tableName = &$_REQUEST['table'];
-} elseif (isset($_REQUEST['view'])) {
- $tableName = &$_REQUEST['view'];
-} else {
- die($lang['strnotableprovided']);
-}
-
$colproperty_controller = new \PHPPgAdmin\Controller\ColPropertyController($container);
-$misc->printHeader($lang['strtables'] . ' - ' . $tableName);
-$misc->printBody();
-
-if (isset($_REQUEST['view'])) {
- $colproperty_controller->doDefault(null, false);
-} else {
- switch ($action) {
- case 'properties':
- if (isset($_POST['cancel'])) {
- $colproperty_controller->doDefault();
- } else {
- $colproperty_controller->doAlter();
- }
-
- break;
- default:
- $colproperty_controller->doDefault();
- break;
- }
-}
-
-$misc->printFooter();
+$colproperty_controller->render();
diff --git a/src/views/constraints.php b/src/views/constraints.php
index 53ba58d8..0bcd06ba 100644
--- a/src/views/constraints.php
+++ b/src/views/constraints.php
@@ -10,83 +10,4 @@
require_once '../lib.inc.php';
$constraint_controller = new \PHPPgAdmin\Controller\ConstraintController($container);
-
-$misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table'] . ' - ' . $lang['strconstraints'],
- "<script src=\"/js/indexes.js\" type=\"text/javascript\"></script>");
-
-if ($action == 'add_unique_key' || $action == 'save_add_unique_key'
- || $action == 'add_primary_key' || $action == 'save_add_primary_key'
- || $action == 'add_foreign_key' || $action == 'save_add_foreign_key') {
- echo "<body onload=\"init();\">";
-} else {
- $misc->printBody();
-}
-
-switch ($action) {
- case 'add_foreign_key':
- $constraint_controller->addForeignKey(1);
- break;
- case 'save_add_foreign_key':
- if (isset($_POST['cancel'])) {
- $constraint_controller->doDefault();
- } else {
- $constraint_controller->addForeignKey($_REQUEST['stage']);
- }
-
- break;
- case 'add_unique_key':
- $constraint_controller->addPrimaryOrUniqueKey('unique', true);
- break;
- case 'save_add_unique_key':
- if (isset($_POST['cancel'])) {
- $constraint_controller->doDefault();
- } else {
- $constraint_controller->addPrimaryOrUniqueKey('unique', false);
- }
-
- break;
- case 'add_primary_key':
- $constraint_controller->addPrimaryOrUniqueKey('primary', true);
- break;
- case 'save_add_primary_key':
- if (isset($_POST['cancel'])) {
- $constraint_controller->doDefault();
- } else {
- $constraint_controller->addPrimaryOrUniqueKey('primary', false);
- }
-
- break;
- case 'add_check':
- $constraint_controller->addCheck(true);
- break;
- case 'save_add_check':
- if (isset($_POST['cancel'])) {
- $constraint_controller->doDefault();
- } else {
- $constraint_controller->addCheck(false);
- }
-
- break;
- case 'save_create':
- $constraint_controller->doSaveCreate();
- break;
- case 'create':
- $constraint_controller->doCreate();
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $constraint_controller->doDrop(false);
- } else {
- $constraint_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $constraint_controller->doDrop(true);
- break;
- default:
- $constraint_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$constraint_controller->render();
diff --git a/src/views/conversions.php b/src/views/conversions.php
index e96b73a1..aa3cf70c 100644
--- a/src/views/conversions.php
+++ b/src/views/conversions.php
@@ -10,14 +10,4 @@
require_once '../lib.inc.php';
$conversion_controller = new \PHPPgAdmin\Controller\ConversionController($container);
-
-$misc->printHeader($lang['strconversions']);
-$misc->printBody();
-
-switch ($action) {
- default:
- $conversion_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$conversion_controller->render();
diff --git a/src/views/database.php b/src/views/database.php
index 05e0dbc7..021777f8 100755
--- a/src/views/database.php
+++ b/src/views/database.php
@@ -11,70 +11,4 @@ require_once '../lib.inc.php';
$database_controller = new \PHPPgAdmin\Controller\DatabaseController($container);
-if ($action == 'refresh_locks') {
- $database_controller->currentLocks(true);
-}
-
-if ($action == 'refresh_processes') {
- $database_controller->currentProcesses(true);
-}
-$scripts = '';
-/* normal flow */
-if ($action == 'locks' or $action == 'processes') {
- $scripts .= "<script src=\"/js/database.js\" type=\"text/javascript\"></script>";
-
- $refreshTime = $conf['ajax_refresh'] * 1000;
-
- $scripts .= "<script type=\"text/javascript\">\n";
- $scripts .= "var Database = {\n";
- $scripts .= "ajax_time_refresh: {$refreshTime},\n";
- $scripts .= "str_start: {text:'{$lang['strstart']}',icon: '" . $misc->icon('Execute') . "'},\n";
- $scripts .= "str_stop: {text:'{$lang['strstop']}',icon: '" . $misc->icon('Stop') . "'},\n";
- $scripts .= "load_icon: '" . $misc->icon('Loading') . "',\n";
- $scripts .= "server:'{$_REQUEST['server']}',\n";
- $scripts .= "dbname:'{$_REQUEST['database']}',\n";
- $scripts .= "action:'refresh_{$action}',\n";
- $scripts .= "errmsg: '" . str_replace("'", "\'", $lang['strconnectionfail']) . "'\n";
- $scripts .= "};\n";
- $scripts .= "</script>\n";
-}
-
-$misc->printHeader($lang['strdatabase'], $scripts);
-$misc->printBody();
-
-switch ($action) {
- case 'find':
- if (isset($_REQUEST['term'])) {
- $database_controller->doFind(false);
- } else {
- $database_controller->doFind(true);
- }
-
- break;
- case 'sql':
- $database_controller->doSQL();
- break;
- case 'variables':
- $database_controller->doVariables();
- break;
- case 'processes':
- $database_controller->doProcesses();
- break;
- case 'locks':
- $database_controller->doLocks();
- break;
- case 'export':
- $database_controller->doExport();
- break;
- case 'signal':
- $database_controller->doSignal();
- break;
- default:
- if (adminActions($action, 'database') === false) {
- $database_controller->doSQL();
- }
-
- break;
-}
-
-$misc->printFooter();
+$database_controller->render();
diff --git a/src/views/dataexport.php b/src/views/dataexport.php
index 0cb2aab2..03504c91 100644
--- a/src/views/dataexport.php
+++ b/src/views/dataexport.php
@@ -7,369 +7,8 @@
* $Id: dataexport.php,v 1.26 2007/07/12 19:26:22 xzilla Exp $
*/
-$extensions = [
- 'sql' => 'sql',
- 'copy' => 'sql',
- 'csv' => 'csv',
- 'tab' => 'txt',
- 'html' => 'html',
- 'xml' => 'xml',
-];
+require_once '../lib.inc.php';
-// Prevent timeouts on large exports (non-safe mode only)
-if (!ini_get('safe_mode')) {
- set_time_limit(0);
-}
+$dataexport_controller = new \PHPPgAdmin\Controller\DataExportController($container);
-// if (!isset($_REQUEST['table']) && !isset($_REQUEST['query']))
-// What must we do in this case? Maybe redirect to the homepage?
-
-// If format is set, then perform the export
-if (isset($_REQUEST['what'])) {
-
- // Include application functions
- $misc->setNoOutput(true);
- require_once '../lib.inc.php';
-
- switch ($_REQUEST['what']) {
- case 'dataonly':
- // Check to see if they have pg_dump set up and if they do, use that
- // instead of custom dump code
- if ($misc->isDumpEnabled()
- && ($_REQUEST['d_format'] == 'copy' || $_REQUEST['d_format'] == 'sql')) {
- include './dbexport.php';
- exit;
- } else {
- $format = $_REQUEST['d_format'];
- $oids = isset($_REQUEST['d_oids']);
- }
- break;
- case 'structureonly':
- // Check to see if they have pg_dump set up and if they do, use that
- // instead of custom dump code
- if ($misc->isDumpEnabled()) {
- include './dbexport.php';
- exit;
- } else {
- $clean = isset($_REQUEST['s_clean']);
- }
-
- break;
- case 'structureanddata':
- // Check to see if they have pg_dump set up and if they do, use that
- // instead of custom dump code
- if ($misc->isDumpEnabled()) {
- include './dbexport.php';
- exit;
- } else {
- $format = $_REQUEST['sd_format'];
- $clean = isset($_REQUEST['sd_clean']);
- $oids = isset($_REQUEST['sd_oids']);
- }
- break;
- }
-
- // Make it do a download, if necessary
- if ($_REQUEST['output'] == 'download') {
- // Set headers. MSIE is totally broken for SSL downloading, so
- // we need to have it download in-place as plain text
- if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && isset($_SERVER['HTTPS'])) {
- header('Content-Type: text/plain');
- } else {
- header('Content-Type: application/download');
-
- if (isset($extensions[$format])) {
- $ext = $extensions[$format];
- } else {
- $ext = 'txt';
- }
-
- header('Content-Disposition: attachment; filename=dump.' . $ext);
- }
- } else {
- header('Content-Type: text/plain');
- }
-
- if (isset($_REQUEST['query'])) {
- $_REQUEST['query'] = trim(urldecode($_REQUEST['query']));
- }
-
- // Set the schema search path
- if (isset($_REQUEST['search_path'])) {
- $data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path'])));
- }
-
- // Set up the dump transaction
- $status = $data->beginDump();
-
- // If the dump is not dataonly then dump the structure prefix
- if ($_REQUEST['what'] != 'dataonly') {
- echo $data->getTableDefPrefix($_REQUEST['table'], $clean);
- }
-
- // If the dump is not structureonly then dump the actual data
- if ($_REQUEST['what'] != 'structureonly') {
- // Get database encoding
- $dbEncoding = $data->getDatabaseEncoding();
-
- // Set fetch mode to NUM so that duplicate field names are properly returned
- $data->conn->setFetchMode(ADODB_FETCH_NUM);
-
- // Execute the query, if set, otherwise grab all rows from the table
- if (isset($_REQUEST['table'])) {
- $rs = $data->dumpRelation($_REQUEST['table'], $oids);
- } else {
- $rs = $data->conn->Execute($_REQUEST['query']);
- }
-
- if ($format == 'copy') {
- $data->fieldClean($_REQUEST['table']);
- echo "COPY \"{$_REQUEST['table']}\"";
- if ($oids) {
- echo " WITH OIDS";
- }
-
- echo " FROM stdin;\n";
- while (!$rs->EOF) {
- $first = true;
- while (list($k, $v) = each($rs->fields)) {
- // Escape value
- $v = $data->escapeBytea($v);
-
- // We add an extra escaping slash onto octal encoded characters
- $v = preg_replace('/\\\\([0-7]{3})/', '\\\\\1', $v);
- if ($first) {
- echo (is_null($v)) ? '\\N' : $v;
- $first = false;
- } else {
- echo "\t", (is_null($v)) ? '\\N' : $v;
- }
-
- }
- echo "\n";
- $rs->moveNext();
- }
- echo "\\.\n";
- } elseif ($format == 'html') {
- echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n";
- echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n";
- echo "<head>\r\n";
- echo "\t<title></title>\r\n";
- echo "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n";
- echo "</head>\r\n";
- echo "<body>\r\n";
- echo "<table class=\"phppgadmin\">\r\n";
- echo "\t<tr>\r\n";
- if (!$rs->EOF) {
- // Output header row
- $j = 0;
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
- if ($finfo->name == $data->id && !$oids) {
- continue;
- }
-
- echo "\t\t<th>", $misc->printVal($finfo->name, true), "</th>\r\n";
- }
- }
- echo "\t</tr>\r\n";
- while (!$rs->EOF) {
- echo "\t<tr>\r\n";
- $j = 0;
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
- if ($finfo->name == $data->id && !$oids) {
- continue;
- }
-
- echo "\t\t<td>", $misc->printVal($v, true, $finfo->type), "</td>\r\n";
- }
- echo "\t</tr>\r\n";
- $rs->moveNext();
- }
- echo "</table>\r\n";
- echo "</body>\r\n";
- echo "</html>\r\n";
- } elseif ($format == 'xml') {
- echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
- echo "<data>\n";
- if (!$rs->EOF) {
- // Output header row
- $j = 0;
- echo "\t<header>\n";
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
- $name = htmlspecialchars($finfo->name);
- $type = htmlspecialchars($finfo->type);
- echo "\t\t<column name=\"{$name}\" type=\"{$type}\" />\n";
- }
- echo "\t</header>\n";
- }
- echo "\t<records>\n";
- while (!$rs->EOF) {
- $j = 0;
- echo "\t\t<row>\n";
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
- $name = htmlspecialchars($finfo->name);
- if (!is_null($v)) {
- $v = htmlspecialchars($v);
- }
-
- echo "\t\t\t<column name=\"{$name}\"", (is_null($v) ? ' null="null"' : ''), ">{$v}</column>\n";
- }
- echo "\t\t</row>\n";
- $rs->moveNext();
- }
- echo "\t</records>\n";
- echo "</data>\n";
- } elseif ($format == 'sql') {
- $data->fieldClean($_REQUEST['table']);
- while (!$rs->EOF) {
- echo "INSERT INTO \"{$_REQUEST['table']}\" (";
- $first = true;
- $j = 0;
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
- $k = $finfo->name;
- // SQL (INSERT) format cannot handle oids
- // if ($k == $data->id) continue;
- // Output field
- $data->fieldClean($k);
- if ($first) {
- echo "\"{$k}\"";
- } else {
- echo ", \"{$k}\"";
- }
-
- if (!is_null($v)) {
- // Output value
- // addCSlashes converts all weird ASCII characters to octal representation,
- // EXCEPT the 'special' ones like \r \n \t, etc.
- $v = addCSlashes($v, "\0..\37\177..\377");
- // We add an extra escaping slash onto octal encoded characters
- $v = preg_replace('/\\\\([0-7]{3})/', '\\\1', $v);
- // Finally, escape all apostrophes
- $v = str_replace("'", "''", $v);
- }
- if ($first) {
- $values = (is_null($v) ? 'NULL' : "'{$v}'");
- $first = false;
- } else {
- $values .= ', ' . ((is_null($v) ? 'NULL' : "'{$v}'"));
- }
-
- }
- echo ") VALUES ({$values});\n";
- $rs->moveNext();
- }
- } else {
- switch ($format) {
- case 'tab':
- $sep = "\t";
- break;
- case 'csv':
- default:
- $sep = ',';
- break;
- }
- if (!$rs->EOF) {
- // Output header row
- $first = true;
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($k);
- $v = $finfo->name;
- if (!is_null($v)) {
- $v = str_replace('"', '""', $v);
- }
-
- if ($first) {
- echo "\"{$v}\"";
- $first = false;
- } else {
- echo "{$sep}\"{$v}\"";
- }
-
- }
- echo "\r\n";
- }
- while (!$rs->EOF) {
- $first = true;
- foreach ($rs->fields as $k => $v) {
- if (!is_null($v)) {
- $v = str_replace('"', '""', $v);
- }
-
- if ($first) {
- echo (is_null($v)) ? "\"\\N\"" : "\"{$v}\"";
- $first = false;
- } else {
- echo is_null($v) ? "{$sep}\"\\N\"" : "{$sep}\"{$v}\"";
- }
-
- }
- echo "\r\n";
- $rs->moveNext();
- }
- }
- }
-
- // If the dump is not dataonly then dump the structure suffix
- if ($_REQUEST['what'] != 'dataonly') {
- // Set fetch mode back to ASSOC for the table suffix to work
- $data->conn->setFetchMode(ADODB_FETCH_ASSOC);
- echo $data->getTableDefSuffix($_REQUEST['table']);
- }
-
- // Finish the dump transaction
- $status = $data->endDump();
-} else {
- // Include application functions
- require_once '../lib.inc.php';
-
- if (!isset($_REQUEST['query']) or empty($_REQUEST['query'])) {
- $_REQUEST['query'] = $_SESSION['sqlquery'];
- }
-
- $misc->printHeader($lang['strexport']);
- $misc->printBody();
- $misc->printTrail(isset($_REQUEST['subject']) ? $_REQUEST['subject'] : 'database');
- $misc->printTitle($lang['strexport']);
- if (isset($msg)) {
- $misc->printMsg($msg);
- }
-
- echo "<form action=\"/src/views/dataexport.php\" method=\"post\">\n";
- echo "<table>\n";
- echo "<tr><th class=\"data\">{$lang['strformat']}:</th><td><select name=\"d_format\">\n";
- // COPY and SQL require a table
- if (isset($_REQUEST['table'])) {
- echo "<option value=\"copy\">COPY</option>\n";
- echo "<option value=\"sql\">SQL</option>\n";
- }
- echo "<option value=\"csv\">CSV</option>\n";
- echo "<option value=\"tab\">{$lang['strtabbed']}</option>\n";
- echo "<option value=\"html\">XHTML</option>\n";
- echo "<option value=\"xml\">XML</option>\n";
- echo "</select></td></tr>";
- echo "</table>\n";
-
- echo "<h3>{$lang['stroptions']}</h3>\n";
- echo "<p><input type=\"radio\" id=\"output1\" name=\"output\" value=\"show\" checked=\"checked\" /><label for=\"output1\">{$lang['strshow']}</label>\n";
- echo "<br/><input type=\"radio\" id=\"output2\" name=\"output\" value=\"download\" /><label for=\"output2\">{$lang['strdownload']}</label></p>\n";
-
- echo "<p><input type=\"hidden\" name=\"action\" value=\"export\" />\n";
- echo "<input type=\"hidden\" name=\"what\" value=\"dataonly\" />\n";
- if (isset($_REQUEST['table'])) {
- echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
- }
- echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars(urlencode($_REQUEST['query'])), "\" />\n";
- if (isset($_REQUEST['search_path'])) {
- echo "<input type=\"hidden\" name=\"search_path\" value=\"", htmlspecialchars($_REQUEST['search_path']), "\" />\n";
- }
- echo $misc->form;
- echo "<input type=\"submit\" value=\"{$lang['strexport']}\" /></p>\n";
- echo "</form>\n";
-
- $misc->printFooter();
-}
+$dataexport_controller->render(); \ No newline at end of file
diff --git a/src/views/dataimport.php b/src/views/dataimport.php
index 3ac2207c..ecf4aecd 100644
--- a/src/views/dataimport.php
+++ b/src/views/dataimport.php
@@ -6,300 +6,8 @@
* $Id: dataimport.php,v 1.11 2007/01/22 16:33:01 soranzo Exp $
*/
-// Prevent timeouts on large exports (non-safe mode only)
-if (!ini_get('safe_mode')) {
- set_time_limit(0);
-}
-
-// Include application functions
require_once '../lib.inc.php';
-// Default state for XML parser
-$state = 'XML';
-$curr_col_name = null;
-$curr_col_val = null;
-$curr_col_null = false;
-$curr_row = [];
-
-/**
- * Open tag handler for XML import feature
- */
-function _startElement($parser, $name, $attrs) {
- global $data, $misc, $lang;
- global $state, $curr_row, $curr_col_name, $curr_col_val, $curr_col_null;
-
- switch ($name) {
- case 'DATA':
- if ($state != 'XML') {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- $state = 'DATA';
- break;
- case 'HEADER':
- if ($state != 'DATA') {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- $state = 'HEADER';
- break;
- case 'RECORDS':
- if ($state != 'READ_HEADER') {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- $state = 'RECORDS';
- break;
- case 'ROW':
- if ($state != 'RECORDS') {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- $state = 'ROW';
- $curr_row = [];
- break;
- case 'COLUMN':
- // We handle columns in rows
- if ($state == 'ROW') {
- $state = 'COLUMN';
- $curr_col_name = $attrs['NAME'];
- $curr_col_null = isset($attrs['NULL']);
- }
- // And we ignore columns in headers and fail in any other context
- elseif ($state != 'HEADER') {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- break;
- default:
- // An unrecognised tag means failure
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
-}
-
-/**
- * Close tag handler for XML import feature
- */
-function _endElement($parser, $name) {
- global $data, $misc, $lang;
- global $state, $curr_row, $curr_col_name, $curr_col_val, $curr_col_null;
-
- switch ($name) {
- case 'DATA':
- $state = 'READ_DATA';
- break;
- case 'HEADER':
- $state = 'READ_HEADER';
- break;
- case 'RECORDS':
- $state = 'READ_RECORDS';
- break;
- case 'ROW':
- // Build value map in order to insert row into table
- $fields = [];
- $vars = [];
- $nulls = [];
- $format = [];
- $types = [];
- $i = 0;
- foreach ($curr_row as $k => $v) {
- $fields[$i] = $k;
- // Check for nulls
- if ($v === null) {
- $nulls[$i] = 'on';
- }
-
- // Add to value array
- $vars[$i] = $v;
- // Format is always VALUE
- $format[$i] = 'VALUE';
- // Type is always text
- $types[$i] = 'text';
- $i++;
- }
- $status = $data->insertRow($_REQUEST['table'], $fields, $vars, $nulls, $format, $types);
- if ($status != 0) {
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- $curr_row = [];
- $state = 'RECORDS';
- break;
- case 'COLUMN':
- $curr_row[$curr_col_name] = ($curr_col_null ? null : $curr_col_val);
- $curr_col_name = null;
- $curr_col_val = null;
- $curr_col_null = false;
- $state = 'ROW';
- break;
- default:
- // An unrecognised tag means failure
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
-}
-
-/**
- * Character data handler for XML import feature
- */
-function _charHandler($parser, $cdata) {
- global $data, $misc, $lang;
- global $state, $curr_col_val;
-
- if ($state == 'COLUMN') {
- $curr_col_val .= $cdata;
- }
-}
-
-function loadNULLArray() {
- $array = [];
- if (isset($_POST['allowednulls'])) {
- foreach ($_POST['allowednulls'] as $null_char) {
- $array[] = $null_char;
- }
-
- }
- return $array;
-}
-
-function determineNull($field, $null_array) {
- return in_array($field, $null_array);
-}
-
-$misc->printHeader($lang['strimport']);
-$misc->printTrail('table');
-$misc->printTabs('table', 'import');
-
-// Check that file is specified and is an uploaded file
-if (isset($_FILES['source']) && is_uploaded_file($_FILES['source']['tmp_name']) && is_readable($_FILES['source']['tmp_name'])) {
-
- $fd = fopen($_FILES['source']['tmp_name'], 'r');
- // Check that file was opened successfully
- if ($fd !== false) {
- $null_array = loadNULLArray();
- $status = $data->beginTransaction();
- if ($status != 0) {
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
-
- // If format is set to 'auto', then determine format automatically from file name
- if ($_REQUEST['format'] == 'auto') {
- $extension = substr(strrchr($_FILES['source']['name'], '.'), 1);
- switch ($extension) {
- case 'csv':
- $_REQUEST['format'] = 'csv';
- break;
- case 'txt':
- $_REQUEST['format'] = 'tab';
- break;
- case 'xml':
- $_REQUEST['format'] = 'xml';
- break;
- default:
- $data->rollbackTransaction();
- $misc->printMsg($lang['strimporterror-fileformat']);
- exit;
- }
- }
-
- // Do different import technique depending on file format
- switch ($_REQUEST['format']) {
- case 'csv':
- case 'tab':
- // XXX: Length of CSV lines limited to 100k
- $csv_max_line = 100000;
- // Set delimiter to tabs or commas
- if ($_REQUEST['format'] == 'csv') {
- $csv_delimiter = ',';
- } else {
- $csv_delimiter = "\t";
- }
-
- // Get first line of field names
- $fields = fgetcsv($fd, $csv_max_line, $csv_delimiter);
- $row = 2; //We start on the line AFTER the field names
- while ($line = fgetcsv($fd, $csv_max_line, $csv_delimiter)) {
- // Build value map
- $t_fields = [];
- $vars = [];
- $nulls = [];
- $format = [];
- $types = [];
- $i = 0;
- foreach ($fields as $f) {
- // Check that there is a column
- if (!isset($line[$i])) {
- $misc->printMsg(sprintf($lang['strimporterrorline-badcolumnnum'], $row));
- exit;
- }
- $t_fields[$i] = $f;
-
- // Check for nulls
- if (determineNull($line[$i], $null_array)) {
- $nulls[$i] = 'on';
- }
- // Add to value array
- $vars[$i] = $line[$i];
- // Format is always VALUE
- $format[$i] = 'VALUE';
- // Type is always text
- $types[$i] = 'text';
- $i++;
- }
-
- $status = $data->insertRow($_REQUEST['table'], $t_fields, $vars, $nulls, $format, $types);
- if ($status != 0) {
- $data->rollbackTransaction();
- $misc->printMsg(sprintf($lang['strimporterrorline'], $row));
- exit;
- }
- $row++;
- }
- break;
- case 'xml':
- $parser = xml_parser_create();
- xml_set_element_handler($parser, '_startElement', '_endElement');
- xml_set_character_data_handler($parser, '_charHandler');
-
- while (!feof($fd)) {
- $line = fgets($fd, 4096);
- xml_parse($parser, $line);
- }
-
- xml_parser_free($parser);
- break;
- default:
- // Unknown type
- $data->rollbackTransaction();
- $misc->printMsg($lang['strinvalidparam']);
- exit;
- }
-
- $status = $data->endTransaction();
- if ($status != 0) {
- $misc->printMsg($lang['strimporterror']);
- exit;
- }
- fclose($fd);
-
- $misc->printMsg($lang['strfileimported']);
- } else {
- // File could not be opened
- $misc->printMsg($lang['strimporterror']);
- }
-} else {
- // Upload went wrong
- $misc->printMsg($lang['strimporterror-uploadedfile']);
-}
+$dataimport_controller = new \PHPPgAdmin\Controller\DataImportController($container);
-$misc->printFooter();
+$dataimport_controller->render(); \ No newline at end of file
diff --git a/src/views/dbexport.php b/src/views/dbexport.php
index 9edd025c..c6c6e7ad 100644
--- a/src/views/dbexport.php
+++ b/src/views/dbexport.php
@@ -6,148 +6,8 @@
* $Id: dbexport.php,v 1.22 2007/03/25 03:15:09 xzilla Exp $
*/
-// Prevent timeouts on large exports (non-safe mode only)
-if (!ini_get('safe_mode')) {
- set_time_limit(0);
-}
-
-// Include application functions
-$f_schema = $f_object = '';
require_once '../lib.inc.php';
-$misc->setNoOutput(true);
-
-// Are we doing a cluster-wide dump or just a per-database dump
-$dumpall = ($_REQUEST['subject'] == 'server');
-
-// Check that database dumps are enabled.
-if ($misc->isDumpEnabled($dumpall)) {
-
- $server_info = $misc->getServerInfo();
-
- // Get the path of the pg_dump/pg_dumpall executable
- $exe = $misc->escapeShellCmd($server_info[$dumpall ? 'pg_dumpall_path' : 'pg_dump_path']);
-
- // Obtain the pg_dump version number and check if the path is good
- $version = [];
- preg_match("/(\d+(?:\.\d+)?)(?:\.\d+)?.*$/", exec($exe . " --version"), $version);
-
- if (empty($version)) {
- if ($dumpall) {
- printf($lang['strbadpgdumpallpath'], $server_info['pg_dumpall_path']);
- } else {
- printf($lang['strbadpgdumppath'], $server_info['pg_dump_path']);
- }
-
- exit;
- }
-
- // Make it do a download, if necessary
- switch ($_REQUEST['output']) {
- case 'show':
- header('Content-Type: text/plain');
- break;
- case 'download':
- // Set headers. MSIE is totally broken for SSL downloading, so
- // we need to have it download in-place as plain text
- if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && isset($_SERVER['HTTPS'])) {
- header('Content-Type: text/plain');
- } else {
- header('Content-Type: application/download');
- header('Content-Disposition: attachment; filename=dump.sql');
- }
- break;
- case 'gzipped':
- // MSIE in SSL mode cannot do this - it should never get to this point
- header('Content-Type: application/download');
- header('Content-Disposition: attachment; filename=dump.sql.gz');
- break;
- }
-
- // Set environmental variables that pg_dump uses
- putenv('PGPASSWORD=' . $server_info['password']);
- putenv('PGUSER=' . $server_info['username']);
- $hostname = $server_info['host'];
- if ($hostname !== null && $hostname != '') {
- putenv('PGHOST=' . $hostname);
- }
- $port = $server_info['port'];
- if ($port !== null && $port != '') {
- putenv('PGPORT=' . $port);
- }
-
- // Build command for executing pg_dump. '-i' means ignore version differences.
- $cmd = $exe . " -i";
-
- // we are PG 7.4+, so we always have a schema
- if (isset($_REQUEST['schema'])) {
- $f_schema = $_REQUEST['schema'];
- $data->fieldClean($f_schema);
- }
-
- // Check for a specified table/view
- switch ($_REQUEST['subject']) {
- case 'schema':
- // This currently works for 8.2+ (due to the orthoganl -t -n issue introduced then)
- $cmd .= " -n " . $misc->escapeShellArg("\"{$f_schema}\"");
- break;
- case 'table':
- case 'view':
- $f_object = $_REQUEST[$_REQUEST['subject']];
- $data->fieldClean($f_object);
-
- // Starting in 8.2, -n and -t are orthagonal, so we now schema qualify
- // the table name in the -t argument and quote both identifiers
- if (((float) $version[1]) >= 8.2) {
- $cmd .= " -t " . $misc->escapeShellArg("\"{$f_schema}\".\"{$f_object}\"");
- } else {
- // If we are 7.4 or higher, assume they are using 7.4 pg_dump and
- // set dump schema as well. Also, mixed case dumping has been fixed
- // then..
- $cmd .= " -t " . $misc->escapeShellArg($f_object)
- . " -n " . $misc->escapeShellArg($f_schema);
- }
- }
-
- // Check for GZIP compression specified
- if ($_REQUEST['output'] == 'gzipped' && !$dumpall) {
- $cmd .= " -Z 9";
- }
-
- switch ($_REQUEST['what']) {
- case 'dataonly':
- $cmd .= ' -a';
- if ($_REQUEST['d_format'] == 'sql') {
- $cmd .= ' --inserts';
- } elseif (isset($_REQUEST['d_oids'])) {
- $cmd .= ' -o';
- }
-
- break;
- case 'structureonly':
- $cmd .= ' -s';
- if (isset($_REQUEST['s_clean'])) {
- $cmd .= ' -c';
- }
-
- break;
- case 'structureanddata':
- if ($_REQUEST['sd_format'] == 'sql') {
- $cmd .= ' --inserts';
- } elseif (isset($_REQUEST['sd_oids'])) {
- $cmd .= ' -o';
- }
-
- if (isset($_REQUEST['sd_clean'])) {
- $cmd .= ' -c';
- }
-
- break;
- }
- if (!$dumpall) {
- putenv('PGDATABASE=' . $_REQUEST['database']);
- }
+$dbexport_controller = new \PHPPgAdmin\Controller\DBExportController($container);
- // Execute command and return the output to the screen
- passthru($cmd);
-}
+$dbexport_controller->render(); \ No newline at end of file
diff --git a/src/views/display.php b/src/views/display.php
index 3db53807..31ef6f10 100644
--- a/src/views/display.php
+++ b/src/views/display.php
@@ -11,969 +11,11 @@
* $Id: display.php,v 1.68 2008/04/14 12:44:27 ioguix Exp $
*/
-// Prevent timeouts on large exports (non-safe mode only)
-if (!ini_get('safe_mode')) {
- set_time_limit(0);
-}
-
// Include application functions
if (!defined('BASE_PATH')) {
require_once '../lib.inc.php';
}
-global $conf, $lang;
-
-$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
-
-/**
- * Show confirmation of edit and perform actual update
- */
-function doEditRow($confirm, $msg = '') {
- global $data, $misc, $conf;
- global $lang;
-
- if (is_array($_REQUEST['key'])) {
- $key = $_REQUEST['key'];
- } else {
- $key = unserialize(urldecode($_REQUEST['key']));
- }
-
- if ($confirm) {
- $misc->printTrail($_REQUEST['subject']);
- $misc->printTitle($lang['streditrow']);
- $misc->printMsg($msg);
-
- $attrs = $data->getTableAttributes($_REQUEST['table']);
- $rs = $data->browseRow($_REQUEST['table'], $key);
-
- if (($conf['autocomplete'] != 'disable')) {
- $fksprops = $misc->getAutocompleteFKProperties($_REQUEST['table']);
- if ($fksprops !== false) {
- echo $fksprops['code'];
- }
-
- } else {
- $fksprops = false;
- }
-
- echo "<form action=\"/src/views/display.php\" method=\"post\" id=\"ac_form\">\n";
- $elements = 0;
- $error = true;
- if ($rs->recordCount() == 1 && $attrs->recordCount() > 0) {
- echo "<table>\n";
-
- // Output table header
- echo "<tr><th class=\"data\">{$lang['strcolumn']}</th><th class=\"data\">{$lang['strtype']}</th>";
- echo "<th class=\"data\">{$lang['strformat']}</th>\n";
- echo "<th class=\"data\">{$lang['strnull']}</th><th class=\"data\">{$lang['strvalue']}</th></tr>";
-
- $i = 0;
- while (!$attrs->EOF) {
-
- $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
- $id = (($i % 2) == 0 ? '1' : '2');
-
- // Initialise variables
- if (!isset($_REQUEST['format'][$attrs->fields['attname']])) {
- $_REQUEST['format'][$attrs->fields['attname']] = 'VALUE';
- }
-
- echo "<tr class=\"data{$id}\">\n";
- echo "<td style=\"white-space:nowrap;\">", $misc->printVal($attrs->fields['attname']), "</td>";
- echo "<td style=\"white-space:nowrap;\">\n";
- echo $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
- echo "<input type=\"hidden\" name=\"types[", htmlspecialchars($attrs->fields['attname']), "]\" value=\"",
- htmlspecialchars($attrs->fields['type']), "\" /></td>";
- $elements++;
- echo "<td style=\"white-space:nowrap;\">\n";
- echo "<select name=\"format[", htmlspecialchars($attrs->fields['attname']), "]\">\n";
- echo "<option value=\"VALUE\"", ($_REQUEST['format'][$attrs->fields['attname']] == 'VALUE') ? ' selected="selected"' : '', ">{$lang['strvalue']}</option>\n";
- echo "<option value=\"EXPRESSION\"", ($_REQUEST['format'][$attrs->fields['attname']] == 'EXPRESSION') ? ' selected="selected"' : '', ">{$lang['strexpression']}</option>\n";
- echo "</select>\n</td>\n";
- $elements++;
- echo "<td style=\"white-space:nowrap;\">";
- // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
- if (!$attrs->fields['attnotnull']) {
- // Set initial null values
- if ($_REQUEST['action'] == 'confeditrow' && $rs->fields[$attrs->fields['attname']] === null) {
- $_REQUEST['nulls'][$attrs->fields['attname']] = 'on';
- }
- echo "<label><span><input type=\"checkbox\" name=\"nulls[{$attrs->fields['attname']}]\"",
- isset($_REQUEST['nulls'][$attrs->fields['attname']]) ? ' checked="checked"' : '', " /></span></label></td>\n";
- $elements++;
- } else {
- echo "&nbsp;</td>";
- }
-
- echo "<td id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
-
- $extras = [];
-
- // If the column allows nulls, then we put a JavaScript action on the data field to unset the
- // NULL checkbox as soon as anything is entered in the field. We use the $elements variable to
- // keep track of which element offset we're up to. We can't refer to the null checkbox by name
- // as it contains '[' and ']' characters.
- if (!$attrs->fields['attnotnull']) {
- $extras['onChange'] = 'elements[' . ($elements - 1) . '].checked = false;';
- }
-
- if (($fksprops !== false) && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
- $extras['id'] = "attr_{$attrs->fields['attnum']}";
- $extras['autocomplete'] = 'off';
- }
-
- echo $data->printField("values[{$attrs->fields['attname']}]", $rs->fields[$attrs->fields['attname']], $attrs->fields['type'], $extras);
-
- echo "</td>";
- $elements++;
- echo "</tr>\n";
- $i++;
- $attrs->moveNext();
- }
- echo "</table>\n";
-
- $error = false;
- } elseif ($rs->recordCount() != 1) {
- echo "<p>{$lang['strrownotunique']}</p>\n";
- } else {
- echo "<p>{$lang['strinvalidparam']}</p>\n";
- }
-
- echo "<input type=\"hidden\" name=\"action\" value=\"editrow\" />\n";
- echo $misc->form;
- if (isset($_REQUEST['table'])) {
- echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
- }
-
- if (isset($_REQUEST['subject'])) {
- echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
- }
-
- if (isset($_REQUEST['query'])) {
- echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars($_REQUEST['query']), "\" />\n";
- }
-
- if (isset($_REQUEST['count'])) {
- echo "<input type=\"hidden\" name=\"count\" value=\"", htmlspecialchars($_REQUEST['count']), "\" />\n";
- }
-
- if (isset($_REQUEST['return'])) {
- echo "<input type=\"hidden\" name=\"return\" value=\"", htmlspecialchars($_REQUEST['return']), "\" />\n";
- }
-
- echo "<input type=\"hidden\" name=\"page\" value=\"", htmlspecialchars($_REQUEST['page']), "\" />\n";
- echo "<input type=\"hidden\" name=\"sortkey\" value=\"", htmlspecialchars($_REQUEST['sortkey']), "\" />\n";
- echo "<input type=\"hidden\" name=\"sortdir\" value=\"", htmlspecialchars($_REQUEST['sortdir']), "\" />\n";
- echo "<input type=\"hidden\" name=\"strings\" value=\"", htmlspecialchars($_REQUEST['strings']), "\" />\n";
- echo "<input type=\"hidden\" name=\"key\" value=\"", htmlspecialchars(urlencode(serialize($key))), "\" />\n";
- echo "<p>";
- if (!$error) {
- echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$lang['strsave']}\" />\n";
- }
-
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
-
- if ($fksprops !== false) {
- if ($conf['autocomplete'] != 'default off') {
- echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
- } else {
- echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
- }
-
- }
-
- echo "</p>\n";
- echo "</form>\n";
- } else {
- if (!isset($_POST['values'])) {
- $_POST['values'] = [];
- }
-
- if (!isset($_POST['nulls'])) {
- $_POST['nulls'] = [];
- }
-
- $status = $data->editRow($_POST['table'], $_POST['values'], $_POST['nulls'],
- $_POST['format'], $_POST['types'], $key);
- if ($status == 0) {
- doBrowse($lang['strrowupdated']);
- } elseif ($status == -2) {
- doEditRow(true, $lang['strrownotunique']);
- } else {
- doEditRow(true, $lang['strrowupdatedbad']);
- }
-
- }
-
-}
-
-/**
- * Show confirmation of drop and perform actual drop
- */
-function doDelRow($confirm) {
- global $data, $misc;
- global $lang;
-
- if ($confirm) {
- $misc->printTrail($_REQUEST['subject']);
- $misc->printTitle($lang['strdeleterow']);
-
- $rs = $data->browseRow($_REQUEST['table'], $_REQUEST['key']);
-
- echo "<form action=\"/src/views/display.php\" method=\"post\">\n";
- echo $misc->form;
-
- if ($rs->recordCount() == 1) {
- echo "<p>{$lang['strconfdeleterow']}</p>\n";
-
- $fkinfo = [];
- echo "<table><tr>";
- printTableHeaderCells($rs, false, true);
- echo "</tr>";
- echo "<tr class=\"data1\">\n";
- printTableRowCells($rs, $fkinfo, true);
- echo "</tr>\n";
- echo "</table>\n";
- echo "<br />\n";
-
- echo "<input type=\"hidden\" name=\"action\" value=\"delrow\" />\n";
- echo "<input type=\"submit\" name=\"yes\" value=\"{$lang['stryes']}\" />\n";
- echo "<input type=\"submit\" name=\"no\" value=\"{$lang['strno']}\" />\n";
- } elseif ($rs->recordCount() != 1) {
- echo "<p>{$lang['strrownotunique']}</p>\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
- } else {
- echo "<p>{$lang['strinvalidparam']}</p>\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
- }
- if (isset($_REQUEST['table'])) {
- echo "<input type=\"hidden\" name=\"table\" value=\"", htmlspecialchars($_REQUEST['table']), "\" />\n";
- }
-
- if (isset($_REQUEST['subject'])) {
- echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
- }
-
- if (isset($_REQUEST['query'])) {
- echo "<input type=\"hidden\" name=\"query\" value=\"", htmlspecialchars($_REQUEST['query']), "\" />\n";
- }
-
- if (isset($_REQUEST['count'])) {
- echo "<input type=\"hidden\" name=\"count\" value=\"", htmlspecialchars($_REQUEST['count']), "\" />\n";
- }
-
- if (isset($_REQUEST['return'])) {
- echo "<input type=\"hidden\" name=\"return\" value=\"", htmlspecialchars($_REQUEST['return']), "\" />\n";
- }
-
- echo "<input type=\"hidden\" name=\"page\" value=\"", htmlspecialchars($_REQUEST['page']), "\" />\n";
- echo "<input type=\"hidden\" name=\"sortkey\" value=\"", htmlspecialchars($_REQUEST['sortkey']), "\" />\n";
- echo "<input type=\"hidden\" name=\"sortdir\" value=\"", htmlspecialchars($_REQUEST['sortdir']), "\" />\n";
- echo "<input type=\"hidden\" name=\"strings\" value=\"", htmlspecialchars($_REQUEST['strings']), "\" />\n";
- echo "<input type=\"hidden\" name=\"key\" value=\"", htmlspecialchars(urlencode(serialize($_REQUEST['key']))), "\" />\n";
- echo "</form>\n";
- } else {
- $status = $data->deleteRow($_POST['table'], unserialize(urldecode($_POST['key'])));
- if ($status == 0) {
- doBrowse($lang['strrowdeleted']);
- } elseif ($status == -2) {
- doBrowse($lang['strrownotunique']);
- } else {
- doBrowse($lang['strrowdeletedbad']);
- }
-
- }
-
-}
-
-/* build & return the FK information data structure
- * used when deciding if a field should have a FK link or not*/
-function &getFKInfo() {
- global $data, $misc, $lang;
-
- // Get the foreign key(s) information from the current table
- $fkey_information = ['byconstr' => [], 'byfield' => []];
-
- if (isset($_REQUEST['table'])) {
- $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
- if ($constraints->recordCount() > 0) {
-
- $fkey_information['common_url'] = $misc->getHREF('schema') . '&amp;subject=table';
-
- /* build the FK constraints data structure */
- while (!$constraints->EOF) {
- $constr = &$constraints->fields;
- if ($constr['contype'] == 'f') {
-
- if (!isset($fkey_information['byconstr'][$constr['conid']])) {
- $fkey_information['byconstr'][$constr['conid']] = [
- 'url_data' => 'table=' . urlencode($constr['f_table']) . '&amp;schema=' . urlencode($constr['f_schema']),
- 'fkeys' => [],
- 'consrc' => $constr['consrc'],
- ];
- }
-
- $fkey_information['byconstr'][$constr['conid']]['fkeys'][$constr['p_field']] = $constr['f_field'];
-
- if (!isset($fkey_information['byfield'][$constr['p_field']])) {
- $fkey_information['byfield'][$constr['p_field']] = [];
- }
-
- $fkey_information['byfield'][$constr['p_field']][] = $constr['conid'];
- }
- $constraints->moveNext();
- }
- }
- }
-
- return $fkey_information;
-}
-
-/* Print table header cells
- * @param $args - associative array for sort link parameters
- * */
-function printTableHeaderCells(&$rs, $args, $withOid) {
- global $misc, $data, $conf;
- $j = 0;
-
- foreach ($rs->fields as $k => $v) {
-
- if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
- $j++;
- continue;
- }
- $finfo = $rs->fetchField($j);
-
- if ($args === false) {
- echo "<th class=\"data\">", $misc->printVal($finfo->name), "</th>\n";
- } else {
- $args['page'] = $_REQUEST['page'];
- $args['sortkey'] = $j + 1;
- // Sort direction opposite to current direction, unless it's currently ''
- $args['sortdir'] = (
- $_REQUEST['sortdir'] == 'asc'
- and $_REQUEST['sortkey'] == ($j + 1)
- ) ? 'desc' : 'asc';
-
- $sortLink = http_build_query($args);
-
- echo "<th class=\"data\"><a href=\"?{$sortLink}\">"
- , $misc->printVal($finfo->name);
- if ($_REQUEST['sortkey'] == ($j + 1)) {
- if ($_REQUEST['sortdir'] == 'asc') {
- echo '<img src="' . $misc->icon('RaiseArgument') . '" alt="asc">';
- } else {
- echo '<img src="' . $misc->icon('LowerArgument') . '" alt="desc">';
- }
-
- }
- echo "</a></th>\n";
- }
- $j++;
- }
-
- reset($rs->fields);
-}
-
-/* Print data-row cells */
-function printTableRowCells(&$rs, &$fkey_information, $withOid) {
- global $data, $misc, $conf;
- $j = 0;
-
- if (!isset($_REQUEST['strings'])) {
- $_REQUEST['strings'] = 'collapsed';
- }
-
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($j++);
-
- if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
- continue;
- } elseif ($v !== null && $v == '') {
- echo "<td>&nbsp;</td>";
- } else {
- echo "<td style=\"white-space:nowrap;\">";
-
- if (($v !== null) && isset($fkey_information['byfield'][$k])) {
- foreach ($fkey_information['byfield'][$k] as $conid) {
-
- $query_params = $fkey_information['byconstr'][$conid]['url_data'];
-
- foreach ($fkey_information['byconstr'][$conid]['fkeys'] as $p_field => $f_field) {
- $query_params .= '&amp;' . urlencode("fkey[{$f_field}]") . '=' . urlencode($rs->fields[$p_field]);
- }
-
- /* $fkey_information['common_url'] is already urlencoded */
- $query_params .= '&amp;' . $fkey_information['common_url'];
- echo "<div style=\"display:inline-block;\">";
- echo "<a class=\"fk fk_" . htmlentities($conid, ENT_QUOTES, 'UTF-8') . "\" href=\"display.php?{$query_params}\">";
- echo "<img src=\"" . $misc->icon('ForeignKey') . "\" style=\"vertical-align:middle;\" alt=\"[fk]\" title=\""
- . htmlentities($fkey_information['byconstr'][$conid]['consrc'], ENT_QUOTES, 'UTF-8')
- . "\" />";
- echo "</a>";
- echo "</div>";
- }
- echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed'), 'class' => 'fk_value']);
- } else {
- echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed')]);
- }
- echo "</td>";
- }
- }
-}
-
-/* Print the FK row, used in ajax requests */
-function doBrowseFK() {
- global $data, $misc, $lang;
-
- $ops = [];
- foreach ($_REQUEST['fkey'] as $x => $y) {
- $ops[$x] = '=';
- }
- $query = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
- $_REQUEST['query'] = $query;
-
- $fkinfo = &getFKInfo();
-
- $max_pages = 1;
- // Retrieve page from query. $max_pages is returned by reference.
- $rs = $data->browseQuery('SELECT', $_REQUEST['table'], $_REQUEST['query'],
- null, null, 1, 1, $max_pages);
-
- echo "<a href=\"\" style=\"display:table-cell;\" class=\"fk_delete\"><img alt=\"[delete]\" src=\"" . $misc->icon('Delete') . "\" /></a>\n";
- echo "<div style=\"display:table-cell;\">";
-
- if (is_object($rs) && $rs->recordCount() > 0) {
- /* we are browsing a referenced table here
- * we should show OID if show_oids is true
- * so we give true to withOid in functions bellow
- * as 3rd paramter */
-
- echo "<table><tr>";
- printTableHeaderCells($rs, false, true);
- echo "</tr>";
- echo "<tr class=\"data1\">\n";
- printTableRowCells($rs, $fkinfo, true);
- echo "</tr>\n";
- echo "</table>\n";
- } else {
- echo $lang['strnodata'];
- }
-
- echo "</div>";
-
- exit;
-}
-
-/**
- * Displays requested data
- */
-function doBrowse($msg = '') {
- global $data, $conf, $misc, $lang, $plugin_manager;
-
- $save_history = false;
- // If current page is not set, default to first page
- if (!isset($_REQUEST['page'])) {
- $_REQUEST['page'] = 1;
- }
-
- if (!isset($_REQUEST['nohistory'])) {
- $save_history = true;
- }
-
- if (isset($_REQUEST['subject'])) {
- $subject = $_REQUEST['subject'];
- if (isset($_REQUEST[$subject])) {
- $object = $_REQUEST[$subject];
- }
-
- } else {
- $subject = '';
- }
-
- $misc->printTrail(isset($subject) ? $subject : 'database');
- $misc->printTabs($subject, 'browse');
-
- /* This code is used when browsing FK in pure-xHTML (without js) */
- if (isset($_REQUEST['fkey'])) {
- $ops = [];
- foreach ($_REQUEST['fkey'] as $x => $y) {
- $ops[$x] = '=';
- }
- $query = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
- $_REQUEST['query'] = $query;
- }
-
- if (isset($object)) {
- if (isset($_REQUEST['query'])) {
- $_SESSION['sqlquery'] = $_REQUEST['query'];
- $misc->printTitle($lang['strselect']);
- $type = 'SELECT';
- } else {
- $type = 'TABLE';
- }
- } else {
- $misc->printTitle($lang['strqueryresults']);
- /*we comes from sql.php, $_SESSION['sqlquery'] has been set there */
- $type = 'QUERY';
- }
-
- $misc->printMsg($msg);
-
- // If 'sortkey' is not set, default to ''
- if (!isset($_REQUEST['sortkey'])) {
- $_REQUEST['sortkey'] = '';
- }
-
- // If 'sortdir' is not set, default to ''
- if (!isset($_REQUEST['sortdir'])) {
- $_REQUEST['sortdir'] = '';
- }
-
- // If 'strings' is not set, default to collapsed
- if (!isset($_REQUEST['strings'])) {
- $_REQUEST['strings'] = 'collapsed';
- }
-
- // Fetch unique row identifier, if this is a table browse request.
- if (isset($object)) {
- $key = $data->getRowIdentifier($object);
- } else {
- $key = [];
- }
-
- // Set the schema search path
- if (isset($_REQUEST['search_path'])) {
- if ($data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path']))) != 0) {
- return;
- }
- }
-
- // Retrieve page from query. $max_pages is returned by reference.
- $rs = $data->browseQuery($type,
- isset($object) ? $object : null,
- isset($_SESSION['sqlquery']) ? $_SESSION['sqlquery'] : null,
- $_REQUEST['sortkey'], $_REQUEST['sortdir'], $_REQUEST['page'],
- $conf['max_rows'], $max_pages);
-
- $fkey_information = &getFKInfo();
-
- // Build strings for GETs in array
- $_gets = [
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- ];
-
- if (isset($_REQUEST['schema'])) {
- $_gets['schema'] = $_REQUEST['schema'];
- }
-
- if (isset($object)) {
- $_gets[$subject] = $object;
- }
-
- if (isset($subject)) {
- $_gets['subject'] = $subject;
- }
-
- if (isset($_REQUEST['query'])) {
- $_gets['query'] = $_REQUEST['query'];
- }
-
- if (isset($_REQUEST['count'])) {
- $_gets['count'] = $_REQUEST['count'];
- }
-
- if (isset($_REQUEST['return'])) {
- $_gets['return'] = $_REQUEST['return'];
- }
-
- if (isset($_REQUEST['search_path'])) {
- $_gets['search_path'] = $_REQUEST['search_path'];
- }
-
- if (isset($_REQUEST['table'])) {
- $_gets['table'] = $_REQUEST['table'];
- }
-
- if (isset($_REQUEST['sortkey'])) {
- $_gets['sortkey'] = $_REQUEST['sortkey'];
- }
-
- if (isset($_REQUEST['sortdir'])) {
- $_gets['sortdir'] = $_REQUEST['sortdir'];
- }
-
- if (isset($_REQUEST['nohistory'])) {
- $_gets['nohistory'] = $_REQUEST['nohistory'];
- }
-
- $_gets['strings'] = $_REQUEST['strings'];
-
- if ($save_history && is_object($rs) && ($type == 'QUERY')) //{
- {
- $misc->saveScriptHistory($_REQUEST['query']);
- }
-
- echo '<form method="POST" action="' . $_SERVER['REQUEST_URI'] . '"><textarea width="90%" name="query" rows="5" cols="100" resizable="true">';
- if (isset($_REQUEST['query'])) {
- $query = $_REQUEST['query'];
- } else {
- $query = "SELECT * FROM {$_REQUEST['schema']}";
- if ($_REQUEST['subject'] == 'view') {
- $query = "{$query}.{$_REQUEST['view']};";
- } else {
- $query = "{$query}.{$_REQUEST['table']};";
- }
- }
- //$query = isset($_REQUEST['query'])? $_REQUEST['query'] : "select * from {$_REQUEST['schema']}.{$_REQUEST['table']};";
- echo $query;
- echo '</textarea><br><input type="submit"/></form>';
-
- if (is_object($rs) && $rs->recordCount() > 0) {
- // Show page navigation
- $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
-
- echo "<table id=\"data\">\n<tr>";
-
- // Check that the key is actually in the result set. This can occur for select
- // operations where the key fields aren't part of the select. XXX: We should
- // be able to support this, somehow.
- foreach ($key as $v) {
- // If a key column is not found in the record set, then we
- // can't use the key.
- if (!in_array($v, array_keys($rs->fields))) {
- $key = [];
- break;
- }
- }
-
- $buttons = [
- 'edit' => [
- 'content' => $lang['stredit'],
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => array_merge([
- 'action' => 'confeditrow',
- 'strings' => $_REQUEST['strings'],
- 'page' => $_REQUEST['page'],
- ], $_gets),
- ],
- ],
- ],
- 'delete' => [
- 'content' => $lang['strdelete'],
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => array_merge([
- 'action' => 'confdelrow',
- 'strings' => $_REQUEST['strings'],
- 'page' => $_REQUEST['page'],
- ], $_gets),
- ],
- ],
- ],
- ];
- $actions = [
- 'actionbuttons' => &$buttons,
- 'place' => 'display-browse',
- ];
- $plugin_manager->do_hook('actionbuttons', $actions);
-
- foreach (array_keys($actions['actionbuttons']) as $action) {
- $actions['actionbuttons'][$action]['attr']['href']['urlvars'] = array_merge(
- $actions['actionbuttons'][$action]['attr']['href']['urlvars'],
- $_gets
- );
- }
-
- $edit_params = isset($actions['actionbuttons']['edit']) ?
- $actions['actionbuttons']['edit'] : [];
- $delete_params = isset($actions['actionbuttons']['delete']) ?
- $actions['actionbuttons']['delete'] : [];
-
- // Display edit and delete actions if we have a key
- $colspan = count($buttons);
- if ($colspan > 0 and count($key) > 0) {
- echo "<th colspan=\"{$colspan}\" class=\"data\">{$lang['stractions']}</th>\n";
- }
-
- /* we show OIDs only if we are in TABLE or SELECT type browsing */
- printTableHeaderCells($rs, $_gets, isset($object));
-
- echo "</tr>\n";
-
- $i = 0;
- reset($rs->fields);
- while (!$rs->EOF) {
- $id = (($i % 2) == 0 ? '1' : '2');
- echo "<tr class=\"data{$id}\">\n";
- // Display edit and delete links if we have a key
- if ($colspan > 0 and count($key) > 0) {
- $keys_array = [];
- $has_nulls = false;
- foreach ($key as $v) {
- if ($rs->fields[$v] === null) {
- $has_nulls = true;
- break;
- }
- $keys_array["key[{$v}]"] = $rs->fields[$v];
- }
- if ($has_nulls) {
- echo "<td colspan=\"{$colspan}\">&nbsp;</td>\n";
- } else {
-
- if (isset($actions['actionbuttons']['edit'])) {
- $actions['actionbuttons']['edit'] = $edit_params;
- $actions['actionbuttons']['edit']['attr']['href']['urlvars'] = array_merge(
- $actions['actionbuttons']['edit']['attr']['href']['urlvars'],
- $keys_array
- );
- }
-
- if (isset($actions['actionbuttons']['delete'])) {
- $actions['actionbuttons']['delete'] = $delete_params;
- $actions['actionbuttons']['delete']['attr']['href']['urlvars'] = array_merge(
- $actions['actionbuttons']['delete']['attr']['href']['urlvars'],
- $keys_array
- );
- }
-
- foreach ($actions['actionbuttons'] as $action) {
- echo "<td class=\"opbutton{$id}\">";
- $misc->printLink($action);
- echo "</td>\n";
- }
- }
- }
-
- print printTableRowCells($rs, $fkey_information, isset($object));
-
- echo "</tr>\n";
- $rs->moveNext();
- $i++;
- }
- echo "</table>\n";
-
- echo "<p>", $rs->recordCount(), " {$lang['strrows']}</p>\n";
- // Show page navigation
- $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
- } else {
- echo "<p>{$lang['strnodata']}</p>\n";
- }
-
- // Navigation links
- $navlinks = [];
-
- $fields = [
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- ];
-
- if (isset($_REQUEST['schema'])) {
- $fields['schema'] = $_REQUEST['schema'];
- }
-
- // Return
- if (isset($_REQUEST['return'])) {
- $urlvars = $misc->getSubjectParams($_REQUEST['return']);
-
- $navlinks['back'] = [
- 'attr' => [
- 'href' => [
- 'url' => $urlvars['url'],
- 'urlvars' => $urlvars['params'],
- ],
- ],
- 'content' => $lang['strback'],
- ];
- }
-
- // Edit SQL link
- if ($type == 'QUERY') {
- $navlinks['edit'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'database.php',
- 'urlvars' => array_merge($fields, [
- 'action' => 'sql',
- 'paginate' => 'on',
- ]),
- ],
- ],
- 'content' => $lang['streditsql'],
- ];
- }
-
- // Expand/Collapse
- if ($_REQUEST['strings'] == 'expanded') {
- $navlinks['collapse'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => array_merge(
- $_gets,
- [
- 'strings' => 'collapsed',
- 'page' => $_REQUEST['page'],
- ]),
- ],
- ],
- 'content' => $lang['strcollapse'],
- ];
- } else {
- $navlinks['collapse'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => array_merge(
- $_gets,
- [
- 'strings' => 'expanded',
- 'page' => $_REQUEST['page'],
- ]),
- ],
- ],
- 'content' => $lang['strexpand'],
- ];
- }
-
- // Create view and download
- if (isset($_REQUEST['query']) && isset($rs) && is_object($rs) && $rs->recordCount() > 0) {
-
- // Report views don't set a schema, so we need to disable create view in that case
- if (isset($_REQUEST['schema'])) {
-
- $navlinks['createview'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'views.php',
- 'urlvars' => array_merge($fields, [
- 'action' => 'create',
- 'formDefinition' => $_REQUEST['query'],
- ]),
- ],
- ],
- 'content' => $lang['strcreateview'],
- ];
- }
-
- $urlvars = [];
- if (isset($_REQUEST['search_path'])) {
- $urlvars['search_path'] = $_REQUEST['search_path'];
- }
-
- $navlinks['download'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'dataexport.php',
- 'urlvars' => array_merge($fields, $urlvars),
- ],
- ],
- 'content' => $lang['strdownload'],
- ];
- }
-
- // Insert
- if (isset($object) && (isset($subject) && $subject == 'table')) {
- $navlinks['insert'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'tables.php',
- 'urlvars' => array_merge($fields, [
- 'action' => 'confinsertrow',
- 'table' => $object,
- ]),
- ],
- ],
- 'content' => $lang['strinsert'],
- ];
- }
-
- // Refresh
- $navlinks['refresh'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'display.php',
- 'urlvars' => array_merge(
- $_gets,
- [
- 'strings' => $_REQUEST['strings'],
- 'page' => $_REQUEST['page'],
- ]),
- ],
- ],
- 'content' => $lang['strrefresh'],
- ];
-
- $misc->printNavLinks($navlinks, 'display-browse', get_defined_vars());
-}
-
-/* shortcuts: this function exit the script for ajax purpose */
-if ($action == 'dobrowsefk') {
- doBrowseFK();
-}
-
-$scripts = "<script src=\"/js/display.js\" type=\"text/javascript\"></script>";
-
-$scripts .= "<script type=\"text/javascript\">\n";
-$scripts .= "var Display = {\n";
-$scripts .= "errmsg: '" . str_replace("'", "\'", $lang['strconnectionfail']) . "'\n";
-$scripts .= "};\n";
-$scripts .= "</script>\n";
-
-// Set the title based on the subject of the request
-if (isset($_REQUEST['subject']) && isset($_REQUEST[$_REQUEST['subject']])) {
- if ($_REQUEST['subject'] == 'table') {
- $misc->printHeader(
- $lang['strtables'] . ': ' . $_REQUEST[$_REQUEST['subject']],
- $scripts
- );
- } else if ($_REQUEST['subject'] == 'view') {
- $misc->printHeader(
- $lang['strviews'] . ': ' . $_REQUEST[$_REQUEST['subject']],
- $scripts
- );
- } else if ($_REQUEST['subject'] == 'column') {
- $misc->printHeader(
- $lang['strcolumn'] . ': ' . $_REQUEST[$_REQUEST['subject']],
- $scripts
- );
- }
-} else {
- $misc->printHeader($lang['strqueryresults']);
-}
-
-$misc->printBody();
-
-switch ($action) {
- case 'editrow':
- if (isset($_POST['save'])) {
- doEditRow(false);
- } else {
- doBrowse();
- }
-
- break;
- case 'confeditrow':
- doEditRow(true);
- break;
- case 'delrow':
- if (isset($_POST['yes'])) {
- doDelRow(false);
- } else {
- doBrowse();
- }
-
- break;
- case 'confdelrow':
- doDelRow(true);
- break;
- default:
- doBrowse();
- break;
-}
+$display_controller = new \PHPPgAdmin\Controller\DisplayController($container);
-$misc->printFooter();
+$display_controller->render();
diff --git a/src/views/domains.php b/src/views/domains.php
index 809356f6..73f977bd 100644
--- a/src/views/domains.php
+++ b/src/views/domains.php
@@ -11,71 +11,4 @@ require_once '../lib.inc.php';
$domain_controller = new \PHPPgAdmin\Controller\DomainController($container);
-$misc->printHeader($lang['strdomains']);
-$misc->printBody();
-
-switch ($action) {
- case 'add_check':
- $domain_controller->addCheck(true);
- break;
- case 'save_add_check':
- if (isset($_POST['cancel'])) {
- $domain_controller->doProperties();
- } else {
- $domain_controller->addCheck(false);
- }
-
- break;
- case 'drop_con':
- if (isset($_POST['drop'])) {
- $domain_controller->doDropConstraint(false);
- } else {
- $domain_controller->doProperties();
- }
-
- break;
- case 'confirm_drop_con':
- $domain_controller->doDropConstraint(true);
- break;
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $domain_controller->doDefault();
- } else {
- $domain_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- $domain_controller->doCreate();
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $domain_controller->doDrop(false);
- } else {
- $domain_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $domain_controller->doDrop(true);
- break;
- case 'save_alter':
- if (isset($_POST['alter'])) {
- $domain_controller->doSaveAlter();
- } else {
- $domain_controller->doProperties();
- }
-
- break;
- case 'alter':
- $domain_controller->doAlter();
- break;
- case 'properties':
- $domain_controller->doProperties();
- break;
- default:
- $domain_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$domain_controller->render();
diff --git a/src/views/fulltext.php b/src/views/fulltext.php
index 16959206..9392e06c 100644
--- a/src/views/fulltext.php
+++ b/src/views/fulltext.php
@@ -10,104 +10,4 @@
require_once '../lib.inc.php';
$fulltext_controller = new \PHPPgAdmin\Controller\FulltextController($container);
-
-$misc->printHeader($lang['strschemas']);
-$misc->printBody();
-
-if (isset($_POST['cancel'])) {
- if (isset($_POST['prev_action'])) {
- $action = $_POST['prev_action'];
- } else {
- $action = '';
- }
-}
-
-switch ($action) {
- case 'createconfig':
- if (isset($_POST['create'])) {
- $fulltext_controller->doSaveCreateConfig();
- } else {
- $fulltext_controller->doCreateConfig();
- }
-
- break;
- case 'alterconfig':
- if (isset($_POST['alter'])) {
- $fulltext_controller->doSaveAlterConfig();
- } else {
- $fulltext_controller->doAlterConfig();
- }
-
- break;
- case 'dropconfig':
- if (isset($_POST['drop'])) {
- $fulltext_controller->doDropConfig(false);
- } else {
- $fulltext_controller->doDropConfig(true);
- }
-
- break;
- case 'viewconfig':
- $fulltext_controller->doViewConfig($_REQUEST['ftscfg']);
- break;
- case 'viewparsers':
- $fulltext_controller->doViewParsers();
- break;
- case 'viewdicts':
- $fulltext_controller->doViewDicts();
- break;
- case 'createdict':
- if (isset($_POST['create'])) {
- $fulltext_controller->doSaveCreateDict();
- } else {
- doCreateDict();
- }
-
- break;
- case 'alterdict':
- if (isset($_POST['alter'])) {
- $fulltext_controller->doSaveAlterDict();
- } else {
- $fulltext_controller->doAlterDict();
- }
-
- break;
- case 'dropdict':
- if (isset($_POST['drop'])) {
- $fulltext_controller->doDropDict(false);
- } else {
- $fulltext_controller->doDropDict(true);
- }
-
- break;
- case 'dropmapping':
- if (isset($_POST['drop'])) {
- $fulltext_controller->doDropMapping(false);
- } else {
- $fulltext_controller->doDropMapping(true);
- }
-
- break;
- case 'altermapping':
- if (isset($_POST['alter'])) {
- $fulltext_controller->doSaveAlterMapping();
- } else {
- $fulltext_controller->doAlterMapping();
- }
-
- break;
- case 'addmapping':
- if (isset($_POST['add'])) {
- $fulltext_controller->doSaveAddMapping();
- } else {
- $fulltext_controller->doAddMapping();
- }
-
- break;
-
- default:
- $fulltext_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$fulltext_controller->render();
diff --git a/src/views/functions.php b/src/views/functions.php
index 20b85f09..a372ebdc 100644
--- a/src/views/functions.php
+++ b/src/views/functions.php
@@ -11,49 +11,4 @@ require_once '../lib.inc.php';
$function_controller = new \PHPPgAdmin\Controller\FunctionController($container);
-$misc->printHeader($lang['strfunctions']);
-$misc->printBody();
-
-switch ($action) {
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $function_controller->doDefault();
- } else {
- $function_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- $function_controller->doCreate();
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $function_controller->doDrop(false);
- } else {
- $function_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $function_controller->doDrop(true);
- break;
- case 'save_edit':
- if (isset($_POST['cancel'])) {
- $function_controller->doDefault();
- } else {
- $function_controller->doSaveEdit();
- }
-
- break;
- case 'edit':
- $function_controller->doEdit();
- break;
- case 'properties':
- $function_controller->doProperties();
- break;
- default:
- $function_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$function_controller->render();
diff --git a/src/views/indexes.php b/src/views/indexes.php
index 6677cf70..6705f640 100644
--- a/src/views/indexes.php
+++ b/src/views/indexes.php
@@ -11,54 +11,4 @@ require_once '../lib.inc.php';
$index_controller = new \PHPPgAdmin\Controller\IndexController($container);
-$misc->printHeader($lang['strindexes'], "<script src=\"/js/indexes.js\" type=\"text/javascript\"></script>");
-
-if ($action == 'create_index' || $action == 'save_create_index') {
- echo "<body onload=\"init();\">";
-} else {
- $misc->printBody();
-}
-
-switch ($action) {
- case 'cluster_index':
- if (isset($_POST['cluster'])) {
- $index_controller->doClusterIndex(false);
- } else {
- $index_controller->doDefault();
- }
-
- break;
- case 'confirm_cluster_index':
- $index_controller->doClusterIndex(true);
- break;
- case 'reindex':
- $index_controller->doReindex();
- break;
- case 'save_create_index':
- if (isset($_POST['cancel'])) {
- $index_controller->doDefault();
- } else {
- $index_controller->doSaveCreateIndex();
- }
-
- break;
- case 'create_index':
- $index_controller->doCreateIndex();
- break;
- case 'drop_index':
- if (isset($_POST['drop'])) {
- $index_controller->doDropIndex(false);
- } else {
- $index_controller->doDefault();
- }
-
- break;
- case 'confirm_drop_index':
- $index_controller->doDropIndex(true);
- break;
- default:
- $index_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$index_controller->render();
diff --git a/src/views/intro.php b/src/views/intro.php
index 6df90801..dd42304c 100755
--- a/src/views/intro.php
+++ b/src/views/intro.php
@@ -6,14 +6,4 @@ if (!defined('BASE_PATH')) {
$intro_controller = new \PHPPgAdmin\Controller\IntroController($container);
-$misc->printHeader($lang['strcasts']);
-$misc->printBody();
-
-switch ($action) {
-
- default:
- $intro_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$intro_controller->render();
diff --git a/src/views/languages.php b/src/views/languages.php
index 200fbb62..7a000e5f 100644
--- a/src/views/languages.php
+++ b/src/views/languages.php
@@ -11,13 +11,4 @@ require_once '../lib.inc.php';
$lang_controller = new \PHPPgAdmin\Controller\LangController($container);
-$misc->printHeader($lang['strlanguages']);
-$misc->printBody();
-
-switch ($action) {
- default:
- $lang_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$lang_controller->render();
diff --git a/src/views/login.php b/src/views/login.php
index a9db4788..99168595 100755
--- a/src/views/login.php
+++ b/src/views/login.php
@@ -5,89 +5,11 @@
*
* $Id: login.php,v 1.38 2007/09/04 19:39:48 ioguix Exp $
*/
-function doLoginForm($container, $msg) {
+if (!defined('BASE_PATH')) {
- $lang = $container->get('lang');
- $conf = $container->get('conf');
- $misc = $container->get('misc');
- //$msg = $container->msg;
+ require_once '../lib.inc.php';
+}
- $login_html = $misc->printHeader($lang['strlogin'], null, false);
- $login_html .= $misc->printBody(false);
- $login_html .= $misc->printTrail('root', false);
+$login_controller = new \PHPPgAdmin\Controller\LoginController($container);
- if (!empty($_POST)) {
- $vars = &$_POST;
- } else {
- $vars = &$_GET;
- }
- foreach ($_REQUEST as $key => $val) {
- if (strpos($key, '?') !== FALSE) {
- $namexploded = explode('?', $key);
- $_REQUEST[$namexploded[1]] = htmlspecialchars($val);
- }
- }
-
- $server_info = $misc->getServerInfo($_REQUEST['server']);
- $title = sprintf($lang['strlogintitle'], $server_info['desc']);
- \PC::debug($title, 'title');
- $printTitle = $misc->printTitle($title, null, false);
- \PC::debug($printTitle, 'printTitle');
-
- $login_html .= $printTitle;
-
- if (isset($msg)) {
- $login_html .= $misc->printMsg($msg, false);
- }
-
- $login_html .= '<form id="login_form" method="post" name="login_form">';
-
- $md5_server = md5($_REQUEST['server']);
-// Pass request vars through form (is this a security risk???)
- foreach ($vars as $key => $val) {
- if (substr($key, 0, 5) == 'login') {
- continue;
- }
- if (strpos($key, '?') !== FALSE) {
- $key = explode('?', $key)[1];
- }
-
- $login_html .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($val) . '" />' . "\n";
- }
-
- $login_html .= '<input type="hidden" name="loginServer" value="' . htmlspecialchars($_REQUEST['server']) . '" />';
- $login_html .= '<table class="navbar" border="0" cellpadding="5" cellspacing="3">';
- $login_html .= '<tr>';
- $login_html .= '<td>' . $lang['strusername'] . '</td>';
- $loginusername = isset($_POST['loginUsername']) ? htmlspecialchars($_POST['loginUsername']) : '';
-
- $login_html .= '<td><input type="text" name="loginUsername" value="' . $loginusername . '" size="24" /></td>';
- $login_html .= '</tr>';
- $login_html .= '<tr>';
- $login_html .= '<td>' . $lang['strpassword'] . '</td>';
- $login_html .= '<td><input id="loginPassword" type="password" name="loginPassword_' . $md5_server . '" size="24" /></td>';
- $login_html .= '</tr>';
- $login_html .= '</table>';
- if (sizeof($conf['servers']) > 1) {
- $checked = isset($_POST['loginShared']) ? 'checked="checked"' : '';
- $login_html .= '<p><input type="checkbox" id="loginShared" name="loginShared" ' . $checked . ' />';
- $login_html .= '<label for="loginShared">' . $lang['strtrycred'] . '</label></p>';
- }
- $login_html .= '<p><input type="submit" name="loginSubmit" value="' . $lang['strlogin'] . '" /></p>';
- $login_html .= '</form>';
-
- $login_html .= '<script type="text/javascript">';
- $login_html .= ' var uname = document.login_form.loginUsername;';
- $login_html .= ' var pword = document.login_form.loginPassword_' . $md5_server . ';';
- $login_html .= ' if (uname.value == "") {';
- $login_html .= ' uname.focus();';
- $login_html .= ' } else {';
- $login_html .= ' pword.focus();';
- $login_html .= ' }';
- $login_html .= '</script>';
-
-// Output footer
- $login_html .= $misc->printFooter(false);
- return $login_html;
-
-} \ No newline at end of file
+$login_controller->render(); \ No newline at end of file
diff --git a/src/views/materialized_views.php b/src/views/materialized_views.php
new file mode 100644
index 00000000..e2df9c41
--- /dev/null
+++ b/src/views/materialized_views.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Manage views in a database
+ *
+ * $Id: views.php,v 1.75 2007/12/15 22:57:43 ioguix Exp $
+ */
+
+// Include application functions
+require_once '../lib.inc.php';
+
+$materialized_view_controller = new \PHPPgAdmin\Controller\MaterializedViewController($container);
+
+$materialized_view_controller->render();
diff --git a/src/views/opclasses.php b/src/views/opclasses.php
index 63e5f2fa..15acb1a2 100644
--- a/src/views/opclasses.php
+++ b/src/views/opclasses.php
@@ -10,14 +10,4 @@
require_once '../lib.inc.php';
$opclasses_controller = new \PHPPgAdmin\Controller\OpClassesController($container);
-
-$misc->printHeader($lang['stropclasses']);
-$misc->printBody();
-
-switch ($action) {
- default:
- $opclasses_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$opclasses_controller->render(); \ No newline at end of file
diff --git a/src/views/operators.php b/src/views/operators.php
index d6f33f27..5ef5b9bf 100644
--- a/src/views/operators.php
+++ b/src/views/operators.php
@@ -10,39 +10,4 @@
require_once '../lib.inc.php';
$operator_controller = new \PHPPgAdmin\Controller\OperatorController($container);
-
-$misc->printHeader($lang['stroperators']);
-$misc->printBody();
-
-switch ($action) {
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $operator_controller->doDefault();
- } else {
- $operator_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- doCreate();
- break;
- case 'drop':
- if (isset($_POST['cancel'])) {
- $operator_controller->doDefault();
- } else {
- $operator_controller->doDrop(false);
- }
-
- break;
- case 'confirm_drop':
- $operator_controller->doDrop(true);
- break;
- case 'properties':
- $operator_controller->doProperties();
- break;
- default:
- $operator_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$operator_controller->render(); \ No newline at end of file
diff --git a/src/views/privileges.php b/src/views/privileges.php
index d0d5be41..b48cc9ad 100644
--- a/src/views/privileges.php
+++ b/src/views/privileges.php
@@ -6,387 +6,8 @@
* $Id: privileges.php,v 1.45 2007/09/13 13:41:01 ioguix Exp $
*/
-// Include application functions
require_once '../lib.inc.php';
-$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
-if (!isset($msg)) {
- $msg = '';
-}
+$privilege_controller = new \PHPPgAdmin\Controller\PrivilegeController($container);
-/**
- * Grant permissions on an object to a user
- * @param $confirm To show entry screen
- * @param $mode 'grant' or 'revoke'
- * @param $msg (optional) A message to show
- */
-function doAlter($confirm, $mode, $msg = '') {
- global $data, $misc;
- global $lang;
-
- if (!isset($_REQUEST['username'])) {
- $_REQUEST['username'] = [];
- }
-
- if (!isset($_REQUEST['groupname'])) {
- $_REQUEST['groupname'] = [];
- }
-
- if (!isset($_REQUEST['privilege'])) {
- $_REQUEST['privilege'] = [];
- }
-
- if ($confirm) {
- // Get users from the database
- $users = $data->getUsers();
- // Get groups from the database
- $groups = $data->getGroups();
-
- $misc->printTrail($_REQUEST['subject']);
-
- switch ($mode) {
- case 'grant':
- $misc->printTitle($lang['strgrant'], 'pg.privilege.grant');
- break;
- case 'revoke':
- $misc->printTitle($lang['strrevoke'], 'pg.privilege.revoke');
- break;
- }
- $misc->printMsg($msg);
-
- echo "<form action=\"/src/views/privileges.php\" method=\"post\">\n";
- echo "<table>\n";
- echo "<tr><th class=\"data left\">{$lang['strusers']}</th>\n";
- echo "<td class=\"data1\"><select name=\"username[]\" multiple=\"multiple\" size=\"", min(6, $users->recordCount()), "\">\n";
- while (!$users->EOF) {
- $uname = htmlspecialchars($users->fields['usename']);
- echo "<option value=\"{$uname}\"",
- in_array($users->fields['usename'], $_REQUEST['username']) ? ' selected="selected"' : '', ">{$uname}</option>\n";
- $users->moveNext();
- }
- echo "</select></td></tr>\n";
- echo "<tr><th class=\"data left\">{$lang['strgroups']}</th>\n";
- echo "<td class=\"data1\">\n";
- echo "<input type=\"checkbox\" id=\"public\" name=\"public\"", (isset($_REQUEST['public']) ? ' checked="checked"' : ''), " /><label for=\"public\">PUBLIC</label>\n";
- // Only show groups if there are groups!
- if ($groups->recordCount() > 0) {
- echo "<br /><select name=\"groupname[]\" multiple=\"multiple\" size=\"", min(6, $groups->recordCount()), "\">\n";
- while (!$groups->EOF) {
- $gname = htmlspecialchars($groups->fields['groname']);
- echo "<option value=\"{$gname}\"",
- in_array($groups->fields['groname'], $_REQUEST['groupname']) ? ' selected="selected"' : '', ">{$gname}</option>\n";
- $groups->moveNext();
- }
- echo "</select>\n";
- }
- echo "</td></tr>\n";
- echo "<tr><th class=\"data left required\">{$lang['strprivileges']}</th>\n";
- echo "<td class=\"data1\">\n";
- foreach ($data->privlist[$_REQUEST['subject']] as $v) {
- $v = htmlspecialchars($v);
- echo "<input type=\"checkbox\" id=\"privilege[$v]\" name=\"privilege[$v]\"",
- isset($_REQUEST['privilege'][$v]) ? ' checked="checked"' : '', " /><label for=\"privilege[$v]\">{$v}</label><br />\n";
- }
- echo "</td></tr>\n";
- // Grant option
- if ($data->hasGrantOption()) {
- echo "<tr><th class=\"data left\">{$lang['stroptions']}</th>\n";
- echo "<td class=\"data1\">\n";
- if ($mode == 'grant') {
- echo "<input type=\"checkbox\" id=\"grantoption\" name=\"grantoption\"",
- isset($_REQUEST['grantoption']) ? ' checked="checked"' : '', " /><label for=\"grantoption\">GRANT OPTION</label>\n";
- } elseif ($mode == 'revoke') {
- echo "<input type=\"checkbox\" id=\"grantoption\" name=\"grantoption\"",
- isset($_REQUEST['grantoption']) ? ' checked="checked"' : '', " /><label for=\"grantoption\">GRANT OPTION FOR</label><br />\n";
- echo "<input type=\"checkbox\" id=\"cascade\" name=\"cascade\"",
- isset($_REQUEST['cascade']) ? ' checked="checked"' : '', " /><label for=\"cascade\">CASCADE</label><br />\n";
- }
- echo "</td></tr>\n";
- }
- echo "</table>\n";
-
- echo "<p><input type=\"hidden\" name=\"action\" value=\"save\" />\n";
- echo "<input type=\"hidden\" name=\"mode\" value=\"", htmlspecialchars($mode), "\" />\n";
- echo "<input type=\"hidden\" name=\"subject\" value=\"", htmlspecialchars($_REQUEST['subject']), "\" />\n";
- if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
- echo "<input type=\"hidden\" name=\"", htmlspecialchars($_REQUEST['subject'] . '_oid'),
- "\" value=\"", htmlspecialchars($_REQUEST[$_REQUEST['subject'] . '_oid']), "\" />\n";
- }
-
- echo "<input type=\"hidden\" name=\"", htmlspecialchars($_REQUEST['subject']),
- "\" value=\"", htmlspecialchars($_REQUEST[$_REQUEST['subject']]), "\" />\n";
- if ($_REQUEST['subject'] == 'column') {
- echo "<input type=\"hidden\" name=\"table\" value=\"",
- htmlspecialchars($_REQUEST['table']), "\" />\n";
- }
-
- echo $misc->form;
- if ($mode == 'grant') {
- echo "<input type=\"submit\" name=\"grant\" value=\"{$lang['strgrant']}\" />\n";
- } elseif ($mode == 'revoke') {
- echo "<input type=\"submit\" name=\"revoke\" value=\"{$lang['strrevoke']}\" />\n";
- }
-
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>";
- echo "</form>\n";
- } else {
- // Determine whether object should be ref'd by name or oid.
- if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
- $object = $_REQUEST[$_REQUEST['subject'] . '_oid'];
- } else {
- $object = $_REQUEST[$_REQUEST['subject']];
- }
-
- if (isset($_REQUEST['table'])) {
- $table = $_REQUEST['table'];
- } else {
- $table = null;
- }
-
- $status = $data->setPrivileges(($mode == 'grant') ? 'GRANT' : 'REVOKE', $_REQUEST['subject'], $object,
- isset($_REQUEST['public']), $_REQUEST['username'], $_REQUEST['groupname'], array_keys($_REQUEST['privilege']),
- isset($_REQUEST['grantoption']), isset($_REQUEST['cascade']), $table);
-
- if ($status == 0) {
- doDefault($lang['strgranted']);
- } elseif ($status == -3 || $status == -4) {
- doAlter(true, $_REQUEST['mode'], $lang['strgrantbad']);
- } else {
- doAlter(true, $_REQUEST['mode'], $lang['strgrantfailed']);
- }
-
- }
-}
-
-/**
- * Show permissions on a database, namespace, relation, language or function
- */
-function doDefault($msg = '') {
- global $data, $misc, $database;
- global $lang;
-
- $misc->printTrail($_REQUEST['subject']);
-
- # @@@FIXME: This switch is just a temporary solution,
- # need a better way, maybe every type of object should
- # have a tab bar???
- switch ($_REQUEST['subject']) {
- case 'server':
- case 'database':
- case 'schema':
- case 'table':
- case 'column':
- case 'view':
- $misc->printTabs($_REQUEST['subject'], 'privileges');
- break;
- default:
- $misc->printTitle($lang['strprivileges'], 'pg.privilege');
- }
- $misc->printMsg($msg);
-
- // Determine whether object should be ref'd by name or oid.
- if (isset($_REQUEST[$_REQUEST['subject'] . '_oid'])) {
- $object = $_REQUEST[$_REQUEST['subject'] . '_oid'];
- } else {
- $object = $_REQUEST[$_REQUEST['subject']];
- }
-
- // Get the privileges on the object, given its type
- if ($_REQUEST['subject'] == 'column') {
- $privileges = $data->getPrivileges($object, 'column', $_REQUEST['table']);
- } else {
- $privileges = $data->getPrivileges($object, $_REQUEST['subject']);
- }
-
- if (sizeof($privileges) > 0) {
- echo "<table>\n";
- if ($data->hasRoles()) {
- echo "<tr><th class=\"data\">{$lang['strrole']}</th>";
- } else {
- echo "<tr><th class=\"data\">{$lang['strtype']}</th><th class=\"data\">{$lang['struser']}/{$lang['strgroup']}</th>";
- }
-
- foreach ($data->privlist[$_REQUEST['subject']] as $v2) {
- // Skip over ALL PRIVILEGES
- if ($v2 == 'ALL PRIVILEGES') {
- continue;
- }
-
- echo "<th class=\"data\">{$v2}</th>\n";
- }
- if ($data->hasGrantOption()) {
- echo "<th class=\"data\">{$lang['strgrantor']}</th>";
- }
- echo "</tr>\n";
-
- // Loop over privileges, outputting them
- $i = 0;
- foreach ($privileges as $v) {
- $id = (($i % 2) == 0 ? '1' : '2');
- echo "<tr class=\"data{$id}\">\n";
- if (!$data->hasRoles()) {
- echo "<td>", $misc->printVal($v[0]), "</td>\n";
- }
-
- echo "<td>", $misc->printVal($v[1]), "</td>\n";
- foreach ($data->privlist[$_REQUEST['subject']] as $v2) {
- // Skip over ALL PRIVILEGES
- if ($v2 == 'ALL PRIVILEGES') {
- continue;
- }
-
- echo "<td>";
- if (in_array($v2, $v[2])) {
- echo $lang['stryes'];
- } else {
- echo $lang['strno'];
- }
-
- // If we have grant option for this, end mark
- if ($data->hasGrantOption() && in_array($v2, $v[4])) {
- echo $lang['strasterisk'];
- }
-
- echo "</td>\n";
- }
- if ($data->hasGrantOption()) {
- echo "<td>", $misc->printVal($v[3]), "</td>\n";
- }
- echo "</tr>\n";
- $i++;
- }
-
- echo "</table>";
- } else {
- echo "<p>{$lang['strnoprivileges']}</p>\n";
- }
-
- // Links for granting to a user or group
- switch ($_REQUEST['subject']) {
- case 'table':
- case 'view':
- case 'sequence':
- case 'function':
- case 'tablespace':
- $alllabel = "showall{$_REQUEST['subject']}s";
- $allurl = "{$_REQUEST['subject']}s.php";
- $alltxt = $lang["strshowall{$_REQUEST['subject']}s"];
- break;
- case 'schema':
- $alllabel = "showallschemas";
- $allurl = "schemas.php";
- $alltxt = $lang["strshowallschemas"];
- break;
- case 'database':
- $alllabel = "showalldatabases";
- $allurl = 'all_db.php';
- $alltxt = $lang['strshowalldatabases'];
- break;
- }
-
- $subject = $_REQUEST['subject'];
- $object = $_REQUEST[$_REQUEST['subject']];
-
- if ($_REQUEST['subject'] == 'function') {
- $objectoid = $_REQUEST[$_REQUEST['subject'] . '_oid'];
- $urlvars = [
- 'action' => 'alter',
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- 'schema' => $_REQUEST['schema'],
- $subject => $object,
- "{$subject}_oid" => $objectoid,
- 'subject' => $subject,
- ];
- } else if ($_REQUEST['subject'] == 'column') {
- $urlvars = [
- 'action' => 'alter',
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- 'schema' => $_REQUEST['schema'],
- $subject => $object,
- 'subject' => $subject,
- ];
-
- if (isset($_REQUEST['table'])) {
- $urlvars['table'] = $_REQUEST['table'];
- } else {
- $urlvars['view'] = $_REQUEST['view'];
- }
-
- } else {
- $urlvars = [
- 'action' => 'alter',
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- $subject => $object,
- 'subject' => $subject,
- ];
- if (isset($_REQUEST['schema'])) {
- $urlvars['schema'] = $_REQUEST['schema'];
- }
- }
-
- $navlinks = [
- 'grant' => [
- 'attr' => [
- 'href' => [
- 'url' => 'privileges.php',
- 'urlvars' => array_merge($urlvars, ['mode' => 'grant']),
- ],
- ],
- 'content' => $lang['strgrant'],
- ],
- 'revoke' => [
- 'attr' => [
- 'href' => [
- 'url' => 'privileges.php',
- 'urlvars' => array_merge($urlvars, ['mode' => 'revoke']),
- ],
- ],
- 'content' => $lang['strrevoke'],
- ],
- ];
-
- if (isset($allurl)) {
- $navlinks[$alllabel] = [
- 'attr' => [
- 'href' => [
- 'url' => $allurl,
- 'urlvars' => [
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
- ],
- ],
- ],
- 'content' => $alltxt,
- ];
- if (isset($_REQUEST['schema'])) {
- $navlinks[$alllabel]['attr']['href']['urlvars']['schema'] = $_REQUEST['schema'];
- }
- }
-
- $misc->printNavLinks($navlinks, 'privileges-privileges', get_defined_vars());
-}
-
-$misc->printHeader($lang['strprivileges']);
-$misc->printBody();
-
-switch ($action) {
- case 'save':
- if (isset($_REQUEST['cancel'])) {
- doDefault();
- } else {
- doAlter(false, $_REQUEST['mode']);
- }
-
- break;
- case 'alter':
- doAlter(true, $_REQUEST['mode']);
- break;
- default:
- doDefault();
- break;
-}
-
-$misc->printFooter();
+$privilege_controller->render(); \ No newline at end of file
diff --git a/src/views/roles.php b/src/views/roles.php
index 92c222da..f4ce2860 100644
--- a/src/views/roles.php
+++ b/src/views/roles.php
@@ -9,828 +9,6 @@
// Include application functions
require_once '../lib.inc.php';
-$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
-if (!isset($msg)) {
- $msg = '';
-}
+$roles_controller = new \PHPPgAdmin\Controller\RolesController($container);
-/**
- * Displays a screen for create a new role
- */
-function doCreate($msg = '') {
- global $data, $misc, $username;
- global $lang;
-
- if (!isset($_POST['formRolename'])) {
- $_POST['formRolename'] = '';
- }
-
- if (!isset($_POST['formPassword'])) {
- $_POST['formPassword'] = '';
- }
-
- if (!isset($_POST['formConfirm'])) {
- $_POST['formConfirm'] = '';
- }
-
- if (!isset($_POST['formConnLimit'])) {
- $_POST['formConnLimit'] = '';
- }
-
- if (!isset($_POST['formExpires'])) {
- $_POST['formExpires'] = '';
- }
-
- if (!isset($_POST['memberof'])) {
- $_POST['memberof'] = [];
- }
-
- if (!isset($_POST['members'])) {
- $_POST['members'] = [];
- }
-
- if (!isset($_POST['adminmembers'])) {
- $_POST['adminmembers'] = [];
- }
-
- $misc->printTrail('role');
- $misc->printTitle($lang['strcreaterole'], 'pg.role.create');
- $misc->printMsg($msg);
-
- echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\" style=\"width: 130px\">{$lang['strname']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"15\" maxlength=\"{$data->_maxNameLen}\" name=\"formRolename\" value=\"", htmlspecialchars($_POST['formRolename']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strpassword']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formPassword\" value=\"", htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconfirm']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formConfirm\" value=\"", htmlspecialchars($_POST['formConfirm']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$lang['strsuper']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
- (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$lang['strcreatedb']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
- (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateRole\">{$lang['strcancreaterole']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateRole\" name=\"formCreateRole\"",
- (isset($_POST['formCreateRole'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formInherits\">{$lang['strinheritsprivs']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formInherits\" name=\"formInherits\"",
- (isset($_POST['formInherits'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCanLogin\">{$lang['strcanlogin']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCanLogin\" name=\"formCanLogin\"",
- (isset($_POST['formCanLogin'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconnlimit']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"4\" name=\"formConnLimit\" value=\"", htmlspecialchars($_POST['formConnLimit']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strexpires']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"23\" name=\"formExpires\" value=\"", htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>\n";
-
- $roles = $data->getRoles();
- if ($roles->recordCount() > 0) {
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmemberof']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"memberof[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['memberof']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
-
- $roles->moveFirst();
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmembers']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"members[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['members']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
-
- $roles->moveFirst();
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['stradminmembers']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"adminmembers[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['adminmembers']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
- }
-
- echo "</table>\n";
- echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
- echo $misc->form;
- echo "<input type=\"submit\" name=\"create\" value=\"{$lang['strcreate']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
- echo "</form>\n";
-}
-
-/**
- * Actually creates the new role in the database
- */
-function doSaveCreate() {
- global $data, $lang;
-
- if (!isset($_POST['memberof'])) {
- $_POST['memberof'] = [];
- }
-
- if (!isset($_POST['members'])) {
- $_POST['members'] = [];
- }
-
- if (!isset($_POST['adminmembers'])) {
- $_POST['adminmembers'] = [];
- }
-
- // Check data
- if ($_POST['formRolename'] == '') {
- doCreate($lang['strroleneedsname']);
- } else if ($_POST['formPassword'] != $_POST['formConfirm']) {
- doCreate($lang['strpasswordconfirm']);
- } else {
- $status = $data->createRole($_POST['formRolename'], $_POST['formPassword'], isset($_POST['formSuper']),
- isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']),
- isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'],
- $_POST['adminmembers']);
- if ($status == 0) {
- doDefault($lang['strrolecreated']);
- } else {
- doCreate($lang['strrolecreatedbad']);
- }
-
- }
-}
-
-/**
- * Function to allow alter a role
- */
-function doAlter($msg = '') {
- global $data, $misc;
- global $lang;
-
- $misc->printTrail('role');
- $misc->printTitle($lang['stralter'], 'pg.role.alter');
- $misc->printMsg($msg);
-
- $roledata = $data->getRole($_REQUEST['rolename']);
-
- if ($roledata->recordCount() > 0) {
- $server_info = $misc->getServerInfo();
- $canRename = $data->hasUserRename() && ($_REQUEST['rolename'] != $server_info['username']);
- $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
- $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
- $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
- $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
- $roledata->fields['rolcanlogin'] = $data->phpBool($roledata->fields['rolcanlogin']);
-
- if (!isset($_POST['formExpires'])) {
- if ($canRename) {
- $_POST['formNewRoleName'] = $roledata->fields['rolname'];
- }
-
- if ($roledata->fields['rolsuper']) {
- $_POST['formSuper'] = '';
- }
-
- if ($roledata->fields['rolcreatedb']) {
- $_POST['formCreateDB'] = '';
- }
-
- if ($roledata->fields['rolcreaterole']) {
- $_POST['formCreateRole'] = '';
- }
-
- if ($roledata->fields['rolinherit']) {
- $_POST['formInherits'] = '';
- }
-
- if ($roledata->fields['rolcanlogin']) {
- $_POST['formCanLogin'] = '';
- }
-
- $_POST['formConnLimit'] = $roledata->fields['rolconnlimit'] == '-1' ? '' : $roledata->fields['rolconnlimit'];
- $_POST['formExpires'] = $roledata->fields['rolvaliduntil'] == 'infinity' ? '' : $roledata->fields['rolvaliduntil'];
- $_POST['formPassword'] = '';
- }
-
- echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\" style=\"width: 130px\">{$lang['strname']}</th>\n";
- echo "\t\t<td class=\"data1\">", ($canRename ? "<input name=\"formNewRoleName\" size=\"15\" maxlength=\"{$data->_maxNameLen}\" value=\"" . htmlspecialchars($_POST['formNewRoleName']) . "\" />" : $misc->printVal($roledata->fields['rolname'])), "</td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strpassword']}</th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"15\" name=\"formPassword\" value=\"", htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconfirm']}</th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"15\" name=\"formConfirm\" value=\"\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$lang['strsuper']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
- (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$lang['strcreatedb']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
- (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateRole\">{$lang['strcancreaterole']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateRole\" name=\"formCreateRole\"",
- (isset($_POST['formCreateRole'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formInherits\">{$lang['strinheritsprivs']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formInherits\" name=\"formInherits\"",
- (isset($_POST['formInherits'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCanLogin\">{$lang['strcanlogin']}</label></th>\n";
- echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCanLogin\" name=\"formCanLogin\"",
- (isset($_POST['formCanLogin'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strconnlimit']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"4\" name=\"formConnLimit\" value=\"", htmlspecialchars($_POST['formConnLimit']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strexpires']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"23\" name=\"formExpires\" value=\"", htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>\n";
-
- if (!isset($_POST['memberof'])) {
- $memberof = $data->getMemberOf($_REQUEST['rolename']);
- if ($memberof->recordCount() > 0) {
- $i = 0;
- while (!$memberof->EOF) {
- $_POST['memberof'][$i++] = $memberof->fields['rolname'];
- $memberof->moveNext();
- }
- } else {
- $_POST['memberof'] = [];
- }
-
- $memberofold = implode(',', $_POST['memberof']);
- }
- if (!isset($_POST['members'])) {
- $members = $data->getMembers($_REQUEST['rolename']);
- if ($members->recordCount() > 0) {
- $i = 0;
- while (!$members->EOF) {
- $_POST['members'][$i++] = $members->fields['rolname'];
- $members->moveNext();
- }
- } else {
- $_POST['members'] = [];
- }
-
- $membersold = implode(',', $_POST['members']);
- }
- if (!isset($_POST['adminmembers'])) {
- $adminmembers = $data->getMembers($_REQUEST['rolename'], 't');
- if ($adminmembers->recordCount() > 0) {
- $i = 0;
- while (!$adminmembers->EOF) {
- $_POST['adminmembers'][$i++] = $adminmembers->fields['rolname'];
- $adminmembers->moveNext();
- }
- } else {
- $_POST['adminmembers'] = [];
- }
-
- $adminmembersold = implode(',', $_POST['adminmembers']);
- }
-
- $roles = $data->getRoles($_REQUEST['rolename']);
- if ($roles->recordCount() > 0) {
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmemberof']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"memberof[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['memberof']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
-
- $roles->moveFirst();
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strmembers']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"members[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['members']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
-
- $roles->moveFirst();
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['stradminmembers']}</th>\n";
- echo "\t\t<td class=\"data\">\n";
- echo "\t\t\t<select name=\"adminmembers[]\" multiple=\"multiple\" size=\"", min(20, $roles->recordCount()), "\">\n";
- while (!$roles->EOF) {
- $rolename = $roles->fields['rolname'];
- echo "\t\t\t\t<option value=\"{$rolename}\"",
- (in_array($rolename, $_POST['adminmembers']) ? ' selected="selected"' : ''), ">", $misc->printVal($rolename), "</option>\n";
- $roles->moveNext();
- }
- echo "\t\t\t</select>\n";
- echo "\t\t</td>\n\t</tr>\n";
- }
- echo "</table>\n";
-
- echo "<p><input type=\"hidden\" name=\"action\" value=\"save_alter\" />\n";
- echo "<input type=\"hidden\" name=\"rolename\" value=\"", htmlspecialchars($_REQUEST['rolename']), "\" />\n";
- echo "<input type=\"hidden\" name=\"memberofold\" value=\"", isset($_POST['memberofold']) ? $_POST['memberofold'] : htmlspecialchars($memberofold), "\" />\n";
- echo "<input type=\"hidden\" name=\"membersold\" value=\"", isset($_POST['membersold']) ? $_POST['membersold'] : htmlspecialchars($membersold), "\" />\n";
- echo "<input type=\"hidden\" name=\"adminmembersold\" value=\"", isset($_POST['adminmembersold']) ? $_POST['adminmembersold'] : htmlspecialchars($adminmembersold), "\" />\n";
- echo $misc->form;
- echo "<input type=\"submit\" name=\"alter\" value=\"{$lang['stralter']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
- echo "</form>\n";
- } else {
- echo "<p>{$lang['strnodata']}</p>\n";
- }
-
-}
-
-/**
- * Function to save after editing a role
- */
-function doSaveAlter() {
- global $data, $lang;
-
- if (!isset($_POST['memberof'])) {
- $_POST['memberof'] = [];
- }
-
- if (!isset($_POST['members'])) {
- $_POST['members'] = [];
- }
-
- if (!isset($_POST['adminmembers'])) {
- $_POST['adminmembers'] = [];
- }
-
- // Check name and password
- if (isset($_POST['formNewRoleName']) && $_POST['formNewRoleName'] == '') {
- doAlter($lang['strroleneedsname']);
- } else if ($_POST['formPassword'] != $_POST['formConfirm']) {
- doAlter($lang['strpasswordconfirm']);
- } else {
- if (isset($_POST['formNewRoleName'])) {
- $status = $data->setRenameRole($_POST['rolename'], $_POST['formPassword'], isset($_POST['formSuper']), isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']), isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'], $_POST['adminmembers'], $_POST['memberofold'], $_POST['membersold'], $_POST['adminmembersold'], $_POST['formNewRoleName']);
- } else {
- $status = $data->setRole($_POST['rolename'], $_POST['formPassword'], isset($_POST['formSuper']), isset($_POST['formCreateDB']), isset($_POST['formCreateRole']), isset($_POST['formInherits']), isset($_POST['formCanLogin']), $_POST['formConnLimit'], $_POST['formExpires'], $_POST['memberof'], $_POST['members'], $_POST['adminmembers'], $_POST['memberofold'], $_POST['membersold'], $_POST['adminmembersold']);
- }
-
- if ($status == 0) {
- doDefault($lang['strrolealtered']);
- } else {
- doAlter($lang['strrolealteredbad']);
- }
-
- }
-}
-
-/**
- * Show confirmation of drop a role and perform actual drop
- */
-function doDrop($confirm) {
- global $data, $misc;
- global $lang;
-
- if ($confirm) {
- $misc->printTrail('role');
- $misc->printTitle($lang['strdroprole'], 'pg.role.drop');
-
- echo "<p>", sprintf($lang['strconfdroprole'], $misc->printVal($_REQUEST['rolename'])), "</p>\n";
-
- echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
- echo "<p><input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
- echo "<input type=\"hidden\" name=\"rolename\" value=\"", htmlspecialchars($_REQUEST['rolename']), "\" />\n";
- echo $misc->form;
- echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
- echo "</form>\n";
- } else {
- $status = $data->dropRole($_REQUEST['rolename']);
- if ($status == 0) {
- doDefault($lang['strroledropped']);
- } else {
- doDefault($lang['strroledroppedbad']);
- }
-
- }
-}
-
-/**
- * Show the properties of a role
- */
-function doProperties($msg = '') {
- global $data, $misc;
- global $lang;
-
- $misc->printTrail('role');
- $misc->printTitle($lang['strproperties'], 'pg.role');
- $misc->printMsg($msg);
-
- $roledata = $data->getRole($_REQUEST['rolename']);
- if ($roledata->recordCount() > 0) {
- $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
- $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
- $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
- $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
- $roledata->fields['rolcanlogin'] = $data->phpBool($roledata->fields['rolcanlogin']);
-
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data\" style=\"width: 130px\">Description</th>\n";
- echo "\t\t<th class=\"data\" style=\"width: 120\">Value</th>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strname']}</td>\n";
- echo "\t\t<td class=\"data1\">", htmlspecialchars($_REQUEST['rolename']), "</td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strsuper']}</td>\n";
- echo "\t\t<td class=\"data2\">", (($roledata->fields['rolsuper']) ? $lang['stryes'] : $lang['strno']), "</td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strcreatedb']}</td>\n";
- echo "\t\t<td class=\"data1\">", (($roledata->fields['rolcreatedb']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strcancreaterole']}</td>\n";
- echo "\t\t<td class=\"data2\">", (($roledata->fields['rolcreaterole']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strinheritsprivs']}</td>\n";
- echo "\t\t<td class=\"data1\">", (($roledata->fields['rolinherit']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strcanlogin']}</td>\n";
- echo "\t\t<td class=\"data2\">", (($roledata->fields['rolcanlogin']) ? $lang['stryes'] : $lang['strno']), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strconnlimit']}</td>\n";
- echo "\t\t<td class=\"data1\">", ($roledata->fields['rolconnlimit'] == '-1' ? $lang['strnolimit'] : $misc->printVal($roledata->fields['rolconnlimit'])), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strexpires']}</td>\n";
- echo "\t\t<td class=\"data2\">", ($roledata->fields['rolvaliduntil'] == 'infinity' || is_null($roledata->fields['rolvaliduntil']) ? $lang['strnever'] : $misc->printVal($roledata->fields['rolvaliduntil'])), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strsessiondefaults']}</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolconfig']), "</td>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['strmemberof']}</td>\n";
- echo "\t\t<td class=\"data2\">";
- $memberof = $data->getMemberOf($_REQUEST['rolename']);
- if ($memberof->recordCount() > 0) {
- while (!$memberof->EOF) {
- echo $misc->printVal($memberof->fields['rolname']), "<br />\n";
- $memberof->moveNext();
- }
- }
- echo "</td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">{$lang['strmembers']}</td>\n";
- echo "\t\t<td class=\"data1\">";
- $members = $data->getMembers($_REQUEST['rolename']);
- if ($members->recordCount() > 0) {
- while (!$members->EOF) {
- echo $misc->printVal($members->fields['rolname']), "<br />\n";
- $members->moveNext();
- }
- }
- echo "</td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data2\">{$lang['stradminmembers']}</td>\n";
- echo "\t\t<td class=\"data2\">";
- $adminmembers = $data->getMembers($_REQUEST['rolename'], 't');
- if ($adminmembers->recordCount() > 0) {
- while (!$adminmembers->EOF) {
- echo $misc->printVal($adminmembers->fields['rolname']), "<br />\n";
- $adminmembers->moveNext();
- }
- }
- echo "</td>\n\t</tr>\n";
- echo "</table>\n";
- } else {
- echo "<p>{$lang['strnodata']}</p>\n";
- }
-
- $navlinks = [
- 'showall' => [
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'server' => $_REQUEST['server'],
- ],
- ],
- ],
- 'content' => $lang['strshowallroles'],
- ],
- 'alter' => [
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'alter',
- 'server' => $_REQUEST['server'],
- 'rolename' => $_REQUEST['rolename'],
- ],
- ],
- ],
- 'content' => $lang['stralter'],
- ],
- 'drop' => [
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'confirm_drop',
- 'server' => $_REQUEST['server'],
- 'rolename' => $_REQUEST['rolename'],
- ],
- ],
- ],
- 'content' => $lang['strdrop'],
- ],
- ];
-
- $misc->printNavLinks($navlinks, 'roles-properties', get_defined_vars());
-}
-
-/**
- * If a role is not a superuser role, then we have an 'account management'
- * page for change his password, etc. We don't prevent them from
- * messing with the URL to gain access to other role admin stuff, because
- * the PostgreSQL permissions will prevent them changing anything anyway.
- */
-function doAccount($msg = '') {
- global $data, $misc;
- global $lang;
-
- $server_info = $misc->getServerInfo();
-
- $roledata = $data->getRole($server_info['username']);
- $_REQUEST['rolename'] = $server_info['username'];
-
- $misc->printTrail('role');
- $misc->printTabs('server', 'account');
- $misc->printMsg($msg);
-
- if ($roledata->recordCount() > 0) {
- $roledata->fields['rolsuper'] = $data->phpBool($roledata->fields['rolsuper']);
- $roledata->fields['rolcreatedb'] = $data->phpBool($roledata->fields['rolcreatedb']);
- $roledata->fields['rolcreaterole'] = $data->phpBool($roledata->fields['rolcreaterole']);
- $roledata->fields['rolinherit'] = $data->phpBool($roledata->fields['rolinherit']);
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data\">{$lang['strname']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strsuper']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strcreatedb']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strcancreaterole']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strinheritsprivs']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strconnlimit']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strexpires']}</th>\n";
- echo "\t\t<th class=\"data\">{$lang['strsessiondefaults']}</th>\n";
- echo "\t</tr>\n";
- echo "\t<tr>\n\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolname']), "</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolsuper'], 'yesno'), "</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolcreatedb'], 'yesno'), "</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolcreaterole'], 'yesno'), "</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolinherit'], 'yesno'), "</td>\n";
- echo "\t\t<td class=\"data1\">", ($roledata->fields['rolconnlimit'] == '-1' ? $lang['strnolimit'] : $misc->printVal($roledata->fields['rolconnlimit'])), "</td>\n";
- echo "\t\t<td class=\"data1\">", ($roledata->fields['rolvaliduntil'] == 'infinity' || is_null($roledata->fields['rolvaliduntil']) ? $lang['strnever'] : $misc->printVal($roledata->fields['rolvaliduntil'])), "</td>\n";
- echo "\t\t<td class=\"data1\">", $misc->printVal($roledata->fields['rolconfig']), "</td>\n";
- echo "\t</tr>\n</table>\n";
- } else {
- echo "<p>{$lang['strnodata']}</p>\n";
- }
-
- $misc->printNavLinks(['changepassword' => [
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'confchangepassword',
- 'server' => $_REQUEST['server'],
- ],
- ],
- ],
- 'content' => $lang['strchangepassword'],
- ]], 'roles-account', get_defined_vars());
-}
-
-/**
- * Show confirmation of change password and actually change password
- */
-function doChangePassword($confirm, $msg = '') {
- global $data, $misc;
- global $lang, $conf;
-
- $server_info = $misc->getServerInfo();
-
- if ($confirm) {
- $_REQUEST['rolename'] = $server_info['username'];
- $misc->printTrail('role');
- $misc->printTitle($lang['strchangepassword'], 'pg.role.alter');
- $misc->printMsg($msg);
-
- if (!isset($_POST['password'])) {
- $_POST['password'] = '';
- }
-
- if (!isset($_POST['confirm'])) {
- $_POST['confirm'] = '';
- }
-
- echo "<form action=\"/src/views/roles.php\" method=\"post\">\n";
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strpassword']}</th>\n";
- echo "\t\t<td><input type=\"password\" name=\"password\" size=\"32\" value=\"",
- htmlspecialchars($_POST['password']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strconfirm']}</th>\n";
- echo "\t\t<td><input type=\"password\" name=\"confirm\" size=\"32\" value=\"\" /></td>\n\t</tr>\n";
- echo "</table>\n";
- echo "<p><input type=\"hidden\" name=\"action\" value=\"changepassword\" />\n";
- echo $misc->form;
- echo "<input type=\"submit\" name=\"ok\" value=\"{$lang['strok']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
- echo "</p></form>\n";
- } else {
- // Check that password is minimum length
- if (strlen($_POST['password']) < $conf['min_password_length']) {
- doChangePassword(true, $lang['strpasswordshort']);
- }
-
- // Check that password matches confirmation password
- elseif ($_POST['password'] != $_POST['confirm']) {
- doChangePassword(true, $lang['strpasswordconfirm']);
- } else {
- $status = $data->changePassword($server_info['username'], $_POST['password']);
- if ($status == 0) {
- doAccount($lang['strpasswordchanged']);
- } else {
- doAccount($lang['strpasswordchangedbad']);
- }
-
- }
- }
-}
-
-/**
- * Show default list of roles in the database
- */
-function doDefault($msg = '') {
- global $data, $misc;
- global $lang;
-
- function renderRoleConnLimit($val) {
- global $lang;
- return $val == '-1' ? $lang['strnolimit'] : htmlspecialchars($val);
- }
-
- function renderRoleExpires($val) {
- global $lang;
- return $val == 'infinity' ? $lang['strnever'] : htmlspecialchars($val);
- }
-
- $misc->printTrail('server');
- $misc->printTabs('server', 'roles');
- $misc->printMsg($msg);
-
- $roles = $data->getRoles();
-
- $columns = [
- 'role' => [
- 'title' => $lang['strrole'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolname'),
- 'url' => "/redirect/role?action=properties&amp;{$misc->href}&amp;",
- 'vars' => ['rolename' => 'rolname'],
- ],
- 'superuser' => [
- 'title' => $lang['strsuper'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolsuper'),
- 'type' => 'yesno',
- ],
- 'createdb' => [
- 'title' => $lang['strcreatedb'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolcreatedb'),
- 'type' => 'yesno',
- ],
- 'createrole' => [
- 'title' => $lang['strcancreaterole'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolcreaterole'),
- 'type' => 'yesno',
- ],
- 'inherits' => [
- 'title' => $lang['strinheritsprivs'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolinherit'),
- 'type' => 'yesno',
- ],
- 'canloging' => [
- 'title' => $lang['strcanlogin'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolcanlogin'),
- 'type' => 'yesno',
- ],
- 'connlimit' => [
- 'title' => $lang['strconnlimit'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolconnlimit'),
- 'type' => 'callback',
- 'params' => ['function' => 'renderRoleConnLimit'],
- ],
- 'expires' => [
- 'title' => $lang['strexpires'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('rolvaliduntil'),
- 'type' => 'callback',
- 'params' => ['function' => 'renderRoleExpires', 'null' => $lang['strnever']],
- ],
- 'actions' => [
- 'title' => $lang['stractions'],
- ],
- ];
-
- $actions = [
- 'alter' => [
- 'content' => $lang['stralter'],
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'alter',
- 'rolename' => \PHPPgAdmin\Decorators\Decorator::field('rolname'),
- ],
- ],
- ],
- ],
- 'drop' => [
- 'content' => $lang['strdrop'],
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'confirm_drop',
- 'rolename' => \PHPPgAdmin\Decorators\Decorator::field('rolname'),
- ],
- ],
- ],
- ],
- ];
-
- echo $misc->printTable($roles, $columns, $actions, 'roles-roles', $lang['strnoroles']);
-
- $navlinks = [
- 'create' => [
- 'attr' => [
- 'href' => [
- 'url' => 'roles.php',
- 'urlvars' => [
- 'action' => 'create',
- 'server' => $_REQUEST['server'],
- ],
- ],
- ],
- 'content' => $lang['strcreaterole'],
- ],
- ];
- $misc->printNavLinks($navlinks, 'roles-roles', get_defined_vars());
-}
-
-$misc->printHeader($lang['strroles']);
-$misc->printBody();
-
-switch ($action) {
- case 'create':
- doCreate();
- break;
- case 'save_create':
- if (isset($_POST['create'])) {
- doSaveCreate();
- } else {
- doDefault();
- }
-
- break;
- case 'alter':
- doAlter();
- break;
- case 'save_alter':
- if (isset($_POST['alter'])) {
- doSaveAlter();
- } else {
- doDefault();
- }
-
- break;
- case 'confirm_drop':
- doDrop(true);
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- doDrop(false);
- } else {
- doDefault();
- }
-
- break;
- case 'properties':
- doProperties();
- break;
- case 'confchangepassword':
- doChangePassword(true);
- break;
- case 'changepassword':
- if (isset($_REQUEST['ok'])) {
- doChangePassword(false);
- } else {
- doAccount();
- }
-
- break;
- case 'account':
- doAccount();
- break;
- default:
- doDefault();
-}
-
-$misc->printFooter();
+$roles_controller->render();
diff --git a/src/views/rules.php b/src/views/rules.php
index 24186352..3557a050 100644
--- a/src/views/rules.php
+++ b/src/views/rules.php
@@ -10,37 +10,4 @@
require_once '../lib.inc.php';
$rule_controller = new \PHPPgAdmin\Controller\RuleController($container);
-
-// Different header if we're view rules or table rules
-$misc->printHeader($_REQUEST[$_REQUEST['subject']] . ' - ' . $lang['strrules']);
-$misc->printBody();
-
-switch ($action) {
- case 'create_rule':
- $rule_controller->createRule(true);
- break;
- case 'save_create_rule':
- if (isset($_POST['cancel'])) {
- $rule_controller->doDefault();
- } else {
- $rule_controller->createRule(false);
- }
-
- break;
- case 'drop':
- if (isset($_POST['yes'])) {
- $rule_controller->doDrop(false);
- } else {
- $rule_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $rule_controller->doDrop(true);
- break;
- default:
- $rule_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$rule_controller->render(); \ No newline at end of file
diff --git a/src/views/schemas.php b/src/views/schemas.php
index 8bb271e8..2afb4f6e 100755
--- a/src/views/schemas.php
+++ b/src/views/schemas.php
@@ -11,44 +11,4 @@ require_once '../lib.inc.php';
$schema_controller = new \PHPPgAdmin\Controller\SchemaController($container);
-$misc->printHeader($lang['strschemas']);
-$misc->printBody();
-
-if (isset($_POST['cancel'])) {
- $action = '';
-}
-
-switch ($action) {
- case 'create':
- if (isset($_POST['create'])) {
- $schema_controller->doSaveCreate();
- } else {
- $schema_controller->doCreate();
- }
-
- break;
- case 'alter':
- if (isset($_POST['alter'])) {
- $schema_controller->doSaveAlter();
- } else {
- $schema_controller->doAlter();
- }
-
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $schema_controller->doDrop(false);
- } else {
- $schema_controller->doDrop(true);
- }
-
- break;
- case 'export':
- $schema_controller->doExport();
- break;
- default:
- $schema_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$schema_controller->render();
diff --git a/src/views/sequences.php b/src/views/sequences.php
index 63ef6d1a..342262c3 100644
--- a/src/views/sequences.php
+++ b/src/views/sequences.php
@@ -10,72 +10,4 @@
require_once '../lib.inc.php';
$sequence_controller = new \PHPPgAdmin\Controller\SequenceController($container);
-
-// Print header
-$misc->printHeader($lang['strsequences']);
-$misc->printBody();
-
-switch ($action) {
- case 'create':
- $sequence_controller->doCreateSequence();
- break;
- case 'save_create_sequence':
- if (isset($_POST['create'])) {
- $sequence_controller->doSaveCreateSequence();
- } else {
- $sequence_controller->doDefault();
- }
-
- break;
- case 'properties':
- $sequence_controller->doProperties();
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $sequence_controller->doDrop(false);
- } else {
- $sequence_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $sequence_controller->doDrop(true);
- break;
- case 'restart':
- $sequence_controller->doRestart();
- break;
- case 'reset':
- $sequence_controller->doReset();
- break;
- case 'nextval':
- $sequence_controller->doNextval();
- break;
- case 'setval':
- if (isset($_POST['setval'])) {
- $sequence_controller->doSaveSetval();
- } else {
- $sequence_controller->doDefault();
- }
-
- break;
- case 'confirm_setval':
- $sequence_controller->doSetval();
- break;
- case 'alter':
- if (isset($_POST['alter'])) {
- $sequence_controller->doSaveAlter();
- } else {
- $sequence_controller->doDefault();
- }
-
- break;
- case 'confirm_alter':
- $sequence_controller->doAlter();
- break;
- default:
- $sequence_controller->doDefault();
- break;
-}
-
-// Print footer
-$misc->printFooter();
+$sequence_controller->render(); \ No newline at end of file
diff --git a/src/views/servers.php b/src/views/servers.php
index fa027788..ef069ed9 100644
--- a/src/views/servers.php
+++ b/src/views/servers.php
@@ -6,106 +6,8 @@
* $Id: servers.php,v 1.12 2008/02/18 22:20:26 ioguix Exp $
*/
-function doLogout($container) {
+require_once '../lib.inc.php';
- $lang = $container->get('lang');
- $misc = $container->get('misc');
- $plugin_manager = $container->get('plugin_manager');
+$server_controller = new \PHPPgAdmin\Controller\ServerController($container);
- $plugin_manager->do_hook('logout', $_REQUEST['logoutServer']);
-
- $server_info = $misc->getServerInfo($_REQUEST['logoutServer']);
- $misc->setServerInfo(null, null, $_REQUEST['logoutServer']);
-
- unset($_SESSION['sharedUsername'], $_SESSION['sharedPassword']);
-
- $misc->setReloadBrowser(true);
-
- return sprintf($lang['strlogoutmsg'], $server_info['desc']);
-
-}
-
-function doDefault($container, $msg = '') {
-
- $lang = $container->get('lang');
- $conf = $container->get('conf');
- $misc = $container->get('misc');
-
- $default_html = '';
-
- $default_html .= $misc->printTabs('root', 'servers', false);
- $default_html .= $misc->printMsg($msg, false);
-
- $group = isset($_GET['group']) ? $_GET['group'] : false;
-
- $groups = $misc->getServersGroups(true, $group);
-
- $columns = [
- 'group' => [
- 'title' => $lang['strgroup'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('desc'),
- 'url' => 'servers.php?',
- 'vars' => ['group' => 'id'],
- ],
- ];
- $actions = [];
-
- if (($group !== false) and (isset($conf['srv_groups'][$group])) and ($groups->recordCount() > 0)) {
- $default_html .= $misc->printTitle(sprintf($lang['strgroupgroups'], htmlentities($conf['srv_groups'][$group]['desc'], ENT_QUOTES, 'UTF-8')), null, false);
- }
-
- $default_html .= $misc->printTable($groups, $columns, $actions, 'servers-servers');
-
- $servers = $misc->getServers(true, $group);
-
- function svPre(&$rowdata, $actions) {
- $actions['logout']['disable'] = empty($rowdata->fields['username']);
- return $actions;
- }
-
- $columns = [
- 'server' => [
- 'title' => $lang['strserver'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('desc'),
- 'url' => "/redirect/server?",
- 'vars' => ['server' => 'id'],
- ],
- 'host' => [
- 'title' => $lang['strhost'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('host'),
- ],
- 'port' => [
- 'title' => $lang['strport'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('port'),
- ],
- 'username' => [
- 'title' => $lang['strusername'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('username'),
- ],
- 'actions' => [
- 'title' => $lang['stractions'],
- ],
- ];
-
- $actions = [
- 'logout' => [
- 'content' => $lang['strlogout'],
- 'attr' => [
- 'href' => [
- 'url' => '/src/views/servers/logout',
- 'urlvars' => [
- 'logoutServer' => \PHPPgAdmin\Decorators\Decorator::field('id'),
- ],
- ],
- ],
- ],
- ];
-
- if (($group !== false) and isset($conf['srv_groups'][$group])) {
- $default_html .= $misc->printTitle(sprintf($lang['strgroupservers'], htmlentities($conf['srv_groups'][$group]['desc'], ENT_QUOTES, 'UTF-8')), null, false);
- $actions['logout']['attr']['href']['urlvars']['group'] = $group;
- }
-
- $default_html .= $misc->printTable($servers, $columns, $actions, 'servers-servers', $lang['strnoobjects'], 'svPre');
- return $default_html;
-}
+$server_controller->render(); \ No newline at end of file
diff --git a/src/views/sql.php b/src/views/sql.php
index 33286356..098b03a0 100644
--- a/src/views/sql.php
+++ b/src/views/sql.php
@@ -9,263 +9,9 @@
* $Id: sql.php,v 1.43 2008/01/10 20:19:27 xzilla Exp $
*/
-global $lang;
-
-// Prevent timeouts on large exports (non-safe mode only)
-if (!ini_get('safe_mode')) {
- set_time_limit(0);
-}
-
// Include application functions
require_once '../lib.inc.php';
-/**
- * This is a callback function to display the result of each separate query
- * @param ADORecordSet $rs The recordset returned by the script execetor
- */
-function sqlCallback($query, $rs, $lineno) {
- global $data, $misc, $lang, $_connection;
- // Check if $rs is false, if so then there was a fatal error
- if ($rs === false) {
- echo htmlspecialchars($_FILES['script']['name']), ':', $lineno, ': ', nl2br(htmlspecialchars($_connection->getLastError())), "<br/>\n";
- } else {
- // Print query results
- switch (pg_result_status($rs)) {
- case PGSQL_TUPLES_OK:
- // If rows returned, then display the results
- $num_fields = pg_numfields($rs);
- echo "<p><table>\n<tr>";
- for ($k = 0; $k < $num_fields; $k++) {
- echo "<th class=\"data\">", $misc->printVal(pg_fieldname($rs, $k)), "</th>";
- }
-
- $i = 0;
- $row = pg_fetch_row($rs);
- while ($row !== false) {
- $id = (($i % 2) == 0 ? '1' : '2');
- echo "<tr class=\"data{$id}\">\n";
- foreach ($row as $k => $v) {
- echo "<td style=\"white-space:nowrap;\">", $misc->printVal($v, pg_fieldtype($rs, $k), ['null' => true]), "</td>";
- }
- echo "</tr>\n";
- $row = pg_fetch_row($rs);
- $i++;
- }
- ;
- echo "</table><br/>\n";
- echo $i, " {$lang['strrows']}</p>\n";
- break;
- case PGSQL_COMMAND_OK:
- // If we have the command completion tag
- if (version_compare(phpversion(), '4.3', '>=')) {
- echo htmlspecialchars(pg_result_status($rs, PGSQL_STATUS_STRING)), "<br/>\n";
- }
- // Otherwise if any rows have been affected
- elseif ($data->conn->Affected_Rows() > 0) {
- echo $data->conn->Affected_Rows(), " {$lang['strrowsaff']}<br/>\n";
- }
- // Otherwise output nothing...
- break;
- case PGSQL_EMPTY_QUERY:
- break;
- default:
- break;
- }
- }
-}
-
-// We need to store the query in a session for editing purposes
-// We avoid GPC vars to avoid truncating long queries
-if (isset($_REQUEST['subject']) && $_REQUEST['subject'] == 'history') {
- // Or maybe we came from the history popup
- $_SESSION['sqlquery'] = $_SESSION['history'][$_REQUEST['server']][$_REQUEST['database']][$_GET['queryid']]['query'];
-} elseif (isset($_POST['query'])) {
- // Or maybe we came from an sql form
- $_SESSION['sqlquery'] = $_POST['query'];
-} else {
- echo "could not find the query!!";
-}
-
-// Pagination maybe set by a get link that has it as FALSE,
-// if that's the case, unset the variable.
-
-if (isset($_REQUEST['paginate']) && $_REQUEST['paginate'] == 'f') {
- unset($_REQUEST['paginate']);
- unset($_POST['paginate']);
- unset($_GET['paginate']);
-}
-// Check to see if pagination has been specified. In that case, send to display
-// script for pagination
-/* if a file is given or the request is an explain, do not paginate */
-if (isset($_REQUEST['paginate']) && !(isset($_FILES['script']) && $_FILES['script']['size'] > 0)
- && (preg_match('/^\s*explain/i', $_SESSION['sqlquery']) == 0)) {
- include './display.php';
- exit;
-}
-
-$subject = isset($_REQUEST['subject']) ? $_REQUEST['subject'] : '';
-$misc->printHeader($lang['strqueryresults']);
-$misc->printBody();
-$misc->printTrail('database');
-$misc->printTitle($lang['strqueryresults']);
-
-// Set the schema search path
-if (isset($_REQUEST['search_path'])) {
- if ($data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path']))) != 0) {
- $misc->printFooter();
- exit;
- }
-}
-
-// May as well try to time the query
-if (function_exists('microtime')) {
- list($usec, $sec) = explode(' ', microtime());
- $start_time = ((float) $usec + (float) $sec);
-} else {
- $start_time = null;
-}
-
-// Execute the query. If it's a script upload, special handling is necessary
-if (isset($_FILES['script']) && $_FILES['script']['size'] > 0) {
- $data->executeScript('script', 'sqlCallback');
-} else {
- // Set fetch mode to NUM so that duplicate field names are properly returned
- $data->conn->setFetchMode(ADODB_FETCH_NUM);
- $rs = $data->conn->Execute($_SESSION['sqlquery']);
-
- // $rs will only be an object if there is no error
- if (is_object($rs)) {
- // Request was run, saving it in history
- if (!isset($_REQUEST['nohistory'])) {
- $misc->saveScriptHistory($_SESSION['sqlquery']);
- }
-
- // Now, depending on what happened do various things
-
- // First, if rows returned, then display the results
- if ($rs->recordCount() > 0) {
- echo "<table>\n<tr>";
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($k);
- echo "<th class=\"data\">", $misc->printVal($finfo->name), "</th>";
- }
- echo "</tr>\n";
- $i = 0;
- while (!$rs->EOF) {
- $id = (($i % 2) == 0 ? '1' : '2');
- echo "<tr class=\"data{$id}\">\n";
- foreach ($rs->fields as $k => $v) {
- $finfo = $rs->fetchField($k);
- echo "<td style=\"white-space:nowrap;\">", $misc->printVal($v, $finfo->type, ['null' => true]), "</td>";
- }
- echo "</tr>\n";
- $rs->moveNext();
- $i++;
- }
- echo "</table>\n";
- echo "<p>", $rs->recordCount(), " {$lang['strrows']}</p>\n";
- }
- // Otherwise if any rows have been affected
- elseif ($data->conn->Affected_Rows() > 0) {
- echo "<p>", $data->conn->Affected_Rows(), " {$lang['strrowsaff']}</p>\n";
- }
- // Otherwise nodata to print
- else {
- echo '<p>', $lang['strnodata'], "</p>\n";
- }
-
- }
-}
-
-// May as well try to time the query
-if ($start_time !== null) {
- list($usec, $sec) = explode(' ', microtime());
- $end_time = ((float) $usec + (float) $sec);
- // Get duration in milliseconds, round to 3dp's
- $duration = number_format(($end_time - $start_time) * 1000, 3);
-} else {
- $duration = null;
-}
-
-// Reload the browser as we may have made schema changes
-$_reload_browser = true;
-
-// Display duration if we know it
-if ($duration !== null) {
- echo "<p>", sprintf($lang['strruntime'], $duration), "</p>\n";
-}
-
-echo "<p>{$lang['strsqlexecuted']}</p>\n";
-
-$navlinks = [];
-$fields = [
- 'server' => $_REQUEST['server'],
- 'database' => $_REQUEST['database'],
-];
-
-if (isset($_REQUEST['schema'])) {
- $fields['schema'] = $_REQUEST['schema'];
-}
-
-// Return
-if (isset($_REQUEST['return'])) {
- $urlvars = $misc->getSubjectParams($_REQUEST['return']);
- $navlinks['back'] = [
- 'attr' => [
- 'href' => [
- 'url' => $urlvars['url'],
- 'urlvars' => $urlvars['params'],
- ],
- ],
- 'content' => $lang['strback'],
- ];
-}
-
-// Edit
-$navlinks['alter'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'database.php',
- 'urlvars' => array_merge($fields, [
- 'action' => 'sql',
- ]),
- ],
- ],
- 'content' => $lang['streditsql'],
-];
-
-// Create view and download
-if (isset($_SESSION['sqlquery']) && isset($rs) && is_object($rs) && $rs->recordCount() > 0) {
- // Report views don't set a schema, so we need to disable create view in that case
- if (isset($_REQUEST['schema'])) {
- $navlinks['createview'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'views.php',
- 'urlvars' => array_merge($fields, [
- 'action' => 'create',
- ]),
- ],
- ],
- 'content' => $lang['strcreateview'],
- ];
- }
-
- if (isset($_REQUEST['search_path'])) {
- $fields['search_path'] = $_REQUEST['search_path'];
- }
-
- $navlinks['download'] = [
- 'attr' => [
- 'href' => [
- 'url' => 'dataexport.php',
- 'urlvars' => $fields,
- ],
- ],
- 'content' => $lang['strdownload'],
- ];
-}
-
-$misc->printNavLinks($navlinks, 'sql-form', get_defined_vars());
+$sqlquery_controller = new \PHPPgAdmin\Controller\SQLQueryController($container);
-$misc->printFooter();
+$sqlquery_controller->render();
diff --git a/src/views/sqledit.php b/src/views/sqledit.php
index 2fa2854e..3689c207 100644
--- a/src/views/sqledit.php
+++ b/src/views/sqledit.php
@@ -8,148 +8,8 @@
// Include application functions
//require_once '../lib.inc.php';
+require_once '../lib.inc.php';
-/**
- * Private function to display server and list of databases
- */
-function _printConnection($container, $action) {
-
- $lang = $container->get('lang');
- $conf = $container->get('conf');
- $misc = $container->get('misc');
-
- $data = $misc->getDatabaseAccessor();
-
- // The javascript action on the select box reloads the
- // popup whenever the server or database is changed.
- // This ensures that the correct page encoding is used.
- $onchange = "onchange=\"location.href='/sqledit/" .
- urlencode($action) . "?server=' + encodeURI(server.options[server.selectedIndex].value) + '&amp;database=' + encodeURI(database.options[database.selectedIndex].value) + ";
-
- // The exact URL to reload to is different between SQL and Find mode, however.
- if ($action == 'find') {
- $onchange .= "'&amp;term=' + encodeURI(term.value) + '&amp;filter=' + encodeURI(filter.value) + '&amp;'\"";
- } else {
- $onchange .= "'&amp;query=' + encodeURI(query.value) + '&amp;search_path=' + encodeURI(search_path.value) + (paginate.checked ? '&amp;paginate=on' : '') + '&amp;'\"";
- }
-
- return $misc->printConnection($onchange, false);
-}
-
-/**
- * Searches for a named database object
- */
-function doFind($container) {
- $lang = $container->get('lang');
- $conf = $container->get('conf');
- $misc = $container->get('misc');
-
- $data = $misc->getDatabaseAccessor();
-
- if (!isset($_REQUEST['term'])) {
- $_REQUEST['term'] = '';
- }
-
- if (!isset($_REQUEST['filter'])) {
- $_REQUEST['filter'] = '';
- }
-
- $default_html = $misc->printTabs($misc->getNavTabs('popup'), 'find', false);
-
- $default_html .= "<form action=\"database.php\" method=\"post\" target=\"detail\">\n";
- $default_html .= _printConnection($container, 'find');
- $default_html .= "<p><input class=\"focusme\" name=\"term\" value=\"" . htmlspecialchars($_REQUEST['term']) . "\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" />\n";
-
- // Output list of filters. This is complex due to all the 'has' and 'conf' feature possibilities
- $default_html .= "<select name=\"filter\">\n";
- $default_html .= "\t<option value=\"\"" . ($_REQUEST['filter'] == '' ? ' selected="selected" ' : '') . ">{$lang['strallobjects']}</option>\n";
- $default_html .= "\t<option value=\"SCHEMA\"" . ($_REQUEST['filter'] == 'SCHEMA' ? ' selected="selected" ' : '') . ">{$lang['strschemas']}</option>\n";
- $default_html .= "\t<option value=\"TABLE\"" . ($_REQUEST['filter'] == 'TABLE' ? ' selected="selected" ' : '') . ">{$lang['strtables']}</option>\n";
- $default_html .= "\t<option value=\"VIEW\"" . ($_REQUEST['filter'] == 'VIEW' ? ' selected="selected" ' : '') . ">{$lang['strviews']}</option>\n";
- $default_html .= "\t<option value=\"SEQUENCE\"" . ($_REQUEST['filter'] == 'SEQUENCE' ? ' selected="selected" ' : '') . ">{$lang['strsequences']}</option>\n";
- $default_html .= "\t<option value=\"COLUMN\"" . ($_REQUEST['filter'] == 'COLUMN' ? ' selected="selected" ' : '') . ">{$lang['strcolumns']}</option>\n";
- $default_html .= "\t<option value=\"RULE\"" . ($_REQUEST['filter'] == 'RULE' ? ' selected="selected" ' : '') . ">{$lang['strrules']}</option>\n";
- $default_html .= "\t<option value=\"INDEX\"" . ($_REQUEST['filter'] == 'INDEX' ? ' selected="selected" ' : '') . ">{$lang['strindexes']}</option>\n";
- $default_html .= "\t<option value=\"TRIGGER\"" . ($_REQUEST['filter'] == 'TRIGGER' ? ' selected="selected" ' : '') . ">{$lang['strtriggers']}</option>\n";
- $default_html .= "\t<option value=\"CONSTRAINT\"" . ($_REQUEST['filter'] == 'CONSTRAINT' ? ' selected="selected" ' : '') . ">{$lang['strconstraints']}</option>\n";
- $default_html .= "\t<option value=\"FUNCTION\"" . ($_REQUEST['filter'] == 'FUNCTION' ? ' selected="selected" ' : '') . ">{$lang['strfunctions']}</option>\n";
- $default_html .= "\t<option value=\"DOMAIN\"" . ($_REQUEST['filter'] == 'DOMAIN' ? ' selected="selected" ' : '') . ">{$lang['strdomains']}</option>\n";
- if ($conf['show_advanced']) {
- $default_html .= "\t<option value=\"AGGREGATE\"" . ($_REQUEST['filter'] == 'AGGREGATE' ? ' selected="selected" ' : '') . ">{$lang['straggregates']}</option>\n";
- $default_html .= "\t<option value=\"TYPE\"" . ($_REQUEST['filter'] == 'TYPE' ? ' selected="selected" ' : '') . ">{$lang['strtypes']}</option>\n";
- $default_html .= "\t<option value=\"OPERATOR\"" . ($_REQUEST['filter'] == 'OPERATOR' ? ' selected="selected" ' : '') . ">{$lang['stroperators']}</option>\n";
- $default_html .= "\t<option value=\"OPCLASS\"" . ($_REQUEST['filter'] == 'OPCLASS' ? ' selected="selected" ' : '') . ">{$lang['stropclasses']}</option>\n";
- $default_html .= "\t<option value=\"CONVERSION\"" . ($_REQUEST['filter'] == 'CONVERSION' ? ' selected="selected" ' : '') . ">{$lang['strconversions']}</option>\n";
- $default_html .= "\t<option value=\"LANGUAGE\"" . ($_REQUEST['filter'] == 'LANGUAGE' ? ' selected="selected" ' : '') . ">{$lang['strlanguages']}</option>\n";
- }
- $default_html .= "</select>\n";
-
- $default_html .= "<input type=\"submit\" value=\"{$lang['strfind']}\" />\n";
- $default_html .= "<input type=\"hidden\" name=\"action\" value=\"find\" /></p>\n";
- $default_html .= "</form>\n";
-
- // Default focus
- //$misc->setFocus('forms[0].term');
- return $default_html;
-}
-
-/**
- * Allow execution of arbitrary SQL statements on a database
- */
-function doDefault($container) {
-
- $lang = $container->get('lang');
- $misc = $container->get('misc');
- $data = $misc->getDatabaseAccessor();
-
- if (!isset($_SESSION['sqlquery'])) {
- $_SESSION['sqlquery'] = '';
- }
-
- $default_html = $misc->printTabs($misc->getNavTabs('popup'), 'sql', false);
-
- $default_html .= '<form action="/src/views/sql.php" method="post" enctype="multipart/form-data" class="sqlform" id="sqlform" target="detail">';
- $default_html .= "\n";
- $default_html .= _printConnection($container, 'sql');
-
- $default_html .= "\n";
-
- if (!isset($_REQUEST['search_path'])) {
- $_REQUEST['search_path'] = implode(',', $data->getSearchPath());
- }
- $search_path = htmlspecialchars($_REQUEST['search_path']);
- $sqlquery = htmlspecialchars($_SESSION['sqlquery']);
-
- $default_html .= ' <div class="searchpath">';
- $default_html .= "<label>";
- $default_html .= $misc->printHelp($lang['strsearchpath'], 'pg.schema.search_path', false);
-
- $default_html .= ": <input type=\"text\" name=\"search_path\" size=\"50\" value=\"" . $search_path . "\" />";
- $default_html .= "</label>\n";
- $default_html .= "</div>\n";
-
- $default_html .= '<div id="queryedition" style="padding:1%;width:98%;float:left;">';
- $default_html .= "\n<textarea style=\"width:98%;\" rows=\"10\" cols=\"50\" name=\"query\" id=\"query\">" . $sqlquery . "</textarea>\n";
- $default_html .= "</div>\n";
-
- // Check that file uploads are enabled
- if (ini_get('file_uploads')) {
- // Don't show upload option if max size of uploads is zero
- $max_size = $misc->inisizeToBytes(ini_get('upload_max_filesize'));
- if (is_double($max_size) && $max_size > 0) {
- $default_html .= "<p><input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"{$max_size}\" />\n";
- $default_html .= "<label for=\"script\">{$lang['struploadscript']}</label> <input id=\"script\" name=\"script\" type=\"file\" /></p>\n";
- }
- }
- $checked = (isset($_REQUEST['paginate']) ? ' checked="checked"' : '');
- $default_html .= "<p><label for=\"paginate\"><input type=\"checkbox\" id=\"paginate\" name=\"paginate\"" . $checked . " />&nbsp;{$lang['strpaginate']}</label></p>\n";
-
- $default_html .= "<p><input type=\"submit\" name=\"execute\" accesskey=\"r\" value=\"{$lang['strexecute']}\" />\n";
- $default_html .= "<input type=\"reset\" accesskey=\"q\" value=\"{$lang['strreset']}\" /></p>\n";
- $default_html .= "</form>\n";
-
- // Default focus
- //$misc->setFocus('forms[0].query');
- return $default_html;
+$sqledit_controller = new \PHPPgAdmin\Controller\SQLEditController($container);
-}
+$sqledit_controller->render();
diff --git a/src/views/tables.php b/src/views/tables.php
index 2e21f5b7..8043bd8f 100644
--- a/src/views/tables.php
+++ b/src/views/tables.php
@@ -10,80 +10,4 @@
require_once '../lib.inc.php';
$table_controller = new \PHPPgAdmin\Controller\TableController($container);
-
-$misc->printHeader($lang['strtables']);
-$misc->printBody();
-
-switch ($action) {
- case 'create':
- if (isset($_POST['cancel'])) {
- $table_controller->doDefault();
- } else {
- $table_controller->doCreate();
- }
-
- break;
- case 'createlike':
- $table_controller->doCreateLike(false);
- break;
- case 'confcreatelike':
- if (isset($_POST['cancel'])) {
- $table_controller->doDefault();
- } else {
- $table_controller->doCreateLike(true);
- }
-
- break;
- case 'selectrows':
- if (!isset($_POST['cancel'])) {
- $table_controller->doSelectRows(false);
- } else {
- $table_controller->doDefault();
- }
-
- break;
- case 'confselectrows':
- $table_controller->doSelectRows(true);
- break;
- case 'insertrow':
- if (!isset($_POST['cancel'])) {
- $table_controller->doInsertRow(false);
- } else {
- $table_controller->doDefault();
- }
-
- break;
- case 'confinsertrow':
- $table_controller->doInsertRow(true);
- break;
- case 'empty':
- if (isset($_POST['empty'])) {
- $table_controller->doEmpty(false);
- } else {
- $table_controller->doDefault();
- }
-
- break;
- case 'confirm_empty':
- $table_controller->doEmpty(true);
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $table_controller->doDrop(false);
- } else {
- $table_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $table_controller->doDrop(true);
- break;
- default:
- if ($table_controller->adminActions($action, 'table') === false) {
- $table_controller->doDefault();
- }
-
- break;
-}
-
-$misc->printFooter();
+$table_controller->render(); \ No newline at end of file
diff --git a/src/views/tablespaces.php b/src/views/tablespaces.php
index 0ec0af61..3fb9e3cb 100755
--- a/src/views/tablespaces.php
+++ b/src/views/tablespaces.php
@@ -9,353 +9,6 @@
// Include application functions
require_once '../lib.inc.php';
-$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : '';
-if (!isset($msg)) {
- $msg = '';
-}
+$tablespaces_controller = new \PHPPgAdmin\Controller\TableSpacesController($container);
-/**
- * Function to allow altering of a tablespace
- */
-function doAlter($msg = '') {
- global $data, $misc;
- global $lang;
-
- $misc->printTrail('tablespace');
- $misc->printTitle($lang['stralter'], 'pg.tablespace.alter');
- $misc->printMsg($msg);
-
- // Fetch tablespace info
- $tablespace = $data->getTablespace($_REQUEST['tablespace']);
- // Fetch all users
- $users = $data->getUsers();
-
- if ($tablespace->recordCount() > 0) {
-
- if (!isset($_POST['name'])) {
- $_POST['name'] = $tablespace->fields['spcname'];
- }
-
- if (!isset($_POST['owner'])) {
- $_POST['owner'] = $tablespace->fields['spcowner'];
- }
-
- if (!isset($_POST['comment'])) {
- $_POST['comment'] = ($data->hasSharedComments()) ? $tablespace->fields['spccomment'] : '';
- }
-
- echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
- echo $misc->form;
- echo "<table>\n";
- echo "<tr><th class=\"data left required\">{$lang['strname']}</th>\n";
- echo "<td class=\"data1\">";
- echo "<input name=\"name\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" value=\"",
- htmlspecialchars($_POST['name']), "\" /></td></tr>\n";
- echo "<tr><th class=\"data left required\">{$lang['strowner']}</th>\n";
- echo "<td class=\"data1\"><select name=\"owner\">";
- while (!$users->EOF) {
- $uname = $users->fields['usename'];
- echo "<option value=\"", htmlspecialchars($uname), "\"",
- ($uname == $_POST['owner']) ? ' selected="selected"' : '', ">", htmlspecialchars($uname), "</option>\n";
- $users->moveNext();
- }
- echo "</select></td></tr>\n";
- if ($data->hasSharedComments()) {
- echo "<tr><th class=\"data left\">{$lang['strcomment']}</th>\n";
- echo "<td class=\"data1\">";
- echo "<textarea rows=\"3\" cols=\"32\" name=\"comment\">",
- htmlspecialchars($_POST['comment']), "</textarea></td></tr>\n";
- }
- echo "</table>\n";
- echo "<p><input type=\"hidden\" name=\"action\" value=\"save_edit\" />\n";
- echo "<input type=\"hidden\" name=\"tablespace\" value=\"", htmlspecialchars($_REQUEST['tablespace']), "\" />\n";
- echo "<input type=\"submit\" name=\"alter\" value=\"{$lang['stralter']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
- echo "</form>\n";
- } else {
- echo "<p>{$lang['strnodata']}</p>\n";
- }
-
-}
-
-/**
- * Function to save after altering a tablespace
- */
-function doSaveAlter() {
- global $data, $lang;
-
- // Check data
- if (trim($_POST['name']) == '') {
- doAlter($lang['strtablespaceneedsname']);
- } else {
- $status = $data->alterTablespace($_POST['tablespace'], $_POST['name'], $_POST['owner'], $_POST['comment']);
- if ($status == 0) {
- // If tablespace has been renamed, need to change to the new name
- if ($_POST['tablespace'] != $_POST['name']) {
- // Jump them to the new table name
- $_REQUEST['tablespace'] = $_POST['name'];
- }
- doDefault($lang['strtablespacealtered']);
- } else {
- doAlter($lang['strtablespacealteredbad']);
- }
-
- }
-}
-
-/**
- * Show confirmation of drop and perform actual drop
- */
-function doDrop($confirm) {
- global $data, $misc;
- global $lang;
-
- if ($confirm) {
- $misc->printTrail('tablespace');
- $misc->printTitle($lang['strdrop'], 'pg.tablespace.drop');
-
- echo "<p>", sprintf($lang['strconfdroptablespace'], $misc->printVal($_REQUEST['tablespace'])), "</p>\n";
-
- echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
- echo $misc->form;
- echo "<input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
- echo "<input type=\"hidden\" name=\"tablespace\" value=\"", htmlspecialchars($_REQUEST['tablespace']), "\" />\n";
- echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
- echo "</form>\n";
- } else {
- $status = $data->droptablespace($_REQUEST['tablespace']);
- if ($status == 0) {
- doDefault($lang['strtablespacedropped']);
- } else {
- doDefault($lang['strtablespacedroppedbad']);
- }
-
- }
-}
-
-/**
- * Displays a screen where they can enter a new tablespace
- */
-function doCreate($msg = '') {
- global $data, $misc, $spcname;
- global $lang;
-
- $server_info = $misc->getServerInfo();
-
- if (!isset($_POST['formSpcname'])) {
- $_POST['formSpcname'] = '';
- }
-
- if (!isset($_POST['formOwner'])) {
- $_POST['formOwner'] = $server_info['username'];
- }
-
- if (!isset($_POST['formLoc'])) {
- $_POST['formLoc'] = '';
- }
-
- if (!isset($_POST['formComment'])) {
- $_POST['formComment'] = '';
- }
-
- // Fetch all users
- $users = $data->getUsers();
-
- $misc->printTrail('server');
- $misc->printTitle($lang['strcreatetablespace'], 'pg.tablespace.create');
- $misc->printMsg($msg);
-
- echo "<form action=\"/src/views/tablespaces.php\" method=\"post\">\n";
- echo $misc->form;
- echo "<table>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strname']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"32\" name=\"formSpcname\" maxlength=\"{$data->_maxNameLen}\" value=\"", htmlspecialchars($_POST['formSpcname']), "\" /></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strowner']}</th>\n";
- echo "\t\t<td class=\"data1\"><select name=\"formOwner\">\n";
- while (!$users->EOF) {
- $uname = $users->fields['usename'];
- echo "\t\t\t<option value=\"", htmlspecialchars($uname), "\"",
- ($uname == $_POST['formOwner']) ? ' selected="selected"' : '', ">", htmlspecialchars($uname), "</option>\n";
- $users->moveNext();
- }
- echo "\t\t</select></td>\n\t</tr>\n";
- echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strlocation']}</th>\n";
- echo "\t\t<td class=\"data1\"><input size=\"32\" name=\"formLoc\" value=\"", htmlspecialchars($_POST['formLoc']), "\" /></td>\n\t</tr>\n";
- // Comments (if available)
- if ($data->hasSharedComments()) {
- echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strcomment']}</th>\n";
- echo "\t\t<td><textarea name=\"formComment\" rows=\"3\" cols=\"32\">",
- htmlspecialchars($_POST['formComment']), "</textarea></td>\n\t</tr>\n";
- }
- echo "</table>\n";
- echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
- echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
- echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
- echo "</form>\n";
-}
-
-/**
- * Actually creates the new tablespace in the cluster
- */
-function doSaveCreate() {
- global $data;
- global $lang;
-
- // Check data
- if (trim($_POST['formSpcname']) == '') {
- doCreate($lang['strtablespaceneedsname']);
- } elseif (trim($_POST['formLoc']) == '') {
- doCreate($lang['strtablespaceneedsloc']);
- } else {
- // Default comment to blank if it isn't set
- if (!isset($_POST['formComment'])) {
- $_POST['formComment'] = null;
- }
-
- $status = $data->createTablespace($_POST['formSpcname'], $_POST['formOwner'], $_POST['formLoc'], $_POST['formComment']);
- if ($status == 0) {
- doDefault($lang['strtablespacecreated']);
- } else {
- doCreate($lang['strtablespacecreatedbad']);
- }
-
- }
-}
-
-/**
- * Show default list of tablespaces in the cluster
- */
-function doDefault($msg = '') {
- global $data, $misc;
- global $lang;
-
- $misc->printTrail('server');
- $misc->printTabs('server', 'tablespaces');
- $misc->printMsg($msg);
-
- $tablespaces = $data->getTablespaces();
-
- $columns = [
- 'database' => [
- 'title' => $lang['strname'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
- ],
- 'owner' => [
- 'title' => $lang['strowner'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('spcowner'),
- ],
- 'location' => [
- 'title' => $lang['strlocation'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('spclocation'),
- ],
- 'actions' => [
- 'title' => $lang['stractions'],
- ],
- ];
-
- if ($data->hasSharedComments()) {
- $columns['comment'] = [
- 'title' => $lang['strcomment'],
- 'field' => \PHPPgAdmin\Decorators\Decorator::field('spccomment'),
- ];
- }
-
- $actions = [
- 'alter' => [
- 'content' => $lang['stralter'],
- 'attr' => [
- 'href' => [
- 'url' => 'tablespaces.php',
- 'urlvars' => [
- 'action' => 'edit',
- 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
- ],
- ],
- ],
- ],
- 'drop' => [
- 'content' => $lang['strdrop'],
- 'attr' => [
- 'href' => [
- 'url' => 'tablespaces.php',
- 'urlvars' => [
- 'action' => 'confirm_drop',
- 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
- ],
- ],
- ],
- ],
- 'privileges' => [
- 'content' => $lang['strprivileges'],
- 'attr' => [
- 'href' => [
- 'url' => 'privileges.php',
- 'urlvars' => [
- 'subject' => 'tablespace',
- 'tablespace' => \PHPPgAdmin\Decorators\Decorator::field('spcname'),
- ],
- ],
- ],
- ],
- ];
-
- echo $misc->printTable($tablespaces, $columns, $actions, 'tablespaces-tablespaces', $lang['strnotablespaces']);
-
- $misc->printNavLinks(['create' => [
- 'attr' => [
- 'href' => [
- 'url' => 'tablespaces.php',
- 'urlvars' => [
- 'action' => 'create',
- 'server' => $_REQUEST['server'],
- ],
- ],
- ],
- 'content' => $lang['strcreatetablespace'],
- ]], 'tablespaces-tablespaces', get_defined_vars());
-}
-
-$misc->printHeader($lang['strtablespaces']);
-$misc->printBody();
-
-switch ($action) {
- case 'save_create':
- if (isset($_REQUEST['cancel'])) {
- doDefault();
- } else {
- doSaveCreate();
- }
-
- break;
- case 'create':
- doCreate();
- break;
- case 'drop':
- if (isset($_REQUEST['cancel'])) {
- doDefault();
- } else {
- doDrop(false);
- }
-
- break;
- case 'confirm_drop':
- doDrop(true);
- break;
- case 'save_edit':
- if (isset($_REQUEST['cancel'])) {
- doDefault();
- } else {
- doSaveAlter();
- }
-
- break;
- case 'edit':
- doAlter();
- break;
- default:
- doDefault();
- break;
-}
-
-$misc->printFooter();
+$tablespaces_controller->render();
diff --git a/src/views/tblproperties.php b/src/views/tblproperties.php
index 0aa6fd54..8072d748 100644
--- a/src/views/tblproperties.php
+++ b/src/views/tblproperties.php
@@ -11,57 +11,4 @@ require_once '../lib.inc.php';
$tableproperty_controller = new \PHPPgAdmin\Controller\TablePropertyController($container);
-$misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table']);
-$misc->printBody();
-
-switch ($action) {
- case 'alter':
- if (isset($_POST['alter'])) {
- $tableproperty_controller->doSaveAlter();
- } else {
- $tableproperty_controller->doDefault();
- }
-
- break;
- case 'confirm_alter':
- $tableproperty_controller->doAlter();
- break;
- case 'import':
- $tableproperty_controller->doImport();
- break;
- case 'export':
- $tableproperty_controller->doExport();
- break;
- case 'add_column':
- if (isset($_POST['cancel'])) {
- $tableproperty_controller->doDefault();
- } else {
- $tableproperty_controller->doAddColumn();
- }
-
- break;
- case 'properties':
- if (isset($_POST['cancel'])) {
- $tableproperty_controller->doDefault();
- } else {
- $tableproperty_controller->doProperties();
- }
-
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $tableproperty_controller->doDrop(false);
- } else {
- $tableproperty_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $tableproperty_controller->doDrop(true);
- break;
- default:
- $tableproperty_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$tableproperty_controller->render(); \ No newline at end of file
diff --git a/src/views/triggers.php b/src/views/triggers.php
index 53b916e2..3c756b95 100644
--- a/src/views/triggers.php
+++ b/src/views/triggers.php
@@ -10,69 +10,4 @@
require_once '../lib.inc.php';
$trigger_controller = new \PHPPgAdmin\Controller\TriggerController($container);
-
-$misc->printHeader($lang['strtables'] . ' - ' . $_REQUEST['table'] . ' - ' . $lang['strtriggers']);
-$misc->printBody();
-
-switch ($action) {
- case 'alter':
- if (isset($_POST['alter'])) {
- $trigger_controller->doSaveAlter();
- } else {
- $trigger_controller->doDefault();
- }
-
- break;
- case 'confirm_alter':
- $trigger_controller->doAlter();
- break;
- case 'confirm_enable':
- $trigger_controller->doEnable(true);
- break;
- case 'confirm_disable':
- $trigger_controller->doDisable(true);
- break;
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $trigger_controller->doDefault();
- } else {
- $trigger_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- $trigger_controller->doCreate();
- break;
- case 'drop':
- if (isset($_POST['yes'])) {
- $trigger_controller->doDrop(false);
- } else {
- $trigger_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $trigger_controller->doDrop(true);
- break;
- case 'enable':
- if (isset($_POST['yes'])) {
- $trigger_controller->doEnable(false);
- } else {
- $trigger_controller->doDefault();
- }
-
- break;
- case 'disable':
- if (isset($_POST['yes'])) {
- $trigger_controller->doDisable(false);
- } else {
- $trigger_controller->doDefault();
- }
-
- break;
- default:
- $trigger_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$trigger_controller->render();
diff --git a/src/views/types.php b/src/views/types.php
index 81e24dba..21ddba16 100644
--- a/src/views/types.php
+++ b/src/views/types.php
@@ -10,55 +10,4 @@
require_once '../lib.inc.php';
$type_controller = new \PHPPgAdmin\Controller\TypeController($container);
-
-$misc->printHeader($lang['strtypes']);
-$misc->printBody();
-
-switch ($action) {
- case 'create_comp':
- if (isset($_POST['cancel'])) {
- $type_controller->doDefault();
- } else {
- $type_controller->doCreateComposite();
- }
-
- break;
- case 'create_enum':
- if (isset($_POST['cancel'])) {
- $type_controller->doDefault();
- } else {
- $type_controller->doCreateEnum();
- }
-
- break;
- case 'save_create':
- if (isset($_POST['cancel'])) {
- $type_controller->doDefault();
- } else {
- $type_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- $type_controller->doCreate();
- break;
- case 'drop':
- if (isset($_POST['cancel'])) {
- $type_controller->doDefault();
- } else {
- $type_controller->doDrop(false);
- }
-
- break;
- case 'confirm_drop':
- $type_controller->doDrop(true);
- break;
- case 'properties':
- $type_controller->doProperties();
- break;
- default:
- $type_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$type_controller->render();
diff --git a/src/views/viewproperties.php b/src/views/viewproperties.php
index 4148854c..f0680ee7 100755
--- a/src/views/viewproperties.php
+++ b/src/views/viewproperties.php
@@ -10,61 +10,4 @@
require_once '../lib.inc.php';
$viewproperty_controller = new \PHPPgAdmin\Controller\ViewPropertyController($container);
-
-$misc->printHeader($lang['strviews'] . ' - ' . $_REQUEST['view']);
-$misc->printBody();
-
-switch ($action) {
- case 'save_edit':
- if (isset($_POST['cancel'])) {
- $viewproperty_controller->doDefinition();
- } else {
- $viewproperty_controller->doSaveEdit();
- }
-
- break;
- case 'edit':
- $viewproperty_controller->doEdit();
- break;
- case 'export':
- $viewproperty_controller->doExport();
- break;
- case 'definition':
- $viewproperty_controller->doDefinition();
- break;
- case 'properties':
- if (isset($_POST['cancel'])) {
- $viewproperty_controller->doDefault();
- } else {
- $viewproperty_controller->doProperties();
- }
-
- break;
- case 'alter':
- if (isset($_POST['alter'])) {
- $viewproperty_controller->doAlter(false);
- } else {
- $viewproperty_controller->doDefault();
- }
-
- break;
- case 'confirm_alter':
- doAlter(true);
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $viewproperty_controller->doDrop(false);
- } else {
- $viewproperty_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $viewproperty_controller->doDrop(true);
- break;
- default:
- $viewproperty_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$viewproperty_controller->render();
diff --git a/src/views/views.php b/src/views/views.php
index e2d62965..9c2cec16 100644
--- a/src/views/views.php
+++ b/src/views/views.php
@@ -11,65 +11,4 @@ require_once '../lib.inc.php';
$view_controller = new \PHPPgAdmin\Controller\ViewController($container);
-$misc->printHeader($lang['strviews']);
-$misc->printBody();
-
-switch ($action) {
- case 'selectrows':
- if (!isset($_REQUEST['cancel'])) {
- $view_controller->doSelectRows(false);
- } else {
- $view_controller->doDefault();
- }
-
- break;
- case 'confselectrows':
- $view_controller->doSelectRows(true);
- break;
- case 'save_create_wiz':
- if (isset($_REQUEST['cancel'])) {
- $view_controller->doDefault();
- } else {
- $view_controller->doSaveCreateWiz();
- }
-
- break;
- case 'wiz_create':
- doWizardCreate();
- break;
- case 'set_params_create':
- if (isset($_POST['cancel'])) {
- $view_controller->doDefault();
- } else {
- $view_controller->doSetParamsCreate();
- }
-
- break;
- case 'save_create':
- if (isset($_REQUEST['cancel'])) {
- $view_controller->doDefault();
- } else {
- $view_controller->doSaveCreate();
- }
-
- break;
- case 'create':
- doCreate();
- break;
- case 'drop':
- if (isset($_POST['drop'])) {
- $view_controller->doDrop(false);
- } else {
- $view_controller->doDefault();
- }
-
- break;
- case 'confirm_drop':
- $view_controller->doDrop(true);
- break;
- default:
- $view_controller->doDefault();
- break;
-}
-
-$misc->printFooter();
+$view_controller->render(); \ No newline at end of file
diff --git a/src/xhtml/HTMLController.php b/src/xhtml/HTMLController.php
new file mode 100644
index 00000000..2a794fde
--- /dev/null
+++ b/src/xhtml/HTMLController.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace PHPPgAdmin\XHtml;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Base controller class
+ */
+class HTMLController {
+ private $container = null;
+ private $_connection = null;
+ private $_reload_browser = false;
+ private $app = null;
+ private $data = null;
+ private $database = null;
+ private $server_id = null;
+ public $appLangFiles = [];
+ public $appThemes = [];
+ public $appName = '';
+ public $appVersion = '';
+ public $form = '';
+ public $href = '';
+ public $lang = [];
+ public $action = '';
+ public $_name = 'HTMLController';
+ public $_title = 'base';
+ private $table_controller = null;
+ private $trail_controller = null;
+
+ /* Constructor */
+ function __construct(\Slim\Container $container) {
+ $this->container = $container;
+ $this->lang = $container->get('lang');
+ $this->conf = $container->get('conf');
+ $this->view = $container->get('view');
+ $this->plugin_manager = $container->get('plugin_manager');
+ $this->appName = $container->get('settings')['appName'];
+ $this->appVersion = $container->get('settings')['appVersion'];
+ $this->appLangFiles = $container->get('appLangFiles');
+ $this->misc = $container->get('misc');
+ $this->appThemes = $container->get('appThemes');
+ $this->action = $container->get('action');
+
+ //\PC::debug($this->_name, 'instanced controller');
+ }
+
+ public function getContainer() {
+ return $this->container;
+ }
+
+ /**
+ * Returns URL given an action associative array.
+ * NOTE: this function does not html-escape, only url-escape
+ * @param $action An associative array of the follow properties:
+ * 'url' => The first part of the URL (before the ?)
+ * 'urlvars' => Associative array of (URL variable => field name)
+ * these are appended to the URL
+ * @param $fields Field data from which 'urlfield' and 'vars' are obtained.
+ */
+ protected function getActionUrl(&$action, &$fields) {
+ $url = Decorator::get_sanitized_value($action['url'], $fields);
+
+ if ($url === false) {
+ return '';
+ }
+
+ if (!empty($action['urlvars'])) {
+ $urlvars = Decorator::get_sanitized_value($action['urlvars'], $fields);
+ } else {
+ $urlvars = [];
+ }
+
+ /* set server, database and schema parameter if not presents */
+ if (isset($urlvars['subject'])) {
+ $subject = Decorator::get_sanitized_value($urlvars['subject'], $fields);
+ } else {
+ $subject = '';
+ }
+
+ if (isset($_REQUEST['server']) and !isset($urlvars['server']) and $subject != 'root') {
+ $urlvars['server'] = $_REQUEST['server'];
+ if (isset($_REQUEST['database']) and !isset($urlvars['database']) and $subject != 'server') {
+ $urlvars['database'] = $_REQUEST['database'];
+ if (isset($_REQUEST['schema']) and !isset($urlvars['schema']) and $subject != 'database') {
+ $urlvars['schema'] = $_REQUEST['schema'];
+ }
+ }
+ }
+
+ $sep = '?';
+ foreach ($urlvars as $var => $varfield) {
+ $url .= $sep . Decorator::value_url($var, $fields) . '=' . Decorator::value_url($varfield, $fields);
+ $sep = '&';
+ }
+ //return '/src/views/' . $url;
+ return $url;
+ }
+ /**
+ * Display a link
+ * @param $link An associative array of link parameters to print
+ * link = array(
+ * 'attr' => array( // list of A tag attribute
+ * 'attrname' => attribute value
+ * ...
+ * ),
+ * 'content' => The link text
+ * 'fields' => (optionnal) the data from which content and attr's values are obtained
+ * );
+ * the special attribute 'href' might be a string or an array. If href is an array it
+ * will be generated by getActionUrl. See getActionUrl comment for array format.
+ */
+ function printLink($link, $do_print = true) {
+ \PC::debug($link, 'printLink');
+
+ if (!isset($link['fields'])) {
+ $link['fields'] = $_REQUEST;
+ }
+
+ $tag = "<a ";
+ foreach ($link['attr'] as $attr => $value) {
+ if ($attr == 'href' and is_array($value)) {
+ $tag .= 'href="' . htmlentities($this->getActionUrl($value, $link['fields'])) . '" ';
+ } else {
+ $tag .= htmlentities($attr) . '="' . Decorator::get_sanitized_value($value, $link['fields'], 'html') . '" ';
+ }
+ }
+ $tag .= ">" . Decorator::get_sanitized_value($link['content'], $link['fields'], 'html') . "</a>\n";
+
+ if ($do_print) {
+ echo $tag;
+ } else {
+ return $tag;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/xhtml/HTMLNavbarController.php b/src/xhtml/HTMLNavbarController.php
new file mode 100644
index 00000000..ab30dade
--- /dev/null
+++ b/src/xhtml/HTMLNavbarController.php
@@ -0,0 +1,535 @@
+<?php
+
+namespace PHPPgAdmin\XHtml;
+
+/**
+ * Class to render tables. Formerly part of Misc.php
+ *
+ */
+class HTMLNavbarController extends HTMLController {
+ public $_name = 'HTMLNavbarController';
+
+ /**
+ * Display a bread crumb trail.
+ * @param $do_print true to echo, false to return html
+ */
+ function printTrail($trail = [], $do_print = true) {
+ $lang = $this->lang;
+ $misc = $this->misc;
+
+ $trail_html = $this->printTopbar(false);
+
+ if (is_string($trail)) {
+ $trail = $this->getTrail($trail);
+ }
+
+ $trail_html .= "<div class=\"trail\"><table><tr>";
+
+ foreach ($trail as $crumb) {
+ $trail_html .= "<td class=\"crumb\">";
+ $crumblink = "<a";
+
+ if (isset($crumb['url'])) {
+ $crumblink .= " href=\"{$crumb['url']}\"";
+ }
+
+ if (isset($crumb['title'])) {
+ $crumblink .= " title=\"{$crumb['title']}\"";
+ }
+
+ $crumblink .= ">";
+
+ if (isset($crumb['title'])) {
+ $iconalt = $crumb['title'];
+ } else {
+ $iconalt = 'Database Root';
+ }
+
+ if (isset($crumb['icon']) && $icon = $misc->icon($crumb['icon'])) {
+ $crumblink .= "<span class=\"icon\"><img src=\"{$icon}\" alt=\"{$iconalt}\" /></span>";
+ }
+
+ $crumblink .= "<span class=\"label\">" . htmlspecialchars($crumb['text']) . "</span></a>";
+
+ if (isset($crumb['help'])) {
+ $trail_html .= $misc->printHelp($crumblink, $crumb['help'], false);
+ } else {
+ $trail_html .= $crumblink;
+ }
+
+ $trail_html .= "{$lang['strseparator']}";
+ $trail_html .= "</td>";
+ }
+
+ $trail_html .= "</tr></table></div>\n";
+ if ($do_print) {
+ echo $trail_html;
+ } else {
+ return $trail_html;
+ }
+ }
+
+ /**
+ * Display the navlinks
+ *
+ * @param $navlinks - An array with the the attributes and values that will be shown. See printLinksList for array format.
+ * @param $place - Place where the $navlinks are displayed. Like 'display-browse', where 'display' is the file (display.php)
+ * @param $env - Associative array of defined variables in the scope of the caller.
+ * Allows to give some environnement details to plugins.
+ * and 'browse' is the place inside that code (doBrowse).
+ * @param bool $do_print if true, print html, if false, return html
+ */
+ function printNavLinks($navlinks, $place, $env = [], $do_print = true) {
+ $plugin_manager = $this->plugin_manager;
+
+ // Navlinks hook's place
+ $plugin_functions_parameters = [
+ 'navlinks' => &$navlinks,
+ 'place' => $place,
+ 'env' => $env,
+ ];
+ $plugin_manager->do_hook('navlinks', $plugin_functions_parameters);
+
+ if (count($navlinks) > 0) {
+ if ($do_print) {
+ $this->printLinksList($navlinks, 'navlink');
+ } else {
+ return $this->printLinksList($navlinks, 'navlink', false);
+ }
+
+ }
+ }
+
+ /**
+ * Display navigation tabs
+ * @param $tabs The name of current section (Ex: intro, server, ...), or an array with tabs (Ex: sqledit.php doFind function)
+ * @param $activetab The name of the tab to be highlighted.
+ * @param $print if false, return html
+ */
+ function printTabs($tabs, $activetab, $do_print = true) {
+
+ $lang = $this->lang;
+ $misc = $this->misc;
+ $data = $misc->getDatabaseAccessor();
+
+ if (is_string($tabs)) {
+ $_SESSION['webdbLastTab'][$tabs] = $activetab;
+ $tabs = $misc->getNavTabs($tabs);
+ }
+ $tabs_html = '';
+ if (count($tabs) > 0) {
+
+ $tabs_html .= "<table class=\"tabs\"><tr>\n";
+
+ # FIXME: don't count hidden tabs
+ $width = (int) (100 / count($tabs)) . '%';
+ foreach ($tabs as $tab_id => $tab) {
+
+ $tabs[$tab_id]['active'] = $active = ($tab_id == $activetab) ? ' active' : '';
+
+ $tabs[$tab_id]['width'] = $width;
+
+ if (!isset($tab['hide']) || $tab['hide'] !== true) {
+
+ $tabs[$tab_id]['tablink'] = htmlentities($this->getActionUrl($tab, $_REQUEST));
+
+ $tablink = '<a href="' . $tabs[$tab_id]['tablink'] . '">';
+
+ if (isset($tab['icon']) && $icon = $misc->icon($tab['icon'])) {
+ $tabs[$tab_id]['iconurl'] = $icon;
+ $tablink .= "<span class=\"icon\"><img src=\"{$icon}\" alt=\"{$tab['title']}\" /></span>";
+ }
+
+ $tablink .= "<span class=\"label\">{$tab['title']}</span></a>";
+
+ $tabs_html .= "<td style=\"width: {$width}\" class=\"tab{$active}\">";
+
+ if (isset($tab['help'])) {
+ $tabs_html .= $misc->printHelp($tablink, $tab['help'], false);
+ } else {
+ $tabs_html .= $tablink;
+ }
+
+ $tabs_html .= "</td>\n";
+ }
+ }
+ $tabs_html .= "</tr></table>\n";
+ }
+
+ if ($do_print) {
+ echo $tabs_html;
+ } else {
+ return $tabs_html;
+ }
+
+ }
+
+ /**
+ * Get the URL for the last active tab of a particular tab bar.
+ */
+ function getLastTabURL($section) {
+ $lang = $this->lang;
+ $misc = $this->misc;
+
+ $tabs = $misc->getNavTabs($section);
+
+ if (isset($_SESSION['webdbLastTab'][$section]) && isset($tabs[$_SESSION['webdbLastTab'][$section]])) {
+ $tab = $tabs[$_SESSION['webdbLastTab'][$section]];
+ } else {
+ $tab = reset($tabs);
+ }
+ \PC::debug(['section' => $section, 'tabs' => $tabs, 'tab' => $tab], 'getLastTabURL');
+ return isset($tab['url']) ? $tab : null;
+ }
+
+ /**
+ * [printTopbar description]
+ * @param bool $do_print true to print, false to return html
+ * @return string
+ */
+ private function printTopbar($do_print = true) {
+
+ $lang = $this->lang;
+ $plugin_manager = $this->plugin_manager;
+ $misc = $this->misc;
+ $appName = $misc->appName;
+ $appVersion = $misc->appVersion;
+ $appLangFiles = $misc->appLangFiles;
+
+ $server_info = $misc->getServerInfo();
+ $reqvars = $misc->getRequestVars('table');
+
+ $topbar_html = "<div class=\"topbar\"><table style=\"width: 100%\"><tr><td>";
+
+ if ($server_info && isset($server_info['platform']) && isset($server_info['username'])) {
+ /* top left informations when connected */
+ $topbar_html .= sprintf($lang['strtopbar'],
+ '<span class="platform">' . htmlspecialchars($server_info['platform']) . '</span>',
+ '<span class="host">' . htmlspecialchars((empty($server_info['host'])) ? 'localhost' : $server_info['host']) . '</span>',
+ '<span class="port">' . htmlspecialchars($server_info['port']) . '</span>',
+ '<span class="username">' . htmlspecialchars($server_info['username']) . '</span>');
+
+ $topbar_html .= "</td>";
+
+ /* top right informations when connected */
+
+ $toplinks = [
+ 'sql' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'sqledit.php',
+ 'urlvars' => array_merge($reqvars, [
+ 'action' => 'sql',
+ ]),
+ ],
+ 'target' => "sqledit",
+ 'id' => 'toplink_sql',
+ ],
+ 'content' => $lang['strsql'],
+ ],
+ 'history' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'history.php',
+ 'urlvars' => array_merge($reqvars, [
+ 'action' => 'pophistory',
+ ]),
+ ],
+ 'id' => 'toplink_history',
+ ],
+ 'content' => $lang['strhistory'],
+ ],
+ 'find' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'sqledit.php',
+ 'urlvars' => array_merge($reqvars, [
+ 'action' => 'find',
+ ]),
+ ],
+ 'target' => "sqledit",
+ 'id' => 'toplink_find',
+ ],
+ 'content' => $lang['strfind'],
+ ],
+ 'logout' => [
+ 'attr' => [
+ 'href' => [
+ 'url' => 'servers.php',
+ 'urlvars' => [
+ 'action' => 'logout',
+ 'logoutServer' => "{$server_info['host']}:{$server_info['port']}:{$server_info['sslmode']}",
+ ],
+ ],
+ 'id' => 'toplink_logout',
+ ],
+ 'content' => $lang['strlogout'],
+ ],
+ ];
+
+ // Toplink hook's place
+ $plugin_functions_parameters = [
+ 'toplinks' => &$toplinks,
+ ];
+
+ $plugin_manager->do_hook('toplinks', $plugin_functions_parameters);
+
+ $topbar_html .= "<td style=\"text-align: right\">";
+
+ $topbar_html .= $this->printLinksList($toplinks, 'toplink', [], false);
+
+ $topbar_html .= "</td>";
+
+ $sql_window_id = htmlentities('sqledit:' . $this->server_id);
+ $history_window_id = htmlentities('history:' . $this->server_id);
+
+ $topbar_html .= "<script type=\"text/javascript\">
+ $('#toplink_sql').click(function() {
+ window.open($(this).attr('href'),'{$sql_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
+ return false;
+ });
+
+ $('#toplink_history').click(function() {
+ window.open($(this).attr('href'),'{$history_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
+ return false;
+ });
+
+ $('#toplink_find').click(function() {
+ window.open($(this).attr('href'),'{$sql_window_id}','toolbar=no,width=700,height=500,resizable=yes,scrollbars=yes').focus();
+ return false;
+ });
+ ";
+
+ if (isset($_SESSION['sharedUsername'])) {
+ $topbar_html .= sprintf("
+ $('#toplink_logout').click(function() {
+ return confirm('%s');
+ });", str_replace("'", "\'", $lang['strconfdropcred']));
+ }
+
+ $topbar_html .= "
+ </script>";
+ } else {
+ $topbar_html .= "<span class=\"appname\">{$appName}</span> <span class=\"version\">{$appVersion}</span>";
+ }
+ /*
+ echo "<td style=\"text-align: right; width: 1%\">";
+
+ echo "<form method=\"get\"><select name=\"language\" onchange=\"this.form.submit()\">\n";
+ $language = isset($_SESSION['webdbLanguage']) ? $_SESSION['webdbLanguage'] : 'english';
+ foreach ($appLangFiles as $k => $v) {
+ echo "<option value=\"{$k}\"",
+ ($k == $language) ? ' selected="selected"' : '',
+ ">{$v}</option>\n";
+ }
+ echo "</select>\n";
+ echo "<noscript><input type=\"submit\" value=\"Set Language\"></noscript>\n";
+ foreach ($_GET as $key => $val) {
+ if ($key == 'language') continue;
+ echo "<input type=\"hidden\" name=\"$key\" value=\"", htmlspecialchars($val), "\" />\n";
+ }
+ echo "</form>\n";
+
+ echo "</td>";
+ */
+ $topbar_html .= "</tr></table></div>\n";
+
+ if ($do_print) {
+ echo $topbar_html;
+ } else {
+ return $topbar_html;
+ }
+ }
+
+ /**
+ * Create a bread crumb trail of the object hierarchy.
+ * @param $object The type of object at the end of the trail.
+ */
+ private function getTrail($subject = null) {
+ $lang = $this->lang;
+ $plugin_manager = $this->plugin_manager;
+ $misc = $this->misc;
+ $appName = $misc->appName;
+
+ $data = $misc->getDatabaseAccessor();
+
+ $trail = [];
+ $vars = '';
+ $done = false;
+
+ $trail['root'] = [
+ 'text' => $appName,
+ 'url' => '/redirect/root',
+ 'icon' => 'Introduction',
+ ];
+
+ if ($subject == 'root') {
+ $done = true;
+ }
+
+ if (!$done) {
+ $server_info = $misc->getServerInfo();
+ $trail['server'] = [
+ 'title' => $lang['strserver'],
+ 'text' => $server_info['desc'],
+ 'url' => $misc->getHREFSubject('server'),
+ 'help' => 'pg.server',
+ 'icon' => 'Server',
+ ];
+ }
+ if ($subject == 'server') {
+ $done = true;
+ }
+
+ if (isset($_REQUEST['database']) && !$done) {
+ $trail['database'] = [
+ 'title' => $lang['strdatabase'],
+ 'text' => $_REQUEST['database'],
+ 'url' => $misc->getHREFSubject('database'),
+ 'help' => 'pg.database',
+ 'icon' => 'Database',
+ ];
+ } elseif (isset($_REQUEST['rolename']) && !$done) {
+ $trail['role'] = [
+ 'title' => $lang['strrole'],
+ 'text' => $_REQUEST['rolename'],
+ 'url' => $misc->getHREFSubject('role'),
+ 'help' => 'pg.role',
+ 'icon' => 'Roles',
+ ];
+ }
+ if ($subject == 'database' || $subject == 'role') {
+ $done = true;
+ }
+
+ if (isset($_REQUEST['schema']) && !$done) {
+ $trail['schema'] = [
+ 'title' => $lang['strschema'],
+ 'text' => $_REQUEST['schema'],
+ 'url' => $misc->getHREFSubject('schema'),
+ 'help' => 'pg.schema',
+ 'icon' => 'Schema',
+ ];
+ }
+ if ($subject == 'schema') {
+ $done = true;
+ }
+
+ if (isset($_REQUEST['table']) && !$done) {
+ $trail['table'] = [
+ 'title' => $lang['strtable'],
+ 'text' => $_REQUEST['table'],
+ 'url' => $misc->getHREFSubject('table'),
+ 'help' => 'pg.table',
+ 'icon' => 'Table',
+ ];
+ } elseif (isset($_REQUEST['view']) && !$done) {
+ $trail['view'] = [
+ 'title' => $lang['strview'],
+ 'text' => $_REQUEST['view'],
+ 'url' => $misc->getHREFSubject('view'),
+ 'help' => 'pg.view',
+ 'icon' => 'View',
+ ];
+ } elseif (isset($_REQUEST['ftscfg']) && !$done) {
+ $trail['ftscfg'] = [
+ 'title' => $lang['strftsconfig'],
+ 'text' => $_REQUEST['ftscfg'],
+ 'url' => $misc->getHREFSubject('ftscfg'),
+ 'help' => 'pg.ftscfg.example',
+ 'icon' => 'Fts',
+ ];
+ }
+ if ($subject == 'table' || $subject == 'view' || $subject == 'ftscfg') {
+ $done = true;
+ }
+
+ if (!$done && !is_null($subject)) {
+ switch ($subject) {
+ case 'function':
+ $trail[$subject] = [
+ 'title' => $lang['str' . $subject],
+ 'text' => $_REQUEST[$subject],
+ 'url' => $misc->getHREFSubject('function'),
+ 'help' => 'pg.function',
+ 'icon' => 'Function',
+ ];
+ break;
+ case 'aggregate':
+ $trail[$subject] = [
+ 'title' => $lang['straggregate'],
+ 'text' => $_REQUEST['aggrname'],
+ 'url' => $misc->getHREFSubject('aggregate'),
+ 'help' => 'pg.aggregate',
+ 'icon' => 'Aggregate',
+ ];
+ break;
+ case 'column':
+ $trail['column'] = [
+ 'title' => $lang['strcolumn'],
+ 'text' => $_REQUEST['column'],
+ 'icon' => 'Column',
+ 'url' => $misc->getHREFSubject('column'),
+ ];
+ break;
+ default:
+ if (isset($_REQUEST[$subject])) {
+ switch ($subject) {
+ case 'domain':$icon = 'Domain';
+ break;
+ case 'sequence':$icon = 'Sequence';
+ break;
+ case 'type':$icon = 'Type';
+ break;
+ case 'operator':$icon = 'Operator';
+ break;
+ default:$icon = null;
+ break;
+ }
+ $trail[$subject] = [
+ 'title' => $lang['str' . $subject],
+ 'text' => $_REQUEST[$subject],
+ 'help' => 'pg.' . $subject,
+ 'icon' => $icon,
+ ];
+ }
+ }
+ }
+
+ // Trail hook's place
+ $plugin_functions_parameters = [
+ 'trail' => &$trail,
+ 'section' => $subject,
+ ];
+
+ $plugin_manager->do_hook('trail', $plugin_functions_parameters);
+
+ return $trail;
+ }
+
+ /**
+ * Display a list of links
+ * @param $links An associative array of links to print. See printLink function for
+ * the links array format.
+ * @param $class An optional class or list of classes seprated by a space
+ * WARNING: This field is NOT escaped! No user should be able to inject something here, use with care.
+ * @param boolean $do_print true to echo, false to return
+ */
+ private function printLinksList($links, $class = '', $do_print = true) {
+ $misc = $this->misc;
+ \PC::debug($links, 'printLinksList');
+ $list_html = "<ul class=\"{$class}\">\n";
+ foreach ($links as $link) {
+ $list_html .= "\t<li>";
+ $list_html .= $this->printLink($link, false);
+ $list_html .= "</li>\n";
+ }
+ $list_html .= "</ul>\n";
+ if ($do_print) {
+ echo $list_html;
+ } else {
+ return $list_html;
+ }
+ }
+
+}
diff --git a/src/xhtml/HTMLTableController.php b/src/xhtml/HTMLTableController.php
new file mode 100644
index 00000000..d2035efd
--- /dev/null
+++ b/src/xhtml/HTMLTableController.php
@@ -0,0 +1,339 @@
+<?php
+
+namespace PHPPgAdmin\XHtml;
+use \PHPPgAdmin\Decorators\Decorator;
+
+/**
+ * Class to render tables. Formerly part of Misc.php
+ *
+ */
+class HTMLTableController extends HTMLController {
+ public $_name = 'HTMLTableController';
+ private $ma = [];
+
+ /**
+ * Display a table of data.
+ * @param $tabledata A set of data to be formatted, as returned by $data->getDatabases() etc.
+ * @param $columns An associative array of columns to be displayed:
+ * $columns = array(
+ * column_id => array(
+ * 'title' => Column heading,
+ * 'class' => The class to apply on the column cells,
+ * 'field' => Field name for $tabledata->fields[...],
+ * 'help' => Help page for this column,
+ * ), ...
+ * );
+ * @param $actions Actions that can be performed on each object:
+ * $actions = array(
+ * * multi action support
+ * * parameters are serialized for each entries and given in $_REQUEST['ma']
+ * 'multiactions' => array(
+ * 'keycols' => Associative array of (URL variable => field name), // fields included in the form
+ * 'url' => URL submission,
+ * 'default' => Default selected action in the form. If null, an empty action is added & selected
+ * ),
+ * * actions *
+ * action_id => array(
+ * 'title' => Action heading,
+ * 'url' => Static part of URL. Often we rely
+ * relative urls, usually the page itself (not '' !), or just a query string,
+ * 'vars' => Associative array of (URL variable => field name),
+ * 'multiaction' => Name of the action to execute.
+ * Add this action to the multi action form
+ * ), ...
+ * );
+ * @param $place Place where the $actions are displayed. Like 'display-browse', where 'display'
+ * is the entrypoint (/src/views/display.php) and 'browse' is the action used inside its controller (in this case, doBrowse).
+ * @param $nodata (optional) Message to display if data set is empty.
+ * @param $pre_fn (optional) callback closure for each row. It will be passed two params: $rowdata and $actions,
+ * it may be used to derive new fields or modify actions.
+ * It can return an array of actions specific to the row, or if nothing is returned then the standard actions are used.
+ * (see TablePropertyController and ConstraintController for examples)
+ * The function must not must not store urls because they are relative and won't work out of context.
+ */
+ public function printTable(&$tabledata, &$columns, &$actions, $place, $nodata = null, $pre_fn = null) {
+
+ $data = $this->data;
+ $misc = $this->misc;
+ $lang = $this->lang;
+ $plugin_manager = $this->plugin_manager;
+
+ // Action buttons hook's place
+ $plugin_functions_parameters = [
+ 'actionbuttons' => &$actions,
+ 'place' => $place,
+ ];
+ $plugin_manager->do_hook('actionbuttons', $plugin_functions_parameters);
+
+ if ($this->has_ma = isset($actions['multiactions'])) {
+ $this->ma = $actions['multiactions'];
+ }
+ $tablehtml = '';
+
+ unset($actions['multiactions']);
+
+ if ($tabledata->recordCount() > 0) {
+
+ // Remove the 'comment' column if they have been disabled
+ if (!$this->conf['show_comments']) {
+ unset($columns['comment']);
+ }
+
+ if (isset($columns['comment'])) {
+ // Uncomment this for clipped comments.
+ // TODO: This should be a user option.
+ //$columns['comment']['params']['clip'] = true;
+ }
+
+ if ($this->has_ma) {
+ $tablehtml .= "<script src=\"/js/multiactionform.js\" type=\"text/javascript\"></script>\n";
+ $tablehtml .= "<form id=\"multi_form\" action=\"{$this->ma['url']}\" method=\"post\" enctype=\"multipart/form-data\">\n";
+ if (isset($this->ma['vars'])) {
+ foreach ($this->ma['vars'] as $k => $v) {
+ $tablehtml .= "<input type=\"hidden\" name=\"$k\" value=\"$v\" />";
+ }
+ }
+
+ }
+
+ $tablehtml .= '<table width="auto" class="' . $place . '">' . "\n";
+
+ $tablehtml .= $this->getThead($columns, $actions);
+
+ $tablehtml .= $this->getTbody($columns, $actions, $tabledata, $pre_fn);
+
+ $tablehtml .= $this->getTfooter($columns, $actions);
+
+ $tablehtml .= "</table>\n";
+
+ // Multi action table footer w/ options & [un]check'em all
+ if ($this->has_ma) {
+ // if default is not set or doesn't exist, set it to null
+ if (!isset($this->ma['default']) || !isset($actions[$this->ma['default']])) {
+ $this->ma['default'] = null;
+ }
+
+ $tablehtml .= "<br />\n";
+ $tablehtml .= "<table>\n";
+ $tablehtml .= "<tr>\n";
+ $tablehtml .= "<th class=\"data\" style=\"text-align: left\" colspan=\"3\">{$lang['stractionsonmultiplelines']}</th>\n";
+ $tablehtml .= "</tr>\n";
+ $tablehtml .= "<tr class=\"row1\">\n";
+ $tablehtml .= "<td>";
+ $tablehtml .= "<a href=\"#\" onclick=\"javascript:checkAll(true);\">{$lang['strselectall']}</a> / ";
+ $tablehtml .= "<a href=\"#\" onclick=\"javascript:checkAll(false);\">{$lang['strunselectall']}</a></td>\n";
+ $tablehtml .= "<td>&nbsp;--->&nbsp;</td>\n";
+ $tablehtml .= "<td>\n";
+ $tablehtml .= "\t<select name=\"action\">\n";
+ if ($this->ma['default'] == null) {
+ $tablehtml .= "\t\t<option value=\"\">--</option>\n";
+ }
+
+ foreach ($actions as $k => $a) {
+ if (isset($a['multiaction'])) {
+ $selected = $this->ma['default'] == $k ? ' selected="selected" ' : '';
+ $tablehtml .= "\t\t";
+ $tablehtml .= '<option value="' . $a['multiaction'] . '" ' . $selected . ' rel="' . $k . '">' . $a['content'] . '</option>';
+ $tablehtml .= "\n";
+ }
+ }
+
+ $tablehtml .= "\t</select>\n";
+ $tablehtml .= "<input type=\"submit\" value=\"{$lang['strexecute']}\" />\n";
+ $tablehtml .= $this->getForm();
+ $tablehtml .= "</td>\n";
+ $tablehtml .= "</tr>\n";
+ $tablehtml .= "</table>\n";
+ $tablehtml .= '</form>';
+ };
+
+ } else {
+ if (!is_null($nodata)) {
+ $tablehtml .= "<p>{$nodata}</p>\n";
+ }
+
+ }
+ return $tablehtml;
+ }
+
+ private function getTbody($columns, $actions, $tabledata, $pre_fn) {
+ // Display table rows
+ $i = 0;
+ $tbody_html = '<tbody>';
+ while (!$tabledata->EOF) {
+ $id = ($i % 2) + 1;
+
+ unset($alt_actions);
+ if (!is_null($pre_fn)) {
+ $alt_actions = $pre_fn($tabledata, $actions);
+ }
+
+ if (!isset($alt_actions)) {
+ $alt_actions = &$actions;
+ }
+
+ $tbody_html .= "<tr class=\"data{$id}\">\n";
+ if ($this->has_ma) {
+ $a = [];
+ foreach ($this->ma['keycols'] as $k => $v) {
+ $a[$k] = $tabledata->fields[$v];
+ }
+ //\Kint::dump($a);
+ $tbody_html .= "<td>";
+ $tbody_html .= "<input type=\"checkbox\" name=\"ma[]\" value=\"" . htmlentities(serialize($a), ENT_COMPAT, 'UTF-8') . "\" />";
+ $tbody_html .= "</td>\n";
+ }
+
+ foreach ($columns as $column_id => $column) {
+
+ // Apply default values for missing parameters
+ if (isset($column['url']) && !isset($column['vars'])) {
+ $column['vars'] = [];
+ }
+
+ switch ($column_id) {
+ case 'actions':
+ foreach ($alt_actions as $action) {
+ if (isset($action['disable']) && $action['disable'] === true) {
+ $tbody_html .= "<td></td>\n";
+ } else {
+ $tbody_html .= "<td class=\"opbutton{$id} {$class}\">";
+ $action['fields'] = $tabledata->fields;
+ $tbody_html .= $this->printLink($action, false);
+ $tbody_html .= "</td>\n";
+ }
+ }
+ break;
+ case 'comment':
+ $tbody_html .= "<td class='comment_cell'>";
+ $val = Decorator::get_sanitized_value($column['field'], $tabledata->fields);
+ if (!is_null($val)) {
+ $tbody_html .= htmlentities($val);
+ }
+ $tbody_html .= "</td>";
+ break;
+ default:
+ $tbody_html .= "<td{$class}>";
+ $val = Decorator::get_sanitized_value($column['field'], $tabledata->fields);
+ if (!is_null($val)) {
+ if (isset($column['url'])) {
+ $tbody_html .= "<a href=\"{$column['url']}";
+ $tbody_html .= $this->printUrlVars($column['vars'], $tabledata->fields, false);
+ $tbody_html .= "\">";
+ }
+ $type = isset($column['type']) ? $column['type'] : null;
+ $params = isset($column['params']) ? $column['params'] : [];
+ $tbody_html .= $this->misc->printVal($val, $type, $params);
+ if (isset($column['url'])) {
+ $tbody_html .= "</a>";
+ }
+
+ }
+
+ $tbody_html .= "</td>\n";
+ break;
+ }
+ }
+ $tbody_html .= "</tr>\n";
+
+ $tabledata->moveNext();
+ $i++;
+ }
+
+ $tbody_html .= '</tbody>';
+ return $tbody_html;
+ }
+
+ private function getThead($columns, $actions) {
+ $thead_html = "<thead><tr>\n";
+
+ // Handle cases where no class has been passed
+ if (isset($column['class'])) {
+ $class = $column['class'] !== '' ? " class=\"{$column['class']}\"" : '';
+ } else {
+ $class = '';
+ }
+
+ // Display column headings
+ if ($this->has_ma) {
+ $thead_html .= "<th></th>";
+ }
+
+ foreach ($columns as $column_id => $column) {
+ switch ($column_id) {
+ case 'actions':
+ if (sizeof($actions) > 0) {
+ $thead_html .= '<th class="data" colspan="' . count($actions) . '">' . $column['title'] . '</th>' . "\n";
+ }
+
+ break;
+ default:
+ $thead_html .= '<th class="data' . $class . '">';
+ if (isset($column['help'])) {
+ $thead_html .= $this->misc->printHelp($column['title'], $column['help'], false);
+ } else {
+ $thead_html .= $column['title'];
+ }
+
+ $thead_html .= "</th>\n";
+ break;
+ }
+ }
+ $thead_html .= "</tr></thead>\n";
+
+ return $thead_html;
+ }
+
+ private function getTfooter($columns, $actions) {
+ $tfoot_html = "<tfoot><tr>\n";
+
+ // Handle cases where no class has been passed
+ if (isset($column['class'])) {
+ $class = $column['class'] !== '' ? " class=\"{$column['class']}\"" : '';
+ } else {
+ $class = '';
+ }
+
+ // Display column headings
+ if ($this->has_ma) {
+ $tfoot_html .= "<td></td>";
+ }
+
+ foreach ($columns as $column_id => $column) {
+ switch ($column_id) {
+ case 'actions':
+ if (sizeof($actions) > 0) {
+ $tfoot_html .= "<td class=\"data\" colspan=\"" . count($actions) . "\"></td>\n";
+ }
+
+ break;
+ default:
+ $tfoot_html .= "<td class=\"data{$class}\"></td>\n";
+ break;
+ }
+ }
+ $tfoot_html .= "</tr></tfoot>\n";
+
+ return $tfoot_html;
+ }
+
+ private function getForm() {
+ if (!$this->form) {
+ $this->form = $this->misc->setForm();
+ }
+ return $this->form;
+ }
+
+ private function printUrlVars(&$vars, &$fields, $do_print = true) {
+ $url_vars_html = '';
+ foreach ($vars as $var => $varfield) {
+ $url_vars_html .= "{$var}=" . urlencode($fields[$varfield]) . "&amp;";
+ }
+ if ($do_print) {
+ echo $url_vars_html;
+ } else {
+ return $url_vars_html;
+ }
+ }
+
+}
diff --git a/src/classes/xhtml/XHTML_Button.php b/src/xhtml/XHTML_Button.php
index 91f49646..91f49646 100644
--- a/src/classes/xhtml/XHTML_Button.php
+++ b/src/xhtml/XHTML_Button.php
diff --git a/src/classes/xhtml/XHTML_Option.php b/src/xhtml/XHTML_Option.php
index b61b7077..b61b7077 100644
--- a/src/classes/xhtml/XHTML_Option.php
+++ b/src/xhtml/XHTML_Option.php
diff --git a/src/classes/xhtml/XHTML_Select.php b/src/xhtml/XHTML_Select.php
index b477abe1..b477abe1 100644
--- a/src/classes/xhtml/XHTML_Select.php
+++ b/src/xhtml/XHTML_Select.php
diff --git a/src/classes/xhtml/XHtmlElement.php b/src/xhtml/XHtmlElement.php
index a2ff382f..a2ff382f 100644
--- a/src/classes/xhtml/XHtmlElement.php
+++ b/src/xhtml/XHtmlElement.php
diff --git a/src/classes/xhtml/XHtmlSimpleElement.php b/src/xhtml/XHtmlSimpleElement.php
index dbe2cc32..dbe2cc32 100644
--- a/src/classes/xhtml/XHtmlSimpleElement.php
+++ b/src/xhtml/XHtmlSimpleElement.php
diff --git a/templates/browser.twig b/templates/browser.twig
index 44510bbf..da2782be 100644
--- a/templates/browser.twig
+++ b/templates/browser.twig
@@ -22,7 +22,7 @@
<div dir="ltr">
<div class="logo">
- <a href="/src/views/intro" target="detail">{{appName}}</a>
+ <a href="/src/views/intro.php" target="detail">{{appName}}</a>
</div>
<div class="refreshTree">
<a href="/tree/browser" target="browser">
@@ -65,7 +65,8 @@
return false;
};
*/
- var tree = new WebFXLoadTree("{{strservers}}", "/tree/servers" /* src for sub branches */ , "/src/views/servers" /* action */ );
+ //var tree = new WebFXLoadTree("{{strservers}}", "/tree/servers" /* src for sub branches */ , "/src/views/servers.php" /* action */ );
+ var tree = new WebFXLoadTree("{{strservers}}", "/src/views/servers.php?action=tree" /* src for sub branches */ , "servers.php" /* action */ );
tree.write();
tree.setExpanded(true);
</script>
diff --git a/templates/datatables_header.twig b/templates/datatables_header.twig
new file mode 100644
index 00000000..07487d5d
--- /dev/null
+++ b/templates/datatables_header.twig
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html xml:lang="{{applocale}}" lang="{{applocale}}" {{dir}}>
+
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" href="/src/themes/{{theme}}/global.css" type="text/css" id="csstheme" />
+
+ <link rel="stylesheet" href="/src/themes/datatables.min.css" type="text/css" />
+
+ <link rel="shortcut icon" href="/images/themes/{{theme}}/Favicon.ico" type="image/vnd.microsoft.icon" />
+ <link rel="icon" type="image/png" href="/images/themes/{{theme}}/Introduction.png" />
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
+
+ <script src="/js/datatables.min.js"></script>
+
+ <title>{{appName}}</title>
+
+ <script type="text/javascript">
+ $(document).ready(function() {
+ if (window.parent.frames.length > 1) {
+ $('#csstheme', window.parent.frames[0].document).attr('href', '/src/themes/{{theme}}/global.css');
+ }
+ });
+ </script>
diff --git a/templates/header.twig b/templates/header.twig
index 3d7e31cc..053466b1 100644
--- a/templates/header.twig
+++ b/templates/header.twig
@@ -4,14 +4,11 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="/src/themes/{{theme}}/global.css" type="text/css" id="csstheme" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" id="csstheme" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css" type="text/css" id="csstheme" />
+
<link rel="shortcut icon" href="/images/themes/{{theme}}/Favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="icon" type="image/png" href="/images/themes/{{theme}}/Introduction.png" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/es.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
+
<title>{{appName}}</title>
<script type="text/javascript">
diff --git a/templates/home.twig b/templates/home.twig
index ae3f8997..a6446bd7 100644
--- a/templates/home.twig
+++ b/templates/home.twig
@@ -5,20 +5,18 @@
{% include 'home_header.twig' %}
</head>
- <frameset cols="{{cols}}">
- {% if rtl == true %}
- <frame src="/src/views/intro.php" name="detail" id="detail" frameborder="0" />
- <frame src="/tree/browser" name="browser" id="browser" frameborder="0" /> {% else %}
- <frame src="/tree/browser" name="browser" id="browser" frameborder="0" />
- <frame src="/src/views/intro.php" name="detail" id="detail" frameborder="0" /> {% endif %}
+ <body class="flexbox_body">
+ <div class="flexbox_wrapper">
- <noframes>
+ <iframe src="/src/views/browser.php" name="browser" id="browser" class="browser_container" frameborder="0" />
+ <p>Your browser does not support iframes.</p>
+ </iframe>
- <body>{{strnoframes}}
- <br />
- <a href="/src/views/intro">{{strnoframeslink}}</a>
- </body>
- </noframes>
- </frameset>
+ <iframe src="/src/views/intro.php" name="detail" id="detail" class="detail_container" frameborder="0" />
+ <p>Your browser does not support iframes.</p>
+ </iframe>
+
+ </div>
+ </body>
</html>
diff --git a/templates/home_header.twig b/templates/home_header.twig
index 07197f85..a0e9f074 100644
--- a/templates/home_header.twig
+++ b/templates/home_header.twig
@@ -1,13 +1,16 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<link rel="stylesheet" href="/src/themes/default/global.css" type="text/css" id="csstheme" />
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" id="csstheme" />
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css" type="text/css" id="csstheme" />
+<link rel="stylesheet" href="/src/themes/global.css" type="text/css" id="csstheme" />
+
<link rel="shortcut icon" href="/images/themes/default/Favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="icon" type="image/png" href="/images/themes/default/Introduction.png" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
{#
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" id="csstheme" />
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css" type="text/css" id="csstheme" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
-<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/es.js"></script>#}
+<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/es.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
+#}
<link rel="stylesheet" href="/js/codemirror/codemirror.css" />
@@ -22,6 +25,4 @@
<script src="/js/codemirror/addon/fold/foldgutter.js"></script>
<script src="/js/codemirror/addon/fold/indent-fold.js"></script>
-<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
-
<title>phpPgAdmin - {{title}}</title>
diff --git a/templates/home_rtl.twig b/templates/home_rtl.twig
new file mode 100644
index 00000000..462ab9c6
--- /dev/null
+++ b/templates/home_rtl.twig
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+
+ <head>
+ {% include 'home_header.twig' %}
+ </head>
+
+ <frameset cols="{{cols}}">
+
+ <frame src="/src/views/intro.php" name="detail" id="detail" frameborder="0" />
+ <frame src="/tree/browser" name="browser" id="browser" frameborder="0" />
+
+ <noframes>
+
+ <body>{{strnoframes}}
+ <br />
+ <a href="/src/views/intro.php">{{strnoframeslink}}</a>
+ </body>
+ </noframes>
+ </frameset>
+
+</html>
diff --git a/templates/select2_header.twig b/templates/select2_header.twig
new file mode 100644
index 00000000..3d7e31cc
--- /dev/null
+++ b/templates/select2_header.twig
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html xml:lang="{{applocale}}" lang="{{applocale}}" {{dir}}>
+
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" href="/src/themes/{{theme}}/global.css" type="text/css" id="csstheme" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" id="csstheme" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css" type="text/css" id="csstheme" />
+ <link rel="shortcut icon" href="/images/themes/{{theme}}/Favicon.ico" type="image/vnd.microsoft.icon" />
+ <link rel="icon" type="image/png" href="/images/themes/{{theme}}/Introduction.png" />
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/es.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
+ <title>{{appName}}</title>
+
+ <script type="text/javascript">
+ $(document).ready(function() {
+ if (window.parent.frames.length > 1) {
+ $('#csstheme', window.parent.frames[0].document).attr('href', '/src/themes/{{theme}}/global.css');
+ }
+ });
+ </script>
diff --git a/templates/sqledit.twig b/templates/sqledit.twig
index 4f6b0995..2d466ce7 100644
--- a/templates/sqledit.twig
+++ b/templates/sqledit.twig
@@ -20,17 +20,16 @@ jQuery(document).ready(function() {
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
hintOptions: {
tables: {
- medios: {
- id: null,
- name: null,
- categoria: null
- },
- posts: {
- id: null,
- name: null,
- id_medio: null,
- created_date: null
- }
+ "medios": ['id', 'name', 'categoria', 'table_name', 'category', 'url'],
+ "posts": ['id', 'name', 'id_medio', 'created_date'],
+ "comments": ['id', 'id_usuario', 'id_post', 'id_medio', 'created_time', 'message'],
+ "users": ['id', 'nombre', 'url'],
+ "reactions": ['id', 'id_usuario', 'id_post', 'id_medio', 'type'],
+ "public.medios": ['id', 'name', 'categoria', 'table_name', 'category', 'url'],
+ "public.posts": ['id', 'name', 'id_medio', 'created_date'],
+ "public.comments": ['id', 'id_usuario', 'id_post', 'id_medio', 'created_time', 'message'],
+ "public.users": ['id', 'nombre', 'url'],
+ "public.reactions": ['id', 'id_usuario', 'id_post', 'id_medio', 'type']
}
}
});
diff --git a/templates/sqledit_header.twig b/templates/sqledit_header.twig
index 1f95893f..728a7c98 100644
--- a/templates/sqledit_header.twig
+++ b/templates/sqledit_header.twig
@@ -4,20 +4,16 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="/src/themes/default/global.css" type="text/css" id="csstheme" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" id="csstheme" />
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css" type="text/css" id="csstheme" />
<link rel="shortcut icon" href="/images/themes/default/Favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="icon" type="image/png" href="/images/themes/default/Introduction.png" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
- {#
- <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/es.js"></script>#}
- <link rel="stylesheet" href="/js/codemirror/codemirror.css" />
+ <link href="/js/codemirror/addon/hint/show-hint.css" rel="stylesheet" />
+ <link href="/js/codemirror/codemirror.css" rel="stylesheet" />
<script src="/js/codemirror/codemirror.js"></script>
<script src="/js/codemirror/mode/sql/sql.js"></script>
- <link rel="stylesheet" href="/js/codemirror/addon/hint/show-hint.css" />
+
<script src="/js/codemirror/addon/hint/show-hint.js"></script>
<script src="/js/codemirror/addon/hint/sql-hint.js"></script>
@@ -26,8 +22,6 @@
<script src="/js/codemirror/addon/fold/foldgutter.js"></script>
<script src="/js/codemirror/addon/fold/indent-fold.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
-
<title>phpPgAdmin - {{title}}</title>
</head>
diff --git a/templates/table_list_footer.twig b/templates/table_list_footer.twig
new file mode 100644
index 00000000..377e27ac
--- /dev/null
+++ b/templates/table_list_footer.twig
@@ -0,0 +1,7 @@
+<script>
+$(document).ready(function() {
+ $('.{{table_class}}').DataTable({
+ "pageLength": 100
+ });
+});
+</script>