├── .htaccess ├── LICENSE ├── README.md ├── bin ├── cake ├── cake.bat └── cake.php ├── composer.json ├── config ├── Migrations │ ├── 20150607191654_initial.php │ ├── 20150607192040_add_report_id_index_to_incidents.php │ ├── 20150607192459_add_dev_report_index_to_notifications.php │ ├── 20160111023543_add_locale_to_incidents.php │ ├── 20170515135017_fix_long_pma_versions.php │ ├── 20170530155816_fix_stale_report_states.php │ └── 20170711060435_AlterIncidents.php ├── app.default.php ├── app_ci.php ├── app_example.php ├── bootstrap.php ├── bootstrap_cli.php ├── forwarding_example.php ├── oauth_example.php ├── paths.php ├── requirements.php ├── routes.php └── schema │ ├── i18n.sql │ └── sessions.sql ├── index.php ├── phpstan-baseline.neon ├── phpstan.neon.dist ├── phpunit.xml.dist ├── src ├── Application.php ├── Console │ └── Installer.php ├── Controller │ ├── AppController.php │ ├── Component │ │ ├── GithubApiComponent.php │ │ ├── MailerComponent.php │ │ └── OrderSearchComponent.php │ ├── DevelopersController.php │ ├── ErrorController.php │ ├── EventsController.php │ ├── GithubController.php │ ├── IncidentsController.php │ ├── NotificationsController.php │ ├── PagesController.php │ ├── ReportsController.php │ └── StatsController.php ├── Forwarding │ └── Sentry.php ├── Mailer │ └── Transport │ │ └── TestTransport.php ├── Model │ └── Table │ │ ├── DevelopersTable.php │ │ ├── IncidentsTable.php │ │ ├── NotificationsTable.php │ │ └── ReportsTable.php ├── Report.php ├── Shell │ ├── CleanOldNotifsShell.php │ ├── ConsoleShell.php │ ├── StatsShell.php │ └── SyncGithubIssueStatesShell.php └── View │ ├── AppView.php │ └── Helper │ ├── AppHelper.php │ ├── IncidentsHelper.php │ └── ReportsHelper.php ├── templates ├── Error │ ├── error400.php │ └── error500.php ├── Github │ └── create_issue.php ├── Incidents │ └── view.php ├── Notifications │ └── index.php ├── Pages │ └── home.php ├── Reports │ ├── index.php │ └── view.php ├── Stats │ └── stats.php ├── element │ └── flash │ │ ├── default.php │ │ ├── error.php │ │ └── success.php ├── email │ ├── html │ │ ├── default.php │ │ └── report.php │ └── text │ │ └── default.php └── layout │ ├── ajax.php │ ├── default.php │ ├── email │ ├── html │ │ └── default.php │ └── text │ │ └── default.php │ ├── error.php │ ├── flash.php │ ├── js │ └── default.php │ ├── rss │ └── default.php │ └── xml │ └── default.php ├── tests ├── Fixture │ ├── DevelopersFixture.php │ ├── IncidentsFixture.php │ ├── NotificationsFixture.php │ ├── ReportsFixture.php │ ├── comment_response.json │ ├── empty │ ├── issue_response.json │ ├── report_issue_16853_js.json │ ├── report_js.json │ ├── report_php.json │ └── user_response.json ├── TestCase │ ├── AllTestsTest.php │ ├── Controller │ │ ├── Component │ │ │ └── empty │ │ ├── DevelopersControllerTest.php │ │ ├── EventsControllerTest.php │ │ ├── GithubControllerTest.php │ │ ├── IncidentsControllerTest.php │ │ ├── NotificationsControllerTest.php │ │ └── ReportsControllerTest.php │ ├── Model │ │ ├── Behavior │ │ │ └── empty │ │ └── Table │ │ │ ├── DevelopersTableTest.php │ │ │ ├── IncidentsTableTest.php │ │ │ ├── NotificationsTableTest.php │ │ │ └── ReportsTableTest.php │ ├── ReportTest.php │ ├── Shell │ │ ├── CleanOldNotifsShellTest.php │ │ ├── StatsShellTest.php │ │ └── SyncGithubIssueStatesShellTest.php │ └── View │ │ └── Helper │ │ ├── ReportsHelperTest.php │ │ └── empty └── bootstrap.php └── webroot ├── .htaccess ├── css ├── bootstrap-responsive.css ├── bootstrap-responsive.min.css ├── bootstrap.css ├── bootstrap.min.css ├── cake.generic.css ├── custom.css ├── jquery.dataTables.css ├── jquery.dataTables_themeroller.css ├── jquery.jqplot.min.css ├── shCore.css └── shThemeDefault.css ├── favicon.ico ├── files └── empty ├── img ├── Sorting icons.psd ├── back_disabled.png ├── back_enabled.png ├── back_enabled_hover.png ├── cake.icon.png ├── cake.power.gif ├── favicon.ico ├── forward_disabled.png ├── forward_enabled.png ├── forward_enabled_hover.png ├── glyphicons-halflings-white.png ├── glyphicons-halflings.png ├── sort_asc.png ├── sort_asc_disabled.png ├── sort_both.png ├── sort_desc.png ├── sort_desc_disabled.png ├── test-error-icon.png ├── test-fail-icon.png ├── test-pass-icon.png └── test-skip-icon.png ├── index.php ├── js ├── bootstrap.js ├── bootstrap.min.js ├── custom.js ├── g.bar-min.js ├── g.dot-min.js ├── g.line-min.js ├── g.pie-min.js ├── g.raphael-min.js ├── jqplot.barRenderer.min.js ├── jqplot.canvasAxisTickRenderer.min.js ├── jqplot.canvasTextRenderer.min.js ├── jqplot.categoryAxisRenderer.min.js ├── jqplot.cursor.min.js ├── jqplot.dateAxisRenderer.min.js ├── jqplot.highlighter.min.js ├── jqplot.pointLabels.min.js ├── jquery.dataTables.min.js ├── jquery.jqplot.min.js ├── jquery.js ├── pie.js ├── raphael-min.js ├── shBrushJScript.js ├── shBrushPhp.js ├── shBrushXml.js └── shCore.js └── robots.txt /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^$ webroot/ [L] 4 | RewriteRule (.*) webroot/$1 [L] 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 phpMyAdmin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /bin/cake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | ################################################################################ 3 | # 4 | # Cake is a shell script for invoking CakePHP shell commands 5 | # 6 | # CakePHP(tm) : Rapid Development Framework (https://cakephp.org) 7 | # Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 8 | # 9 | # Licensed under The MIT License 10 | # For full copyright and license information, please see the LICENSE.txt 11 | # Redistributions of files must retain the above copyright notice. 12 | # 13 | # @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 14 | # @link https://cakephp.org CakePHP(tm) Project 15 | # @since 1.2.0 16 | # @license https://opensource.org/licenses/mit-license.php MIT License 17 | # 18 | ################################################################################ 19 | 20 | # Canonicalize by following every symlink of the given name recursively 21 | canonicalize() { 22 | NAME="$1" 23 | if [ -f "$NAME" ] 24 | then 25 | DIR=$(dirname -- "$NAME") 26 | NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME") 27 | fi 28 | while [ -h "$NAME" ]; do 29 | DIR=$(dirname -- "$NAME") 30 | SYM=$(readlink "$NAME") 31 | NAME=$(cd "$DIR" > /dev/null && cd "$(dirname -- "$SYM")" > /dev/null && pwd)/$(basename -- "$SYM") 32 | done 33 | echo "$NAME" 34 | } 35 | 36 | # Find a CLI version of PHP 37 | findCliPhp() { 38 | for TESTEXEC in php php-cli /usr/local/bin/php 39 | do 40 | SAPI=$(echo "" | $TESTEXEC 2>/dev/null) 41 | if [ "$SAPI" = "cli" ] 42 | then 43 | echo $TESTEXEC 44 | return 45 | fi 46 | done 47 | echo "Failed to find a CLI version of PHP; falling back to system standard php executable" >&2 48 | echo "php"; 49 | } 50 | 51 | # If current path is a symlink, resolve to real path 52 | realname="$0" 53 | if [ -L "$realname" ] 54 | then 55 | realname=$(readlink -f "$0") 56 | fi 57 | 58 | CONSOLE=$(dirname -- "$(canonicalize "$realname")") 59 | APP=$(dirname "$CONSOLE") 60 | 61 | # If your CLI PHP is somewhere that this doesn't find, you can define a PHP environment 62 | # variable with the correct path in it. 63 | if [ -z "$PHP" ] 64 | then 65 | PHP=$(findCliPhp) 66 | fi 67 | 68 | if [ "$(basename "$realname")" != 'cake' ] 69 | then 70 | exec "$PHP" "$CONSOLE"/cake.php "$(basename "$realname")" "$@" 71 | else 72 | exec "$PHP" "$CONSOLE"/cake.php "$@" 73 | fi 74 | 75 | exit 76 | -------------------------------------------------------------------------------- /bin/cake.bat: -------------------------------------------------------------------------------- 1 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 2 | :: 3 | :: Cake is a Windows batch script for invoking CakePHP shell commands 4 | :: 5 | :: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) 6 | :: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 7 | :: 8 | :: Licensed under The MIT License 9 | :: Redistributions of files must retain the above copyright notice. 10 | :: 11 | :: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 12 | :: @link https://cakephp.org CakePHP(tm) Project 13 | :: @since 2.0.0 14 | :: @license https://opensource.org/licenses/mit-license.php MIT License 15 | :: 16 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 17 | 18 | @echo off 19 | 20 | SET app=%0 21 | SET lib=%~dp0 22 | 23 | php "%lib%cake.php" %* 24 | 25 | echo. 26 | 27 | exit /B %ERRORLEVEL% 28 | -------------------------------------------------------------------------------- /bin/cake.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | run($argv)); 13 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpmyadmin/error-reporting-server", 3 | "type": "application", 4 | "description": "phpMyAdmin server side component for the error reporting system", 5 | "keywords": ["phpmyadmin","mysql","web"], 6 | "homepage": "https://www.phpmyadmin.net/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "The phpMyAdmin Team", 11 | "email": "developers@phpmyadmin.net", 12 | "homepage": "https://www.phpmyadmin.net/team/" 13 | } 14 | ], 15 | "support": { 16 | "issues": "https://github.com/phpmyadmin/error-reporting-server/issues", 17 | "wiki": "https://wiki.phpmyadmin.net/", 18 | "source": "https://github.com/phpmyadmin/error-reporting-server" 19 | }, 20 | "require": { 21 | "php": "^7.4 || ^8.0", 22 | "ext-curl": "*", 23 | "cakephp/cakephp": "4.2.x", 24 | "cakephp/migrations": "^3.0.0", 25 | "cakephp/plugin-installer": "^1.3", 26 | "composer/ca-bundle": "^1.3" 27 | }, 28 | "require-dev": { 29 | "cakedc/cakephp-phpstan": "^2", 30 | "cakephp/bake": "^2.1.1", 31 | "cakephp/debug_kit": "^4.1.2", 32 | "php-mock/php-mock-phpunit": "^2.6", 33 | "phpmyadmin/coding-standard": "^3", 34 | "phpstan/phpstan": "^1", 35 | "phpunit/phpunit": "^9", 36 | "psy/psysh": "@stable" 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "App\\": "src/" 41 | } 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "App\\Test\\": "tests/", 46 | "Cake\\Test\\": "vendor/cakephp/cakephp/tests/" 47 | } 48 | }, 49 | "scripts": { 50 | "post-install-cmd": "App\\Console\\Installer::postInstall", 51 | "check": [ 52 | "@test", 53 | "@phpcs" 54 | ], 55 | "phpcs": "@php ./vendor/bin/phpcs", 56 | "phpcbf": "@php ./vendor/bin/phpcbf", 57 | "test": "@php ./vendor/bin/phpunit", 58 | "phpstan": "@php ./vendor/bin/phpstan" 59 | }, 60 | "prefer-stable": true, 61 | "config": { 62 | "sort-packages": true, 63 | "allow-plugins": { 64 | "dealerdirect/phpcodesniffer-composer-installer": true, 65 | "cakephp/plugin-installer": true 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/Migrations/20150607192040_add_report_id_index_to_incidents.php: -------------------------------------------------------------------------------- 1 | table('incidents'); 16 | $table->addIndex( 17 | ['report_id'] 18 | ); 19 | $table->update(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /config/Migrations/20150607192459_add_dev_report_index_to_notifications.php: -------------------------------------------------------------------------------- 1 | table('notifications'); 16 | $table->addIndex([ 17 | 'developer_id', 18 | 'report_id', 19 | ]); 20 | $table->update(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /config/Migrations/20160111023543_add_locale_to_incidents.php: -------------------------------------------------------------------------------- 1 | table('incidents'); 31 | $table 32 | ->addColumn('locale', 'string', [ 33 | 'default' => null, 34 | 'limit' => 30, 35 | 'null' => true, 36 | ]) 37 | ->update(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config/Migrations/20170515135017_fix_long_pma_versions.php: -------------------------------------------------------------------------------- 1 | _sanitizeVersionsInTable('reports'); 18 | Log::debug($count . ' reports updated'); 19 | 20 | // Strip phpMyAdmin versions in `incidents` table 21 | $count = $this->_sanitizeVersionsInTable('incidents'); 22 | Log::debug($count . ' incidents updated'); 23 | } 24 | 25 | public function down(): void 26 | { 27 | // Once applied, this migration can't be reversed 28 | // but the migration itself is idempotent 29 | } 30 | 31 | private function _sanitizeVersionsInTable(string $table): int 32 | { 33 | $sql = 'SELECT `id`, `pma_version` FROM `' . $table . '`'; 34 | $count = 0; 35 | $incidentsTable = TableRegistry::getTableLocator()->get('Incidents'); 36 | $result = $this->query($sql); 37 | 38 | while ($row = $result->fetch()) { 39 | $strippedVersionString 40 | = IncidentsTable::getStrippedPmaVersion( 41 | $row['pma_version'] 42 | ); 43 | 44 | if ($strippedVersionString === $row['pma_version']) { 45 | continue; 46 | } 47 | 48 | $this->execute('UPDATE ' . $table . ' SET `pma_version` = \'' 49 | . $strippedVersionString . '\' WHERE `id` = \'' 50 | . $row['id'] . '\''); 51 | 52 | ++$count; 53 | } 54 | 55 | return $count; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /config/Migrations/20170530155816_fix_stale_report_states.php: -------------------------------------------------------------------------------- 1 | _migrateBasedOnGithubLinked(); 15 | $this->_migrateToNewStatus(); 16 | $this->_migrateDuplicateReports(); 17 | } 18 | 19 | private function _migrateBasedOnGithubLinked(): void 20 | { 21 | $sql = 'UPDATE `reports` SET `status` = \'forwarded\'' 22 | . ' WHERE `sourceforge_bug_id` IS NOT NULL AND `status` <> \'fixed\''; 23 | $rowsAffected = $this->execute($sql); 24 | 25 | Log::debug( 26 | $rowsAffected . ' reports are linked to a' 27 | . ' Github issue.' 28 | . ' These have been marked to have a \'forwarded\' status.' 29 | ); 30 | } 31 | 32 | private function _migrateToNewStatus(): void 33 | { 34 | $statusMap = [ 35 | 'fixed' => 'resolved', 36 | 'open' => 'new', 37 | 'out-of-date' => 'invalid', 38 | 'works-for-me' => 'invalid', 39 | 'wontfix' => 'invalid', 40 | ]; 41 | 42 | foreach ($statusMap as $oldStatus => $newStatus) { 43 | $sql = 'UPDATE `reports` SET `status` = \'' . $newStatus . '\'' 44 | . ' WHERE `status` = \'' . $oldStatus . '\''; 45 | $rowsAffected = $this->execute($sql); 46 | 47 | Log::debug( 48 | $rowsAffected . ' reports with \'' . $oldStatus . '\'' 49 | . ' were mapped to \'' . $newStatus . '\'.' 50 | ); 51 | } 52 | } 53 | 54 | private function _migrateDuplicateReports(): void 55 | { 56 | // Find the original reports and set the status 57 | // of their duplicate reports same as their status 58 | $sql = 'SELECT `id`, `status` FROM `reports` WHERE' 59 | . ' `related_to` IS NULL'; 60 | $origCount = 0; 61 | $result = $this->query($sql); 62 | $rowsAffected = 0; 63 | 64 | while ($row = $result->fetch()) { 65 | $update_sql = 'UPDATE `reports` SET `status` = \'' 66 | . $row['status'] . '\' WHERE `id` = \'' 67 | . $row['id'] . '\''; 68 | 69 | $rowsAffected += $this->execute($update_sql); 70 | $origCount++; 71 | } 72 | 73 | Log::debug( 74 | $rowsAffected . ' duplicate reports were mapped to ' 75 | . ' same status as their ' . $origCount . ' original reports' 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /config/Migrations/20170711060435_AlterIncidents.php: -------------------------------------------------------------------------------- 1 | table('incidents'); 16 | $table->changeColumn('error_message', 'string', [ 17 | 'default' => null, 18 | 'limit' => 200, 19 | 'null' => true, 20 | ]); 21 | $table->update(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/bootstrap_cli.php: -------------------------------------------------------------------------------- 1 | 'https://sentry.domain.tld',// Without the last / 15 | 'project_id' => 2,// Settings > Security headers (can be found in the URL: api/{project_id}/security) 16 | 'key' => 'xxxxxxxxxxxxxxxxxx',// Settings > Security headers 17 | 'secret' => 'xxxxxxxxxxxxxxxxxxxx',// Settings > Client Keys > DSN (deprecated, use password value of the http basic auth) 18 | // Used to send user feedback 19 | 'dsn_url' => 'https://xxxxxxxxxxxxxxxxxx@sentry.domain.tld/{project_id}',// Settings > Client Keys > DSN 20 | ]); 21 | 22 | // Remove this line or the forwarding will be disabled 23 | Configure::write('Forwarding.Sentry', null); 24 | -------------------------------------------------------------------------------- /config/oauth_example.php: -------------------------------------------------------------------------------- 1 | '', 13 | 'client_secret' => '', 14 | ]); 15 | 16 | /** 17 | * Configures the github repo to check commit access for 18 | */ 19 | Configure::write('GithubRepoPath', 'phpmyadmin/phpmyadmin'); 20 | 21 | /** 22 | * Access token for syncing Github issue states 23 | */ 24 | Configure::write('GithubAccessToken', ''); 25 | -------------------------------------------------------------------------------- /config/paths.php: -------------------------------------------------------------------------------- 1 | connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); 55 | 56 | /** 57 | * ...and connect the rest of 'Pages' controller's URLs. 58 | */ 59 | $routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); 60 | 61 | $routes->connect('/stats', ['controller' => 'Stats', 'action' => 'stats']); 62 | 63 | $routes->connect( 64 | '/reports/view/{id}', 65 | ['controller' => 'Reports', 'action' => 'view'], 66 | ['_name' => 'reports:view'] 67 | ) 68 | ->setPass(['id', 'reportId']) 69 | ->setPatterns([ 70 | 'id' => '[0-9]+', 71 | ]); 72 | 73 | $routes->connect( 74 | '/github/sync_issue_status', 75 | ['controller' => 'Github', 'action' => 'sync_issue_status'] 76 | ); 77 | 78 | $routes->connect( 79 | '/github/create_issue/{id}', 80 | ['controller' => 'Github', 'action' => 'create_issue'], 81 | ['_name' => 'github:create_issue'] 82 | ) 83 | ->setPass(['id', 'reportId']) 84 | ->setPatterns([ 85 | 'id' => '[0-9]+', 86 | ]); 87 | 88 | $routes->connect( 89 | '/github/unlink_issue/{id}', 90 | ['controller' => 'Github', 'action' => 'unlink_issue'], 91 | ['_name' => 'github:unlink_issue'] 92 | ) 93 | ->setPass(['id', 'reportId']) 94 | ->setPatterns([ 95 | 'id' => '[0-9]+', 96 | ]); 97 | 98 | $routes->connect( 99 | '/github/link_issue/{id}', 100 | ['controller' => 'Github', 'action' => 'link_issue'], 101 | ['_name' => 'github:link_issue'] 102 | ) 103 | ->setPass(['id', 'reportId']) 104 | ->setPatterns([ 105 | 'id' => '[0-9]+', 106 | ]); 107 | /** 108 | * Connect catchall routes for all controllers. 109 | * 110 | * Using the argument `DashedRoute`, the `fallbacks` method is a shortcut for 111 | * 112 | * ``` 113 | * $routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'DashedRoute']); 114 | * $routes->connect('/:controller/:action/*', [], ['routeClass' => 'DashedRoute']); 115 | * ``` 116 | * 117 | * Any route class can be used with this method, such as: 118 | * - DashedRoute 119 | * - InflectedRoute 120 | * - Route 121 | * - Or your own route class 122 | * 123 | * You can remove these routes once you've connected the 124 | * routes you want in your application. 125 | */ 126 | $routes->fallbacks(InflectedRoute::class); 127 | }); 128 | 129 | /** 130 | * If you need a different set of middleware or none at all, 131 | * open new scope and define routes there. 132 | * 133 | * ``` 134 | * Router::scope('/api', function (RouteBuilder $routes) { 135 | * // No $routes->applyMiddleware() here. 136 | * // Connect API actions here. 137 | * }); 138 | * ``` 139 | */ 140 | -------------------------------------------------------------------------------- /config/schema/i18n.sql: -------------------------------------------------------------------------------- 1 | # Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 2 | # 3 | # Licensed under The MIT License 4 | # For full copyright and license information, please see the LICENSE.txt 5 | # Redistributions of files must retain the above copyright notice. 6 | # MIT License (https://opensource.org/licenses/mit-license.php) 7 | 8 | CREATE TABLE i18n ( 9 | id int NOT NULL auto_increment, 10 | locale varchar(6) NOT NULL, 11 | model varchar(255) NOT NULL, 12 | foreign_key int(10) NOT NULL, 13 | field varchar(255) NOT NULL, 14 | content text, 15 | PRIMARY KEY (id), 16 | UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), 17 | INDEX I18N_FIELD(model, foreign_key, field) 18 | ); 19 | -------------------------------------------------------------------------------- /config/schema/sessions.sql: -------------------------------------------------------------------------------- 1 | # Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 2 | # 3 | # Licensed under The MIT License 4 | # For full copyright and license information, please see the LICENSE.txt 5 | # Redistributions of files must retain the above copyright notice. 6 | # MIT License (https://opensource.org/licenses/mit-license.php) 7 | 8 | CREATE TABLE `sessions` ( 9 | `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, 10 | `created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+ 11 | `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+ 12 | `data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob 13 | `expires` int(10) unsigned DEFAULT NULL, 14 | PRIMARY KEY (`id`) 15 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 16 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | tests/TestCase/ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | src/ 38 | plugins/*/src/ 39 | 40 | src/Console/Installer.php 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Application.php: -------------------------------------------------------------------------------- 1 | bootstrapCli(); 49 | } 50 | 51 | /* 52 | * Only try to load DebugKit in development mode 53 | * Debug Kit should not be installed on a production system 54 | */ 55 | if (Configure::read('debug')) { 56 | $this->addPlugin(Plugin::class); 57 | } 58 | 59 | // Load more plugins here 60 | } 61 | 62 | /** 63 | * Setup the middleware queue your application will use. 64 | * 65 | * @param MiddlewareQueue $middlewareQueue The middleware queue to setup. 66 | * @return MiddlewareQueue The updated middleware queue. 67 | */ 68 | public function middleware($middlewareQueue): MiddlewareQueue 69 | { 70 | $middlewareQueue 71 | // Catch any exceptions in the lower layers, 72 | // and make an error page/response 73 | ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) 74 | 75 | // Handle plugin/theme assets like CakePHP normally does. 76 | ->add(new AssetMiddleware([ 77 | 'cacheTime' => Configure::read('Asset.cacheTime'), 78 | ])) 79 | 80 | // Add routing middleware. 81 | // If you have a large number of routes connected, turning on routes 82 | // caching in production could improve performance. For that when 83 | // creating the middleware instance specify the cache config name by 84 | // using it's second constructor argument: 85 | // `new RoutingMiddleware($this, '_cake_routes_')` 86 | ->add(new RoutingMiddleware($this)); 87 | 88 | return $middlewareQueue; 89 | } 90 | 91 | protected function bootstrapCli(): void 92 | { 93 | try { 94 | $this->addPlugin('Bake'); 95 | } catch (MissingPluginException $e) {// phpcs:ignore 96 | // Do not halt if the plugin is missing 97 | } 98 | 99 | $this->addPlugin('Migrations'); 100 | 101 | // Load more plugins here 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Controller/Component/MailerComponent.php: -------------------------------------------------------------------------------- 1 | viewBuilder()->setLayout('default'); 55 | $email->viewBuilder()->setTemplate('report'); 56 | 57 | $email->setTransport($emailTransport) 58 | ->setViewVars($viewVars) 59 | ->setSubject( 60 | sprintf( 61 | 'A new report has been submitted on the Error Reporting Server: %s', 62 | $viewVars['report']['id'] 63 | ) 64 | ) 65 | ->setEmailFormat('html') 66 | ->setTo($emailTo) 67 | ->setFrom($emailFrom) 68 | ->send(); 69 | 70 | return true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Controller/Component/OrderSearchComponent.php: -------------------------------------------------------------------------------- 1 | []]; 45 | $keys = array_keys($aColumns); 46 | $columnsCount = count($aColumns); 47 | 48 | $sSearch = $request->getQuery('sSearch'); 49 | if ($sSearch !== '' && $sSearch !== null) { 50 | for ($i = 0; $i < $columnsCount; ++$i) { 51 | if ($request->getQuery('bSearchable_' . ($i + 1)) !== 'true') { 52 | continue; 53 | } 54 | 55 | $searchConditions['OR'][] = [$aColumns[$keys[$i]] . ' LIKE' => '%' . $sSearch . '%']; 56 | } 57 | } 58 | 59 | /* Individual column filtering */ 60 | for ($i = 0; $i < $columnsCount; ++$i) { 61 | $searchTerm = $request->getQuery('sSearch_' . ($i + 1)); 62 | if ($searchTerm === '' || $searchTerm === null) { 63 | continue; 64 | } 65 | 66 | $searchConditions[] = [$aColumns[$keys[$i]] . ' LIKE' => $searchTerm]; 67 | } 68 | 69 | return $searchConditions; 70 | } 71 | 72 | /** 73 | * @param string[] $aColumns The columns 74 | * 75 | * @return array|null 76 | */ 77 | public function getOrder(array $aColumns, ServerRequest $request): ?array 78 | { 79 | if ($request->getQuery('iSortCol_0') !== null) { 80 | $order = []; 81 | //Seems like we need to sort with only one column each time, so no need to loop 82 | $sort_column_index = intval($request->getQuery('iSortCol_0')); 83 | 84 | $keys = array_keys($aColumns); 85 | 86 | if ( 87 | $sort_column_index > 0 88 | && $request->getQuery('bSortable_' . $sort_column_index) === 'true' 89 | ) { 90 | $order[$aColumns[$keys[$sort_column_index - 1]]] = $request->getQuery('sSortDir_0'); 91 | } 92 | 93 | return $order; 94 | } 95 | 96 | return null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Controller/DevelopersController.php: -------------------------------------------------------------------------------- 1 | loadComponent('GithubApi'); 43 | $this->viewBuilder()->setHelpers([ 44 | 'Html', 45 | 'Form', 46 | ]); 47 | } 48 | 49 | public function login(): void 50 | { 51 | $url = $this->GithubApi->getRedirectUrl('user:email,public_repo'); 52 | $this->redirect($url); 53 | } 54 | 55 | public function callback(): ?Response 56 | { 57 | $code = $this->request->getQuery('code'); 58 | $accessToken = $this->GithubApi->getAccessToken($code); 59 | if (empty($code) || empty($accessToken)) { 60 | $flash_class = 'alert alert-error'; 61 | $this->Flash->set( 62 | 'We were not able to authenticate you.' 63 | . ' Please try again later', 64 | ['params' => ['class' => $flash_class]] 65 | ); 66 | 67 | return $this->redirect('/'); 68 | } 69 | 70 | [$userInfo, $status] = $this->GithubApi->getUserInfo($accessToken); 71 | if ($status !== 200) { 72 | $flash_class = 'alert alert-error'; 73 | $this->Flash->set( 74 | $userInfo['message'], 75 | ['params' => ['class' => $flash_class]] 76 | ); 77 | 78 | return $this->redirect('/'); 79 | } 80 | 81 | $userInfo['has_commit_access'] = $this->GithubApi->canCommitTo( 82 | $userInfo['login'], 83 | Configure::read('GithubRepoPath'), 84 | Configure::read('GithubAccessToken') 85 | ); 86 | 87 | $this->authenticateDeveloper($userInfo, $accessToken); 88 | 89 | $flash_class = 'alert alert-success'; 90 | $this->Flash->set( 91 | 'You have been logged in successfully', 92 | ['params' => ['class' => $flash_class]] 93 | ); 94 | 95 | $last_page = $this->request->getSession()->read('last_page'); 96 | if (empty($last_page)) { 97 | $last_page = [ 98 | 'controller' => 'reports', 99 | 'action' => 'index', 100 | ]; 101 | } 102 | 103 | return $this->redirect($last_page); 104 | } 105 | 106 | public function logout(): void 107 | { 108 | $this->request->getSession()->destroy(); 109 | 110 | $flash_class = 'alert alert-success'; 111 | $this->Flash->set( 112 | 'You have been logged out successfully', 113 | ['params' => ['class' => $flash_class]] 114 | ); 115 | $this->redirect('/'); 116 | } 117 | 118 | protected function authenticateDeveloper(array $userInfo, string $accessToken): void 119 | { 120 | $developers = $this->Developers->findByGithubId($userInfo['id']); 121 | $developer = $developers->all()->first(); 122 | if (! $developer) { 123 | $developer = $this->Developers->newEmptyEntity(); 124 | } else { 125 | $this->Developers->id = $developer['id']; 126 | } 127 | 128 | $this->Developers->id = $this->Developers->saveFromGithub($userInfo, $accessToken, $developer); 129 | $this->request->getSession()->write('Developer.id', $this->Developers->id); 130 | $this->request->getSession()->write('access_token', $accessToken); 131 | $this->request->getSession()->write('read_only', ! $userInfo['has_commit_access']); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Controller/ErrorController.php: -------------------------------------------------------------------------------- 1 | viewBuilder()->setTemplatePath('Error'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Controller/PagesController.php: -------------------------------------------------------------------------------- 1 | redirect('/'); 62 | } 63 | 64 | $page = $subpage = $title_for_layout = null; 65 | if (! empty($path[0])) { 66 | $page = $path[0]; 67 | } 68 | 69 | if (! empty($path[1])) { 70 | $subpage = $path[1]; 71 | } 72 | 73 | if (! empty($path[$count - 1])) { 74 | $title_for_layout = Inflector::humanize($path[$count - 1]); 75 | } 76 | 77 | $this->set([ 78 | 'page' => $page, 79 | 'subpage' => $subpage, 80 | 'title_for_layout' => $title_for_layout, 81 | ]); 82 | 83 | try { 84 | $this->render(implode('/', $path)); 85 | } catch (MissingViewException $e) { 86 | if (Configure::read('debug')) { 87 | throw $e; 88 | } 89 | 90 | throw new NotFoundException(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Controller/StatsController.php: -------------------------------------------------------------------------------- 1 | viewBuilder()->setHelpers(['Reports']); 46 | $this->loadModel('Incidents'); 47 | } 48 | 49 | public function stats(): void 50 | { 51 | $filter = $this->getTimeFilter(); 52 | $relatedEntries = []; 53 | $filter_string = $this->request->getQuery('filter'); 54 | if (! $filter_string) { 55 | $filter_string = 'all_time'; 56 | } 57 | 58 | $entriesWithCount = []; 59 | //Cache::clear(); 60 | foreach ($this->Incidents->summarizableFields as $field) { 61 | $entriesWithCount = Cache::read($field . '_' . $filter_string); 62 | if ($entriesWithCount === null) { 63 | $entriesWithCount = TableRegistry::getTableLocator()->get('Reports')-> 64 | getRelatedByField($field, 25, false, false, $filter['limit']); 65 | $entriesWithCount = json_encode($entriesWithCount); 66 | Cache::write($field . '_' . $filter_string, $entriesWithCount); 67 | } 68 | 69 | $relatedEntries[$field] = json_decode($entriesWithCount, true); 70 | } 71 | 72 | $this->set('related_entries', $relatedEntries); 73 | $this->set('columns', $this->Incidents->summarizableFields); 74 | $this->set('filter_times', $this->Incidents->filterTimes); 75 | $this->set('selected_filter', $this->request->getQuery('filter')); 76 | 77 | $query = [ 78 | 'group' => 'grouped_by', 79 | 'order' => 'Incidents.created', 80 | ]; 81 | 82 | if (isset($filter['limit'])) { 83 | $query['conditions'] = [ 84 | 'Incidents.created >=' => $filter['limit'], 85 | ]; 86 | } 87 | 88 | $this->Incidents->recursive = -1; 89 | $downloadStats = Cache::read('downloadStats_' . $filter_string); 90 | if ($downloadStats === null) { 91 | $downloadStats = $this->Incidents->find('all', $query); 92 | $downloadStats->select([ 93 | 'grouped_by' => $filter['group'], 94 | 'date' => "DATE_FORMAT(Incidents.created, '%a %b %d %Y %T')", 95 | 'count' => $downloadStats->func()->count('*'), 96 | ]); 97 | $downloadStats = json_encode($downloadStats->toArray()); 98 | Cache::write('downloadStats_' . $filter_string, $downloadStats); 99 | } 100 | 101 | $this->set('download_stats', json_decode($downloadStats, true)); 102 | } 103 | 104 | /** 105 | * @return mixed I am not sure about the type 106 | */ 107 | protected function getTimeFilter() 108 | { 109 | if ($this->request->getQuery('filter')) { 110 | $filter = $this->Incidents->filterTimes[$this->request->getQuery('filter')]; 111 | } 112 | 113 | if (isset($filter)) { 114 | return $filter; 115 | } 116 | 117 | return $this->Incidents->filterTimes['all_time']; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Mailer/Transport/TestTransport.php: -------------------------------------------------------------------------------- 1 | getHeaders( 26 | [ 27 | 'from', 28 | 'sender', 29 | 'replyTo', 30 | 'readReceipt', 31 | 'returnPath', 32 | 'to', 33 | 'cc', 34 | 'subject', 35 | ] 36 | ); 37 | 38 | $message = trim($message->getBodyString()); 39 | $result = [ 40 | 'headers' => $headers, 41 | 'message' => $message, 42 | ]; 43 | 44 | Configure::write('test_transport_email', $result); 45 | 46 | return $result; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Model/Table/DevelopersTable.php: -------------------------------------------------------------------------------- 1 | full_name = $githubInfo['name']; 41 | $developer->gravatar_id = $githubInfo['gravatar_id']; 42 | $developer->email = $githubInfo['email']; 43 | $developer->github_id = $githubInfo['id']; 44 | $developer->access_token = $accessToken; 45 | $developer->has_commit_access = $githubInfo['has_commit_access'] ? 1 : 0; 46 | if ($this->save($developer)) { 47 | return $developer->id; 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Model/Table/NotificationsTable.php: -------------------------------------------------------------------------------- 1 | [ 44 | 'numeric' => [ 45 | 'rule' => ['numeric'], 46 | 'allowEmpty' => false, 47 | 'required' => true, 48 | ], 49 | ], 50 | 'report_id' => [ 51 | 'numeric' => [ 52 | 'rule' => ['numeric'], 53 | 'allowEmpty' => false, 54 | 'required' => true, 55 | ], 56 | ], 57 | ]; 58 | 59 | /** 60 | * The Associations below have been created with all possible keys, 61 | * those that are not needed can be removed. 62 | */ 63 | 64 | /** 65 | * belongsTo associations. 66 | * 67 | * @param array $config Config array 68 | * @return void Nothing 69 | */ 70 | public function initialize(array $config): void 71 | { 72 | $this->belongsTo('Reports', [ 73 | 'className' => 'Reports', 74 | 'foreignKey' => 'report_id', 75 | ]); 76 | } 77 | 78 | /** 79 | * To Add Multiple Notifications for New report. 80 | * 81 | * @param int $report_id id of the new Report 82 | * 83 | * @return bool|object value. True on success. False on any type of failure. 84 | */ 85 | public static function addNotifications(int $report_id) 86 | { 87 | if (! is_int($report_id)) { 88 | throw new InvalidArgumentException('Invalid Argument "$report_id"! Integer Expected.'); 89 | } 90 | 91 | $devs = TableRegistry::getTableLocator()->get('Developers')->find('all'); 92 | $notoficationTable = TableRegistry::getTableLocator()->get('Notifications'); 93 | $res = true; 94 | foreach ($devs as $dev) { 95 | $notification = $notoficationTable->newEmptyEntity(); 96 | $notification->developer_id = $dev['id']; 97 | $notification->report_id = $report_id; 98 | $notification->created = date('Y-m-d H:i:s', time()); 99 | $notification->modified = date('Y-m-d H:i:s', time()); 100 | $res = $notoficationTable->save($notification); 101 | } 102 | 103 | return $res; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Shell/CleanOldNotifsShell.php: -------------------------------------------------------------------------------- 1 | setCommand($this->name) 56 | ->setDescription('Clean old notifications'); 57 | } 58 | 59 | public function initialize(): void 60 | { 61 | parent::initialize(); 62 | $this->loadModel('Notifications'); 63 | } 64 | 65 | public function execute(Arguments $args, ConsoleIo $io) 66 | { 67 | $XTime = time() - 60 * 24 * 3600; 68 | $conditions = ['Notifications.created <' => date('Y-m-d H:i:s', $XTime)]; 69 | 70 | if ($this->Notifications->find('all', ['conditions' => $conditions])->count() === 0) { 71 | // Check if there are any notifications to delete 72 | Log::write( 73 | 'info', 74 | 'No notifications found for deleting!', 75 | 'cron_jobs' 76 | ); 77 | } elseif ($this->Notifications->deleteAll($conditions)) { 78 | // Try deleting the matched records 79 | Log::write( 80 | 'info', 81 | 'Old notifications deleted successfully!', 82 | 'cron_jobs' 83 | ); 84 | } else { 85 | // If NOT successful, print out an error message 86 | Log::write( 87 | 'error', 88 | 'Deleting old notifications failed!', 89 | 'cron_jobs' 90 | ); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Shell/ConsoleShell.php: -------------------------------------------------------------------------------- 1 | setCommand($this->name) 54 | ->setDescription('Open an interactive console'); 55 | } 56 | 57 | /** 58 | * Start the shell and interactive console. 59 | * 60 | * @return int|void 61 | */ 62 | public function execute(Arguments $args, ConsoleIo $io) 63 | { 64 | if (! class_exists(PsyShell::class)) { 65 | $io->err('Unable to load Psy\Shell.'); 66 | $io->err(''); 67 | $io->err('Make sure you have installed psysh as a dependency,'); 68 | $io->err('and that Psy\Shell is registered in your autoloader.'); 69 | $io->err(''); 70 | $io->err('If you are using composer run'); 71 | $io->err(''); 72 | $io->err('$ php composer.phar require --dev psy/psysh'); 73 | $io->err(''); 74 | 75 | return 1; 76 | } 77 | 78 | $io->out('You can exit with `CTRL-C` or `exit`'); 79 | $io->out(''); 80 | 81 | Log::drop('debug'); 82 | Log::drop('error'); 83 | $io->setLoggers(false); 84 | restore_error_handler(); 85 | restore_exception_handler(); 86 | 87 | $psy = new PsyShell(); 88 | $psy->run(); 89 | } 90 | 91 | /** 92 | * Display help for this console. 93 | * 94 | * @return ConsoleOptionParser The console option 95 | */ 96 | public function getOptionParser(): ConsoleOptionParser 97 | { 98 | $parser = new ConsoleOptionParser($this->name, false); 99 | $parser->setDescription( 100 | 'This shell provides a REPL that you can use to interact ' . 101 | 'with your application in an interactive fashion. You can use ' . 102 | 'it to run adhoc queries with your models, or experiment ' . 103 | 'and explore the features of CakePHP and your application.' . 104 | "\n\n" . 105 | 'You will need to have psysh installed for this Shell to work.' 106 | ); 107 | 108 | return $parser; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Shell/StatsShell.php: -------------------------------------------------------------------------------- 1 | setCommand($this->name) 57 | ->setDescription('Build stats'); 58 | } 59 | 60 | public function initialize(): void 61 | { 62 | parent::initialize(); 63 | $this->loadModel('Incidents'); 64 | $this->loadModel('Reports'); 65 | } 66 | 67 | public function execute(Arguments $args, ConsoleIo $io) 68 | { 69 | foreach ($this->Incidents->filterTimes as $filter_string => $filter) { 70 | foreach ($this->Incidents->summarizableFields as $field) { 71 | $io->out('processing ' . $filter_string . ':' . $field); 72 | $entriesWithCount = $this->Reports-> 73 | getRelatedByField($field, 25, false, false, $filter['limit']); 74 | $entriesWithCount = json_encode($entriesWithCount); 75 | Cache::write($field . '_' . $filter_string, $entriesWithCount); 76 | } 77 | 78 | $query = [ 79 | 'group' => 'grouped_by', 80 | 'order' => 'Incidents.created', 81 | ]; 82 | if (isset($filter['limit'])) { 83 | $query['conditions'] = [ 84 | 'Incidents.created >=' => $filter['limit'], 85 | ]; 86 | } 87 | 88 | $downloadStats = $this->Incidents->find('all', $query); 89 | $downloadStats->select([ 90 | 'grouped_by' => $filter['group'], 91 | 'date' => "DATE_FORMAT(Incidents.created, '%a %b %d %Y %T')", 92 | 'count' => $downloadStats->func()->count('*'), 93 | ]); 94 | $downloadStats = json_encode($downloadStats->toArray()); 95 | Cache::write('downloadStats_' . $filter_string, $downloadStats); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Shell/SyncGithubIssueStatesShell.php: -------------------------------------------------------------------------------- 1 | setCommand($this->name) 58 | ->setDescription('Sync GitHub issues states'); 59 | } 60 | 61 | /** 62 | * @return int 63 | */ 64 | public function execute(Arguments $args, ConsoleIo $io) 65 | { 66 | Log::debug( 67 | 'STARTED: Job "' 68 | . 'github/sync_issue_status' 69 | . '" at ' 70 | . date('d-m-Y G:i:s (e)'), 71 | ['scope' => 'cron_jobs'] 72 | ); 73 | 74 | Configure::write('CronDispatcher', true); 75 | if (PHP_SAPI !== 'cli') { 76 | exit; 77 | } 78 | 79 | $session = Session::create(); 80 | $session->write('Developer.id', 1); 81 | $request = new ServerRequest([ 82 | 'url' => '/github/sync_issue_status', 83 | 'params' => [ 84 | 'controller' => 'Github', 85 | 'action' => 'sync_issue_status', 86 | ], 87 | 'session' => $session, 88 | ]); 89 | 90 | $server = new Application(''); 91 | $response = $server->handle($request); 92 | if ($response->getStatusCode() === 200) { 93 | Log::debug( 94 | 'FINISHED: Job "' 95 | . 'github/sync_issue_status' 96 | . '" at ' 97 | . date('d-m-Y G:i:s (e)'), 98 | ['scope' => 'cron_jobs'] 99 | ); 100 | 101 | return 0; 102 | } 103 | 104 | Log::error( 105 | 'ERROR: Job "' 106 | . 'github/sync_issue_status' 107 | . '" at ' 108 | . date('d-m-Y G:i:s (e)') 109 | . ' response code: ' . $response->getStatusCode(), 110 | ['scope' => 'cron_jobs'] 111 | ); 112 | 113 | return 1; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/View/AppView.php: -------------------------------------------------------------------------------- 1 | (' 31 | . $entry['count'] . ')'; 32 | } 33 | 34 | $fullString = implode(', ', $values); 35 | $remaining = $totalCount - count($values); 36 | if ($remaining) { 37 | $fullString .= ' and ' . $remaining . ' others'; 38 | } 39 | 40 | return $fullString; 41 | } 42 | 43 | /** 44 | * @param mixed $reports The reports 45 | * @return string comma separated list 46 | */ 47 | public function createReportsLinks($reports): string 48 | { 49 | $links = []; 50 | foreach ($reports as $report) { 51 | $links[] = $this->linkToReport($report); 52 | } 53 | 54 | return implode(', ', $links); 55 | } 56 | 57 | /** 58 | * @param mixed $report The report 59 | * @return string HTML link 60 | */ 61 | public function linkToReport($report): string 62 | { 63 | $reportId = $report['id']; 64 | 65 | return '#' . $reportId . ''; 66 | } 67 | 68 | /** 69 | * @param mixed $incident The incident 70 | * @return string HTML link 71 | */ 72 | public function linkToReportFromIncident($incident): string 73 | { 74 | $reportId = $incident['report_id']; 75 | 76 | return '#' . $reportId . ''; 77 | } 78 | 79 | /** 80 | * @param mixed $incidents The incidents 81 | * @return string HTML 82 | */ 83 | public function getStacktracesForIncidents($incidents): string 84 | { 85 | $count = 0; 86 | $html = '
'; 87 | foreach ($incidents as $incident) { 88 | $class = 'well span5'; 89 | 90 | if ($count % 2 === 1) { 91 | $class .= ' '; 92 | } else { 93 | $html .= '
'; 94 | } 95 | 96 | $html .= $this->Incidents->getStacktrace($incident, $class); 97 | ++$count; 98 | } 99 | 100 | $html .= '
'; 101 | 102 | return $html; 103 | } 104 | 105 | /** 106 | * @return string HTML code 107 | */ 108 | public function getChartArray(string $arrayName, array $columns, array $relatedEntries): string 109 | { 110 | $finalData = []; 111 | foreach ($columns as $column) { 112 | $data = [ 113 | 'name' => $column, 114 | 'title' => Inflector::humanize($column), 115 | 'labels' => [], 116 | 'values' => [], 117 | ]; 118 | foreach ($relatedEntries[$column] as $entry) { 119 | $count = $entry['count']; 120 | $data['labels'][] = $entry[$column] . ' (' . $count . ')'; 121 | $data['values'][] = $count; 122 | } 123 | 124 | $finalData[] = $data; 125 | } 126 | 127 | return 'var ' . $arrayName . ' = ' 128 | . json_encode( 129 | $finalData, 130 | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE 131 | ) . ';'; 132 | } 133 | 134 | /** 135 | * @return string HTML 136 | */ 137 | public function getLineChartData(string $arrayName, array $entries): string 138 | { 139 | $data = []; 140 | foreach ($entries as $entry) { 141 | $data[] = [$entry['date'], $entry['count']]; 142 | } 143 | 144 | return 'var ' . $arrayName . ' = ' 145 | . json_encode( 146 | $data, 147 | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE 148 | ) . ';'; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /templates/Error/error400.php: -------------------------------------------------------------------------------- 1 | layout = 'dev_error'; 7 | 8 | $this->assign('title', $message); 9 | //$this->assign('templateName', 'error400'); 10 | 11 | $this->start('file'); 12 | ?> 13 | queryString)) : ?> 14 |

15 | SQL Query: 16 | queryString) ?> 17 |

18 | 19 | params)) : ?> 20 | SQL Query Params: 21 | params) ?> 22 | 23 | element('auto_table_warning') ?> 24 | end(); 31 | } 32 | 33 | ?> 34 |

35 |

36 | : 37 | ' . $url . '' 40 | ) ?> 41 |

42 | -------------------------------------------------------------------------------- /templates/Error/error500.php: -------------------------------------------------------------------------------- 1 | layout = 'dev_error'; 8 | 9 | $this->assign('title', $message); 10 | $this->assign('templateName', 'error500.php'); 11 | 12 | $this->start('file'); 13 | ?> 14 | queryString)) : ?> 15 |

16 | SQL Query: 17 | queryString) ?> 18 |

19 | 20 | params)) : ?> 21 | SQL Query Params: 22 | params) ?> 23 | 24 | element('auto_table_warning'); 26 | 27 | if (extension_loaded('xdebug') && stripos((string) ini_get('xdebug.mode'), 'coverage') !== false) { 28 | xdebug_print_function_stack(); 29 | } 30 | 31 | $this->end(); 32 | } 33 | 34 | ?> 35 |

36 |

37 | : 38 | 39 |

40 | -------------------------------------------------------------------------------- /templates/Github/create_issue.php: -------------------------------------------------------------------------------- 1 | Form->create( 3 | null, 4 | [ 5 | 'label' => ['class' => 'control-label'], 6 | 'div' => ['class' => 'control-group'], 7 | 'class' => 'input-xxlarge', 8 | 'between' => '
', 9 | 'after' => '
', 10 | 'class' => 'form-horizontal', 11 | ] 12 | ); 13 | ?> 14 |
15 | GitHub Issue 16 | Form->input( 18 | 'summary', 19 | [ 20 | 'placeholder' => 'Summary', 21 | 'value' => $error_name, 22 | ] 23 | ); 24 | ?> 25 | Form->input( 27 | 'description', 28 | [ 29 | 'placeholder' => 'Description', 30 | 'rows' => 10, 31 | 'value' => $error_message, 32 | ] 33 | ); 34 | ?> 35 | Form->input('labels', ['placeholder' => 'Labels']); ?> 36 | 37 |
38 |
39 |

40 | Heads up! 41 | The link to the error report is automatically added to the end of the description and an extra label is added to denote that the report is from the automated error reporting system 42 |

43 |
44 |
45 | 46 |
47 | 48 | -------------------------------------------------------------------------------- /templates/Incidents/view.php: -------------------------------------------------------------------------------- 1 |

Incident # 2 | [Report Reports->linkToReportFromIncident($incident); ?>] 3 |

4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | 70 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 89 | 90 | 91 | 92 | 95 | 96 |
Error Type 8 | 9 |
Error Name 14 | 15 |
Error Message 20 | 21 |
Submission Date 26 | 27 |
PMA Version 32 | 33 |
PHP Version 38 | 39 |
Browser 44 | 48 |
Useragent 53 | 54 |
Server Software 59 | 60 |
User OS 65 | 66 |
Locale 71 | 72 |
Script name 77 | 78 |
URI 83 | 84 | 85 | 86 | 87 | 88 |
Configuration storage enabled 93 | 94 |
97 | 98 | 99 |

Description submited by user:

100 |
101 |         
102 |     
103 | 104 | 105 |

Stacktrace:

106 | Incidents->getStacktrace($incident, 'well'); ?> 107 | 108 | 109 |

Microhistory:

110 |
111 |     
112 |         
113 |     
114 |         
120 |     
121 | 
122 | 123 | -------------------------------------------------------------------------------- /templates/Notifications/index.php: -------------------------------------------------------------------------------- 1 | 6 |
7 |

8 |
14 |
15 | 18 | 21 | 22 | Action for Selected Notifications: 23 | 24 | 25 |
26 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
SelectIDException NameMessagePMA VersionException TypeTime
60 |
61 |
62 | -------------------------------------------------------------------------------- /templates/Pages/home.php: -------------------------------------------------------------------------------- 1 | assign('title', 'Home'); ?> 2 |

phpMyAdmin Error Reporting System

3 |

This is the error reporting system for phpMyAdmin. Error reports are sent 4 | here where they are collected, stored, tagged and archived. They may also be 5 | siphoned off by the team developers to the bug tracker at github.com

6 |

You can view the error reports by logging in with GitHub. For editing the reports, you need to have commit access to the phpmyadmin repo on GitHub. 7 | To validate your status you need to click the login button on top and authorize us to validate your commit access status on GitHub.

8 | -------------------------------------------------------------------------------- /templates/Reports/index.php: -------------------------------------------------------------------------------- 1 | 'reports', 8 | 'action' => 'data_tables', 9 | ] 10 | ); 11 | ?> 12 |

Reports

13 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 45 | 46 | 56 | 66 | 76 | 83 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
SelectIDException NameMessageLocationPMA VersionStatusException TypeIncident count
32 | 34 | 36 | 44 | 47 | 55 | 57 | 65 | 67 | 75 | 77 | 82 | 84 |
104 | 105 | 106 | 107 |
108 | 111 | 114 | 115 | With selected Change state to: 116 | 117 | Form->select( 119 | 'state', 120 | $statuses, 121 | ['empty' => false] 122 | ); 123 | ?> 124 | 125 |
126 | 127 |
128 | -------------------------------------------------------------------------------- /templates/Stats/stats.php: -------------------------------------------------------------------------------- 1 |

Stats and Graphs

2 |
3 |
4 | 15 |
16 |
17 | 18 |

There were no incidents reported in this time period

19 | 20 | 21 | 22 | 23 | 87 | -------------------------------------------------------------------------------- /templates/element/flash/default.php: -------------------------------------------------------------------------------- 1 | 8 |
9 | -------------------------------------------------------------------------------- /templates/element/flash/error.php: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /templates/element/flash/success.php: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /templates/email/html/default.php: -------------------------------------------------------------------------------- 1 | 16 | ' . $line . "

\n"; 21 | endforeach; 22 | -------------------------------------------------------------------------------- /templates/email/html/report.php: -------------------------------------------------------------------------------- 1 | 21 |

22 | A new error report has been added on the phpMyAdmin's Error Reporting System. 23 |

24 |

25 | The details of the report are as follows: 26 |

27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 52 | 53 | 54 | 55 | 64 | 65 | 66 | 67 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 98 | 99 | 100 | 101 | 102 | 111 | 112 | 113 | 114 | 123 | 124 | 125 | 126 | 135 | 136 | 137 | 138 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 157 | 158 |
Error Type
Error Name
Error Message
PMA Versions 44 | Reports->entriesFromIncidents( 46 | $related_entries['pma_version'], 47 | $pma_version_distinct_count, 48 | 'pma_version' 49 | ); 50 | ?> 51 |
PHP Versions 56 | Reports->entriesFromIncidents( 58 | $related_entries['php_version'], 59 | $php_version_distinct_count, 60 | 'php_version' 61 | ); 62 | ?> 63 |
Browsers 68 | Reports->entriesFromIncidents( 70 | $related_entries['browser'], 71 | $browser_distinct_count, 72 | 'browser' 73 | ); 74 | ?> 75 |
Location
Line Number
Script Name 90 | Reports->entriesFromIncidents( 92 | $related_entries['script_name'], 93 | $script_name_distinct_count, 94 | 'script_name' 95 | ); 96 | ?> 97 |
Configuration Storage 103 | Reports->entriesFromIncidents( 105 | $related_entries['configuration_storage'], 106 | $configuration_storage_distinct_count, 107 | 'configuration_storage' 108 | ); 109 | ?> 110 |
Server Software 115 | Reports->entriesFromIncidents( 117 | $related_entries['server_software'], 118 | $server_software_distinct_count, 119 | 'server_software' 120 | ); 121 | ?> 122 |
User OS 127 | Reports->entriesFromIncidents( 129 | $related_entries['user_os'], 130 | $user_os_distinct_count, 131 | 'user_os' 132 | ); 133 | ?> 134 |
Locale 139 | Reports->entriesFromIncidents( 141 | $related_entries['locale'], 142 | $locale_distinct_count, 143 | 'locale' 144 | ); 145 | ?> 146 |
Incident Count
Submission Date 155 | 156 |
159 | 160 |

161 | You can view the detailed report at 162 | 166 | #. 167 |

168 | -------------------------------------------------------------------------------- /templates/email/text/default.php: -------------------------------------------------------------------------------- 1 | 16 | 16 | fetch('content'); 17 | -------------------------------------------------------------------------------- /templates/layout/default.php: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | 25 | Html->charset(); ?> 26 | 27 | <?= $this->fetch('title'); ?> 28 | phpMyAdmin - Error Reporting Server 29 | 30 | 31 | Html->meta('icon'); ?> 32 | 33 | 34 | Html->css($css_files); ?> 35 | 36 | 37 | Html->script($js_files); ?> 38 | 39 | 40 | Html->scriptBlock( 42 | 'var notifications_count = ' . $notif_count . ';', 43 | ['inline' => true] 44 | ); 45 | ?> 46 | 47 | 48 | 49 | 101 |
102 | 103 |
104 | Flash->render(); ?> 105 | fetch('content'); ?> 106 |
107 | 108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /templates/layout/email/html/default.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | New Report Notification - phpMyAdmin Error Reporting System 22 | 27 | 28 | 29 | fetch('content'); ?> 30 | 31 |

32 | This email was automatically sent by 33 | phpMyAdmin's 34 | Error Reporting System. 35 | 36 |

37 | 38 | -------------------------------------------------------------------------------- /templates/layout/email/text/default.php: -------------------------------------------------------------------------------- 1 | 17 | fetch('content'); ?> 18 | 19 | This email was sent using the CakePHP Framework, http://cakephp.org. 20 | -------------------------------------------------------------------------------- /templates/layout/error.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | Html->charset(); ?> 24 | 25 | <?php echo $this->fetch('title'); ?> 26 | phpMyAdmin - Error Reporting Server - Error 27 | 28 | 29 | Html->meta('icon'); ?> 30 | 31 | 32 | Html->css($css_files); 34 | } 35 | 36 | ?> 37 | 38 | 39 | 40 | 45 |
46 | 47 |
48 | fetch('content'); ?> 49 | Code:
50 | Url:
51 | 52 |
53 | 56 |
57 | 58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /templates/layout/flash.php: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | Html->charset(); ?> 21 | <?php echo $page_title; ?> 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 |

34 | 35 | 36 | -------------------------------------------------------------------------------- /templates/layout/js/default.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/layout/rss/default.php: -------------------------------------------------------------------------------- 1 | fetch('title'); 7 | endif; 8 | 9 | echo $this->Rss->document( 10 | $this->Rss->channel( 11 | [], 12 | $channel, 13 | $this->fetch('content') 14 | ) 15 | ); 16 | -------------------------------------------------------------------------------- /templates/layout/xml/default.php: -------------------------------------------------------------------------------- 1 | fetch('content'); 3 | -------------------------------------------------------------------------------- /tests/Fixture/DevelopersFixture.php: -------------------------------------------------------------------------------- 1 | 'developers']; 18 | 19 | /** 20 | * Records. 21 | * 22 | * @var array 23 | */ 24 | public $records = [ 25 | [ 26 | 'id' => 1, 27 | 'github_id' => 1, 28 | 'full_name' => 'Lorem ipsum dolor sit amet', 29 | 'email' => 'Lorem ipsum dolor sit amet', 30 | 'gravatar_id' => 'Lorem ipsum dolor sit amet', 31 | 'access_token' => 'Lorem ipsum dolor sit amet', 32 | 'created' => '2013-08-29 22:11:02', 33 | 'modified' => '2013-08-29 22:11:02', 34 | 'has_commit_access' => 1, 35 | ], 36 | [ 37 | 'id' => 2, 38 | 'github_id' => 2, 39 | 'full_name' => 'Lorem ipsum dolor sit amet', 40 | 'email' => 'Lorem ipsum dolor sit amet', 41 | 'gravatar_id' => 'Lorem ipsum dolor sit amet', 42 | 'access_token' => 'Lorem ipsum dolor sit amet', 43 | 'created' => '2013-08-29 22:11:02', 44 | 'modified' => '2013-08-29 22:11:02', 45 | 'has_commit_access' => 0, 46 | ], 47 | ]; 48 | } 49 | -------------------------------------------------------------------------------- /tests/Fixture/IncidentsFixture.php: -------------------------------------------------------------------------------- 1 | 'incidents']; 18 | 19 | /** 20 | * Records. 21 | * 22 | * @var array 23 | */ 24 | public $records = [ 25 | [ 26 | 'id' => 1, 27 | 'error_name' => 'Lorem ipsum dolor sit amet', 28 | 'error_message' => 'Lorem ipsum dolor sit amet', 29 | 'pma_version' => 'Lorem ipsum dolor sit amet', 30 | 'php_version' => '5.5', 31 | 'browser' => 'Lorem ipsum dolor sit amet', 32 | 'user_os' => 'Lorem ipsum dolor sit amet', 33 | 'locale' => 'Lorem ipsum dolor sit amet', 34 | 'server_software' => 'Lorem ipsum dolor sit amet', 35 | 'stackhash' => 'hash1', 36 | 'configuration_storage' => 'Lorem ipsum dolor sit amet', 37 | 'script_name' => 'Lorem ipsum dolor sit amet', 38 | 'steps' => 'Lorem ipsum dolor sit amet', 39 | 'stacktrace' => '[{"context": ["test"]}]', 40 | 'full_report' => '{"pma_version": "", "php_version": "","browser_name": "" 41 | , "browser_version": "", "user_agent_string": "", "server_software": 42 | "", "locale": "", "exception":{"uri":""}, "configuration_storage":"", 43 | "microhistory":""}', 44 | 'report_id' => 1, 45 | 'created' => '2013-08-29 18:10:01', 46 | 'modified' => '2013-08-29 18:10:01', 47 | ], 48 | [ 49 | 'id' => 2, 50 | 'error_name' => 'Lorem ipsum dolor sit amet', 51 | 'error_message' => 'Lorem ipsum dolor sit amet', 52 | 'pma_version' => 'Lorem ipsum dolor sit amet', 53 | 'php_version' => '5.3', 54 | 'browser' => 'Lorem ipsum dolor sit amet', 55 | 'user_os' => 'Lorem ipsum dolor sit amet', 56 | 'locale' => 'Lorem ipsum dolor sit amet', 57 | 'server_software' => 'Lorem ipsum dolor sit amet', 58 | 'stackhash' => 'hash4', 59 | 'configuration_storage' => 'Lorem ipsum dolor sit amet', 60 | 'script_name' => 'Lorem ipsum dolor sit amet', 61 | 'steps' => 'Lorem ipsum dolor sit amet', 62 | 'stacktrace' => '[{"context": ["test"]}]', 63 | 'full_report' => '{"pma_version": "1.2"}', 64 | 'report_id' => 4, 65 | 'created' => '2013-08-29 18:10:01', 66 | 'modified' => '2013-08-29 18:10:01', 67 | ], 68 | [ 69 | 'id' => 3, 70 | 'error_name' => 'Lorem ipsum dolor sit amet', 71 | 'error_message' => 'Lorem ipsum dolor sit amet', 72 | 'pma_version' => 'Lorem ipsum dolor sit amet', 73 | 'php_version' => '5.3', 74 | 'browser' => 'Lorem ipsum dolor sit amet', 75 | 'user_os' => 'Lorem ipsum dolor sit amet', 76 | 'locale' => 'Lorem ipsum dolor sit amet', 77 | 'server_software' => 'Lorem ipsum dolor sit amet', 78 | 'stackhash' => 'hash4', 79 | 'configuration_storage' => 'Lorem ipsum dolor sit amet', 80 | 'script_name' => 'Lorem ipsum dolor sit amet', 81 | 'steps' => null, 82 | 'stacktrace' => '[{"context": ["test"]}]', 83 | 'full_report' => '{"pma_version": "1.2"}', 84 | 'report_id' => 4, 85 | 'created' => '2013-08-29 18:10:00', 86 | 'modified' => '2013-08-29 18:10:00', 87 | ], 88 | 89 | [ 90 | 'id' => 4, 91 | 'error_name' => 'Lorem ipsum dolor sit amet', 92 | 'error_message' => 'Lorem ipsum dolor sit amet', 93 | 'pma_version' => 'Lorem ipsum dolor sit amet', 94 | 'php_version' => '5.3', 95 | 'browser' => 'Lorem ipsum dolor sit amet', 96 | 'user_os' => 'Lorem ipsum dolor sit amet', 97 | 'locale' => 'Lorem ipsum dolor sit amet', 98 | 'server_software' => 'Lorem ipsum dolor sit amet', 99 | 'stackhash' => 'hash3', 100 | 'configuration_storage' => 'Lorem ipsum dolor sit amet', 101 | 'script_name' => 'Lorem ipsum dolor sit amet', 102 | 'steps' => null, 103 | 'stacktrace' => '[{"context": ["test"]}]', 104 | 'full_report' => '{"pma_version": "1.2"}', 105 | 'report_id' => 2, 106 | 'created' => '2013-08-29 18:10:00', 107 | 'modified' => '2013-08-29 18:10:00', 108 | ], 109 | 110 | [ 111 | 'id' => 5, 112 | 'error_name' => 'Lorem ipsum dolor sit amet', 113 | 'error_message' => 'Lorem ipsum dolor sit amet', 114 | 'pma_version' => 'Lorem ipsum dolor sit amet', 115 | 'php_version' => '5.3', 116 | 'browser' => 'Lorem ipsum dolor sit amet', 117 | 'user_os' => 'Lorem ipsum dolor sit amet', 118 | 'locale' => 'Lorem ipsum dolor sit amet', 119 | 'server_software' => 'Lorem ipsum dolor sit amet', 120 | 'stackhash' => 'hash3', 121 | 'configuration_storage' => 'Lorem ipsum dolor sit amet', 122 | 'script_name' => 'Lorem ipsum dolor sit amet', 123 | 'steps' => null, 124 | 'stacktrace' => '[{"context": ["test"]}]', 125 | 'full_report' => '{"pma_version": "1.2"}', 126 | 'report_id' => 5, 127 | 'created' => '2013-08-29 18:10:00', 128 | 'modified' => '2013-08-29 18:10:00', 129 | ], 130 | ]; 131 | } 132 | -------------------------------------------------------------------------------- /tests/Fixture/NotificationsFixture.php: -------------------------------------------------------------------------------- 1 | 'notifications']; 18 | 19 | /** 20 | * Records. 21 | * 22 | * @var array 23 | */ 24 | public $records = [ 25 | [ 26 | 'id' => 1, 27 | 'developer_id' => 1, 28 | 'report_id' => 1, 29 | 'created' => '2014-01-01 07:05:09', 30 | 'modified' => '2014-01-01 07:05:09', 31 | ], 32 | [ 33 | 'id' => 2, 34 | 'developer_id' => 1, 35 | 'report_id' => 4, 36 | 'created' => '2014-01-02 07:05:09', 37 | 'modified' => '2014-01-03 07:05:09', 38 | ], 39 | [ 40 | 'id' => 3, 41 | 'developer_id' => 2, 42 | 'report_id' => 4, 43 | 'created' => '2014-07-02 07:05:09', 44 | 'modified' => '2014-07-03 07:05:09', 45 | ], 46 | ]; 47 | } 48 | -------------------------------------------------------------------------------- /tests/Fixture/ReportsFixture.php: -------------------------------------------------------------------------------- 1 | 'reports']; 18 | 19 | /** @var array */ 20 | public $records = [ 21 | [ 22 | 'id' => 1, 23 | 'error_message' => 'Lorem ipsum dolor sit amet', 24 | 'error_name' => 'error2', 25 | 'pma_version' => '4.0', 26 | 'status' => 'forwarded', 27 | 'location' => 'filename_1.php', 28 | 'linenumber' => 1, 29 | 'sourceforge_bug_id' => 1, 30 | 'related_to' => null, 31 | 'created' => '2013-08-28 21:47:17', 32 | 'modified' => '2013-08-28 21:47:17', 33 | ], 34 | [ 35 | 'id' => 2, 36 | 'error_message' => 'Lorem ipsum dolor sit amet', 37 | 'error_name' => 'error2', 38 | 'pma_version' => '4.0', 39 | 'status' => 'forwarded', 40 | 'location' => 'filename_2.php', 41 | 'linenumber' => 2, 42 | 'sourceforge_bug_id' => 2, 43 | 'related_to' => null, 44 | 'created' => '2013-08-28 21:47:17', 45 | 'modified' => '2013-08-28 21:47:17', 46 | ], 47 | [ 48 | 'id' => 4, 49 | 'error_message' => 'Lorem ipsum dolor sit amet', 50 | 'error_name' => 'error1', 51 | 'pma_version' => '3.8', 52 | 'status' => 'forwarded', 53 | 'location' => 'filename_3.js', 54 | 'linenumber' => 4, 55 | 'sourceforge_bug_id' => 4, 56 | 'related_to' => null, 57 | 'created' => '2013-08-28 21:47:17', 58 | 'modified' => '2013-08-28 21:47:17', 59 | ], 60 | [ 61 | 'id' => 5, 62 | 'error_message' => 'Lorem ipsum dolor sit amet', 63 | 'error_name' => 'error1', 64 | 'pma_version' => '3.8', 65 | 'status' => 'new', 66 | 'location' => 'filename_4.js', 67 | 'linenumber' => 3, 68 | 'sourceforge_bug_id' => null, 69 | 'related_to' => null, 70 | 'created' => '2013-08-28 21:47:17', 71 | 'modified' => '2013-08-28 21:47:17', 72 | ], 73 | ]; 74 | } 75 | -------------------------------------------------------------------------------- /tests/Fixture/comment_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "url": "https://api.github.com/repos/phpmyadmin/Hello-World/issues/comments/1", 4 | "html_url": "https://github.com/pma-bot/Hello-World/issues/1347#issuecomment-1", 5 | "body": "Me too", 6 | "user": { 7 | "login": "pma-bot", 8 | "id": 1, 9 | "avatar_url": "https://github.com/images/error/pma-bot_happy.gif", 10 | "gravatar_id": "", 11 | "url": "https://api.github.com/users/pma-bot", 12 | "html_url": "https://github.com/phpmyadmin", 13 | "followers_url": "https://api.github.com/users/pma-bot/followers", 14 | "following_url": "https://api.github.com/users/pma-bot/following{/other_user}", 15 | "gists_url": "https://api.github.com/users/pma-bot/gists{/gist_id}", 16 | "starred_url": "https://api.github.com/users/pma-bot/starred{/owner}{/repo}", 17 | "subscriptions_url": "https://api.github.com/users/pma-bot/subscriptions", 18 | "organizations_url": "https://api.github.com/users/pma-bot/orgs", 19 | "repos_url": "https://api.github.com/users/pma-bot/repos", 20 | "events_url": "https://api.github.com/users/pma-bot/events{/privacy}", 21 | "received_events_url": "https://api.github.com/users/pma-bot/received_events", 22 | "type": "User", 23 | "site_admin": false 24 | }, 25 | "created_at": "2011-04-14T16:00:49Z", 26 | "updated_at": "2011-04-14T16:00:49Z" 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixture/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phpmyadmin/error-reporting-server/e3b009546293dd94cdb1711c21bb803944bc5627/tests/Fixture/empty -------------------------------------------------------------------------------- /tests/Fixture/report_issue_16853_js.json: -------------------------------------------------------------------------------- 1 | { 2 | "pma_version": "5.2.0-dev", 3 | "browser_name": "FIREFOX", 4 | "browser_version": "90.0", 5 | "user_os": "Win", 6 | "server_software": "nginx/1.14.2", 7 | "user_agent_string": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0", 8 | "locale": "en", 9 | "configuration_storage": "disabled", 10 | "php_version": "7.4.14", 11 | "script_name": "index.php", 12 | "exception_type": "js", 13 | "exception": { 14 | "mode": "stack", 15 | "name": "TypeError", 16 | "message": "can't access property \"transaction\", db is null", 17 | "stack": [ 18 | { 19 | "func": "DesignerOfflineDB').append(data.message).dialog({" 74 | ], 75 | "uri": "js/dist/designer/move.js?v=5.2.0-dev", 76 | "scriptname": "js/dist/designer/move.js" 77 | }, 78 | { 79 | "func": "c", 80 | "line": "2", 81 | "column": "28327", 82 | "context": [ 83 | "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.o//...", 84 | "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof modul//...", 85 | "" 86 | ], 87 | "uri": "js/vendor/jquery/jquery.min.js?v=5.2.0-dev", 88 | "scriptname": "js/vendor/jquery/jquery.min.js" 89 | }, 90 | { 91 | "func": "fireWith", 92 | "line": "2", 93 | "column": "29072", 94 | "context": [ 95 | "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.o//...", 96 | "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof modul//...", 97 | "" 98 | ], 99 | "uri": "js/vendor/jquery/jquery.min.js?v=5.2.0-dev", 100 | "scriptname": "js/vendor/jquery/jquery.min.js" 101 | }, 102 | { 103 | "func": "l", 104 | "line": "2", 105 | "column": "79901", 106 | "context": [ 107 | "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.o//...", 108 | "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof modul//...", 109 | "" 110 | ], 111 | "uri": "js/vendor/jquery/jquery.min.js?v=5.2.0-dev", 112 | "scriptname": "js/vendor/jquery/jquery.min.js" 113 | }, 114 | { 115 | "func": "o/<", 116 | "line": "2", 117 | "column": "82355", 118 | "context": [ 119 | "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.o//...", 120 | "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof modul//...", 121 | "" 122 | ], 123 | "uri": "js/vendor/jquery/jquery.min.js?v=5.2.0-dev", 124 | "scriptname": "js/vendor/jquery/jquery.min.js" 125 | } 126 | ], 127 | "uri": "index.php?route=%2Fdatabase%2Fdesigner" 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/Fixture/report_js.json: -------------------------------------------------------------------------------- 1 | { 2 | "exception": { 3 | "mode": "stack", 4 | "name": "ReferenceError", 5 | "message": "a is not defined", 6 | "stack": [ 7 | { 8 | "func": "PMA_exception", 9 | "line": 312, 10 | "column": "5", 11 | "context": [ 12 | " }", 13 | "", 14 | "}", 15 | "", 16 | "function PMA_exception() {", 17 | " a()", 18 | "}", 19 | "", 20 | "function exception(){", 21 | " a()", 22 | "}" 23 | ], 24 | "filename": "error.js" 25 | }, 26 | { 27 | "func": "new_func", 28 | "line": 257, 29 | "column": "33", 30 | "context": [ 31 | " */", 32 | " wrap_function: function(func) {", 33 | " if (!func.wrapped) {", 34 | " var new_func = function(){", 35 | " try {", 36 | " return func.apply(this, arguments)", 37 | " } catch(x) {", 38 | " TraceKit.report(x);", 39 | " }", 40 | " };", 41 | " new_func.wrapped = true;" 42 | ], 43 | "filename": "error_report.js" 44 | } 45 | ], 46 | "useragent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36", 47 | "uri": "tbl_structure.php?target=" 48 | }, 49 | "steps": "