├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── changelog.md ├── composer.json ├── phpunit.xml ├── readme.md ├── src ├── Data │ ├── Repositories │ │ ├── Agent.php │ │ ├── Connection.php │ │ ├── Cookie.php │ │ ├── Device.php │ │ ├── Domain.php │ │ ├── Error.php │ │ ├── Event.php │ │ ├── EventLog.php │ │ ├── GeoIp.php │ │ ├── Language.php │ │ ├── Log.php │ │ ├── Path.php │ │ ├── Query.php │ │ ├── QueryArgument.php │ │ ├── Referer.php │ │ ├── Repository.php │ │ ├── RepositoryInterface.php │ │ ├── Route.php │ │ ├── RoutePath.php │ │ ├── RoutePathParameter.php │ │ ├── Session.php │ │ ├── SqlQuery.php │ │ ├── SqlQueryBinding.php │ │ ├── SqlQueryBindingParameter.php │ │ ├── SqlQueryLog.php │ │ ├── SystemClass.php │ │ └── User.php │ ├── RepositoryManager.php │ └── RepositoryManagerInterface.php ├── Eventing │ └── EventStorage.php ├── Repositories │ └── Message.php ├── Services │ └── Authentication.php ├── Support │ ├── Cache.php │ ├── CrawlerDetector.php │ ├── Exceptions │ │ ├── CompileError.php │ │ ├── CompileWarning.php │ │ ├── CoreError.php │ │ ├── CoreWarning.php │ │ ├── Deprecated.php │ │ ├── Error.php │ │ ├── ExceptionFactory.php │ │ ├── Handler.php │ │ ├── Notice.php │ │ ├── Parse.php │ │ ├── RecoverableError.php │ │ ├── Strict.php │ │ ├── UserDeprecated.php │ │ ├── UserError.php │ │ ├── UserNotice.php │ │ ├── UserWarning.php │ │ └── Warning.php │ ├── Filesystem.php │ ├── LanguageDetect.php │ ├── Migration.php │ ├── Minutes.php │ ├── MobileDetect.php │ ├── RefererParser.php │ └── UserAgentParser.php ├── Tracker.php ├── Vendor │ └── Laravel │ │ ├── Artisan │ │ ├── Base.php │ │ ├── Tables.php │ │ └── UpdateGeoIp.php │ │ ├── Controllers │ │ └── Stats.php │ │ ├── Facade.php │ │ ├── Middlewares │ │ └── Tracker.php │ │ ├── Models │ │ ├── Agent.php │ │ ├── Base.php │ │ ├── Connection.php │ │ ├── Cookie.php │ │ ├── Device.php │ │ ├── Domain.php │ │ ├── Error.php │ │ ├── Event.php │ │ ├── EventLog.php │ │ ├── GeoIp.php │ │ ├── Language.php │ │ ├── Log.php │ │ ├── Path.php │ │ ├── Query.php │ │ ├── QueryArgument.php │ │ ├── Referer.php │ │ ├── RefererSearchTerm.php │ │ ├── Route.php │ │ ├── RoutePath.php │ │ ├── RoutePathParameter.php │ │ ├── Session.php │ │ ├── SqlQuery.php │ │ ├── SqlQueryBinding.php │ │ ├── SqlQueryBindingParameter.php │ │ ├── SqlQueryLog.php │ │ ├── SystemClass.php │ │ └── User.php │ │ ├── ServiceProvider.php │ │ └── Support │ │ └── Session.php ├── config │ ├── .gitkeep │ └── config.php ├── lang │ └── en │ │ └── tracker.php ├── migrations │ ├── 2015_03_07_311070_create_tracker_paths_table.php │ ├── 2015_03_07_311071_create_tracker_queries_table.php │ ├── 2015_03_07_311072_create_tracker_queries_arguments_table.php │ ├── 2015_03_07_311073_create_tracker_routes_table.php │ ├── 2015_03_07_311074_create_tracker_routes_paths_table.php │ ├── 2015_03_07_311075_create_tracker_route_path_parameters_table.php │ ├── 2015_03_07_311076_create_tracker_agents_table.php │ ├── 2015_03_07_311077_create_tracker_cookies_table.php │ ├── 2015_03_07_311078_create_tracker_devices_table.php │ ├── 2015_03_07_311079_create_tracker_domains_table.php │ ├── 2015_03_07_311080_create_tracker_referers_table.php │ ├── 2015_03_07_311081_create_tracker_geoip_table.php │ ├── 2015_03_07_311082_create_tracker_sessions_table.php │ ├── 2015_03_07_311083_create_tracker_errors_table.php │ ├── 2015_03_07_311084_create_tracker_system_classes_table.php │ ├── 2015_03_07_311085_create_tracker_log_table.php │ ├── 2015_03_07_311086_create_tracker_events_table.php │ ├── 2015_03_07_311087_create_tracker_events_log_table.php │ ├── 2015_03_07_311088_create_tracker_sql_queries_table.php │ ├── 2015_03_07_311089_create_tracker_sql_query_bindings_table.php │ ├── 2015_03_07_311090_create_tracker_sql_query_bindings_parameters_table.php │ ├── 2015_03_07_311091_create_tracker_sql_queries_log_table.php │ ├── 2015_03_07_311092_create_tracker_connections_table.php │ ├── 2015_03_07_311093_create_tracker_tables_relations.php │ ├── 2015_03_13_311094_create_tracker_referer_search_term_table.php │ ├── 2015_03_13_311095_add_tracker_referer_columns.php │ ├── 2015_11_23_311096_add_tracker_referer_column_to_log.php │ ├── 2015_11_23_311097_create_tracker_languages_table.php │ ├── 2015_11_23_311098_add_language_id_column_to_sessions.php │ ├── 2015_11_23_311099_add_tracker_language_foreign_key_to_sessions.php │ ├── 2015_11_23_311100_add_nullable_to_tracker_error.php │ ├── 2017_01_31_311101_fix_agent_name.php │ ├── 2017_06_20_311102_add_agent_name_hash.php │ └── 2017_12_13_150000_fix_query_arguments.php └── views │ ├── _dataTable.blade.php │ ├── _datatables.blade.php │ ├── _summaryPiechart.blade.php │ ├── errors.blade.php │ ├── events.blade.php │ ├── html.blade.php │ ├── index.blade.php │ ├── layout.blade.php │ ├── log.blade.php │ ├── message.blade.php │ ├── screenshots │ ├── errors.png │ ├── events.png │ ├── summary.png │ ├── users.png │ └── visits.png │ ├── summary.blade.php │ └── users.blade.php ├── tests └── .gitkeep └── upgrading.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **System** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | .DS_Store 4 | .idea 5 | .idea/ 6 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | # .scrutinizer.yml 2 | tools: 3 | php_cs_fixer: 4 | config: { level: psr2 } # or psr1 if you would just like to get fixes for PSR1 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | - COMPOSER_MEMORY_LIMIT=-1 5 | 6 | php: 7 | - 5.3 8 | - 5.4 9 | - 5.5 10 | - 5.6 11 | - 7.0 12 | - 7.1 13 | - 7.2 14 | - 7.3 15 | - 7.4 16 | - hhvm 17 | 18 | install: 19 | - travis_retry composer install --no-interaction --prefer-source 20 | 21 | script: 22 | - phpunit --verbose 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) Antonio Carlos Ribeiro 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 furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | 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 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [3.4.1] - 2019-09-11 4 | ### Fixed 5 | - Removed support for Laravel 4 6 | 7 | ## [3.4.0] - 2019-09-10 8 | ### New 9 | - Added support for Laravel 6 10 | 11 | ## [3.3] - 2018-03-19 12 | ### Fixed 13 | - Laravel 5.6 compatibility 14 | 15 | ## [3.1.8] - 2017-07-31 16 | ### New 17 | - Added a config to enable/disable console logging 18 | 19 | ## [3.1.7] - 2017-07-31 20 | ### New 21 | - Disable console logging 22 | 23 | ## [3.1.6] - 2017-07-31 24 | ### Fixed 25 | - Fix wrong date column being used in log view 26 | 27 | ## [3.1.5] - 2017-07-30 28 | ### Fixed 29 | - Fix multiple sessions for the same request 30 | 31 | ## [3.1.4] - 2017-06-25 32 | ### Fixed 33 | - Browser Agent name is now being hashed 34 | 35 | ## [3.1.3] - 2017-06-20 36 | ### New 37 | - Show untrackable items in log 38 | 39 | 40 | ## [3.1.2] - 2017-01-31 41 | ### New 42 | - Upgraded datatables 43 | 44 | ## [3.1.1] - 2017-01-31 45 | ### New 46 | - Upgraded to SBAdmin 2 3.3.7+1 47 | 48 | 49 | ## [3.1.0] - 2017-01-31 50 | ### New 51 | - Upgrade to Laravel 5.4 52 | 53 | ## [3.0.0] - 2016-08-24 54 | ### New 55 | - Cache 56 | 57 | ## [2.0.9] - 2016-08-23 58 | ### Added 59 | - Support for multiple authentication drivers 60 | ### Fixed 61 | - Migrations for languages 62 | - Query log for Laravel 5.2 and Laravel 5.3 63 | 64 | ## [2.0.6] - 2016-08-23 65 | ### Added 66 | - Changed to PSR-2 using StyleCI 67 | 68 | ## [2.0.5] - 2016-08-23 69 | ### Added 70 | - Add onlineUsers method 71 | 72 | ## [2.0.4] - 2016-08-22 73 | ### Fixed 74 | - Route name on log 75 | - Sessions for tracking visitors 76 | ### Added 77 | - Log languages (please check upgrade.md) 78 | - Localization 79 | 80 | ## [2.0.3] - 2016-08-22 81 | ### Fixed 82 | - Routes for stats are now being correctly ignored on Laravel 5.x 83 | 84 | ## [2.0.2] - 2016-08-20 85 | ### Add 86 | - Support for Laravel 5.3 87 | - Middleware for Laravel 5.x 88 | - Allow table to be prefixed 89 | ### Fixed 90 | - SB Admin 2 install instructions 91 | 92 | ## [2.0.1] - 2016-08-18 93 | ### Changed 94 | - Upgraded Ramsey UUID to V3 95 | 96 | ## [2.0.0] - 2015-11-23 97 | ### Notes ! This is a breaking change 98 | - You must execute 99 | php artisan tracker:tables 100 | then 101 | php artisan migrate 102 | ### Added 103 | - Referer to tracker_log 104 | - Method Tracker::userDevices() 105 | - Range filter on data access methods 106 | ### Fixed 107 | - Prevent migrations files from being overwritten 108 | 109 | ## [1.0.8] - 2015-11-23 110 | ### Fixed 111 | - Event log error when opening stats 112 | ### Added 113 | - Must be admin to access stats 114 | 115 | ## [1.0.7] - 2015-11-22 116 | ### Added 117 | - Support for GeoIp2 118 | ### Changed 119 | - Upgraded SB Admin 2 to 1.0.7 120 | 121 | ## [1.0.6] - 2015-11-21 122 | ### Added 123 | - Referer parsing, to store marketing attribution data (medium, source and search term) 124 | - Tracker::trackVisit() 125 | - Tracker::trackEvent() 126 | ### Changed 127 | - Move to UA-PHP instead of PragmaRX/UaParser 128 | - Using a better bot detector 129 | ### Fixed 130 | - Correctly get the application url 131 | - Migrations for MySQL 132 | - Sessions now change when a different users logs in 133 | - isPhone compatibility 134 | ### Changed 135 | - Use ua-php instead of ua-parser directly 136 | - No need to execute tracker:updateparser during install anymore 137 | 138 | ## [1.0.5] - 2015-03-10 139 | ### Added 140 | - Middleware filter to routes 141 | 142 | ## [1.0.4] - 2015-03-10 143 | ### Fixed 144 | - Console exception handler 145 | 146 | ## [1.0.3] - 2015-03-08 147 | ### Fixed 148 | - Datatables bug 149 | 150 | ## [1.0.2] - 2015-03-07 151 | ### Fixed 152 | - Migrations for MySQL 153 | 154 | ## [1.0.1] - 2015-03-06 155 | ### Changed 156 | - Use a stable version of datatables 157 | 158 | ## [1.0.0] - 2015-02-21 159 | ### Changed 160 | - Added support for Laravel 5 161 | 162 | ## [0.5.2] - 2014-07-06 163 | ### Fixed 164 | - HTTP cache by removing PHP session_start 165 | 166 | ## [0.5.1] - 2014-07-03 167 | ### Added 168 | - A 'Today' filter option on Stats Panel 169 | - All stats tables are now Google Charts Tables (paginated) 170 | 171 | ## [0.5.0] - 2014-07-02 172 | ### Changed 173 | - Moved pie charts from flot to Google Charts 174 | 175 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pragmarx/tracker", 3 | 4 | "description": "A Laravel Visitor Tracker", 5 | 6 | "keywords": ["tracker", "tracking", "visitor", "logging", "user agent", "mobile detection", "laravel", "pragmarx"], 7 | 8 | "license": "MIT", 9 | 10 | "authors": [ 11 | { 12 | "name": "Antonio Carlos Ribeiro", 13 | "email": "acr@antoniocarlosribeiro.com", 14 | "role": "Creator & Designer" 15 | } 16 | ], 17 | 18 | "require": { 19 | "php": ">=7.0", 20 | "doctrine/dbal": "^2.6", 21 | "laravel/framework": "~5|~6|~7|~8", 22 | "pragmarx/support": "~0.6|~0.7|~0.8|~0.9", 23 | "ramsey/uuid": "^3 || ^4", 24 | "jenssegers/agent": "~2.1", 25 | "ua-parser/uap-php" : "~3.4", 26 | "pragmarx/datatables": "^1.4.12", 27 | "snowplow/referer-parser": "~0.1", 28 | "jaybizzle/crawler-detect": "~1.0", 29 | "psr/log": "~1.0" 30 | }, 31 | 32 | "suggest": { 33 | "geoip/geoip": "~1.14", 34 | "geoip2/geoip2": "~2.0" 35 | }, 36 | 37 | "require-dev": { 38 | "mockery/mockery": "~0.8" 39 | }, 40 | 41 | "autoload": { 42 | "psr-4": { 43 | "PragmaRX\\Tracker\\": "src/" 44 | } 45 | }, 46 | 47 | "extra": { 48 | "branch-alias": { 49 | "dev-master": "2.1.x-dev" 50 | }, 51 | "laravel": { 52 | "providers": [ 53 | "PragmaRX\\Tracker\\Vendor\\Laravel\\ServiceProvider" 54 | ], 55 | "aliases": { 56 | "Tracker": "PragmaRX\\Tracker\\Vendor\\Laravel\\Facade" 57 | } 58 | } 59 | }, 60 | 61 | "minimum-stability": "dev", 62 | "prefer-stable": true 63 | } 64 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Data/Repositories/Agent.php: -------------------------------------------------------------------------------- 1 | config = $config; 21 | 22 | $this->request = $request; 23 | 24 | $this->cookieJar = $cookieJar; 25 | 26 | parent::__construct($model); 27 | } 28 | 29 | public function getId() 30 | { 31 | if (!$this->config->get('store_cookie_tracker')) { 32 | return; 33 | } 34 | 35 | if (!$cookie = $this->request->cookie($this->config->get('tracker_cookie_name'))) { 36 | $cookie = UUID::uuid4()->toString(); 37 | 38 | $this->cookieJar->queue($this->config->get('tracker_cookie_name'), $cookie, 0); 39 | } 40 | 41 | return $this->findOrCreate(['uuid' => $cookie]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Data/Repositories/Device.php: -------------------------------------------------------------------------------- 1 | getMessage()) { 10 | return $message; 11 | } 12 | 13 | return $message; 14 | } 15 | 16 | public function getCodeFromThrowable($throwable) 17 | { 18 | if (method_exists($throwable, 'getCode') && $code = $throwable->getCode()) { 19 | return $code; 20 | } 21 | 22 | if (method_exists($throwable, 'getStatusCode') && $code = $throwable->getStatusCode()) { 23 | return $code; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Data/Repositories/Event.php: -------------------------------------------------------------------------------- 1 | eventStorage = $eventStorage; 46 | 47 | $this->eventLogRepository = $eventLogRepository; 48 | 49 | $this->systemClassRepository = $systemClassRepository; 50 | 51 | $this->logRepository = $logRepository; 52 | 53 | $this->config = $config; 54 | } 55 | 56 | public function logEvents() 57 | { 58 | if (!$this->logRepository->getCurrentLogId()) { 59 | return; 60 | } 61 | 62 | foreach ($this->eventStorage->popAll() as $event) { 63 | if ($this->isLoggableEvent($event)) { 64 | $this->logEvent($event); 65 | } 66 | } 67 | } 68 | 69 | private function isLoggableEvent($event) 70 | { 71 | $forbidden = $this->config->get('do_not_log_events'); 72 | 73 | // Illuminate Query may cause infinite recursion 74 | $forbidden[] = 'illuminate.query'; 75 | 76 | return 77 | $event['event'] != $this->getObject($event['object']) 78 | 79 | && 80 | 81 | !in_array_wildcard($event['event'], $forbidden) 82 | 83 | && 84 | 85 | !$this->config->get('log_only_events') 86 | || in_array($event['event'], $this->config->get('log_only_events')); 87 | } 88 | 89 | public function logEvent($event) 90 | { 91 | $event = $this->makeEventArray($event); 92 | 93 | $evenId = $this->getEventId($event); 94 | 95 | if ($evenId) { 96 | $objectName = $this->getObjectName($event); 97 | 98 | $classId = $this->getClassId($objectName); 99 | 100 | $this->eventLogRepository->create( 101 | [ 102 | 'log_id' => $this->logRepository->getCurrentLogId(), 103 | 'event_id' => $evenId, 104 | 'class_id' => $classId, 105 | ] 106 | ); 107 | } 108 | } 109 | 110 | private function getObject($object) 111 | { 112 | if (is_object($object)) { 113 | $object = get_class($object); 114 | } elseif (is_array($object)) { 115 | $object = serialize($object); 116 | } 117 | 118 | return $object; 119 | } 120 | 121 | public function getAll($minutes, $results) 122 | { 123 | return $this->getModel()->allInThePeriod($minutes, $results); 124 | } 125 | 126 | /** 127 | * Get the object name from an event. 128 | * 129 | * @param $event 130 | * 131 | * @return null|string 132 | */ 133 | private function getObjectName($event) 134 | { 135 | return isset($event['object']) 136 | ? $this->getObject($event['object']) 137 | : null; 138 | } 139 | 140 | /** 141 | * Get the system class id by object name. 142 | * 143 | * @param null|string $objectName 144 | * 145 | * @return null 146 | */ 147 | private function getClassId($objectName) 148 | { 149 | return $objectName 150 | ? $this->systemClassRepository->findOrCreate( 151 | ['name' => $objectName], 152 | ['name'] 153 | ) 154 | : null; 155 | } 156 | 157 | /** 158 | * Get the event id. 159 | * 160 | * @param $event 161 | * 162 | * @return null 163 | */ 164 | private function getEventId($event) 165 | { 166 | return $event['event'] 167 | ? $this->findOrCreate( 168 | ['name' => $event['event']], 169 | ['name'] 170 | ) 171 | : null; 172 | } 173 | 174 | private function makeEventArray($event) 175 | { 176 | if (is_string($event)) { 177 | $event = [ 178 | 'event' => $event, 179 | 'object' => null, 180 | ]; 181 | } 182 | 183 | return $event; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Data/Repositories/EventLog.php: -------------------------------------------------------------------------------- 1 | route_path_id = $route_path_id; 15 | } 16 | 17 | $model = $this->getModel(); 18 | 19 | if ($model->id && $this->route_path_id && !$model->route_path_id) { 20 | $model->route_path_id = $this->route_path_id; 21 | 22 | $model->save(); 23 | } 24 | 25 | return $model; 26 | } 27 | 28 | public function updateError($error_id) 29 | { 30 | $model = $this->getModel(); 31 | 32 | if ($model->id) { 33 | $model->error_id = $error_id; 34 | 35 | $model->save(); 36 | } 37 | 38 | return $model; 39 | } 40 | 41 | public function bySession($sessionId, $results = true) 42 | { 43 | $query = $this 44 | ->getModel() 45 | ->where('session_id', $sessionId)->orderBy('updated_at', 'desc'); 46 | 47 | if ($results) { 48 | return $query->get(); 49 | } 50 | 51 | return $query; 52 | } 53 | 54 | /** 55 | * @return null 56 | */ 57 | public function getCurrentLogId() 58 | { 59 | return $this->currentLogId; 60 | } 61 | 62 | /** 63 | * @param null|$currentLogId 64 | * 65 | * @return null|int 66 | */ 67 | public function setCurrentLogId($currentLogId) 68 | { 69 | return $this->currentLogId = $currentLogId; 70 | } 71 | 72 | public function createLog($data) 73 | { 74 | $log = $this->create($data); 75 | 76 | $this->updateRoute(); 77 | 78 | return $this->setCurrentLogId($log->id); 79 | } 80 | 81 | public function pageViews($minutes, $results) 82 | { 83 | return $this->getModel()->pageViews($minutes, $results); 84 | } 85 | 86 | public function pageViewsByCountry($minutes, $results) 87 | { 88 | return $this->getModel()->pageViewsByCountry($minutes, $results); 89 | } 90 | 91 | public function getErrors($minutes, $results) 92 | { 93 | return $this->getModel()->errors($minutes, $results); 94 | } 95 | 96 | public function allByRouteName($name, $minutes = null) 97 | { 98 | return $this->getModel()->allByRouteName($name, $minutes); 99 | } 100 | 101 | public function delete() 102 | { 103 | $this->currentLogId = null; 104 | 105 | $this->getModel()->delete(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Data/Repositories/Path.php: -------------------------------------------------------------------------------- 1 | refererParser = $refererParser; 34 | 35 | $this->currentUrl = $currentUrl; 36 | 37 | $this->searchTermModel = $searchTermModel; 38 | } 39 | 40 | /** 41 | * @param $refererUrl 42 | * @param $host 43 | * @param $domain_id 44 | * 45 | * @return mixed 46 | */ 47 | public function store($refererUrl, $host, $domain_id) 48 | { 49 | $attributes = [ 50 | 'url' => $refererUrl, 51 | 'host' => $host, 52 | 'domain_id' => $domain_id, 53 | 'medium' => null, 54 | 'source' => null, 55 | 'search_terms_hash' => null, 56 | ]; 57 | 58 | $parsed = $this->refererParser->parse($refererUrl, $this->currentUrl); 59 | 60 | if ($parsed->isKnown()) { 61 | $attributes['medium'] = $parsed->getMedium(); 62 | 63 | $attributes['source'] = $parsed->getSource(); 64 | 65 | $attributes['search_terms_hash'] = sha1($parsed->getSearchTerm()); 66 | } 67 | 68 | $referer = $this->findOrCreate( 69 | $attributes, 70 | ['url', 'search_terms_hash'] 71 | ); 72 | 73 | $referer = $this->find($referer); 74 | 75 | if ($parsed->isKnown()) { 76 | $this->storeSearchTerms($referer, $parsed); 77 | } 78 | 79 | return $referer->id; 80 | } 81 | 82 | private function storeSearchTerms($referer, $parsed) 83 | { 84 | foreach (explode(' ', $parsed->getSearchTerm()) as $term) { 85 | $this->findOrCreate( 86 | [ 87 | 'referer_id' => $referer->id, 88 | 'search_term' => $term, 89 | ], 90 | ['referer_id', 'search_term'], 91 | $created, 92 | $this->searchTermModel 93 | ); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Data/Repositories/Repository.php: -------------------------------------------------------------------------------- 1 | model = $model; 27 | 28 | $this->className = get_class($model); 29 | 30 | $this->connection = $this->getModel()->getConnectionName(); 31 | 32 | $this->cache = app('tracker.cache'); 33 | } 34 | 35 | public function where($key, $operation, $value = null) 36 | { 37 | $this->builder = $this->builder ?: $this->newQuery(); 38 | 39 | $this->builder = $this->builder->where($key, $operation, $value = null); 40 | 41 | return $this; 42 | } 43 | 44 | public function first() 45 | { 46 | $this->result = $this->builder->first(); 47 | 48 | return $this->result ? $this : null; 49 | } 50 | 51 | public function find($id) 52 | { 53 | list($model, $cacheKey) = $this->cache->findCached($id, null, $this->className); 54 | 55 | if (!$model) { 56 | $model = $this->newQuery(); 57 | 58 | if ($this->relations) { 59 | $model->with($this->relations); 60 | } 61 | 62 | if ($model = $model->find($id)) { 63 | $this->cache->cachePut($cacheKey, $model); 64 | } 65 | } 66 | 67 | $this->model = $model; 68 | $this->result = $model; 69 | 70 | return $model; 71 | } 72 | 73 | public function create($attributes, $model = null) 74 | { 75 | $model = $model && !$model->exists() ? $model : $this->newModel($model); 76 | 77 | foreach ($attributes as $attribute => $value) { 78 | if (in_array($attribute, $model->getFillable())) { 79 | $model->{$attribute} = $value; 80 | } 81 | } 82 | 83 | $model->save(); 84 | 85 | return $model; 86 | } 87 | 88 | public function getId() 89 | { 90 | return $this->getAttribute('id'); 91 | } 92 | 93 | /** 94 | * @param string $attribute 95 | */ 96 | public function getAttribute($attribute) 97 | { 98 | return $this->result ? $this->result->{$attribute} : null; 99 | } 100 | 101 | public function setAttribute($attribute, $value) 102 | { 103 | return $this->result->{$attribute} = $value; 104 | } 105 | 106 | public function save() 107 | { 108 | return $this->result->save(); 109 | } 110 | 111 | /** 112 | * @param string[] $keys 113 | */ 114 | public function findOrCreate($attributes, $keys = null, &$created = false, $otherModel = null) 115 | { 116 | list($model, $cacheKey) = $this->cache->findCached($attributes, $keys, $this->className); 117 | 118 | if (!$model) { 119 | $model = $this->newQuery($otherModel); 120 | 121 | $keys = $keys ?: array_keys($attributes); 122 | 123 | foreach ($keys as $key) { 124 | $model = $model->where($key, $attributes[$key]); 125 | } 126 | 127 | if (!$model = $model->first()) { 128 | $model = $this->create($attributes, $otherModel); 129 | 130 | $created = true; 131 | } 132 | 133 | $this->cache->cachePut($cacheKey, $model); 134 | } 135 | 136 | $this->model = $model; 137 | 138 | return $model->id; 139 | } 140 | 141 | public function getModel() 142 | { 143 | if ($this->model instanceof Illuminate\Database\Eloquent\Builder) { 144 | $this->model = new $this->className(); 145 | } 146 | 147 | if ($this->connection) { 148 | $this->model->setConnection($this->connection); 149 | } 150 | 151 | return $this->model; 152 | } 153 | 154 | public function newModel($model = null) 155 | { 156 | $className = $this->className; 157 | 158 | if ($model) { 159 | $className = get_class($model); 160 | } 161 | 162 | $this->model = new $className(); 163 | 164 | return $this->getModel(); 165 | } 166 | 167 | public function newQuery($model = null) 168 | { 169 | $className = $this->className; 170 | 171 | if ($model) { 172 | $className = get_class($model); 173 | } 174 | 175 | $this->builder = new $className(); 176 | 177 | if ($this->connection) { 178 | $this->builder = $this->builder->on($this->connection); 179 | } 180 | 181 | return $this->builder->newQuery(); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/Data/Repositories/RepositoryInterface.php: -------------------------------------------------------------------------------- 1 | config = $config; 14 | } 15 | 16 | public function isTrackable($route) 17 | { 18 | $forbidden = $this->config->get('do_not_track_routes'); 19 | 20 | return 21 | !$forbidden || 22 | !$route->currentRouteName() || 23 | !in_array_wildcard($route->currentRouteName(), $forbidden); 24 | } 25 | 26 | public function pathIsTrackable($path) 27 | { 28 | $forbidden = $this->config->get('do_not_track_paths'); 29 | 30 | return 31 | !$forbidden || 32 | empty($path) || 33 | !in_array_wildcard($path, $forbidden); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Data/Repositories/RoutePath.php: -------------------------------------------------------------------------------- 1 | config = $config; 23 | 24 | $this->session = $session; 25 | 26 | parent::__construct($model); 27 | } 28 | 29 | public function findByUuid($uuid) 30 | { 31 | list($model, $cacheKey) = $this->cache->findCached($uuid, 'uuid', 'PragmaRX\Tracker\Vendor\Laravel\Models\Session'); 32 | 33 | if (!$model) { 34 | $model = $this->newQuery()->where('uuid', $uuid)->with($this->relations)->first(); 35 | 36 | $this->cache->cachePut($cacheKey, $model); 37 | } 38 | 39 | return $model; 40 | } 41 | 42 | public function getCurrentId($sessionInfo) 43 | { 44 | $this->setSessionData($sessionInfo); 45 | 46 | return $this->sessionGetId($sessionInfo); 47 | } 48 | 49 | public function setSessionData($sessinInfo) 50 | { 51 | $this->generateSession($sessinInfo); 52 | 53 | if ($this->sessionIsKnownOrCreateSession()) { 54 | $this->ensureSessionDataIsComplete(); 55 | } 56 | } 57 | 58 | private function generateSession($sessionInfo) 59 | { 60 | $this->sessionInfo = $sessionInfo; 61 | 62 | if (!$this->sessionIsReliable()) { 63 | $this->regenerateSystemSession(); 64 | } 65 | 66 | $this->checkSessionUuid(); 67 | } 68 | 69 | private function sessionIsReliable() 70 | { 71 | $data = $this->getSessionData(); 72 | 73 | if (isset($data['user_id'])) { 74 | if ($data['user_id'] !== $this->sessionInfo['user_id']) { 75 | return false; 76 | } 77 | } 78 | 79 | if (isset($data['client_ip'])) { 80 | if ($data['client_ip'] !== $this->sessionInfo['client_ip']) { 81 | return false; 82 | } 83 | } 84 | 85 | if (isset($data['user_agent'])) { 86 | if ($data['user_agent'] !== $this->sessionInfo['user_agent']) { 87 | return false; 88 | } 89 | } 90 | 91 | return true; 92 | } 93 | 94 | private function sessionIsKnownOrCreateSession() 95 | { 96 | if (!$known = $this->sessionIsKnown()) { 97 | $this->sessionSetId($this->findOrCreate($this->sessionInfo, ['uuid'])); 98 | } else { 99 | $session = $this->find($this->getSessionData('id')); 100 | 101 | $session->updated_at = Carbon::now(); 102 | 103 | $session->save(); 104 | 105 | $this->sessionInfo['id'] = $this->getSessionData('id'); 106 | } 107 | 108 | return $known; 109 | } 110 | 111 | private function sessionIsKnown() 112 | { 113 | if (!$this->session->has($this->getSessionKey())) { 114 | return false; 115 | } 116 | 117 | if (!$this->getSessionData('uuid') == $this->getSystemSessionId()) { 118 | return false; 119 | } 120 | 121 | if (!$this->findByUuid($this->getSessionData('uuid'))) { 122 | return false; 123 | } 124 | 125 | return true; 126 | } 127 | 128 | private function ensureSessionDataIsComplete() 129 | { 130 | $sessionData = $this->getSessionData(); 131 | 132 | $wasComplete = true; 133 | 134 | foreach ($this->sessionInfo as $key => $value) { 135 | if ($key === 'user_agent') { 136 | continue; 137 | } 138 | if ($sessionData[$key] !== $value) { 139 | if (!isset($model)) { 140 | $model = $this->find($this->sessionInfo['id']); 141 | } 142 | 143 | $model->setAttribute($key, $value); 144 | 145 | $model->save(); 146 | 147 | $wasComplete = false; 148 | } 149 | } 150 | 151 | if (!$wasComplete) { 152 | $this->storeSession(); 153 | } 154 | } 155 | 156 | private function sessionGetId() 157 | { 158 | return $this->sessionInfo['id']; 159 | } 160 | 161 | private function sessionSetId($id) 162 | { 163 | $this->sessionInfo['id'] = $id; 164 | 165 | $this->storeSession(); 166 | } 167 | 168 | private function storeSession() 169 | { 170 | $this->putSessionData($this->sessionInfo); 171 | } 172 | 173 | private function getSystemSessionId() 174 | { 175 | $sessionData = $this->getSessionData(); 176 | 177 | if (isset($sessionData['uuid'])) { 178 | return $sessionData['uuid']; 179 | } 180 | 181 | return UUID::uuid4()->toString(); 182 | } 183 | 184 | private function regenerateSystemSession($data = null) 185 | { 186 | $data = $data ?: $this->getSessionData(); 187 | 188 | if (!$data) { 189 | $this->resetSessionUuid($data); 190 | 191 | $this->sessionIsKnownOrCreateSession(); 192 | } 193 | 194 | return $this->sessionInfo; 195 | } 196 | 197 | /** 198 | * @param string $variable 199 | */ 200 | private function getSessionData($variable = null) 201 | { 202 | $data = $this->session->get($this->getSessionKey()); 203 | 204 | return $variable 205 | ? (isset($data[$variable]) ? $data[$variable] : null) 206 | : $data; 207 | } 208 | 209 | private function putSessionData($data) 210 | { 211 | $this->session->put($this->getSessionKey(), $data); 212 | } 213 | 214 | private function getSessionKey() 215 | { 216 | return $this->config->get('tracker_session_name'); 217 | } 218 | 219 | private function getSessions() 220 | { 221 | return $this 222 | ->newQuery() 223 | ->with($this->relations) 224 | ->orderBy('updated_at', 'desc'); 225 | } 226 | 227 | public function all() 228 | { 229 | return $this->getSessions()->get(); 230 | } 231 | 232 | public function last($minutes, $returnResults) 233 | { 234 | $query = $this 235 | ->getSessions() 236 | ->period($minutes); 237 | 238 | if ($returnResults) { 239 | $cacheKey = 'last-sessions'; 240 | 241 | $result = $this->cache->findCachedWithKey($cacheKey); 242 | 243 | if (!$result) { 244 | $result = $query->get(); 245 | 246 | $this->cache->cachePut($cacheKey, $result, 1); // cache only for 1 minute 247 | 248 | return $result; 249 | } 250 | 251 | return $result; 252 | } 253 | 254 | return $query; 255 | } 256 | 257 | public function userDevices($minutes, $user_id, $results) 258 | { 259 | if (!$user_id) { 260 | return []; 261 | } 262 | 263 | $sessions = $this 264 | ->getSessions() 265 | ->period($minutes) 266 | ->where('user_id', $user_id); 267 | 268 | if ($results) { 269 | $sessions = $sessions->get()->pluck('device')->unique(); 270 | } 271 | 272 | return $sessions; 273 | } 274 | 275 | public function users($minutes, $results) 276 | { 277 | return $this->getModel()->users($minutes, $results); 278 | } 279 | 280 | public function getCurrent() 281 | { 282 | return $this->getModel(); 283 | } 284 | 285 | public function updateSessionData($data) 286 | { 287 | $session = $this->checkIfUserChanged($data, $this->find($this->getSessionData('id'))); 288 | 289 | foreach ($session->getAttributes() as $name => $value) { 290 | if (isset($data[$name]) && $name !== 'id' && $name !== 'uuid') { 291 | $session->{$name} = $data[$name]; 292 | } 293 | } 294 | 295 | $session->save(); 296 | 297 | return $data; 298 | } 299 | 300 | private function checkIfUserChanged($data, $model) 301 | { 302 | if (!is_null($model->user_id) && !is_null($data['user_id']) && $data['user_id'] !== $model->user_id) { 303 | $newSession = $this->regenerateSystemSession($data); 304 | 305 | $model = $this->findByUuid($newSession['uuid']); 306 | } 307 | 308 | return $model; 309 | } 310 | 311 | private function checkSessionUuid() 312 | { 313 | if (!isset($this->sessionInfo['uuid']) || !$this->sessionInfo['uuid']) { 314 | $this->sessionInfo['uuid'] = $this->getSystemSessionId(); 315 | } 316 | } 317 | 318 | private function resetSessionUuid($data = null) 319 | { 320 | $this->sessionInfo['uuid'] = null; 321 | 322 | $data = $data ?: $this->sessionInfo; 323 | 324 | unset($data['uuid']); 325 | 326 | $this->putSessionData($data); 327 | 328 | $this->checkSessionUuid(); 329 | 330 | return $data; 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/Data/Repositories/SqlQuery.php: -------------------------------------------------------------------------------- 1 | sqlQueryLogRepository = $sqlQueryLogRepository; 53 | 54 | $this->sqlQueryBindingRepository = $sqlQueryBindingRepository; 55 | 56 | $this->sqlQueryBindingParameterRepository = $sqlQueryBindingParameterRepository; 57 | 58 | $this->connectionRepository = $connectionRepository; 59 | 60 | $this->logRepository = $logRepository; 61 | 62 | $this->config = $config; 63 | } 64 | 65 | public function fire() 66 | { 67 | if (!$this->logRepository->getCurrentLogId()) { 68 | return; 69 | } 70 | 71 | foreach ($this->queries as $query) { 72 | $this->logQuery($query); 73 | } 74 | 75 | $this->clear(); 76 | } 77 | 78 | private function sqlQueryIsLoggable($sqlQuery) 79 | { 80 | return strpos($sqlQuery, '"tracker_') === false; 81 | } 82 | 83 | private function serializeBindings($bindings) 84 | { 85 | return serialize($bindings); 86 | } 87 | 88 | public function push($query) 89 | { 90 | $this->queries[] = $query; 91 | 92 | $this->fire(); 93 | } 94 | 95 | private function logQuery($query) 96 | { 97 | $sqlQuery = htmlentities($query['query']); 98 | 99 | $bindings = $query['bindings']; 100 | 101 | $time = $query['time']; 102 | 103 | $name = $query['name']; 104 | 105 | if (!$this->sqlQueryIsLoggable($sqlQuery)) { 106 | return; 107 | } 108 | 109 | $connectionId = $this->connectionRepository->findOrCreate( 110 | ['name' => $name], 111 | ['name'] 112 | ); 113 | 114 | $sqlQueryId = $this->findOrCreate( 115 | [ 116 | 'sha1' => sha1($sqlQuery), 117 | 'statement' => $sqlQuery, 118 | 'time' => $time, 119 | 'connection_id' => $connectionId, 120 | ], 121 | ['sha1'] 122 | ); 123 | 124 | if ($bindings && $this->canLogBindings()) { 125 | $bindingsSerialized = $this->serializeBindings($bindings); 126 | 127 | $sqlQuery_bindings_id = $this->sqlQueryBindingRepository->findOrCreate( 128 | ['sha1' => sha1($bindingsSerialized), 'serialized' => $bindingsSerialized], 129 | ['sha1'], 130 | $created 131 | ); 132 | 133 | if ($created) { 134 | foreach ($bindings as $parameter => $value) { 135 | $this->sqlQueryBindingParameterRepository->create( 136 | [ 137 | 'sql_query_bindings_id' => $sqlQuery_bindings_id, 138 | 139 | // unfortunately laravel uses question marks, 140 | // but hopefully someday this will change 141 | 'name' => '?', 142 | 143 | 'value' => $value, 144 | ] 145 | ); 146 | } 147 | } 148 | } 149 | 150 | $this->sqlQueryLogRepository->create( 151 | [ 152 | 'log_id' => $this->logRepository->getCurrentLogId(), 153 | 'sql_query_id' => $sqlQueryId, 154 | ] 155 | ); 156 | } 157 | 158 | private function canLogBindings() 159 | { 160 | return $this->config->get('log_sql_queries_bindings'); 161 | } 162 | 163 | /** 164 | * @return array 165 | */ 166 | private function clear() 167 | { 168 | return $this->queries = []; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Data/Repositories/SqlQueryBinding.php: -------------------------------------------------------------------------------- 1 | events[] = [ 20 | 'event' => $event, 21 | 'object' => $object, 22 | ]; 23 | } 24 | 25 | public function popAll() 26 | { 27 | $events = $this->events; 28 | 29 | $this->events = []; 30 | 31 | return $events; 32 | } 33 | 34 | public function turnOff() 35 | { 36 | $this->isOn = false; 37 | } 38 | 39 | public function turnOn() 40 | { 41 | $this->isOn = true; 42 | } 43 | 44 | public function isOn() 45 | { 46 | return $this->isOn; 47 | } 48 | 49 | public function isOff() 50 | { 51 | return !$this->isOn; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Repositories/Message.php: -------------------------------------------------------------------------------- 1 | messageList = collect(); 20 | } 21 | 22 | /** 23 | * Add a message to the messages list. 24 | * 25 | * @param $message 26 | * 27 | * @return void 28 | */ 29 | public function addMessage($message) 30 | { 31 | collect((array) $message)->each(function ($item) { 32 | collect($item)->flatten()->each(function ($flattened) { 33 | $this->messageList->push($flattened); 34 | }); 35 | }); 36 | } 37 | 38 | /** 39 | * Get the messages. 40 | * 41 | * @return \Illuminate\Support\Collection 42 | */ 43 | public function getMessages() 44 | { 45 | return $this->messageList; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Services/Authentication.php: -------------------------------------------------------------------------------- 1 | app = $app; 19 | 20 | $this->config = $config; 21 | } 22 | 23 | public function check() 24 | { 25 | return $this->executeAuthMethod($this->config->get('authenticated_check_method')); 26 | } 27 | 28 | private function executeAuthMethod($method) 29 | { 30 | $guards = $this->config->get('authentication_guards'); 31 | // Make sure authentication_guards at least contains a null value to DRY code 32 | if (empty($guards)) { 33 | $guards[] = null; 34 | } 35 | 36 | foreach ($this->getAuthentication() as $auth) { 37 | foreach ($guards as $guard) { 38 | // Call guard() if not null 39 | if ($guard && $guard != 'null') { 40 | $auth = $auth->guard($guard); 41 | } 42 | } 43 | if (is_callable([$auth, $method], true, $callable_name)) { 44 | if ($data = $auth->$method()) { 45 | return $data; 46 | } 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | 53 | private function getAuthentication() 54 | { 55 | foreach ((array) $this->config->get('authentication_ioc_binding') as $binding) { 56 | $this->authentication[] = $this->app->make($binding); 57 | } 58 | 59 | return $this->authentication; 60 | } 61 | 62 | public function user() 63 | { 64 | return $this->executeAuthMethod($this->config->get('authenticated_user_method')); 65 | } 66 | 67 | public function getCurrentUserId() 68 | { 69 | if ($this->check()) { 70 | return $this->user()->{$this->config->get('authenticated_user_id_column')}; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Support/Cache.php: -------------------------------------------------------------------------------- 1 | config = $config; 19 | 20 | $this->app = $app; 21 | } 22 | 23 | public function cachePut($cacheKey, $model) 24 | { 25 | if ($this->config->get('cache_enabled')) { 26 | IlluminateCache::put($cacheKey, $model, 10); 27 | } 28 | } 29 | 30 | private function extractAttributes($attributes) 31 | { 32 | if (is_array($attributes) || is_string($attributes)) { 33 | return $attributes; 34 | } 35 | 36 | if (is_string($attributes) || is_numeric($attributes)) { 37 | return (array) $attributes; 38 | } 39 | 40 | if ($attributes instanceof Model) { 41 | return $attributes->getAttributes(); 42 | } 43 | } 44 | 45 | /** 46 | * @param $attributes 47 | * @param $keys 48 | * 49 | * @return array 50 | */ 51 | private function extractKeys($attributes, $keys) 52 | { 53 | if (!$keys) { 54 | $keys = array_keys($attributes); 55 | } 56 | 57 | if (!is_array($keys)) { 58 | $keys = (array) $keys; 59 | 60 | return $keys; 61 | } 62 | 63 | return $keys; 64 | } 65 | 66 | /** 67 | * @param string $key 68 | * 69 | * @return array 70 | */ 71 | public function findCachedWithKey($key) 72 | { 73 | if ($this->config->get('cache_enabled')) { 74 | return IlluminateCache::get($key); 75 | } 76 | } 77 | 78 | public function makeKeyAndPut($model, $key) 79 | { 80 | $key = $this->makeCacheKey($model, $key, get_class($model)); 81 | 82 | $this->cachePut($key, $model); 83 | } 84 | 85 | /** 86 | * @param string $identifier 87 | */ 88 | public function findCached($attributes, $keys, $identifier = null) 89 | { 90 | if (!$this->config->get('cache_enabled')) { 91 | return; 92 | } 93 | 94 | $key = $this->makeCacheKey($attributes, $keys, $identifier); 95 | 96 | return [ 97 | $this->findCachedWithKey($key), 98 | $key, 99 | ]; 100 | } 101 | 102 | public function makeCacheKey($attributes, $keys, $identifier) 103 | { 104 | $attributes = $this->extractAttributes($attributes); 105 | 106 | $cacheKey = "className=$identifier;"; 107 | 108 | $keys = $this->extractKeys($attributes, $keys, $identifier); 109 | 110 | foreach ($keys as $key) { 111 | if (isset($attributes[$key])) { 112 | $cacheKey .= "$key=$attributes[$key];"; 113 | } 114 | } 115 | 116 | return sha1($cacheKey); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Support/CrawlerDetector.php: -------------------------------------------------------------------------------- 1 | detector = new CrawlerDetect($headers, $agent); 25 | } 26 | 27 | /** 28 | * Check if current request is from a bot. 29 | * 30 | * @return bool 31 | */ 32 | public function isRobot() 33 | { 34 | return $this->detector->isCrawler(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Support/Exceptions/CompileError.php: -------------------------------------------------------------------------------- 1 | tracker = $tracker; 22 | 23 | $this->illuminateHandler = $illuminateHandler; 24 | 25 | $this->initializeHandlers(); 26 | } 27 | 28 | private function initializeHandlers() 29 | { 30 | $this->originalExceptionHandler = set_exception_handler([$this, 'handleThrowable']); 31 | 32 | $this->originalErrorHandler = set_error_handler([$this, 'handleError']); 33 | } 34 | 35 | public function handleThrowable(Throwable $throwable) 36 | { 37 | try { 38 | $this->tracker->handleThrowable($throwable); 39 | } catch (\Exception $e) { 40 | // Ignore Tracker exceptions 41 | } 42 | 43 | // Call Laravel Exception Handler 44 | return call_user_func($this->originalExceptionHandler, $throwable); 45 | } 46 | 47 | public function handleError($err_severity, $err_msg, $err_file, $err_line, array $err_context) 48 | { 49 | try { 50 | $error = ExceptionFactory::make($err_severity, $err_msg); 51 | 52 | $this->tracker->handleThrowable($error); 53 | } catch (\Exception $e) { 54 | // Ignore Tracker exceptions 55 | } 56 | 57 | // Call Laravel Exception Handler 58 | return call_user_func($this->originalErrorHandler, $err_severity, $err_msg, $err_file, $err_line); 59 | } 60 | 61 | public function report(Throwable $e) 62 | { 63 | try { 64 | $this->tracker->handleThrowable($e); 65 | } catch (Exception $exception) { 66 | // ignore 67 | } 68 | 69 | $this->illuminateHandler->report($e); 70 | } 71 | 72 | public function shouldReport(Throwable $e) 73 | { 74 | return $this->illuminateHandler->shouldReport($e); 75 | } 76 | 77 | public function render($request, Throwable $e) 78 | { 79 | return $this->illuminateHandler->render($request, $e); 80 | } 81 | 82 | public function renderForConsole($output, Throwable $e) 83 | { 84 | return $this->illuminateHandler->renderForConsole($output, $e); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Support/Exceptions/Notice.php: -------------------------------------------------------------------------------- 1 | $this->getLanguagePreference(), 18 | 'language-range' => $this->getLanguageRange(), 19 | ]; 20 | } 21 | 22 | /** 23 | * Get language prefernece. 24 | * 25 | * @return string 26 | */ 27 | public function getLanguagePreference() 28 | { 29 | $languages = $this->languages(); 30 | 31 | return count($languages) ? $languages[0] : 'en'; 32 | } 33 | 34 | /** 35 | * Get languages ranges. 36 | * 37 | * @return string 38 | */ 39 | public function getLanguageRange() 40 | { 41 | return implode(',', $this->languages()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Support/Migration.php: -------------------------------------------------------------------------------- 1 | manager = app()->make('db'); 12 | 13 | $this->connection = $this->manager->connection(config('tracker.connection')); 14 | 15 | parent::checkConnection(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Support/Minutes.php: -------------------------------------------------------------------------------- 1 | minutes = $minutes; 36 | 37 | if ($minutes instanceof self) { 38 | $this->start = $minutes->getStart(); 39 | 40 | $this->end = $minutes->getEnd(); 41 | } else { 42 | $this->calculateStartEnd(); 43 | } 44 | } 45 | 46 | /** 47 | * Calculate start and end dates. 48 | */ 49 | private function calculateStartEnd() 50 | { 51 | if ($this->minutes == 0) { 52 | $this->setToday(); 53 | } else { 54 | $this->start = Carbon::now()->subMinutes($this->minutes); 55 | 56 | $this->end = Carbon::now(); 57 | } 58 | } 59 | 60 | /** 61 | * @return mixed 62 | */ 63 | public function getEnd() 64 | { 65 | return $this->end; 66 | } 67 | 68 | /** 69 | * @return mixed 70 | */ 71 | public function getMinutes() 72 | { 73 | return $this->minutes; 74 | } 75 | 76 | /** 77 | * @return mixed 78 | */ 79 | public function getStart() 80 | { 81 | return $this->start; 82 | } 83 | 84 | /** 85 | * @param $minutes 86 | * 87 | * @return static 88 | */ 89 | public static function make($minutes) 90 | { 91 | return new static($minutes); 92 | } 93 | 94 | /** 95 | * @param mixed $end 96 | */ 97 | public function setEnd($end) 98 | { 99 | $this->end = $end; 100 | } 101 | 102 | /** 103 | * @param mixed $start 104 | */ 105 | public function setStart($start) 106 | { 107 | $this->start = $start; 108 | } 109 | 110 | /** 111 | * Today. 112 | */ 113 | private function setToday() 114 | { 115 | $this->start = Carbon::now()->setTime(0, 0, 0); 116 | 117 | $this->end = Carbon::now()->setTime(23, 59, 59); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Support/MobileDetect.php: -------------------------------------------------------------------------------- 1 | $this->getDeviceKind(), 18 | 'model' => $this->device(), 19 | 'is_mobile' => $this->isMobile(), 20 | 'is_robot' => $this->isRobot(), 21 | ]; 22 | } 23 | 24 | /** 25 | * Get the kind of device. 26 | * 27 | * @internal param $mobile 28 | * 29 | * @return string 30 | */ 31 | public function getDeviceKind() 32 | { 33 | $kind = 'unavailable'; 34 | 35 | if ($this->isTablet()) { 36 | $kind = 'Tablet'; 37 | } elseif ($this->isPhone()) { 38 | $kind = 'Phone'; 39 | } elseif ($this->isComputer()) { 40 | $kind = 'Computer'; 41 | } 42 | 43 | return $kind; 44 | } 45 | 46 | /** 47 | * Is this a phone? 48 | * 49 | * @return bool 50 | */ 51 | public function isPhone($userAgent = null, $httpHeaders = null) 52 | { 53 | return !$this->isTablet() && !$this->isComputer(); 54 | } 55 | 56 | /** 57 | * Is this a computer? 58 | * 59 | * @return bool 60 | */ 61 | public function isComputer() 62 | { 63 | return !$this->isMobile(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Support/RefererParser.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 31 | } 32 | 33 | /** 34 | * Parse a referer. 35 | * 36 | * @return RefererParser 37 | */ 38 | public function parse($refererUrl, $pageUrl) 39 | { 40 | $this->setReferer($this->parser->parse($refererUrl, $pageUrl)); 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * Get the search medium. 47 | * 48 | * @return string|null 49 | */ 50 | public function getMedium() 51 | { 52 | if ($this->isKnown()) { 53 | return $this->referer->getMedium(); 54 | } 55 | } 56 | 57 | /** 58 | * Get the search source. 59 | * 60 | * @return string|null 61 | */ 62 | public function getSource() 63 | { 64 | if ($this->isKnown()) { 65 | return $this->referer->getSource(); 66 | } 67 | } 68 | 69 | /** 70 | * Get the search term. 71 | * 72 | * @return string|null 73 | */ 74 | public function getSearchTerm() 75 | { 76 | if ($this->isKnown()) { 77 | return $this->referer->getSearchTerm(); 78 | } 79 | } 80 | 81 | /** 82 | * Check if the referer is knwon. 83 | * 84 | * @return bool 85 | */ 86 | public function isKnown() 87 | { 88 | return $this->referer->isKnown(); 89 | } 90 | 91 | /** 92 | * Set the referer. 93 | * 94 | * @param \Snowplow\RefererParser\Referer $referer 95 | * 96 | * @return RefererParser 97 | */ 98 | public function setReferer($referer) 99 | { 100 | $this->referer = $referer; 101 | 102 | return $this; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Support/UserAgentParser.php: -------------------------------------------------------------------------------- 1 | parser = Parser::create()->parse($this->getUserAgent($userAgent)); 22 | 23 | $this->userAgent = $this->parser->ua; 24 | 25 | $this->operatingSystem = $this->parser->os; 26 | 27 | $this->device = $this->parser->device; 28 | 29 | $this->basePath = $basePath; 30 | 31 | $this->originalUserAgent = $this->parser->originalUserAgent; 32 | } 33 | 34 | public function getOperatingSystemVersion() 35 | { 36 | return $this->operatingSystem->major. 37 | ($this->operatingSystem->minor !== null ? '.'.$this->operatingSystem->minor : ''). 38 | ($this->operatingSystem->patch !== null ? '.'.$this->operatingSystem->patch : ''); 39 | } 40 | 41 | protected function getUserAgent($userAgent) 42 | { 43 | if (!empty($userAgent)) { 44 | return $userAgent; 45 | } 46 | 47 | if (isset($_SERVER['HTTP_USER_AGENT']) && !empty($_SERVER['HTTP_USER_AGENT'])) { 48 | return $_SERVER['HTTP_USER_AGENT']; 49 | } 50 | 51 | return config('tracker.default_user_agent', ''); 52 | } 53 | 54 | public function getUserAgentVersion() 55 | { 56 | return $this->userAgent->major. 57 | ($this->userAgent->minor !== null ? '.'.$this->userAgent->minor : ''). 58 | ($this->userAgent->patch !== null ? '.'.$this->userAgent->patch : ''); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Artisan/Base.php: -------------------------------------------------------------------------------- 1 | $type($message); 28 | } 29 | } 30 | 31 | /** 32 | * Get the console command arguments. 33 | * 34 | * @return array 35 | */ 36 | protected function getArguments() 37 | { 38 | return [ 39 | ['query', InputArgument::IS_ARRAY, 'The SQL query to be executed'], 40 | ]; 41 | } 42 | 43 | /** 44 | * Get the console command options. 45 | * 46 | * @return array 47 | */ 48 | protected function getOptions() 49 | { 50 | $baseOptions = [ 51 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], 52 | ]; 53 | 54 | return array_merge($baseOptions, isset($this->options) ? $this->options : []); 55 | } 56 | 57 | /** 58 | * Display results. 59 | * 60 | * @param $result 61 | * @param string $method 62 | */ 63 | public function display($result, $method = 'info') 64 | { 65 | if ($result) { 66 | if (is_array($result)) { 67 | $this->displayTable($result); 68 | } elseif (is_bool($result)) { 69 | $this->{$method}($result ? 'Statement executed sucessfully.' : 'And error ocurred while executing the statement.'); 70 | } else { 71 | $this->{$method}($result); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Display results in table format. 78 | * 79 | * @param $table 80 | */ 81 | public function displayTable($table) 82 | { 83 | $headers = $this->makeHeaders($table[0]); 84 | 85 | $rows = []; 86 | 87 | foreach ($table as $row) { 88 | $rows[] = (array) $row; 89 | } 90 | 91 | $this->table = $this->getHelperSet()->get('table'); 92 | 93 | $this->table->setHeaders($headers)->setRows($rows); 94 | 95 | $this->table->render($this->getOutput()); 96 | } 97 | 98 | /** 99 | * Extract headers from result. 100 | * 101 | * @param $items 102 | * 103 | * @return array 104 | */ 105 | private function makeHeaders($items) 106 | { 107 | return array_keys((array) $items); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Artisan/Tables.php: -------------------------------------------------------------------------------- 1 | fire(); 29 | } 30 | 31 | /** 32 | * Execute the command. 33 | * 34 | * @return void 35 | */ 36 | public function fire() 37 | { 38 | $files = $this->laravel->make('files'); 39 | 40 | foreach ($files->files($this->getPackageMigrationsPath()) as $file) { 41 | if (!file_exists($destination = $this->makeMigrationPath($file))) { 42 | $files->copy($file, $destination); 43 | 44 | $this->info("Migration created: $destination"); 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * Get the package migrations folder. 51 | * 52 | * @return string 53 | */ 54 | protected function getPackageMigrationsPath() 55 | { 56 | $ds = DIRECTORY_SEPARATOR; 57 | 58 | return __DIR__."{$ds}..{$ds}..{$ds}..{$ds}migrations"; 59 | } 60 | 61 | /** 62 | * Get the system migrations folder. 63 | * 64 | * @return string 65 | */ 66 | protected function getBaseMigrationsPath() 67 | { 68 | $path = 'database'.DIRECTORY_SEPARATOR.'migrations'; 69 | 70 | return base_path($path); 71 | } 72 | 73 | /** 74 | * Make a full path migration name. 75 | * 76 | * @param $file 77 | * 78 | * @return string 79 | */ 80 | private function makeMigrationPath($file) 81 | { 82 | return $this->getBaseMigrationsPath().DIRECTORY_SEPARATOR.basename($file); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Artisan/UpdateGeoIp.php: -------------------------------------------------------------------------------- 1 | updateGeoIp() 31 | ? 'info' 32 | : 'error'; 33 | 34 | $this->displayMessages($type, $tracker->getMessages()); 35 | } 36 | 37 | /** 38 | * Handle the command. 39 | * 40 | * @return void 41 | */ 42 | public function handle() 43 | { 44 | $this->fire(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Controllers/Stats.php: -------------------------------------------------------------------------------- 1 | authentication = app()->make('tracker.authentication'); 24 | } 25 | 26 | public function index(Session $session) 27 | { 28 | if (!$this->isAuthenticated()) { 29 | return View::make('pragmarx/tracker::message')->with('message', trans('tracker::tracker.auth_required')); 30 | } 31 | 32 | if (!$this->hasAdminProperty()) { 33 | return View::make('pragmarx/tracker::message')->with('message', trans('tracker::tracker.miss_admin_prop')); 34 | } 35 | 36 | if (!$this->isAdmin()) { 37 | return View::make('pragmarx/tracker::message')->with('message', trans('tracker::tracker.not_admin')); 38 | } 39 | 40 | return $this->showPage($session, $session->getValue('page')); 41 | } 42 | 43 | /** 44 | * @param Session $session 45 | */ 46 | public function showPage($session, $page) 47 | { 48 | $me = $this; 49 | 50 | if (method_exists($me, $page)) { 51 | return $this->$page($session); 52 | } 53 | } 54 | 55 | public function visits(Session $session) 56 | { 57 | $datatables_data = 58 | [ 59 | 'datatables_ajax_route' => route('tracker.stats.api.visits'), 60 | 'datatables_columns' => ' 61 | { "data" : "id", "title" : "'.trans('tracker::tracker.id').'", "orderable": true, "searchable": true }, 62 | { "data" : "client_ip", "title" : "'.trans('tracker::tracker.ip_address').'", "orderable": true, "searchable": true }, 63 | { "data" : "country", "title" : "'.trans('tracker::tracker.country_city').'", "orderable": true, "searchable": true }, 64 | { "data" : "user", "title" : "'.trans('tracker::tracker.user').'", "orderable": true, "searchable": true }, 65 | { "data" : "device", "title" : "'.trans('tracker::tracker.device').'", "orderable": true, "searchable": true }, 66 | { "data" : "browser", "title" : "'.trans('tracker::tracker.browser').'", "orderable": true, "searchable": true }, 67 | { "data" : "language", "title" : "'.trans('tracker::tracker.language').'", "orderable": true, "searchable": true }, 68 | { "data" : "referer", "title" : "'.trans('tracker::tracker.referer').'", "orderable": true, "searchable": true }, 69 | { "data" : "pageViews", "title" : "'.trans('tracker::tracker.page_views').'", "orderable": true, "searchable": true }, 70 | { "data" : "lastActivity","title" : "'.trans('tracker::tracker.last_activity').'", "orderable": true, "searchable": true }, 71 | ', 72 | ]; 73 | 74 | return View::make('pragmarx/tracker::index') 75 | ->with('sessions', Tracker::sessions($session->getMinutes())) 76 | ->with('title', ''.trans('tracker::tracker.visits').'') 77 | ->with('username_column', Tracker::getConfig('authenticated_user_username_column')) 78 | ->with('datatables_data', $datatables_data); 79 | } 80 | 81 | public function log($uuid) 82 | { 83 | return View::make('pragmarx/tracker::log') 84 | ->with('uuid', $uuid) 85 | ->with('title', 'log'); 86 | } 87 | 88 | public function summary() 89 | { 90 | return View::make('pragmarx/tracker::summary') 91 | ->with('title', ''.trans('tracker::tracker.page_views_summary').''); 92 | } 93 | 94 | public function apiPageviews(Session $session) 95 | { 96 | return Tracker::pageViews($session->getMinutes())->toJson(); 97 | } 98 | 99 | public function apiPageviewsByCountry(Session $session) 100 | { 101 | return Tracker::pageViewsByCountry($session->getMinutes())->toJson(); 102 | } 103 | 104 | public function apiLog($uuid) 105 | { 106 | $query = Tracker::sessionLog($uuid, false); 107 | 108 | $query->select([ 109 | 'id', 110 | 'session_id', 111 | 'method', 112 | 'path_id', 113 | 'query_id', 114 | 'route_path_id', 115 | 'is_ajax', 116 | 'is_secure', 117 | 'is_json', 118 | 'wants_json', 119 | 'error_id', 120 | 'created_at', 121 | ]); 122 | 123 | return Datatables::of($query) 124 | ->edit_column('route_name', function ($row) { 125 | $path = $row->routePath; 126 | 127 | return $row->routePath 128 | ? $row->routePath->route->name.'
'.$row->routePath->route->action 129 | : ($row->path ? $row->path->path : ''); 130 | }) 131 | 132 | ->edit_column('route', function ($row) { 133 | $route = null; 134 | 135 | if ($row->routePath) { 136 | foreach ($row->routePath->parameters as $parameter) { 137 | $route .= ($route ? '
' : '').$parameter->parameter.'='.$parameter->value; 138 | } 139 | } 140 | 141 | return $route; 142 | }) 143 | 144 | ->edit_column('query', function ($row) { 145 | $query = null; 146 | 147 | if ($row->logQuery) { 148 | foreach ($row->logQuery->arguments as $argument) { 149 | $query .= ($query ? '
' : '').$argument->argument.'='.$argument->value; 150 | } 151 | } 152 | 153 | return $query; 154 | }) 155 | 156 | ->edit_column('is_ajax', function ($row) { 157 | return $row->is_ajax ? 'yes' : 'no'; 158 | }) 159 | 160 | ->edit_column('is_secure', function ($row) { 161 | return $row->is_secure ? 'yes' : 'no'; 162 | }) 163 | 164 | ->edit_column('is_json', function ($row) { 165 | return $row->is_json ? 'yes' : 'no'; 166 | }) 167 | 168 | ->edit_column('wants_json', function ($row) { 169 | return $row->wants_json ? 'yes' : 'no'; 170 | }) 171 | 172 | ->edit_column('error', function ($row) { 173 | return $row->error ? 'yes' : 'no'; 174 | }) 175 | 176 | ->make(true); 177 | } 178 | 179 | public function users(Session $session) 180 | { 181 | return View::make('pragmarx/tracker::users') 182 | ->with('users', Tracker::users($session->getMinutes())) 183 | ->with('title', ''.trans('tracker::tracker.users').'') 184 | ->with('username_column', Tracker::getConfig('authenticated_user_username_column')); 185 | } 186 | 187 | private function events(Session $session) 188 | { 189 | return View::make('pragmarx/tracker::events') 190 | ->with('events', Tracker::events($session->getMinutes())) 191 | ->with('title', ''.trans('tracker::tracker.events').''); 192 | } 193 | 194 | public function errors(Session $session) 195 | { 196 | return View::make('pragmarx/tracker::errors') 197 | ->with('error_log', Tracker::errors($session->getMinutes())) 198 | ->with('title', ''.trans('tracker::tracker.errors').''); 199 | } 200 | 201 | public function apiErrors(Session $session) 202 | { 203 | $query = Tracker::errors($session->getMinutes(), false); 204 | 205 | $query->select([ 206 | 'id', 207 | 'error_id', 208 | 'session_id', 209 | 'path_id', 210 | 'updated_at', 211 | ]); 212 | 213 | return Datatables::of($query) 214 | ->edit_column('updated_at', function ($row) { 215 | return "{$row->updated_at->diffForHumans()}"; 216 | }) 217 | ->make(true); 218 | } 219 | 220 | public function apiEvents(Session $session) 221 | { 222 | $query = Tracker::events($session->getMinutes(), false); 223 | 224 | return Datatables::of($query)->make(true); 225 | } 226 | 227 | public function apiUsers(Session $session) 228 | { 229 | $username_column = Tracker::getConfig('authenticated_user_username_column'); 230 | 231 | return Datatables::of(Tracker::users($session->getMinutes(), false)) 232 | ->edit_column('user_id', function ($row) use ($username_column) { 233 | return "{$row->user->$username_column}"; 234 | }) 235 | ->edit_column('updated_at', function ($row) { 236 | return "{$row->updated_at->diffForHumans()}"; 237 | }) 238 | ->make(true); 239 | } 240 | 241 | public function apiVisits(Session $session) 242 | { 243 | $username_column = Tracker::getConfig('authenticated_user_username_column'); 244 | 245 | $query = Tracker::sessions($session->getMinutes(), false); 246 | 247 | $query->select([ 248 | 'id', 249 | 'uuid', 250 | 'user_id', 251 | 'device_id', 252 | 'agent_id', 253 | 'client_ip', 254 | 'referer_id', 255 | 'cookie_id', 256 | 'geoip_id', 257 | 'language_id', 258 | 'is_robot', 259 | 'updated_at', 260 | ]); 261 | 262 | return Datatables::of($query) 263 | ->edit_column('id', function ($row) { 264 | $uri = route('tracker.stats.log', $row->uuid); 265 | 266 | return ''.$row->id.''; 267 | }) 268 | 269 | ->add_column('country', function ($row) { 270 | $cityName = $row->geoip && $row->geoip->city ? ' - '.$row->geoip->city : ''; 271 | 272 | $countryName = ($row->geoip ? $row->geoip->country_name : '').$cityName; 273 | 274 | $countryCode = strtolower($row->geoip ? $row->geoip->country_code : ''); 275 | 276 | $flag = $countryCode 277 | ? "" 278 | : ''; 279 | 280 | return "$flag $countryName"; 281 | }) 282 | 283 | ->add_column('user', function ($row) use ($username_column) { 284 | return $row->user ? $row->user->$username_column : 'guest'; 285 | }) 286 | 287 | ->add_column('device', function ($row) { 288 | $model = ($row->device && $row->device->model && $row->device->model !== 'unavailable' ? '['.$row->device->model.']' : ''); 289 | 290 | $platform = ($row->device && $row->device->platform ? ' ['.trim($row->device->platform.' '.$row->device->platform_version).']' : ''); 291 | 292 | $mobile = ($row->device && $row->device->is_mobile ? ' [mobile device]' : ''); 293 | 294 | return $model || $platform || $mobile 295 | ? $row->device->kind.' '.$model.' '.$platform.' '.$mobile 296 | : ''; 297 | }) 298 | 299 | ->add_column('browser', function ($row) { 300 | return $row->agent && $row->agent 301 | ? $row->agent->browser.' ('.$row->agent->browser_version.')' 302 | : ''; 303 | }) 304 | 305 | ->add_column('language', function ($row) { 306 | return $row->language && $row->language 307 | ? $row->language->preference 308 | : ''; 309 | }) 310 | 311 | ->add_column('referer', function ($row) { 312 | return $row->referer ? $row->referer->domain->name : ''; 313 | }) 314 | 315 | ->add_column('pageViews', function ($row) { 316 | return $row->page_views; 317 | }) 318 | 319 | ->add_column('lastActivity', function ($row) { 320 | return $row->updated_at->diffForHumans(); 321 | }) 322 | 323 | ->make(true); 324 | } 325 | 326 | private function isAuthenticated() 327 | { 328 | return $this->authentication->check(); 329 | } 330 | 331 | private function hasAdminProperty() 332 | { 333 | $user = $this->authentication->user(); 334 | 335 | foreach ($this->adminProperties as $property) { 336 | $propertyCamel = Str::camel($property); 337 | 338 | if ( 339 | isset($user->$property) || 340 | isset($user->$propertyCamel) || 341 | method_exists($user, $property) || 342 | method_exists($user, $propertyCamel) 343 | ) { 344 | return true; 345 | } 346 | } 347 | 348 | return false; 349 | } 350 | 351 | private function isAdmin() 352 | { 353 | $user = $this->authentication->user(); 354 | 355 | foreach ($this->adminProperties as $property) { 356 | $propertyCamel = Str::camel($property); 357 | 358 | if ( 359 | (isset($user->$property) && $user->$property) || 360 | (isset($user->$propertyCamel) && $user->$propertyCamel) || 361 | (method_exists($user, $property) && $user->$property()) || 362 | (method_exists($user, $propertyCamel) && $user->$propertyCamel()) 363 | ) { 364 | return true; 365 | } 366 | } 367 | 368 | return false; 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Facade.php: -------------------------------------------------------------------------------- 1 | boot(); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/Agent.php: -------------------------------------------------------------------------------- 1 | setConnection($this->getConfig()->get('connection')); 19 | } 20 | 21 | public function getConfig() 22 | { 23 | if ($this->config) { 24 | return $this->config; 25 | } elseif (isset($GLOBALS['app']) && $GLOBALS['app'] instanceof Application) { 26 | return $GLOBALS['app']['tracker.config']; 27 | } 28 | 29 | return app()->make('tracker.config'); 30 | } 31 | 32 | public function save(array $options = []) 33 | { 34 | parent::save($options); 35 | 36 | app('tracker.cache')->makeKeyAndPut($this, $this->getKeyName()); 37 | } 38 | 39 | public function setConfig($config) 40 | { 41 | $this->config = $config; 42 | } 43 | 44 | public function scopePeriod($query, $minutes, $alias = '') 45 | { 46 | $alias = $alias ? "$alias." : ''; 47 | 48 | return $query 49 | ->where($alias.'updated_at', '>=', $minutes->getStart() ? $minutes->getStart() : 1) 50 | ->where($alias.'updated_at', '<=', $minutes->getEnd() ? $minutes->getEnd() : 1); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/Connection.php: -------------------------------------------------------------------------------- 1 | select( 18 | 'tracker_events.id', 19 | 'tracker_events.name', 20 | $this->getConnection()->raw('count('.$this->getEventLogTableName().'.id) as total') 21 | ) 22 | ->from('tracker_events') 23 | ->period($minutes, 'tracker_events_log') 24 | ->join('tracker_events_log', 'tracker_events_log.event_id', '=', 'tracker_events.id') 25 | ->groupBy('tracker_events.id', 'tracker_events.name') 26 | ->orderBy('total', 'desc'); 27 | 28 | if ($result) { 29 | return $query->get(); 30 | } 31 | 32 | return $query; 33 | } 34 | 35 | private function getEventLogTableName() 36 | { 37 | return $this->getTablePrefix().'tracker_events_log'; 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | private function getTablePrefix() 44 | { 45 | return $this->getConnection()->getTablePrefix(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/EventLog.php: -------------------------------------------------------------------------------- 1 | belongsTo($this->getConfig()->get('session_model')); 26 | } 27 | 28 | public function path() 29 | { 30 | return $this->belongsTo($this->getConfig()->get('path_model')); 31 | } 32 | 33 | public function error() 34 | { 35 | return $this->belongsTo($this->getConfig()->get('error_model')); 36 | } 37 | 38 | public function logQuery() 39 | { 40 | return $this->belongsTo($this->getConfig()->get('query_model'), 'query_id'); 41 | } 42 | 43 | public function routePath() 44 | { 45 | return $this->belongsTo($this->getConfig()->get('route_path_model'), 'route_path_id'); 46 | } 47 | 48 | public function pageViews($minutes, $results) 49 | { 50 | $query = $this->select( 51 | $this->getConnection()->raw('DATE(created_at) as date, count(*) as total') 52 | )->groupBy( 53 | $this->getConnection()->raw('DATE(created_at)') 54 | ) 55 | ->period($minutes) 56 | ->orderBy('date'); 57 | 58 | if ($results) { 59 | return $query->get(); 60 | } 61 | 62 | return $query; 63 | } 64 | 65 | public function pageViewsByCountry($minutes, $results) 66 | { 67 | $query = 68 | $this 69 | ->select( 70 | 'tracker_geoip.country_name as label', 71 | $this->getConnection()->raw('count(tracker_log.id) as value') 72 | ) 73 | ->join('tracker_sessions', 'tracker_log.session_id', '=', 'tracker_sessions.id') 74 | ->join('tracker_geoip', 'tracker_sessions.geoip_id', '=', 'tracker_geoip.id') 75 | ->groupBy('tracker_geoip.country_name') 76 | ->period($minutes, 'tracker_log') 77 | ->whereNotNull('tracker_sessions.geoip_id') 78 | ->orderBy('value', 'desc'); 79 | 80 | if ($results) { 81 | return $query->get(); 82 | } 83 | 84 | return $query; 85 | } 86 | 87 | public function errors($minutes, $results) 88 | { 89 | $query = $this 90 | ->with('error') 91 | ->with('session') 92 | ->with('path') 93 | ->period($minutes, 'tracker_log') 94 | ->whereNotNull('error_id') 95 | ->orderBy('created_at', 'desc'); 96 | 97 | if ($results) { 98 | return $query->get(); 99 | } 100 | 101 | return $query; 102 | } 103 | 104 | public function allByRouteName($name, $minutes = null) 105 | { 106 | $result = $this 107 | ->join('tracker_route_paths', 'tracker_route_paths.id', '=', 'tracker_log.route_path_id') 108 | 109 | ->leftJoin( 110 | 'tracker_route_path_parameters', 111 | 'tracker_route_path_parameters.route_path_id', 112 | '=', 113 | 'tracker_route_paths.id' 114 | ) 115 | 116 | ->join('tracker_routes', 'tracker_routes.id', '=', 'tracker_route_paths.route_id') 117 | 118 | ->where('tracker_routes.name', $name); 119 | 120 | if ($minutes) { 121 | $result->period($minutes, 'tracker_log'); 122 | } 123 | 124 | return $result; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/Path.php: -------------------------------------------------------------------------------- 1 | hasMany($this->getConfig()->get('query_argument_model')); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/QueryArgument.php: -------------------------------------------------------------------------------- 1 | belongsTo($this->getConfig()->get('domain_model')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/RefererSearchTerm.php: -------------------------------------------------------------------------------- 1 | hasMany($this->getConfig()->get('route_path_model')); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/RoutePath.php: -------------------------------------------------------------------------------- 1 | hasMany($this->getConfig()->get('route_path_parameter_model')); 17 | } 18 | 19 | public function route() 20 | { 21 | return $this->belongsTo($this->getConfig()->get('route_model'), 'route_id'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/RoutePathParameter.php: -------------------------------------------------------------------------------- 1 | belongsTo($this->getConfig()->get('user_model')); 25 | } 26 | 27 | public function device() 28 | { 29 | return $this->belongsTo($this->getConfig()->get('device_model')); 30 | } 31 | 32 | public function language() 33 | { 34 | return $this->belongsTo($this->getConfig()->get('language_model')); 35 | } 36 | 37 | public function agent() 38 | { 39 | return $this->belongsTo($this->getConfig()->get('agent_model')); 40 | } 41 | 42 | public function referer() 43 | { 44 | return $this->belongsTo($this->getConfig()->get('referer_model')); 45 | } 46 | 47 | public function geoIp() 48 | { 49 | return $this->belongsTo($this->getConfig()->get('geoip_model'), 'geoip_id'); 50 | } 51 | 52 | public function cookie() 53 | { 54 | return $this->belongsTo($this->getConfig()->get('cookie_model'), 'cookie_id'); 55 | } 56 | 57 | public function log() 58 | { 59 | return $this->hasMany($this->getConfig()->get('log_model')); 60 | } 61 | 62 | public function getPageViewsAttribute() 63 | { 64 | return $this->log()->count(); 65 | } 66 | 67 | public function users($minutes, $result) 68 | { 69 | $query = $this 70 | ->select( 71 | 'user_id', 72 | $this->getConnection()->raw('max(updated_at) as updated_at') 73 | ) 74 | ->groupBy('user_id') 75 | ->from('tracker_sessions') 76 | ->period($minutes) 77 | ->whereNotNull('user_id') 78 | ->orderBy($this->getConnection()->raw('max(updated_at)'), 'desc'); 79 | 80 | if ($result) { 81 | return $query->get(); 82 | } 83 | 84 | return $query; 85 | } 86 | 87 | public function userDevices($minutes, $result, $user_id) 88 | { 89 | $query = $this 90 | ->select( 91 | 'user_id', 92 | $this->getConnection()->raw('max(updated_at) as updated_at') 93 | ) 94 | ->groupBy('user_id') 95 | ->from('tracker_sessions') 96 | ->period($minutes) 97 | ->whereNotNull('user_id') 98 | ->orderBy($this->getConnection()->raw('max(updated_at)'), 'desc'); 99 | 100 | if ($result) { 101 | return $query->get(); 102 | } 103 | 104 | return $query; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Vendor/Laravel/Models/SqlQuery.php: -------------------------------------------------------------------------------- 1 | getValue('days', 1)); 16 | 17 | LaravelSession::put('tracker.stats.page', $this->getValue('page', 'visits')); 18 | 19 | $this->minutes = new Minutes(60 * 24 * LaravelSession::get('tracker.stats.days')); 20 | } 21 | 22 | /** 23 | * @return Minutes 24 | */ 25 | public function getMinutes() 26 | { 27 | return $this->minutes; 28 | } 29 | 30 | public function getValue($variable, $default = null) 31 | { 32 | if (Request::has($variable)) { 33 | $value = Request::get($variable); 34 | } else { 35 | $value = LaravelSession::get('tracker.stats.'.$variable, $default); 36 | } 37 | 38 | return $value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/config/.gitkeep -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | false, 9 | 10 | /* 11 | * Enable cache? 12 | */ 13 | 'cache_enabled' => true, 14 | 15 | /* 16 | * Deffer booting for middleware use 17 | */ 18 | 'use_middleware' => false, 19 | 20 | /* 21 | * Robots should be tracked? 22 | */ 23 | 'do_not_track_robots' => false, 24 | 25 | /* 26 | * Which environments are not trackable? 27 | */ 28 | 'do_not_track_environments' => [ 29 | // defaults to none 30 | ], 31 | 32 | /* 33 | * Which routes names are not trackable? 34 | */ 35 | 'do_not_track_routes' => [ 36 | 'tracker.stats.*', 37 | ], 38 | 39 | /* 40 | * Which route paths are not trackable? 41 | */ 42 | 'do_not_track_paths' => [ 43 | 'api/*', 44 | ], 45 | 46 | /* 47 | * The Do Not Track Ips is used to disable Tracker for some IP addresses: 48 | * 49 | * '127.0.0.1', '192.168.1.1' 50 | * 51 | * You can set ranges of IPs 52 | * '192.168.0.1-192.168.0.100' 53 | * 54 | * And use net masks 55 | * '10.0.0.0/32' 56 | * '172.17.0.0/255.255.0.0' 57 | */ 58 | 'do_not_track_ips' => [ 59 | '127.0.0.0/24', /// range 127.0.0.1 - 127.0.0.255 60 | ], 61 | 62 | /* 63 | * When an IP is not trackable, show a message in log. 64 | */ 65 | 'log_untrackable_sessions' => true, 66 | 67 | /* 68 | * Log every single access? 69 | * 70 | * The log table can become huge if your site is popular, but... 71 | * 72 | * Log table is also responsible for storing information on: 73 | * 74 | * - Routes and controller actions accessed 75 | * - HTTP method used (GET, POST...) 76 | * - Error log 77 | * - URL queries (including values) 78 | */ 79 | 'log_enabled' => false, 80 | 81 | /* 82 | * Log artisan commands? 83 | */ 84 | 'console_log_enabled' => false, 85 | 86 | /* 87 | * Log SQL queries? 88 | * 89 | * Log must be enabled for this option to work. 90 | */ 91 | 'log_sql_queries' => false, 92 | 93 | /* 94 | * If you prefer to store Tracker data on a different database or connection, 95 | * you can set it here. 96 | * 97 | * To avoid SQL queries log recursion, create a different connection for Tracker, 98 | * point it to the same database (or not) and forbid logging of this connection in 99 | * do_not_log_sql_queries_connections. 100 | */ 101 | 'connection' => 'tracker', 102 | 103 | /* 104 | * Forbid logging of SQL queries for some connections. 105 | * 106 | * To avoid recursion, you better ignore Tracker connection here. 107 | * 108 | * Please create a separate database connection for Tracker. It can hit 109 | * the same database of your application, but the connection itself 110 | * has to have a different name, so the package can ignore its own queries 111 | * and avoid recursion. 112 | * 113 | */ 114 | 'do_not_log_sql_queries_connections' => [ 115 | 'tracker', 116 | ], 117 | 118 | /* 119 | * GeoIp2 database path. 120 | * 121 | * To get a fresh version of this file, use the command 122 | * 123 | * php artisan tracker:updategeoip 124 | * 125 | */ 126 | 127 | 'geoip_database_path' => __DIR__.'/geoip', //storage_path('geoip'), 128 | 129 | /* 130 | * Also log SQL query bindings? 131 | * 132 | * Log must be enabled for this option to work. 133 | */ 134 | 'log_sql_queries_bindings' => false, 135 | 136 | /* 137 | * Log events? 138 | */ 139 | 'log_events' => false, 140 | 141 | /* 142 | * Which events do you want to log exactly? 143 | */ 144 | 'log_only_events' => [ 145 | // defaults to logging all events 146 | ], 147 | 148 | /* 149 | * What are the names of the id columns on your system? 150 | * 151 | * 'id' is the most common, but if you have one or more different, 152 | * please add them here in your preference order. 153 | */ 154 | 'id_columns_names' => [ 155 | 'id', 156 | ], 157 | /* 158 | * Do not log events for the following patterns. 159 | * Strings accepts wildcards: 160 | * 161 | * eloquent.* 162 | * 163 | */ 164 | 'do_not_log_events' => [ 165 | 'illuminate.log', 166 | 'eloquent.*', 167 | 'router.*', 168 | 'composing: *', 169 | 'creating: *', 170 | ], 171 | 172 | /* 173 | * Do you wish to log Geo IP data? 174 | * 175 | * You will need to install the geoip package 176 | * 177 | * composer require "geoip/geoip":"~1.14" 178 | * 179 | * And remove the PHP module 180 | * 181 | * sudo apt-get purge php5-geoip 182 | * 183 | */ 184 | 'log_geoip' => false, 185 | 186 | /* 187 | * Do you wish to log the user agent? 188 | */ 189 | 'log_user_agents' => false, 190 | 191 | /* 192 | * Do you wish to log your users? 193 | */ 194 | 'log_users' => false, 195 | 196 | /* 197 | * Do you wish to log devices? 198 | */ 199 | 'log_devices' => false, 200 | 201 | /* 202 | * Do you wish to log languages? 203 | */ 204 | 'log_languages' => false, 205 | 206 | /* 207 | * Do you wish to log HTTP referers? 208 | */ 209 | 'log_referers' => false, 210 | 211 | /* 212 | * Do you wish to log url paths? 213 | */ 214 | 'log_paths' => false, 215 | 216 | /* 217 | * Do you wish to log url queries and query arguments? 218 | */ 219 | 'log_queries' => false, 220 | 221 | /* 222 | * Do you wish to log routes and route parameters? 223 | */ 224 | 'log_routes' => false, 225 | 226 | /* 227 | * Log errors and exceptions? 228 | */ 229 | 'log_exceptions' => false, 230 | 231 | /* 232 | * A cookie may be created on your visitor device, so you can have information 233 | * on everything made using that device on your site. * 234 | */ 235 | 'store_cookie_tracker' => false, 236 | 237 | /* 238 | * If you are storing cookies, you better change it to a name you of your own. 239 | */ 240 | 'tracker_cookie_name' => 'please_change_this_cookie_name', 241 | 242 | /* 243 | * Internal tracker session name. 244 | */ 245 | 'tracker_session_name' => 'tracker_session', 246 | 247 | /* 248 | * ** IMPORTANT ** 249 | * Change the user model to your own. 250 | * If the model is under a different connection, be specific. 251 | * ... 252 | * class ModelName { 253 | * protected $connection = 'mysql'; 254 | * ... 255 | */ 256 | 'user_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\User', 257 | 258 | /* 259 | * You can use your own model for every single table Tracker has. 260 | */ 261 | 262 | 'session_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Session', 263 | 264 | 'log_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Log', 265 | 266 | 'path_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Path', 267 | 268 | 'query_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Query', 269 | 270 | 'query_argument_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\QueryArgument', 271 | 272 | 'agent_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Agent', 273 | 274 | 'device_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Device', 275 | 276 | 'cookie_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Cookie', 277 | 278 | 'domain_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Domain', 279 | 280 | 'referer_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Referer', 281 | 282 | 'referer_search_term_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\RefererSearchTerm', 283 | 284 | 'route_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Route', 285 | 286 | 'route_path_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\RoutePath', 287 | 288 | 'route_path_parameter_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\RoutePathParameter', 289 | 290 | 'error_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Error', 291 | 292 | 'geoip_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\GeoIp', 293 | 294 | 'sql_query_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\SqlQuery', 295 | 296 | 'sql_query_binding_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\SqlQueryBinding', 297 | 298 | 'sql_query_binding_parameter_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\SqlQueryBindingParameter', 299 | 300 | 'sql_query_log_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\SqlQueryLog', 301 | 302 | 'connection_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Connection', 303 | 304 | 'event_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Event', 305 | 306 | 'event_log_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\EventLog', 307 | 308 | 'system_class_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\SystemClass', 309 | 310 | 'language_model' => 'PragmaRX\Tracker\Vendor\Laravel\Models\Language', 311 | 312 | /* 313 | * Laravel internal variables on user authentication and login. 314 | */ 315 | 'authentication_ioc_binding' => ['auth'], // defaults to 'auth' in Illuminate\Support\Facades\Auth 316 | 317 | 'authentication_guards' => [], // defaults to [] 318 | 319 | 'authenticated_check_method' => 'check', // to Auth::check() 320 | 321 | 'authenticated_user_method' => 'user', // to Auth::user() 322 | 323 | 'authenticated_user_id_column' => 'id', // to Auth::user()->id 324 | 325 | 'authenticated_user_username_column' => 'email', // to Auth::user()->email 326 | 327 | /* 328 | * Enable the Stats Panel? 329 | */ 330 | 'stats_panel_enabled' => false, 331 | 332 | /* 333 | * Stats Panel routes before filter 334 | * 335 | */ 336 | 'stats_routes_before_filter' => '', 337 | 338 | /* 339 | * Stats Panel routes after filter 340 | * 341 | */ 342 | 'stats_routes_after_filter' => '', 343 | 344 | /* 345 | * Stats Panel routes middleware 346 | * 347 | */ 348 | 'stats_routes_middleware' => 'web', 349 | 350 | /* 351 | * Stats Panel template path 352 | */ 353 | 'stats_template_path' => '/templates/sb-admin-2', 354 | 355 | /* 356 | * Stats Panel base uri. 357 | * 358 | * If your site url is http://wwww.mysite.com, then your stats page will be: 359 | * 360 | * http://wwww.mysite.com/stats 361 | * 362 | */ 363 | 'stats_base_uri' => 'stats', 364 | 365 | /* 366 | * Stats Panel layout view 367 | */ 368 | 'stats_layout' => 'pragmarx/tracker::layout', 369 | 370 | /* 371 | * Stats Panel controllers namespace 372 | */ 373 | 'stats_controllers_namespace' => 'PragmaRX\Tracker\Vendor\Laravel\Controllers', 374 | 375 | /* 376 | * Set a default user agent 377 | */ 378 | 'default_user_agent' => '', 379 | ]; 380 | -------------------------------------------------------------------------------- /src/lang/en/tracker.php: -------------------------------------------------------------------------------- 1 | 'Code', 5 | 'session' => 'Session', 6 | 'message' => 'Message', 7 | 'path' => 'Path', 8 | 'when' => 'When?', 9 | 'name' => 'Name', 10 | 'no_occurrences_in_period' => '# of occurrences in the period', 11 | 'tracker_title' => 'Laravel Stats Tracker', 12 | 'toggle_navigation' => 'Toggle navigation', 13 | 'today' => 'Today', 14 | 'no_days' => ':count Day|Days', 15 | 'no_years' => ':count Year|Years', 16 | 'visits' => 'Visits', 17 | 'summary' => 'Summary', 18 | 'users' => 'Users', 19 | 'user' => 'User', 20 | 'events' => 'Events', 21 | 'errors' => 'Errors', 22 | 'method' => 'Method', 23 | 'route_name_action' => 'Route Name / Action', 24 | 'route' => 'Route', 25 | 'language' => 'Language', 26 | 'query' => 'Query', 27 | 'is_ajax' => 'Is Ajax?', 28 | 'is_secure' => 'Is Secure?', 29 | 'is_json' => 'Is Json?', 30 | 'wants_json' => 'Wants Json?', 31 | 'error_q' => 'Error?', 32 | 'created_at' => 'Created At', 33 | 'go_home' => 'Go home »', 34 | 'page_views_by_country' => 'Page Views by Country', 35 | 'email' => 'Email', 36 | 'last_seen' => 'Last Seen', 37 | 'regex_file_not_available' => 'Tracker: uaparser regex file not available. "Execute php artisan tracker:updateparser" to generate it.', 38 | 'auth_required' => 'Authentication required', 39 | 'miss_admin_prop' => 'User model misses admin property', 40 | 'not_admin' => 'You are not Admin', 41 | 'id' => 'Id', 42 | 'ip_address' => 'IP Address', 43 | 'country_city' => 'Country / City', 44 | 'device' => 'Device', 45 | 'browser' => 'Browser', 46 | 'referer' => 'Referer', 47 | 'page_views' => 'Page Views', 48 | 'last_activity' => 'Last Activity', 49 | 'log' => 'Log', 50 | 'page_views_summary' => 'Page Views Summary', 51 | ]; 52 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311070_create_tracker_paths_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('path')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311071_create_tracker_queries_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('query')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311072_create_tracker_queries_arguments_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('query_id')->unsigned()->index(); 27 | $table->string('argument')->index(); 28 | $table->string('value')->index(); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311073_create_tracker_routes_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->index(); 27 | $table->string('action')->index(); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311074_create_tracker_routes_paths_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('route_id')->unsigned()->index(); 27 | $table->string('path')->index(); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311075_create_tracker_route_path_parameters_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('route_path_id')->unsigned()->index(); 27 | $table->string('parameter')->index(); 28 | $table->string('value')->index(); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311076_create_tracker_agents_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->unique(); 27 | $table->string('browser')->index(); 28 | $table->string('browser_version'); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311077_create_tracker_cookies_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('uuid')->unique(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311078_create_tracker_devices_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('kind', 16)->index(); 27 | $table->string('model', 64)->index(); 28 | $table->string('platform', 64)->index(); 29 | $table->string('platform_version', 16)->index(); 30 | $table->boolean('is_mobile'); 31 | 32 | $table->unique(['kind', 'model', 'platform', 'platform_version']); 33 | 34 | $table->timestamps(); 35 | $table->index('created_at'); 36 | $table->index('updated_at'); 37 | } 38 | ); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | * 44 | * @return void 45 | */ 46 | public function migrateDown() 47 | { 48 | $this->drop($this->table); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311079_create_tracker_domains_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311080_create_tracker_referers_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('domain_id')->unsigned()->index(); 27 | $table->string('url')->index(); 28 | $table->string('host'); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311081_create_tracker_geoip_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->double('latitude')->nullable()->index(); 27 | $table->double('longitude')->nullable()->index(); 28 | 29 | $table->string('country_code', 2)->nullable()->index(); 30 | $table->string('country_code3', 3)->nullable()->index(); 31 | $table->string('country_name')->nullable()->index(); 32 | $table->string('region', 2)->nullable(); 33 | $table->string('city', 50)->nullable()->index(); 34 | $table->string('postal_code', 20)->nullable(); 35 | $table->bigInteger('area_code')->nullable(); 36 | $table->double('dma_code')->nullable(); 37 | $table->double('metro_code')->nullable(); 38 | $table->string('continent_code', 2)->nullable(); 39 | 40 | $table->timestamps(); 41 | $table->index('created_at'); 42 | $table->index('updated_at'); 43 | } 44 | ); 45 | } 46 | 47 | /** 48 | * Reverse the migrations. 49 | * 50 | * @return void 51 | */ 52 | public function migrateDown() 53 | { 54 | $this->drop($this->table); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311082_create_tracker_sessions_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('uuid')->unique()->index(); 27 | $table->bigInteger('user_id')->unsigned()->nullable()->index(); 28 | $table->bigInteger('device_id')->unsigned()->nullable()->index(); 29 | $table->bigInteger('agent_id')->unsigned()->nullable()->index(); 30 | $table->string('client_ip')->index(); 31 | $table->bigInteger('referer_id')->unsigned()->nullable()->index(); 32 | $table->bigInteger('cookie_id')->unsigned()->nullable()->index(); 33 | $table->bigInteger('geoip_id')->unsigned()->nullable()->index(); 34 | $table->boolean('is_robot'); 35 | 36 | $table->timestamps(); 37 | $table->index('created_at'); 38 | $table->index('updated_at'); 39 | } 40 | ); 41 | } 42 | 43 | /** 44 | * Reverse the migrations. 45 | * 46 | * @return void 47 | */ 48 | public function migrateDown() 49 | { 50 | $this->drop($this->table); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311083_create_tracker_errors_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('code')->index(); 27 | $table->string('message')->index(); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311084_create_tracker_system_classes_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311085_create_tracker_log_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('session_id')->unsigned()->index(); 27 | $table->bigInteger('path_id')->unsigned()->nullable()->index(); 28 | $table->bigInteger('query_id')->unsigned()->nullable()->index(); 29 | $table->string('method', 10)->index(); 30 | $table->bigInteger('route_path_id')->unsigned()->nullable()->index(); 31 | $table->boolean('is_ajax'); 32 | $table->boolean('is_secure'); 33 | $table->boolean('is_json'); 34 | $table->boolean('wants_json'); 35 | $table->bigInteger('error_id')->unsigned()->nullable()->index(); 36 | 37 | $table->timestamps(); 38 | $table->index('created_at'); 39 | $table->index('updated_at'); 40 | } 41 | ); 42 | } 43 | 44 | /** 45 | * Reverse the migrations. 46 | * 47 | * @return void 48 | */ 49 | public function migrateDown() 50 | { 51 | $this->drop($this->table); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311086_create_tracker_events_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311087_create_tracker_events_log_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('event_id')->unsigned()->index(); 27 | $table->bigInteger('class_id')->unsigned()->nullable()->index(); 28 | $table->bigInteger('log_id')->unsigned()->index(); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311088_create_tracker_sql_queries_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('sha1', 40)->index(); 27 | $table->text('statement'); 28 | $table->double('time')->index(); 29 | $table->integer('connection_id')->unsigned(); 30 | 31 | $table->timestamps(); 32 | $table->index('created_at'); 33 | $table->index('updated_at'); 34 | } 35 | ); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function migrateDown() 44 | { 45 | $this->drop($this->table); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311089_create_tracker_sql_query_bindings_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('sha1', 40)->index(); 27 | $table->text('serialized'); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311090_create_tracker_sql_query_bindings_parameters_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('sql_query_bindings_id')->unsigned()->nullable(); 27 | $table->string('name')->nullable()->index(); 28 | $table->text('value')->nullable(); 29 | 30 | $table->timestamps(); 31 | $table->index('created_at'); 32 | $table->index('updated_at'); 33 | } 34 | ); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function migrateDown() 43 | { 44 | $this->drop($this->table); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311091_create_tracker_sql_queries_log_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('log_id')->unsigned()->index(); 27 | $table->bigInteger('sql_query_id')->unsigned()->index(); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311092_create_tracker_connections_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('name')->index(); 27 | 28 | $table->timestamps(); 29 | $table->index('created_at'); 30 | $table->index('updated_at'); 31 | } 32 | ); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function migrateDown() 41 | { 42 | $this->drop($this->table); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/migrations/2015_03_07_311093_create_tracker_tables_relations.php: -------------------------------------------------------------------------------- 1 | builder->table('tracker_query_arguments', function ($table) { 15 | $table->foreign('query_id') 16 | ->references('id') 17 | ->on('tracker_queries') 18 | ->onUpdate('cascade') 19 | ->onDelete('cascade'); 20 | }); 21 | 22 | $this->builder->table('tracker_route_paths', function ($table) { 23 | $table->foreign('route_id') 24 | ->references('id') 25 | ->on('tracker_routes') 26 | ->onUpdate('cascade') 27 | ->onDelete('cascade'); 28 | }); 29 | 30 | $this->builder->table('tracker_route_path_parameters', function ($table) { 31 | $table->foreign('route_path_id') 32 | ->references('id') 33 | ->on('tracker_route_paths') 34 | ->onUpdate('cascade') 35 | ->onDelete('cascade'); 36 | }); 37 | 38 | $this->builder->table('tracker_referers', function ($table) { 39 | $table->foreign('domain_id') 40 | ->references('id') 41 | ->on('tracker_domains') 42 | ->onUpdate('cascade') 43 | ->onDelete('cascade'); 44 | }); 45 | 46 | $this->builder->table('tracker_sessions', function ($table) { 47 | $table->foreign('device_id') 48 | ->references('id') 49 | ->on('tracker_devices') 50 | ->onUpdate('cascade') 51 | ->onDelete('cascade'); 52 | }); 53 | 54 | $this->builder->table('tracker_sessions', function ($table) { 55 | $table->foreign('agent_id') 56 | ->references('id') 57 | ->on('tracker_agents') 58 | ->onUpdate('cascade') 59 | ->onDelete('cascade'); 60 | }); 61 | 62 | $this->builder->table('tracker_sessions', function ($table) { 63 | $table->foreign('referer_id') 64 | ->references('id') 65 | ->on('tracker_referers') 66 | ->onUpdate('cascade') 67 | ->onDelete('cascade'); 68 | }); 69 | 70 | $this->builder->table('tracker_sessions', function ($table) { 71 | $table->foreign('cookie_id') 72 | ->references('id') 73 | ->on('tracker_cookies') 74 | ->onUpdate('cascade') 75 | ->onDelete('cascade'); 76 | }); 77 | 78 | $this->builder->table('tracker_sessions', function ($table) { 79 | $table->foreign('geoip_id') 80 | ->references('id') 81 | ->on('tracker_geoip') 82 | ->onUpdate('cascade') 83 | ->onDelete('cascade'); 84 | }); 85 | 86 | $this->builder->table('tracker_log', function ($table) { 87 | $table->foreign('session_id') 88 | ->references('id') 89 | ->on('tracker_sessions') 90 | ->onUpdate('cascade') 91 | ->onDelete('cascade'); 92 | }); 93 | 94 | $this->builder->table('tracker_log', function ($table) { 95 | $table->foreign('path_id') 96 | ->references('id') 97 | ->on('tracker_paths') 98 | ->onUpdate('cascade') 99 | ->onDelete('cascade'); 100 | }); 101 | 102 | $this->builder->table('tracker_log', function ($table) { 103 | $table->foreign('query_id') 104 | ->references('id') 105 | ->on('tracker_queries') 106 | ->onUpdate('cascade') 107 | ->onDelete('cascade'); 108 | }); 109 | 110 | $this->builder->table('tracker_log', function ($table) { 111 | $table->foreign('route_path_id') 112 | ->references('id') 113 | ->on('tracker_route_paths') 114 | ->onUpdate('cascade') 115 | ->onDelete('cascade'); 116 | }); 117 | 118 | $this->builder->table('tracker_log', function ($table) { 119 | $table->foreign('error_id') 120 | ->references('id') 121 | ->on('tracker_errors') 122 | ->onUpdate('cascade') 123 | ->onDelete('cascade'); 124 | }); 125 | 126 | $this->builder->table('tracker_events_log', function ($table) { 127 | $table->foreign('event_id') 128 | ->references('id') 129 | ->on('tracker_events') 130 | ->onUpdate('cascade') 131 | ->onDelete('cascade'); 132 | }); 133 | 134 | $this->builder->table('tracker_events_log', function ($table) { 135 | $table->foreign('class_id') 136 | ->references('id') 137 | ->on('tracker_system_classes') 138 | ->onUpdate('cascade') 139 | ->onDelete('cascade'); 140 | }); 141 | 142 | $this->builder->table('tracker_events_log', function ($table) { 143 | $table->foreign('log_id') 144 | ->references('id') 145 | ->on('tracker_log') 146 | ->onUpdate('cascade') 147 | ->onDelete('cascade'); 148 | }); 149 | 150 | $this->builder->table('tracker_sql_query_bindings_parameters', function ($table) { 151 | $table->foreign('sql_query_bindings_id', 'tracker_sqlqb_parameters') 152 | ->references('id') 153 | ->on('tracker_sql_query_bindings') 154 | ->onUpdate('cascade') 155 | ->onDelete('cascade'); 156 | }); 157 | 158 | $this->builder->table('tracker_sql_queries_log', function ($table) { 159 | $table->foreign('log_id') 160 | ->references('id') 161 | ->on('tracker_log') 162 | ->onUpdate('cascade') 163 | ->onDelete('cascade'); 164 | }); 165 | 166 | $this->builder->table('tracker_sql_queries_log', function ($table) { 167 | $table->foreign('sql_query_id') 168 | ->references('id') 169 | ->on('tracker_sql_queries') 170 | ->onUpdate('cascade') 171 | ->onDelete('cascade'); 172 | }); 173 | } 174 | 175 | /** 176 | * Reverse the migrations. 177 | * 178 | * @return void 179 | */ 180 | public function migrateDown() 181 | { 182 | // Tables will be dropped in the correct order... :) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/migrations/2015_03_13_311094_create_tracker_referer_search_term_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->bigInteger('referer_id')->unsigned()->index(); 27 | $table->string('search_term')->index(); 28 | 29 | $table->timestamps(); 30 | $table->index('created_at'); 31 | $table->index('updated_at'); 32 | } 33 | ); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function migrateDown() 42 | { 43 | $this->drop($this->table); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/2015_03_13_311095_add_tracker_referer_columns.php: -------------------------------------------------------------------------------- 1 | builder->table( 24 | $this->table, 25 | function ($table) { 26 | $table->string('medium')->nullable()->index(); 27 | $table->string('source')->nullable()->index(); 28 | $table->string('search_terms_hash')->nullable()->index(); 29 | } 30 | ); 31 | 32 | $this->builder->table($this->foreign, function ($table) { 33 | $table->foreign('referer_id', 'tracker_referers_referer_id_fk') 34 | ->references('id') 35 | ->on('tracker_referers') 36 | ->onUpdate('cascade') 37 | ->onDelete('cascade'); 38 | }); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | * 44 | * @return void 45 | */ 46 | public function migrateDown() 47 | { 48 | $this->builder->table( 49 | $this->table, 50 | function ($table) { 51 | $table->dropColumn('medium'); 52 | $table->dropColumn('source'); 53 | $table->dropColumn('search_terms_hash'); 54 | } 55 | ); 56 | 57 | $this->builder->table( 58 | $this->foreign, 59 | function ($table) { 60 | $table->dropForeign('tracker_referers_referer_id_fk'); 61 | } 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/migrations/2015_11_23_311096_add_tracker_referer_column_to_log.php: -------------------------------------------------------------------------------- 1 | builder->table( 22 | $this->table, 23 | function ($table) { 24 | $table->integer('referer_id')->unsigned()->nullable()->index(); 25 | } 26 | ); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function migrateDown() 35 | { 36 | $this->builder->table( 37 | $this->table, 38 | function ($table) { 39 | $table->dropColumn('referer_id'); 40 | } 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/migrations/2015_11_23_311097_create_tracker_languages_table.php: -------------------------------------------------------------------------------- 1 | builder->create( 22 | $this->table, 23 | function ($table) { 24 | $table->bigIncrements('id'); 25 | 26 | $table->string('preference')->index(); 27 | $table->string('language-range')->index(); 28 | 29 | $table->unique(['preference', 'language-range']); 30 | 31 | $table->timestamps(); 32 | $table->index('created_at'); 33 | $table->index('updated_at'); 34 | } 35 | ); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function migrateDown() 44 | { 45 | $this->drop($this->table); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/migrations/2015_11_23_311098_add_language_id_column_to_sessions.php: -------------------------------------------------------------------------------- 1 | builder->table( 22 | $this->table, 23 | function ($table) { 24 | $table->bigInteger('language_id')->unsigned()->nullable()->index(); 25 | } 26 | ); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function migrateDown() 35 | { 36 | $this->builder->table( 37 | $this->table, 38 | function ($table) { 39 | $table->dropColumn('language_id'); 40 | } 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/migrations/2015_11_23_311099_add_tracker_language_foreign_key_to_sessions.php: -------------------------------------------------------------------------------- 1 | builder->table('tracker_sessions', function ($table) { 15 | $table->foreign('language_id') 16 | ->references('id') 17 | ->on('tracker_languages') 18 | ->onUpdate('cascade') 19 | ->onDelete('cascade'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function migrateDown() 29 | { 30 | $this->builder->table('tracker_sessions', function ($table) { 31 | $table->dropForeign(['language_id']); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/migrations/2015_11_23_311100_add_nullable_to_tracker_error.php: -------------------------------------------------------------------------------- 1 | builder->table( 23 | $this->table, 24 | function ($table) { 25 | $table->string('code')->nullable()->change(); 26 | } 27 | ); 28 | } catch (\Exception $e) { 29 | } 30 | } 31 | 32 | /** 33 | * Reverse the migrations. 34 | * 35 | * @return void 36 | */ 37 | public function migrateDown() 38 | { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/migrations/2017_01_31_311101_fix_agent_name.php: -------------------------------------------------------------------------------- 1 | builder->table( 23 | $this->table, 24 | function ($table) { 25 | $table->dropUnique('tracker_agents_name_unique'); 26 | } 27 | ); 28 | 29 | $this->builder->table( 30 | $this->table, 31 | function ($table) { 32 | $table->mediumText('name')->change(); 33 | } 34 | ); 35 | 36 | $this->builder->table( 37 | $this->table, 38 | function ($table) { 39 | $table->unique('id', 'tracker_agents_name_unique'); // this is a dummy index 40 | } 41 | ); 42 | } catch (\Exception $e) { 43 | dd($e->getMessage()); 44 | } 45 | } 46 | 47 | /** 48 | * Reverse the migrations. 49 | * 50 | * @return void 51 | */ 52 | public function migrateDown() 53 | { 54 | try { 55 | $this->builder->table( 56 | $this->table, 57 | function ($table) { 58 | $table->string('name', 255)->change(); 59 | $table->unique('name'); 60 | } 61 | ); 62 | } catch (\Exception $e) { 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/migrations/2017_06_20_311102_add_agent_name_hash.php: -------------------------------------------------------------------------------- 1 | builder->table( 24 | $this->table, 25 | function ($table) { 26 | $table->dropUnique('tracker_agents_name_unique'); 27 | 28 | $table->string('name_hash', 65)->nullable(); 29 | } 30 | ); 31 | 32 | Agent::all()->each(function ($agent) { 33 | $agent->name_hash = hash('sha256', $agent->name); 34 | 35 | $agent->save(); 36 | }); 37 | 38 | $this->builder->table( 39 | $this->table, 40 | function ($table) { 41 | $table->unique('name_hash'); 42 | } 43 | ); 44 | } catch (\Exception $e) { 45 | dd($e->getMessage()); 46 | } 47 | } 48 | 49 | /** 50 | * Reverse the migrations. 51 | * 52 | * @return void 53 | */ 54 | public function migrateDown() 55 | { 56 | try { 57 | $this->builder->table( 58 | $this->table, 59 | function ($table) { 60 | $table->dropUnique('tracker_agents_name_hash_unique'); 61 | 62 | $table->dropColumn('name_hash'); 63 | 64 | $table->mediumText('name')->unique()->change(); 65 | } 66 | ); 67 | } catch (\Exception $e) { 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/migrations/2017_12_13_150000_fix_query_arguments.php: -------------------------------------------------------------------------------- 1 | builder->table( 23 | $this->table, 24 | function ($table) { 25 | $table->string('value')->nullable()->change(); 26 | } 27 | ); 28 | } catch (\Exception $e) { 29 | dd($e->getMessage()); 30 | } 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function migrateDown() 39 | { 40 | try { 41 | $this->builder->table( 42 | $this->table, 43 | function ($table) { 44 | $table->string('value')->change(); 45 | } 46 | ); 47 | } catch (\Exception $e) { 48 | dd($e->getMessage()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/views/_dataTable.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 67 | -------------------------------------------------------------------------------- /src/views/_datatables.blade.php: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#table_div').dataTable( { 3 | "processing": true, 4 | "serverSide": true, 5 | "bFilter": false, 6 | "ajax": "{{$datatables_ajax_route}}", 7 | "columnDefs": [ { 8 | "targets": "_all", 9 | "defaultContent": "" 10 | } ], 11 | "columns": [ 12 | 13 | ] 14 | } ); 15 | } ); 16 | -------------------------------------------------------------------------------- /src/views/_summaryPiechart.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 45 | -------------------------------------------------------------------------------- /src/views/errors.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('inline-javascript') 8 | @include( 9 | 'pragmarx/tracker::_datatables', 10 | array( 11 | 'datatables_ajax_route' => route('tracker.stats.api.errors'), 12 | 'datatables_columns' => 13 | ' 14 | { "data" : "error.code", "title" : "'.trans('tracker::tracker.code').'", "orderable": true, "searchable": false }, 15 | { "data" : "session.uuid", "title" : "'.trans('tracker::tracker.session').'", "orderable": true, "searchable": false }, 16 | { "data" : "error.message", "title" : "'.trans('tracker::tracker.message').'", "orderable": true, "searchable": false }, 17 | { "data" : "path.path", "title" : "'.trans('tracker::tracker.path').'", "orderable": true, "searchable": false }, 18 | { "data" : "updated_at", "title" : "'.trans('tracker::tracker.when').'", "orderable": true, "searchable": false }, 19 | ' 20 | ) 21 | ) 22 | @stop 23 | -------------------------------------------------------------------------------- /src/views/events.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('inline-javascript') 8 | @include( 9 | 'pragmarx/tracker::_datatables', 10 | array( 11 | 'datatables_ajax_route' => route('tracker.stats.api.events'), 12 | 'datatables_columns' => 13 | ' 14 | { "data" : "name", "title" : "'.trans('tracker::tracker.name').'", "orderable": true, "searchable": false }, 15 | { "data" : "total", "title" : "'.trans('tracker::tracker.no_occurrences_in_period').'", "orderable": true, "searchable": false }, 16 | ' 17 | ) 18 | ) 19 | @stop 20 | -------------------------------------------------------------------------------- /src/views/html.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | @lang("tracker::tracker.tracker_title") 9 | 10 | 11 | 12 | @yield('required-scripts-top') 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | @yield('body') 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | @yield('required-scripts-bottom') 52 | 53 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/views/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('inline-javascript') 8 | @include('pragmarx/tracker::_datatables', $datatables_data) 9 | @stop 10 | -------------------------------------------------------------------------------- /src/views/layout.blade.php: -------------------------------------------------------------------------------- 1 | @extends('pragmarx/tracker::html') 2 | 3 | @section('body') 4 |
5 | 65 | 66 |
67 |
68 |
69 | 70 |
71 | 72 |
73 | @yield('page-contents') 74 |
75 |
76 | 77 |
78 |
79 | @yield('page-secondary-contents') 80 |
81 |
82 | 83 | 84 |
85 | 86 | 87 |
88 | 89 | @stop 90 | -------------------------------------------------------------------------------- /src/views/log.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('inline-javascript') 8 | @include( 9 | 'pragmarx/tracker::_datatables', 10 | array( 11 | 'datatables_ajax_route' => route('tracker.stats.api.log', array('uuid' => $uuid)), 12 | 'datatables_columns' => 13 | ' 14 | { "data" : "method", "title" : "'.trans('tracker::tracker.method').'", "orderable": true, "searchable": false }, 15 | { "data" : "route_name", "title" : "'.trans('tracker::tracker.route_name_action').'", "orderable": true, "searchable": false }, 16 | { "data" : "route", "title" : "'.trans('tracker::tracker.route').'", "orderable": true, "searchable": false }, 17 | { "data" : "query", "title" : "'.trans('tracker::tracker.query').'", "orderable": true, "searchable": false }, 18 | { "data" : "is_ajax", "title" : "'.trans('tracker::tracker.is_ajax').'", "orderable": true, "searchable": false }, 19 | { "data" : "is_secure", "title" : "'.trans('tracker::tracker.is_secure').'", "orderable": true, "searchable": false }, 20 | { "data" : "is_json", "title" : "'.trans('tracker::tracker.is_json').'", "orderable": true, "searchable": false }, 21 | { "data" : "wants_json", "title" : "'.trans('tracker::tracker.wants_json').'", "orderable": true, "searchable": false }, 22 | { "data" : "error", "title" : "'.trans('tracker::tracker.error_q').'", "orderable": true, "searchable": false }, 23 | { "data" : "created_at", "title" : "'.trans('tracker::tracker.created_at').'", "orderable": true, "searchable": false }, 24 | ' 25 | ) 26 | ) 27 | @stop 28 | 29 | -------------------------------------------------------------------------------- /src/views/message.blade.php: -------------------------------------------------------------------------------- 1 | @extends('pragmarx/tracker::html') 2 | 3 | @section('body') 4 |
5 |
6 |

{{ $message }}

7 |
8 |

9 | @lang('tracker::tracker.go_home') 10 |

11 |
12 |
13 | @stop 14 | -------------------------------------------------------------------------------- /src/views/screenshots/errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/views/screenshots/errors.png -------------------------------------------------------------------------------- /src/views/screenshots/events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/views/screenshots/events.png -------------------------------------------------------------------------------- /src/views/screenshots/summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/views/screenshots/summary.png -------------------------------------------------------------------------------- /src/views/screenshots/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/views/screenshots/users.png -------------------------------------------------------------------------------- /src/views/screenshots/visits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/src/views/screenshots/visits.png -------------------------------------------------------------------------------- /src/views/summary.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('page-secondary-contents') 8 |
9 |
10 |
11 |
12 |

@lang('tracker::tracker.page_views_by_country')

13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 | @include('pragmarx/tracker::_summaryPiechart') 22 | @stop 23 | 24 | @section('inline-javascript') 25 | jQuery(function() 26 | { 27 | console.log(jQuery('#pageViews')); 28 | 29 | var pageViewsLine = Morris.Line({ 30 | element: 'pageViewsLine', 31 | parseTime:false, 32 | grid: true, 33 | data: [{'date': 0, 'total': 0}], 34 | xkey: 'date', 35 | ykeys: ['total'], 36 | labels: ['Page Views'] 37 | }); 38 | 39 | jQuery.ajax({ 40 | type: "GET", 41 | url: "{{ route('tracker.stats.api.pageviews') }}", 42 | data: { } 43 | }) 44 | .done(function( data ) { 45 | console.log(data); 46 | pageViewsLine.setData(formatDates(data)); 47 | }); 48 | 49 | var convertToPlottableData = function(data) 50 | { 51 | plottable = []; 52 | 53 | jsondata = JSON.parse(data); 54 | 55 | for(key in jsondata) 56 | { 57 | plottable[key] = { 58 | label: jsondata[key].label, 59 | data: jsondata[key].value 60 | } 61 | } 62 | 63 | return plottable; 64 | }; 65 | 66 | var formatDates = function(data) 67 | { 68 | data = JSON.parse(data); 69 | 70 | for(key in data) 71 | { 72 | if (data[key].date !== 'undefined') 73 | { 74 | data[key].date = moment(data[key].date, "YYYY-MM-DD").format('dddd[,] MMM Do'); 75 | } 76 | } 77 | 78 | return data; 79 | }; 80 | }); 81 | @stop 82 | 83 | @section('required-scripts-top') 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | @stop 95 | -------------------------------------------------------------------------------- /src/views/users.blade.php: -------------------------------------------------------------------------------- 1 | @extends($stats_layout) 2 | 3 | @section('page-contents') 4 |
5 | @stop 6 | 7 | @section('inline-javascript') 8 | @include( 9 | 'pragmarx/tracker::_datatables', 10 | array( 11 | 'datatables_ajax_route' => route('tracker.stats.api.users'), 12 | 'datatables_columns' => 13 | ' 14 | { "data" : "user_id", "title" : "'.trans('tracker::tracker.email').'", "orderable": true, "searchable": false }, 15 | { "data" : "updated_at", "title" : "'.trans('tracker::tracker.last_seen').'", "orderable": true, "searchable": false }, 16 | ' 17 | ) 18 | ) 19 | @stop 20 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonioribeiro/tracker/07e45b8a5846ac61160e73a952713306deeb5d92/tests/.gitkeep -------------------------------------------------------------------------------- /upgrading.md: -------------------------------------------------------------------------------- 1 | # Laravel Stats Tracker Upgrading Guide 2 | 3 | ## from 2.0.x to 3.0.0 4 | 5 | Add cache_enabled key to your config\tracker.php: 6 | 7 | /* 8 | * Enable cache? 9 | */ 10 | 'cache_enabled' => true, 11 | 12 | ## from 2.0.x to 2.0.4 or 2.0.9 13 | 14 | #### Run Artisan tracker:tables command 15 | 16 | php artisan tracker:tables 17 | 18 | #### Migrate 19 | 20 | php artisan migrate 21 | 22 | #### If you already have executed and get the error "1215 Cannot add foreign key constraint" 23 | 24 | You'll have to upgrade your migrations: 25 | 26 | 1 - Rollback the last one: `php artisan tracker:tables` 27 | 28 | 2 - Delete the following files from your database\migrations: 29 | 30 | 2015_11_23_311097_create_tracker_languages_table.php 31 | 2015_11_23_311098_add_language_id_column_to_sessions.php 32 | 2015_11_23_311099_add_tracker_language_foreign_key_to_sessions.php 33 | 2015_11_23_311100_add_nullable_to_tracker_error.php 34 | 35 | 3 - Run `php artisan tracker:tables` to upgrade them 36 | 37 | 4 - Migrate: `php artisan migrate` 38 | 39 | ## to 0.6.0 40 | 41 | A [massive update happened at StartBootstrap](https://github.com/IronSummitMedia/startbootstrap/commit/66716399cf8eb5109498d41a2dad95a093c18f2b), you need to download and unzip the admin frontend again: 42 | 43 | ``` 44 | rm -rf public/templates/sb-admin-v2 45 | wget --output-document=/tmp/sba2.zip http://startbootstrap.com/downloads/sb-admin-2.zip 46 | unzip /tmp/sba2.zip -d public/templates/ 47 | ``` 48 | 49 | ## to 0.5.1 50 | 51 | #### As `tracker_route_paths.route_id` column was wrongly set to string, you need to change it to int8 or bigint. This is how you do this 52 | 53 | ##### In PostgreSQL 54 | ``` 55 | ALTER TABLE "tracker_route_paths" ALTER COLUMN route_id TYPE BIGINT 56 | USING CAST(CASE route_id WHEN '' THEN NULL ELSE route_id END AS BIGINT) 57 | ``` 58 | 59 | ##### In MySQL 60 | ``` 61 | ALTER TABLE tracker_route_paths CHANGE route_id route_id bigint unsigned NULL; 62 | ``` 63 | 64 | #### Add the following keys to your `app/config/packages/pragmarx/tracker/config.php`: 65 | 66 | ``` 67 | 'log_exceptions' => true, 68 | 69 | 'authenticated_user_username_column' => 'email', 70 | 71 | 'do_not_track_routes' => array( 72 | 'tracker.stats.*', 73 | ), 74 | ``` 75 | 76 | ## to 0.5.0 77 | 78 | #### Download sb-panel v2, if you want to access the new Stats Panel: 79 | 80 | ``` 81 | wget --output-document=/tmp/sba2.zip http://startbootstrap.com/downloads/sb-admin-v2.zip 82 | unzip /tmp/sba2.zip -d public/templates/ 83 | ``` 84 | 85 | #### Add the following keys to your `app/config/packages/pragmarx/tracker/config.php`: 86 | 87 | ``` 88 | /** 89 | * Enable the Stats Panel? 90 | */ 91 | 'stats_panel_enabled' => false, 92 | 93 | /** 94 | * Stats Panel routes before filter 95 | * 96 | * You better drop an 'auth' filter here. 97 | */ 98 | 'stats_routes_before_filter' => '', 99 | 100 | /** 101 | * Stats Panel template path 102 | */ 103 | 'stats_template_path' => '/templates/sb-admin-v2', 104 | 105 | /** 106 | * Stats Panel base uri. 107 | * 108 | * If your site url is http://wwww.mysite.com, then your stats page will be: 109 | * 110 | * http://wwww.mysite.com/stats 111 | * 112 | */ 113 | 'stats_base_uri' => 'stats', 114 | 115 | /** 116 | * Stats Panel layout view 117 | */ 118 | 'stats_layout' => 'pragmarx/tracker::layout', 119 | 120 | /** 121 | * Stats Panel controllers namespace 122 | */ 123 | 'stats_controllers_namespace' => 'PragmaRX\Tracker\Vendor\Laravel\Controllers', 124 | ``` 125 | 126 | #### The Stats Panel must be enabled in your config file 127 | 128 | ``` 129 | 'stats_panel_enabled' => true, 130 | ``` 131 | 132 | ## to 0.4.0 133 | 134 | #### Add the following keys to your `app/config/packages/pragmarx/tracker/config.php`: 135 | 136 | ``` 137 | 'log_geoip' => true, 138 | 'log_user_agents' => true, 139 | 'log_users' => true, 140 | 'log_devices' => true, 141 | 'log_referers' => true, 142 | 'log_paths' => true, 143 | 'log_queries' => true, 144 | 'log_routes' => true, 145 | ``` 146 | 147 | #### On `tracker_sessions` table, alter columns `device_id` and `agent_id` to be nullable. 148 | #### On `tracker_log` table, alter column `path_id` to be nullable. 149 | 150 | ## to 0.3.2 151 | 152 | - Add a is_robot boolean column to: `ALTER TABLE tracker_sessions ADD is_robot BOOL;` 153 | - Add `'do_not_track_robots' => true or false,` to `tracker\config.php`. 154 | - Change `tracker_events_log.class_id` to be a nullable column. 155 | --------------------------------------------------------------------------------