├── README.md ├── Vagrantfile ├── _install ├── mini3.png ├── mysql │ ├── 01-create-database.sql │ ├── 02-create-table-song.sql │ └── 03-insert-demo-data-into-table-song.sql └── nginx │ └── default ├── application ├── Controller │ ├── ErrorController.php │ ├── HomeController.php │ └── SongsController.php ├── Core │ ├── Application.php │ └── Model.php ├── Model │ └── Song.php ├── config │ └── config.php └── view │ ├── _templates │ ├── footer.php │ └── header.php │ ├── error │ └── index.php │ ├── home │ ├── example_one.php │ ├── example_two.php │ └── index.php │ └── songs │ ├── edit.php │ └── index.php ├── bootstrap.sh ├── composer.json └── public ├── .htaccess ├── css └── style.css ├── img └── demo-image.png ├── index.php └── js └── application.js /README.md: -------------------------------------------------------------------------------- 1 | ![MINI3 - A naked barebone PHP application](_install/mini3.png) 2 | 3 | # MINI3 4 | 5 | MINI3 is an extremely simple and easy to understand skeleton PHP application, reduced to the max. 6 | MINI3 is NOT a professional framework and does not come with all the stuff real frameworks have. 7 | If you just want to show some pages, do a few database calls and a little-bit of AJAX here and there, without 8 | reading in massive documentations of highly complex professional frameworks, then MINI3 might be very useful for you. 9 | MINI3 is easy to install, runs nearly everywhere and doesn't make things more complicated than necessary. 10 | 11 | 12 | [MINI](https://github.com/panique/mini) (original version) and [MINI2](https://github.com/panique/mini2) (used Slim router) were built by me (panique), MINI3 is an excellent and improved version 13 | of the original MINI, made by [JaoNoctus](https://github.com/JaoNoctus). Big thanks, man! :) 14 | 15 | ## Features 16 | 17 | - extremely simple, easy to understand 18 | - simple but clean structure 19 | - makes "beautiful" clean URLs 20 | - demo CRUD actions: Create, Read, Update and Delete database entries easily 21 | - demo AJAX call 22 | - tries to follow PSR coding guidelines 23 | - uses PDO for any database requests, comes with an additional PDO debug tool to emulate your SQL statements 24 | - commented code 25 | - uses only native PHP code, so people don't have to learn a framework 26 | - uses PSR-4 autoloader 27 | 28 | ## Requirements (but it's auto-installed) 29 | 30 | - PHP 8 31 | - MySQL 32 | - basic knowledge of Composer for sure 33 | - for auto-installation: VirtualBox, Vagrant 34 | 35 | ## Forks 36 | 37 | There are some nice upgraded versions of this mini framework, check it out at https://github.com/ribafs/php-router 38 | 39 | ## Installation (in Vagrant, 100% automatic) 40 | 41 | To keep things super-simple, we are using Vagrant here, a simple technology to run virtual machines for development. 42 | It's outdated, but does the job, and is much easier to understand than Docker. Just install VirtualBox, Vagrant, then 43 | copy this repo's code to a folder, go to that folder and type: 44 | 45 | ```bash 46 | vagrant up 47 | ``` 48 | 49 | This will create a virtual machine with the configs given in `Vagrantfile`: It will create an Ubuntu 2022.04 Jammy64 50 | VM with 1024MB RAM, sync the current folder to `/var/www/html` inside the VM, make the VM available on the IP 51 | `192.168.56.77` and start the bash script `bootstrap.sh`, which is just a set of commands that will install all 52 | necessary software. 53 | 54 | If the auto-installer is finished, go to http://192.168.56.77 in your browser and click around a bit ;) 55 | 56 | # OLD INSTALLATION TUTORIALS FROM 2016 57 | 58 | Below you'll find installation tutorial for the old version of MINI3 from 2016. 59 | 60 | ## Installation (in Vagrant, 100% automatic) 61 | 62 | If you are using Vagrant for your development, then you can install MINI3 with one click (or one command on the 63 | command line) [[Vagrant doc](https://docs.vagrantup.com/v2/getting-started/provisioning.html)]. MINI3 comes with a demo 64 | Vagrant-file (defines your Vagrant box) and a demo bootstrap.sh which automatically installs Apache, PHP, MySQL, 65 | PHPMyAdmin, git and Composer, sets a chosen password in MySQL and PHPMyadmin and even inside the application code, 66 | downloads the Composer-dependencies, activates mod_rewrite and edits the Apache settings, downloads the code from GitHub 67 | and runs the demo SQL statements (for demo data). This is 100% automatic, you'll end up after +/- 5 minutes with a fully 68 | running installation of MINI3 inside an Ubuntu 14.04 LTS Vagrant box. 69 | 70 | To do so, put `Vagrantfile` and `bootstrap.sh` from `_vagrant` inside a folder (and nothing else). 71 | Do `vagrant box add ubuntu/trusty64` to add Ubuntu 14.04 LTS ("Trusty Thar") 64bit to Vagrant (unless you already have 72 | it), then do `vagrant up` to run the box. When installation is finished you can directly use the fully installed demo 73 | app on `192.168.33.66`. As this just a quick demo environment the MySQL root password and the PHPMyAdmin root password 74 | are set to `12345678`, the project is installed in `/var/www/html/myproject`. You can change this for sure inside 75 | `bootstrap.sh`. 76 | 77 | ## Auto-Installation on Ubuntu 14.04 LTS (in 30 seconds) 78 | 79 | You can install MINI3 including Apache, MySQL, PHP and PHPMyAdmin, mod_rewrite, Composer, all necessary settings and 80 | even the passwords inside the configs file by simply downloading one file and executing it, the entire installation 81 | will run 100% automatically. If you are stuck somehow, also have a look into this tutorial for the original MINI1, 82 | it's basically the same installation process: 83 | [Install MINI in 30 seconds inside Ubuntu 14.04 LTS](http://www.dev-metal.com/install-mini-30-seconds-inside-ubuntu-14-04-lts/) 84 | 85 | ## Manual Installation 86 | 87 | 1. Edit the database credentials in `application/config/config.php` 88 | 2. Execute the .sql statements in the `_install/`-folder (with PHPMyAdmin for example). 89 | 3. Make sure you have mod_rewrite activated on your server / in your environment. Some guidelines: 90 | [Ubuntu 14.04 LTS](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-14-04-lts/), 91 | [Ubuntu 12.04 LTS](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-12-04-lts/), 92 | [EasyPHP on Windows](http://stackoverflow.com/questions/8158770/easyphp-and-htaccess), 93 | [AMPPS on Windows/Mac OS](http://www.softaculous.com/board/index.php?tid=3634&title=AMPPS_rewrite_enable/disable_option%3F_please%3F), 94 | [XAMPP for Windows](http://www.leonardaustin.com/blog/technical/enable-mod_rewrite-in-xampp/), 95 | [MAMP on Mac OS](http://stackoverflow.com/questions/7670561/how-to-get-htaccess-to-work-on-mamp) 96 | 4. Install composer and run `composer install` in the project's folder to create the PSR-4 autoloading stuff from Composer automatically. 97 | If you have no idea what this means: Remember the "good" old times when we were using "include file.php" all over our projects to include and use something ? 98 | PSR-0/4 is the modern, clean and automatic version of that. Please have a google research if that's important for you. 99 | 100 | Feel free to commit your guideline for Ubuntu 16.04 LTS or other linuxes to the list! 101 | 102 | MINI3 runs without any further configuration. You can also put it inside a sub-folder, it will work without any 103 | further configuration. 104 | Maybe useful: A simple tutorial on [How to install LAMPP (Linux, Apache, MySQL, PHP, PHPMyAdmin) on Ubuntu 14.04 LTS](http://www.dev-metal.com/installsetup-basic-lamp-stack-linux-apache-mysql-php-ubuntu-14-04-lts/) 105 | and [the same for Ubuntu 12.04 LTS](http://www.dev-metal.com/setup-basic-lamp-stack-linux-apache-mysql-php-ubuntu-12-04/). 106 | 107 | ## Server configs for 108 | 109 | ### nginx 110 | 111 | ```nginx 112 | server { 113 | server_name default_server _; # Listen to any servername 114 | listen [::]:80; 115 | listen 80; 116 | 117 | root /var/www/html/myproject/public; 118 | 119 | location / { 120 | index index.php; 121 | try_files /$uri /$uri/ /index.php?url=$uri; 122 | } 123 | 124 | location ~ \.(php)$ { 125 | fastcgi_pass unix:/var/run/php5-fpm.sock; 126 | fastcgi_index index.php; 127 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 128 | include fastcgi_params; 129 | } 130 | } 131 | ``` 132 | 133 | A deeper discussion on nginx setups can be found [here](https://github.com/panique/mini/issues/55). 134 | 135 | ## Security 136 | 137 | The script makes use of mod_rewrite and blocks all access to everything outside the /public folder. 138 | Your .git folder/files, operating system temp files, the application-folder and everything else is not accessible 139 | (when set up correctly). For database requests PDO is used, so no need to think about SQL injection (unless you 140 | are using extremely outdated MySQL versions). 141 | 142 | ## How to include stuff / use PSR-4 143 | 144 | As this project uses proper PSR-4 namespaces, make sure you load/use your stuff correctly: 145 | Instead of including classes with old-school code like `include xxx.php`, simply do something like `use Mini\Model\Song;` on top of your file (modern IDEs even do that automatically). 146 | This would automatically include the file *Song.php* from the folder *Mini/Model* (it's case-sensitive!). 147 | 148 | But wait, there's no `Mini/Model/Song.php` in the project, but a `application/Model/Song.php`, right ? 149 | To keep things cleaner, the composer.json sets a *namespace* (see code below), which is basically a name or an alias, for a certain folder / area of your application, 150 | in this case the folder `application` is now reachable via `Mini` when including stuff. 151 | 152 | ``` 153 | { 154 | "psr-4": 155 | { 156 | "Mini\\" : "application/" 157 | } 158 | } 159 | ``` 160 | 161 | This might look stupid at first, but comes in handy later. To sum it up: 162 | 163 | To load the file `application/Model/Song.php`, write a `use Mini\Model\Song;` on top of your controller file. 164 | Have a look into the SongController to get an idea how everything works! 165 | 166 | FYI: As decribed in the install tutorial, you'll need do perform a "composer install" when setting up your application for the first time, which will 167 | create a set of files (= the autoloader) inside /vendor folder. This is the normal way Composer handle this stuff. If you delete your vendor folder 168 | the autoloading will not work anymore. If you change something in the composer.json, always make sure to run composer install/update again! 169 | 170 | ## Goodies 171 | 172 | MINI3 comes with a little customized [PDO debugger tool](https://github.com/panique/pdo-debug) (find the code in 173 | application/libs/helper.php), trying to emulate your PDO-SQL statements. It's extremely easy to use: 174 | 175 | ```php 176 | $sql = "SELECT id, artist, track, link FROM song WHERE id = :song_id LIMIT 1"; 177 | $query = $this->db->prepare($sql); 178 | $parameters = array(':song_id' => $song_id); 179 | 180 | echo Helper::debugPDO($sql, $parameters); 181 | 182 | $query->execute($parameters); 183 | ``` 184 | 185 | ## License 186 | 187 | This project is licensed under the MIT License. 188 | This means you can use and modify it for free in private or commercial projects. 189 | 190 | ## Quick-Start 191 | 192 | #### The structure in general 193 | 194 | The application's URL-path translates directly to the controllers (=files) and their methods inside 195 | application/controllers. 196 | 197 | `example.com/home/exampleOne` will do what the *exampleOne()* method in application/Controller/HomeController.php says. 198 | 199 | `example.com/home` will do what the *index()* method in application/Controller/HomeController.php says. 200 | 201 | `example.com` will do what the *index()* method in application/Controller/HomeController.php says (default fallback). 202 | 203 | `example.com/songs` will do what the *index()* method in application/Controller/SongsController.php says. 204 | 205 | `example.com/songs/editsong/17` will do what the *editsong()* method in application/Controller/SongsController.php says and 206 | will pass `17` as a parameter to it. 207 | 208 | Self-explaining, right ? 209 | 210 | #### Showing a view 211 | 212 | Let's look at the exampleOne()-method in the home-controller (application/Controller/HomeController.php): This simply shows 213 | the header, footer and the example_one.php page (in views/home/). By intention as simple and native as possible. 214 | 215 | ```php 216 | public function exampleOne() 217 | { 218 | // load view 219 | require APP . 'views/_templates/header.php'; 220 | require APP . 'views/home/example_one.php'; 221 | require APP . 'views/_templates/footer.php'; 222 | } 223 | ``` 224 | 225 | #### Working with data 226 | 227 | Let's look into the index()-method in the songs-controller (application/Controller/SongsController.php): Similar to exampleOne, 228 | but here we also request data. Again, everything is extremely reduced and simple: $Song->getAllSongs() simply 229 | calls the getAllSongs()-method in application/Model/Song.php (when $Song = new Song()). 230 | 231 | ```php 232 | namespace Mini\Controller 233 | 234 | use Mini\Model\Song; 235 | 236 | class SongsController 237 | { 238 | public function index() 239 | { 240 | // Instance new Model (Song) 241 | $Song = new Song(); 242 | // getting all songs and amount of songs 243 | $songs = $Song->getAllSongs(); 244 | $amount_of_songs = $Song->getAmountOfSongs(); 245 | 246 | // load view. within the view files we can echo out $songs and $amount_of_songs easily 247 | require APP . 'views/_templates/header.php'; 248 | require APP . 'views/songs/index.php'; 249 | require APP . 'views/_templates/footer.php'; 250 | } 251 | } 252 | ``` 253 | 254 | For extreme simplicity, data-handling methods are in application/model/ClassName.php. Have a look how getAllSongs() in model.php looks like: Pure and 255 | super-simple PDO. 256 | 257 | ```php 258 | namespace Mini\Model 259 | 260 | use Mini\Core\Model; 261 | 262 | class Song extends Model 263 | { 264 | public function getAllSongs() 265 | { 266 | $sql = "SELECT id, artist, track, link FROM song"; 267 | $query = $this->db->prepare($sql); 268 | $query->execute(); 269 | 270 | return $query->fetchAll(); 271 | } 272 | } 273 | ``` 274 | 275 | The result, here $songs, can then easily be used directly 276 | inside the view files (in this case application/views/songs/index.php, in a simplified example): 277 | 278 | ```php 279 | 280 | 281 | 282 | artist)) echo htmlspecialchars($song->artist, ENT_QUOTES, 'UTF-8'); ?> 283 | track)) echo htmlspecialchars($song->track, ENT_QUOTES, 'UTF-8'); ?> 284 | 285 | 286 | 287 | ``` 288 | 289 | ## Contribute 290 | 291 | Please commit into the develop branch (which holds the in-development version), not into master branch 292 | (which holds the tested and stable version). 293 | 294 | ## Changelog 295 | 296 | **August 2016** 297 | 298 | - [panique] fix for weird lowercase/uppercase path problem (also big thanks to @snickbit & @ugurozturk for the fix!) 299 | - [panique] forking joanoctus's excellent PSR4 ersion of MINI to MINI3 300 | 301 | **January 2016** 302 | 303 | - [joanoctus] implementing PSR-4 autoloader 304 | 305 | **February 2015** 306 | 307 | - [jeroenseegers] nginx setup configuration 308 | 309 | **December 2014** 310 | - [panique] css fixes 311 | - [panique] renamed controller / view to singular 312 | - [panique] added charset to PDO creation (increased security) 313 | 314 | **November 2014** 315 | - [panique] auto-install script for Vagrant 316 | - [panique] basic documentation 317 | - [panique] PDO-debugger is now a static helper-method, not a global function anymore 318 | - [panique] folder renaming 319 | - [reg4in] JS AJAX calls runs now properly even when using script in sub-folder 320 | - [panique] removed all "models", using one model file now 321 | - [panique] full project renaming, re-branding 322 | 323 | **October 2014** 324 | - [tarcnux/panique] PDO debugging 325 | - [panique] demo ajax call 326 | - [panique] better output escaping 327 | - [panique] renamed /libs to /core 328 | - [tarcnux] basic CRUD (create/read/update/delete) examples have now an U (update) 329 | - [panique] URL is now config-free, application detects URL and sub-folder 330 | - [elysdir] htaccess has some good explanation-comments now 331 | - [bst27] fallback for non-existing controller / method 332 | - [panique] fallback will show error-page now 333 | - [digitaltoast] URL split fix to make php-mvc work flawlessly on nginx 334 | - [AD7six] security improvement: moved index.php to /public, route ALL request to /public 335 | 336 | **September 2014** 337 | - [panique] added link to support forum 338 | - [panique] added link to Facebook page 339 | 340 | **August 2014** 341 | - [panique] several changes in the README, donate-button changes 342 | 343 | **June 2014** 344 | - [digitaltoast] removed X-UA-Compatible meta tag from header (as it's not needed anymore these days) 345 | - [digitaltoast] removed protocol in jQuery URL (modern way to load external files, making it independent to protocol change) 346 | - [digitaltoast] downgraded jQuery from 2.1 to 1.11 to avoid problems when working with IE7/8 (jQuery 2 dropped IE7/8 support) 347 | - [panique] moved jQuery loading to footer (to avoid page render blocking) 348 | 349 | **April 2014** 350 | - [panique] updated jQuery link to 2.1 351 | - [panique] more than 3 parameters (arguments to be concrete) are possible 352 | - [panique] cleaner way of parameter handling 353 | - [panique] smaller cleanings and improvements 354 | - [panique] Apache 2.4 install information 355 | 356 | **January 2014** 357 | - [panique] fixed .htaccess issue when there's a controller named "index" and a base index.php (which collide) 358 | 359 | ## Other stuff 360 | 361 | And by the way, I'm also blogging at [Dev Metal](http://www.dev-metal.com) :) 362 | 363 | ## Support the project 364 | 365 | Buy Me A Coffee 366 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | 9 | # Ubuntu 2022.04 10 | config.vm.box = "ubuntu/jammy64" 11 | 12 | # Create a private network, which allows host-only access to the machine using a specific IP. 13 | config.vm.network "private_network", ip: "192.168.56.77" 14 | 15 | config.vm.provider "virtualbox" do |vb| 16 | vb.memory = "1024" 17 | end 18 | 19 | # Share an additional folder to the guest VM. The first argument is the path on the host to the actual folder. 20 | # The second argument is the path on the guest to mount the folder. 21 | config.vm.synced_folder "./", "/var/www/html" 22 | 23 | # Define the bootstrap file: A (shell) script that runs after first setup of your box (= provisioning) 24 | config.vm.provision :shell, path: "bootstrap.sh" 25 | 26 | end 27 | -------------------------------------------------------------------------------- /_install/mini3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panique/mini3/34a2867e8175319074e9ff122227e34c1e428a8f/_install/mini3.png -------------------------------------------------------------------------------- /_install/mysql/01-create-database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `mini`; 2 | -------------------------------------------------------------------------------- /_install/mysql/02-create-table-song.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `mini`.`song` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `artist` text COLLATE utf8_unicode_ci NOT NULL, 4 | `track` text COLLATE utf8_unicode_ci NOT NULL, 5 | `link` text COLLATE utf8_unicode_ci, 6 | PRIMARY KEY (`id`), 7 | UNIQUE KEY `id` (`id`) 8 | ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 9 | -------------------------------------------------------------------------------- /_install/mysql/03-insert-demo-data-into-table-song.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `mini`.`song` (`id`, `artist`, `track`, `link`) VALUES 2 | (1, 'Rin', 'Ljubav/Beichtstuhl', 'https://www.youtube.com/watch?v=MDHJMirQ5PI'), 3 | (2, 'Jeremih feat. Natasha Mosley', 'F*** U All The Time', 'https://www.youtube.com/watch?v=6-Bq7PCKCJ4'), 4 | (3, 'Nao', 'In the Morning', 'https://www.youtube.com/watch?v=uuocmqLRgOM'), 5 | (4, 'Sofi / Tukker', 'Matadora', 'https://www.youtube.com/watch?v=d6GJeap4Oqo'), 6 | (5, 'Yung Hurn', 'Nein', 'https://www.youtube.com/watch?v=22m5eU6uxeQ'), 7 | (6, 'Rin', 'Error', 'https://www.youtube.com/watch?v=VzajBMa-2P8'), 8 | (7, 'Detachments', 'Circles (Martyn Remix)', 'http://www.youtube.com/watch?v=UzS7Gvn7jJ0'), 9 | (8, 'Survive', 'Hourglass', 'https://www.youtube.com/watch?v=JVOe2oGoHLk'), 10 | (9, 'Big Narstie', 'Hello Hi', 'https://www.youtube.com/watch?v=q10WwZJmPew'), 11 | (10, 'Sleaford Mods', 'Tarantula Deadly Cargo', 'https://www.youtube.com/watch?v=E-gvxxhcS8s'), 12 | (11, 'Mykki Blanco & Woodkid', 'Highschool never ends', 'https://www.youtube.com/watch?v=cNGR4ciDmTA'), 13 | (12, 'Secondcity & Tyler Rowe', 'I Enter', 'https://www.youtube.com/watch?v=vAmDJAxNMi0'), 14 | (13, 'Maxxi Soundsystem', 'Regrets we have no use for (Radio1 Rip)', 'https://soundcloud.com/maxxisoundsystem/maxxi-soundsystem-ft-name-one'), 15 | (14, 'Jamie T', 'Don''t you find', 'https://www.youtube.com/watch?v=-tmoaFAT108'), 16 | (15, 'Sierra Kid', 'Ein Fan von Dir', 'https://www.youtube.com/watch?v=dfabdmbpQeQ'), 17 | (16, 'SSIO', 'Nullkommaneun', 'https://www.youtube.com/watch?v=Slei8n08Cqk'), 18 | (17, 'Pupkulies & Rebecca', 'ICI', 'https://www.youtube.com/watch?v=60tebPRj_D0'), 19 | (18, 'Color War', 'Shapeshifting', 'https://vimeo.com/111250184'), 20 | (19, 'RÜFÜS', 'Innerbloom', 'https://www.youtube.com/watch?v=IA1liCmUsAM'), 21 | (20, 'RÜFÜS', 'Tonight', 'https://www.youtube.com/watch?v=GCa_TKn9ghI'); -------------------------------------------------------------------------------- /_install/nginx/default: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | 5 | root /var/www/html/public; 6 | 7 | index index.html index.htm index.nginx-debian.html; 8 | 9 | server_name _; 10 | 11 | location / { 12 | index index.php; 13 | try_files /$uri /$uri/ /index.php?url=$uri; 14 | } 15 | 16 | location ~ \.php$ { 17 | include snippets/fastcgi-php.conf; 18 | fastcgi_pass unix:/run/php/php8.1-fpm.sock; 19 | } 20 | 21 | location ~ /\.ht { 22 | deny all; 23 | } 24 | } -------------------------------------------------------------------------------- /application/Controller/ErrorController.php: -------------------------------------------------------------------------------- 1 | getAllSongs(); 31 | $amount_of_songs = $Song->getAmountOfSongs(); 32 | 33 | // load views. within the views we can echo out $songs and $amount_of_songs easily 34 | require APP . 'view/_templates/header.php'; 35 | require APP . 'view/songs/index.php'; 36 | require APP . 'view/_templates/footer.php'; 37 | } 38 | 39 | /** 40 | * ACTION: addSong 41 | * This method handles what happens when you move to http://yourproject/songs/addsong 42 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "add a song" form on songs/index 43 | * directs the user after the form submit. This method handles all the POST data from the form and then redirects 44 | * the user back to songs/index via the last line: header(...) 45 | * This is an example of how to handle a POST request. 46 | */ 47 | public function addSong() 48 | { 49 | // if we have POST data to create a new song entry 50 | if (isset($_POST["submit_add_song"])) { 51 | // Instance new Model (Song) 52 | $Song = new Song(); 53 | // do addSong() in model/model.php 54 | $Song->addSong($_POST["artist"], $_POST["track"], $_POST["link"]); 55 | } 56 | 57 | // where to go after song has been added 58 | header('location: ' . URL . 'songs/index'); 59 | } 60 | 61 | /** 62 | * ACTION: deleteSong 63 | * This method handles what happens when you move to http://yourproject/songs/deletesong 64 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "delete a song" button on songs/index 65 | * directs the user after the click. This method handles all the data from the GET request (in the URL!) and then 66 | * redirects the user back to songs/index via the last line: header(...) 67 | * This is an example of how to handle a GET request. 68 | * @param int $song_id Id of the to-delete song 69 | */ 70 | public function deleteSong($song_id) 71 | { 72 | // if we have an id of a song that should be deleted 73 | if (isset($song_id)) { 74 | // Instance new Model (Song) 75 | $Song = new Song(); 76 | // do deleteSong() in model/model.php 77 | $Song->deleteSong($song_id); 78 | } 79 | 80 | // where to go after song has been deleted 81 | header('location: ' . URL . 'songs/index'); 82 | } 83 | 84 | /** 85 | * ACTION: editSong 86 | * This method handles what happens when you move to http://yourproject/songs/editsong 87 | * @param int $song_id Id of the to-edit song 88 | */ 89 | public function editSong($song_id) 90 | { 91 | // if we have an id of a song that should be edited 92 | if (isset($song_id)) { 93 | // Instance new Model (Song) 94 | $Song = new Song(); 95 | // do getSong() in model/model.php 96 | $song = $Song->getSong($song_id); 97 | 98 | // in a real application we would also check if this db entry exists and therefore show the result or 99 | // redirect the user to an error page or similar 100 | 101 | // load views. within the views we can echo out $song easily 102 | require APP . 'view/_templates/header.php'; 103 | require APP . 'view/songs/edit.php'; 104 | require APP . 'view/_templates/footer.php'; 105 | } else { 106 | // redirect user to songs index page (as we don't have a song_id) 107 | header('location: ' . URL . 'songs/index'); 108 | } 109 | } 110 | 111 | /** 112 | * ACTION: updateSong 113 | * This method handles what happens when you move to http://yourproject/songs/updatesong 114 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "update a song" form on songs/edit 115 | * directs the user after the form submit. This method handles all the POST data from the form and then redirects 116 | * the user back to songs/index via the last line: header(...) 117 | * This is an example of how to handle a POST request. 118 | */ 119 | public function updateSong() 120 | { 121 | // if we have POST data to create a new song entry 122 | if (isset($_POST["submit_update_song"])) { 123 | // Instance new Model (Song) 124 | $Song = new Song(); 125 | // do updateSong() from model/model.php 126 | $Song->updateSong($_POST["artist"], $_POST["track"], $_POST["link"], $_POST['song_id']); 127 | } 128 | 129 | // where to go after song has been added 130 | header('location: ' . URL . 'songs/index'); 131 | } 132 | 133 | /** 134 | * AJAX-ACTION: ajaxGetStats 135 | * TODO documentation 136 | */ 137 | public function ajaxGetStats() 138 | { 139 | // Instance new Model (Song) 140 | $Song = new Song(); 141 | $amount_of_songs = $Song->getAmountOfSongs(); 142 | 143 | // simply echo out something. A supersimple API would be possible by echoing JSON here 144 | echo $amount_of_songs; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /application/Core/Application.php: -------------------------------------------------------------------------------- 1 | splitUrl(); 24 | 25 | // check for controller: no controller given ? then load start-page 26 | if (!$this->url_controller) { 27 | 28 | $page = new \Mini\Controller\HomeController(); 29 | $page->index(); 30 | 31 | } elseif (file_exists(APP . 'Controller/' . ucfirst($this->url_controller) . 'Controller.php')) { 32 | // here we did check for controller: does such a controller exist ? 33 | 34 | // if so, then load this file and create this controller 35 | // like \Mini\Controller\CarController 36 | $controller = "\\Mini\\Controller\\" . ucfirst($this->url_controller) . 'Controller'; 37 | $this->url_controller = new $controller(); 38 | 39 | // check for method: does such a method exist in the controller ? 40 | if (method_exists($this->url_controller, (string) $this->url_action)) { 41 | 42 | if (!empty($this->url_params)) { 43 | // Call the method and pass arguments to it 44 | call_user_func_array(array($this->url_controller, $this->url_action), $this->url_params); 45 | } else { 46 | // If no parameters are given, just call the method without parameters, like $this->home->method(); 47 | $this->url_controller->{$this->url_action}(); 48 | } 49 | 50 | } else { 51 | if (empty($this->url_action)) { 52 | // no action defined: call the default index() method of a selected controller 53 | $this->url_controller->index(); 54 | } else { 55 | header('location: ' . URL . 'error'); 56 | } 57 | } 58 | } else { 59 | header('location: ' . URL . 'error'); 60 | } 61 | } 62 | 63 | /** 64 | * Get and split the URL 65 | */ 66 | private function splitUrl() 67 | { 68 | if (isset($_GET['url'])) { 69 | 70 | // split URL 71 | $url = trim($_GET['url'], '/'); 72 | $url = filter_var($url, FILTER_SANITIZE_URL); 73 | $url = explode('/', $url); 74 | 75 | // Put URL parts into according properties 76 | // By the way, the syntax here is just a short form of if/else, called "Ternary Operators" 77 | // @see http://davidwalsh.name/php-shorthand-if-else-ternary-operators 78 | $this->url_controller = isset($url[0]) ? $url[0] : null; 79 | $this->url_action = isset($url[1]) ? $url[1] : null; 80 | 81 | // Remove controller and action from the split URL 82 | unset($url[0], $url[1]); 83 | 84 | // Rebase array keys and store the URL params 85 | $this->url_params = array_values($url); 86 | 87 | // for debugging. uncomment this if you have problems with the URL 88 | //echo 'Controller: ' . $this->url_controller . '
'; 89 | //echo 'Action: ' . $this->url_action . '
'; 90 | //echo 'Parameters: ' . print_r($this->url_params, true) . '
'; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /application/Core/Model.php: -------------------------------------------------------------------------------- 1 | user_name ! 33 | // For example, fetch mode FETCH_ASSOC would return results like this: $result["user_name] ! 34 | // @see http://www.php.net/manual/en/pdostatement.fetch.php 35 | $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING); 36 | 37 | // generate a database connection, using the PDO connector 38 | // @see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/ 39 | $this->db = new PDO(DB_TYPE . ':host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=' . DB_CHARSET, DB_USER, DB_PASS, $options); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /application/Model/Song.php: -------------------------------------------------------------------------------- 1 | db->prepare($sql); 26 | $query->execute(); 27 | 28 | // fetchAll() is the PDO method that gets all result rows, here in object-style because we defined this in 29 | // core/controller.php! If you prefer to get an associative array as the result, then do 30 | // $query->fetchAll(PDO::FETCH_ASSOC); or change core/controller.php's PDO options to 31 | // $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ... 32 | return $query->fetchAll(); 33 | } 34 | 35 | /** 36 | * Add a song to database 37 | * TODO put this explanation into readme and remove it from here 38 | * Please note that it's not necessary to "clean" our input in any way. With PDO all input is escaped properly 39 | * automatically. We also don't use strip_tags() etc. here so we keep the input 100% original (so it's possible 40 | * to save HTML and JS to the database, which is a valid use case). Data will only be cleaned when putting it out 41 | * in the views (see the views for more info). 42 | * @param string $artist Artist 43 | * @param string $track Track 44 | * @param string $link Link 45 | */ 46 | public function addSong($artist, $track, $link) 47 | { 48 | $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; 49 | $query = $this->db->prepare($sql); 50 | $parameters = array(':artist' => $artist, ':track' => $track, ':link' => $link); 51 | 52 | // useful for debugging: you can see the SQL behind above construction by using: 53 | // echo '[ PDO DEBUG ]: ' . Helper::debugPDO($sql, $parameters); exit(); 54 | 55 | $query->execute($parameters); 56 | } 57 | 58 | /** 59 | * Delete a song in the database 60 | * Please note: this is just an example! In a real application you would not simply let everybody 61 | * add/update/delete stuff! 62 | * @param int $song_id Id of song 63 | */ 64 | public function deleteSong($song_id) 65 | { 66 | $sql = "DELETE FROM song WHERE id = :song_id"; 67 | $query = $this->db->prepare($sql); 68 | $parameters = array(':song_id' => $song_id); 69 | 70 | // useful for debugging: you can see the SQL behind above construction by using: 71 | // echo '[ PDO DEBUG ]: ' . Helper::debugPDO($sql, $parameters); exit(); 72 | 73 | $query->execute($parameters); 74 | } 75 | 76 | /** 77 | * Get a song from database 78 | * @param integer $song_id 79 | */ 80 | public function getSong($song_id) 81 | { 82 | $sql = "SELECT id, artist, track, link FROM song WHERE id = :song_id LIMIT 1"; 83 | $query = $this->db->prepare($sql); 84 | $parameters = array(':song_id' => $song_id); 85 | 86 | // useful for debugging: you can see the SQL behind above construction by using: 87 | // echo '[ PDO DEBUG ]: ' . Helper::debugPDO($sql, $parameters); exit(); 88 | 89 | $query->execute($parameters); 90 | 91 | // fetch() is the PDO method that get exactly one result 92 | return $query->fetch(); 93 | } 94 | 95 | /** 96 | * Update a song in database 97 | * // TODO put this explaination into readme and remove it from here 98 | * Please note that it's not necessary to "clean" our input in any way. With PDO all input is escaped properly 99 | * automatically. We also don't use strip_tags() etc. here so we keep the input 100% original (so it's possible 100 | * to save HTML and JS to the database, which is a valid use case). Data will only be cleaned when putting it out 101 | * in the views (see the views for more info). 102 | * @param string $artist Artist 103 | * @param string $track Track 104 | * @param string $link Link 105 | * @param int $song_id Id 106 | */ 107 | public function updateSong($artist, $track, $link, $song_id) 108 | { 109 | $sql = "UPDATE song SET artist = :artist, track = :track, link = :link WHERE id = :song_id"; 110 | $query = $this->db->prepare($sql); 111 | $parameters = array(':artist' => $artist, ':track' => $track, ':link' => $link, ':song_id' => $song_id); 112 | 113 | // useful for debugging: you can see the SQL behind above construction by using: 114 | // echo '[ PDO DEBUG ]: ' . Helper::debugPDO($sql, $parameters); exit(); 115 | 116 | $query->execute($parameters); 117 | } 118 | 119 | /** 120 | * Get simple "stats". This is just a simple demo to show 121 | * how to use more than one model in a controller (see application/controller/songs.php for more) 122 | */ 123 | public function getAmountOfSongs() 124 | { 125 | $sql = "SELECT COUNT(id) AS amount_of_songs FROM song"; 126 | $query = $this->db->prepare($sql); 127 | $query->execute(); 128 | 129 | // fetch() is the PDO method that get exactly one result 130 | return $query->fetch()->amount_of_songs; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /application/config/config.php: -------------------------------------------------------------------------------- 1 | 3 | Find MINI3 on GitHub. 4 |
If you like the project, feel free to buy me a coffee by clicking the yellow button ;)
5 | Buy Me A Coffee 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /application/view/_templates/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MINI3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /application/view/error/index.php: -------------------------------------------------------------------------------- 1 |
2 |

This is the Error-page. Will be shown when a page (= controller / method) does not exist.

3 |
4 | -------------------------------------------------------------------------------- /application/view/home/example_one.php: -------------------------------------------------------------------------------- 1 |
2 |

Home/exampleone (a subpage of home)

3 |

You are in the View: application/view/home/example_one.php (everything in the box comes from this file)

4 |

In a real application this could be a normal page.

5 |
6 | -------------------------------------------------------------------------------- /application/view/home/example_two.php: -------------------------------------------------------------------------------- 1 |
2 |

Home/exampletwo (another subpage of home)

3 |

You are in the View: application/view/home/example_two.php (everything in the box comes from this file)

4 |

In a real application this could be a normal page.

5 |
6 | -------------------------------------------------------------------------------- /application/view/home/index.php: -------------------------------------------------------------------------------- 1 |
2 |

Home

3 |

You are in the View: application/view/home/index.php (everything in the box comes from this file)

4 |

In a real application this could be the homepage.

5 |
6 | -------------------------------------------------------------------------------- /application/view/songs/edit.php: -------------------------------------------------------------------------------- 1 |
2 |

You are in the View: application/view/song/edit.php (everything in this box comes from that file)

3 | 4 |
5 |

Edit a song

6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /application/view/songs/index.php: -------------------------------------------------------------------------------- 1 |
2 |

Songs

3 |

You are in the View: application/view/song/index.php (everything in this box comes from that file)

4 | 5 |
6 |

Add a song

7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 |
19 |

Amount of songs:

20 |

Amount of songs (via AJAX)

21 |
22 |
23 | 24 |
25 |

List of songs (data from model)

26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 |
IdArtistTrackLinkDELETEEDIT
id)) echo htmlspecialchars($song->id, ENT_QUOTES, 'UTF-8'); ?>artist)) echo htmlspecialchars($song->artist, ENT_QUOTES, 'UTF-8'); ?>track)) echo htmlspecialchars($song->track, ENT_QUOTES, 'UTF-8'); ?> 44 | link)) { ?> 45 | link, ENT_QUOTES, 'UTF-8'); ?> 46 | 47 | deleteedit
54 |
55 |
56 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Use single quotes instead of double quotes to make it work with special-character passwords. 4 | # If you change it here, also change it in the config.php file! 5 | PASSWORD='12345678' 6 | 7 | # update / upgrade 8 | sudo apt-get update 9 | sudo apt-get -y upgrade 10 | 11 | # php 12 | sudo apt install -y php8.1-fpm 13 | # php modules 14 | sudo apt install -y php-xml 15 | sudo apt install -y php-mbstring 16 | sudo apt install -y php-zip 17 | 18 | # nginx, copy nginx config into Vagrant box, syntax check, restart nginx 19 | sudo apt install -y nginx 20 | cp /var/www/html/_install/nginx/default /etc/nginx/sites-available/default 21 | sudo nginx -t 22 | sudo systemctl restart nginx 23 | 24 | # mysql (pw 12345678), user "root" 25 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password $PASSWORD" 26 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password $PASSWORD" 27 | sudo apt-get install -y mysql-server 28 | sudo apt-get install -y php8.1-mysql 29 | 30 | # run SQL statements 31 | sudo mysql -h "localhost" -u "root" "-p${PASSWORD}" < "/var/www/html/_install/mysql/01-create-database.sql" 32 | sudo mysql -h "localhost" -u "root" "-p${PASSWORD}" < "/var/www/html/_install/mysql/02-create-table-song.sql" 33 | sudo mysql -h "localhost" -u "root" "-p${PASSWORD}" < "/var/www/html/_install/mysql/03-insert-demo-data-into-table-song.sql" 34 | 35 | # phpmyadmin (and add symlink to it's reachable via /phpmyadmin) 36 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/dbconfig-install boolean true" 37 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/app-password-confirm password $PASSWORD" 38 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/admin-pass password $PASSWORD" 39 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/app-pass password $PASSWORD" 40 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/reconfigure-webserver multiselect nginx" 41 | sudo apt-get install -y phpmyadmin 42 | sudo ln -s /usr/share/phpmyadmin /var/www/html/public/phpmyadmin 43 | 44 | # install Composer 45 | curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer 46 | 47 | # install Git 48 | sudo apt-get install -y git 49 | 50 | # initial composer install, necessary to make the whole application work 51 | cd /var/www/html && composer update 52 | 53 | # delete demo file from nginx 54 | cd /var/www/html && rm index.nginx-debian.html 55 | 56 | # clickable link 57 | echo "Hello, Hello! Click to start: http://192.168.56.77" -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "panique/mini3", 3 | "minimum-stability": "dev", 4 | "require": {}, 5 | "autoload": 6 | { 7 | "psr-4": 8 | { 9 | "Mini\\" : "application/" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | # Necessary to prevent problems when using a controller named "index" and having a root index.php 2 | # more here: http://httpd.apache.org/docs/2.2/content-negotiation.html 3 | Options -MultiViews 4 | 5 | # Activates URL rewriting (like myproject.com/controller/action/1/2/3) 6 | RewriteEngine On 7 | 8 | # Prevent people from looking directly into folders 9 | Options -Indexes 10 | 11 | # If the following conditions are true, then rewrite the URL: 12 | # If the requested filename is not a directory, 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | # and if the requested filename is not a regular file that exists, 15 | RewriteCond %{REQUEST_FILENAME} !-f 16 | # and if the requested filename is not a symbolic link, 17 | RewriteCond %{REQUEST_FILENAME} !-l 18 | # then rewrite the URL in the following way: 19 | # Take the whole request filename and provide it as the value of a 20 | # "url" query parameter to index.php. Append any query string from 21 | # the original URL as further query parameters (QSA), and stop 22 | # processing this .htaccess file (L). 23 | RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 24 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | font-size: 14px; 4 | color: #454545; 5 | } 6 | .logo { 7 | width: 200px; 8 | height: 200px; 9 | /* this is a real image encoded via base64 text, an awesome technology! check https://varvy.com/pagespeed/base64-images.html for more info */ 10 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUuNSAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0Y5QTBFQ0I1ODI2MTFFNkI2MTlGMzkyOUFDNTQwMUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0Y5QTBFQ0M1ODI2MTFFNkI2MTlGMzkyOUFDNTQwMUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3RjlBMEVDOTU4MjYxMUU2QjYxOUYzOTI5QUM1NDAxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3RjlBMEVDQTU4MjYxMUU2QjYxOUYzOTI5QUM1NDAxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pui2oKgAAD2QSURBVHja7J13XFRH18cXBEUp0i3YCLGgYq+xRLEm9hJb1Ghi93li15hYYoxRsWui0Rixl8ResRsVK4oFUVAUFOxSRZD6frMn2c++CxI0POwCd/7Yz917Z87MPfObc34zd4pRamqqSglKyOpgrKhACQqwlKAASwkKsJSgBAVYSlCApQQFWEpQggIsJSjAUoICLCUoQQGWEhRgKUEBlhKUoABLCQqwlKAASwlKUIClBAVYBhy8vb0PHTqUVdIQhUBFq3kdWElJSWPGjLlx40ZWCUQUAhGrACsvhvj4eA+PWUlJiXt27wkKCqpfv35WSUYUAhGLcLIgIwVYeSgcP3bs8MEjyamq3V4HihQpUqJEiaySjCgE7j54AOFkQUYKsPJQ8AsMfJ2YGB0VlRgXZ2pqmpiYmFWSEfWnwFdxMVFRZEFGCrDyULC2snry5GFU5AsHe/vw8PBHjx5llWREIdDB3iEyMpwsyEgBVp4IwWFhmzZtdnJwjImNDQy44+ZWNTo66uXL2KySjygEurm5BQbcJgsnB4dNmzaRqQKsXB6iXrxYvHB+Maeideo2WLXGMy4+2tzc4v79e1klH1EIjIuPQThZFHMqRnZkqgArl4eijkWeR0RcvnxpwMAB90JD93vtdSxS5OQff2SVfEQhELEIJwsyeh4RSaYKsHJ5KFK0SO1adZf+vMzB2qpe9dpxL5MsLSzOnvM5dOjgvxeOEEQhELEId7C2JCOyI9O8pmejPLV3Q1BQUJFiRZ4+etKkSbMPGtY2K2D24MEjIyPV8+cvSpUq/ttvv5mZmb+z8Pj42G7dut2//9De3g6llixZLP51/JnTF0+cOOpYrMiTR09cXFwUi5U7w4VzFz5o9IGlrcWokRO9T157cD8kNTXFyMioSBFHP79b30377t8IJzlCEIVAxCKcLMiI7MiUrBVXmGtD649bx8fEN2/Ssn7Dip90aWGazyw5JTUlJRkclHAq8/v23du2b3g3ySQkOUIQhUDEIpwsyIjsyJSsFWDl2mBjY3PgwAEzM7OVK5blL2SVL7+lvYNjqso4OTmpXPmS5cq7TPrmhyNeh99WLElISHKEIAqBiEU4WZAR2ZEpWecpVef79ttv89QL+/vfsLNzuB/67PHTx6/jYlTGxqkpqpTkxBRVcnJ8SlKB5KMXT7iWLF+qTOlMCjx94tR4j68LmJmaGxdMSI6Pj3ttYmqGN0x8HRsTG2tkXKhVy5ZWVhalSpVWLFZuDjt37po3b96rOKpc9TohITY6IiU5wdg4X8Tz6OiYl2VKlXYp/f6Ub6f4B/hnCqYB/kQmCQlJjhBEIRCxCCcLMiI7MlWGG3J5mDVrVvfu3X0v+cZGRRYwy29knE+VmqzKV9jEzDqfqSo+4nXh1ELBD0KOHs7Ux2OiEZkkJCQ5QhCFQMQinCzIiOzIVAFWLg8mJiZz53osWbLQzs4RG5XwOsFIZWykSkxJfm2UapSQ9PpeaLAqNVWVkrlRGKKlppKEhCT/U4gqEYGIRThZkBHZkakCrNwf5s2be/fulR49P61UoVpRJ8ckLMzr6IJm+S3t7CIiXoSFPjQ1Nb/oc+7Vq+iM5RCBaEQmCQlJjhBEIRCxCCcLMiK7PKjk7ABWWFhYFk4f+PehadNmXl5nNmz81dLeNMXISJWakppqlN+sYFRUTOVKlfv26ffLSs+g28HLlq3IWA4RiEZkkpCQ5AhBFAIRi3CyICOy0/srh4aGPn78OLcBKykp6dq1awkJCQYCrBo1qq9bt6G5e4d8+U3CQkNVSUbGJvlio54XLmhZrHjJ8RNGmhq/btG2ZcDtwKDbd94khEdEIBqRSUJCkiMEUQhELMLJgozITr/v++rVKz8/v2zONJs+6cTHx9NiihUrVqBAAQOB19PwZ3M9Fj99gjl9mhgfk5SYVK9hg+ioKCen4hd9/au7lbcsbBlw/eavnqvSTf5F/8/Lu7nGRMX4Xg+oXb1iWNhDq8KFz532NjE1MTWzdCrm6FjEaez4Lx1tHfT7mnFxcWjeyckpf/782cplsycbMzMzR0fHhw8fFi9e3ECwFR4ecf/p4zLFixaytfHzuZCckvL40SMba5uN6zdY2xe5npBg7+z4OObF4iULS5Ys0cy9lZWVJamg5EePHXzwIJRHRvfuPL/39FnE843rfVu2ak1yhBinppStVN7KrEDwo8dkoV9gYauePHlCe85mVKmy+SO05j3Bmd6B9TL65eixo54lRJsbmwVeu2Ftaxf3KjYxKcnSwrJAAdOo2LiiBZIKmBS8EfEy36uImrUqVa3ZnFRXLx255HMjuZBNJRuL10lxj1+bFDYv+Pp1YszLGFMTk4KFzCPDX5SrUik2Jd4hv9X8uQssrCz0jiq9aDu7ZzfExsY+ffrUEOzWy5cvr169/sPsH6zMzRMTVZGRz1GFEVwepRgZpahU+ZMTrKysze0co148fR4VbmllzaOY6Ej7wraF7RxjXzyNjo5MyJcflio6lOTW1vampqro2NivJ3xdtaqbhYVFHkRV9rlCTTA3Ny9SpAg+Ue92iypv0KB+rWo1qlWrapyvQP/+fVwrupoXMqerYazGVnSSyiK/maO9bSFz8xKprg0a1SaV96mLyUbxFgULBMVEE6GgET3K1BT18Fjsq9ib/jc9PdelJL++cuUqwvXIq/TuGfQzH0sYpSH4RFp2UFBQanLqiRMndu3Zee/uPUsrqxSVkZW1Tf58+V4nxbZq8XFcXEJpF5uUl38uizC2iA4JiihYMP/Bw/sLmJgnJCdHR0YYq1KBmfN7zh3adWzSpIlRPiMXF5dChQrlTVulT2AZGt+SMGnS5HLl3ncq7nT1fvCh3zcbxcXHGxUsUdo2MjwkOjzCzqIYcV68fGRla2NtWzo0JNwsNS61oFnLT3pULVUm7GFYYOCd77+frvd2glYNgWnocwap4fAtqRK6TppvLxt/mrfx5+UFzGyN7C3fq+3sbOPq3qoB948d9L4XcfPuxXupz2Nex4f3GjK41/AxmuG6hIQEfRkqQ2ur+vyGZTh8i6ANiIdhYedvPfB/mdS2QaWePT+v3bCBSb6/HpWvWCcpWXXxtPemTav2HjxOtCZhYcWdnFRqmqXHb4KGwKv+X0jVd6Cd3b17F72kGkaIjIzs2bNnRTe3tRs2ZByTCEQjMkn0W2ZsPzqMj49PNZig/6/uBQsWxG49evSoaNGiXOu3MNHR0Zs3b/bx8Rk7blzvnj3j1GHHjh0XL160tPxzgDQmJqZ27dqdOnWiqH9GiI2dO2cOSYCXlZ4WPRsUozAIjmWArDM0NHTEiBHh4eFNmzYNuHUrIjIyOTn5/v378CfxlZQTf1eqVKl8+fLZWFuXr1Dh+PHjtra2ixYtysKdRXJ0H0j/HEuH4hgC37p3796LFy9obHv27ME42dvbly9f3sjICGDJ2OmfRt7ExMnJKSAgAO8TePs2JScJCbMfWAbHqwwQWIItvCE+UY+aoqoAENABQBUrVixdurSLiwuIATq4GyI4Ojra2dk5ODjg+EJCQjBmpqamWDUS5tmRBUMHliHwLWdnZ6yRsbEx0OEarF++fBlz9fLlS/mOi02Fhz148OC9996jhFQqwEpJSSGywqsMkWMZDm/Yvn27l5cXWV+/fh3Q1KxZ09zcfN++ffxKjbZp04bfS5cuJSYmurm50Rdr3bp1586ds1M/oIq2Z4Ae0KCBpV9soZBhw4ZFREDcI2Hlbdu2jYqK4r5MVBS7Vbhw4b1798Lxra2tbWxsli5dKgxM8YAG6goNgW8BkXLlyuHs3N3d8YkHDhw4f/48nT5fX1+e8ohuY926dXmKB7x161bJkiUVVOUYi6Wh0nr5Vn379u1+/fq5urqC78DAwA8++KBMmTL0+4SEBQcHnzlzBoRRzTdv3ly9enXZsmWzh1c9e/bMoGbh5lRg6dEnDh48mBy7dOliYWGBrYJL1alTh/sXLlyAe1WvXh06v23bNgjW8uXLFbaeY1yhtk+UfmJ2Youun6OjY4o6lChRYtSoUbB47BaPsFVw9nbt2vn7+4M5KysrmW+dDWw9p9iqnGGxtH1ito1B7Ny588aNG1hK2JWlpSX54gcrV67MIz8/P7whhYmJiYFpAfpKlSp17NhRsVU6IWcsWAVP1C41nT3jkACFXiG0HXtpYmJC1osXL3ZUBy74y00eEYFoCqrSDTlmtxmcEV19tIxD/F/MTsHrPX/+HKfz4sWL33//Hf6O68HZNWvWDCKF3cIJRkZG4hkpQ40aNaKiougP4hYh1Pb29gkJCaSlAWRhDzHnokqV47aK/N9x+bt3744dO9ba2hr0XLlypV69euTCNYgJUwfp+gE4J3UA5USG/J07d65atWqScO7cue+9915eG1nIwa5Qm8vL+FaW+8TChQuDJJgTRqJq1ao2NjbAy9bWltql7bVo0SKfOnDBX27yiAhEIzJJSEhyhGSVrcrRqFLlxE1BhG9Bn7MWWzdv3kxKSpJvhXXq1KlYsSJ+ELuF28Ug0SWMVQcu+MtNHhGBaEQmCQlJjpA87gFzzHDDm7BFd4w2nYXfyxqqA35typQpGB4oFLgpVaoU1YxxMjY2lpPiuOAvN3lEhOjoaCJD4UeNGgXUFFTlVI6VJXyLhOfPn6fmoE0ODg5YvqNHj758+TI8PPzevXv+/v7cwetBw0EwPg5/Z2dnFxwcTNoyZcrA7kESdF6mUycnJxMN04XRAnPC97kDqYeQvX79um7duplcYZHTeVWOt1g6fOttx7cADVQdri1z+h4+fLh3715wMGzYsDlz5kybNm3dunXSJVSpv0kDHSBCKiHvmChpjdwBjhSgT58+U6dOXbNmzYIFC0BqaGgo4ACXgYGBkHoZWc07tirHWywJ7zx2CrCADrDw8fGhK1elShWc3b59+1avXv38+XNQC54cHR0BnLm5ORjCdJEKQ8V9QACAwAH3MTP29vb9+vVr06YN3OvatWugtlatWkAT2AGszBSGmGSUa1Clyh0nU8gXD1jXW2ELekR1gpKSJUvKnR9//HH37t0bN248ffo0pisxMZFH2KeQkJCEhAQZPAN8+fPnL126NCTswYMHpqam48aNg5z16tWrffv2//nPf0QUj8CcfPPJa7bqL7eQC7bjpnYxMNgegJX5sdOff/554cKFFy5cqFGjhng3Kpj+HRcHDx7kjlxjmbgAf66urtR9SkoKdApWB3kCXtgq7CW/2Kdy5crBwPCA9+/fxzNu3rwZI/ePjF5aRS5DVc4GFlXi6el58eJFDAPmCoqNBcoMkYcggwaoFbanevXqTZo0UamnYdHju3nzJp7x1q1bgEamIGOZyAhDVbNmTbK4c+cOcJHTLnGLwI7I4lUh6eL4ACVPgTulwmJh+d605wyiyIUOpnZ7QODWrVsvX74MlBGiuMJsDWh//Pjx4KNEiRJgwsXFpUePHjgs8YnUcQZpsSV08b766is6d2AFrr1r167evXtjhIDIjh07zpw5Q2UTB0PFTcTKKIPqz+3awjFaIBIDg7PDRGGfoOedOnUCVdxcv359hw4dKJUInzVrFnEoW7oNQ6Wekko/FPCRBRlt27btz01KUlMpFcTRw8OjQoUKCrCyL3zxxRcQ7ZUrV9KzgyxfvXr1+vXr3KQ6t2zZ0rlz5wkTJuCe0iaMj4+fNGkSHhBbxQXW5fvvv4etAykQg6HiGvTI6KucQY+5ktW9YthkGY9KvU2hDKqBObwhVAxwAC+ukYypQ7Kvr2+dOnW40DGloIrsiLNkyRKIXbt27fDma9eudXNzq1q1Kp0JOzu7AQMG0CH49ddfFVeY9QHVHzp06OTJk6BE1iJL2LNnD5QoLCzs8OHD2C04ChSHaqAiuTh37hwcHGNQrVo1iX/ixAnqW7b9CAwMJBqGYfv27bdv38ZgfP755zAnvKqfnx9IxX4AJjAq40+kAhb51UEMFSDjF/zxC/jwrZg3ItM3pHeJ/QPo0H+Eg8LGjRvLiAOeGluIAZOuA/7u66+/xqvCw3iEnQPNyKdf6e3tjRemGLw1fQLNW+O+Mbe8tYGvpDD0cawVK1agfVRM5a1atYo20KpVK3lEbVF/XCxduhQV49eIg9LhQHixzz777Pfff9csMR09evS9e/d27twpvkx4OnUDGqjL2bNnr1mzBohgrkAVFwgErJD6Zs2aYcBAKo5JIw03BzSB6dGjR2FCyMFQ4TRJDm64QCAdTJwyGEIOSchUxk65zxvNnDkTg4Rtg6K1bt0a8wlWKDz+naznz5/PuwwbNgwnO2LECI026FKgAQQSh+5q165dBw0apLjCtw5jx47dsGEDPTIqAGOAlgHBvHnz2rZtK/jo27fv/v37gRd+BPDh/qSnxi+poFm4EuwTrbxp06ZUf58+fTAMXl5emCIZBQAEIAY7V7lyZbwqT6kwmSTToEEDyI3ACOsFBEEP2CIJcEGsQA0oYF3g4FQ2ZQA6ZIrZw02TUMZUMU6QLQBEpqtXr8ZxHz9+XOZKkOkff/yBwcNQ4Un5xYhSBuwxBuzjjz/GOQq327t375gxY3C7NBvcKI4bLH766adz585VXOHb2aqffvqpfPny5ubmxupAXYItnAsaL6gOVBXOQuZwQpXef/99QEbFUz1gcfr06aRSqSda4UmxH6dPn6bHx19wwyPwJ9+SsVLnz58HVYMHD6YXRi4fffQRUEM4ccAK7I0OGkiCXFMk3Bz2RmwbCCbCjRs3MG8tW7bEmeJhARzJiUB8U3XAoeOdT506haVE1PDhw6VspAKCPKWd8GjhwoXiu8kCeC1YsEBQRUvAdAFcmg1l5hXAMY94L3Knu2qANWiIsxtoi9gqlCidbcwGjoxmCtugqqh4iYZmMWDdunWTv+7u7rg8tA9rASU3b94kIeYBH0pNAyZ4GDgAgn+e+abuhcGUhw4dSs+L6sGEIJA6wzrKR2hHR0egQ3+TmMBX+DsX/OUmj4hANCKDchKSHCGI8vCYgyNDODHJSL3jrTUJcXwgEtzj9XgLWT0mH4Uo9qhRo3gFeRdeilcTVBGIzIvz+igBVYiTQTmoCEWhLoVjZSrQEGESaA3fJx1+nAKGqnbt2p988oksHKVWVOq5BkTAFOG/oF+YHPg1rOvs2bPwaGdnZ8wGegdVNHGkURliKhCCT6GGZs2aBVsCHzi4TZs2Qb3xm9AgbnKHfEEGNgZYSN8QMPGXm3g6jA3+CxIGIBBOcqCAaZw71+PZs+dgCP8LYoAd2cmuEKAW80MnEZSDM1ACqpA5Y8aM/v37C5GiqDhi2T+CIMaVyJhw2BhdyGPHjkkfAnNOAVBXz549FY71z4F6/e6772jl4AAfQWVARFD0tm3bfvvtt2XLlmEkqFqUi5OaNu1bCwtLfAc4oPmia3pVixYtwu8AFyoVZgMKqR6IDmRIBqVkOFTGFJDPtQxpUsc4HVAFSuhIIhP9dOrUsVix4ppNQSBt9AOQAPH68MMPe/XqBbYWL14MVaJUarOX/OpVnIxEkDWIFJcKOOBbWB1kYqhoDJStUaNGX375ZdWqVXGFcCZk4g2JBkXB5pGQUsGo8J40qi5duiCNTBFCRmTHoylTpkDkFYuViTKZmGCHAAE8CU4DPqT5osoffviBX5TLHQFHhw4dcXDckaUWAAur8+uvv/r6+oIV5MjWVqSimmnfxJFRqJQ/T1IykskLtH5xW9Q01gKjQseNOsacYPO4KWxMvBLGpnfv3hikYsWKgTzkA1mwCBpkgxARKMJlIhcXFIzeJXEoJ3CR+aiUqnr1v07aoWD0PzCN5IWVFfTLi5MXJo2sZRMlmg1NKyAgYPLkyeRloGfWpRpeOHDgAD0yiC0MKVUJbwgoB1CiKNRlgMXTD3nHW3l6er7pPDC8AwSIlg1VUinhDQFmiXdGUagr3QioFyWj6rzSK4R+9ujRA6o0btw42chFJxw+fBjCgS/I5iP2clbAXeJMURTqSveLBepFyagahed+YD179owXhsfQnYakb9myRfspVBTaDm8FVTCb7D+zKgcF+i6oCEWhLpSG6rSfoljUi5JRNQpH7bmcvMM0PTw86NR4eXlBXeXkVfpZtCr4744dO7DwMoKFkdcM5CghbUA5qEi6lnRW4PKdOnWif9q+fXu6FygWyo+S3d3d27Ztm/0EXz/DDXTmf/75Z9rchg0b+ItG7t27R2/cXB3oNNG3okM+bNiwgQMHKhhKNyxdunTlypXyZQJNyuo0LJOzszPtkwi9evWCZg0ZMkQvo1z6Ie+8v6+vb9OmTQsWLDho0CD+VqpUiU57oUKF6JyXLFlSvgfLrBUlpBtQDirCaKEuGVJBgagRZaJSFIutQsnZ7wT1OY7l6upKI8PreXt7h4aGlilTRsZ+VH9PfhJaqrjCDAIGHqOOuZIxWBkY41r2iIOzo0aUjKrzSq+Q0KJFi9mzZ8O0ZC91UKX6+zhJS0tLoHbnzp26detm1Yr1XBmgVvXq1bt79y7qkplqokCUiUpRLOpFyahaL8XT8yedOXPmeHp6OqkPOZJlVWXLlg0ODqYtzpw5EwXVrl07M3JIePz48cTERDF1H374YcaboYWHhx85cgR/QaZogPjyhQQhVAntHnsgc+HpWJw5c4b+KWJxNFWqVElXINGwvliIuLi4OnXqyO7cZ8+evX//vswuxDEJfbx9+7bYGDyXRKMMgYGB8CHuy+fwzLwypYqOjv7qq69IgpVCrCxTk5GI/v370xnUY83q82sATeqPP/6QhQbyDadixYqzZs3y9/eHmU6fPr1v376ZBBZp6XLTJ5KlgtDVxYsXZxD/p59+WrRoEeCjOsHTtm3bBFj0z8mdWv/ggw8EWFT5qFGjMAnEtLa2Jib8L61APz+/ESNGIOTx48dkLYhZv3795s2bSUXy6tWrU/HADkDz4jCkypUrSzSyQw8UHmDBmTIJrMuXL23atNnFxYUuDnoDYZScjGTZGQKHDx+uxzPu9DltZvny5egCvaNQNA6XhxAUK1asWbNmrVu3prlT05kUhTYxbw7qgJaPHTuWwYhzSEjI3r17y5cvT2RJJb1xWTHBX0dHR8yJZriIv0QDBBSS+kvXxks0SatZ3ogrl5skF1tCFtQ61Y8j00QzMjICc5bqkHko0CIwWigKdaE0VIcCKSHKRKUoNns2RzU4YNGj2bdvnyxFx8tQW717975+/TqGh6cgA1tF832LNzE2lm/JZmZmKHfVqlVvirl27dqIiAjpqAvn1eyWprkjs2uk1uUO17jsCxcu4L7ToRTqaJI8A2mqv5djSMj4ZsYB5eBzURTXKA3VoUCZdY0QFIt6UXKeA5aPjw8+S1Z+vvfee7AEyBbkYP/+/VCHBQsWYOHhDe8gGVQVLVoUjV+9ejXtUwgc5kom7r2DcPr2v/zyi5eXl97JO8pBRSgKdaE0VIcCUSPKRKUoVnYPyHPAgu3CLWTuCvQFew6Dhnnwyx1MOhQHp/YOkkGMqakpMjdt2pT26YYNG2jHuKF3AJasKMTRTJ069cGDB/oFlpubG7qS+YPoSlSHGnlx7kj/ACXnOWDRwnh/SHeJEiUwMKdOnaLx0RkcO3Ys3JZODSxBtlZ/u/dRz7ckQDuOHj0K9dYZmMXYkB3sjZrQ9lCZoXEyug1notjjx4/XL7Dq1q0rC9dQ15gxY1AdCkSNvB0qpYSoV4+zlvUGrKdPn0J4YdC0KlRD7wb7RH8eYM2fP58GB2NYs2bN29oqepdVqlSRSaH4CB0JmCtZPgXBolMG/jIvnKpq1KgReIWfwW8uXrzo4eGhR2CtXLmyT58+vC/qQmmoDgWiRpSJSlEs6pVZr3kLWLL2hq41aHiuDnTN6D3Rm8PSFFWHd9gnAzdHRwnXIGcOYJ80Rgstb9++Xbb1lkXxZPpWklu1ajVhwgRZ0SpkK90pK9kTUI5oSbrAqA4FiiZRKYoVDec5YMkSLhjo8OHDZWkXCDh//jwtr169etgVWcX1tmJhGJirzz77TLpjOIjVq1drzJXsSAMsevbsSU287bdIktNXxZTSA8Ds2djYTJo0KSQkRPX34o7sDLa2tqgIReETURqqQ4GoEWWiUhQrGs5zwKpQoYJ8xqJiQABc2MrKytnZmWssOb92dnbA4h0MIbqm0+fu7i4HAR84cEDmKu3YsYP72K1atWqRERdv1aClS8EFRgtvGxoaKst7Jk6cyE1Km83Ykq9hGnXxRihQ9uUSrKNePW4oojdgwQZo8TS7JUuWYL0tLS2pJNkRFGSIGefvO0gW5gSrpe2iZWzYr7/+itvChWHMkN+3b1+VeieFd0Ct+KA5c+YgGbJF9x5TgS20sLDI5g2ucIIyeCYbSKMrFIgaUSYqRbGoFyXnOWDRvKiJNm3a4O9kuwTZCJmGKFvKyqKUd2Nv/FatWhWjhfOii7Rz5861a9cWL/7nEi58WcuWLTXR3i28//773377LVWIV4XIw6MBLhWZbQcXivNFRXRQRF2oDgWiRpSJSlGsTDHNc8CiU0Y1QwUaNmwIKaZHQweHCotSBzc3t+bNm8tGxe8c+vfvb2pqKmvFZLkYLRv6lSXl79ixY/fu3XE6MtAPg6b82Qmsu3fvoiIUJRpDdRQANaJMVIpiUa8cLJXneoW8P70qatrb2xseDT/w8/NTqbcqmDx58n//+99/mYVs/E9Tzp8/PwgTc5WF00hgV7DmsLAwbAauMPvniaAiFIW6VOqv4LItD8pEpSgW9ebFXiGBFk8jky3Xafo4F/QCVxgyZAiEtFy5cg0aNPiXtSWfz2RuTGxsbK9evbKw/Obm5jNnzpQNlbO5CrGRKKds2bIoCnWhNFSHAmXLGlTKK6NePVauHoD16tUrzLhKvSMexhx2UrJkSdr9s2fPPv744/bt248bN05m/2W842NmAtqvX7++7PeHXxB2lYUB1/PNN99gFHG42ekHyYtXA14oCnWhNFSHAlGjDLChWNmZDVXLnpS5H1hQXRnEmzt3Lq9Nu79z5w59nJEjR6KLL774QqU+01vmKGe+tmRtuxgn7fvjx4/HX1D9U6dO1V6sIpZMJ4lGiGbsIN1o2qFr166ffPKJ7B4jyTOQpvr/S88zvpkxsPil64CiVOqNM1EdCkSNKBOVoljUK4PPKDz7azm7J/rx2lu3bqWntn37dqw35gRbAvtBF9zcu3dvrVq1xFDhYjQT4f8xEBMN0mTxSrg87Ufl1CFtEog8lgbuJRtDCg7Cw8MRIvtEagYvKB7deCLrSNaEKVOm3Lhxw9fXl44hkTWnR8Gp+ZuQkMCbyvRrWX0kE/000cRNy8zVzFsXbeXg+1AdCkSCfCtDsdyEBdapUweFY89g97kZWMePH0endP6pic8///zKlSsoGvIru8HcunWLrtzQoUNlZDzzYql4aheIUItVq1bNpJdcsWJFoUKFqFeZ+AXIxowZo5marMHlggULoP9U+ZskU/4ff/wR1gyFJ5rm2zkMr3HjxjI1WWYOEhMnpZma/FcdmJh8+OGHmqnJmXxlWc1Lc9q0adOyZcuAKdCRHepQKYotUqTIqlWrUCkKR+3ZDKzsnvP+xx9/+Pj4UJ27du2iCiMjI6tUqYIuAgICQFLx4sUvXryIVaM3R7e5c+fOWXWuZO4LaAkdenl5nTlzBtDQ4YXIg7Py5cuD2mvXrsmU6A4dOoB1/ADYzc0Wixbs6OiIrbazs5s+fbqbmxsAoj1hLTDstPvAwMDZs2dfunRJdnYgjoKhdAMM3cPDAxh17NhxwoQJWFaonuwsh0qxF/iEESNGACm8efZ/28n2lddGRugCKrN582b4ZrNmzeiyBQcHy+oXVFCpUqUNGzZgyWmO2Wy9c1ZwdXWdNm0a/cFq1arBt3B/JdTB398flaLYoKAglOzu7m5ra5udPVb9uEIJGzdupJsGwmhhoaGhskJ12LBhqGnmzJnz58/X9Kr0OMRn4EFbOaNHj4an7969e+nSpfIBAITBOCEYM2bMyNrRO4MeIIVIYa6gq48fP4ZFtWnThr+QUDovqr8XXqKX7J+LkrOAJauYRF2oDgWiRpSJSlEs6uUvqtZL8fSzrlCWhNO26Df16dMHW31aHV68eDFw4ECx2zIsZKD7IOo7ACZwI8MNqGvIkCEy0UgOIIZprFu3bvv27dyUkZS8AizI07Fjx2AJ9AohnsCLPkvdunU1h11pDq5RMPQmqqpRFNcwdzlgEQXS716/fj2uEPWeOnVKXzxVP8Dq2rWrp6fn5cuXLSwssNWyKF6HTimoyjhoL2D8i9aoFbh8+fI9e/bUrl0bOg/O9LWhsn44lpOT03fffffq1as7d+7gB+UUE51BdlntKQd6v4OnkN/Mr6XOgMpk8uZbBQqmKeQ7vJ0ch67TFEWBKBOVoljUi5JlXww92FQ9bgpy9+7dQ4cOubi4ZDCV5fz58wsXLpQtCbSNmc6HNtEy3AIXMGnSJAyhfPGgl4RrgHzIaRRyIErm6y8+Pr5BgwaOjo7Hjx+Hu6jUW8o2bdr06dOn3t7eUON/NKtysAqWQ3u2FlQSC00vWAqJafn+++/DwsJsbGzk86LOImntl4UzEX/kyJEZfKE/fPhwUFBQy5Yt9Ti8rE9qzGvDOjOOEx0dffbs2QLqoG2NdNylfIl78uQJ7VVz+pysW0TLpUuXlq896UJBx3Jo1rkjk1RwFMCEv5Y9QmgJERERZcuWPXPmjAzqqrQ+IWvSan7lZA0KL5vXy/0HDx7UqFFDE0c+RHp5eRUpUkTbcotAHbopqyY///zzDJTWQh3066kNtM+1Zs2a1q1bBwQETJ06tVSpUml3uUXdMpCj0TgX9vb2jx49mjFjxrhx4ySJnNaMJRBUydZkmvi0fiobfMj3YDlmV3Yzk5ukBRMFCxb84osvZME+F7dv30ZUhQoVNGt+5BsfSWgGKvUHQdlQSewoWSCKJHK+kthOjKhK/ZWQgsG7EVWpUiXKrIGRdIrT/QZPEtQiqzKBY1bNic0TwKIW165dK82dOtOexCKNmFqkVqARwjPEOMk01JMnT967dw8nSD3BMIjAfVnCqo0q0iIBa4GomjVrEpm01Le5uTleCYhERUXVq1ePhHJ8IS6VhLgwM3XgEf6xcOHCwJFc6H/g4GrVqkWtBwYGCkBlLRr5AjVZakaOoJm8ihYtKuZn+PDhGzZsAMGIQizlkULKYRZElmOetGcQEY28pkyZAuwy+cVdAdZf4csvv6xTpw66xulonzMgx+NQMdzs0qUL/cpLly7JaTkwIaoHb0K9btu2DSb06aefiu8jPrWrcXZyZFLFihXBE/Utq8SoUaqZOJiikiVLXrt2rXHjxhRA/Bd0UNbocYEFBS7Eb9euHXlVqVIFBPj6+lKAMmXKyHE9xYsXJ2vK5u/vT0LBInlJGTA2sgvm1q1bKWrHjh3LlSuHN5Rzy8iO16RsuMtNmzbxl4JRYM0qS16W18RwEs1gD/Y1OGChLLRJVY0aNercuXPwcWFX0o7bt29/8+bNkJAQaoun0B3xOxgMvANQECGAki53z549iaA5bFdoEzUnx+CAAyDy7NkzrBEWjjtYKZBE1iADUyFrqUGDq6sr4MNCqNQHKcrh8gDl1q1bRONadkSi5EgjlUyScXBwkEno3JcDeQRbmlFNfkEVLUezeQm2dvTo0UjjpcAZLygLQOCIZHrixAnNmIJsUofVPHXqFN6QNzW0bTUNDliLFy+mi4SNOXLkCDQLqyNn4/IIBGCljh07duXKFQwGRoVKQqHgAGBRH8Tx8PCgT0DjBlLiMcWVCLPhDpjDxmB4kEatcH///v1QMZKAV5CENJ5iUbAKMv3whx9+4KJZs2Yq9ZwCIPv1118jnwjAaMeOHeSIZeIaHxoZGfn8+XMKDOy4kH2FKKqfn5+scgMoMeogQEcydoscx48fzzUvwqvx+kAZXFIMMgI37u7umzdvFgdKGwOpKIqiNm/enF8APXnyZAVYGQUfHx8qHn9Ed5oWLHPipHtYSR1+++03+VIhu75ggWSPvJkzZ65ater8+fPgsnLlyn369CEapkjOkQNVVCrOBYHAixrCjVL3XHTt2pUISOA+dgIXA+YwM1gXQMMjPCPJb9y4IZYGawSwkAMKAZMQf1mvJlSPR9evX0cIVqpatWqIlbOAyVFYF6AhQqtWrSjkokWL+vbt+9FHH/GCvIKgSsY2QZWcEwZA5d0pmywVJAviQ/hwo7yFHvfBeuP4raEd3YvZoFJliA8jHxwcLLM3UfS+ffto97NmzeIpagV8tHKsDlWL3YLvywEytOY2bdoMHTpUepehoaEgTw6Kfv/996lUzAMVQysHwZ988onUH4YBsDo7O2PSsDGywB+LglfCkJB7w4YNwc2FCxeky4a/Qyx4ql+//gcffEAc8qWcWD68G60CXOLHSU7Fy+aiFA+IgG/pSHbu3BmsABQKP3HixJ9++ungwYNEw2iRCpBRThoGOObVyGXw4MGrV68WHBOHAqMc9EB5eCrDbIrFemMYMGAA7sPb2xsHBA2CNtEocVWyu+agQYOoUaBGRdLoidCpU6clS5ZQE6ABykJ8T09PESUb+lCRMsgEOIACNQdLw/xgS5o0aQKPOXPmDPVEZCoeyt+2bVuZ2EStcx+EHT58OCAggFQq9YaAdPLp4QMjyiD8j6rFUkonEYPEfaD28ccfc40TxLKSqYxHyNIM7uMxkUnb6KoOdDnxdLyCTN7/73//y6vxgoiSmc28OHFoVLwpEXbu3AnEaWndu3fHUWIsFfL+DwEM9ejRo0WLFi1btqRScWpi/GmaKJcWLB1s4BIUFLR7925sg4wjyHRvPAtuRUSBTnptwkvEfYBO/BoQQSaoOnr06N69e6XnSD8OBAMCOSiVm0LkxbrIcg+Vei8kDBI3Ze9rOT5DBixAD5WNcwSjW7ZsAZTQMlnMTaZYL81gB8DCjmKQECVcHnMl29rK2ZnVq1fn1e7fvw+4uSlGi9f/8ccfRRsUHuNN7iBYhsQUYGUqoCw689p3ZFEodU9V0axxZEAQR4bJkZNR5Zxf6vXs2bO4BpV6E1s5pFS2eKShU530CXAiuCE5vN7FxQVIgSEQjExsAEKobLgOj8qVK4d9ojBkLetnuOCvHHAKagE3BaM8+FOsXbdu3ahvLB+WEuEYRTpu27dvhxdinCiADNvKaBb2FatG2cA3fh8fSknwv7wOyXk1HB9EDaxTHl4cfkZeAix+gZTKgIPBcax0A62fcoKGUaNG7dq169KlS/QW0TV0mDqTkVL+AiPuQKQwYxs3btywYYPmE6y4QhkAg3TTV+cOT/E+oIdax7whGTxBm4AOrgp3ic0AfxhL7BM+F/TgnoARODh9+jTkCQdHcmwV0biDZwRecsw490mFHSK5DIYBC/AkI2okxEvSryS7devWgS36fdB5KCNCZCADs03jgWgiijgYUbw8hD1HnLaXM6bR0ejxAtQrvkM64ZMmTcIrASDcBLWIGXN1dQVh+CAqErrz3XffUW3yDYdKxVqAvA4dOkydOpX6ppKwDTgj0CP7hWBmAAHUCv8Fl8duYXKwcDAbem3kS5Wr1Edq0TWTvWvoY1IA2QANJ4shhK4hENwAC7wYsJDPBjzFkQFcuBGlla/mXFAYuBSIJy3mTQAK3PH40CkKAP3aunUr+MY0YolRAn0L+pKGX2U5Y0Y5XglzJasOqeOVK1fSagEBFTlw4EAch9AX6BfcFmMjJEkzLko1AzIgCLvC2GCxqG8qjPqD7Ddq1Agjh1UgLU8hN0CWHGHEGDARCNT2qQMX/OUmj4hANCKThITcRAiiEIhYhJMFGZEdT8maAlAMGT0RbEkPkZYABMETqOIpv5B3LCgvyGvysrwyL87rowQ9bnmVCy0WfKJ06dI+Pj7UDTRZeDSBm9iMhQsXcp8Wj8eEfIAziD+Y27Rpk6xEhV/Xrl2bp7gkqBuVTaVC0uExuEKuMQPYITwspgXyBK3Bt4IYfsUBzZgxQ9blcYe/oIcL+YyDbSMJ5gdgUTZAAPjICOT9/vvvcgAJmcKWKAYu+OLFi1J+OZqFoq5Zs0ZOhJNRK4Dbpk0bzcEqvDsNhrJh1WrVqqWv+VW5E1gq9dxAHZ1So/7+/qCHChs6dCiQ4o7AiF/MCfRLJtfT7jEYVA+Vd/jwYbwbXgYXduLEiZMnT8KaZZU9fTc8JiQMtgRPwvuAWkwFDgvqJr1RLhAybdo04AX7wX5gYEgin/+QJtNpAAdGlDs4L8ojdhEvSQRhSFIwGSYgAgXgfpkyZXC7Hh4e9G0HDBiAcBnOAIiar1U5JeQMYOGDZKdNakKMENUGAaKaQ0JCJkyYIAexAhpqVGY1Uf24ErwMBoM6o/IOHToEqZIN0ImDAYCuyYJ0OdMV4NLd446sRyBH4uDRwAQXUrWAg7/clM+UyIHbARcSSpHkvNMkdQCpmCIYEv3NPXv24BAphoyrYRplqAyZCIHh8Y68Be+CLcQPIlMm7YgGEEJ/FkMIt8OeKcD6t4GamDhx4t69e6Wrj3NZv349JgFHVkUdwAFuglo5qQ40bjEt1KjM0ZNvNSQJCwsDOlQVXoZUIINKAnbUGf5LMITHJBVwxE9hAqljjKKMxIr/4hGWDCDyiAjyyZkkgAMfJ6c7IRAPC16RyR2yE3ZPAWTGlUzagfvL9g0U+MCBA6tWrQJ5TZo0AZ1gV6ZAyhRkWsvgwYPlgwE3QdjMmTNllyIFWO8Y6OWhR/gTVYiPwAZQScuWLaOasTd0lI4dO8YjlC6cGsuhmaKEO9NMuZFpfcK4f/nllwsXLlBh9erVg3jh/iDdQJBrKhW0nTt3TnqU1Cv0CKAQX46iAGTI5ybGEmkUBuQhh4QUgFIhCoFkjWWVjRWI3759e96CAmgsEAXTDGwSgWJTQmwqGZEchFEYd3d33otHnTp14pWxoJQBMyYKMcz5fTmmVwgZopXDfLmGM2EDsDHU0Pz580eOHAnssA2YGcwAUMBIlC9fvnr16pIWk4ChklZOtVGpx48fp28FtuQTEMjAtWE8sEAQdsgWv3Am6hJRuCeSi9eD3cuZdVyIbeMREYhGZJJokosxQyzCyUK2lyFTsqYAmiWTJKd4Uk4KTLERJUdH8Rf3evDgQfqGs2bN4t0LFSpEEjmYnb8oBLUoww3vHtAydQaxoM8FnjZv3gyYZDCJKkS/NHrTvwM2gLrhkcYqQGKoV82KTfmYfeXKFX5h5dQ65gdXS51B7fFZclwUdzA82Bv+4nDnzZuHw4JWP1QHLvjLTR4RgWhEJokcjYQQRCGQOwgnCzLSZKoZ2KRIFIziyV8KTLFlxqmM4uL07e3tgSxgldXhY8aM2bFjBwhDFSgEtej3aNycDSyqCj1ST2PHjqUmYEJwWwEcfgdyA32RbY8gHHjG+vXr43Q0yYWJa09rBgQYGznLNDAwUE5TlhOU5YsvAvE11DF1j1PDPtHbBx+y8zaBC/5yk0ey7SCRSUJC+S4u+/rLydZkQUYkIVOeau/0R8G0p+ZRbArPK/AivA4vhdOX7wTS1f3yyy/d3NxgdaNHj5ZpPwa+7tLQXWHLli1xPZAeKobKgM1wc/r06c7OzjyCjLdo0YJWDg0CeVOmTNFWN5VE3WivAZQxblq8jBIhU5b3CE3mWub7IopoYjawGRgq2SuQwAV/uckjIkjfkCQklEUTIk1m8MlIG9dE0yzyVv19PJ02+6bYFB5RvAgxeSleDR8KdlesWKFSH+QhO45i/ChDlm+mmueARde6adOm33zzDbWIbRg0aBB9K/wC/UT5aIOW6alBdWfPng1T0U5L5clx4jo3Ib/UEIYHhwJEqEK8m6wFoh+AkRDTgqXZv38/nAYvDG4c1YEL/nKTR3LGk2yjQEKSIwRRCEQswsmCjMhO5+ueTPPSuUnheQVehNfhpXg1XhD3N2nSJF65X79+mC5Zn4Nz1MzgMNhg6B+h8TVVqlRBp3SX5HQrWDDGgD7/rl27vL296ZQ9ffqU5t6tWzedtNQc3Xieah+0jBAMD+DABuC5wCsuCcYtrEVWlkLd+MWi0F8DHNgbHJN8FOIX60JaLkglO1zK6SmYK/lcTULZyBTDA20XLGobJ+IDPniVzuoufD0x9+zZE6EOvCMIRsLcuXOhdPC24OBgzNicOXP0eIp4bhhuoJ6AFOr29PRcsmQJRgLN4gugINR9fnXAJGDG0l3AKQfypN2fWCwTWJH1M5Ah0FO8eHEojizLvnbtGmYGTs1NcVvYDJmaDJ7AhHzYBqBgiz4p0JfFx3I8xN27d2WTJqAmliztBslvOvONFwFA27dvlwlbCAHxwF06EDABWdNh+MHIYDsXgipqUcNOaOjUN0SY5it7LVNn8C13d/d0JRC/b9++wEVnBYvsLNWxY0fsCjBFCGiQMYjLly8DF+gzBqNmzZp03CA9MHGiCSWi1qtVq0YcvB51f+nSJRmJJU6NGjVAEiWUk6GIRtd1586dsn2jdgHgZMB67dq1b9rK9tixY5MnT0YIzhfODprhlK1atZLVOxjFzO+Bq1isdFBFvWrvj4U2QYNKvfdBjx49hGy9CVVC3ql12r3O9gdUMwig/y/zpSA34EZ2sabKfX19sYLUIlnzSJZICMkTUOLakAZecXZEIz7wql69Om5LPg1hwAICAhBOFmSks1uErBeiYBkMnfNSiJ03bx5cbdy4cdpzRKUzIdpQgPV2gdrFYOigSjtcvXqVeqX6cVtU0sKFC3GRI0aM0NnPXY5spbJ1gCVDR5CzBg0aYK4gyLa2tgjEKnTv3n3o0KGkSnu8EbZHpT6bSWfiip+fH8yPZgAUEELhEYi5AjpQwLR7kMgCVDkQVedRYGDgokWLcHkjR47k1dasWYNAXla78SAQK2742DIxQFsFqtBdBnv5YYSoP1khePLkSTk6FeL1yy+/6Cw5p5JkUU1aXk+d8duuXTvitG7dGrjgnuQpHYXOnTvT/9KWJse06OB72rRpX331lewXQhJkwrS8vLzwX/TvJAudVMLPyDSttIEDB8raxrp16zZu3BigUxLy1bHKyCQO2EKOZgaRAqy39oBpAy4GHoOtgnCcPn3aysoKZwG5hrXgPrRjCvtOV4h8lsaSYeogYdpH2MnaCu4gUyZE4LbEhoE2aJZ8rqFSiaY9ckZM4I4DXbZsGfe1x650sEVCnZsUHj5HLrwXL4UQ2Vkk3bMqtO2WYWLLgID1jx5QE4oWLUpN41DoIsFsZOgIYpu2DqhmGUmXOQ46dQNJwiTI0dTQ5P79+/MXeoS0WrVqabqBgADgypgq1kK+MgkQiebj47Nr1y5wQF6AQ5bxnD17lr9kIce9akMW8FEkzekV2q0FafQJgPv58+cxXaTlNTWrjNIaXQoGtmQGvQKsN9qqyMjIzKBKpd7/acWKFXK4DU0cggy2oDhphw2pwmbNmq1evRqrpg0smblAtw4PSAdToHPw4EHiNGrUiFoHiOmWBPKkM8wGUIDjrVu3SI4Q7iAQsQgnCx2jJbPv+/XrR8F0JPMi9CFwvjL0j2fk7fCYGex0pfGJBsi3DGK4ITO8Ki2VHjZsGOYExMiEAq63bNkiu7hohxMnTgwZMgTo6PT55VAuYCfD5VRMGXWQVVaoBTsn6/FlDaqM4MsuHWTHBQJJJTunUcfB6iDTsHBqR48eld0udZwgOPj555+FlmkHOABdB1oL2WHq0AnXS5culY5wBoHXh2I6ODgYlE/UP7AyyavShm3bti1fvlyO6YLxeHh4aOaJ6xgVSDG+CQBp8y2ZoUXFg0U54Vdm1WEt5ALTIofCaVbZy/IbGUSQFYtyrqIkkQtuwo14I/5yrTPMAeDq169PJyPdl8X9jR8/XnoJYH3w4MFdunTJjCpoDDhQzWxEBVhvwaveBEp6YYBATkx5U8BV4X3wUzItWAdbcuab7EiDqZBpCLJPpJyZKLZNbI8suRbjJ9N1ZPdi3JZsr6VZfqODKuKQEfwMv5yWYGmHa9eukSPO9K2OCgdb2EJZcJbXgQWq4FVv5QHfOUycOHH9+vWurq7pdhKlZyeeS7OPqGwkKRhKu/GpQE1KLnjSHHiZrkqRc/Pmzd69e8+cOfN/9I7iEw0EW3oD1jt7wHcLdNYGDBgQGBgo81jSrXiV1h6y6Y5+aR5pdgrVHnqVi3RTAUHcXLly5VauXInD+t+9puHYLf0AK5tRpWHH/fv39/f3l8FJnRfXUHvt7ZP/UWbayDrAkvuQ+ooVK3p6eqbtW/wvsAXfgsvrF1t6mI/1L3nVOwcqddWqVdWqVZPNZ3U6iTrbcWdyfqZO5LRgJSOyI1OyzgZUyRgE7IKeh0zmySvAyk5elTbA39etW0evXiYoa89dkf2907XfOs7uTaZLO7lIlknJZEem2bnZkIyd6hdb2eoK9eIB0w1bt25dunQpHoqeF4ZEe2p8Jg3Vm/QGnmD9vCYvi88dNmyYvk6z0S/fyj5g6csDvinQoIHXkSNHbt26BVAgJTJ9NJPJtTde1zB0HN+zZ89kT+/mzZsDqbc6Mj038a1sAhYtOCYmRl8eMIMgX3w3btx46tQpmSYlH3MyrxYZDOPtZMVEo0aNevXq1bBhwzd9gdaL3bJWh9wGrKCgoDt37tCC0z3Aw0DCoUOHTpw4cf36dViR7OchJwa8ST9iruLi4mgzuD9nZ2c3N7cmTZoY4PoZsHX06FGKp1nJmEuAhXeQwzxUBh9iY2MvXLggyzToZFBsjYvU+D5hUbxUfHw8ZqBBgwYdOnSoU6eOwU6NIvAuskgkl49jGX4IDAw8efIk8Lp06RKG1tHRETABLK6fPn1KJdWsWRNINW7cWGfaqhIUYGUqHDhwYM6cOVBg+cIdEhICUxw3bpzhr+xTgGXo4cmTJ6NGjcJ0cY2hWrBggWzBpQQFWFnAvbp27Yor3Lp1q+EvFlWAlZPCo0ePVOopqYoqFGApQW/BWFGBEhRgKUEBlhIUYClBCQqwlKAASwkKsJSgBAVYSlCApQQFWEpQggIsJSjAUoICLCUoQQGWEhRgKUEBlhKUoABLCQqwlKAASwlKUIClBAVYSlCApQQlKMBSggIsJSjAUoISFGApweDC/wkwAP1GL5KhxsX+AAAAAElFTkSuQmCC'); 11 | margin: 20px 30px; 12 | } 13 | .navigation { 14 | margin-left: 33px; 15 | } 16 | .navigation a { 17 | display: inline-block; 18 | padding: 8px 12px; 19 | border-radius: 3px; 20 | text-decoration: none; 21 | font-size: 12px; 22 | text-transform: uppercase; 23 | font-weight: bold; 24 | border: 1px solid #454545; 25 | color: #454545; 26 | background: transparent; 27 | } 28 | .navigation a:hover { 29 | background: #454545; 30 | color: #fff; 31 | } 32 | .container { 33 | border: 1px solid #454545; 34 | border-radius: 3px; 35 | padding: 20px; 36 | margin: 33px; 37 | } 38 | .container a { 39 | color: #454545; 40 | } 41 | .container table { 42 | font-size: 11px; 43 | margin-top: 20px; 44 | } 45 | .container table thead td { 46 | background-color: #f5f5f5; 47 | padding: 4px 10px; 48 | } 49 | .container table tbody td { 50 | padding: 4px 10px; 51 | } 52 | .container .box { 53 | border-top: 1px solid #ddd; 54 | padding-top: 10px; 55 | margin-top: 30px; 56 | } 57 | .container input { 58 | background-color: #f5f5f5; 59 | border: 0; 60 | padding: 5px 10px; 61 | } 62 | .container input[type="submit"] { 63 | background-color: #ccc; 64 | cursor: pointer; 65 | } 66 | .container input[type="submit"]:hover { 67 | background-color: #222; 68 | color: #fff; 69 | } 70 | .container button { 71 | background-color: #ccc; 72 | border: 0; 73 | padding: 5px 10px; 74 | cursor: pointer; 75 | } 76 | .container button:hover { 77 | background-color: #222; 78 | color: #fff; 79 | } 80 | .footer { 81 | border-radius: 3px; 82 | padding: 20px; 83 | margin: 33px; 84 | text-align: right; 85 | font-size: 11px; 86 | } 87 | .footer a { 88 | color: #454545; 89 | } 90 | -------------------------------------------------------------------------------- /public/img/demo-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panique/mini3/34a2867e8175319074e9ff122227e34c1e428a8f/public/img/demo-image.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * For more info about namespaces plase @see http://php.net/manual/en/language.namespaces.importing.php 19 | */ 20 | 21 | // set a constant that holds the project's folder path, like "/var/www/". 22 | // DIRECTORY_SEPARATOR adds a slash to the end of the path 23 | define('ROOT', dirname(__DIR__) . DIRECTORY_SEPARATOR); 24 | // set a constant that holds the project's "application" folder, like "/var/www/application". 25 | define('APP', ROOT . 'application' . DIRECTORY_SEPARATOR); 26 | 27 | // This is the auto-loader for Composer-dependencies (to load tools into your project). 28 | require ROOT . 'vendor/autoload.php'; 29 | 30 | // load application config (error reporting etc.) 31 | require APP . 'config/config.php'; 32 | 33 | // load application class 34 | use Mini\Core\Application; 35 | 36 | // start the application 37 | $app = new Application(); 38 | -------------------------------------------------------------------------------- /public/js/application.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // just a super-simple JS demo 4 | 5 | var demoHeaderBox; 6 | 7 | // simple demo to show create something via javascript on the page 8 | if ($('#javascript-header-demo-box').length !== 0) { 9 | demoHeaderBox = $('#javascript-header-demo-box'); 10 | demoHeaderBox 11 | .hide() 12 | .text('Hello from JavaScript! This line has been added by public/js/application.js') 13 | .css('color', 'green') 14 | .fadeIn('slow'); 15 | } 16 | 17 | // if #javascript-ajax-button exists 18 | if ($('#javascript-ajax-button').length !== 0) { 19 | 20 | $('#javascript-ajax-button').on('click', function(){ 21 | 22 | // send an ajax-request to this URL: current-server.com/songs/ajaxGetStats 23 | // "url" is defined in views/_templates/footer.php 24 | $.ajax(url + "/songs/ajaxGetStats") 25 | .done(function(result) { 26 | // this will be executed if the ajax-call was successful 27 | // here we get the feedback from the ajax-call (result) and show it in #javascript-ajax-result-box 28 | $('#javascript-ajax-result-box').html(result); 29 | }) 30 | .fail(function() { 31 | // this will be executed if the ajax-call had failed 32 | }) 33 | .always(function() { 34 | // this will ALWAYS be executed, regardless if the ajax-call was success or not 35 | }); 36 | }); 37 | } 38 | 39 | }); 40 | --------------------------------------------------------------------------------