├── .editorconfig ├── .gitattributes ├── .gitignore ├── .styleci.yml ├── LICENCE ├── README.md ├── composer.json └── src ├── Builder.php ├── Builder └── Link.php ├── Config └── links.php ├── Controllers └── LinksController.php ├── Facades └── Links.php ├── Farcades └── Link.php ├── LinksServiceProvider.php ├── Middleware └── LinksMiddleware.php ├── Migrations ├── 2016_10_30_174357_links.php └── 2016_10_30_174359_link_views.php ├── Models ├── Link.php └── View.php ├── Routes └── web.php ├── Views ├── link.blade.php ├── links.blade.php ├── login.blade.php ├── pagination.blade.php ├── specific.blade.php └── template.blade.php └── countries.json /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.yml] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | phpunit.xml 3 | vendor 4 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | linting: true 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Laralum (Èrik Campobadal - ConsoleTVs) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Links 2 | ### Links statistics for laravel 5 3 | 4 | [![StyleCI](https://styleci.io/repos/72780598/shield?branch=master)](https://styleci.io/repos/72780598) 5 | ![StyleCI](https://img.shields.io/badge/Built_for-Laravel-green.svg?style=flat-square) 6 | ![StyleCI](https://img.shields.io/github/license/consoletvs/links.svg?style=flat-square) 7 | 8 | 9 | ![Links Logo](http://i.imgur.com/tWlribC.png) 10 | 11 | ![Sample 1](https://i.gyazo.com/7ec31509b21f392ff93b1b4339a001c9.png) 12 | 13 | ![Sample 2](https://i.gyazo.com/faa9a5b99a816d366348a6f85826602b.png) 14 | 15 | ![Sample 3](https://i.gyazo.com/51d0f03789670f7d31cc4cceead62ab5.png) 16 | 17 | ![Sample 4](https://i.gyazo.com/fbdc7fdc83ca27cf3818ad2c4479f893.png) 18 | 19 | ## Table Of Contents 20 | 21 | - [Installation](#installation) 22 | - [Configuration](#configuration) 23 | - [Usage](#usage) 24 | 25 | # Installation 26 | 27 | To install charts use composer 28 | 29 | ### Download 30 | 31 | ``` 32 | composer require consoletvs/links 33 | ``` 34 | 35 | ### Add service provider & alias 36 | 37 | Add the following service provider to the array in: ```config/app.php``` 38 | 39 | ```php 40 | ConsoleTVs\Links\LinksServiceProvider::class, 41 | ``` 42 | 43 | Add the following alias to the array in: ```config/app.php``` 44 | 45 | ```php 46 | 'Links' => ConsoleTVs\Links\Facades\Links::class, 47 | ``` 48 | ### Publish the assets 49 | 50 | ``` 51 | php artisan vendor:publish 52 | ``` 53 | 54 | ### Migrate 55 | 56 | ``` 57 | php artisan migrate 58 | ``` 59 | # Configuration 60 | 61 | ## Default Settings 62 | 63 | The file in: ```config/links.php``` contains an array of settings, you can find the default settings in there. 64 | 65 | ```php 66 | ConsoleTVs\Links\Middleware\LinksMiddleware::class, 71 | 72 | /* Password to use if ConsoleTVs\Links\Middleware\LinksMiddleware is beeing used */ 73 | 'password' => 'LinksRocks', 74 | 75 | /* The views layout */ 76 | 'layout' => 'links::template', 77 | 78 | /* The route prefix, will be applied to all of the routes. */ 79 | 'prefix' => 'links', 80 | ]; 81 | ``` 82 | 83 | You should now modify the password if you're willing to use the default middleware. 84 | 85 | 86 | ### The Middleware 87 | 88 | The middleware is applied to the statistics page, this middleware can be changed and you're able to apply your own access rules. 89 | 90 | *Default:* ```ConsoleTVs\Links\Middleware\LinksMiddleware::class``` 91 | 92 | The default middleware requires a simple password to login. 93 | 94 | ### The Password (Only with the default middleware) 95 | 96 | The password needs to be set if you are using the default middleware. This will allow you to login. 97 | 98 | *Default:* ```LinksRocks``` 99 | 100 | ### The Layout 101 | 102 | The layout can be changed, but the current pages are designed using **Bootstrap 4** keep that in mind. 103 | 104 | *Default:* ```links::template``` 105 | 106 | ### The prefix 107 | 108 | The prefix will be used in all of the routes. It determines the root of all the routes of the package. 109 | 110 | *Default:* ```links``` 111 | 112 | # Usage 113 | 114 | ## Create Links 115 | 116 | To create links, go in the view where you want to add a traked link and instead of using the typical url operations: 117 | 118 | ```php 119 | {{ url('http://google.com') }} 120 | {{ route('google') }} 121 | ``` 122 | 123 | Use the package facade: 124 | 125 | ```php 126 | {{ Links::url('http://google.com') }} 127 | {{ Links::route('google') }} 128 | ``` 129 | 130 | ## Track Pages 131 | 132 | if you want to track down the current page, simply do this: 133 | 134 | **Note:** It uses jQuery! 135 | 136 | ```php 137 | // If jQuery .js is already included and you don't want conflits: 138 | {!! Links::track() !!} 139 | 140 | // If jQuery .js is not included in your view, this will also add it. 141 | {!! Links::track(true) !!} 142 | ``` 143 | 144 | **Quick tip:** Adding the track to the views layout will track all pages using that layout once visited! 145 | 146 | ## View the statistics 147 | 148 | To view all the links statistics go to the root of the package (the prefix). 149 | The default prefix is: ```links```. 150 | 151 | Once you are inside the links app. You'll need to login if you're using the default mdiddleware. 152 | The default password is: ```LinksRocks``` 153 | 154 | Once you're in the web app, you're ready to explore the statistics. 155 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "consoletvs/links", 3 | "description": "Links statistics for laravel 5", 4 | "require": { 5 | "php": ">=5.6.4", 6 | "laravel/framework": "^5.4", 7 | "consoletvs/charts": "2.*", 8 | "consoletvs/identify": "1.*" 9 | }, 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Erik Campobadal", 14 | "email": "soc@erik.cat" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "ConsoleTVs\\Links\\": "src/" 20 | } 21 | }, 22 | "minimum-stability": "dev" 23 | } 24 | -------------------------------------------------------------------------------- /src/Builder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ConsoleTVs\Links; 13 | 14 | use Request; 15 | use ConsoleTVs\Links\Builder\Link; 16 | use Illuminate\Support\Facades\File; 17 | use Illuminate\Support\Facades\Facade; 18 | 19 | /** 20 | * This is the link facade class. 21 | * 22 | * @author Erik Campobadal 23 | */ 24 | class Builder 25 | { 26 | /** 27 | * Return a new link instance from a url. 28 | * 29 | * @param string $url 30 | */ 31 | public static function url($url) 32 | { 33 | return new Link($url); 34 | } 35 | 36 | /** 37 | * Return a new link instance from a route name. 38 | * 39 | * @param string $url 40 | */ 41 | public static function route($name) 42 | { 43 | return new Link(route($name)); 44 | } 45 | 46 | /** 47 | * Create a new link from the current page url. 48 | * 49 | * @param string $url 50 | */ 51 | public static function track($jquery = false) 52 | { 53 | $link = new Link(Request::url()); 54 | 55 | return $link->ajax($jquery); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Builder/Link.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ConsoleTVs\Links\Builder; 13 | 14 | use ConsoleTVs\Links\Models\Link as Linker; 15 | 16 | /** 17 | * This is the link class. 18 | * 19 | * @author Erik Campobadal 20 | */ 21 | class Link 22 | { 23 | public $link; 24 | 25 | /** 26 | * Create a new link instance. 27 | * 28 | * @param string $url 29 | */ 30 | public function __construct($url) 31 | { 32 | if (! $link = Linker::where('url', $url)->first()) { 33 | $link = Linker::create([ 34 | 'url' => $url, 35 | 'slug' => $this->randomString(6), 36 | ]); 37 | } 38 | 39 | $this->link = $link; 40 | } 41 | 42 | /** 43 | * Return a random string. 44 | * 45 | * @param int $length 46 | */ 47 | public function randomString($length = 10) 48 | { 49 | return str_random($length); 50 | } 51 | 52 | /** 53 | * Returns the link. 54 | */ 55 | public function __toString() 56 | { 57 | return $this->link->shortered(); 58 | } 59 | 60 | /** 61 | * Returns the javascript code to send a and ajax request to the short url. 62 | */ 63 | public function ajax($jquery = false) 64 | { 65 | $url = $this->link->shortered(); 66 | 67 | $code = $jquery ? "" : ''; 68 | 69 | $code .= ''; 72 | 73 | return $code; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Config/links.php: -------------------------------------------------------------------------------- 1 | ConsoleTVs\Links\Middleware\LinksMiddleware::class, 6 | 7 | /* Password to use if ConsoleTVs\Links\Middleware\LinksMiddleware is beeing used */ 8 | 'password' => 'LinksRocks', 9 | 10 | /* The views layout */ 11 | 'layout' => 'links::template', 12 | 13 | /* The route prefix, will be applied to all of the routes. */ 14 | 'prefix' => 'links', 15 | ]; 16 | -------------------------------------------------------------------------------- /src/Controllers/LinksController.php: -------------------------------------------------------------------------------- 1 | route('links::links'); 20 | } 21 | 22 | return view('links::login'); 23 | } 24 | 25 | /** 26 | * Login the user. 27 | */ 28 | public function login(Request $request) 29 | { 30 | session(['links.password' => Crypt::encrypt($request->input('password'))]); 31 | 32 | if (Crypt::decrypt(session('links.password')) != config('links.password')) { 33 | return redirect()->route('links::login')->with('msg', 'The password is not correct'); 34 | } 35 | 36 | return redirect()->route('links::links'); 37 | } 38 | 39 | /** 40 | * Logout function. 41 | */ 42 | public function logout(Request $request) 43 | { 44 | $request->session()->forget('links.password'); 45 | 46 | return redirect()->route('links::links'); 47 | } 48 | 49 | /** 50 | * Show all the links statistics. 51 | */ 52 | public function links() 53 | { 54 | return view('links::links'); 55 | } 56 | 57 | /** 58 | * Show a single link statistics. 59 | * 60 | * @param int $id 61 | */ 62 | public function link($slug) 63 | { 64 | $link = Link::where('slug', $slug)->first(); 65 | 66 | if (! $link) { 67 | abort(404, 'The link was not found'); 68 | } 69 | 70 | return view('links::link', ['link' => $link]); 71 | } 72 | 73 | /** 74 | * Show a single link statistics. 75 | * 76 | * @param int $id 77 | */ 78 | public function specific($slug, $specific, $specific_value) 79 | { 80 | $link = Link::where('slug', $slug)->first(); 81 | 82 | if (! $link) { 83 | abort(404, 'The link was not found'); 84 | } 85 | 86 | $views = View::where($specific, $specific_value)->orderBy('id', 'desc')->get(); 87 | 88 | if (! $views->toArray()) { 89 | abort(404, 'No views found'); 90 | } 91 | 92 | $raw_specific_value = $specific_value; 93 | 94 | // Fancy specific_value if it's a language 95 | $countries = json_decode(file_get_contents(__DIR__.'/../countries.json'), true); 96 | foreach ($countries as $country) { 97 | if ($country['code'] == $specific_value) { 98 | $specific_value = explode(' ', str_replace(';', '', $country['name']))[0]; 99 | break; 100 | } 101 | } 102 | 103 | return view('links::specific', [ 104 | 'link' => $link, 105 | 'views' => $views, 106 | 'specific' => $specific, 107 | 'specific_value' => $specific_value, 108 | 'raw_specific_value' => $raw_specific_value, 109 | ]); 110 | } 111 | 112 | /** 113 | * Redirects the user to the link. 114 | * 115 | * @param string $slug 116 | */ 117 | public function redirect($slug) 118 | { 119 | if (! $link = Link::where('slug', $slug)->first()) { 120 | abort(404, 'Unable to find this link'); 121 | } 122 | 123 | $link->addView(); 124 | 125 | return redirect($link->url); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Facades/Links.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ConsoleTVs\Links\Facades; 13 | 14 | use ConsoleTVs\Links\Builder; 15 | use Illuminate\Support\Facades\Facade; 16 | 17 | /** 18 | * This is the links facade class. 19 | * 20 | * @author Erik Campobadal 21 | */ 22 | class Links extends Facade 23 | { 24 | /** 25 | * Get the registered name of the component. 26 | * 27 | * @return string 28 | */ 29 | protected static function getFacadeAccessor() 30 | { 31 | return Builder::class; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Farcades/Link.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ConsoleTVs\Links\Facades; 13 | 14 | use ConsoleTVs\Links\Builder; 15 | use Illuminate\Support\Facades\Facade; 16 | 17 | /** 18 | * This is the links facade class. 19 | * 20 | * @author Erik Campobadal 21 | */ 22 | class Link extends Facade 23 | { 24 | /** 25 | * Get the registered name of the component. 26 | * 27 | * @return string 28 | */ 29 | protected static function getFacadeAccessor() 30 | { 31 | return Builder::class; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/LinksServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->routesAreCached()) { 19 | require __DIR__.'/Routes/web.php'; 20 | } 21 | 22 | $this->publishes([ 23 | __DIR__.'/Config/links.php' => config_path('links.php'), 24 | ], 'links_config'); 25 | 26 | $router->aliasMiddleware('links.middleware', config('links.middleware')); 27 | 28 | $this->app->register(IdentifyServiceProvider::class); 29 | $this->app->register(ChartsServiceProvider::class); 30 | 31 | $this->loadViewsFrom(__DIR__.'/Views', 'links'); 32 | 33 | $this->loadMigrationsFrom(__DIR__.'/Migrations'); 34 | } 35 | 36 | /** 37 | * Register the application services. 38 | * 39 | * @return void 40 | */ 41 | public function register() 42 | { 43 | $this->mergeConfigFrom(__DIR__.'/Config/links.php', 'links'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Middleware/LinksMiddleware.php: -------------------------------------------------------------------------------- 1 | route('links::login') 23 | ->with('msg', 'Your login has expired or does not exist.'); 24 | } 25 | 26 | return $next($request); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Migrations/2016_10_30_174357_links.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('url'); 19 | $table->string('slug')->unique(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('links'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Migrations/2016_10_30_174359_link_views.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('link_id'); 19 | $table->string('language'); 20 | $table->string('browser'); 21 | $table->string('browser_version'); 22 | $table->string('os'); 23 | $table->string('os_version'); 24 | $table->string('ip'); 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('link_views'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Models/Link.php: -------------------------------------------------------------------------------- 1 | $this->slug]); 27 | } 28 | 29 | /** 30 | * Returns the link views. 31 | */ 32 | public function views() 33 | { 34 | return $this->hasMany('ConsoleTVs\Links\Models\View'); 35 | } 36 | 37 | /** 38 | * Returns the link unique views. 39 | */ 40 | public function uniqueViews() 41 | { 42 | return $this->views->unique('ip'); 43 | } 44 | 45 | /** 46 | * Returns the link total views number. 47 | */ 48 | public function totalViews() 49 | { 50 | return count($this->views); 51 | } 52 | 53 | /** 54 | * Returns the link total unique views number. 55 | */ 56 | public function totalUniqueViews() 57 | { 58 | return count($this->uniqueViews()); 59 | } 60 | 61 | /** 62 | * Returns the link browsers. 63 | */ 64 | public function usedBrowsers() 65 | { 66 | $results = []; 67 | 68 | foreach ($this->views as $view) { 69 | array_key_exists($view->browser, $results) ? $results[$view->browser]++ : $results[$view->browser] = 1; 70 | } 71 | 72 | return $results; 73 | } 74 | 75 | /** 76 | * Returns the link most used browser. 77 | */ 78 | public function mostUsedBrowser() 79 | { 80 | $max = 0; 81 | $max_browser = null; 82 | 83 | foreach ($this->usedBrowsers() as $browser => $count) { 84 | if ($count >= $max) { 85 | $max = $count; 86 | $max_browser = $browser; 87 | } 88 | } 89 | 90 | return $max_browser; 91 | } 92 | 93 | /** 94 | * Returns the link OSs (Operating Systems). 95 | */ 96 | public function usedOSs() 97 | { 98 | $results = []; 99 | 100 | foreach ($this->views as $view) { 101 | array_key_exists($view->os, $results) ? $results[$view->os]++ : $results[$view->os] = 1; 102 | } 103 | 104 | return $results; 105 | } 106 | 107 | /** 108 | * Returns the link most used OS (Operating System). 109 | */ 110 | public function mostUsedOS() 111 | { 112 | $max = 0; 113 | $max_os = null; 114 | 115 | foreach ($this->usedOSs() as $os => $count) { 116 | if ($count >= $max) { 117 | $max = $count; 118 | $max_os = $os; 119 | } 120 | } 121 | 122 | return $max_os; 123 | } 124 | 125 | /** 126 | * Returns the link OSs (Operating Systems). 127 | * 128 | * @param bool $fancy 129 | */ 130 | public function usedLanguages($fancy = false) 131 | { 132 | $collection = collect($this->views->toArray())->groupBy('language')->map(function ($item, $key) { 133 | return count($item); 134 | }); 135 | 136 | if ($fancy) { 137 | $collection = $collection->groupBy(function ($item, $key) { 138 | $countries = json_decode(file_get_contents(__DIR__.'/../countries.json'), true); 139 | foreach ($countries as $country) { 140 | if ($country['code'] == $key) { 141 | return explode(' ', str_replace(';', '', $country['name']))[0]; 142 | } 143 | } 144 | 145 | return $key; 146 | })->map(function ($item, $key) { 147 | return count($item); 148 | }); 149 | } 150 | 151 | return $collection->toArray(); 152 | } 153 | 154 | /** 155 | * Returns the link most used OS (Operating System). 156 | * 157 | * @param bool $fancy 158 | */ 159 | public function mostUsedLanguage($fancy = false) 160 | { 161 | $max = collect($languages = $this->usedLanguages())->max(); 162 | 163 | foreach ($languages as $lang => $views) { 164 | if ($views == $max) { 165 | if ($fancy) { 166 | $countries = json_decode(file_get_contents(__DIR__.'/../countries.json'), true); 167 | foreach ($countries as $country) { 168 | if ($country['code'] == $lang) { 169 | return explode(' ', str_replace(';', '', $country['name']))[0]; 170 | } 171 | } 172 | } 173 | 174 | return $lang; 175 | } 176 | } 177 | } 178 | 179 | /** 180 | * Adds a new view to the link. 181 | */ 182 | public function addView() 183 | { 184 | $view = View::create([ 185 | 'link_id' => $this->id, 186 | 'language' => Identify::lang()->getLanguage(), 187 | 'browser' => Identify::browser()->getName(), 188 | 'browser_version' => Identify::browser()->getVersion(), 189 | 'os' => Identify::os()->getName(), 190 | 'os_version' => Identify::os()->getVersion(), 191 | 'ip' => $this->getIP(), 192 | ]); 193 | } 194 | 195 | /** 196 | * Gets the real client IP. 197 | */ 198 | public function getIP() 199 | { 200 | if (! empty($_SERVER['HTTP_CF_CONNECTING_IP'])) { //check ip from cloudflare 201 | $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; 202 | } elseif (! empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet 203 | $ip = $_SERVER['HTTP_CLIENT_IP']; 204 | } elseif (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy 205 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 206 | } else { 207 | $ip = $_SERVER['REMOTE_ADDR']; 208 | } 209 | 210 | return $ip; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/Models/View.php: -------------------------------------------------------------------------------- 1 | language) { 25 | return explode(' ', str_replace(';', '', $country['name']))[0]; 26 | } 27 | } 28 | 29 | return $this->language; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Routes/web.php: -------------------------------------------------------------------------------- 1 | 'web', 'as' => 'links::', 'prefix' => config('links.prefix'), 'namespace' => 'ConsoleTVs\Links\Controllers'], function () { 4 | Route::get('/login', 'LinksController@showLogin')->name('login'); 5 | Route::post('/login', 'LinksController@login'); 6 | 7 | Route::group(['middleware' => 'links.middleware'], function () { 8 | Route::get('/', 'LinksController@links')->name('links'); 9 | Route::get('/logout', 'LinksController@logout')->name('logout'); 10 | Route::get('/{slug}/stats', 'LinksController@link')->name('link'); 11 | Route::get('/{slug}/stats/{specific}/{specific_value}', 'LinksController@specific')->name('specific'); 12 | }); 13 | 14 | Route::get('/{slug}', 'LinksController@redirect')->name('redirect'); 15 | }); 16 | -------------------------------------------------------------------------------- /src/Views/link.blade.php: -------------------------------------------------------------------------------- 1 | @extends(config('links.layout')) 2 | 3 | @section('title', 'Link: ' . $link->slug . ' - Links - Automatic links statistics') 4 | 5 | @section('bigTitle', 'Link Statistics') 6 | 7 | @section('subtitle', $link->shortered()) 8 | 9 | @section('actions') 10 | All Links 11 | @endsection 12 | 13 | @section('content') 14 | 15 |
16 |
17 |
18 |
19 |
20 | {{ $link->totalViews() }}
21 | Views 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{ $link->totalUniqueViews() }}
31 | Unique Views 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 | {{ $link->mostUsedBrowser() }}
46 | Most used browser 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {{ $link->mostUsedOS() }}
56 | Most used OS 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | {{ $link->mostUsedLanguage(true) }}
66 | Most used language 67 |
68 |
69 |
70 |
71 |
72 | 73 |
74 | 75 |
76 |
77 |
78 |
79 |
80 | {!! 81 | ConsoleTVs\Charts\Charts::database($link->views, 'donut', 'morris') 82 | ->setTitle($t = 'Used browsers')->setDimensions(0, 300)->setResponsive(false) 83 | ->groupBy('browser')->render(); 84 | !!} 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | {!! 94 | ConsoleTVs\Charts\Charts::database($link->views, 'donut', 'morris') 95 | ->setTitle($t = 'Used Operating Systems')->setDimensions(0, 300)->setResponsive(false) 96 | ->groupBy('os')->render(); 97 | !!} 98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | {!! 107 | ConsoleTVs\Charts\Charts::database($link->views, 'donut', 'morris') 108 | ->setTitle($t = 'Used Languages')->setDimensions(0, 300)->setResponsive(false) 109 | ->groupBy('language')->render(); 110 | !!} 111 |
112 |
113 |
114 |
115 |
116 | 117 |
118 | 119 |
120 |
121 |
122 |
123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | @foreach($link->usedBrowsers() as $browser => $value) 133 | 134 | 135 | 136 | 137 | @endforeach 138 | 139 |
BrowserViews
{{ $browser }}{{ $value }}
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | @foreach($link->usedOSs() as $os => $value) 157 | 158 | 159 | 160 | 161 | @endforeach 162 | 163 |
Operating SystemViews
{{ $os }}{{ $value }}
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | @foreach($link->usedLanguages(true) as $lang => $value) 181 | 182 | 183 | 184 | 185 | @endforeach 186 | 187 |
LanguageViews
{{ $lang }}{{ $value }}
188 |
189 |
190 |
191 |
192 |
193 | 194 |
195 | 196 |
197 |
198 |
199 |
200 | {!! 201 | ConsoleTVs\Charts\Charts::database($link->views, 'line', 'morris') 202 | ->setTitle($t = 'Views')->setDimensions(0, 300)->setResponsive(false) 203 | ->setElementLabel($t)->setColors(['#0275d8'])->lastByDay(7, true)->render(); 204 | !!} 205 |
206 |
207 |
208 |
209 |
210 |
211 | {!! 212 | ConsoleTVs\Charts\Charts::database($link->uniqueViews(), 'line', 'morris') 213 | ->setTitle($t = 'Unique Views')->setDimensions(0, 300)->setResponsive(false) 214 | ->setElementLabel($t)->setColors(['#0275d8'])->lastByDay(7, true)->render(); 215 | !!} 216 |
217 |
218 |
219 |
220 | 221 |
222 |
223 |
224 |
225 |
226 |
227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | @foreach($views = ConsoleTVs\Links\Models\View::where('link_id', $link->id)->orderBy('id', 'desc')->paginate(10) as $view) 239 | 240 | 241 | 246 | 251 | 256 | 257 | 258 | @endforeach 259 | 260 |
Visit DateBrowserOperating SystemLanguageIP
{{ $view->created_at->diffForHumans() }} 242 | 243 | {{ $view->browser }} {{ $view->browser_version }} 244 | 245 | 247 | 248 | {{ $view->os }} {{ $view->os_version }} 249 | 250 | 252 | 253 | {{ $view->languageFancy() }} 254 | 255 | {{ $view->ip }}
261 | {{ $views->links('links::pagination') }} 262 |
263 |
264 |
265 |
266 |
267 | 268 |
269 | @endsection 270 | -------------------------------------------------------------------------------- /src/Views/links.blade.php: -------------------------------------------------------------------------------- 1 | @extends(config('links.layout')) 2 | 3 | @section('title', 'Links - Automatic links statistics') 4 | 5 | @section('bigTitle', 'Links') 6 | 7 | @section('subtitle', 'Automatic links statistics') 8 | 9 | @section('content') 10 | 11 |
12 |
13 |
14 |
15 |
16 | {{ count($links = ConsoleTVs\Links\Models\Link::all()) }}
17 | Total Links 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ count($views = ConsoleTVs\Links\Models\View::all()) }}
27 | Total Views 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | uniqueViews()->toArray()); 40 | } 41 | ?> 42 | {{ count($unique_views) }}
43 | Total Unique Views 44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 | 52 |
53 |
54 |
55 |
56 | {!! 57 | ConsoleTVs\Charts\Charts::database($views, 'line', 'morris') 58 | ->setTitle($t = 'Total Views')->setDimensions(0, 300)->setResponsive(false) 59 | ->setElementLabel($t)->setColors(['#0275d8'])->lastByDay(7, true)->render(); 60 | !!} 61 |
62 |
63 |
64 |
65 | 66 |
67 | 68 |
69 |
70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | @foreach($links = ConsoleTVs\Links\Models\Link::paginate(10) as $link) 87 | 88 | 89 | 90 | 91 | 92 | 95 | 98 | 101 | 102 | @endforeach 103 | 104 |
#SlugViewsUnique ViewsOriginal URLShort URLStatistics
{{ $link->id }}{{ $link->slug }}{{ $link->totalViews() }}{{ $link->totalUniqueViews() }} 93 | Visit 94 | 96 | Visit 97 | 99 | Statistics 100 |
105 | {{ $links->links('links::pagination') }} 106 |
107 |
108 |
109 |
110 |
111 | @endsection 112 | -------------------------------------------------------------------------------- /src/Views/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends(config('links.layout')) 2 | 3 | @section('title', "Login - Links - Automatic links statistics") 4 | 5 | @section('content') 6 | 7 |
8 |
9 |
10 |

Login to Links

11 |
12 |
13 |
14 |
15 |
16 |
17 | {{ csrf_field() }} 18 |

19 | 20 | @if(session('msg')) 21 | {{ session('msg') }} 22 | @endif 23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | @endsection 33 | -------------------------------------------------------------------------------- /src/Views/pagination.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 |
    3 | 4 | @if ($paginator->onFirstPage()) 5 |
  • «
  • 6 | @else 7 |
  • 8 | @endif 9 | 10 | 11 | @foreach ($elements as $element) 12 | 13 | @if (is_string($element)) 14 |
  • {{ $element }}
  • 15 | @endif 16 | 17 | 18 | @if (is_array($element)) 19 | @foreach ($element as $page => $url) 20 | @if ($page == $paginator->currentPage()) 21 |
  • {{ $page }}
  • 22 | @else 23 |
  • {{ $page }}
  • 24 | @endif 25 | @endforeach 26 | @endif 27 | @endforeach 28 | 29 | 30 | @if ($paginator->hasMorePages()) 31 |
  • 32 | @else 33 |
  • »
  • 34 | @endif 35 |
36 | @endif 37 | -------------------------------------------------------------------------------- /src/Views/specific.blade.php: -------------------------------------------------------------------------------- 1 | @extends(config('links.layout')) 2 | 3 | @section('title', "Link: $link->slug - $specific_value - Links - Automatic links statistics") 4 | 5 | @section('bigTitle', "$specific_value Link Statistics") 6 | 7 | @section('subtitle', $link->shortered()) 8 | 9 | @section('actions') 10 | All Links 11 | Disable Filter 12 | @endsection 13 | 14 | @section('content') 15 |
16 |
17 |
18 |
19 |
20 | {{ count($views) }}
21 | Views 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{ count(collect($views)->groupBy('ip')) }}
31 | Unique Views 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 51 | {!! 52 | ConsoleTVs\Charts\Charts::database($views, 'donut', 'morris') 53 | ->setTitle($title)->setDimensions(0, 300)->setResponsive(false) 54 | ->groupBy($grouper)->render(); 55 | !!} 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 73 | {!! 74 | ConsoleTVs\Charts\Charts::database($views, 'donut', 'morris') 75 | ->setTitle($title)->setDimensions(0, 300)->setResponsive(false) 76 | ->groupBy($grouper)->render(); 77 | !!} 78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 95 | {!! 96 | ConsoleTVs\Charts\Charts::database($views, 'donut', 'morris') 97 | ->setTitle($title)->setDimensions(0, 300)->setResponsive(false) 98 | ->groupBy($grouper)->render(); 99 | !!} 100 |
101 |
102 |
103 |
104 |
105 | 106 |
107 | 108 |
109 |
110 |
111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | @foreach($o_views = ConsoleTVs\Links\Models\View::where(['link_id' => $link->id, $specific => $raw_specific_value])->paginate(10) as $view) 125 | 126 | 127 | 132 | 137 | 142 | 143 | 144 | @endforeach 145 | 146 |
Visit DateBrowserOperating SystemLanguageIP
{{ $view->created_at->diffForHumans() }} 128 | 129 | {{ $view->browser }} {{ $view->browser_version }} 130 | 131 | 133 | 134 | {{ $view->os }} {{ $view->os_version }} 135 | 136 | 138 | 139 | {{ $view->languageFancy() }} 140 | 141 | {{ $view->ip }}
147 | {{ $o_views->links('links::pagination') }} 148 |
149 |
150 |
151 |
152 |
153 | 154 |
155 | @endsection 156 | -------------------------------------------------------------------------------- /src/Views/template.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @yield('title') 8 | 9 | 10 | 11 | 12 | 13 | 14 | 38 | 39 | {!! ConsoleTVs\Charts\Charts::assets() !!} 40 | 41 | 44 | 45 | 46 | 47 |
48 |

49 | @yield('bigTitle')
50 | @yield('subtitle') 51 |

52 | @yield('actions') 53 | @if(session('links.password') && Crypt::decrypt(session('links.password')) == config('links.password')) 54 | Logout 55 | @endif 56 |

57 |
58 | 59 |
60 | @yield('content') 61 |
62 | 63 | 64 | 65 | 66 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/countries.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"code":"ab","name":"Abkhaz","nativeName":"аҧсуа"}, 3 | {"code":"aa","name":"Afar","nativeName":"Afaraf"}, 4 | {"code":"af","name":"Afrikaans","nativeName":"Afrikaans"}, 5 | {"code":"ak","name":"Akan","nativeName":"Akan"}, 6 | {"code":"sq","name":"Albanian","nativeName":"Shqip"}, 7 | {"code":"am","name":"Amharic","nativeName":"አማርኛ"}, 8 | {"code":"ar","name":"Arabic","nativeName":"العربية"}, 9 | {"code":"an","name":"Aragonese","nativeName":"Aragonés"}, 10 | {"code":"hy","name":"Armenian","nativeName":"Հայերեն"}, 11 | {"code":"as","name":"Assamese","nativeName":"অসমীয়া"}, 12 | {"code":"av","name":"Avaric","nativeName":"авар мацӀ, магӀарул мацӀ"}, 13 | {"code":"ae","name":"Avestan","nativeName":"avesta"}, 14 | {"code":"ay","name":"Aymara","nativeName":"aymar aru"}, 15 | {"code":"az","name":"Azerbaijani","nativeName":"azərbaycan dili"}, 16 | {"code":"bm","name":"Bambara","nativeName":"bamanankan"}, 17 | {"code":"ba","name":"Bashkir","nativeName":"башҡорт теле"}, 18 | {"code":"eu","name":"Basque","nativeName":"euskara, euskera"}, 19 | {"code":"be","name":"Belarusian","nativeName":"Беларуская"}, 20 | {"code":"bn","name":"Bengali","nativeName":"বাংলা"}, 21 | {"code":"bh","name":"Bihari","nativeName":"भोजपुरी"}, 22 | {"code":"bi","name":"Bislama","nativeName":"Bislama"}, 23 | {"code":"bs","name":"Bosnian","nativeName":"bosanski jezik"}, 24 | {"code":"br","name":"Breton","nativeName":"brezhoneg"}, 25 | {"code":"bg","name":"Bulgarian","nativeName":"български език"}, 26 | {"code":"my","name":"Burmese","nativeName":"ဗမာစာ"}, 27 | {"code":"ca","name":"Catalan; Valencian","nativeName":"Català"}, 28 | {"code":"ch","name":"Chamorro","nativeName":"Chamoru"}, 29 | {"code":"ce","name":"Chechen","nativeName":"нохчийн мотт"}, 30 | {"code":"ny","name":"Chichewa; Chewa; Nyanja","nativeName":"chiCheŵa, chinyanja"}, 31 | {"code":"zh","name":"Chinese","nativeName":"中文 (Zhōngwén), 汉语, 漢語"}, 32 | {"code":"cv","name":"Chuvash","nativeName":"чӑваш чӗлхи"}, 33 | {"code":"kw","name":"Cornish","nativeName":"Kernewek"}, 34 | {"code":"co","name":"Corsican","nativeName":"corsu, lingua corsa"}, 35 | {"code":"cr","name":"Cree","nativeName":"ᓀᐦᐃᔭᐍᐏᐣ"}, 36 | {"code":"hr","name":"Croatian","nativeName":"hrvatski"}, 37 | {"code":"cs","name":"Czech","nativeName":"česky, čeština"}, 38 | {"code":"da","name":"Danish","nativeName":"dansk"}, 39 | {"code":"dv","name":"Divehi; Dhivehi; Maldivian;","nativeName":"ދިވެހި"}, 40 | {"code":"nl","name":"Dutch","nativeName":"Nederlands, Vlaams"}, 41 | {"code":"en","name":"English","nativeName":"English"}, 42 | {"code":"eo","name":"Esperanto","nativeName":"Esperanto"}, 43 | {"code":"et","name":"Estonian","nativeName":"eesti, eesti keel"}, 44 | {"code":"ee","name":"Ewe","nativeName":"Eʋegbe"}, 45 | {"code":"fo","name":"Faroese","nativeName":"føroyskt"}, 46 | {"code":"fj","name":"Fijian","nativeName":"vosa Vakaviti"}, 47 | {"code":"fi","name":"Finnish","nativeName":"suomi, suomen kieli"}, 48 | {"code":"fr","name":"French","nativeName":"français, langue française"}, 49 | {"code":"ff","name":"Fula; Fulah; Pulaar; Pular","nativeName":"Fulfulde, Pulaar, Pular"}, 50 | {"code":"gl","name":"Galician","nativeName":"Galego"}, 51 | {"code":"ka","name":"Georgian","nativeName":"ქართული"}, 52 | {"code":"de","name":"German","nativeName":"Deutsch"}, 53 | {"code":"el","name":"Greek, Modern","nativeName":"Ελληνικά"}, 54 | {"code":"gn","name":"Guaraní","nativeName":"Avañeẽ"}, 55 | {"code":"gu","name":"Gujarati","nativeName":"ગુજરાતી"}, 56 | {"code":"ht","name":"Haitian; Haitian Creole","nativeName":"Kreyòl ayisyen"}, 57 | {"code":"ha","name":"Hausa","nativeName":"Hausa, هَوُسَ"}, 58 | {"code":"he","name":"Hebrew (modern)","nativeName":"עברית"}, 59 | {"code":"hz","name":"Herero","nativeName":"Otjiherero"}, 60 | {"code":"hi","name":"Hindi","nativeName":"हिन्दी, हिंदी"}, 61 | {"code":"ho","name":"Hiri Motu","nativeName":"Hiri Motu"}, 62 | {"code":"hu","name":"Hungarian","nativeName":"Magyar"}, 63 | {"code":"ia","name":"Interlingua","nativeName":"Interlingua"}, 64 | {"code":"id","name":"Indonesian","nativeName":"Bahasa Indonesia"}, 65 | {"code":"ie","name":"Interlingue","nativeName":"Originally called Occidental; then Interlingue after WWII"}, 66 | {"code":"ga","name":"Irish","nativeName":"Gaeilge"}, 67 | {"code":"ig","name":"Igbo","nativeName":"Asụsụ Igbo"}, 68 | {"code":"ik","name":"Inupiaq","nativeName":"Iñupiaq, Iñupiatun"}, 69 | {"code":"io","name":"Ido","nativeName":"Ido"}, 70 | {"code":"is","name":"Icelandic","nativeName":"Íslenska"}, 71 | {"code":"it","name":"Italian","nativeName":"Italiano"}, 72 | {"code":"iu","name":"Inuktitut","nativeName":"ᐃᓄᒃᑎᑐᑦ"}, 73 | {"code":"ja","name":"Japanese","nativeName":"日本語 (にほんご/にっぽんご)"}, 74 | {"code":"jv","name":"Javanese","nativeName":"basa Jawa"}, 75 | {"code":"kl","name":"Kalaallisut, Greenlandic","nativeName":"kalaallisut, kalaallit oqaasii"}, 76 | {"code":"kn","name":"Kannada","nativeName":"ಕನ್ನಡ"}, 77 | {"code":"kr","name":"Kanuri","nativeName":"Kanuri"}, 78 | {"code":"ks","name":"Kashmiri","nativeName":"कश्मीरी, كشميري‎"}, 79 | {"code":"kk","name":"Kazakh","nativeName":"Қазақ тілі"}, 80 | {"code":"km","name":"Khmer","nativeName":"ភាសាខ្មែរ"}, 81 | {"code":"ki","name":"Kikuyu, Gikuyu","nativeName":"Gĩkũyũ"}, 82 | {"code":"rw","name":"Kinyarwanda","nativeName":"Ikinyarwanda"}, 83 | {"code":"ky","name":"Kirghiz, Kyrgyz","nativeName":"кыргыз тили"}, 84 | {"code":"kv","name":"Komi","nativeName":"коми кыв"}, 85 | {"code":"kg","name":"Kongo","nativeName":"KiKongo"}, 86 | {"code":"ko","name":"Korean","nativeName":"한국어 (韓國語), 조선말 (朝鮮語)"}, 87 | {"code":"ku","name":"Kurdish","nativeName":"Kurdî, كوردی‎"}, 88 | {"code":"kj","name":"Kwanyama, Kuanyama","nativeName":"Kuanyama"}, 89 | {"code":"la","name":"Latin","nativeName":"latine, lingua latina"}, 90 | {"code":"lb","name":"Luxembourgish, Letzeburgesch","nativeName":"Lëtzebuergesch"}, 91 | {"code":"lg","name":"Luganda","nativeName":"Luganda"}, 92 | {"code":"li","name":"Limburgish, Limburgan, Limburger","nativeName":"Limburgs"}, 93 | {"code":"ln","name":"Lingala","nativeName":"Lingála"}, 94 | {"code":"lo","name":"Lao","nativeName":"ພາສາລາວ"}, 95 | {"code":"lt","name":"Lithuanian","nativeName":"lietuvių kalba"}, 96 | {"code":"lu","name":"Luba-Katanga","nativeName":""}, 97 | {"code":"lv","name":"Latvian","nativeName":"latviešu valoda"}, 98 | {"code":"gv","name":"Manx","nativeName":"Gaelg, Gailck"}, 99 | {"code":"mk","name":"Macedonian","nativeName":"македонски јазик"}, 100 | {"code":"mg","name":"Malagasy","nativeName":"Malagasy fiteny"}, 101 | {"code":"ms","name":"Malay","nativeName":"bahasa Melayu, بهاس ملايو‎"}, 102 | {"code":"ml","name":"Malayalam","nativeName":"മലയാളം"}, 103 | {"code":"mt","name":"Maltese","nativeName":"Malti"}, 104 | {"code":"mi","name":"Māori","nativeName":"te reo Māori"}, 105 | {"code":"mr","name":"Marathi (Marāṭhī)","nativeName":"मराठी"}, 106 | {"code":"mh","name":"Marshallese","nativeName":"Kajin M̧ajeļ"}, 107 | {"code":"mn","name":"Mongolian","nativeName":"монгол"}, 108 | {"code":"na","name":"Nauru","nativeName":"Ekakairũ Naoero"}, 109 | {"code":"nv","name":"Navajo, Navaho","nativeName":"Diné bizaad, Dinékʼehǰí"}, 110 | {"code":"nb","name":"Norwegian Bokmål","nativeName":"Norsk bokmål"}, 111 | {"code":"nd","name":"North Ndebele","nativeName":"isiNdebele"}, 112 | {"code":"ne","name":"Nepali","nativeName":"नेपाली"}, 113 | {"code":"ng","name":"Ndonga","nativeName":"Owambo"}, 114 | {"code":"nn","name":"Norwegian Nynorsk","nativeName":"Norsk nynorsk"}, 115 | {"code":"no","name":"Norwegian","nativeName":"Norsk"}, 116 | {"code":"ii","name":"Nuosu","nativeName":"ꆈꌠ꒿ Nuosuhxop"}, 117 | {"code":"nr","name":"South Ndebele","nativeName":"isiNdebele"}, 118 | {"code":"oc","name":"Occitan","nativeName":"Occitan"}, 119 | {"code":"oj","name":"Ojibwe, Ojibwa","nativeName":"ᐊᓂᔑᓈᐯᒧᐎᓐ"}, 120 | {"code":"cu","name":"Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic","nativeName":"ѩзыкъ словѣньскъ"}, 121 | {"code":"om","name":"Oromo","nativeName":"Afaan Oromoo"}, 122 | {"code":"or","name":"Oriya","nativeName":"ଓଡ଼ିଆ"}, 123 | {"code":"os","name":"Ossetian, Ossetic","nativeName":"ирон æвзаг"}, 124 | {"code":"pa","name":"Panjabi, Punjabi","nativeName":"ਪੰਜਾਬੀ, پنجابی‎"}, 125 | {"code":"pi","name":"Pāli","nativeName":"पाऴि"}, 126 | {"code":"fa","name":"Persian","nativeName":"فارسی"}, 127 | {"code":"pl","name":"Polish","nativeName":"polski"}, 128 | {"code":"ps","name":"Pashto, Pushto","nativeName":"پښتو"}, 129 | {"code":"pt","name":"Portuguese","nativeName":"Português"}, 130 | {"code":"qu","name":"Quechua","nativeName":"Runa Simi, Kichwa"}, 131 | {"code":"rm","name":"Romansh","nativeName":"rumantsch grischun"}, 132 | {"code":"rn","name":"Kirundi","nativeName":"kiRundi"}, 133 | {"code":"ro","name":"Romanian, Moldavian, Moldovan","nativeName":"română"}, 134 | {"code":"ru","name":"Russian","nativeName":"русский язык"}, 135 | {"code":"sa","name":"Sanskrit (Saṁskṛta)","nativeName":"संस्कृतम्"}, 136 | {"code":"sc","name":"Sardinian","nativeName":"sardu"}, 137 | {"code":"sd","name":"Sindhi","nativeName":"सिन्धी, سنڌي، سندھی‎"}, 138 | {"code":"se","name":"Northern Sami","nativeName":"Davvisámegiella"}, 139 | {"code":"sm","name":"Samoan","nativeName":"gagana faa Samoa"}, 140 | {"code":"sg","name":"Sango","nativeName":"yângâ tî sängö"}, 141 | {"code":"sr","name":"Serbian","nativeName":"српски језик"}, 142 | {"code":"gd","name":"Scottish Gaelic; Gaelic","nativeName":"Gàidhlig"}, 143 | {"code":"sn","name":"Shona","nativeName":"chiShona"}, 144 | {"code":"si","name":"Sinhala, Sinhalese","nativeName":"සිංහල"}, 145 | {"code":"sk","name":"Slovak","nativeName":"slovenčina"}, 146 | {"code":"sl","name":"Slovene","nativeName":"slovenščina"}, 147 | {"code":"so","name":"Somali","nativeName":"Soomaaliga, af Soomaali"}, 148 | {"code":"st","name":"Southern Sotho","nativeName":"Sesotho"}, 149 | {"code":"es","name":"Spanish; Castilian","nativeName":"español, castellano"}, 150 | {"code":"su","name":"Sundanese","nativeName":"Basa Sunda"}, 151 | {"code":"sw","name":"Swahili","nativeName":"Kiswahili"}, 152 | {"code":"ss","name":"Swati","nativeName":"SiSwati"}, 153 | {"code":"sv","name":"Swedish","nativeName":"svenska"}, 154 | {"code":"ta","name":"Tamil","nativeName":"தமிழ்"}, 155 | {"code":"te","name":"Telugu","nativeName":"తెలుగు"}, 156 | {"code":"tg","name":"Tajik","nativeName":"тоҷикӣ, toğikī, تاجیکی‎"}, 157 | {"code":"th","name":"Thai","nativeName":"ไทย"}, 158 | {"code":"ti","name":"Tigrinya","nativeName":"ትግርኛ"}, 159 | {"code":"bo","name":"Tibetan Standard, Tibetan, Central","nativeName":"བོད་ཡིག"}, 160 | {"code":"tk","name":"Turkmen","nativeName":"Türkmen, Түркмен"}, 161 | {"code":"tl","name":"Tagalog","nativeName":"Wikang Tagalog, ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔"}, 162 | {"code":"tn","name":"Tswana","nativeName":"Setswana"}, 163 | {"code":"to","name":"Tonga (Tonga Islands)","nativeName":"faka Tonga"}, 164 | {"code":"tr","name":"Turkish","nativeName":"Türkçe"}, 165 | {"code":"ts","name":"Tsonga","nativeName":"Xitsonga"}, 166 | {"code":"tt","name":"Tatar","nativeName":"татарча, tatarça, تاتارچا‎"}, 167 | {"code":"tw","name":"Twi","nativeName":"Twi"}, 168 | {"code":"ty","name":"Tahitian","nativeName":"Reo Tahiti"}, 169 | {"code":"ug","name":"Uighur, Uyghur","nativeName":"Uyƣurqə, ئۇيغۇرچە‎"}, 170 | {"code":"uk","name":"Ukrainian","nativeName":"українська"}, 171 | {"code":"ur","name":"Urdu","nativeName":"اردو"}, 172 | {"code":"uz","name":"Uzbek","nativeName":"zbek, Ўзбек, أۇزبېك‎"}, 173 | {"code":"ve","name":"Venda","nativeName":"Tshivenḓa"}, 174 | {"code":"vi","name":"Vietnamese","nativeName":"Tiếng Việt"}, 175 | {"code":"vo","name":"Volapük","nativeName":"Volapük"}, 176 | {"code":"wa","name":"Walloon","nativeName":"Walon"}, 177 | {"code":"cy","name":"Welsh","nativeName":"Cymraeg"}, 178 | {"code":"wo","name":"Wolof","nativeName":"Wollof"}, 179 | {"code":"fy","name":"Western Frisian","nativeName":"Frysk"}, 180 | {"code":"xh","name":"Xhosa","nativeName":"isiXhosa"}, 181 | {"code":"yi","name":"Yiddish","nativeName":"ייִדיש"}, 182 | {"code":"yo","name":"Yoruba","nativeName":"Yorùbá"}, 183 | {"code":"za","name":"Zhuang, Chuang","nativeName":"Saɯ cueŋƅ, Saw cuengh"} 184 | ] 185 | --------------------------------------------------------------------------------