├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── config.dist.php ├── controllers ├── actions.php ├── cron.php ├── gearman.php ├── hashes.php ├── keys.php ├── lists.php ├── sets.php ├── stats.php ├── strings.php ├── terminal.php ├── welcome.php └── zsets.php ├── docker ├── crontab ├── default.conf ├── php.ini └── start.sh ├── helpers └── redis.php ├── libraries ├── app.php ├── controller.php ├── db.php ├── drivers │ ├── db │ │ └── redis.php │ ├── log │ │ ├── file.php │ │ └── std.php │ └── template │ │ ├── json.php │ │ └── php.php ├── error.php ├── inputs.php ├── log.php ├── model.php ├── router.php ├── session.php └── template.php ├── models ├── info.php └── stats.php ├── public ├── .htaccess ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.min.css │ │ └── bootstrap.min.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ └── bootstrap.min.js ├── css │ └── custom.css ├── font-awesome │ ├── .gitignore │ ├── css │ │ ├── font-awesome-ie7.min.css │ │ └── font-awesome.min.css │ └── font │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff ├── img │ └── ajax-loader.gif ├── index.php └── js │ ├── jquery-ui │ ├── css │ │ ├── images │ │ │ ├── ui-bg_flat_30_cccccc_40x100.png │ │ │ ├── ui-bg_flat_50_5c5c5c_40x100.png │ │ │ ├── ui-bg_glass_20_555555_1x400.png │ │ │ ├── ui-bg_glass_40_0078a3_1x400.png │ │ │ ├── ui-bg_glass_40_ffc73d_1x400.png │ │ │ ├── ui-bg_gloss-wave_25_333333_500x100.png │ │ │ ├── ui-bg_highlight-soft_80_eeeeee_1x100.png │ │ │ ├── ui-bg_inset-soft_25_000000_1x100.png │ │ │ ├── ui-bg_inset-soft_30_f58400_1x100.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_4b8e0b_256x240.png │ │ │ ├── ui-icons_a83300_256x240.png │ │ │ ├── ui-icons_cccccc_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ └── jquery-ui.min.css │ └── js │ │ └── jquery-ui.min.js │ ├── jquery.min.js │ ├── moment.min.js │ ├── nvd3 │ ├── .gitignore │ ├── lib │ │ ├── cie.js │ │ ├── crossfilter.js │ │ ├── crossfilter.min.js │ │ ├── d3.v2.js │ │ ├── d3.v2.min.js │ │ ├── fisheye.js │ │ ├── hive.js │ │ ├── horizon.js │ │ └── sankey.js │ ├── nv.d3.js │ ├── nv.d3.min.js │ └── src │ │ ├── core.js │ │ ├── intro.js │ │ ├── models │ │ ├── axis.js │ │ ├── backup │ │ │ ├── bullet.js │ │ │ └── bulletChart.js │ │ ├── bullet.js │ │ ├── bulletChart.js │ │ ├── cumulativeLineChart.js │ │ ├── discreteBar.js │ │ ├── discreteBarChart.js │ │ ├── distribution.js │ │ ├── historicalBar.js │ │ ├── indentedTree.js │ │ ├── legend.js │ │ ├── line.js │ │ ├── lineChart.js │ │ ├── linePlusBarChart.js │ │ ├── linePlusBarWithFocusChart.js │ │ ├── lineWithFisheye.js │ │ ├── lineWithFisheyeChart.js │ │ ├── lineWithFocusChart.js │ │ ├── multiBar.js │ │ ├── multiBarChart.js │ │ ├── multiBarHorizontal.js │ │ ├── multiBarHorizontalChart.js │ │ ├── multiBarTimeSeries.js │ │ ├── multiBarTimeSeriesChart.js │ │ ├── multiChart.js │ │ ├── ohlcBar.js │ │ ├── pie.js │ │ ├── pieChart.js │ │ ├── scatter.js │ │ ├── scatterChart.js │ │ ├── scatterPlusLineChart.js │ │ ├── sparkline.js │ │ ├── sparklinePlus.js │ │ ├── stackedArea.js │ │ └── stackedAreaChart.js │ │ ├── nv.d3.css │ │ ├── outro.js │ │ ├── tooltip.js │ │ └── utils.js │ └── redmin │ ├── actions.js │ ├── hashes.js │ ├── lists.js │ ├── main.js │ ├── modal.js │ ├── remlists.js │ ├── sets.js │ ├── strings.js │ ├── terminal.js │ └── zsets.js └── views ├── 404.php ├── generalmodals.php ├── hashes ├── add.php ├── edit.php └── view.php ├── keys ├── move.php ├── rename.php ├── search.php └── ttl.php ├── layout.php ├── lists ├── add.php └── view.php ├── navigation.php ├── sets ├── add.php ├── edit.php └── view.php ├── strings ├── add.php └── view.php ├── terminal └── index.php ├── welcome ├── config.php ├── index.php ├── info.php ├── slowlog.php └── stats.php └── zsets ├── add.php └── view.php /.dockerignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | README.md 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | logs/ 5 | /nbproject/private/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:5.6-apache 2 | 3 | MAINTAINER sasan.rose@gmail.com 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | cron \ 7 | gearman-job-server \ 8 | git-core \ 9 | libgearman-dev \ 10 | redis-tools \ 11 | && pecl install gearman \ 12 | && docker-php-ext-enable gearman 13 | 14 | 15 | EXPOSE 80 16 | 17 | COPY docker/default.conf /etc/apache2/sites-available/000-default.conf 18 | COPY docker/php.ini /usr/local/etc/php/ 19 | COPY docker/start.sh /usr/src/start.sh 20 | 21 | WORKDIR /etc/cron.d 22 | COPY docker/crontab phpredmin 23 | RUN chmod 0644 phpredmin 24 | 25 | WORKDIR /usr/src 26 | 27 | RUN git clone https://github.com/phpredis/phpredis.git 28 | WORKDIR /usr/src/phpredis 29 | # Version 3 has a bug with zAdd so checkout to 2.2.8 30 | RUN git checkout tags/2.2.8 \ 31 | && phpize \ 32 | && ./configure \ 33 | && make \ 34 | && make install \ 35 | && docker-php-ext-enable redis 36 | 37 | WORKDIR /var/www/html 38 | COPY . phpredmin/ 39 | 40 | # Clean up 41 | RUN rm -rf /usr/src/phpredis 42 | 43 | RUN apt-get --purge remove -y git-core \ 44 | && apt-get clean 45 | 46 | ENV PHPREDMIN_LOG_DRIVER="std" 47 | ENV PHPREDMIN_LOG_THRESHOLD="4" 48 | 49 | WORKDIR /var/www/html/phpredmin/public 50 | 51 | RUN chmod u+x /usr/src/start.sh 52 | CMD ["/usr/src/start.sh"] 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright © 2013, Sasan Rose 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 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. 11 | Neither the name of the PHPRedMin nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | 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 HOLDER 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. 14 | -------------------------------------------------------------------------------- /config.dist.php: -------------------------------------------------------------------------------- 1 | 'Welcome', 4 | 'default_action' => 'Index', 5 | 'production' => true, 6 | 'default_layout' => 'layout', 7 | 'timezone' => 'Europe/Amsterdam', 8 | 'auth' => array( 9 | 'username' => 'admin', 10 | 'password' => password_hash('admin', PASSWORD_DEFAULT) 11 | ), 12 | 'log' => array( 13 | 'driver' => 'file', 14 | 'threshold' => 1, /* 0: Disable Logging 1: Error 2: Notice 3: Info 4: Warning 5: Debug */ 15 | 'file' => array( 16 | 'directory' => 'logs' 17 | ) 18 | ), 19 | 'database' => array( 20 | 'driver' => 'redis', 21 | 'mysql' => array( 22 | 'host' => 'localhost', 23 | 'username' => 'root', 24 | 'password' => 'root' 25 | ), 26 | 'redis' => array( 27 | array( 28 | 'host' => 'localhost', 29 | 'port' => '6379', 30 | 'password' => null, 31 | 'database' => 0, 32 | 'max_databases' => 16, /* Manual configuration of max databases for Redis < 2.6 */ 33 | 'stats' => array( 34 | 'enable' => 1, 35 | 'database' => 0, 36 | ), 37 | 'dbNames' => array( /* Name databases. key should be database id and value is the name */ 38 | ), 39 | ), 40 | ), 41 | ), 42 | 'session' => array( 43 | 'lifetime' => 7200, 44 | 'gc_probability' => 2, 45 | 'name' => 'phpredminsession' 46 | ), 47 | 'gearman' => array( 48 | 'host' => '127.0.0.1', 49 | 'port' => 4730 50 | ), 51 | 'terminal' => array( 52 | 'enable' => true, 53 | 'history' => 200 54 | ) 55 | ); 56 | 57 | return $config; 58 | -------------------------------------------------------------------------------- /controllers/actions.php: -------------------------------------------------------------------------------- 1 | statsModel = new Stats_Model($this->app->current); 10 | $this->template = Template::factory('json'); 11 | } 12 | 13 | public function resetAction() 14 | { 15 | $this->statsModel->resetStats(); 16 | 17 | $this->template->render(true); 18 | } 19 | 20 | public function fallAction() 21 | { 22 | $this->db->flushAll(); 23 | 24 | $this->template->render(true); 25 | } 26 | 27 | public function fdbAction() 28 | { 29 | $this->db->flushDB(); 30 | 31 | $this->template->render(true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /controllers/cron.php: -------------------------------------------------------------------------------- 1 | app->config['database']['redis'] as $serverId => $server) { 8 | if (!empty($server['stats']['enable'])) { 9 | $time = time(); 10 | 11 | $db = Db::factory($server); 12 | $info = $db->info(); 13 | 14 | $statsModel = new Stats_Model($server); 15 | $statsModel->addKey('memory', $info['used_memory'], $time); 16 | $statsModel->addKey('connections', $info['total_connections_received'], $time); 17 | $statsModel->addKey('commands', $info['total_commands_processed'], $time); 18 | $statsModel->addKey('expired_keys', $info['expired_keys'], $time); 19 | $statsModel->addKey('hits', $info['keyspace_hits'], $time); 20 | $statsModel->addKey('misses', $info['keyspace_misses'], $time); 21 | $statsModel->addKey('clients', $info['connected_clients'], $time); 22 | $statsModel->addKey('user_cpu', $info['used_cpu_user'], $time); 23 | $statsModel->addKey('system_cpu', $info['used_cpu_sys'], $time); 24 | if ($info['aof_enabled']) { 25 | $statsModel->addKey('aof_size', $info['aof_current_size'], $time); 26 | $statsModel->addKey('aof_base', $info['aof_base_size'], $time); 27 | } 28 | 29 | foreach ($this->infoModel->getDbs($info) as $i) { 30 | if (preg_match('/keys=([0-9]+),expires=([0-9]+)/', $info["db{$i}"], $matches)) { 31 | $statsModel->addKey("db{$i}:keys", $matches[1], $time); 32 | $statsModel->addKey("db{$i}:expired_keys", $matches[2], $time); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /controllers/gearman.php: -------------------------------------------------------------------------------- 1 | config; 8 | $worker = new GearmanWorker(); 9 | 10 | $worker->addServer($config['gearman']['host'], $config['gearman']['port']); 11 | $worker->addFunction('delete_keys', array($this, 'deleteKeys')); 12 | $worker->addFunction('move_keys', array($this, 'moveKeys')); 13 | 14 | while ($worker->work()); 15 | } 16 | 17 | public function deleteKeys($job) 18 | { 19 | $data = unserialize($job->workload()); 20 | 21 | Log::factory()->write(Log::NOTICE, "Try to delete: {$data['key']} at {$data['server']['host']}:{$data['server']['port']}, DB: {$data['server']['database']}", 'Gearman'); 22 | 23 | $db = Db::factory($data['server']); 24 | $db->changeDB($data['server']['database']); 25 | 26 | $keys = $db->keys($data['key']); 27 | $count = count($keys); 28 | 29 | if ($count) { 30 | $db->set("phpredmin:gearman:deletecount:{$data['key']}", $count); 31 | $db->del("phpredmin:gearman:deleted:{$data['key']}"); 32 | $db->del("phpredmin:gearman:requests:{$data['key']}"); 33 | 34 | foreach ($keys as $key) { 35 | if ($db->delete($key) !== false) { 36 | $db->incrBy("phpredmin:gearman:deleted:{$data['key']}", 1); 37 | $db->expireAt("phpredmin:gearman:deleted:{$data['key']}", strtotime('+10 minutes')); 38 | } else { 39 | Log::factory()->write(Log::INFO, "Unable to delete {$key}", 'Gearman'); 40 | } 41 | } 42 | 43 | $db->del("phpredmin:gearman:deletecount:{$data['key']}"); 44 | } 45 | } 46 | 47 | public function moveKeys($job) 48 | { 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /controllers/hashes.php: -------------------------------------------------------------------------------- 1 | router->method == Router::POST) { 10 | $value = $this->inputs->post('value', null); 11 | $key = $this->inputs->post('key', null); 12 | $hashkey = $this->inputs->post('hashkey', null); 13 | 14 | if (isset($value) && trim($value) != '' && isset($key) && trim($key) != '' && isset($hashkey) && trim($hashkey) != '') { 15 | if ($this->db->hSet($key, $hashkey, $value) !== false) { 16 | $added = true; 17 | } 18 | } 19 | } 20 | 21 | Template::factory('json')->render($added); 22 | } 23 | 24 | public function viewAction($key) 25 | { 26 | $members = $this->db->hGetAll(urldecode($key)); 27 | 28 | Template::factory()->render('hashes/view', array('members' => $members, 'key' => urldecode($key))); 29 | } 30 | 31 | public function deleteAction($key, $member) 32 | { 33 | Template::factory('json')->render($this->db->hDel(urldecode($key), urldecode($member))); 34 | } 35 | 36 | public function delallAction() 37 | { 38 | if ($this->router->method == Router::POST) { 39 | $results = array(); 40 | $values = $this->inputs->post('values', array()); 41 | $keyinfo = $this->inputs->post('keyinfo', null); 42 | 43 | foreach ($values as $key => $value) { 44 | $results[$value] = $this->db->hDel($keyinfo, $value); 45 | } 46 | 47 | Template::factory('json')->render($results); 48 | } 49 | } 50 | 51 | public function editAction($key, $member) 52 | { 53 | $edited = null; 54 | 55 | if ($this->router->method == Router::POST) { 56 | $newvalue = $this->inputs->post('newvalue', null); 57 | $member = $this->inputs->post('member', null); 58 | $key = $this->inputs->post('key', null); 59 | 60 | if (!isset($newvalue) || trim($newvalue) == '' || !isset($key) || trim($key) == '' || 61 | !isset($member) || trim($member) == '') { 62 | $edited = false; 63 | } elseif ($this->db->hDel($key, $member)) { 64 | $edited = $this->db->hSet($key, $member, $newvalue); 65 | } 66 | } 67 | 68 | $value = $this->db->hGet(urldecode($key), urldecode($member)); 69 | 70 | Template::factory()->render('hashes/edit', array('member' => urldecode($member), 'key' => urldecode($key), 'value' => $value, 'edited' => $edited)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /controllers/keys.php: -------------------------------------------------------------------------------- 1 | inputs->get('key', null); 8 | $keys = array(); 9 | 10 | if (isset($key) && trim($key) != '') { 11 | $keys = $this->db->keys($key); 12 | asort($keys); 13 | } 14 | 15 | Template::factory()->render('keys/search', array('keys' => $keys, 'search' => $key)); 16 | } 17 | 18 | public function moveAction($key) 19 | { 20 | $moved = null; 21 | 22 | if ($this->router->method == Router::POST) { 23 | $db = $this->inputs->post('db', null); 24 | $key = $this->inputs->post('key', null); 25 | 26 | if (!isset($db) || trim($db) == '' || !isset($key) || trim($key) == '') { 27 | $moved = false; 28 | } else { 29 | $moved = $this->db->move($key, $db); 30 | } 31 | } 32 | 33 | Template::factory()->render('keys/move', array('moved' => $moved, 'key' => urldecode($key))); 34 | } 35 | 36 | public function renameAction($key) 37 | { 38 | $renamed = null; 39 | 40 | if ($this->router->method == Router::POST) { 41 | $newkey = $this->inputs->post('newkey', null); 42 | $key = $this->inputs->post('key', null); 43 | 44 | if (!isset($newkey) || trim($newkey) == '' || !isset($key) || trim($key) == '') { 45 | $renamed = false; 46 | } else { 47 | $renamed = $this->db->rename($key, $newkey); 48 | } 49 | } 50 | 51 | Template::factory()->render('keys/rename', array('renamed' => $renamed, 'key' => urldecode($key))); 52 | } 53 | 54 | public function expireAction($key) 55 | { 56 | $updated = null; 57 | $oldttl = $this->db->ttl(urldecode($key)); 58 | 59 | if ($this->router->method == Router::POST) { 60 | $ttl = $this->inputs->post('ttl', null); 61 | $key = $this->inputs->post('key', null); 62 | 63 | if (!isset($ttl) || trim($ttl) == '' || !isset($key) || trim($key) == '') { 64 | $updated = false; 65 | } elseif ((int)$ttl > 0) { 66 | $updated = $this->db->expire($key, $ttl); 67 | } elseif ($oldttl > 0) { 68 | $updated = $this->db->persist($key); 69 | } else { 70 | $updated = true; 71 | } 72 | } 73 | 74 | 75 | 76 | Template::factory()->render('keys/ttl', array('updated' => $updated, 'key' => urldecode($key), 'ttl' => $oldttl)); 77 | } 78 | 79 | public function moveallAction() 80 | { 81 | if ($this->router->method == Router::POST) { 82 | $results = array(); 83 | $values = $this->inputs->post('values', array()); 84 | $destination = $this->inputs->post('destination'); 85 | 86 | foreach ($values as $key => $value) { 87 | $results[$value] = $this->db->move($value, $destination); 88 | } 89 | 90 | Template::factory('json')->render($results); 91 | } 92 | } 93 | 94 | public function delallAction() 95 | { 96 | if ($this->router->method == Router::POST) { 97 | $results = array(); 98 | $values = $this->inputs->post('values', array()); 99 | 100 | foreach ($values as $key => $value) { 101 | $results[$value] = $this->db->del($value); 102 | } 103 | 104 | Template::factory('json')->render($results); 105 | } 106 | } 107 | 108 | public function bulkdeleteAction() 109 | { 110 | if ($this->router->method == Router::POST) { 111 | $key = $this->inputs->post('key', null); 112 | 113 | if (isset($key) && trim($key) != '') { 114 | $config = App::instance()->config; 115 | $client = new GearmanClient(); 116 | 117 | $client->addServer($config['gearman']['host'], $config['gearman']['port']); 118 | $client->doBackground('delete_keys', 119 | serialize(array( 120 | 'key' => $key, 121 | 'server' => $this->app->current, 122 | )) 123 | ); 124 | } 125 | } 126 | } 127 | 128 | public function deleteinfoAction($key) 129 | { 130 | $this->db->incrBy("phpredmin:gearman:requests:{$key}", 1); 131 | $this->db->expireAt("phpredmin:gearman:requests:{$key}", strtotime('+10 minutes')); 132 | 133 | $key = urldecode($key); 134 | $total = $this->db->get("phpredmin:gearman:deletecount:{$key}"); 135 | $count = $this->db->get("phpredmin:gearman:deleted:{$key}"); 136 | $requests = $this->db->get("phpredmin:gearman:requests:{$key}"); 137 | 138 | if ($total === false && $count !== false && $requests == 1) { 139 | $total = $count; 140 | } 141 | 142 | $result = array($total, $count); 143 | 144 | Template::factory('json')->render($result); 145 | } 146 | 147 | public function deleteAction($key) 148 | { 149 | Template::factory('json')->render($this->db->del(urldecode($key))); 150 | } 151 | 152 | public function viewAction($key) 153 | { 154 | switch ($this->db->type(urldecode($key))) { 155 | case Redis::REDIS_STRING: 156 | $this->router->redirect("strings/view/{$this->app->current['serverId']}/{$this->app->current['database']}/{$key}"); 157 | break; 158 | case Redis::REDIS_SET: 159 | $this->router->redirect("sets/view/{$this->app->current['serverId']}/{$this->app->current['database']}/{$key}"); 160 | break; 161 | case Redis::REDIS_LIST: 162 | $this->router->redirect("lists/view/{$this->app->current['serverId']}/{$this->app->current['database']}/{$key}"); 163 | break; 164 | case Redis::REDIS_ZSET: 165 | $this->router->redirect("zsets/view/{$this->app->current['serverId']}/{$this->app->current['database']}/{$key}"); 166 | break; 167 | case Redis::REDIS_HASH: 168 | $this->router->redirect("hashes/view/{$this->app->current['serverId']}/{$this->app->current['database']}/{$key}"); 169 | break; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /controllers/lists.php: -------------------------------------------------------------------------------- 1 | router->method == Router::POST) { 10 | $value = $this->inputs->post('value', null); 11 | $key = $this->inputs->post('key', null); 12 | $type = $this->inputs->post('type', null); 13 | $pivot = $this->inputs->post('pivot', null); 14 | 15 | if (isset($value) && trim($value) != '' && isset($key) && trim($key) != '' && 16 | (isset($type) && in_array($type, array('before', 'after', 'prepend', 'append')))) { 17 | if (($type == 'before' || $type == 'after') && (!isset($pivot) || $pivot == '')) { 18 | $added = false; 19 | } else { 20 | switch ($type) { 21 | case 'prepend': 22 | $added = (boolean) $this->db->lPush($key, $value); 23 | break; 24 | case 'append': 25 | $added = (boolean) $this->db->rPush($key, $value); 26 | break; 27 | case 'before': 28 | $added = (boolean) $this->db->lInsert($key, Redis::BEFORE, $pivot, $value); 29 | break; 30 | case 'after': 31 | $added = (boolean) $this->db->lInsert($key, Redis::AFTER, $pivot, $value); 32 | break; 33 | } 34 | } 35 | } 36 | } 37 | 38 | Template::factory('json')->render($added); 39 | } 40 | 41 | public function viewAction($key, $page = 0) 42 | { 43 | $count = $this->db->lSize(urldecode($key)); 44 | $start = $page * 30; 45 | $values = $this->db->lRange(urldecode($key), $start, $start + 29); 46 | 47 | Template::factory()->render('lists/view', array('count' => $count, 'values' => $values, 'key' => urldecode($key), 48 | 'page' => $page)); 49 | } 50 | 51 | public function delAction() 52 | { 53 | if ($this->router->method == Router::POST) { 54 | $key = $this->inputs->post('key'); 55 | $value = $this->inputs->post('value'); 56 | $type = $this->inputs->post('type_options'); 57 | 58 | if ($type == 'all') { 59 | $this->db->lRem($key, $value, 0); 60 | } elseif ($type == 'count') { 61 | $count = $this->inputs->post('count'); 62 | $this->db->lRem($key, $value, $count); 63 | } 64 | } 65 | 66 | $this->router->redirect("lists/view/{$this->app->current['serverId']}/{$this->app->current['database']}/" . urlencode($key)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /controllers/sets.php: -------------------------------------------------------------------------------- 1 | router->method == Router::POST) { 10 | $value = $this->inputs->post('value', null); 11 | $key = $this->inputs->post('key', null); 12 | 13 | if (isset($value) && trim($value) != '' && isset($key) && trim($key) != '') { 14 | $added = $this->db->sAdd($key, $value); 15 | } 16 | } 17 | 18 | Template::factory('json')->render($added); 19 | } 20 | 21 | public function viewAction($key) 22 | { 23 | $members = $this->db->sMembers(urldecode($key)); 24 | 25 | Template::factory()->render('sets/view', array('members' => $members, 'key' => urldecode($key))); 26 | } 27 | 28 | /** 29 | * Edit action ( edit members in Sets ) 30 | * 31 | * @param string $key 32 | * @param string $member 33 | */ 34 | 35 | public function editAction($key, $member) 36 | { 37 | $edited = null; 38 | 39 | if ($this->router->method == Router::POST) { 40 | $member = $this->inputs->post('oldmember', null); 41 | $newmember = $this->inputs->post('newmember', null); 42 | $key = $this->inputs->post('key', null); 43 | 44 | if (!isset($newmember) || trim($newmember) == '' || !isset($key) || trim($key) == '') { 45 | $edited = false; 46 | } elseif ($this->db->sRem($key, $member)) { 47 | $edited = $this->db->sAdd($key, $newmember); 48 | } 49 | } 50 | 51 | Template::factory()->render('sets/edit', array('member' => urldecode($member), 'key' => urldecode($key), 'edited' => $edited)); 52 | } 53 | 54 | public function deleteAction($key, $value) 55 | { 56 | Template::factory('json')->render($this->db->sRem(urldecode($key), urldecode($value))); 57 | } 58 | 59 | public function delallAction() 60 | { 61 | if ($this->router->method == Router::POST) { 62 | $results = array(); 63 | $values = $this->inputs->post('values', array()); 64 | $keyinfo = $this->inputs->post('keyinfo', null); 65 | 66 | foreach ($values as $key => $value) { 67 | $results[$value] = $this->db->sRem($keyinfo, $value); 68 | } 69 | 70 | Template::factory('json')->render($results); 71 | } 72 | } 73 | 74 | public function moveallAction() 75 | { 76 | if ($this->router->method == Router::POST) { 77 | $results = array(); 78 | $values = $this->inputs->post('values', array()); 79 | $destination = $this->inputs->post('destination'); 80 | $keyinfo = $this->inputs->post('keyinfo'); 81 | 82 | foreach ($values as $key => $value) { 83 | $results[$value] = $this->db->sMove($value, $keyinfo, $destination); 84 | } 85 | 86 | Template::factory('json')->render($results); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /controllers/stats.php: -------------------------------------------------------------------------------- 1 | statsModel = new Stats_Model($this->app->current); 10 | $this->template = Template::factory('json'); 11 | } 12 | 13 | public function keysAction() 14 | { 15 | $result = array(); 16 | $from = $this->router->query('from', strtotime('-12 hours')); 17 | $to = $this->router->query('to', time()); 18 | $hits = $this->statsModel->getKeys('hits', $from, $to); 19 | $misses = $this->statsModel->getKeys('misses', $from, $to); 20 | $expiredKeys = $this->statsModel->getKeys('expired_keys', $from, $to); 21 | 22 | $result[] = array('key' => 'Keyspace Misses', 'values' => $misses); 23 | $result[] = array('key' => 'Keyspace Hits', 'values' => $hits); 24 | $result[] = array('key' => 'Expired Keys', 'values' => $expiredKeys); 25 | 26 | $this->template->render($result); 27 | } 28 | 29 | public function commandsAction() 30 | { 31 | $result = array(); 32 | $from = $this->router->query('from', strtotime('-12 hours')); 33 | $to = $this->router->query('to', time()); 34 | $connections = $this->statsModel->getKeys('connections', $from, $to); 35 | $commands = $this->statsModel->getKeys('commands', $from, $to); 36 | 37 | $result[] = array('key' => 'Connections Received', 'values' => $connections); 38 | $result[] = array('key' => 'Commands Processed', 'values' => $commands); 39 | 40 | $this->template->render($result); 41 | } 42 | 43 | public function clientsAction() 44 | { 45 | $result = array(); 46 | $from = $this->router->query('from', strtotime('-12 hours')); 47 | $to = $this->router->query('to', time()); 48 | $clients = $this->statsModel->getKeys('clients', $from, $to); 49 | 50 | $result[] = array('key' => 'Clients', 'values' => $clients); 51 | 52 | $this->template->render($result); 53 | } 54 | 55 | public function memoryAction() 56 | { 57 | $result = array(); 58 | $from = $this->router->query('from', strtotime('-12 hours')); 59 | $to = $this->router->query('to', time()); 60 | $memory = $this->statsModel->getKeys('memory', $from, $to); 61 | 62 | $result[] = array('key' => 'Memory Usage', 'values' => $memory); 63 | 64 | $this->template->render($result); 65 | } 66 | 67 | public function dbkeysAction() 68 | { 69 | $result = array(); 70 | $from = $this->router->query('from', strtotime('-12 hours')); 71 | $to = $this->router->query('to', time()); 72 | 73 | foreach ($this->infoModel->getDbs($this->db->info()) as $db) { 74 | $keys = $this->statsModel->getKeys("db{$db}:keys", $from, $to); 75 | $result[] = array('key' => "DB{$db} Keys", 'values' => $keys); 76 | } 77 | 78 | $this->template->render($result); 79 | } 80 | 81 | public function dbexpiresAction() 82 | { 83 | $result = array(); 84 | $from = $this->router->query('from', strtotime('-12 hours')); 85 | $to = $this->router->query('to', time()); 86 | 87 | foreach ($this->infoModel->getDbs($this->db->info()) as $db) { 88 | $keys = $this->statsModel->getKeys("db{$db}:expired_keys", $from, $to); 89 | $result[] = array('key' => "DB{$db} Expired Keys", 'values' => $keys); 90 | } 91 | 92 | $this->template->render($result); 93 | } 94 | 95 | public function cpuAction() 96 | { 97 | $result = array(); 98 | $from = $this->router->query('from', strtotime('-12 hours')); 99 | $to = $this->router->query('to', time()); 100 | $user_cpu = $this->statsModel->getKeys('user_cpu', $from, $to); 101 | $system_cpu = $this->statsModel->getKeys('system_cpu', $from, $to); 102 | 103 | $result[] = array('key' => 'User CPU Usage', 'values' => $user_cpu); 104 | $result[] = array('key' => 'System CPU Usage', 'values' => $system_cpu); 105 | 106 | $this->template->render($result); 107 | } 108 | 109 | public function aofAction() 110 | { 111 | $result = array(); 112 | $from = $this->router->query('from', strtotime('-12 hours')); 113 | $to = $this->router->query('to', time()); 114 | $aof_size = $this->statsModel->getKeys('aof_size', $from, $to); 115 | $aof_base = $this->statsModel->getKeys('aof_base', $from, $to); 116 | 117 | $result[] = array('key' => 'AOF current size', 'values' => $aof_size); 118 | $result[] = array('key' => 'AOF base size', 'values' => $aof_base); 119 | 120 | $this->template->render($result); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /controllers/strings.php: -------------------------------------------------------------------------------- 1 | router->method == Router::POST) { 10 | $newvalue = $this->inputs->post('newvalue', null); 11 | $key = $this->inputs->post('key', null); 12 | 13 | if (!isset($newvalue) || trim($newvalue) == '' || !isset($key) || trim($key) == '') { 14 | $edited = false; 15 | } else { 16 | $edited = $this->db->set($key, $newvalue); 17 | } 18 | } 19 | 20 | $value = $this->db->get(urldecode($key)); 21 | 22 | Template::factory()->render('strings/view', array('edited' => $edited, 'key' => urldecode($key), 'value' => $value)); 23 | } 24 | 25 | public function addAction() 26 | { 27 | $added = false; 28 | 29 | if ($this->router->method == Router::POST) { 30 | $value = $this->inputs->post('value', null); 31 | $key = $this->inputs->post('key', null); 32 | 33 | if (isset($value) && trim($value) != '' && isset($key) && trim($key) != '') { 34 | $added = $this->db->set($key, $value); 35 | } 36 | } 37 | 38 | Template::factory('json')->render($added); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /controllers/terminal.php: -------------------------------------------------------------------------------- 1 | config = App::instance()->config['terminal']; 13 | } 14 | 15 | public function indexAction() 16 | { 17 | $this->db->set("phpredmin:terminal:history:pointer", -1); 18 | 19 | Template::factory()->render('terminal/index'); 20 | } 21 | 22 | public function runAction() 23 | { 24 | if (!$this->config['enable']) { 25 | echo "Terminal is not enabled"; 26 | die; 27 | } 28 | 29 | $command = $this->inputs->post('command', null); 30 | 31 | if (isset($command)) { 32 | $historylimit = $this->config['history']; 33 | $historykey = "phpredmin:terminal:history"; 34 | 35 | if ($historylimit > 0) { 36 | $this->db->lRem($historykey, $command, 0); 37 | $this->db->rPush($historykey, $command); 38 | $this->db->lTrim($historykey, $historylimit * -1, -1); 39 | 40 | $this->db->set("phpredmin:terminal:history:pointer", -1); 41 | } else { 42 | $this->db->del($historykey); 43 | } 44 | 45 | $command = escapeshellcmd($command); 46 | exec("redis-cli -h {$this->app->current['host']} -p {$this->app->current['port']} {$command}", $result); 47 | 48 | Template::factory('json')->render(array('result' => $result)); 49 | } 50 | } 51 | 52 | public function historyAction() 53 | { 54 | $historylimit = $this->config['history']; 55 | $historykey = "phpredmin:terminal:history"; 56 | $historylen = $this->db->lLen($historykey); 57 | $command = ''; 58 | $reset = false; 59 | 60 | if ($historylimit > 0 && $historylen > 0) { 61 | $navigation = $this->inputs->get('navigation', self::NAVIGATION_UP); 62 | $start = $this->inputs->get('start'); 63 | $pointer = $this->db->get("phpredmin:terminal:history:pointer"); 64 | 65 | if ($historylen > $historylimit) { 66 | $this->db->lTrim($historykey, $historylimit * -1, -1); 67 | $historylen = $historylimit; 68 | } 69 | 70 | if ($navigation == self::NAVIGATION_UP) { 71 | if ($historylen <= ($pointer * -1)) { 72 | $pointer = $historylen * -1; 73 | } elseif (!isset($start)) { 74 | $pointer--; 75 | } 76 | } elseif ($pointer != -1) { 77 | $pointer++; 78 | } else { 79 | $reset = true; 80 | } 81 | 82 | $command = $this->db->lRange($historykey, $pointer, $pointer); 83 | 84 | $this->db->set("phpredmin:terminal:history:pointer", $pointer); 85 | } 86 | 87 | Template::factory('json')->render(array('command' => $command, 'reset' => $reset)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /controllers/welcome.php: -------------------------------------------------------------------------------- 1 | render('welcome/index'); 8 | } 9 | 10 | public function configAction() 11 | { 12 | $config = $this->db->config('GET', '*'); 13 | 14 | Template::factory()->render('welcome/config', array('config' => $config)); 15 | } 16 | 17 | public function statsAction() 18 | { 19 | Template::factory()->render('welcome/stats'); 20 | } 21 | 22 | public function infoAction() 23 | { 24 | $info = $this->db->info(); 25 | $uptimeDays = floor($info['uptime_in_seconds'] / 86400); 26 | $dbSize = $this->db->dbSize(); 27 | $lastSave = $this->db->lastSave(); 28 | 29 | 30 | Template::factory()->render('welcome/info', array('info' => $info, 31 | 'uptimeDays' => $uptimeDays, 32 | 'dbSize' => $dbSize, 33 | 'lastSave' => $lastSave)); 34 | } 35 | 36 | public function saveAction($async = 0) 37 | { 38 | $saved = $async ? $this->db->bgSave() : $this->db->save(); 39 | $filename = current($this->db->config('GET', 'dbfilename')); 40 | 41 | Template::factory('json')->render(array( 42 | 'status' => $saved, 43 | 'async' => $async, 44 | 'filename' => $filename, 45 | )); 46 | } 47 | 48 | public function slowlogAction() 49 | { 50 | $support = false; 51 | $slowlogs = array(); 52 | $serverInfo = $this->db->info('server'); 53 | $count = $this->inputs->post('count', null); 54 | $count = isset($count) ? $count : 10; 55 | 56 | if (!preg_match('/^(0|1)/', $serverInfo['redis_version']) && !preg_match('/^2\.[0-5]/', $serverInfo['redis_version'])) { 57 | $slowlogs = $this->db->eval("return redis.call('slowlog', 'get', {$count})"); 58 | $support = true; 59 | } 60 | 61 | Template::factory()->render('welcome/slowlog', array('slowlogs' => $slowlogs, 62 | 'support' => $support, 63 | 'version' => $serverInfo['redis_version'], 64 | 'count' => $count)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /controllers/zsets.php: -------------------------------------------------------------------------------- 1 | router->method == Router::POST) { 10 | $value = $this->inputs->post('value', null); 11 | $key = $this->inputs->post('key', null); 12 | $score = $this->inputs->post('score', null); 13 | 14 | if (isset($value) && trim($value) != '' && isset($key) && trim($key) != '' && isset($score) && trim($score) != '') { 15 | $added = (boolean) $this->db->zAdd($key, (double) $score, $value); 16 | } 17 | } 18 | 19 | Template::factory('json')->render($added); 20 | } 21 | 22 | public function viewAction($key, $page = 0) 23 | { 24 | $count = $this->db->zSize(urldecode($key)); 25 | $start = $page * 30; 26 | $values = $this->db->zRange(urldecode($key), $start, $start + 29, true); 27 | 28 | Template::factory()->render('zsets/view', array('count' => $count, 'values' => $values, 'key' => urldecode($key), 29 | 'page' => $page)); 30 | } 31 | 32 | public function deleteAction($key, $value) 33 | { 34 | Template::factory('json')->render($this->db->zDelete(urldecode($key), urldecode($value))); 35 | } 36 | 37 | public function delallAction() 38 | { 39 | if ($this->router->method == Router::POST) { 40 | $results = array(); 41 | $values = $this->inputs->post('values', array()); 42 | $keyinfo = $this->inputs->post('keyinfo', null); 43 | 44 | foreach ($values as $key => $value) { 45 | $results[$value] = $this->db->zDelete($keyinfo, $value); 46 | } 47 | 48 | Template::factory('json')->render($results); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docker/crontab: -------------------------------------------------------------------------------- 1 | # Cron tab for phpredmin to collect redis statistics 2 | * * * * * root . /root/.profile; cd /var/www/html/phpredmin/public && /usr/local/bin/php index.php cron/index > /dev/stdout 2>&1 3 | -------------------------------------------------------------------------------- /docker/default.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | DocumentRoot /var/www/html/phpredmin/public 4 | 5 | ErrorLog ${APACHE_LOG_DIR}/error.log 6 | CustomLog ${APACHE_LOG_DIR}/access.log combined 7 | 8 | 9 | -------------------------------------------------------------------------------- /docker/php.ini: -------------------------------------------------------------------------------- 1 | variables_order = "EGPCS" 2 | log_errors = On 3 | error_log = /dev/stderr 4 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Copy PHPREDMIN env variables to .profile for cron jobs 4 | printenv | grep PHPREDMIN | xargs -rl echo "export$1" >> $HOME/.profile 5 | 6 | # Run gearman job server 7 | gearmand -d 8 | # Run cron daemon 9 | cron 10 | 11 | # Trap sigkill 12 | trap "pkill gearmand && pkill -WINCH apache2" TERM 13 | 14 | # Run gearman work & Start web server 15 | php index.php gearman/index & apache2-foreground 16 | -------------------------------------------------------------------------------- /helpers/redis.php: -------------------------------------------------------------------------------- 1 | db = Db::factory(App::instance()->current); 12 | } 13 | 14 | public static function instance() 15 | { 16 | if (!isset(self::$_instance)) { 17 | self::$_instance = new self; 18 | } 19 | 20 | return self::$_instance; 21 | } 22 | 23 | public function getType($key) 24 | { 25 | switch ($this->db->type($key)) { 26 | case Redis::REDIS_STRING: 27 | return 'String'; 28 | case Redis::REDIS_SET: 29 | return 'Set'; 30 | case Redis::REDIS_LIST: 31 | return 'List'; 32 | case Redis::REDIS_ZSET: 33 | return 'ZSet'; 34 | case Redis::REDIS_HASH: 35 | return 'Hash'; 36 | default: 37 | return '-'; 38 | } 39 | } 40 | 41 | public function getTTL($key) 42 | { 43 | return $this->_time($this->db->ttl($key)); 44 | } 45 | 46 | public function getIdleTime($key) 47 | { 48 | return $this->_time($this->db->object("idletime", $key)); 49 | } 50 | 51 | public function getCount($key) 52 | { 53 | return $this->db->object("refcount", $key); 54 | } 55 | 56 | public function getEncoding($key) 57 | { 58 | return $this->db->object("encoding", $key); 59 | } 60 | 61 | public function getSize($key) 62 | { 63 | switch ($this->db->type($key)) { 64 | case Redis::REDIS_LIST: 65 | $size = $this->db->lSize($key); 66 | break; 67 | case Redis::REDIS_SET: 68 | $size = $this->db->sCard($key); 69 | break; 70 | case Redis::REDIS_HASH: 71 | $size = $this->db->hLen($key); 72 | break; 73 | case Redis::REDIS_ZSET: 74 | $size = $this->db->zCard($key); 75 | break; 76 | case Redis::REDIS_STRING: 77 | $size = $this->db->strlen($key); 78 | break; 79 | default: 80 | $size = '-'; 81 | } 82 | 83 | return $size <=0 ? '-' : $size; 84 | } 85 | 86 | protected function _time($time) 87 | { 88 | if ($time <= 0) { 89 | return '-'; 90 | } else { 91 | $days = floor($time / 86400); 92 | 93 | return ($days > 0 ? "{$days} Days " : '') . gmdate('H:i:s', $time); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /libraries/app.php: -------------------------------------------------------------------------------- 1 | _data['config'] = include_once(file_exists('../config.php') ? '../config.php' : '../config.dist.php'); 11 | $this->_data['drivers'] = 'drivers/'; 12 | 13 | $this->readEnvConfig(); 14 | } 15 | 16 | public static function instance() 17 | { 18 | if (!self::$_instance) { 19 | self::$_instance = new self; 20 | } 21 | 22 | return self::$_instance; 23 | } 24 | 25 | public function __get($key) 26 | { 27 | return isset($this->_data[$key]) ? $this->_data[$key] : null; 28 | } 29 | 30 | public function __set($key, $value) 31 | { 32 | $this->_data[$key] = $value; 33 | } 34 | 35 | protected function readEnvConfig() { 36 | $envConf = preg_grep('/^PHPREDMIN_/', array_keys($_ENV)); 37 | 38 | if (!empty($envConf)) { 39 | foreach ($envConf as $conf) { 40 | $keys = explode('_', $conf); 41 | 42 | if (!empty($keys)) { 43 | array_shift($keys); 44 | 45 | self::setConfig($this->_data['config'], $keys, $_ENV[$conf]); 46 | } 47 | } 48 | } 49 | } 50 | 51 | protected static function setConfig(&$config, $keys, $value) { 52 | $key = array_shift($keys); 53 | 54 | $key = strtolower($key); 55 | 56 | if (isset($config[$key])) { 57 | if (is_array($config[$key])) { 58 | return self::setConfig($config[$key], $keys, $value); 59 | } else { 60 | $config[$key] = $value; 61 | return True; 62 | } 63 | } 64 | 65 | return False; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /libraries/controller.php: -------------------------------------------------------------------------------- 1 | _objects['app'] = App::instance(); 9 | $this->_objects['router'] = Router::instance(); 10 | $this->_objects['inputs'] = Inputs::instance(); 11 | $this->_objects['session'] = Session::instance(); 12 | $this->_objects['log'] = Log::factory(); 13 | 14 | if (!isset($this->app->config['database']['redis'][$config['serverId']])) { 15 | $config['serverId'] = 0; 16 | } 17 | 18 | $current = $this->app->config['database']['redis'][$config['serverId']]; 19 | $current['serverId'] = $config['serverId']; 20 | 21 | $this->_objects['db'] = Db::factory($current); 22 | $this->_objects['infoModel'] = new Info_Model($current); 23 | 24 | $info = $this->db->info(); 25 | $dbs = $this->infoModel->getDbs($info); 26 | 27 | if (!isset($current['max_databases'])) { 28 | $databasesConfig = $this->_objects['db']->config('GET', 'databases'); 29 | $current['max_databases'] = $databasesConfig['databases']; 30 | } 31 | 32 | // Take care of invalid dbId's. If invalid, set to first available database 33 | if (!is_numeric($config['dbId']) 34 | || $config['dbId'] < 0 35 | || $config['dbId'] >= $current['max_databases'] 36 | ) { 37 | $config['dbId'] = $dbs[0]; 38 | } 39 | 40 | $current['newDB'] = (!in_array($config['dbId'], $dbs) ? true : false); 41 | 42 | $current['database'] = $config['dbId']; 43 | 44 | // Extract number of keys 45 | foreach ($dbs as $i) { 46 | if (preg_match('/^keys=([0-9]+),expires=([0-9]+)/', $info["db{$i}"], $matches)) { 47 | $current['dbs'][$i] = array( 48 | 'id' => $i, 49 | 'keys' => $matches[1], 50 | 'name' => (isset($current['dbNames'][$i]) ? $current['dbNames'][$i] : null), 51 | ); 52 | } 53 | } 54 | $this->db->select($current['database']); 55 | 56 | $this->app->current = $current; 57 | } 58 | 59 | public function __get($object) 60 | { 61 | return isset($this->_objects[$object]) ? $this->_objects[$object] : null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /libraries/db.php: -------------------------------------------------------------------------------- 1 | config['database']['driver']; 9 | 10 | $instanceName = $driver . ':' . $config['host'] . ':' . $config['port']; 11 | 12 | if (!isset(self::$_instances[$instanceName])) { 13 | include_once(App::instance()->drivers.'db/'.(strtolower($driver)).'.php'); 14 | 15 | $class = ucwords(strtolower($driver)).'Db'; 16 | self::$_instances[$instanceName] = new $class($config); 17 | } 18 | 19 | return self::$_instances[$instanceName]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libraries/drivers/db/redis.php: -------------------------------------------------------------------------------- 1 | connect($config['host'], $config['port']); 7 | 8 | if (isset($config['password'])) { 9 | $this->auth($config['password']); 10 | } 11 | 12 | $this->select($config['database']); 13 | } 14 | 15 | public function changeDB($db) 16 | { 17 | $this->select($db); 18 | 19 | return $this; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libraries/drivers/log/file.php: -------------------------------------------------------------------------------- 1 | config['log']['file']['directory']; 10 | 11 | $this->threshold = App::instance()->config['log']['threshold']; 12 | 13 | if ($this->threshold > 0) { 14 | if (!$config_dir) { 15 | die('Please provide a log directory in your config file'); 16 | } else { 17 | $this->_dir = dirname(__FILE__).'/../../../'.$config_dir.'/'.PHP_SAPI.'/'; 18 | 19 | if (!is_writable($this->_dir)) { 20 | if (!mkdir($this->_dir, 0755, true)) { 21 | die("{$this->_dir} does not exist or is not writable"); 22 | } 23 | } 24 | } 25 | } 26 | } 27 | 28 | public function write($type, $msg, $namespace = null) 29 | { 30 | if ($this->threshold < Log::instance()->$type) { 31 | return; 32 | } 33 | 34 | $logfile = $this->_dir.date('Y-m-d').'.log'; 35 | 36 | if (($file = fopen($logfile, 'a+')) === false) { 37 | die('Can not open file: '.$logfile); 38 | } 39 | 40 | $ip = isset($_SERVER['REMOTE_ADDR']) ? "[{$_SERVER['REMOTE_ADDR']}]" : ''; 41 | $namespace = isset($namespace) ? '['.ucwords(strtolower($namespace)).']' : ''; 42 | $date = '['.date('Y-m-d H:i:s').']'; 43 | 44 | fwrite($file, "{$date} {$ip} {$namespace} [{$type}]: {$msg}\n"); 45 | fclose($file); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /libraries/drivers/log/std.php: -------------------------------------------------------------------------------- 1 | config['log']['threshold'] < Log::instance()->$type) { 7 | return; 8 | } 9 | 10 | $ip = isset($_SERVER['REMOTE_ADDR']) ? "[{$_SERVER['REMOTE_ADDR']}]" : '[Unknown Address]'; 11 | $namespace = isset($namespace) ? '['.ucwords(strtolower($namespace)).']' : ''; 12 | 13 | if (in_array($type, [Log::ERROR, Log::NOTICE, Log::WARNING])) { 14 | $stream = fopen('php://stderr', 'w'); 15 | } else { 16 | $stream = fopen('php://stdout', 'w'); 17 | } 18 | 19 | fwrite($stream, "PHPREDMIN: {$ip} {$namespace} [{$type}]: {$msg}\n"); 20 | fclose($stream); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /libraries/drivers/template/json.php: -------------------------------------------------------------------------------- 1 | _dir = '../views/'; 17 | $this->app = App::instance(); 18 | $this->router = Router::instance(); 19 | } 20 | 21 | public function render($__view, $data = array()) 22 | { 23 | echo $this->renderPartial(App::instance()->config['default_layout'], array('content' => $this->renderPartial($__view, $data))); 24 | } 25 | 26 | public function renderPartial($__view, $__data = array()) 27 | { 28 | $this->_data = array_merge($__data, $this->_data); 29 | 30 | ob_start(); 31 | 32 | include($this->_dir.$__view.'.php'); 33 | 34 | $content = ob_get_contents(); 35 | 36 | ob_end_clean(); 37 | 38 | return $content; 39 | } 40 | 41 | public function addHeader($header) 42 | { 43 | if (!in_array($header, $this->_headers)) { 44 | $this->_headers[] = $header; 45 | } 46 | } 47 | 48 | public function getHeaders() 49 | { 50 | return $this->_headers; 51 | } 52 | 53 | public function __set($key, $value) 54 | { 55 | $this->_data[$key] = $value; 56 | } 57 | 58 | public function __get($key) 59 | { 60 | return isset($this->_data[$key]) ? $this->_data[$key] : null; 61 | } 62 | 63 | public function __isset($key) 64 | { 65 | return isset($this->_data[$key]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /libraries/error.php: -------------------------------------------------------------------------------- 1 | config['production']) { 8 | ini_set('display_errors', 0); 9 | } 10 | 11 | // Set a custom error Handler 12 | set_error_handler(array($this, 'errorHandler')); 13 | // Set a custom Exception Handler 14 | set_exception_handler(array($this, 'exceptionHandler')); 15 | // Set Shutdown Handler 16 | register_shutdown_function(array($this, 'shutdownHandler')); 17 | } 18 | 19 | public static function shutdownHandler() 20 | { 21 | $error = error_get_last(); 22 | 23 | if ($error) { 24 | $type = self::_getError($error['type']); 25 | Log::factory()->write($type, "{$error['message']} on {$error['file']}:{$error['line']}", 'Shutdown Handler'); 26 | } 27 | } 28 | 29 | public function exceptionHandler(Exception $e) 30 | { 31 | Log::factory()->write(Log::ERROR, "{$e->getMessage()} on {$e->getFile()}:{$e->getLine()}", 'Exception Handler'); 32 | } 33 | 34 | public function errorHandler($no, $str, $file, $line, $context) 35 | { 36 | if (!(error_reporting() & $no)) { 37 | // This error code is not included in error_reporting 38 | return; 39 | } 40 | 41 | $type = self::_getError($no); 42 | 43 | Log::factory()->write($type, "{$str} on {$file}:{$line}", 'Error Handler'); 44 | } 45 | 46 | protected static function _getError($type) 47 | { 48 | switch ($type) { 49 | case E_WARNING: // 2 // 50 | return Log::WARNING; 51 | case E_NOTICE: // 8 // 52 | return Log::NOTICE; 53 | case E_CORE_WARNING: // 32 // 54 | return Log::WARNING; 55 | case E_USER_WARNING: // 512 // 56 | return Log::WARNING; 57 | case E_USER_NOTICE: // 1024 // 58 | return Log::NOTICE; 59 | case E_DEPRECATED: // 8192 // 60 | return Log::WARNING; 61 | case E_USER_DEPRECATED: // 16384 // 62 | return Log::WARNING; 63 | } 64 | 65 | return Log::ERROR; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /libraries/inputs.php: -------------------------------------------------------------------------------- 1 | method) { 18 | case Router::POST: 19 | $result = $this->post($key, $default); 20 | break; 21 | case Router::PUT: 22 | $result = $this->put($key, $default); 23 | break; 24 | case Router::GET: 25 | $result = $this->get($key, $default); 26 | break; 27 | default: 28 | $result = $default; 29 | } 30 | 31 | return $result; 32 | } 33 | 34 | public function post($key, $default = null) 35 | { 36 | if (isset($_POST[$key])) { 37 | if (is_array($_POST[$key])) { 38 | $results = array(); 39 | 40 | foreach ($_POST[$key] as $index => $value) { 41 | $results[$index] = filter_var($value, FILTER_SANITIZE_STRING); 42 | } 43 | 44 | return $results; 45 | } else { 46 | return filter_var($_POST[$key], FILTER_SANITIZE_STRING); 47 | } 48 | } else { 49 | return $default; 50 | } 51 | } 52 | 53 | public function get($key, $default = null) 54 | { 55 | $result = Router::instance()->query($key, $default); 56 | return $result ? $result : $default; 57 | } 58 | 59 | public function put($key, $default = null) 60 | { 61 | parse_str(file_get_contents("php://input"), $vars); 62 | return isset($vars[$key]) ? filter_var($vars[$key], FILTER_SANITIZE_STRING) : $default; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /libraries/log.php: -------------------------------------------------------------------------------- 1 | 1, 10 | 'notice' => 2, 11 | 'info' => 3, 12 | 'warning' => 4, 13 | 'debug' => 5 14 | ); 15 | 16 | const INFO = 'info'; 17 | const DEBUG = 'debug'; 18 | const ERROR = 'error'; 19 | const WARNING = 'warning'; 20 | const NOTICE = 'notice'; 21 | 22 | public static function instance() 23 | { 24 | if (!isset(self::$_instance)) { 25 | self::$_instance = new self; 26 | } 27 | 28 | return self::$_instance; 29 | } 30 | 31 | public static function factory($driver = null) 32 | { 33 | $driver = isset($driver) ? $driver : App::instance()->config['log']['driver']; 34 | 35 | if (!isset(self::$_instances[$driver])) { 36 | include_once(App::instance()->drivers.'log/'.(strtolower($driver)).'.php'); 37 | 38 | $class = ucwords(strtolower($driver)).'Log'; 39 | self::$_instances[$driver] = new $class; 40 | } 41 | 42 | return self::$_instances[$driver]; 43 | } 44 | 45 | public function __get($key) 46 | { 47 | return isset($this->_levels[$key]) ? $this->_levels[$key] : 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /libraries/model.php: -------------------------------------------------------------------------------- 1 | _objects['app'] = App::instance(); 9 | $this->_objects['router'] = Router::instance(); 10 | $this->_objects['session'] = Session::instance(); 11 | $this->_objects['db'] = Db::factory($config); 12 | $this->_objects['log'] = Log::factory(); 13 | } 14 | 15 | public function __get($object) 16 | { 17 | return isset($this->_objects[$object]) ? $this->_objects[$object] : null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libraries/router.php: -------------------------------------------------------------------------------- 1 | parse(); 18 | } 19 | 20 | public static function instance() 21 | { 22 | if (!self::$_instance) { 23 | self::$_instance = new self; 24 | } 25 | 26 | return self::$_instance; 27 | } 28 | 29 | protected function parse() 30 | { 31 | $this->path = ''; 32 | $this->host = ''; 33 | $this->method = self::GET; 34 | 35 | if (PHP_SAPI != 'cli') { 36 | $this->host = $_SERVER['HTTP_HOST']; 37 | $this->method = $_SERVER['REQUEST_METHOD']; 38 | 39 | $this->path = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['REQUEST_URI']); 40 | Log::factory()->write(Log::INFO, $_SERVER['REQUEST_URI'], 'Router'); 41 | 42 | if ($this->path == $_SERVER['REQUEST_URI']) { 43 | $this->path = ''; 44 | } 45 | } elseif (isset($_SERVER['argv'][1])) { 46 | $this->path = $_SERVER['argv'][1]; 47 | } 48 | 49 | $this->baseUrl = '//'.$this->host; 50 | $this->url = '//'.$this->host.$_SERVER['SCRIPT_NAME']; 51 | $this->request = $this->url.$this->path; 52 | 53 | if (preg_match('/^(.*)\/(.*)$/', $_SERVER['SCRIPT_NAME'], $matches)) { 54 | $this->baseUrl .= $matches[1]; 55 | } 56 | 57 | if (preg_match('/^(.*)\?(.*)$/', $this->path, $matches)) { 58 | $this->path = $matches[1]; 59 | 60 | foreach (explode('&', $matches[2]) as $match) { 61 | if (preg_match('/^(.*)=(.*)$/', $match, $strings)) { 62 | if ($strings[2]) { 63 | $this->_query_strings[$strings[1]] = urldecode($strings[2]); 64 | } 65 | } 66 | } 67 | } 68 | 69 | $this->_params = explode('/', trim($this->path, '/')); 70 | 71 | if (!$this->controller = ucwords(strtolower(array_shift($this->_params)))) { 72 | $this->controller = App::instance()->config['default_controller']; 73 | } 74 | 75 | if (!$this->action = array_shift($this->_params)) { 76 | $this->action = App::instance()->config['default_action']; 77 | } 78 | 79 | if (!$this->serverId = array_shift($this->_params)) { 80 | $this->serverId = 0; 81 | } 82 | 83 | if (!$this->dbId = array_shift($this->_params)) { 84 | $this->dbId = 0; 85 | } 86 | } 87 | 88 | public function __get($key) 89 | { 90 | return isset($this->_data[$key]) ? $this->_data[$key] : null; 91 | } 92 | 93 | public function __set($key, $value) 94 | { 95 | $this->_data[$key] = $value; 96 | } 97 | 98 | public function query($key, $default = null) 99 | { 100 | return isset($this->_query_strings[$key]) ? filter_var($this->_query_strings[$key], FILTER_SANITIZE_STRING) : $default; 101 | } 102 | 103 | public function route() 104 | { 105 | $class = $this->controller.'_Controller'; 106 | $method = $this->action.'Action'; 107 | 108 | if (class_exists($class)) { 109 | $controller = new $class( 110 | array( 111 | 'serverId' => $this->serverId, 112 | 'dbId' => $this->dbId, 113 | ) 114 | ); 115 | if (method_exists($controller, $method)) { 116 | call_user_func_array(array($controller, $method), $this->_params); 117 | } 118 | 119 | return; 120 | } 121 | 122 | header("HTTP/1.0 404 Not Found"); 123 | Template::factory()->render('404'); 124 | } 125 | 126 | public function redirect($url) 127 | { 128 | header("Location: {$this->url}/{$url}"); 129 | exit; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /libraries/session.php: -------------------------------------------------------------------------------- 1 | config['session']['gc_probability']); 10 | ini_set('session.gc_divisor', 100); 11 | ini_set('session.gc_maxlifetime', App::instance()->config['session']['lifetime']); 12 | 13 | session_name(App::instance()->config['session']['name']); 14 | session_start(); 15 | } 16 | 17 | public static function instance() 18 | { 19 | if (!isset(self::$_instance)) { 20 | self::$_instance = new self; 21 | } 22 | 23 | return self::$_instance; 24 | } 25 | 26 | public function set($key, $value) 27 | { 28 | $_SESSION[$key] = $value; 29 | 30 | session_write_close(); 31 | } 32 | 33 | public function get($key, $default = null) 34 | { 35 | return isset($_SESSION[$key]) ? $_SESSION[$key] : $default; 36 | } 37 | 38 | public function get_once($key, $default = null) 39 | { 40 | $result = $this->get($key, $default); 41 | 42 | unset($_SESSION[$key]); 43 | 44 | return $result; 45 | } 46 | 47 | public function del($key) 48 | { 49 | unset($_SESSION[$key]); 50 | } 51 | 52 | public function has($key) 53 | { 54 | return isset($_SESSION[$key]); 55 | } 56 | 57 | public function __set($key, $value) 58 | { 59 | $this->set($key, $value); 60 | } 61 | 62 | public function __get($key) 63 | { 64 | return $this->get($key); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /libraries/template.php: -------------------------------------------------------------------------------- 1 | drivers.'template/'.(strtolower($driver)).'.php'); 12 | 13 | $class = ucwords(strtolower($driver)).'Template'; 14 | self::$_instances[$driver] = new $class; 15 | } 16 | 17 | return self::$_instances[$driver]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /models/info.php: -------------------------------------------------------------------------------- 1 | currentServer = $config; 18 | } 19 | 20 | public function resetStats() 21 | { 22 | $this->db->changeDB($this->currentServer['stats']['database']); 23 | 24 | $keys = $this->db->keys(self::STATS_MODEL_KEY . '*'); 25 | 26 | foreach ($keys as $key) { 27 | $this->db->del($key); 28 | } 29 | 30 | $this->db->changeDB($this->currentServer['database']); 31 | } 32 | 33 | public function addKey($key, $value, $time) 34 | { 35 | $this->db->changeDB($this->currentServer['stats']['database']); 36 | 37 | // add value with timestamp prefix to make it unique 38 | // in other case non-unique value won't be added 39 | // @see http://redis.io/commands/zadd 40 | $this->db->zAdd(self::STATS_MODEL_KEY . $key, $time, $time . ':' . $value); 41 | 42 | $this->db->changeDB($this->currentServer['database']); 43 | } 44 | 45 | public function getKeys($key, $from, $to) 46 | { 47 | $this->db->changeDB($this->currentServer['stats']['database']); 48 | 49 | $results = array(); 50 | $keys = $this->db->zRevRangeByScore(self::STATS_MODEL_KEY . $key, $to, $from, array('withscores' => true)); 51 | 52 | foreach ($keys as $value => $time) { 53 | $value = explode(':', $value); 54 | $results[] = array($time, (float)$value[1]); 55 | } 56 | 57 | $this->db->changeDB($this->currentServer['database']); 58 | 59 | return $results; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | # Enable the short tags for PHP scripts 2 | php_value short_open_tag 1 -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/css/custom.css: -------------------------------------------------------------------------------- 1 | .action { 2 | color: #000; 3 | text-decoration: none; 4 | } 5 | .action:hover { 6 | color: #000; 7 | text-decoration: none; 8 | } 9 | textarea { 10 | width: 232px; 11 | } 12 | .nav-pills>li.server>a { 13 | padding-right: 6px; 14 | font-size: 90%; 15 | } 16 | .nav-pills>li.database .label { 17 | margin-top: -2px; 18 | } 19 | .terminal { 20 | margin-left: 0; 21 | background-color: #f5f5f5; 22 | color: #657b83; 23 | padding: 10px; 24 | font-family: monospace; 25 | } 26 | .terminal-console { 27 | border: #ddd 1px solid; 28 | height: 370px; 29 | border-top-left-radius: 4px; 30 | border-top-right-radius: 4px; 31 | border-bottom-right-radius: 4px; 32 | overflow-y: scroll; 33 | } 34 | .terminal-command-line { 35 | color: #002b36; 36 | background-color: #f5f5f5; 37 | margin-left: auto !important; 38 | margin-right: auto !important; 39 | border-left: #ddd 1px solid; 40 | border-right: #ddd 1px solid; 41 | border-bottom: #ddd 1px solid; 42 | border-bottom-left-radius: 4px; 43 | border-bottom-right-radius: 4px; 44 | } 45 | #terminal-input { 46 | color: #002b36; 47 | background-color: #f5f5f5; 48 | border: 0px; 49 | } 50 | .dark_theme #terminal-input { background-color: #696969 !important; color: #D2D2D2; } 51 | .dark_theme .terminal { background-color: #3C3C3C !important; color: #D2D2D2; } 52 | .dark_theme .terminal-console { border: #2D2D2D 1px solid !important; } 53 | .dark_theme .terminal-command-line { 54 | background-color: #696969 !important; 55 | border-color: #2D2D2D !important; 56 | } 57 | .terminal-prompt { 58 | padding-top: 5px; 59 | padding-left: 4px; 60 | } 61 | .ajax-loading { 62 | background-image: url(../img/ajax-loader.gif); 63 | background-repeat: no-repeat; 64 | background-position: left center; 65 | } 66 | .term_theme { 67 | display: inline-block; 68 | font-size: 1px; 69 | overflow: hidden; 70 | text-indent: 100px; 71 | width: 12px; 72 | height: 12px; 73 | } 74 | #term_light, #term_dark { 75 | outline: 1px solid #B4B4B4; 76 | border: 1px solid #fff; 77 | margin-left: 7px; 78 | } 79 | #term_light:hover, #term_dark:hover { 80 | outline-color: #3C3C3C; 81 | } 82 | #term_light { 83 | background-color: #A5A5A5; 84 | background: -moz-linear-gradient(left top, #fff, #A5A5A5); 85 | background: -ms-linear-gradient(left top, #fff, #A5A5A5); 86 | background: -webkit-linear-gradient(left top, #fff, #A5A5A5); 87 | } 88 | #term_dark { 89 | background-color: #000; 90 | background: -moz-linear-gradient(left top, #A5A5A5, #000); 91 | background: -ms-linear-gradient(left top, #A5A5A5, #000); 92 | background: -webkit-linear-gradient(left top, #A5A5A5, #000); 93 | } 94 | .terminal-clear { 95 | color: #657b83; 96 | float: left; 97 | margin: 10px; 98 | cursor: pointer; 99 | } 100 | #dbTabs { 101 | margin-right: -15px; 102 | margin-top: -42px; 103 | } 104 | 105 | #dbTabs ul { 106 | margin: 0px 0px 0px 15px; 107 | padding-top: 30px; 108 | width: 100%; 109 | } 110 | 111 | #redisTab { 112 | margin: 0px; 113 | } 114 | 115 | .tab-content { 116 | padding: 10px 0 0 20px; 117 | } 118 | 119 | #srvList { 120 | margin-right: -31px; 121 | padding: 0 31px 100% 0; 122 | border-right: 1px solid #ddd; 123 | } 124 | 125 | #mainContainer { 126 | padding-left: 1em; 127 | } 128 | 129 | #mainContainer > h3 { 130 | margin-left: -14px; 131 | border-left: 1px solid #ddd; 132 | border-top: 1px solid #ddd; 133 | padding: 10px 0 0 15px; 134 | margin-top: 0px; 135 | border-radius: 4px 0 0 0; 136 | } 137 | 138 | #dbTabs .nav-tabs > .active > a:hover { 139 | cursor: pointer; 140 | } 141 | 142 | .dropdown-menu li>a.danger-action { 143 | color: #d62728; 144 | } 145 | 146 | .dropdown-menu li>a.warning-action { 147 | color: #c67605; 148 | } 149 | -------------------------------------------------------------------------------- /public/font-awesome/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | *.db 4 | *.db.old 5 | *.swp 6 | *.db-journal 7 | 8 | .coverage 9 | .DS_Store 10 | .installed.cfg 11 | 12 | .idea/* 13 | .svn/* 14 | src/website/static/* 15 | src/website/media/* 16 | 17 | bin 18 | build 19 | cfcache 20 | develop-eggs 21 | dist 22 | downloads 23 | eggs 24 | parts 25 | tmp 26 | .sass-cache 27 | 28 | src/website/settingslocal.py 29 | stunnel.log -------------------------------------------------------------------------------- /public/font-awesome/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/font-awesome/font/FontAwesome.otf -------------------------------------------------------------------------------- /public/font-awesome/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/font-awesome/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/font-awesome/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/font-awesome/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/font-awesome/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/font-awesome/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/img/ajax-loader.gif -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | config['timezone'])) { 20 | date_default_timezone_set(App::instance()->config['timezone']); 21 | 22 | } 23 | 24 | $authenticated = true; 25 | 26 | if (PHP_SAPI !== 'cli' && isset(App::instance()->config['auth'])) { 27 | $username = null; 28 | $password = null; 29 | 30 | $auth = App::instance()->config['auth']; 31 | 32 | if(isset($auth['username']) && isset($auth['password'])) 33 | { 34 | // mod_php 35 | if (isset($_SERVER['PHP_AUTH_USER'])) { 36 | $username = $_SERVER['PHP_AUTH_USER']; 37 | $password = $_SERVER['PHP_AUTH_PW']; 38 | // most other servers 39 | } elseif (isset($_SERVER['HTTP_AUTHORIZATION']) && strpos(strtolower($_SERVER['HTTP_AUTHORIZATION']), 'basic') === 0) { 40 | list($username, $password) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); 41 | } 42 | 43 | if ($username != $auth['username'] || !password_verify($password, $auth['password'])) { 44 | $authenticated = false; 45 | } 46 | } 47 | } 48 | 49 | if ($authenticated) { 50 | $error = new Error(); 51 | Router::instance()->route(); 52 | } else { 53 | header('WWW-Authenticate: Basic realm="PHPRedis Administrator"'); 54 | header('HTTP/1.0 401 Unauthorized'); 55 | echo 'Not Authorized'; 56 | die(); 57 | } 58 | -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_flat_30_cccccc_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_flat_30_cccccc_40x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_flat_50_5c5c5c_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_flat_50_5c5c5c_40x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_glass_20_555555_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_glass_20_555555_1x400.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_glass_40_0078a3_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_glass_40_0078a3_1x400.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_glass_40_ffc73d_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_glass_40_ffc73d_1x400.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_gloss-wave_25_333333_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_gloss-wave_25_333333_500x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_inset-soft_25_000000_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_inset-soft_25_000000_1x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-bg_inset-soft_30_f58400_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-bg_inset-soft_30_f58400_1x100.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-icons_4b8e0b_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-icons_4b8e0b_256x240.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-icons_a83300_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-icons_a83300_256x240.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-icons_cccccc_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-icons_cccccc_256x240.png -------------------------------------------------------------------------------- /public/js/jquery-ui/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/jquery-ui/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /public/js/nvd3/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Jekyll Files # 3 | ################ 4 | _site 5 | 6 | 7 | # Random Files # 8 | ################ 9 | *.swp 10 | *~ 11 | *.log 12 | 13 | 14 | # Private Test Data # 15 | ##################### 16 | *REALDATA* 17 | 18 | 19 | # OS generated files # 20 | ###################### 21 | .DS_Store* 22 | ehthumbs.db 23 | Icon? 24 | Thumbs.db 25 | -------------------------------------------------------------------------------- /public/js/nvd3/lib/cie.js: -------------------------------------------------------------------------------- 1 | (function(d3) { 2 | var cie = d3.cie = {}; 3 | 4 | cie.lab = function(l, a, b) { 5 | return arguments.length === 1 6 | ? (l instanceof Lab ? lab(l.l, l.a, l.b) 7 | : (l instanceof Lch ? lch_lab(l.l, l.c, l.h) 8 | : rgb_lab((l = d3.rgb(l)).r, l.g, l.b))) 9 | : lab(+l, +a, +b); 10 | }; 11 | 12 | cie.lch = function(l, c, h) { 13 | return arguments.length === 1 14 | ? (l instanceof Lch ? lch(l.l, l.c, l.h) 15 | : (l instanceof Lab ? lab_lch(l.l, l.a, l.b) 16 | : lab_lch((l = rgb_lab((l = d3.rgb(l)).r, l.g, l.b)).l, l.a, l.b))) 17 | : lch(+l, +c, +h); 18 | }; 19 | 20 | cie.interpolateLab = function(a, b) { 21 | a = cie.lab(a); 22 | b = cie.lab(b); 23 | var al = a.l, 24 | aa = a.a, 25 | ab = a.b, 26 | bl = b.l - al, 27 | ba = b.a - aa, 28 | bb = b.b - ab; 29 | return function(t) { 30 | return lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; 31 | }; 32 | }; 33 | 34 | cie.interpolateLch = function(a, b) { 35 | a = cie.lch(a); 36 | b = cie.lch(b); 37 | var al = a.l, 38 | ac = a.c, 39 | ah = a.h, 40 | bl = b.l - al, 41 | bc = b.c - ac, 42 | bh = b.h - ah; 43 | if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; // shortest path 44 | return function(t) { 45 | return lch_lab(al + bl * t, ac + bc * t, ah + bh * t) + ""; 46 | }; 47 | }; 48 | 49 | function lab(l, a, b) { 50 | return new Lab(l, a, b); 51 | } 52 | 53 | function Lab(l, a, b) { 54 | this.l = l; 55 | this.a = a; 56 | this.b = b; 57 | } 58 | 59 | Lab.prototype.brighter = function(k) { 60 | return lab(Math.min(100, this.l + K * (arguments.length ? k : 1)), this.a, this.b); 61 | }; 62 | 63 | Lab.prototype.darker = function(k) { 64 | return lab(Math.max(0, this.l - K * (arguments.length ? k : 1)), this.a, this.b); 65 | }; 66 | 67 | Lab.prototype.rgb = function() { 68 | return lab_rgb(this.l, this.a, this.b); 69 | }; 70 | 71 | Lab.prototype.toString = function() { 72 | return this.rgb() + ""; 73 | }; 74 | 75 | function lch(l, c, h) { 76 | return new Lch(l, c, h); 77 | } 78 | 79 | function Lch(l, c, h) { 80 | this.l = l; 81 | this.c = c; 82 | this.h = h; 83 | } 84 | 85 | Lch.prototype.brighter = function(k) { 86 | return lch(Math.min(100, this.l + K * (arguments.length ? k : 1)), this.c, this.h); 87 | }; 88 | 89 | Lch.prototype.darker = function(k) { 90 | return lch(Math.max(0, this.l - K * (arguments.length ? k : 1)), this.c, this.h); 91 | }; 92 | 93 | Lch.prototype.rgb = function() { 94 | return lch_lab(this.l, this.c, this.h).rgb(); 95 | }; 96 | 97 | Lch.prototype.toString = function() { 98 | return this.rgb() + ""; 99 | }; 100 | 101 | // Corresponds roughly to RGB brighter/darker 102 | var K = 18; 103 | 104 | // D65 standard referent 105 | var X = 0.950470, Y = 1, Z = 1.088830; 106 | 107 | function lab_rgb(l, a, b) { 108 | var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; 109 | x = lab_xyz(x) * X; 110 | y = lab_xyz(y) * Y; 111 | z = lab_xyz(z) * Z; 112 | return d3.rgb( 113 | xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), 114 | xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), 115 | xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z) 116 | ); 117 | } 118 | 119 | function rgb_lab(r, g, b) { 120 | r = rgb_xyz(r); 121 | g = rgb_xyz(g); 122 | b = rgb_xyz(b); 123 | var x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / X), 124 | y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / Y), 125 | z = xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / Z); 126 | return lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); 127 | } 128 | 129 | function lab_lch(l, a, b) { 130 | var c = Math.sqrt(a * a + b * b), 131 | h = Math.atan2(b, a) / Math.PI * 180; 132 | return lch(l, c, h); 133 | } 134 | 135 | function lch_lab(l, c, h) { 136 | h = h * Math.PI / 180; 137 | return lab(l, Math.cos(h) * c, Math.sin(h) * c); 138 | } 139 | 140 | function lab_xyz(x) { 141 | return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037; 142 | } 143 | 144 | function xyz_lab(x) { 145 | return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; 146 | } 147 | 148 | function xyz_rgb(r) { 149 | return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055)); 150 | } 151 | 152 | function rgb_xyz(r) { 153 | return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); 154 | } 155 | })(d3); 156 | -------------------------------------------------------------------------------- /public/js/nvd3/lib/crossfilter.min.js: -------------------------------------------------------------------------------- 1 | (function(a){function b(a){return a}function c(a,b){for(var c=0,d=b.length,e=new Array(d);c>1;a(b[f])>1;c>>1)+1;while(--f>0)d(a,f,e,b);return a}function c(a,b,c){var e=c-b,f;while(--e>0)f=a[b],a[b]=a[b+e],a[b+e]=f,d(a,1,e,b);return a}function d(b,c,d,e){var f=b[--e+c],g=a(f),h;while((h=c<<1)<=d){ha(b[e+h+1])&&h++;if(g<=a(b[e+h]))break;b[e+c]=b[e+h],c=h}b[e+c]=f}return b.sort=c,b}function i(a){function c(c,d,e,f){var g=new Array(f=Math.min(e-d,f)),h,i,j,k;for(i=0;ih)g[0]=k,h=a(b(g,0,f)[0]);while(++dc&&a(b[f-1])>h;--f)b[f]=b[f-1];b[f]=g}return b}return b}function m(a){function c(a,c,e){return(e-c>1,j=i-f,k=i+f,l=b[g],m=a(l),n=b[j],o=a(n),p=b[i],q=a(p),r=b[k],s=a(r),t=b[h],u=a(t),v;m>o&&(v=l,l=n,n=v,v=m,m=o,o=v),s>u&&(v=r,r=t,t=v,v=s,s=u,u=v),m>q&&(v=l,l=p,p=v,v=m,m=q,q=v),o>q&&(v=n,n=p,p=v,v=o,o=q,q=v),m>s&&(v=l,l=r,r=v,v=m,m=s,s=v),q>s&&(v=p,p=r,r=v,v=q,q=s,s=v),o>u&&(v=n,n=t,t=v,v=o,o=u,u=v),o>q&&(v=n,n=p,p=v,v=o,o=q,q=v),s>u&&(v=r,r=t,t=v,v=s,s=u,u=v);var w=n,x=o,y=r,z=s;b[g]=l,b[j]=b[d],b[i]=p,b[k]=b[e-1],b[h]=t;var A=d+1,B=e-2,C=x<=z&&x>=z;if(C)for(var D=A;D<=B;++D){var E=b[D],F=a(E);if(Fx)for(;;){var G=a(b[B]);if(G>x){B--;continue}if(Gz)for(;;){var G=a(b[B]);if(G>z){B--;if(Bh){var H,G;while((H=a(b[A]))<=x&&H>=x)++A;while((G=a(b[B]))<=z&&G>=z)--B;for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<=x&&F>=x)D!==A&&(b[D]=b[A],b[A]=E),A++;else if(F<=z&&F>=z)for(;;){var G=a(b[B]);if(G<=z&&G>=z){B--;if(BN)for(b=N,c=Math.min(e,O);bO)for(b=Math.max(e,O),c=f;b=N&&a>0)k[d=D[c]]||(b.push(e[d]),--a);return b}function X(a){function K(b,c,g,i){function Q(){++n===m&&(p=s(p,j<<=1),h=s(h,j),m=G(j))}var o=d,p=E(n,m),t=v,u=F,w=n,y=0,z=0,A,B,C,D,K,L;J&&(t=u=x),d=new Array(n),n=0,h=w>1?r(h,f):E(f,m),w&&(C=(B=o[0]).key);while(z=D))++z;while(zL)){h[A=c[z]+g]=n,k[A]&q||(K.value=t(K.value,e[A]));if(++z>=i)break;D=a(b[z])}Q()}while(yy)for(y=0;y1?(H=M,I=O):(n===1?(H=N,I=P):(H=x,I=x),h=null),l[A]=H}function M(a,b,c){if(a===p||J)return;var f,g,i,j;for(f=0,i=b.length;fj&&(k=s(k,j<<=1)),P(e,0,f),Q(e,0,f),o}function t(){function i(a,d,g){var i;if(h)return;for(i=d;i= radius) return {x: d.x, y: d.y, z: 1}; 18 | var k = k0 * (1 - Math.exp(-dd * k1)) / dd * .75 + .25; 19 | return {x: focus[0] + dx * k, y: focus[1] + dy * k, z: Math.min(k, 10)}; 20 | } 21 | 22 | function rescale() { 23 | k0 = Math.exp(distortion); 24 | k0 = k0 / (k0 - 1) * radius; 25 | k1 = distortion / radius; 26 | return fisheye; 27 | } 28 | 29 | fisheye.radius = function(_) { 30 | if (!arguments.length) return radius; 31 | radius = +_; 32 | return rescale(); 33 | }; 34 | 35 | fisheye.distortion = function(_) { 36 | if (!arguments.length) return distortion; 37 | distortion = +_; 38 | return rescale(); 39 | }; 40 | 41 | fisheye.focus = function(_) { 42 | if (!arguments.length) return focus; 43 | focus = _; 44 | return fisheye; 45 | }; 46 | 47 | return rescale(); 48 | } 49 | }; 50 | 51 | function d3_fisheye_scale(scale, d, a) { 52 | 53 | function fisheye(_) { 54 | var x = scale(_), 55 | left = x < a, 56 | v, 57 | range = d3.extent(scale.range()), 58 | min = range[0], 59 | max = range[1], 60 | m = left ? a - min : max - a; 61 | if (m == 0) m = max - min; 62 | return (left ? -1 : 1) * m * (d + 1) / (d + (m / Math.abs(x - a))) + a; 63 | } 64 | 65 | fisheye.distortion = function(_) { 66 | if (!arguments.length) return d; 67 | d = +_; 68 | return fisheye; 69 | }; 70 | 71 | fisheye.focus = function(_) { 72 | if (!arguments.length) return a; 73 | a = +_; 74 | return fisheye; 75 | }; 76 | 77 | fisheye.copy = function() { 78 | return d3_fisheye_scale(scale.copy(), d, a); 79 | }; 80 | 81 | fisheye.nice = scale.nice; 82 | fisheye.ticks = scale.ticks; 83 | fisheye.tickFormat = scale.tickFormat; 84 | return d3.rebind(fisheye, scale, "domain", "range"); 85 | } 86 | })(); 87 | -------------------------------------------------------------------------------- /public/js/nvd3/lib/hive.js: -------------------------------------------------------------------------------- 1 | d3.hive = {}; 2 | 3 | d3.hive.link = function() { 4 | var source = function(d) { return d.source; }, 5 | target = function(d) { return d.target; }, 6 | angle = function(d) { return d.angle; }, 7 | startRadius = function(d) { return d.radius; }, 8 | endRadius = startRadius, 9 | arcOffset = -Math.PI / 2; 10 | 11 | function link(d, i) { 12 | var s = node(source, this, d, i), 13 | t = node(target, this, d, i), 14 | x; 15 | if (t.a < s.a) x = t, t = s, s = x; 16 | if (t.a - s.a > Math.PI) s.a += 2 * Math.PI; 17 | var a1 = s.a + (t.a - s.a) / 3, 18 | a2 = t.a - (t.a - s.a) / 3; 19 | return s.r0 - s.r1 || t.r0 - t.r1 20 | ? "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 21 | + "L" + Math.cos(s.a) * s.r1 + "," + Math.sin(s.a) * s.r1 22 | + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1 23 | + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1 24 | + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1 25 | + "L" + Math.cos(t.a) * t.r0 + "," + Math.sin(t.a) * t.r0 26 | + "C" + Math.cos(a2) * t.r0 + "," + Math.sin(a2) * t.r0 27 | + " " + Math.cos(a1) * s.r0 + "," + Math.sin(a1) * s.r0 28 | + " " + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 29 | : "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 30 | + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1 31 | + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1 32 | + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1; 33 | } 34 | 35 | function node(method, thiz, d, i) { 36 | var node = method.call(thiz, d, i), 37 | a = +(typeof angle === "function" ? angle.call(thiz, node, i) : angle) + arcOffset, 38 | r0 = +(typeof startRadius === "function" ? startRadius.call(thiz, node, i) : startRadius), 39 | r1 = (startRadius === endRadius ? r0 : +(typeof endRadius === "function" ? endRadius.call(thiz, node, i) : endRadius)); 40 | return {r0: r0, r1: r1, a: a}; 41 | } 42 | 43 | link.source = function(_) { 44 | if (!arguments.length) return source; 45 | source = _; 46 | return link; 47 | }; 48 | 49 | link.target = function(_) { 50 | if (!arguments.length) return target; 51 | target = _; 52 | return link; 53 | }; 54 | 55 | link.angle = function(_) { 56 | if (!arguments.length) return angle; 57 | angle = _; 58 | return link; 59 | }; 60 | 61 | link.radius = function(_) { 62 | if (!arguments.length) return startRadius; 63 | startRadius = endRadius = _; 64 | return link; 65 | }; 66 | 67 | link.startRadius = function(_) { 68 | if (!arguments.length) return startRadius; 69 | startRadius = _; 70 | return link; 71 | }; 72 | 73 | link.endRadius = function(_) { 74 | if (!arguments.length) return endRadius; 75 | endRadius = _; 76 | return link; 77 | }; 78 | 79 | return link; 80 | }; 81 | -------------------------------------------------------------------------------- /public/js/nvd3/lib/horizon.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | d3.horizon = function() { 3 | var bands = 1, // between 1 and 5, typically 4 | mode = "offset", // or mirror 5 | interpolate = "linear", // or basis, monotone, step-before, etc. 6 | x = d3_horizonX, 7 | y = d3_horizonY, 8 | w = 960, 9 | h = 40, 10 | duration = 0; 11 | 12 | var color = d3.scale.linear() 13 | .domain([-1, 0, 1]) 14 | .range(["#d62728", "#fff", "#1f77b4"]); 15 | 16 | // For each small multiple… 17 | function horizon(g) { 18 | g.each(function(d, i) { 19 | var g = d3.select(this), 20 | n = 2 * bands + 1, 21 | xMin = Infinity, 22 | xMax = -Infinity, 23 | yMax = -Infinity, 24 | x0, // old x-scale 25 | y0, // old y-scale 26 | id; // unique id for paths 27 | 28 | // Compute x- and y-values along with extents. 29 | var data = d.map(function(d, i) { 30 | var xv = x.call(this, d, i), 31 | yv = y.call(this, d, i); 32 | if (xv < xMin) xMin = xv; 33 | if (xv > xMax) xMax = xv; 34 | if (-yv > yMax) yMax = -yv; 35 | if (yv > yMax) yMax = yv; 36 | return [xv, yv]; 37 | }); 38 | 39 | // Compute the new x- and y-scales, and transform. 40 | var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), 41 | y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]), 42 | t1 = d3_horizonTransform(bands, h, mode); 43 | 44 | // Retrieve the old scales, if this is an update. 45 | if (this.__chart__) { 46 | x0 = this.__chart__.x; 47 | y0 = this.__chart__.y; 48 | t0 = this.__chart__.t; 49 | id = this.__chart__.id; 50 | } else { 51 | x0 = x1.copy(); 52 | y0 = y1.copy(); 53 | t0 = t1; 54 | id = ++d3_horizonId; 55 | } 56 | 57 | // We'll use a defs to store the area path and the clip path. 58 | var defs = g.selectAll("defs") 59 | .data([null]); 60 | 61 | // The clip path is a simple rect. 62 | defs.enter().append("defs").append("clipPath") 63 | .attr("id", "d3_horizon_clip" + id) 64 | .append("rect") 65 | .attr("width", w) 66 | .attr("height", h); 67 | 68 | defs.select("rect").transition() 69 | .duration(duration) 70 | .attr("width", w) 71 | .attr("height", h); 72 | 73 | // We'll use a container to clip all horizon layers at once. 74 | g.selectAll("g") 75 | .data([null]) 76 | .enter().append("g") 77 | .attr("clip-path", "url(#d3_horizon_clip" + id + ")"); 78 | 79 | // Instantiate each copy of the path with different transforms. 80 | var path = g.select("g").selectAll("path") 81 | .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); 82 | 83 | var d0 = d3_horizonArea 84 | .interpolate(interpolate) 85 | .x(function(d) { return x0(d[0]); }) 86 | .y0(h * bands) 87 | .y1(function(d) { return h * bands - y0(d[1]); }) 88 | (data); 89 | 90 | var d1 = d3_horizonArea 91 | .x(function(d) { return x1(d[0]); }) 92 | .y1(function(d) { return h * bands - y1(d[1]); }) 93 | (data); 94 | 95 | path.enter().append("path") 96 | .style("fill", color) 97 | .attr("transform", t0) 98 | .attr("d", d0); 99 | 100 | path.transition() 101 | .duration(duration) 102 | .style("fill", color) 103 | .attr("transform", t1) 104 | .attr("d", d1); 105 | 106 | path.exit().transition() 107 | .duration(duration) 108 | .attr("transform", t1) 109 | .attr("d", d1) 110 | .remove(); 111 | 112 | // Stash the new scales. 113 | this.__chart__ = {x: x1, y: y1, t: t1, id: id}; 114 | }); 115 | d3.timer.flush(); 116 | } 117 | 118 | horizon.duration = function(x) { 119 | if (!arguments.length) return duration; 120 | duration = +x; 121 | return horizon; 122 | }; 123 | 124 | horizon.bands = function(x) { 125 | if (!arguments.length) return bands; 126 | bands = +x; 127 | color.domain([-bands, 0, bands]); 128 | return horizon; 129 | }; 130 | 131 | horizon.mode = function(x) { 132 | if (!arguments.length) return mode; 133 | mode = x + ""; 134 | return horizon; 135 | }; 136 | 137 | horizon.colors = function(x) { 138 | if (!arguments.length) return color.range(); 139 | color.range(x); 140 | return horizon; 141 | }; 142 | 143 | horizon.interpolate = function(x) { 144 | if (!arguments.length) return interpolate; 145 | interpolate = x + ""; 146 | return horizon; 147 | }; 148 | 149 | horizon.x = function(z) { 150 | if (!arguments.length) return x; 151 | x = z; 152 | return horizon; 153 | }; 154 | 155 | horizon.y = function(z) { 156 | if (!arguments.length) return y; 157 | y = z; 158 | return horizon; 159 | }; 160 | 161 | horizon.width = function(x) { 162 | if (!arguments.length) return w; 163 | w = +x; 164 | return horizon; 165 | }; 166 | 167 | horizon.height = function(x) { 168 | if (!arguments.length) return h; 169 | h = +x; 170 | return horizon; 171 | }; 172 | 173 | return horizon; 174 | }; 175 | 176 | var d3_horizonArea = d3.svg.area(), 177 | d3_horizonId = 0; 178 | 179 | function d3_horizonX(d) { 180 | return d[0]; 181 | } 182 | 183 | function d3_horizonY(d) { 184 | return d[1]; 185 | } 186 | 187 | function d3_horizonTransform(bands, h, mode) { 188 | return mode == "offset" 189 | ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } 190 | : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; 191 | } 192 | })(); 193 | -------------------------------------------------------------------------------- /public/js/nvd3/nv.d3.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasanrose/phpredmin/969c5bba17355b688193e4347d62d9ec26ab2a5c/public/js/nvd3/nv.d3.min.js -------------------------------------------------------------------------------- /public/js/nvd3/src/core.js: -------------------------------------------------------------------------------- 1 | 2 | var nv = window.nv || {}; 3 | 4 | nv.version = '0.0.1a'; 5 | nv.dev = true //set false when in production 6 | 7 | window.nv = nv; 8 | 9 | nv.tooltip = {}; // For the tooltip system 10 | nv.utils = {}; // Utility subsystem 11 | nv.models = {}; //stores all the possible models/components 12 | nv.charts = {}; //stores all the ready to use charts 13 | nv.graphs = []; //stores all the graphs currently on the page 14 | nv.logs = {}; //stores some statistics and potential error messages 15 | 16 | nv.dispatch = d3.dispatch('render_start', 'render_end'); 17 | 18 | // ************************************************************************* 19 | // Development render timers - disabled if dev = false 20 | 21 | if (nv.dev) { 22 | nv.dispatch.on('render_start', function(e) { 23 | nv.logs.startTime = +new Date(); 24 | }); 25 | 26 | nv.dispatch.on('render_end', function(e) { 27 | nv.logs.endTime = +new Date(); 28 | nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime; 29 | nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times 30 | }); 31 | } 32 | 33 | // ******************************************** 34 | // Public Core NV functions 35 | 36 | // Logs all arguments, and returns the last so you can test things in place 37 | nv.log = function() { 38 | if (nv.dev && console.log && console.log.apply) 39 | console.log.apply(console, arguments) 40 | else if (nv.dev && console.log && Function.prototype.bind) { 41 | var log = Function.prototype.bind.call(console.log, console); 42 | log.apply(console, arguments); 43 | } 44 | return arguments[arguments.length - 1]; 45 | }; 46 | 47 | 48 | nv.render = function render(step) { 49 | step = step || 1; // number of graphs to generate in each timout loop 50 | 51 | render.active = true; 52 | nv.dispatch.render_start(); 53 | 54 | setTimeout(function() { 55 | var chart, graph; 56 | 57 | for (var i = 0; i < step && (graph = render.queue[i]); i++) { 58 | chart = graph.generate(); 59 | if (typeof graph.callback == typeof(Function)) graph.callback(chart); 60 | nv.graphs.push(chart); 61 | } 62 | 63 | render.queue.splice(0, i); 64 | 65 | if (render.queue.length) setTimeout(arguments.callee, 0); 66 | else { nv.render.active = false; nv.dispatch.render_end(); } 67 | }, 0); 68 | }; 69 | 70 | nv.render.active = false; 71 | nv.render.queue = []; 72 | 73 | nv.addGraph = function(obj) { 74 | if (typeof arguments[0] === typeof(Function)) 75 | obj = {generate: arguments[0], callback: arguments[1]}; 76 | 77 | nv.render.queue.push(obj); 78 | 79 | if (!nv.render.active) nv.render(); 80 | }; 81 | 82 | nv.identity = function(d) { return d; }; 83 | 84 | nv.strip = function(s) { return s.replace(/(\s|&)/g,''); }; 85 | 86 | function daysInMonth(month,year) { 87 | return (new Date(year, month+1, 0)).getDate(); 88 | } 89 | 90 | function d3_time_range(floor, step, number) { 91 | return function(t0, t1, dt) { 92 | var time = floor(t0), times = []; 93 | if (time < t0) step(time); 94 | if (dt > 1) { 95 | while (time < t1) { 96 | var date = new Date(+time); 97 | if ((number(date) % dt === 0)) times.push(date); 98 | step(time); 99 | } 100 | } else { 101 | while (time < t1) { times.push(new Date(+time)); step(time); } 102 | } 103 | return times; 104 | }; 105 | } 106 | 107 | d3.time.monthEnd = function(date) { 108 | return new Date(date.getFullYear(), date.getMonth(), 0); 109 | }; 110 | 111 | d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) { 112 | date.setUTCDate(date.getUTCDate() + 1); 113 | date.setDate(daysInMonth(date.getMonth() + 1, date.getFullYear())); 114 | }, function(date) { 115 | return date.getMonth(); 116 | } 117 | ); 118 | 119 | -------------------------------------------------------------------------------- /public/js/nvd3/src/intro.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | -------------------------------------------------------------------------------- /public/js/nvd3/src/models/distribution.js: -------------------------------------------------------------------------------- 1 | 2 | nv.models.distribution = function() { 3 | 4 | //============================================================ 5 | // Public Variables with Default Settings 6 | //------------------------------------------------------------ 7 | 8 | var margin = {top: 0, right: 0, bottom: 0, left: 0} 9 | , width = 400 //technically width or height depending on x or y.... 10 | , size = 8 11 | , axis = 'x' // 'x' or 'y'... horizontal or vertical 12 | , getData = function(d) { return d[axis] } // defaults d.x or d.y 13 | , color = nv.utils.defaultColor() 14 | , scale = d3.scale.linear() 15 | , domain 16 | ; 17 | 18 | //============================================================ 19 | 20 | 21 | //============================================================ 22 | // Private Variables 23 | //------------------------------------------------------------ 24 | 25 | var scale0; 26 | 27 | //============================================================ 28 | 29 | 30 | function chart(selection) { 31 | selection.each(function(data) { 32 | var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom), 33 | naxis = axis == 'x' ? 'y' : 'x', 34 | container = d3.select(this); 35 | 36 | 37 | //------------------------------------------------------------ 38 | // Setup Scales 39 | 40 | scale0 = scale0 || scale; 41 | 42 | //------------------------------------------------------------ 43 | 44 | 45 | //------------------------------------------------------------ 46 | // Setup containers and skeleton of chart 47 | 48 | var wrap = container.selectAll('g.nv-distribution').data([data]); 49 | var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution'); 50 | var gEnter = wrapEnter.append('g'); 51 | var g = wrap.select('g'); 52 | 53 | wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 54 | 55 | //------------------------------------------------------------ 56 | 57 | 58 | var distWrap = g.selectAll('g.nv-dist') 59 | .data(function(d) { return d }, function(d) { return d.key }); 60 | 61 | distWrap.enter().append('g'); 62 | distWrap 63 | .attr('class', function(d,i) { return 'nv-dist nv-series-' + i }) 64 | .style('stroke', function(d,i) { return color(d, i) }); 65 | 66 | var dist = distWrap.selectAll('line.nv-dist' + axis) 67 | .data(function(d) { return d.values }) 68 | dist.enter().append('line') 69 | .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) }) 70 | .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) }) 71 | d3.transition(distWrap.exit().selectAll('line.nv-dist' + axis)) 72 | .attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) 73 | .attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) 74 | .style('stroke-opacity', 0) 75 | .remove(); 76 | dist 77 | .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i }) 78 | .attr(naxis + '1', 0) 79 | .attr(naxis + '2', size); 80 | d3.transition(dist) 81 | .attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) 82 | .attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) 83 | 84 | 85 | scale0 = scale.copy(); 86 | 87 | }); 88 | 89 | return chart; 90 | } 91 | 92 | 93 | //============================================================ 94 | // Expose Public Variables 95 | //------------------------------------------------------------ 96 | 97 | chart.margin = function(_) { 98 | if (!arguments.length) return margin; 99 | margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 100 | margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 101 | margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 102 | margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 103 | return chart; 104 | }; 105 | 106 | chart.width = function(_) { 107 | if (!arguments.length) return width; 108 | width = _; 109 | return chart; 110 | }; 111 | 112 | chart.axis = function(_) { 113 | if (!arguments.length) return axis; 114 | axis = _; 115 | return chart; 116 | }; 117 | 118 | chart.size = function(_) { 119 | if (!arguments.length) return size; 120 | size = _; 121 | return chart; 122 | }; 123 | 124 | chart.getData = function(_) { 125 | if (!arguments.length) return getData; 126 | getData = d3.functor(_); 127 | return chart; 128 | }; 129 | 130 | chart.scale = function(_) { 131 | if (!arguments.length) return scale; 132 | scale = _; 133 | return chart; 134 | }; 135 | 136 | chart.color = function(_) { 137 | if (!arguments.length) return color; 138 | color = nv.utils.getColor(_); 139 | return chart; 140 | }; 141 | 142 | //============================================================ 143 | 144 | 145 | return chart; 146 | } 147 | -------------------------------------------------------------------------------- /public/js/nvd3/src/models/legend.js: -------------------------------------------------------------------------------- 1 | nv.models.legend = function() { 2 | 3 | //============================================================ 4 | // Public Variables with Default Settings 5 | //------------------------------------------------------------ 6 | 7 | var margin = {top: 5, right: 0, bottom: 5, left: 0} 8 | , width = 400 9 | , height = 20 10 | , getKey = function(d) { return d.key } 11 | , color = nv.utils.defaultColor() 12 | , align = true 13 | , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout') 14 | ; 15 | 16 | //============================================================ 17 | 18 | 19 | function chart(selection) { 20 | selection.each(function(data) { 21 | var availableWidth = width - margin.left - margin.right, 22 | container = d3.select(this); 23 | 24 | 25 | //------------------------------------------------------------ 26 | // Setup containers and skeleton of chart 27 | 28 | var wrap = container.selectAll('g.nv-legend').data([data]); 29 | var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g'); 30 | var g = wrap.select('g'); 31 | 32 | wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 33 | 34 | //------------------------------------------------------------ 35 | 36 | 37 | var series = g.selectAll('.nv-series') 38 | .data(function(d) { return d }); 39 | var seriesEnter = series.enter().append('g').attr('class', 'nv-series') 40 | .on('mouseover', function(d,i) { 41 | dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects 42 | }) 43 | .on('mouseout', function(d,i) { 44 | dispatch.legendMouseout(d,i); 45 | }) 46 | .on('click', function(d,i) { 47 | dispatch.legendClick(d,i); 48 | }) 49 | .on('dblclick', function(d,i) { 50 | dispatch.legendDblclick(d,i); 51 | }); 52 | seriesEnter.append('circle') 53 | .style('stroke-width', 2) 54 | .attr('r', 5); 55 | seriesEnter.append('text') 56 | .attr('text-anchor', 'start') 57 | .attr('dy', '.32em') 58 | .attr('dx', '8'); 59 | series.classed('disabled', function(d) { return d.disabled }); 60 | series.exit().remove(); 61 | series.select('circle') 62 | .style('fill', function(d,i) { return d.color || color(d,i)}) 63 | .style('stroke', function(d,i) { return d.color || color(d, i) }); 64 | series.select('text').text(getKey); 65 | 66 | 67 | //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option) 68 | 69 | // NEW ALIGNING CODE, TODO: clean up 70 | if (align) { 71 | var seriesWidths = []; 72 | series.each(function(d,i) { 73 | seriesWidths.push(d3.select(this).select('text').node().getComputedTextLength() + 28); // 28 is ~ the width of the circle plus some padding 74 | }); 75 | 76 | //nv.log('Series Widths: ', JSON.stringify(seriesWidths)); 77 | 78 | var seriesPerRow = 0; 79 | var legendWidth = 0; 80 | var columnWidths = []; 81 | 82 | while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) { 83 | columnWidths[seriesPerRow] = seriesWidths[seriesPerRow]; 84 | legendWidth += seriesWidths[seriesPerRow++]; 85 | } 86 | 87 | 88 | while ( legendWidth > availableWidth && seriesPerRow > 1 ) { 89 | columnWidths = []; 90 | seriesPerRow--; 91 | 92 | for (k = 0; k < seriesWidths.length; k++) { 93 | if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) ) 94 | columnWidths[k % seriesPerRow] = seriesWidths[k]; 95 | } 96 | 97 | legendWidth = columnWidths.reduce(function(prev, cur, index, array) { 98 | return prev + cur; 99 | }); 100 | } 101 | //console.log(columnWidths, legendWidth, seriesPerRow); 102 | 103 | var xPositions = []; 104 | for (var i = 0, curX = 0; i < seriesPerRow; i++) { 105 | xPositions[i] = curX; 106 | curX += columnWidths[i]; 107 | } 108 | 109 | series 110 | .attr('transform', function(d, i) { 111 | return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * 20) + ')'; 112 | }); 113 | 114 | //position legend as far right as possible within the total width 115 | g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')'); 116 | 117 | height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * 20); 118 | 119 | } else { 120 | 121 | var ypos = 5, 122 | newxpos = 5, 123 | maxwidth = 0, 124 | xpos; 125 | series 126 | .attr('transform', function(d, i) { 127 | var length = d3.select(this).select('text').node().getComputedTextLength() + 28; 128 | xpos = newxpos; 129 | 130 | if (width < margin.left + margin.right + xpos + length) { 131 | newxpos = xpos = 5; 132 | ypos += 20; 133 | } 134 | 135 | newxpos += length; 136 | if (newxpos > maxwidth) maxwidth = newxpos; 137 | 138 | return 'translate(' + xpos + ',' + ypos + ')'; 139 | }); 140 | 141 | //position legend as far right as possible within the total width 142 | g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')'); 143 | 144 | height = margin.top + margin.bottom + ypos + 15; 145 | 146 | } 147 | 148 | }); 149 | 150 | return chart; 151 | } 152 | 153 | 154 | //============================================================ 155 | // Expose Public Variables 156 | //------------------------------------------------------------ 157 | 158 | chart.dispatch = dispatch; 159 | 160 | chart.margin = function(_) { 161 | if (!arguments.length) return margin; 162 | margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 163 | margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 164 | margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 165 | margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 166 | return chart; 167 | }; 168 | 169 | chart.width = function(_) { 170 | if (!arguments.length) return width; 171 | width = _; 172 | return chart; 173 | }; 174 | 175 | chart.height = function(_) { 176 | if (!arguments.length) return height; 177 | height = _; 178 | return chart; 179 | }; 180 | 181 | chart.key = function(_) { 182 | if (!arguments.length) return getKey; 183 | getKey = _; 184 | return chart; 185 | }; 186 | 187 | chart.color = function(_) { 188 | if (!arguments.length) return color; 189 | color = nv.utils.getColor(_); 190 | return chart; 191 | }; 192 | 193 | chart.align = function(_) { 194 | if (!arguments.length) return align; 195 | align = _; 196 | return chart; 197 | }; 198 | 199 | //============================================================ 200 | 201 | 202 | return chart; 203 | } 204 | -------------------------------------------------------------------------------- /public/js/nvd3/src/models/lineWithFisheye.js: -------------------------------------------------------------------------------- 1 | 2 | nv.models.line = function() { 3 | //Default Settings 4 | var margin = {top: 0, right: 0, bottom: 0, left: 0}, 5 | width = 960, 6 | height = 500, 7 | color = nv.utils.defaultColor(), // function that returns colors 8 | id = Math.floor(Math.random() * 10000), //Create semi-unique ID incase user doesn't select one 9 | getX = function(d) { return d.x }, // accessor to get the x value from a data point 10 | getY = function(d) { return d.y }, // accessor to get the y value from a data point 11 | clipEdge = false, // if true, masks lines within x and y scale 12 | interpolate = "linear"; // controls the line interpolation 13 | 14 | 15 | var scatter = nv.models.scatter() 16 | .id(id) 17 | .size(16) // default size 18 | .sizeDomain([16,256]), //set to speed up calculation, needs to be unset if there is a cstom size accessor 19 | //x = scatter.xScale(), 20 | //y = scatter.yScale(), 21 | x, y, 22 | x0, y0, timeoutID; 23 | 24 | 25 | function chart(selection) { 26 | selection.each(function(data) { 27 | var availableWidth = width - margin.left - margin.right, 28 | availableHeight = height - margin.top - margin.bottom; 29 | 30 | //get the scales inscase scatter scale was set manually 31 | x = x || scatter.xScale(); 32 | y = y || scatter.yScale(); 33 | 34 | //store old scales if they exist 35 | x0 = x0 || x; 36 | y0 = y0 || y; 37 | 38 | 39 | var wrap = d3.select(this).selectAll('g.nv-wrap.nv-line').data([data]); 40 | var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line'); 41 | var defsEnter = wrapEnter.append('defs'); 42 | var gEnter = wrapEnter.append('g'); 43 | var g = wrap.select('g') 44 | 45 | wrapEnter.append('g').attr('class', 'nv-scatterWrap'); 46 | var scatterWrap = wrap.select('.nv-scatterWrap').datum(data); 47 | 48 | gEnter.append('g').attr('class', 'nv-groups'); 49 | 50 | 51 | scatter 52 | .width(availableWidth) 53 | .height(availableHeight) 54 | 55 | d3.transition(scatterWrap).call(scatter); 56 | 57 | 58 | wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 59 | 60 | 61 | defsEnter.append('clipPath') 62 | .attr('id', 'nv-edge-clip-' + id) 63 | .append('rect'); 64 | 65 | wrap.select('#nv-edge-clip-' + id + ' rect') 66 | .attr('width', availableWidth) 67 | .attr('height', availableHeight); 68 | 69 | g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); 70 | scatterWrap 71 | .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); 72 | 73 | 74 | 75 | 76 | var groups = wrap.select('.nv-groups').selectAll('.nv-group') 77 | .data(function(d) { return d }, function(d) { return d.key }); 78 | groups.enter().append('g') 79 | .style('stroke-opacity', 1e-6) 80 | .style('fill-opacity', 1e-6); 81 | d3.transition(groups.exit()) 82 | .style('stroke-opacity', 1e-6) 83 | .style('fill-opacity', 1e-6) 84 | .remove(); 85 | groups 86 | .attr('class', function(d,i) { return 'nv-group nv-series-' + i }) 87 | .classed('hover', function(d) { return d.hover }) 88 | .style('fill', function(d,i){ return color(d, i) }) 89 | .style('stroke', function(d,i){ return color(d, i) }) 90 | d3.transition(groups) 91 | .style('stroke-opacity', 1) 92 | .style('fill-opacity', .5) 93 | 94 | 95 | var paths = groups.selectAll('path') 96 | .data(function(d, i) { return [d.values] }); 97 | paths.enter().append('path') 98 | .attr('class', 'nv-line') 99 | .attr('d', d3.svg.line() 100 | .interpolate(interpolate) 101 | .x(function(d,i) { return x0(getX(d,i)) }) 102 | .y(function(d,i) { return y0(getY(d,i)) }) 103 | ); 104 | d3.transition(groups.exit().selectAll('path')) 105 | .attr('d', d3.svg.line() 106 | .interpolate(interpolate) 107 | .x(function(d,i) { return x(getX(d,i)) }) 108 | .y(function(d,i) { return y(getY(d,i)) }) 109 | ) 110 | .remove(); // redundant? line is already being removed 111 | d3.transition(paths) 112 | .attr('d', d3.svg.line() 113 | .interpolate(interpolate) 114 | .x(function(d,i) { return x(getX(d,i)) }) 115 | .y(function(d,i) { return y(getY(d,i)) }) 116 | ); 117 | 118 | 119 | //store old scales for use in transitions on update, to animate from old to new positions 120 | x0 = x.copy(); 121 | y0 = y.copy(); 122 | 123 | }); 124 | 125 | return chart; 126 | } 127 | 128 | 129 | chart.dispatch = scatter.dispatch; 130 | 131 | d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius'); 132 | 133 | chart.margin = function(_) { 134 | if (!arguments.length) return margin; 135 | margin = _; 136 | return chart; 137 | }; 138 | 139 | chart.width = function(_) { 140 | if (!arguments.length) return width; 141 | width = _; 142 | return chart; 143 | }; 144 | 145 | chart.height = function(_) { 146 | if (!arguments.length) return height; 147 | height = _; 148 | return chart; 149 | }; 150 | 151 | chart.x = function(_) { 152 | if (!arguments.length) return getX; 153 | getX = _; 154 | scatter.x(_); 155 | return chart; 156 | }; 157 | 158 | chart.y = function(_) { 159 | if (!arguments.length) return getY; 160 | getY = _; 161 | scatter.y(_); 162 | return chart; 163 | }; 164 | 165 | chart.clipEdge = function(_) { 166 | if (!arguments.length) return clipEdge; 167 | clipEdge = _; 168 | return chart; 169 | }; 170 | 171 | chart.color = function(_) { 172 | if (!arguments.length) return color; 173 | color = nv.utils.getColor(_); 174 | scatter.color(color); 175 | return chart; 176 | }; 177 | 178 | chart.id = function(_) { 179 | if (!arguments.length) return id; 180 | id = _; 181 | return chart; 182 | }; 183 | 184 | chart.interpolate = function(_) { 185 | if (!arguments.length) return interpolate; 186 | interpolate = _; 187 | return chart; 188 | }; 189 | 190 | chart.defined = function(_) { 191 | if (!arguments.length) return defined; 192 | defined = _; 193 | return chart; 194 | }; 195 | 196 | return chart; 197 | } 198 | -------------------------------------------------------------------------------- /public/js/nvd3/src/models/sparkline.js: -------------------------------------------------------------------------------- 1 | 2 | nv.models.sparkline = function() { 3 | 4 | //============================================================ 5 | // Public Variables with Default Settings 6 | //------------------------------------------------------------ 7 | 8 | var margin = {top: 2, right: 0, bottom: 2, left: 0} 9 | , width = 400 10 | , height = 32 11 | , animate = true 12 | , x = d3.scale.linear() 13 | , y = d3.scale.linear() 14 | , getX = function(d) { return d.x } 15 | , getY = function(d) { return d.y } 16 | , color = nv.utils.getColor(['#000']) 17 | , xDomain 18 | , yDomain 19 | ; 20 | 21 | //============================================================ 22 | 23 | 24 | function chart(selection) { 25 | selection.each(function(data) { 26 | var availableWidth = width - margin.left - margin.right, 27 | availableHeight = height - margin.top - margin.bottom, 28 | container = d3.select(this); 29 | 30 | 31 | //------------------------------------------------------------ 32 | // Setup Scales 33 | 34 | x .domain(xDomain || d3.extent(data, getX )) 35 | .range([0, availableWidth]); 36 | 37 | y .domain(yDomain || d3.extent(data, getY )) 38 | .range([availableHeight, 0]); 39 | 40 | //------------------------------------------------------------ 41 | 42 | 43 | //------------------------------------------------------------ 44 | // Setup containers and skeleton of chart 45 | 46 | var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]); 47 | var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline'); 48 | var gEnter = wrapEnter.append('g'); 49 | var g = wrap.select('g'); 50 | 51 | wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 52 | 53 | //------------------------------------------------------------ 54 | 55 | 56 | var paths = wrap.selectAll('path') 57 | .data(function(d) { return [d] }); 58 | paths.enter().append('path'); 59 | paths.exit().remove(); 60 | paths 61 | .style('stroke', function(d,i) { return d.color || color(d, i) }) 62 | .attr('d', d3.svg.line() 63 | .x(function(d,i) { return x(getX(d,i)) }) 64 | .y(function(d,i) { return y(getY(d,i)) }) 65 | ); 66 | 67 | 68 | // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent) 69 | var points = wrap.selectAll('circle.nv-point') 70 | .data(function(data) { 71 | var yValues = data.map(function(d, i) { return getY(d,i); }); 72 | function pointIndex(index) { 73 | if (index != -1) { 74 | var result = data[index]; 75 | result.pointIndex = index; 76 | return result; 77 | } else { 78 | return null; 79 | } 80 | } 81 | var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])), 82 | minPoint = pointIndex(yValues.indexOf(y.domain()[0])), 83 | currentPoint = pointIndex(yValues.length - 1); 84 | return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;}); 85 | }); 86 | points.enter().append('circle'); 87 | points.exit().remove(); 88 | points 89 | .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) }) 90 | .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) }) 91 | .attr('r', 2) 92 | .attr('class', function(d,i) { 93 | return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' : 94 | getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue' 95 | }); 96 | }); 97 | 98 | return chart; 99 | } 100 | 101 | 102 | //============================================================ 103 | // Expose Public Variables 104 | //------------------------------------------------------------ 105 | 106 | chart.margin = function(_) { 107 | if (!arguments.length) return margin; 108 | margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 109 | margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 110 | margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 111 | margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 112 | return chart; 113 | }; 114 | 115 | chart.width = function(_) { 116 | if (!arguments.length) return width; 117 | width = _; 118 | return chart; 119 | }; 120 | 121 | chart.height = function(_) { 122 | if (!arguments.length) return height; 123 | height = _; 124 | return chart; 125 | }; 126 | 127 | chart.x = function(_) { 128 | if (!arguments.length) return getX; 129 | getX = d3.functor(_); 130 | return chart; 131 | }; 132 | 133 | chart.y = function(_) { 134 | if (!arguments.length) return getY; 135 | getY = d3.functor(_); 136 | return chart; 137 | }; 138 | 139 | chart.xScale = function(_) { 140 | if (!arguments.length) return x; 141 | x = _; 142 | return chart; 143 | }; 144 | 145 | chart.yScale = function(_) { 146 | if (!arguments.length) return y; 147 | y = _; 148 | return chart; 149 | }; 150 | 151 | chart.xDomain = function(_) { 152 | if (!arguments.length) return xDomain; 153 | xDomain = _; 154 | return chart; 155 | }; 156 | 157 | chart.yDomain = function(_) { 158 | if (!arguments.length) return yDomain; 159 | yDomain = _; 160 | return chart; 161 | }; 162 | 163 | chart.animate = function(_) { 164 | if (!arguments.length) return animate; 165 | animate = _; 166 | return chart; 167 | }; 168 | 169 | chart.color = function(_) { 170 | if (!arguments.length) return color; 171 | color = nv.utils.getColor(_); 172 | return chart; 173 | }; 174 | 175 | //============================================================ 176 | 177 | 178 | return chart; 179 | } 180 | -------------------------------------------------------------------------------- /public/js/nvd3/src/outro.js: -------------------------------------------------------------------------------- 1 | })(); -------------------------------------------------------------------------------- /public/js/nvd3/src/tooltip.js: -------------------------------------------------------------------------------- 1 | 2 | /***** 3 | * A no-frills tooltip implementation. 4 | *****/ 5 | 6 | 7 | (function() { 8 | 9 | var nvtooltip = window.nv.tooltip = {}; 10 | 11 | nvtooltip.show = function(pos, content, gravity, dist, parentContainer, classes) { 12 | 13 | var container = document.createElement('div'); 14 | container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip'); 15 | 16 | gravity = gravity || 's'; 17 | dist = dist || 20; 18 | 19 | var body = parentContainer ? parentContainer : document.getElementsByTagName('body')[0]; 20 | 21 | container.innerHTML = content; 22 | container.style.left = 0; 23 | container.style.top = 0; 24 | container.style.opacity = 0; 25 | 26 | body.appendChild(container); 27 | 28 | var height = parseInt(container.offsetHeight), 29 | width = parseInt(container.offsetWidth), 30 | windowWidth = nv.utils.windowSize().width, 31 | windowHeight = nv.utils.windowSize().height, 32 | scrollTop = window.scrollY, 33 | scrollLeft = window.scrollX, 34 | left, top; 35 | 36 | windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16; 37 | windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16; 38 | 39 | var tooltipTop = function ( Elem ) { 40 | var offsetTop = top; 41 | do { 42 | if( !isNaN( Elem.offsetTop ) ) { 43 | offsetTop += (Elem.offsetTop); 44 | } 45 | } while( Elem = Elem.offsetParent ); 46 | return offsetTop; 47 | } 48 | 49 | var tooltipLeft = function ( Elem ) { 50 | var offsetLeft = left; 51 | do { 52 | if( !isNaN( Elem.offsetLeft ) ) { 53 | offsetLeft += (Elem.offsetLeft); 54 | } 55 | } while( Elem = Elem.offsetParent ); 56 | return offsetLeft; 57 | } 58 | 59 | switch (gravity) { 60 | case 'e': 61 | left = pos[0] - width - dist; 62 | top = pos[1] - (height / 2); 63 | var tLeft = tooltipLeft(container); 64 | var tTop = tooltipTop(container); 65 | if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left; 66 | if (tTop < scrollTop) top = scrollTop - tTop + top; 67 | if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; 68 | break; 69 | case 'w': 70 | left = pos[0] + dist; 71 | top = pos[1] - (height / 2); 72 | if (tLeft + width > windowWidth) left = pos[0] - width - dist; 73 | if (tTop < scrollTop) top = scrollTop + 5; 74 | if (tTop + height > scrollTop + windowHeight) top = scrollTop - height - 5; 75 | break; 76 | case 'n': 77 | left = pos[0] - (width / 2) - 5; 78 | top = pos[1] + dist; 79 | var tLeft = tooltipLeft(container); 80 | var tTop = tooltipTop(container); 81 | if (tLeft < scrollLeft) left = scrollLeft + 5; 82 | if (tLeft + width > windowWidth) left = left - width/2 + 5; 83 | if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; 84 | break; 85 | case 's': 86 | left = pos[0] - (width / 2); 87 | top = pos[1] - height - dist; 88 | var tLeft = tooltipLeft(container); 89 | var tTop = tooltipTop(container); 90 | if (tLeft < scrollLeft) left = scrollLeft + 5; 91 | if (tLeft + width > windowWidth) left = left - width/2 + 5; 92 | if (scrollTop > tTop) top = scrollTop; 93 | break; 94 | } 95 | 96 | 97 | container.style.left = left+'px'; 98 | container.style.top = top+'px'; 99 | container.style.opacity = 1; 100 | container.style.position = 'absolute'; //fix scroll bar issue 101 | container.style.pointerEvents = 'none'; //fix scroll bar issue 102 | 103 | return container; 104 | }; 105 | 106 | nvtooltip.cleanup = function() { 107 | 108 | // Find the tooltips, mark them for removal by this class (so others cleanups won't find it) 109 | var tooltips = document.getElementsByClassName('nvtooltip'); 110 | var purging = []; 111 | while(tooltips.length) { 112 | purging.push(tooltips[0]); 113 | tooltips[0].style.transitionDelay = '0 !important'; 114 | tooltips[0].style.opacity = 0; 115 | tooltips[0].className = 'nvtooltip-pending-removal'; 116 | } 117 | 118 | 119 | setTimeout(function() { 120 | 121 | while (purging.length) { 122 | var removeMe = purging.pop(); 123 | removeMe.parentNode.removeChild(removeMe); 124 | } 125 | }, 500); 126 | }; 127 | 128 | 129 | })(); 130 | -------------------------------------------------------------------------------- /public/js/nvd3/src/utils.js: -------------------------------------------------------------------------------- 1 | 2 | nv.utils.windowSize = function() { 3 | // Sane defaults 4 | var size = {width: 640, height: 480}; 5 | 6 | // Earlier IE uses Doc.body 7 | if (document.body && document.body.offsetWidth) { 8 | size.width = document.body.offsetWidth; 9 | size.height = document.body.offsetHeight; 10 | } 11 | 12 | // IE can use depending on mode it is in 13 | if (document.compatMode=='CSS1Compat' && 14 | document.documentElement && 15 | document.documentElement.offsetWidth ) { 16 | size.width = document.documentElement.offsetWidth; 17 | size.height = document.documentElement.offsetHeight; 18 | } 19 | 20 | // Most recent browsers use 21 | if (window.innerWidth && window.innerHeight) { 22 | size.width = window.innerWidth; 23 | size.height = window.innerHeight; 24 | } 25 | return (size); 26 | }; 27 | 28 | 29 | 30 | // Easy way to bind multiple functions to window.onresize 31 | // TODO: give a way to remove a function after its bound, other than removing alkl of them 32 | nv.utils.windowResize = function(fun){ 33 | var oldresize = window.onresize; 34 | 35 | window.onresize = function(e) { 36 | if (typeof oldresize == 'function') oldresize(e); 37 | fun(e); 38 | } 39 | } 40 | 41 | // Backwards compatible way to implement more d3-like coloring of graphs. 42 | // If passed an array, wrap it in a function which implements the old default 43 | // behavior 44 | nv.utils.getColor = function(color) { 45 | if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back 46 | 47 | if( Object.prototype.toString.call( color ) === '[object Array]' ) 48 | return function(d, i) { return d.color || color[i % color.length]; }; 49 | else 50 | return color; 51 | //can't really help it if someone passes rubbish as color 52 | } 53 | 54 | // Default color chooser uses the index of an object as before. 55 | nv.utils.defaultColor = function() { 56 | var colors = d3.scale.category20().range(); 57 | return function(d, i) { return d.color || colors[i % colors.length] }; 58 | } 59 | 60 | 61 | // Returns a color function that takes the result of 'getKey' for each series and 62 | // looks for a corresponding color from the dictionary, 63 | nv.utils.customTheme = function(dictionary, getKey, defaultColors) { 64 | getKey = getKey || function(series) { return series.key }; // use default series.key if getKey is undefined 65 | defaultColors = defaultColors || d3.scale.category20().range(); //default color function 66 | 67 | var defIndex = defaultColors.length; //current default color (going in reverse) 68 | 69 | return function(series, index) { 70 | var key = getKey(series); 71 | 72 | if (!defIndex) defIndex = defaultColors.length; //used all the default colors, start over 73 | 74 | if (typeof dictionary[key] !== "undefined") 75 | return (typeof dictionary[key] === "function") ? dictionary[key]() : dictionary[key]; 76 | else 77 | return defaultColors[--defIndex]; // no match in dictionary, use default color 78 | } 79 | } 80 | 81 | 82 | 83 | // From the PJAX example on d3js.org, while this is not really directly needed 84 | // it's a very cool method for doing pjax, I may expand upon it a little bit, 85 | // open to suggestions on anything that may be useful 86 | nv.utils.pjax = function(links, content) { 87 | d3.selectAll(links).on("click", function() { 88 | history.pushState(this.href, this.textContent, this.href); 89 | load(this.href); 90 | d3.event.preventDefault(); 91 | }); 92 | 93 | function load(href) { 94 | d3.html(href, function(fragment) { 95 | var target = d3.select(content).node(); 96 | target.parentNode.replaceChild(d3.select(fragment).select(content).node(), target); 97 | nv.utils.pjax(links, content); 98 | }); 99 | } 100 | 101 | d3.select(window).on("popstate", function() { 102 | if (d3.event.state) load(d3.event.state); 103 | }); 104 | } 105 | 106 | -------------------------------------------------------------------------------- /public/js/redmin/actions.js: -------------------------------------------------------------------------------- 1 | var deleteRow = function(e) { 2 | e.preventDefault(); 3 | 4 | var tr = $(e.target).parents('tr'); 5 | var type = $(e.target).attr('keytype'); 6 | var keyinfo = $(e.target).attr('keyinfo'); 7 | var id = $(e.target).attr('id'); 8 | 9 | if (typeof(keyinfo) == 'undefined') { 10 | var url = baseurl+'/'+type+'/delete/'+currentServerDb+'/'+encodeURIComponent(id); 11 | } else { 12 | var url = baseurl+'/'+type+'/delete/'+currentServerDb+'/'+encodeURIComponent(keyinfo)+'/'+encodeURIComponent(id); 13 | } 14 | 15 | modalPopup.confirm( 16 | function() { 17 | $.ajax({ 18 | url: url, 19 | dataType: 'json', 20 | success: function(data) { 21 | modalPopup.hide('confirm'); 22 | 23 | if (data == 1) { 24 | tr.remove(); 25 | } 26 | } 27 | }); 28 | } 29 | ); 30 | } 31 | 32 | $(document).ready(function() { 33 | $('.del').click(function(e) { 34 | deleteRow(e); 35 | }); 36 | 37 | $('.keys-table td').click(function(e) { 38 | if ($(e.target).hasClass('icon') || $(e.target).hasClass('key-checkbox')) 39 | return; 40 | 41 | var input = $($(e.target).parents('tr')[0]).find("input[name=keys\\[\\]]"); 42 | 43 | if (!input.hasClass('all-key-checkbox')) 44 | input.attr('checked', !input.is(':checked')); 45 | }); 46 | 47 | $('#checkall').click(function(e) { 48 | $("input[name=keys\\[\\]]").attr('checked', $(e.target).is(':checked')); 49 | }); 50 | 51 | $('.moveall').click(function(e) { 52 | e.preventDefault(); 53 | var checkboxes = $("input[name=keys\\[\\]]:checked"); 54 | 55 | if (checkboxes.length > 0) { 56 | modalPopup.moveKeys( 57 | function() { 58 | var destination = $('.modal-body input').val().trim(); 59 | var type = $(e.target).attr('keytype'); 60 | var keyinfo = $(e.target).attr('keyinfo'); 61 | 62 | if (destination != '') { 63 | var values = []; 64 | checkboxes.each(function() { 65 | values.push($(this).val()); 66 | }); 67 | 68 | if (typeof(keyinfo) == 'undefined') { 69 | var postdata = {'values[]': values, 'destination': destination}; 70 | } else { 71 | var postdata = {'values[]': values, 'destination': destination, 'keyinfo': keyinfo}; 72 | } 73 | 74 | $.post( 75 | baseurl+'/'+type+'/moveall/'+currentServerDb, 76 | postdata, 77 | function(data) { 78 | modalPopup.hide('moveKeys'); 79 | 80 | checkboxes.each(function() { 81 | if (data[$(this).val()]) { 82 | $(this).parents('tr').remove(); 83 | } 84 | }); 85 | } 86 | ); 87 | }; 88 | }, 89 | $(e.target).attr('modaltitle'), 90 | $(e.target).attr('modaltip') 91 | ); 92 | } else { 93 | modalPopup.alert('invalid', 'Please select key(s) to move'); 94 | } 95 | }); 96 | 97 | 98 | $('.delall').click(function(e) { 99 | e.preventDefault(); 100 | var checkboxes = $("input[name=keys\\[\\]]:checked"); 101 | var type = $(e.target).attr('keytype'); 102 | var keyinfo = $(e.target).attr('keyinfo'); 103 | 104 | if (checkboxes.length > 0) { 105 | modalPopup.confirm( 106 | function() { 107 | var values = []; 108 | checkboxes.each(function() { 109 | values.push($(this).val()); 110 | }); 111 | 112 | if (typeof(keyinfo) == 'undefined') { 113 | var postdata = {'values[]': values}; 114 | } else { 115 | var postdata = {'values[]': values, 'keyinfo': keyinfo}; 116 | } 117 | 118 | $.post( 119 | baseurl+'/'+type+'/delall/'+currentServerDb, 120 | postdata, 121 | function(data) { 122 | modalPopup.hide('confirm'); 123 | 124 | checkboxes.each(function() { 125 | if (data[$(this).val()] == 1) { 126 | $(this).parents('tr').remove(); 127 | } 128 | }); 129 | } 130 | ); 131 | } 132 | ); 133 | } else { 134 | modalPopup.alert('invalid', 'Please select key(s) to delete'); 135 | } 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /public/js/redmin/hashes.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add_hash, #add_edit_hash').click(function(e) { 3 | e.preventDefault(); 4 | 5 | var form = $(e.target).parents('form'); 6 | var key = form.find('input[name="key"]').val().trim(); 7 | var hashkey = form.find('input[name="hashkey"]').val().trim(); 8 | var str = form.find('textarea[name="value"]').val().trim(); 9 | 10 | if (key != '' && str != '' && hashkey != '') { 11 | $.ajax({ 12 | url: baseurl+'/hashes/add/' + currentServerDb, 13 | dataType: 'json', 14 | type: 'POST', 15 | data: 'key='+key+'&value='+str+'&hashkey='+hashkey, 16 | success: function(data) { 17 | if (data) { 18 | var oldkey = form.find('input[name="oldkey"]'); 19 | form.find('textarea').val(''); 20 | 21 | if (oldkey.length > 0) { 22 | location.reload(); 23 | } else { 24 | if (e.target.id == 'add_edit_hash') { 25 | location.href = baseurl + '/keys/view/' + currentServerDb + '/' + encodeURIComponent(key); 26 | } else { 27 | form.find('input').val(''); 28 | modalPopup.alert('saved'); 29 | } 30 | } 31 | } else { 32 | modalPopup.alert('error'); 33 | } 34 | } 35 | }); 36 | } else { 37 | modalPopup.alert('invalid') 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /public/js/redmin/lists.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add_list, #add_edit_list').click(function(e) { 3 | e.preventDefault(); 4 | 5 | var form = $(e.target).parents('form'); 6 | var key = form.find('input[name="key"]').val().trim(); 7 | var str = form.find('textarea[name="value"]').val().trim(); 8 | var type = form.find('select[name="position"]').val(); 9 | var pivot = form.find('input[name="pivot"]'); 10 | 11 | if (key != '' && str != '' && (type == 'append' || type == 'prepend' || type == 'before' || type == 'after')) { 12 | if ((type == 'before' || type == 'after') && pivot.val().trim() == '') { 13 | modalPopup.alert('invalid') 14 | } else { 15 | if (pivot.length > 0) { 16 | pivot = pivot.val().trim(); 17 | } 18 | else { 19 | pivot = ''; 20 | } 21 | 22 | $.ajax({ 23 | url: baseurl+'/lists/add/' + currentServerDb, 24 | dataType: 'json', 25 | type: 'POST', 26 | data: 'key='+key+'&value='+str+'&type='+type+'&pivot='+pivot, 27 | success: function(data) { 28 | if (data) { 29 | var oldkey = form.find('input[name="oldkey"]'); 30 | form.find('textarea').val(''); 31 | 32 | if (oldkey.length > 0) { 33 | location.reload(); 34 | } else { 35 | if (e.target.id == 'add_edit_list') { 36 | location.href = baseurl + '/keys/view/' + currentServerDb + '/' + encodeURIComponent(key); 37 | } else { 38 | form.find('input').val(''); 39 | modalPopup.alert('saved'); 40 | } 41 | } 42 | } else { 43 | modalPopup.alert('error'); 44 | } 45 | } 46 | }); 47 | } 48 | } else { 49 | modalPopup.alert('invalid') 50 | } 51 | }); 52 | 53 | $('#list_position').change(function(e) { 54 | var val = $(e.target).val(); 55 | 56 | if (val == 'after' || val == 'before') { 57 | if ($('#list_type').find('input').length <= 0) { 58 | $('').appendTo($('#list_type')); 59 | } 60 | } else { 61 | $('#list_type').empty(); 62 | } 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /public/js/redmin/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.disabled').click(function(e) { 3 | e.preventDefault(); 4 | }); 5 | 6 | $('#reset_stats').click(function(e) { 7 | e.preventDefault(); 8 | modalPopup.confirm( 9 | function() { 10 | $.ajax({ 11 | url: baseurl + '/actions/reset/' + currentServerDb, 12 | dataType: 'json', 13 | success: function(data) { 14 | location.href = baseurl + '/welcome/index/' + currentServerDb; 15 | } 16 | }); 17 | } 18 | ); 19 | }); 20 | 21 | $('#flush_db').click(function(e) { 22 | e.preventDefault(); 23 | 24 | modalPopup.confirm( 25 | function() { 26 | $.ajax({ 27 | url: baseurl + '/actions/fdb/' + currentServerDb, 28 | dataType: 'json', 29 | success: function(data) { 30 | location.href = baseurl + '/welcome/index/' + currentServer + '/0'; 31 | } 32 | }); 33 | } 34 | ); 35 | }); 36 | 37 | $('#flush_all').click(function(e) { 38 | e.preventDefault(); 39 | modalPopup.confirm( 40 | function() { 41 | $.ajax({ 42 | url: baseurl + '/actions/fall/' + currentServerDb, 43 | dataType: 'json', 44 | success: function(data) { 45 | location.href = baseurl + '/welcome/index/' + currentServer + '/0'; 46 | } 47 | }); 48 | } 49 | ); 50 | }); 51 | 52 | function executeSave(async) { 53 | $.ajax({ 54 | url: baseurl + '/welcome/save/' + currentServerDb + '/' + async, 55 | dataType: 'json', 56 | success: function(data) { 57 | modalPopup.hide('confirm'); 58 | if (data.status) { 59 | modalPopup.alert('saved', 'All DBs are saved to file ' + data.filename); 60 | } 61 | else { 62 | modalPopup.alert('error', 'Problem while saving DBs to file ' + data.filename); 63 | } 64 | } 65 | }); 66 | } 67 | 68 | $('#save_sync').click(function(e) { 69 | e.preventDefault(); 70 | 71 | modalPopup.confirm( 72 | function() { 73 | executeSave(0); 74 | }, 75 | 'You almost never want to call synchronous save in production environments where it will block all the other clients. Instead usually asynchronous save is used' 76 | ); 77 | }); 78 | 79 | $('#save_async').click(function(e) { 80 | e.preventDefault(); 81 | 82 | executeSave(1); 83 | }); 84 | 85 | $('#add_db').click(function(e) { 86 | e.preventDefault(); 87 | 88 | modalPopup.addDb( 89 | function() { 90 | var dbIdx = parseInt($('#dbIdx').val()); 91 | location.href = baseurl + '/welcome/index/' + currentServer + '/' + dbIdx; 92 | } 93 | ); 94 | }); 95 | }); -------------------------------------------------------------------------------- /public/js/redmin/modal.js: -------------------------------------------------------------------------------- 1 | var modalPopup = { 2 | 3 | // link modals to the template ids 4 | _modals : { 5 | alert : 'generalmodal', 6 | confirm : 'confirmation', 7 | addDb : 'addDB', 8 | moveKeys : 'move_confirmation' 9 | }, 10 | 11 | _alertDefault : { 12 | saved : { 13 | title : 'Saved', 14 | message : 'New value inserted', 15 | btn_class : 'btn-success' 16 | }, 17 | error : { 18 | title : 'Error', 19 | message : 'There was a problem saving the value', 20 | btn_class : 'btn-danger' 21 | }, 22 | invalid : { 23 | title : 'Invalid', 24 | message : 'Please enter a valid input', 25 | btn_class : 'btn-danger' 26 | } 27 | }, 28 | 29 | _getModal : function(type) { 30 | return $('#' + this._modals[type]); 31 | }, 32 | 33 | hide : function(type) { 34 | if (typeof type === 'undefined') { 35 | type = 'alert'; 36 | } 37 | 38 | this._getModal(type).modal('hide'); 39 | }, 40 | 41 | alert : function(type, message, title) { 42 | if (typeof type === 'undefined') { 43 | type = 'saved'; 44 | } 45 | if (typeof title === 'undefined') { 46 | title = this._alertDefault[type]['title']; 47 | } 48 | if (typeof message === 'undefined') { 49 | message = this._alertDefault[type]['message']; 50 | } 51 | 52 | this._getModal('alert').find('h3').text(title); 53 | this._getModal('alert').find('p').text(message); 54 | this._getModal('alert').find('button').attr('class', 'btn ' + this._alertDefault[type]['btn_class']); 55 | 56 | this._getModal('alert').modal('show'); 57 | }, 58 | 59 | confirm : function(action, message, title) { 60 | if (typeof title === 'undefined') { 61 | title = 'Are you sure?'; 62 | } 63 | if (typeof message === 'undefined') { 64 | message = 'You can not undo this action'; 65 | } 66 | 67 | this._getModal('confirm').find('.modal-footer .save').unbind(); 68 | this._getModal('confirm').find('.modal-footer .save').click(action); 69 | this._getModal('confirm').find('h3').text(title); 70 | this._getModal('confirm').find('p').text(message); 71 | 72 | this._getModal('confirm').modal('show'); 73 | }, 74 | 75 | addDb : function(action) { 76 | this._getModal('addDb').find('.modal-footer .save').unbind(); 77 | this._getModal('addDb').find('.modal-footer .save').click(action); 78 | 79 | this._getModal('addDb').modal('show'); 80 | }, 81 | 82 | moveKeys : function(action, title, tip) { 83 | this._getModal('moveKeys').find('.modal-footer .movekey').unbind(); 84 | this._getModal('moveKeys').find('.modal-footer .movekey').click(action); 85 | this._getModal('moveKeys').find('h3').text(title); 86 | this._getModal('moveKeys').find('input').attr('placeholder', tip); 87 | 88 | this._getModal('moveKeys').modal('show'); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /public/js/redmin/remlists.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#list_remove_options').change(function(e) { 3 | var val = $(e.target).val(); 4 | 5 | if (val == 'count') { 6 | if ($('#list_remove_type').find('input').length <= 0) { 7 | $('').appendTo($('#list_remove_type')); 8 | } 9 | } else { 10 | $('#list_remove_type').empty(); 11 | } 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /public/js/redmin/sets.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add_set, #add_edit_set').click(function(e) { 3 | e.preventDefault(); 4 | 5 | var form = $(e.target).parents('form'); 6 | var key = form.find('input[name="key"]').val().trim(); 7 | var str = form.find('textarea[name="value"]').val().trim(); 8 | 9 | if (key != '' && str != '') { 10 | $.ajax({ 11 | url: baseurl+'/sets/add/' + currentServerDb, 12 | dataType: 'json', 13 | type: 'POST', 14 | data: 'key='+key+'&value='+str, 15 | success: function(data) { 16 | if (data) { 17 | var oldkey = form.find('input[name="oldkey"]'); 18 | form.find('textarea').val(''); 19 | 20 | if (oldkey.length > 0) { 21 | var tr = $('.settable tr:first'); 22 | $(''+str+''+ 23 | '').insertAfter(tr); 24 | $('.settable tr').eq(1).find('a.del').click(function(e) { 25 | deleteRow(e); 26 | }); 27 | location.reload(); 28 | } else { 29 | if (e.target.id == 'add_edit_set') { 30 | location.href = baseurl + '/keys/view/' + currentServerDb + '/' + encodeURIComponent(key); 31 | } else { 32 | form.find('input').val(''); 33 | modalPopup.alert('saved'); 34 | } 35 | } 36 | } 37 | else { 38 | modalPopup.alert('error'); 39 | } 40 | } 41 | }); 42 | } else { 43 | modalPopup.alert('invalid') 44 | } 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /public/js/redmin/strings.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add_string').click(function(e) { 3 | e.preventDefault(); 4 | 5 | var form = $(e.target).parents('form'); 6 | var key = form.find('input[name="key"]').val().trim(); 7 | var str = form.find('textarea[name="value"]').val().trim(); 8 | 9 | if (key != '' && str != '') { 10 | $.ajax({ 11 | url: baseurl+'/strings/add/' + currentServerDb, 12 | dataType: 'json', 13 | type: 'POST', 14 | data: 'key='+key+'&value='+str, 15 | success: function(data) { 16 | form.find('input').val(''); 17 | form.find('textarea').val(''); 18 | 19 | if (data) 20 | modalPopup.alert('saved'); 21 | else 22 | modalPopup.alert('error'); 23 | } 24 | }); 25 | } else { 26 | modalPopup.alert('invalid') 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /public/js/redmin/terminal.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | function getCookie(name) { 4 | var value = "; " + document.cookie; 5 | var parts = value.split("; " + name + "="); 6 | if (parts.length == 2) return parts.pop().split(";").shift(); 7 | } 8 | 9 | var prompt = "redis " + currentHost + ":" + currentPort + ">"; 10 | 11 | if (getCookie('term_theme') == 'dark') { $("#mainContainer").addClass('dark_theme'); } 12 | 13 | $("#term_light").on("click", function(e) { 14 | e.preventDefault(); 15 | $("#mainContainer").removeClass('dark_theme'); 16 | document.cookie = "term_theme=light; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/"; 17 | }) 18 | 19 | $("#term_dark").on("click", function(e) { 20 | e.preventDefault(); 21 | $("#mainContainer").addClass('dark_theme'); 22 | document.cookie = "term_theme=dark; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/"; 23 | }) 24 | 25 | $(".terminal-clear").tooltip({title: "Clear", placement: "right"}); 26 | 27 | $(".terminal-clear").on("click", function(e) { 28 | e.preventDefault(); 29 | $(".terminal-console").html(prompt); 30 | $('#terminal-input').focus(); 31 | }) 32 | $('#terminal-input').focus(); 33 | 34 | $('.terminal-prompt').ajaxStart(function() { 35 | $(this).html(""); 36 | $(this).addClass('ajax-loading'); 37 | }); 38 | 39 | $('.terminal-prompt').ajaxStop(function() { 40 | $(this).removeClass('ajax-loading'); 41 | $(this).html(">"); 42 | }); 43 | 44 | $('#terminal-input').on('keypress', function(e) { 45 | var key = e.keyCode; 46 | var command = $('#terminal-input').val().trim(); 47 | 48 | if (key == '38' || key == '40') { 49 | if (key == '40' && command == '') 50 | return false; 51 | else if (key == '38') 52 | var navigation = 'up'; 53 | else 54 | var navigation = 'down'; 55 | 56 | var url = baseurl+'/terminal/history/' + currentServerDb + '?navigation=' + navigation; 57 | 58 | if (key == '38' && command == '') 59 | url += '&start=true'; 60 | 61 | $.ajax({ 62 | url: url, 63 | dataType: 'json', 64 | type: 'GET', 65 | success: function(data) { 66 | if (data) { 67 | if (data.reset) 68 | $('#terminal-input').val(''); 69 | else 70 | $('#terminal-input').val(data.command); 71 | } else { 72 | modalPopup.alert('error', 'Could not run the command'); 73 | } 74 | } 75 | }); 76 | } 77 | 78 | if (key == '13') { 79 | if (command != '') { 80 | $.ajax({ 81 | url: baseurl+'/terminal/run/' + currentServerDb, 82 | dataType: 'json', 83 | type: 'POST', 84 | data: 'command='+command, 85 | success: function(data) { 86 | if (data) { 87 | terminal = $(".terminal-console").html(); 88 | terminal += " " + command + "
"; 89 | 90 | $.each(data.result, function(key, value) { 91 | if (value.trim() != "") 92 | terminal += value + "
"; 93 | }); 94 | 95 | terminal += prompt; 96 | $(".terminal-console").html(terminal); 97 | $(".terminal-console").animate({ scrollTop: $('.terminal-console')[0].scrollHeight}, 1000); 98 | } else { 99 | modalPopup.alert('error', 'Could not run the command'); 100 | } 101 | 102 | $('#terminal-input').val(''); 103 | } 104 | }); 105 | } else 106 | $(".terminal-console").animate({ scrollTop: $('.terminal-console')[0].scrollHeight}, 1000); 107 | } 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /public/js/redmin/zsets.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#add_zset, #add_edit_zset').click(function(e) { 3 | e.preventDefault(); 4 | 5 | var form = $(e.target).parents('form'); 6 | var key = form.find('input[name="key"]').val().trim(); 7 | var score = form.find('input[name="score"]').val().trim(); 8 | var str = form.find('textarea[name="value"]').val().trim(); 9 | 10 | if (key != '' && str != '' && score != '') { 11 | $.ajax({ 12 | url: baseurl+'/zsets/add/' + currentServerDb, 13 | dataType: 'json', 14 | type: 'POST', 15 | data: 'key='+key+'&value='+str+'&score='+score, 16 | success: function(data) { 17 | if (data) { 18 | var oldkey = form.find('input[name="oldkey"]'); 19 | form.find('textarea').val(''); 20 | 21 | if (oldkey.length > 0) { 22 | location.reload(); 23 | } else { 24 | if (e.target.id == 'add_edit_zset') { 25 | location.href = baseurl + '/keys/view/' + currentServerDb + '/' + encodeURIComponent(key); 26 | } else { 27 | form.find('input').val(''); 28 | modalPopup.alert('saved'); 29 | } 30 | } 31 | } 32 | else { 33 | modalPopup.alert('error'); 34 | } 35 | } 36 | }); 37 | } else { 38 | modalPopup.alert('invalid') 39 | } 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /views/404.php: -------------------------------------------------------------------------------- 1 | Page not found 2 | -------------------------------------------------------------------------------- /views/generalmodals.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 27 | 28 | 41 | 42 | 74 | -------------------------------------------------------------------------------- /views/hashes/add.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 | oldkey)) { 4 | echo ""; 5 | } else { 6 | echo "Add Hash"; 7 | } ?> 8 |
9 | 10 | oldkey)): ?> 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 | oldkey)): ?> 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /views/hashes/edit.php: -------------------------------------------------------------------------------- 1 |
2 |

Edit hash key

3 | edited) && $this->edited): ?> 4 |
5 | × 6 | Hash key edited successfuly 7 |
8 | edited)): ?> 9 |
10 | × 11 | There was a problem editing the hash key 12 |
13 | 14 |
15 |
key?> / member?>
16 |
17 | 18 |
19 | 20 | 21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /views/hashes/view.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 |

Edit Hash

4 | renderPartial('hashes/add', array('oldkey' => $this->key))?> 5 |
key?>
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | members as $member => $value): ?> 15 | 16 | 19 | 22 | 27 | 32 | 35 | 36 | 37 | members)): ?> 38 | 39 | 41 | 46 | 49 | 50 | 51 |
KeyValueEditDelete
17 | 18 | 20 | 21 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 33 | 34 |
40 | 42 | 43 | 44 | 45 | 47 | 48 |
52 |
53 | -------------------------------------------------------------------------------- /views/keys/move.php: -------------------------------------------------------------------------------- 1 |
2 |

Move key

3 | moved) && $this->moved): ?> 4 |
5 | × 6 | Key moved successfuly 7 |
8 | moved)): ?> 9 |
10 | × 11 | There was a problem moving the key 12 |
13 | 14 | moved) || (isset($this->moved) && !$this->moved)): ?> 15 | 23 | 24 |
25 | -------------------------------------------------------------------------------- /views/keys/rename.php: -------------------------------------------------------------------------------- 1 |
2 |

Rename key

3 | renamed) && $this->renamed): ?> 4 |
5 | × 6 | Key renamed successfuly 7 |
8 | renamed)): ?> 9 |
10 | × 11 | There was a problem renaming the key 12 |
13 | 14 | renamed) || (isset($this->renamed) && !$this->renamed)): ?> 15 | 23 | 24 |
25 | -------------------------------------------------------------------------------- /views/keys/search.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 |

Search Results keys)?> result(s) found

4 | 5 |
6 | × 7 | Since this doesn't support pagination yet, try to limit your search. Otherwise your browser might crash 8 |
9 | search && !count($this->keys)): ?> 10 |
11 | × 12 | Your search did not match any keys 13 |
14 | 15 | search): ?> 16 |
17 | × 18 | Please enter valid search criteria 19 |
20 | 21 |
Redis Keys
22 | 29 | keys)): ?> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | keys as $key): ?> 45 | 46 | 49 | 52 | 55 | 58 | 61 | 66 | 71 | 76 | 81 | 86 | 89 | 90 | 91 | 92 | 94 | 99 | 104 | 107 | 108 |
KeyTypeTTLEncodingSizeExpireRenameViewMoveDelete
47 | 48 | 50 | getType($key)?> 51 | 53 | getTTL($key)?> 54 | 56 | getEncoding($key)?> 57 | 59 | getSize($key)?> 60 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 77 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 87 | 88 |
93 | 95 | 96 | 97 | 98 | 100 | 101 | 102 | 103 | 105 | 106 |
109 | 110 |
111 | -------------------------------------------------------------------------------- /views/keys/ttl.php: -------------------------------------------------------------------------------- 1 |
2 |

Key Expiration

3 |
4 | × 5 | 0 means no ttl (Values lower than 0, make the key persistant) 6 |
7 | updated) && $this->updated): ?> 8 |
9 | × 10 | Key updated successfuly 11 |
12 | updated)): ?> 13 |
14 | × 15 | There was a problem updating the key 16 |
17 | 18 | updated) || (isset($this->updated) && !$this->updated)): ?> 19 | 32 | 33 |
34 | -------------------------------------------------------------------------------- /views/lists/add.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 | oldkey) ? "" : "Add List" ?> 4 |
5 | 6 | oldkey)): ?> 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 |
16 |
17 | 23 |
24 |
25 |
26 | 27 | oldkey)): ?> 28 | 29 | 30 |
31 | -------------------------------------------------------------------------------- /views/lists/view.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 |

Edit List

4 | renderPartial('lists/add', array('oldkey' => $this->key))?> 5 |
6 | Remove from List 7 |
8 | 9 | 10 | 11 |
12 |
13 | 17 |
18 |
19 |
20 | 21 |
22 |
key?>
23 | 24 | 25 | 26 | 27 | 28 | values as $member => $value): ?> 29 | 30 | 33 | 36 | 37 | 38 |
IndexValue
31 | 32 | 34 | 35 |
39 | count > 30): ?> 40 | 52 | 53 |
54 | -------------------------------------------------------------------------------- /views/navigation.php: -------------------------------------------------------------------------------- 1 | 9 |
10 | 32 |
33 | app->config['database']['redis']) > 1) : ?> 34 | 46 | 47 | -------------------------------------------------------------------------------- /views/sets/add.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 | oldkey) ? "" : "Add Set"; ?> 4 |
5 | 6 | oldkey)): ?> 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 |
16 | 17 | oldkey)): ?> 18 | 19 | 20 |
21 | -------------------------------------------------------------------------------- /views/sets/edit.php: -------------------------------------------------------------------------------- 1 |
2 | edited) && $this->edited): ?> 3 |
4 | × 5 | Set Key member edited successfully 6 |
7 | edited)): ?> 8 |
9 | × 10 | There was a problem editing the Set Key member 11 |
12 | 13 | edited) || (isset($this->edited) && !$this->edited)): ?> 14 |
15 | Edit Set key 16 |
key?> / member?>
17 |
18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 |
26 | -------------------------------------------------------------------------------- /views/sets/view.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 |

Edit Set

4 | renderPartial('sets/add', array('oldkey' => $this->key))?> 5 |
key?>
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | members as $member): ?> 15 | 16 | 19 | 24 | 29 | 32 | 33 | 34 | members)): ?> 35 | 36 | 38 | 48 | 49 | 50 |
ValueEditDelete
17 | 18 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 30 | 31 |
37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
51 |
52 | -------------------------------------------------------------------------------- /views/strings/add.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 | Add String 4 |
5 | 6 | 7 |
8 |
9 | 10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /views/strings/view.php: -------------------------------------------------------------------------------- 1 |
2 |

Edit Value

3 | edited) && $this->edited): ?> 4 |
5 | × 6 | Key edited successfuly 7 |
8 | edited)): ?> 9 |
10 | × 11 | There was a problem editing the key 12 |
13 | 14 |
15 |
key?>
16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 | -------------------------------------------------------------------------------- /views/terminal/index.php: -------------------------------------------------------------------------------- 1 | config['terminal']['enable']): ?> 2 |
3 |

Terminal

4 | addHeader(""); ?> 5 |
6 | redis app->current['host'] ?>:app->current['port'] ?>> 7 |
8 |
9 |
10 |
11 |
>
12 |
13 |
14 |
15 |
16 |
17 |
18 | × 19 | This functionaliy takes advantage of PHP's exec function. Although, all the commands are escaped for security, you can disable terminal from configuration file. 20 |
21 |
22 | 23 |
24 | Terminal is not enabled. 25 |
26 | 27 | -------------------------------------------------------------------------------- /views/welcome/config.php: -------------------------------------------------------------------------------- 1 |
2 |

Redis Config

3 | 4 | config as $key => $value): ?> 5 | 6 | 9 | 12 | 13 | 14 |
7 | 8 | 10 | 11 |
15 |
16 | -------------------------------------------------------------------------------- /views/welcome/info.php: -------------------------------------------------------------------------------- 1 |
2 |

Redis Info

3 | 4 | 5 | 8 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | 24 | 27 | 28 | 29 | 32 | 35 | 36 | 37 | 40 | 43 | 44 | 45 | 48 | 53 | 54 | 55 | 58 | 61 | 62 | info['role'] == 'master'): ?> 63 | 64 | 67 | 70 | 71 | 72 | 73 | 76 | 79 | 80 | 81 | 84 | 87 | 88 | 89 | 92 | 95 | 96 | 97 | 100 | 104 | 105 | 106 | 109 | 112 | 113 | 114 | 117 | 120 | 121 | 122 | 125 | 128 | 129 | 130 | 133 | 136 | 137 | 138 | 141 | 144 | 145 | 146 | 149 | 152 | 153 | 154 | 157 | 160 | 161 | 162 | 165 | 168 | 169 | 170 | 173 | 176 | 177 |
6 | Version: 7 | 9 | info['redis_version']?> 10 |
14 | Mode: 15 | 17 | info['redis_mode']?> 18 |
22 | Role: 23 | 25 | info['role']?> 26 |
30 | OS: 31 | 33 | info['os']?> 34 |
38 | Process ID: 39 | 41 | info['process_id']?> 42 |
46 | Uptime: 47 | 49 | uptimeDays > 0) { 50 | echo "{$this->uptimeDays} days "; 51 | } echo gmdate('H:i:s', $this->info['uptime_in_seconds']);?> 52 |
56 | Clients: 57 | 59 | info['connected_clients']?> 60 |
65 | Slaves: 66 | 68 | info['connected_slaves']?> 69 |
74 | Used Memory: 75 | 77 | info['used_memory_human']?> 78 |
82 | Used Memory Peak: 83 | 85 | info['used_memory_peak_human']?> 86 |
90 | Memory Fragmentation Ratio: 91 | 93 | info['mem_fragmentation_ratio']?> 94 |
98 | Last Save Time: 99 | 101 | info['last_save_time']) ? $this->info['last_save_time'] 102 | : $this->info['rdb_last_save_time'])?> 103 |
107 | Total Connections Received: 108 | 110 | info['total_connections_received'])?> 111 |
115 | Total Commands Processed: 116 | 118 | info['total_commands_processed'])?> 119 |
123 | Expired Keys: 124 | 126 | info['expired_keys'])?> 127 |
131 | Keyspace Hits: 132 | 134 | info['keyspace_hits'])?> 135 |
139 | Keyspace Misses: 140 | 142 | info['keyspace_misses'])?> 143 |
147 | System CPU Usage: 148 | 150 | info['used_cpu_sys']?> 151 |
155 | User CPU Usage: 156 | 158 | info['used_cpu_user']?> 159 |
163 | Database Size: 164 | 166 | dbSize)?> 167 |
171 | Last save to disk: 172 | 174 | lastSave)?> 175 |
178 |
179 | -------------------------------------------------------------------------------- /views/welcome/slowlog.php: -------------------------------------------------------------------------------- 1 |
2 |

Redis Slow Log (count?> most recent)

3 |
4 | × 5 | PHPRedmin uses Eval to fetch slowlogs 6 |
7 | support): ?> 8 |
9 |
10 | 11 | 12 |
13 | 14 |
15 | 16 | 17 | 20 | 23 | 26 | 27 | slowlogs as $log): ?> 28 | 29 | 30 | 31 | 32 | 33 | 34 |
18 | Time 19 | 21 | Duration (Microseconds) 22 | 24 | Info 25 |
35 | 36 |
37 | × 38 | Eval has been available since redis version 2.6.0 but your redis version is version?> 39 |
40 | 41 |
42 | -------------------------------------------------------------------------------- /views/welcome/stats.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 | addHeader(""); ?> 3 | addHeader(""); ?> 4 | addHeader(""); ?> 5 | 109 |
110 |

Redis Stats

111 |
112 | × 113 | In order to view stats, you have to setup cron located in controllers directory 114 |
115 | 116 | 117 |
118 | 119 | 120 | 123 |
124 | 125 | 139 |
140 |
141 | 142 | 143 |
144 |
145 | 146 |
147 |
148 | 149 | 150 | 151 |
152 |
153 | 154 |
155 |
156 | 157 |
158 |
159 |
160 | -------------------------------------------------------------------------------- /views/zsets/add.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 | oldkey)) { 4 | echo ""; 5 | } else { 6 | echo "Add Sorted Set"; 7 | } ?> 8 |
9 | 10 | oldkey)): ?> 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 | oldkey)): ?> 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /views/zsets/view.php: -------------------------------------------------------------------------------- 1 | addHeader(""); ?> 2 |
3 |

Edit Sorted Set

4 | renderPartial('zsets/add', array('oldkey' => $this->key))?> 5 |
key?>
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | values as $member => $value): ?> 14 | 15 | 18 | 21 | 26 | 29 | 30 | 31 | values)): ?> 32 | 33 | 35 | 40 | 43 | 44 | 45 |
ValueScoreDelete
16 | 17 | 19 | 20 | 22 | 23 | 24 | 25 | 27 | 28 |
34 | 36 | 37 | 38 | 39 | 41 | 42 |
46 | count > 30): ?> 47 | 59 | 60 |
61 | --------------------------------------------------------------------------------