├── public ├── favicon.ico ├── packages │ └── .gitkeep ├── robots.txt ├── assets │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── css │ │ └── custom.css │ └── js │ │ ├── img.send.js │ │ ├── login.ajax.js │ │ ├── vote.ajax.js │ │ ├── infinitescroll.js │ │ └── bootstrap.min.js ├── .htaccess └── index.php ├── app ├── commands │ └── .gitkeep ├── config │ ├── packages │ │ └── .gitkeep │ ├── compile.php │ ├── testing │ │ ├── cache.php │ │ └── session.php │ ├── workbench.php │ ├── view.php │ ├── remote.php │ ├── auth.php │ ├── queue.php │ ├── cache.php │ ├── database.php │ ├── mail.php │ ├── session.php │ └── app.php ├── controllers │ ├── .gitkeep │ ├── BaseController.php │ ├── HomeController.php │ ├── VoteController.php │ ├── ImageController.php │ └── UserController.php ├── database │ ├── seeds │ │ ├── .gitkeep │ │ ├── DatabaseSeeder.php │ │ ├── ImagesTableSeeder.php │ │ └── UsersTableSeeder.php │ ├── migrations │ │ ├── .gitkeep │ │ ├── 2013_09_17_161723_createvotestable.php │ │ ├── 2013_08_20_133641_createimagestable.php │ │ └── 2013_08_20_133617_createuserstable.php │ └── production.sqlite ├── start │ ├── local.php │ ├── artisan.php │ └── global.php ├── storage │ ├── .gitignore │ ├── cache │ │ └── .gitignore │ ├── logs │ │ └── .gitignore │ ├── meta │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ └── views │ │ └── .gitignore ├── tests │ ├── ExampleTest.php │ └── TestCase.php ├── lang │ └── en │ │ ├── pagination.php │ │ ├── reminders.php │ │ └── validation.php ├── models │ ├── Votes.php │ ├── User.php │ ├── Images.php │ └── Users.php ├── views │ ├── images │ │ ├── list.blade.php │ │ └── show.blade.php │ ├── main │ │ ├── register.blade.php │ │ └── index.blade.php │ ├── user │ │ ├── index.blade.php │ │ └── settings │ │ │ ├── notification_history.blade.php │ │ │ ├── profile.blade.php │ │ │ ├── notification.blade.php │ │ │ └── account.blade.php │ └── layouts │ │ └── master.blade.php ├── filters.php └── routes.php ├── .gitattributes ├── .gitignore ├── server.php ├── phpunit.xml ├── composer.json ├── README.md ├── bootstrap ├── paths.php ├── start.php └── autoload.php └── artisan /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/commands/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /app/config/packages/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/database/seeds/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/packages/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/database/production.sqlite: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/start/local.php: -------------------------------------------------------------------------------- 1 | client->request('GET', '/'); 13 | 14 | $this->assertTrue($this->client->getResponse()->isOk()); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /app/controllers/BaseController.php: -------------------------------------------------------------------------------- 1 | layout)) 13 | { 14 | $this->layout = View::make($this->layout); 15 | } 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Redirect Trailing Slashes... 9 | RewriteRule ^(.*)/$ /$1 [L,R=301] 10 | 11 | # Handle Front Controller... 12 | RewriteCond %{REQUEST_FILENAME} !-d 13 | RewriteCond %{REQUEST_FILENAME} !-f 14 | RewriteRule ^ index.php [L] 15 | -------------------------------------------------------------------------------- /app/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | call('UserTableSeeder'); 15 | $this->call('UsersTableSeeder'); 16 | $this->command->info('Users table seeded!'); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/database/seeds/ImagesTableSeeder.php: -------------------------------------------------------------------------------- 1 | truncate(); 9 | 10 | $images = array( 11 | 12 | ); 13 | 14 | // Uncomment the below to run the seeder 15 | // DB::table('images')->insert($images); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/start/artisan.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 18 | 'next' => 'Next »', 19 | 20 | ); -------------------------------------------------------------------------------- /app/controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | with('title', 'Homepage') 14 | ->with('images', Images::orderBy('created_at', 'desc') 15 | ->where('private', 0) 16 | ->limit(6) 17 | ->get()); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /app/config/testing/cache.php: -------------------------------------------------------------------------------- 1 | 'array', 19 | 20 | ); -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./app/tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/assets/js/img.send.js: -------------------------------------------------------------------------------- 1 | $('#form-buttons').hide(); 2 | 3 | $("#browse").click(function () { 4 | $('#multiple-files').show(''); 5 | $("#multiple-files").click(); 6 | }); 7 | 8 | $('#multiple-files').on('change', function() { 9 | $('#form-buttons').show(); 10 | $('#notifications').hide(); 11 | $('#files').html(''); 12 | for (var i = 0; i < this.files.length; i++) { 13 | $('#files').append('
Nazwa pliku: ' + this.files[i].name + '
'); 14 | } 15 | }); 16 | 17 | $('#reset').on('click', function() { 18 | $('#files').html(''); 19 | $('#form-buttons').hide(); 20 | }); -------------------------------------------------------------------------------- /app/config/testing/session.php: -------------------------------------------------------------------------------- 1 | 'array', 20 | 21 | ); -------------------------------------------------------------------------------- /public/assets/js/login.ajax.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#login-alert').hide(); 3 | $('#login-form').on('submit', function(event) { 4 | event.preventDefault(); 5 | var data = $(this).serialize(); 6 | $.ajax({ 7 | url: $(this).attr('action'), 8 | type: 'POST', 9 | data: data, 10 | success: function(data) { 11 | if(data.success === true ) { 12 | location.href = data.redirect; 13 | } else { 14 | $('#login-alert').text(data.message).show(); 15 | } 16 | } 17 | }); 18 | return false; 19 | }); 20 | }); -------------------------------------------------------------------------------- /app/lang/en/reminders.php: -------------------------------------------------------------------------------- 1 | "Passwords must be six characters and match the confirmation.", 17 | 18 | "user" => "We can't find a user with that e-mail address.", 19 | 20 | "token" => "This password reset token is invalid.", 21 | 22 | ); -------------------------------------------------------------------------------- /public/assets/js/vote.ajax.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | var alert = $('#vote-alert'); 3 | alert.hide(); 4 | $('.vote').on('click', function(event) { 5 | event.preventDefault(); 6 | var data = $(this).serialize(); 7 | $.ajax({ 8 | url: $(this).attr('href'), 9 | type: 'GET', 10 | data: data, 11 | success: function(data) { 12 | if(data.success === true ) { 13 | alert.text(data.message).show(); 14 | alert.addClass('alert-success').removeClass('alert-danger'); 15 | $('.vote').attr('disabled', true); 16 | } else { 17 | alert.text(data.message).show(); 18 | $('.vote').attr('disabled', true); 19 | } 20 | } 21 | }); 22 | return false; 23 | }); 24 | }); -------------------------------------------------------------------------------- /app/database/migrations/2013_09_17_161723_createvotestable.php: -------------------------------------------------------------------------------- 1 | increments('id'); 17 | $table->integer('user_id'); 18 | $table->string('image_id'); 19 | $table->boolean('vote'); 20 | $table->boolean('notification'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::drop('votes'); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "description": "The Laravel Framework.", 4 | "keywords": ["framework", "laravel"], 5 | "require": { 6 | "laravel/framework": "4.1.*", 7 | "way/generators": "dev-master", 8 | "intervention/image": "dev-master" 9 | }, 10 | "autoload": { 11 | "classmap": [ 12 | "app/commands", 13 | "app/controllers", 14 | "app/models", 15 | "app/database/migrations", 16 | "app/database/seeds", 17 | "app/tests/TestCase.php" 18 | ] 19 | }, 20 | "scripts": { 21 | "post-install-cmd": [ 22 | "php artisan optimize" 23 | ], 24 | "pre-update-cmd": [ 25 | "php artisan clear-compiled" 26 | ], 27 | "post-update-cmd": [ 28 | "php artisan optimize" 29 | ], 30 | "post-create-project-cmd": [ 31 | "php artisan key:generate" 32 | ] 33 | }, 34 | "config": { 35 | "preferred-install": "dist" 36 | }, 37 | "minimum-stability": "dev" 38 | } 39 | -------------------------------------------------------------------------------- /app/database/migrations/2013_08_20_133641_createimagestable.php: -------------------------------------------------------------------------------- 1 | string('id'); 17 | $table->integer('user_id'); 18 | $table->string('img_min'); 19 | $table->string('img_big'); 20 | $table->string('ip'); 21 | $table->boolean('private'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::drop('images'); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/database/migrations/2013_08_20_133617_createuserstable.php: -------------------------------------------------------------------------------- 1 | increments('id'); 17 | $table->string('email')->unique(); 18 | $table->string('username'); 19 | $table->string('password'); 20 | $table->string('name'); 21 | $table->string('url'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::drop('users'); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/config/workbench.php: -------------------------------------------------------------------------------- 1 | '', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Workbench Author E-Mail Address 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Like the option above, your e-mail address is used when generating new 24 | | workbench packages. The e-mail is placed in your composer.json file 25 | | automatically after the package is created by the workbench tool. 26 | | 27 | */ 28 | 29 | 'email' => '', 30 | 31 | ); -------------------------------------------------------------------------------- /app/config/view.php: -------------------------------------------------------------------------------- 1 | array(__DIR__.'/../views'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Pagination View 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This view will be used to render the pagination link output, and can 24 | | be easily customized here to show any view you like. A clean view 25 | | compatible with Twitter's Bootstrap is given to you by default. 26 | | 27 | */ 28 | 29 | 'pagination' => 'pagination::simple', 30 | 31 | ); 32 | -------------------------------------------------------------------------------- /app/models/Votes.php: -------------------------------------------------------------------------------- 1 | belongsTo('Users', 'user_id'); 17 | } 18 | 19 | public function images() 20 | { 21 | return $this->belongsTo('Images', 'image_id'); 22 | } 23 | 24 | public static function insertVote($imageId, $voteChoice) 25 | { 26 | return Votes::insert(array( 27 | 'user_id' => Auth::user()->id, 28 | 'image_id' => $imageId, 29 | 'vote' => $voteChoice, 30 | 'notification' => 1, 31 | 'created_at' => new DateTime, 32 | 'updated_at' => new DateTime 33 | )); 34 | } 35 | 36 | public static function editNotification($vote) 37 | { 38 | $vote->notification = 0; 39 | 40 | return $vote->save(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/models/User.php: -------------------------------------------------------------------------------- 1 | getKey(); 30 | } 31 | 32 | /** 33 | * Get the password for the user. 34 | * 35 | * @return string 36 | */ 37 | public function getAuthPassword() 38 | { 39 | return $this->password; 40 | } 41 | 42 | /** 43 | * Get the e-mail address where password reminders are sent. 44 | * 45 | * @return string 46 | */ 47 | public function getReminderEmail() 48 | { 49 | return $this->email; 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Laravel 4 + jQuery Image Uploader 2 | 3 | ~~Simple~~ Application written in Laravel 4. 4 | - Upload multiple images 5 | - User account management 6 | - After register possible to view users public / private images 7 | - Pagination in image list (infinite scrolling via jQuery script) 8 | - Information about image (user and date), inputs with links to image 9 | - Image rating system for registered users (AJAX) 10 | - Notification system (latest and history). Notification counter in navigation bar. 11 | - **New!** Upgrade to Laravel 4.1 12 | 13 | Also work on mobile devices via responsive design. 14 | 15 | [Youtube presentation](http://www.youtube.com/watch?v=3lrkrJQlNJ0) - Some features (like rating system) are not included in the presentation 16 | 17 | ### Tools 18 | 19 | - Laravel 4 20 | - jQuery 1.10.2 (with AJAX) 21 | - Twitter Bootstrap v3.0.0 22 | - Intervention Image Class 23 | - Infinite Scroll - jQuery Plugin 24 | 25 | ### How to use 26 | 27 | 1. Clone repo 28 | 2. Run `composer install` 29 | 3. Run `php artisan migrate` to migrate database tables 30 | 3. If you want run `php artisan db:seed` to seed database with example data (such as users) 31 | 4. Enjoy it 32 | 33 | ### TODO 34 | 35 | - Users comment system 36 | - Tags (with search) 37 | 38 | ### License 39 | 40 | This application is licensed under the [MIT License](http://opensource.org/licenses/MIT). 41 | 42 | Copyright 2014 [Radosław Kosiński](http://rkosinski.pl/) 43 | -------------------------------------------------------------------------------- /app/database/seeds/UsersTableSeeder.php: -------------------------------------------------------------------------------- 1 | truncate(); 9 | DB::table('users')->delete(); 10 | 11 | $users = array( 12 | 'email' => 'radek@blog.com', 13 | 'username' => 'rdoe', 14 | 'password' => Hash::make('radek'), 15 | 'name' => 'Radek Doe', 16 | 'url' => 'http://rkosinski.pl', 17 | 'created_at' => new DateTime, 18 | 'updated_at' => new DateTime 19 | ); 20 | 21 | DB::table('users')->insert($users); 22 | 23 | $users = array( 24 | 'email' => 'piotr@blog.com', 25 | 'username' => 'piotr89', 26 | 'password' => Hash::make('piotr'), 27 | 'name' => 'Piotr Doe', 28 | 'url' => 'http://piotr-doe.pl', 29 | 'created_at' => new DateTime, 30 | 'updated_at' => new DateTime 31 | ); 32 | 33 | DB::table('users')->insert($users); 34 | 35 | $users = array( 36 | 'email' => 'agata@blog.com', 37 | 'username' => 'agaaaa', 38 | 'password' => Hash::make('agata'), 39 | 'name' => 'Agata Doe', 40 | 'url' => 'http://agata.pl', 41 | 'created_at' => new DateTime, 42 | 'updated_at' => new DateTime 43 | ); 44 | 45 | DB::table('users')->insert($users); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/views/images/list.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 | @foreach($images as $image) 16 | 17 |
18 | 19 | {{ HTML::image('uploads/' . $image->id . '/' . $image->img_min, $image->img_min, array('class' => 'img-responsive img-thumbnail')) }} 20 | 21 |
22 | 23 | @endforeach 24 | 25 |
26 | links(); ?> 27 | 28 |
29 | 30 | @stop 31 | 32 | @section('add_script') 33 | {{ HTML::script('assets/js/infinitescroll.js') }} 34 | 55 | @stop -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | /* 10 | |-------------------------------------------------------------------------- 11 | | Register The Auto Loader 12 | |-------------------------------------------------------------------------- 13 | | 14 | | Composer provides a convenient, automatically generated class loader 15 | | for our application. We just need to utilize it! We'll require it 16 | | into the script here so that we do not have to worry about the 17 | | loading of any our classes "manually". Feels great to relax. 18 | | 19 | */ 20 | 21 | require __DIR__.'/../bootstrap/autoload.php'; 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Turn On The Lights 26 | |-------------------------------------------------------------------------- 27 | | 28 | | We need to illuminate PHP development, so let's turn on the lights. 29 | | This bootstraps the framework and gets it ready for use, then it 30 | | will load up this application so that we can run it and send 31 | | the responses back to the browser and delight these users. 32 | | 33 | */ 34 | 35 | $app = require_once __DIR__.'/../bootstrap/start.php'; 36 | 37 | /* 38 | |-------------------------------------------------------------------------- 39 | | Run The Application 40 | |-------------------------------------------------------------------------- 41 | | 42 | | Once we have the application, we can simply call the run method, 43 | | which will execute the request and send the response back to 44 | | the client's browser allowing them to enjoy the creative 45 | | and wonderful application we have whipped up for them. 46 | | 47 | */ 48 | 49 | $app->run(); -------------------------------------------------------------------------------- /app/controllers/VoteController.php: -------------------------------------------------------------------------------- 1 | false, 'message' => 'You must be logged in to vote!')); 16 | 17 | } else { 18 | $vote = Votes::where('image_id', $imageId)->where('user_id', Auth::user()->id)->count(); 19 | 20 | if ((int) $vote !== 0) { 21 | return Response::json(array('success' => false, 'message' => 'You already voted!')); 22 | 23 | } else { 24 | Votes::insertVote($imageId, $voteChoice); 25 | 26 | return Response::json(array('success' => true, 'message' => 'Thank you for voting!')); 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Mark notification as read. 33 | * 34 | * @return object Redirect 35 | */ 36 | public function markNotification() 37 | { 38 | if (Input::get('user_id') === Auth::user()->id) { 39 | Votes::editNotification(Votes::find(Input::get('id'))); 40 | 41 | return Redirect::route('notification_user') 42 | ->with('status', 'alert-success') 43 | ->with('message', 'Success! Marked notification as read.'); 44 | } else { 45 | return Redirect::route('notification_user') 46 | ->withErrors(array('error' => 'Error! Cannot mark notification as read.')); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/config/remote.php: -------------------------------------------------------------------------------- 1 | 'production', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Remote Server Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | These are the servers that will be accessible via the SSH task runner 24 | | facilities of Laravel. This feature radically simplifies executing 25 | | tasks on your servers, such as deploying out these applications. 26 | | 27 | */ 28 | 29 | 'connections' => array( 30 | 31 | 'production' => array( 32 | 'host' => '', 33 | 'username' => '', 34 | 'password' => '', 35 | 'key' => '', 36 | 'keyphrase' => '', 37 | 'root' => '/var/www', 38 | ), 39 | 40 | ), 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Remote Server Groups 45 | |-------------------------------------------------------------------------- 46 | | 47 | | Here you may list connections under a single group name, which allows 48 | | you to easily access all of the servers at once using a short name 49 | | that is extremely easy to remember, such as "web" or "database". 50 | | 51 | */ 52 | 53 | 'groups' => array( 54 | 55 | 'web' => array('production') 56 | 57 | ), 58 | 59 | ); -------------------------------------------------------------------------------- /bootstrap/paths.php: -------------------------------------------------------------------------------- 1 | __DIR__.'/../app', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Public Path 21 | |-------------------------------------------------------------------------- 22 | | 23 | | The public path contains the assets for your web application, such as 24 | | your JavaScript and CSS files, and also contains the primary entry 25 | | point for web requests into these applications from the outside. 26 | | 27 | */ 28 | 29 | 'public' => __DIR__.'/../public', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Base Path 34 | |-------------------------------------------------------------------------- 35 | | 36 | | The base path is the root of the Laravel installation. Most likely you 37 | | will not need to change this value. But, if for some wild reason it 38 | | is necessary you will do so here, just proceed with some caution. 39 | | 40 | */ 41 | 42 | 'base' => __DIR__.'/..', 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Storage Path 47 | |-------------------------------------------------------------------------- 48 | | 49 | | The storage path is used by Laravel to store cached Blade views, logs 50 | | and other pieces of information. You may modify the path here when 51 | | you want to change the location of this directory for your apps. 52 | | 53 | */ 54 | 55 | 'storage' => __DIR__.'/../app/storage', 56 | 57 | ); 58 | -------------------------------------------------------------------------------- /app/models/Images.php: -------------------------------------------------------------------------------- 1 | 'mimes:jpeg,bmp,png|max:3000' 24 | ); 25 | 26 | /** 27 | * Validate image method. 28 | * 29 | * @param file $data 30 | * @return object Validator 31 | */ 32 | public static function validateImage($data) 33 | { 34 | return Validator::make($data, static::$rules); 35 | } 36 | 37 | /** 38 | * Insert image to database table; 39 | * 40 | * @param string $folderName 41 | * @param string $fileName 42 | * @return array 43 | */ 44 | public static function insertImage($folderName, $fileName) 45 | { 46 | return Images::insert(array( 47 | 'id' => $folderName, 48 | 'user_id' => Auth::check() ? Auth::user()->id : 0, 49 | 'img_big' => $fileName, 50 | 'img_min' => 'min_' . $fileName, 51 | 'ip' => Request::getClientIp(), 52 | 'private' => Input::get('private') ? 1 : 0, 53 | 'created_at' => date('Y-m-d H:i:s'), 54 | 'updated_at' => date('Y-m-d H:i:s') 55 | )); 56 | } 57 | 58 | /** 59 | * Relation with Users table. 60 | * 61 | * @return void 62 | */ 63 | public function users() 64 | { 65 | return $this->belongsTo('Users', 'user_id'); 66 | } 67 | 68 | /** 69 | * Relation with Votes table. 70 | * 71 | * @return void 72 | */ 73 | public function votes() 74 | { 75 | return $this->hasMany('Votes'); 76 | } 77 | } -------------------------------------------------------------------------------- /app/config/auth.php: -------------------------------------------------------------------------------- 1 | 'eloquent', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Authentication Model 23 | |-------------------------------------------------------------------------- 24 | | 25 | | When using the "Eloquent" authentication driver, we need to know which 26 | | Eloquent model should be used to retrieve your users. Of course, it 27 | | is often just the "User" model but you may use whatever you like. 28 | | 29 | */ 30 | 31 | 'model' => 'User', 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Authentication Table 36 | |-------------------------------------------------------------------------- 37 | | 38 | | When using the "Database" authentication driver, we need to know which 39 | | table should be used to retrieve your users. We have chosen a basic 40 | | default value but you may easily change it to any table you like. 41 | | 42 | */ 43 | 44 | 'table' => 'users', 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Password Reminder Settings 49 | |-------------------------------------------------------------------------- 50 | | 51 | | Here you may set the settings for password reminders, including a view 52 | | that should be used as your password reminder e-mail. You will also 53 | | be able to set the name of the table that holds the reset tokens. 54 | | 55 | */ 56 | 57 | 'reminder' => array( 58 | 59 | 'email' => 'emails.auth.reminder', 60 | 61 | 'table' => 'password_reminders', 62 | 63 | 'expire' => 60, 64 | 65 | ), 66 | 67 | ); -------------------------------------------------------------------------------- /app/config/queue.php: -------------------------------------------------------------------------------- 1 | 'sync', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Queue Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may configure the connection information for each server that 26 | | is used by your application. A default configuration has been added 27 | | for each back-end shipped with Laravel. You are free to add more. 28 | | 29 | */ 30 | 31 | 'connections' => array( 32 | 33 | 'sync' => array( 34 | 'driver' => 'sync', 35 | ), 36 | 37 | 'beanstalkd' => array( 38 | 'driver' => 'beanstalkd', 39 | 'host' => 'localhost', 40 | 'queue' => 'default', 41 | ), 42 | 43 | 'sqs' => array( 44 | 'driver' => 'sqs', 45 | 'key' => 'your-public-key', 46 | 'secret' => 'your-secret-key', 47 | 'queue' => 'your-queue-url', 48 | 'region' => 'us-east-1', 49 | ), 50 | 51 | 'iron' => array( 52 | 'driver' => 'iron', 53 | 'project' => 'your-project-id', 54 | 'token' => 'your-token', 55 | 'queue' => 'your-queue-name', 56 | ), 57 | 58 | ), 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Failed Queue Jobs 63 | |-------------------------------------------------------------------------- 64 | | 65 | | These options configure the behavior of failed queue job logging so you 66 | | can control which database and table are used to store the jobs that 67 | | have failed. You may change them to any database / table you wish. 68 | | 69 | */ 70 | 71 | 'failed' => array( 72 | 73 | 'database' => 'mysql', 'table' => 'failed_jobs', 74 | 75 | ), 76 | 77 | ); 78 | -------------------------------------------------------------------------------- /app/views/main/register.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 | @if($errors->count() > 0) 14 | 15 | @foreach($errors->all() as $error) 16 | 17 |
18 | 19 | {{ $error }} 20 |
21 | 22 | @endforeach 23 | 24 | @endif 25 | 26 | {{ Form::open(array('url' => 'register-user', 'method' => 'post', 'role' => 'form', 'class' => 'form-horizontal')) }} 27 | 28 |
29 | {{ Form::label('email', 'E-mail *', array('class' => 'col-lg-4 control-label')) }} 30 |
31 | {{ Form::text('email', '', array('class' => 'form-control', 'placeholder' => 'E-mail')) }} 32 |
33 |
34 | 35 |
36 | {{ Form::label('username', 'Username', array('class' => 'col-lg-4 control-label')) }} 37 |
38 | {{ Form::text('username', '', array('class' => 'form-control', 'placeholder' => 'Username')) }} 39 |
40 |
41 | 42 |
43 | {{ Form::label('password', 'Password *', array('class' => 'col-lg-4 control-label')) }} 44 |
45 | {{ Form::password('password', array('class' => 'form-control', 'placeholder' => 'Password')) }} 46 |
47 |
48 | 49 |
50 | {{ Form::label('password_confirmation', 'Password confirmation *', array('class' => 'col-lg-4 control-label')) }} 51 |
52 | {{ Form::password('password_confirmation', array('class' => 'form-control', 'placeholder' => 'Password confirmation')) }} 53 |
54 |
55 | 56 |
57 |
58 | 59 | 60 |
61 |
62 | 63 | {{ Form::close() }} 64 | 65 | @stop -------------------------------------------------------------------------------- /bootstrap/start.php: -------------------------------------------------------------------------------- 1 | detectEnvironment(array( 28 | 29 | 'local' => array('your-machine-name'), 30 | 31 | )); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Bind Paths 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Here we are binding the paths configured in paths.php to the app. You 39 | | should not be changing these here. If you need to change these you 40 | | may do so within the paths.php file and they will be bound here. 41 | | 42 | */ 43 | 44 | $app->bindInstallPaths(require __DIR__.'/paths.php'); 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Load The Application 49 | |-------------------------------------------------------------------------- 50 | | 51 | | Here we will load the Illuminate application. We'll keep this is in a 52 | | separate location so we can isolate the creation of an application 53 | | from the actual running of the application with a given request. 54 | | 55 | */ 56 | 57 | $framework = $app['path.base'].'/vendor/laravel/framework/src'; 58 | 59 | require $framework.'/Illuminate/Foundation/start.php'; 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Return The Application 64 | |-------------------------------------------------------------------------- 65 | | 66 | | This script returns the application instance. The instance is given to 67 | | the calling script so we can separate the building of the instances 68 | | from the actual running of the application and sending responses. 69 | | 70 | */ 71 | 72 | return $app; 73 | -------------------------------------------------------------------------------- /app/models/Users.php: -------------------------------------------------------------------------------- 1 | 'required|unique:users|email', 14 | 'username' => 'min:4', 15 | 'password' => 'required|alphanum|between:4,8|confirmed', 16 | 'password_confirmation' => 'required' 17 | ); 18 | 19 | public static $rulesLogin = array( 20 | 'email' => 'required|email', 21 | 'password' => 'required' 22 | ); 23 | 24 | public static $rulesPasswordChange = array( 25 | 'old_password' => 'required', 26 | 'new_password' => 'required|alphanum|between:4,8|confirmed', 27 | 'new_password_confirmation' => 'required' 28 | ); 29 | 30 | public static function validateRegister($data) 31 | { 32 | return Validator::make($data, static::$rulesRegister); 33 | } 34 | 35 | public static function validateLogin($data) 36 | { 37 | return Validator::make($data, static::$rulesLogin); 38 | } 39 | 40 | public static function validatePublic($data) 41 | { 42 | return Validator::make($data, 43 | array( 44 | 'name' => 'min:4', 45 | 'email' => 'required|email|unique:users,email,' . Auth::user()->id, 46 | 'url' => 'url' 47 | ) 48 | ); 49 | } 50 | 51 | public static function validatePasswordChange($data) 52 | { 53 | return Validator::make($data, static::$rulesPasswordChange); 54 | } 55 | 56 | public static function insertUser() 57 | { 58 | return Users::insert(array( 59 | 'email' => Input::get('email'), 60 | 'username' => Input::get('username'), 61 | 'password' => Hash::make(Input::get('password')), 62 | 'created_at' => date('Y-m-d H:i:s'), 63 | 'updated_at' => date('Y-m-d H:i:s') 64 | )); 65 | } 66 | 67 | public static function editUserProfile($user) 68 | { 69 | $user->name = Input::get('name'); 70 | $user->email = Input::get('email'); 71 | $user->url = Input::get('url'); 72 | 73 | return $user->save(); 74 | } 75 | 76 | public static function editUserPassword($user) 77 | { 78 | $user->password = Hash::make(Input::get('new_password')); 79 | 80 | return $user->save(); 81 | } 82 | 83 | public static function deleteUserAccount($user) 84 | { 85 | return $user->delete(); 86 | } 87 | } -------------------------------------------------------------------------------- /app/views/main/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('latest_images') 12 | 13 |
14 | 15 |
16 | 17 | @foreach($images as $image) 18 | 19 | 24 | 25 | @endforeach 26 | 27 |
28 | 29 |
30 | 31 | @stop 32 | 33 | @section('content') 34 | 35 | {{ Form::open(array('url' => 'upload', 'method' => 'post', 'id' => 'upload-image', 'enctype' => 'multipart/form-data', 'files' => true)) }} 36 | 37 |
38 |
Select images
39 |
40 | 41 | {{ Form::file('file[]', array('multiple' => 'multiple', 'id' => 'multiple-files', 'accept' => 'image/*')) }} 42 | 43 |
44 | 45 |
46 | 47 |
48 | 52 |
53 | 54 | {{ Form::submit('Upload images', array('class' => 'btn btn-success btn-lg btn-block')) }} 55 | 56 | {{ Form::reset('Reset', array('class' => 'btn btn-warning btn-block', 'id' => 'reset')) }} 57 |
58 | 59 | {{ Form::close() }} 60 | 61 |
62 | 63 | @if (Session::has('image-message')) 64 |
65 | 66 | {{ Session::get('image-message') }} 67 |
68 | @endif 69 | 70 | @if (Session::has('files')) 71 | 72 | @foreach (Session::get('files') as $file) 73 |
{{ HTML::link('show/' . $file, 'Link to your image.') }}
74 | @endforeach 75 | 76 | @endif 77 | 78 |
79 | 80 | @stop -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | setRequestForConsoleEnvironment(); 45 | 46 | $artisan = Illuminate\Console\Application::start($app); 47 | 48 | /* 49 | |-------------------------------------------------------------------------- 50 | | Run The Artisan Application 51 | |-------------------------------------------------------------------------- 52 | | 53 | | When we run the console application, the current CLI command will be 54 | | executed in this console and the response sent back to a terminal 55 | | or another output device for the developers. Here goes nothing! 56 | | 57 | */ 58 | 59 | $status = $artisan->run(); 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Shutdown The Application 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Once Artisan has finished running. We will fire off the shutdown events 67 | | so that any final work may be done by the application before we shut 68 | | down the process. This is the last thing to happen to the request. 69 | | 70 | */ 71 | 72 | $app->shutdown(); 73 | 74 | exit($status); -------------------------------------------------------------------------------- /bootstrap/autoload.php: -------------------------------------------------------------------------------- 1 | with('status', 'alert-danger') 41 | ->with('message', 'Musisz być zalogowany, aby przeglądać tę stronę.'); 42 | }); 43 | 44 | 45 | Route::filter('auth.basic', function() 46 | { 47 | return Auth::basic(); 48 | }); 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | Guest Filter 53 | |-------------------------------------------------------------------------- 54 | | 55 | | The "guest" filter is the counterpart of the authentication filters as 56 | | it simply checks that the current user is not logged in. A redirect 57 | | response will be issued if they are, which you may freely change. 58 | | 59 | */ 60 | 61 | Route::filter('guest', function() 62 | { 63 | if (Auth::check()) return Redirect::to('/'); 64 | }); 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | CSRF Protection Filter 69 | |-------------------------------------------------------------------------- 70 | | 71 | | The CSRF filter is responsible for protecting your application against 72 | | cross-site request forgery attacks. If this special token in a user 73 | | session does not match the one given in this request, we'll bail. 74 | | 75 | */ 76 | 77 | Route::filter('csrf', function() 78 | { 79 | if (Session::token() != Input::get('_token')) 80 | { 81 | throw new Illuminate\Session\TokenMismatchException; 82 | } 83 | }); 84 | 85 | /** 86 | * Composer views 87 | */ 88 | 89 | View::composer('layouts.master', function($view) 90 | { 91 | if (! Auth::guest()) { 92 | $view->with('notifications', Votes::where('user_id', '<>', Auth::user()->id) 93 | ->where('notification', 1) 94 | ->get()); 95 | } 96 | }); -------------------------------------------------------------------------------- /app/routes.php: -------------------------------------------------------------------------------- 1 | 'main', 15 | 'uses' => 'HomeController@index' 16 | )); 17 | 18 | /* Images */ 19 | Route::get('images', array( 20 | 'as' => 'images', 21 | 'uses' => 'ImageController@index' 22 | )); 23 | 24 | Route::post('upload', array( 25 | 'uses' => 'ImageController@upload' 26 | )); 27 | 28 | Route::get('show/{id}', array( 29 | 'as' => 'show_image', 30 | 'uses' => 'ImageController@show' 31 | )); 32 | 33 | Route::delete('user/image/destroy/{id}', array( 34 | 'uses' => 'ImageController@destroy' 35 | ))->before('auth'); 36 | 37 | /* Login */ 38 | Route::post('login', array( 39 | 'uses' => 'UserController@login' 40 | )); 41 | 42 | Route::get('logout', array( 43 | 'as' => 'logout', 44 | 'uses' => 'UserController@logout' 45 | ))->before('auth'); 46 | 47 | /* Register */ 48 | Route::get('register', array( 49 | 'as' => 'show_register', 50 | 'uses' => 'UserController@showRegister' 51 | ))->before('guest'); 52 | 53 | Route::post('register-user', array( 54 | 'uses' => 'UserController@register' 55 | ))->before('guest'); 56 | 57 | /* User acc */ 58 | Route::get('user/images', array( 59 | 'as' => 'images_user', 60 | 'uses' => 'UserController@showImages' 61 | ))->before('auth'); 62 | 63 | Route::get('user/profile', array( 64 | 'as' => 'profile_user', 65 | 'uses' => 'UserController@showProfile' 66 | ))->before('auth'); 67 | 68 | Route::post('user/profile/edit', array( 69 | 'uses' => 'UserController@editProfile' 70 | ))->before('auth'); 71 | 72 | Route::get('user/account', array( 73 | 'as' => 'account_user', 74 | 'uses' => 'UserController@showAccount' 75 | ))->before('auth'); 76 | 77 | Route::post('user/account/edit', array( 78 | 'uses' => 'UserController@editAccount' 79 | ))->before('auth'); 80 | 81 | Route::post('user/account/delete', array( 82 | 'uses' => 'UserController@deleteAccount' 83 | ))->before('auth'); 84 | 85 | Route::get('user/notification/latest', array( 86 | 'as' => 'notification_user', 87 | 'uses' => 'UserController@showNotification' 88 | ))->before('auth'); 89 | 90 | Route::post('user/notification/read', array( 91 | 'uses' => 'VoteController@markNotification' 92 | ))->before('auth'); 93 | 94 | Route::get('user/notification/history', array( 95 | 'as' => 'notification_history', 96 | 'uses' => 'UserController@showNotificationHistory' 97 | ))->before('auth'); 98 | 99 | /* Voting */ 100 | Route::get('vote/{image_id}/{vote}', array( 101 | 'uses' => 'VoteController@vote' 102 | ))->where('vote', '[0-1]'); 103 | -------------------------------------------------------------------------------- /app/config/cache.php: -------------------------------------------------------------------------------- 1 | 'file', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | File Cache Location 23 | |-------------------------------------------------------------------------- 24 | | 25 | | When using the "file" cache driver, we need a location where the cache 26 | | files may be stored. A sensible default has been specified, but you 27 | | are free to change it to any other place on disk that you desire. 28 | | 29 | */ 30 | 31 | 'path' => storage_path().'/cache', 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Database Cache Connection 36 | |-------------------------------------------------------------------------- 37 | | 38 | | When using the "database" cache driver you may specify the connection 39 | | that should be used to store the cached items. When this option is 40 | | null the default database connection will be utilized for cache. 41 | | 42 | */ 43 | 44 | 'connection' => null, 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Database Cache Table 49 | |-------------------------------------------------------------------------- 50 | | 51 | | When using the "database" cache driver we need to know the table that 52 | | should be used to store the cached items. A default table name has 53 | | been provided but you're free to change it however you deem fit. 54 | | 55 | */ 56 | 57 | 'table' => 'cache', 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | Memcached Servers 62 | |-------------------------------------------------------------------------- 63 | | 64 | | Now you may specify an array of your Memcached servers that should be 65 | | used when utilizing the Memcached cache driver. All of the servers 66 | | should contain a value for "host", "port", and "weight" options. 67 | | 68 | */ 69 | 70 | 'memcached' => array( 71 | 72 | array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), 73 | 74 | ), 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | Cache Key Prefix 79 | |-------------------------------------------------------------------------- 80 | | 81 | | When utilizing a RAM based store such as APC or Memcached, there might 82 | | be other applications utilizing the same cache. So, we'll specify a 83 | | value to get prefixed to all our keys so we can avoid collisions. 84 | | 85 | */ 86 | 87 | 'prefix' => 'laravel', 88 | 89 | ); 90 | -------------------------------------------------------------------------------- /app/views/user/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 |
16 | 17 | 21 | 22 |
23 | 24 |
25 | 26 | @foreach($images as $image) 27 | 28 | @if ($image->private == 0) 29 |
30 | 31 | {{ HTML::image('uploads/' . $image->id . '/' . $image->img_min, $image->img_min, array('class' => 'img-responsive img-thumbnail', 'style' => 'margin-bottom: 0;')) }} 32 | 33 | {{ Form::open(array('url' => 'user/image/destroy/' . $image->id, 'method' => 'delete')) }} 34 | {{ Form::submit('Delete', array('class' => 'delete-button btn btn-danger btn-sm btn-block', 'style' => 'margin: 10px 0')) }} 35 | {{ Form::close() }} 36 |
37 | @endif 38 | 39 | @endforeach 40 | 41 |
42 | 43 |
44 | 45 | @foreach($images as $image) 46 | 47 | @if ($image->private == 1) 48 |
49 | 50 | {{ HTML::image('uploads/' . $image->id . '/' . $image->img_min, $image->img_min, array('class' => 'img-responsive img-thumbnail', 'style' => 'margin-bottom: 0;')) }} 51 | 52 | {{ Form::open(array('url' => 'user/image/destroy/' . $image->id, 'method' => 'delete')) }} 53 | {{ Form::submit('Delete', array('class' => 'delete-button btn btn-danger btn-sm btn-block', 'style' => 'margin: 10px 0')) }} 54 | {{ Form::close() }} 55 |
56 | @endif 57 | 58 | @endforeach 59 | 60 |
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 | @stop 69 | 70 | @section('add_script') 71 | 82 | @stop -------------------------------------------------------------------------------- /app/views/user/settings/notification_history.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 |
16 |
17 | {{ HTML::linkRoute('profile_user', 'Your profile', array(), array('class' => 'list-group-item')) }} 18 | {{ HTML::linkRoute('account_user', 'Account settings', array(), array('class' => 'list-group-item')) }} 19 | {{ HTML::linkRoute('notification_user', 'Latest notifications', array(), array('class' => 'list-group-item')) }} 20 | {{ HTML::linkRoute('notification_history', 'Notification history', array(), array('class' => 'list-group-item active')) }} 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 |
Notification history
29 | 30 |
31 | 32 |
There are no notifications to show.
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {{-- */$i=1;/* --}} 49 | 50 | @foreach ($notifications as $notification) 51 | 52 | 53 | @if ($notification->images->user_id === Auth::user()->id) 54 | 55 | 56 | 57 | 60 | @endif 61 | 62 | 63 | @endforeach 64 | 65 | 66 | 67 |
IDUserNotificationAction
{{ $i++ }}{{ $notification->users->username }}User voted on your image. 58 | Show image 59 |
68 | 69 |
70 | 71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 | 79 |
{{ $i }}
80 | 81 | @stop 82 | 83 | @section('add_script') 84 | 96 | @stop -------------------------------------------------------------------------------- /app/views/images/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 | 14 | {{ HTML::image('uploads/' . $image->id . '/' . $image->img_big, $image->img_big, array('class' => 'img-responsive img-thumbnail', 'style' => 'display: block; margin: 0 auto;')) }} 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 | @if ($votes['auth']) 30 | 31 | @else 32 | 33 | @endif 34 | 35 | 36 | {{ $user_image }} 37 | 38 | 39 | 40 | {{ date_format($image->created_at, 'd-m-Y') }} 41 | 42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 | 62 | @stop 63 | 64 | @section('add_script') 65 | {{ HTML::script('assets/js/vote.ajax.js') }} 66 | 77 | @stop -------------------------------------------------------------------------------- /app/views/user/settings/profile.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 |
16 |
17 | {{ HTML::linkRoute('profile_user', 'Your profile', array(), array('class' => 'list-group-item active')) }} 18 | {{ HTML::linkRoute('account_user', 'Account settings', array(), array('class' => 'list-group-item')) }} 19 | {{ HTML::linkRoute('notification_user', 'Latest notifications', array(), array('class' => 'list-group-item')) }} 20 | {{ HTML::linkRoute('notification_history', 'Notification history', array(), array('class' => 'list-group-item')) }} 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 |
Your public profile data
29 | 30 |
31 | 32 | @if($errors->count() > 0) 33 | 34 | @foreach($errors->all() as $error) 35 | 36 |
37 | 38 | {{ $error }} 39 |
40 | 41 | @endforeach 42 | 43 | @endif 44 | 45 | {{ Form::open(array('url' => 'user/profile/edit', 'method' => 'post', 'role' => 'form', 'class' => 'form-horizontal')) }} 46 | 47 |
48 | {{ Form::label('name', 'Name', array('class' => 'col-lg-2 control-label')) }} 49 |
50 | {{ Form::text('name', '', array('class' => 'form-control', 'placeholder' => Auth::user()->name)) }} 51 |
52 |
53 | 54 |
55 | {{ Form::label('email', 'Public e-mail', array('class' => 'col-lg-2 control-label')) }} 56 |
57 | {{ Form::email('email', '', array('class' => 'form-control', 'placeholder' => Auth::user()->email)) }} 58 |
59 |
60 | 61 |
62 | {{ Form::label('url', 'URL', array('class' => 'col-lg-2 control-label')) }} 63 |
64 | {{ Form::text('url', '', array('class' => 'form-control', 'placeholder' => Auth::user()->url)) }} 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
73 | 74 | {{ Form::close() }} 75 | 76 |
77 | 78 |
79 | 80 |
81 | 82 |
83 | 84 | @stop 85 | 86 | @section('add_script') 87 | 95 | @stop -------------------------------------------------------------------------------- /app/config/database.php: -------------------------------------------------------------------------------- 1 | PDO::FETCH_CLASS, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Database Connection Name 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may specify which of the database connections below you wish 24 | | to use as your default connection for all database work. Of course 25 | | you may use many connections at once using the Database library. 26 | | 27 | */ 28 | 29 | 'default' => 'mysql', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Database Connections 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here are each of the database connections setup for your application. 37 | | Of course, examples of configuring each database platform that is 38 | | supported by Laravel is shown below to make development simple. 39 | | 40 | | 41 | | All database work in Laravel is done through the PHP PDO facilities 42 | | so make sure you have the driver for your particular database of 43 | | choice installed on your machine before you begin development. 44 | | 45 | */ 46 | 47 | 'connections' => array( 48 | 49 | 'sqlite' => array( 50 | 'driver' => 'sqlite', 51 | 'database' => __DIR__.'/../database/production.sqlite', 52 | 'prefix' => '', 53 | ), 54 | 55 | 'mysql' => array( 56 | 'driver' => 'mysql', 57 | 'host' => 'localhost', 58 | 'database' => 'laravel-img', 59 | 'username' => 'root', 60 | 'password' => '', 61 | 'charset' => 'utf8', 62 | 'collation' => 'utf8_unicode_ci', 63 | 'prefix' => '', 64 | ), 65 | 66 | 'pgsql' => array( 67 | 'driver' => 'pgsql', 68 | 'host' => 'localhost', 69 | 'database' => 'database', 70 | 'username' => 'root', 71 | 'password' => '', 72 | 'charset' => 'utf8', 73 | 'prefix' => '', 74 | 'schema' => 'public', 75 | ), 76 | 77 | 'sqlsrv' => array( 78 | 'driver' => 'sqlsrv', 79 | 'host' => 'localhost', 80 | 'database' => 'database', 81 | 'username' => 'root', 82 | 'password' => '', 83 | 'prefix' => '', 84 | ), 85 | 86 | ), 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Migration Repository Table 91 | |-------------------------------------------------------------------------- 92 | | 93 | | This table keeps track of all the migrations that have already run for 94 | | your application. Using this information, we can determine which of 95 | | the migrations on disk have not actually be run in the databases. 96 | | 97 | */ 98 | 99 | 'migrations' => 'migrations', 100 | 101 | /* 102 | |-------------------------------------------------------------------------- 103 | | Redis Databases 104 | |-------------------------------------------------------------------------- 105 | | 106 | | Redis is an open source, fast, and advanced key-value store that also 107 | | provides a richer set of commands than a typical key-value systems 108 | | such as APC or Memcached. Laravel makes it easy to dig right in. 109 | | 110 | */ 111 | 112 | 'redis' => array( 113 | 114 | 'cluster' => true, 115 | 116 | 'default' => array( 117 | 'host' => '127.0.0.1', 118 | 'port' => 6379, 119 | 'database' => 0, 120 | ), 121 | 122 | ), 123 | 124 | ); 125 | -------------------------------------------------------------------------------- /app/views/user/settings/notification.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 |
16 |
17 | {{ HTML::linkRoute('profile_user', 'Your profile', array(), array('class' => 'list-group-item')) }} 18 | {{ HTML::linkRoute('account_user', 'Account settings', array(), array('class' => 'list-group-item')) }} 19 | {{ HTML::linkRoute('notification_user', 'Latest notifications', array(), array('class' => 'list-group-item active')) }} 20 | {{ HTML::linkRoute('notification_history', 'Notification history', array(), array('class' => 'list-group-item')) }} 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 |
Latest notifications
29 | 30 |
31 | 32 |
There are no notifications to show.
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {{-- */$i=1;/* --}} 49 | 50 | @foreach ($notifications as $notification) 51 | 52 | 53 | @if ($notification->images->user_id === Auth::user()->id) 54 | 55 | 56 | 57 | 65 | @endif 66 | 67 | 68 | @endforeach 69 | 70 | 71 | 72 |
IDUserNotificationAction
{{ $i++ }}{{ $notification->users->username }}User voted on your image. 58 | {{ Form::open(array('url' => 'user/notification/read', 'method' => 'post', 'role' => 'form')) }} 59 | Show image 60 | {{ Form::hidden('id', $value = $notification->id) }} 61 | {{ Form::hidden('user_id', $value = $notification->images->user_id) }} 62 | 63 | {{ Form::close() }} 64 |
73 | 74 |
75 | 76 |
77 | 78 |
79 | 80 |
81 | 82 |
83 | 84 |
{{ $i }}
85 | 86 | @stop 87 | 88 | @section('add_script') 89 | 101 | @stop -------------------------------------------------------------------------------- /app/config/mail.php: -------------------------------------------------------------------------------- 1 | 'smtp', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | SMTP Host Address 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may provide the host address of the SMTP server used by your 26 | | applications. A default option is provided that is compatible with 27 | | the Postmark mail service, which will provide reliable delivery. 28 | | 29 | */ 30 | 31 | 'host' => 'smtp.mailgun.org', 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | SMTP Host Port 36 | |-------------------------------------------------------------------------- 37 | | 38 | | This is the SMTP port used by your application to delivery e-mails to 39 | | users of your application. Like the host we have set this value to 40 | | stay compatible with the Postmark e-mail application by default. 41 | | 42 | */ 43 | 44 | 'port' => 587, 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Global "From" Address 49 | |-------------------------------------------------------------------------- 50 | | 51 | | You may wish for all e-mails sent by your application to be sent from 52 | | the same address. Here, you may specify a name and address that is 53 | | used globally for all e-mails that are sent by your application. 54 | | 55 | */ 56 | 57 | 'from' => array('address' => null, 'name' => null), 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | E-Mail Encryption Protocol 62 | |-------------------------------------------------------------------------- 63 | | 64 | | Here you may specify the encryption protocol that should be used when 65 | | the application send e-mail messages. A sensible default using the 66 | | transport layer security protocol should provide great security. 67 | | 68 | */ 69 | 70 | 'encryption' => 'tls', 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | SMTP Server Username 75 | |-------------------------------------------------------------------------- 76 | | 77 | | If your SMTP server requires a username for authentication, you should 78 | | set it here. This will get used to authenticate with your server on 79 | | connection. You may also set the "password" value below this one. 80 | | 81 | */ 82 | 83 | 'username' => null, 84 | 85 | /* 86 | |-------------------------------------------------------------------------- 87 | | SMTP Server Password 88 | |-------------------------------------------------------------------------- 89 | | 90 | | Here you may set the password required by your SMTP server to send out 91 | | messages from your application. This will be given to the server on 92 | | connection so that the application will be able to send messages. 93 | | 94 | */ 95 | 96 | 'password' => null, 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | Sendmail System Path 101 | |-------------------------------------------------------------------------- 102 | | 103 | | When using the "sendmail" driver to send e-mails, we will need to know 104 | | the path to where Sendmail lives on this server. A default path has 105 | | been provided here, which will work well on most of your systems. 106 | | 107 | */ 108 | 109 | 'sendmail' => '/usr/sbin/sendmail -bs', 110 | 111 | /* 112 | |-------------------------------------------------------------------------- 113 | | Mail "Pretend" 114 | |-------------------------------------------------------------------------- 115 | | 116 | | When this option is enabled, e-mail will not actually be sent over the 117 | | web and will instead be written to your application's logs files so 118 | | you may inspect the message. This is great for local development. 119 | | 120 | */ 121 | 122 | 'pretend' => false, 123 | 124 | ); -------------------------------------------------------------------------------- /app/config/session.php: -------------------------------------------------------------------------------- 1 | 'native', 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Session Lifetime 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may specify the number of minutes that you wish the session 27 | | to be allowed to remain idle for it is expired. If you want them 28 | | to immediately expire when the browser closes, set it to zero. 29 | | 30 | */ 31 | 32 | 'lifetime' => 120, 33 | 34 | 'expire_on_close' => false, 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | Session File Location 39 | |-------------------------------------------------------------------------- 40 | | 41 | | When using the native session driver, we need a location where session 42 | | files may be stored. A default has been set for you but a different 43 | | location may be specified. This is only needed for file sessions. 44 | | 45 | */ 46 | 47 | 'files' => storage_path().'/sessions', 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Session Database Connection 52 | |-------------------------------------------------------------------------- 53 | | 54 | | When using the "database" session driver, you may specify the database 55 | | connection that should be used to manage your sessions. This should 56 | | correspond to a connection in your "database" configuration file. 57 | | 58 | */ 59 | 60 | 'connection' => null, 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Session Database Table 65 | |-------------------------------------------------------------------------- 66 | | 67 | | When using the "database" session driver, you may specify the table we 68 | | should use to manage the sessions. Of course, a sensible default is 69 | | provided for you; however, you are free to change this as needed. 70 | | 71 | */ 72 | 73 | 'table' => 'sessions', 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Session Sweeping Lottery 78 | |-------------------------------------------------------------------------- 79 | | 80 | | Some session drivers must manually sweep their storage location to get 81 | | rid of old sessions from storage. Here are the chances that it will 82 | | happen on a given request. By default, the odds are 2 out of 100. 83 | | 84 | */ 85 | 86 | 'lottery' => array(2, 100), 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Session Cookie Name 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Here you may change the name of the cookie used to identify a session 94 | | instance by ID. The name specified here will get used every time a 95 | | new session cookie is created by the framework for every driver. 96 | | 97 | */ 98 | 99 | 'cookie' => 'laravel_session', 100 | 101 | /* 102 | |-------------------------------------------------------------------------- 103 | | Session Cookie Path 104 | |-------------------------------------------------------------------------- 105 | | 106 | | The session cookie path determines the path for which the cookie will 107 | | be regarded as available. Typically, this will be the root path of 108 | | your application but you are free to change this when necessary. 109 | | 110 | */ 111 | 112 | 'path' => '/', 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Session Cookie Domain 117 | |-------------------------------------------------------------------------- 118 | | 119 | | Here you may change the domain of the cookie used to identify a session 120 | | in your application. This will determine which domains the cookie is 121 | | available to in your application. A sensible default has been set. 122 | | 123 | */ 124 | 125 | 'domain' => null, 126 | 127 | ); 128 | -------------------------------------------------------------------------------- /app/lang/en/validation.php: -------------------------------------------------------------------------------- 1 | "The :attribute must be accepted.", 17 | "active_url" => "The :attribute is not a valid URL.", 18 | "after" => "The :attribute must be a date after :date.", 19 | "alpha" => "The :attribute may only contain letters.", 20 | "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", 21 | "alpha_num" => "The :attribute may only contain letters and numbers.", 22 | "array" => "The :attribute must be an array.", 23 | "before" => "The :attribute must be a date before :date.", 24 | "between" => array( 25 | "numeric" => "The :attribute must be between :min - :max.", 26 | "file" => "The :attribute must be between :min - :max kilobytes.", 27 | "string" => "The :attribute must be between :min - :max characters.", 28 | "array" => "The :attribute must have between :min - :max items.", 29 | ), 30 | "confirmed" => "The :attribute confirmation does not match.", 31 | "date" => "The :attribute is not a valid date.", 32 | "date_format" => "The :attribute does not match the format :format.", 33 | "different" => "The :attribute and :other must be different.", 34 | "digits" => "The :attribute must be :digits digits.", 35 | "digits_between" => "The :attribute must be between :min and :max digits.", 36 | "email" => "The :attribute format is invalid.", 37 | "exists" => "The selected :attribute is invalid.", 38 | "image" => "The :attribute must be an image.", 39 | "in" => "The selected :attribute is invalid.", 40 | "integer" => "The :attribute must be an integer.", 41 | "ip" => "The :attribute must be a valid IP address.", 42 | "max" => array( 43 | "numeric" => "The :attribute may not be greater than :max.", 44 | "file" => "The :attribute may not be greater than :max kilobytes.", 45 | "string" => "The :attribute may not be greater than :max characters.", 46 | "array" => "The :attribute may not have more than :max items.", 47 | ), 48 | "mimes" => "The :attribute must be a file of type: :values.", 49 | "min" => array( 50 | "numeric" => "The :attribute must be at least :min.", 51 | "file" => "The :attribute must be at least :min kilobytes.", 52 | "string" => "The :attribute must be at least :min characters.", 53 | "array" => "The :attribute must have at least :min items.", 54 | ), 55 | "not_in" => "The selected :attribute is invalid.", 56 | "numeric" => "The :attribute must be a number.", 57 | "regex" => "The :attribute format is invalid.", 58 | "required" => "The :attribute field is required.", 59 | "required_if" => "The :attribute field is required when :other is :value.", 60 | "required_with" => "The :attribute field is required when :values is present.", 61 | "required_without" => "The :attribute field is required when :values is not present.", 62 | "same" => "The :attribute and :other must match.", 63 | "size" => array( 64 | "numeric" => "The :attribute must be :size.", 65 | "file" => "The :attribute must be :size kilobytes.", 66 | "string" => "The :attribute must be :size characters.", 67 | "array" => "The :attribute must contain :size items.", 68 | ), 69 | "unique" => "The :attribute has already been taken.", 70 | "url" => "The :attribute format is invalid.", 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Custom Validation Language Lines 75 | |-------------------------------------------------------------------------- 76 | | 77 | | Here you may specify custom validation messages for attributes using the 78 | | convention "attribute.rule" to name the lines. This makes it quick to 79 | | specify a specific custom language line for a given attribute rule. 80 | | 81 | */ 82 | 83 | 'custom' => array(), 84 | 85 | /* 86 | |-------------------------------------------------------------------------- 87 | | Custom Validation Attributes 88 | |-------------------------------------------------------------------------- 89 | | 90 | | The following language lines are used to swap attribute place-holders 91 | | with something more reader friendly such as E-Mail Address instead 92 | | of "email". This simply helps us make messages a little cleaner. 93 | | 94 | */ 95 | 96 | 'attributes' => array(), 97 | 98 | ); 99 | -------------------------------------------------------------------------------- /app/views/user/settings/account.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('main_menu') 4 | @parent 5 | @stop 6 | 7 | @section('login') 8 | @parent 9 | @stop 10 | 11 | @section('content') 12 | 13 |
14 | 15 |
16 |
17 | {{ HTML::linkRoute('profile_user', 'Your profile', array(), array('class' => 'list-group-item')) }} 18 | {{ HTML::linkRoute('account_user', 'Account settings', array(), array('class' => 'list-group-item active')) }} 19 | {{ HTML::linkRoute('notification_user', 'Latest notifications', array(), array('class' => 'list-group-item')) }} 20 | {{ HTML::linkRoute('notification_history', 'Notification history', array(), array('class' => 'list-group-item')) }} 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 |
Change account password
29 | 30 |
31 | 32 | @if($errors->count() > 0) 33 | 34 | @foreach($errors->all() as $error) 35 | 36 |
37 | 38 | {{ $error }} 39 |
40 | 41 | @endforeach 42 | 43 | @endif 44 | 45 | {{ Form::open(array('url' => 'user/account/edit', 'method' => 'post', 'role' => 'form', 'class' => 'form-horizontal')) }} 46 | 47 |
48 | {{ Form::label('old_password', 'Current password', array('class' => 'col-lg-3 control-label')) }} 49 |
50 | {{ Form::password('old_password', array('class' => 'form-control')) }} 51 |
52 |
53 | 54 |
55 | {{ Form::label('new_password', 'New password', array('class' => 'col-lg-3 control-label')) }} 56 |
57 | {{ Form::password('new_password', array('class' => 'form-control')) }} 58 |
59 |
60 | 61 |
62 | {{ Form::label('new_password_confirmation', 'Confirm password', array('class' => 'col-lg-3 control-label')) }} 63 |
64 | {{ Form::password('new_password_confirmation', array('class' => 'form-control')) }} 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
73 | 74 | {{ Form::close() }} 75 | 76 |
77 | 78 |
79 | 80 |
81 | 82 |
Delete account
83 | 84 |
85 | 86 | {{ Form::open(array('url' => 'user/account/delete', 'method' => 'post', 'role' => 'form', 'class' => 'form-horizontal')) }} 87 | 88 |
89 | {{ Form::label('password', 'Password', array('class' => 'col-lg-3 control-label')) }} 90 |
91 | {{ Form::password('password', array('class' => 'form-control', 'id' => 'delete-password')) }} 92 |
93 |
94 | 95 |
96 |
97 | 98 |
99 |
100 | 101 | {{ Form::close() }} 102 | 103 |
104 | 105 |
106 | 107 |
108 | 109 |
110 | 111 | @stop 112 | 113 | @section('add_script') 114 | 127 | @stop -------------------------------------------------------------------------------- /app/controllers/ImageController.php: -------------------------------------------------------------------------------- 1 | with('title', 'List of images') 15 | ->with('images', Images::orderBy('created_at', 'desc') 16 | ->where('private', 0) 17 | ->paginate('42')); 18 | } 19 | 20 | /** 21 | * Show image by id. Show good votes and bad votes (with percent bar). 22 | * Additional information about image - showing user. 23 | * 24 | * @param int $id 25 | * @return object View 26 | */ 27 | public function show($id) 28 | { 29 | // Showing goodvotes and badvotes 30 | $goodVotes = $this->vote($id, 1); 31 | $badVotes = $this->vote($id, 0); 32 | 33 | // If image doesn't have votes, set 100% to good votes percent 34 | // Else calculate percent of good votes 35 | // And substract good votes percent from 100% 36 | if ($goodVotes == false && $badVotes == false) { 37 | $goodVotesPercent = 100; 38 | $badVotesPercent = 0; 39 | } else { 40 | $goodVotesPercent = ($goodVotes / ($goodVotes + $badVotes)) * 100; 41 | $badVotesPercent = 100 - $goodVotesPercent; 42 | } 43 | 44 | return View::make('images/show') 45 | ->with('title', 'Your images') 46 | ->with('image', Images::findOrFail($id)) 47 | ->with('user_image', $this->showUser($id)) 48 | ->with('votes', array( 49 | 'auth' => $this->checkAuth($id), 50 | 'good_votes' => $goodVotes, 51 | 'bad_votes' => $badVotes, 52 | 'good_percent' => $goodVotesPercent, 53 | 'bad_percent' => $badVotesPercent 54 | )); 55 | } 56 | 57 | /** 58 | * Counting current image votes. 59 | * 60 | * @param int $id 61 | * @param bool $type 62 | * @return int | bool 63 | */ 64 | private function vote($id, $type) 65 | { 66 | $votes = Votes::where('image_id', $id)->where('vote', $type)->count(); 67 | if ($votes !== 0) { 68 | return $votes; 69 | } else { 70 | return false; 71 | } 72 | } 73 | 74 | /** 75 | * Showing author of current image. 76 | * 77 | * @param int $id 78 | * @return string 79 | */ 80 | private function showUser($id) 81 | { 82 | $image = Images::findOrFail($id); 83 | 84 | // If current user is not anonymous (id of 0, or deleted user) 85 | // Show author username 86 | // Else return anonymous 87 | if (! ($image->user_id == 0 || empty($image->users->name))) { 88 | return $image->users->name; 89 | } else { 90 | return 'anonymous'; 91 | } 92 | } 93 | 94 | /** 95 | * Checking user authentication. 96 | * 97 | * @param int $id 98 | * @return int | bool 99 | */ 100 | private function checkAuth($id) 101 | { 102 | if (! Auth::guest()) { 103 | return Votes::where('image_id', $id)->where('user_id', Auth::user()->id)->count(); 104 | } else { 105 | return false; 106 | } 107 | } 108 | 109 | /** 110 | * Uploading multiple images method. 111 | * 112 | * @return object Redirect 113 | */ 114 | public function upload() 115 | { 116 | $files = Input::file('file'); 117 | $serializedFile = array(); 118 | 119 | foreach ($files as $file) { 120 | // Validate files from input file 121 | $validation = Images::validateImage(array('file'=> $file)); 122 | 123 | if (! $validation->fails()) { 124 | 125 | // If validation pass, get filename and extension 126 | // Generate random (12 characters) string 127 | // And specify a folder name of uploaded image 128 | $fileName = $file->getClientOriginalName(); 129 | $extension = $file->getClientOriginalExtension(); 130 | $folderName = str_random(12); 131 | $destinationPath = 'uploads/' . $folderName; 132 | 133 | // Move file to generated folder 134 | $file->move($destinationPath, $fileName); 135 | 136 | // Crop image (possible by Intervention Image Class) 137 | // And save as miniature 138 | Image::make($destinationPath . '/' . $fileName)->crop(250, 250, 10, 10)->save($destinationPath . '/min_' . $fileName); 139 | 140 | // Insert image information to database 141 | Images::insertImage($folderName, $fileName); 142 | } else { 143 | return Redirect::route('main') 144 | ->with('status', 'alert-danger') 145 | ->with('image-message', 'There is a problem uploading your image!'); 146 | } 147 | 148 | $serializedFile[] = $folderName; 149 | } 150 | 151 | return Redirect::route('main') 152 | ->with('status', 'alert-success') 153 | ->with('files', $serializedFile) 154 | ->with('image-message', 'Congratulations! Your photo(s) has been added'); 155 | } 156 | 157 | /** 158 | * Delete specified image from database. 159 | * 160 | * @param int $id 161 | * @return object Redirect 162 | */ 163 | public function destroy($id) 164 | { 165 | Images::find($id)->delete(); 166 | 167 | return Redirect::route('images_user') 168 | ->with('status', 'alert-success') 169 | ->with('message', 'Image removed properly.'); 170 | } 171 | 172 | } -------------------------------------------------------------------------------- /app/views/layouts/master.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Img-upload - {{ $title }} - rkosinski.pl 5 | 6 | 7 | {{ HTML::style('assets/css/bootstrap.min.css') }} 8 | {{ HTML::style('assets/css/custom.css') }} 9 | 10 | 11 | 15 | 16 | 17 | 18 | 86 | 87 |
88 | 89 |
90 | 91 | @if(! Auth::check()) 92 |
93 | @else 94 |
95 | @endif 96 | 97 | @if (Session::has('message')) 98 |
99 | 100 | {{ Session::get('message') }} 101 |
102 | @endif 103 | 104 | @yield('content') 105 | 106 |
107 | 108 | @if(! Auth::check()) 109 | 110 |
111 | 112 |
113 |
114 |

Login panel

115 |
116 |
117 | 118 | {{ Form::open(array('url' => 'login', 'method' => 'post', 'class' => '', 'id' => 'login-form')) }} 119 | 120 |
121 | 122 |
123 |
124 | 125 | {{ Form::text('email', '', array('class' => 'form-control', 'placeholder' => 'E-mail', 'type' => 'email', 'required')) }} 126 |
127 |
128 | 129 |
130 | 131 | {{ Form::password('password', array('class' => 'form-control', 'placeholder' => 'Password', 'required')) }} 132 |
133 | 134 | 135 | 136 | {{ HTML::linkRoute('show_register', 'Register', array(), array('class' => 'btn btn-warning'))}} 137 | 138 | {{ Form::close() }} 139 | 140 |
141 |
142 | 143 |
144 | 145 | @endif 146 | 147 |
148 | 149 |
150 | 151 |
152 | 153 | @section('latest_images') 154 | @show 155 |
156 |
157 |

Copyright by Radosław Kosiński {{ date('Y') }}. Visit {{ HTML::link('http://rkosinski.pl/', 'rkosinski.pl') }} for more.

158 |
159 | 160 | 161 | {{ HTML::script('http://code.jquery.com/jquery.js') }} 162 | 163 | {{ HTML::script('assets/js/bootstrap.min.js') }} 164 | 165 | {{ HTML::script('assets/js/login.ajax.js') }} 166 | 167 | {{ HTML::script('assets/js/img.send.js') }} 168 | 169 | 176 | @section('add_script') 177 | @show 178 | 179 | 180 | -------------------------------------------------------------------------------- /app/controllers/UserController.php: -------------------------------------------------------------------------------- 1 | with('title', 'List of images') 14 | ->with('images', Images::orderBy('created_at', 'desc') 15 | ->where('user_id', Auth::user()->id) 16 | ->get()); 17 | } 18 | 19 | /** 20 | * Login user method. Checking validation, inputs etc. 21 | * 22 | * @return json Response array with messages, and success information 23 | */ 24 | public function login() 25 | { 26 | $inputs = array( 27 | 'email' => Input::get('email'), 28 | 'password' => Input::get('password') 29 | ); 30 | 31 | $validation = Users::validateLogin(Input::all()); 32 | 33 | if (! $validation->fails()) { 34 | if (Auth::attempt($inputs, true)) { 35 | return Response::json(array('success' => true, 'redirect' => URL::to('/'))); 36 | } else { 37 | return Response::json(array('success' => false, 'message' => 'Incorrect data.')); 38 | } 39 | } else { 40 | return Response::json(array('success' => false, 'message' => 'Incorrect data.')); 41 | } 42 | } 43 | 44 | /** 45 | * Logout current auth user. 46 | * 47 | * @return object Redirect to main with success message 48 | */ 49 | public function logout() 50 | { 51 | Auth::logout(); 52 | 53 | return Redirect::route('main') 54 | ->with('status', 'alert-success') 55 | ->with('message', 'You have been properly logged out.'); 56 | } 57 | 58 | /** 59 | * Showing registration form. 60 | * 61 | * @return object View with register form 62 | */ 63 | public function showRegister() 64 | { 65 | return View::make('main/register') 66 | ->with('title', 'Registration'); 67 | } 68 | 69 | /** 70 | * Register new user. Validating inputs etc. 71 | * 72 | * @return object Redirect with message (success or validation errors) 73 | */ 74 | public function register() 75 | { 76 | $validation = Users::validateRegister(Input::all()); 77 | 78 | if (! $validation->fails()) { 79 | Users::insertUser(); 80 | 81 | return Redirect::route('show_register') 82 | ->with('status', 'alert-success') 83 | ->with('message', 'You have been correctly registered.'); 84 | } else { 85 | return Redirect::route('show_register') 86 | ->withErrors($validation); 87 | } 88 | } 89 | 90 | /** 91 | * Showing form with auth profile data. 92 | * 93 | * @return object View 94 | */ 95 | public function showProfile() 96 | { 97 | return View::make('user/settings/profile') 98 | ->with('title', 'Your profile'); 99 | } 100 | 101 | /** 102 | * Edit profile data. Validate inputs etc. 103 | * 104 | * @return object Redirect with message (success or validation errors) 105 | */ 106 | public function editProfile() 107 | { 108 | $validation = Users::validatePublic(Input::all()); 109 | 110 | if (! $validation->fails()) { 111 | Users::editUserProfile(Users::find(Auth::user()->id)); 112 | 113 | return Redirect::route('profile_user') 114 | ->with('status', 'alert-success') 115 | ->with('message', 'Your public data has been correctly edited.'); 116 | } else { 117 | return Redirect::route('profile_user') 118 | ->withErrors($validation); 119 | } 120 | } 121 | 122 | /** 123 | * Show account form data. Change password form, and delete account. 124 | * 125 | * @return object View 126 | */ 127 | public function showAccount() 128 | { 129 | return View::make('user/settings/account') 130 | ->with('title', 'Account settings'); 131 | } 132 | 133 | /** 134 | * Edit current user password. Validating inputs. Checking current password etc. 135 | * 136 | * @return object Redirect with message (success or validation errors) 137 | */ 138 | public function editAccount() 139 | { 140 | $validation = Users::validatePasswordChange(Input::all()); 141 | 142 | $inputs = array( 143 | 'email' => Auth::user()->email, 144 | 'password' => Input::get('old_password') 145 | ); 146 | 147 | if (Auth::attempt($inputs)) { 148 | if (! $validation->fails()) { 149 | Users::editUserPassword(Users::find(Auth::user()->id)); 150 | 151 | return Redirect::route('account_user') 152 | ->with('status', 'alert-success') 153 | ->with('message', 'Your account password has been correctly edited.'); 154 | } else { 155 | return Redirect::route('account_user') 156 | ->withErrors($validation); 157 | } 158 | } else { 159 | return Redirect::route('account_user') 160 | ->withErrors(array('error' => 'Wrong current password!')); 161 | } 162 | } 163 | 164 | /** 165 | * Deleting current user account. 166 | * 167 | * @return object Redirect with message (success or validation errors) 168 | */ 169 | public function deleteAccount() 170 | { 171 | $inputs = array( 172 | 'email' => Auth::user()->email, 173 | 'password' => Input::get('password') 174 | ); 175 | 176 | if (Auth::attempt($inputs)) { 177 | Users::deleteUserAccount(Users::find(Auth::user()->id)); 178 | 179 | return Redirect::route('main') 180 | ->with('status', 'alert-success') 181 | ->with('message', 'Your account has been properly deleted.'); 182 | } else { 183 | return Redirect::route('account_user') 184 | ->withErrors(array('error' => 'Wrong password!')); 185 | } 186 | } 187 | 188 | /** 189 | * Notification query. 190 | * 191 | * @param int $type 192 | * @return mixed 193 | */ 194 | private function notification($type) 195 | { 196 | return Votes::where('user_id', '<>', Auth::user()->id) 197 | ->where('notification', $type) 198 | ->get(); 199 | } 200 | 201 | /** 202 | * Show latest notifications table. 203 | * Contains all of the unmarked votes commited on current user images. 204 | * 205 | * @return object View 206 | */ 207 | public function showNotification() 208 | { 209 | return View::make('user/settings/notification') 210 | ->with('title', 'Latest notifications') 211 | ->with('notifications', $this->notification(1)); 212 | } 213 | 214 | /** 215 | * Show latest notifications table. 216 | * Contains all of the marked votes commited on current user images. 217 | * 218 | * @return object View 219 | */ 220 | public function showNotificationHistory() 221 | { 222 | return View::make('user/settings/notification_history') 223 | ->with('title', 'Notification history') 224 | ->with('notifications', $this->notification(0)); 225 | } 226 | 227 | } -------------------------------------------------------------------------------- /app/config/app.php: -------------------------------------------------------------------------------- 1 | true, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application URL 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This URL is used by the console to properly generate URLs when using 24 | | the Artisan command line tool. You should set this to the root of 25 | | your application so that it is used when running Artisan tasks. 26 | | 27 | */ 28 | 29 | 'url' => 'http://localhost/laravel-img-upload', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Timezone 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may specify the default timezone for your application, which 37 | | will be used by the PHP date and date-time functions. We have gone 38 | | ahead and set this to a sensible default for you out of the box. 39 | | 40 | */ 41 | 42 | 'timezone' => 'UTC', 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application Locale Configuration 47 | |-------------------------------------------------------------------------- 48 | | 49 | | The application locale determines the default locale that will be used 50 | | by the translation service provider. You are free to set this value 51 | | to any of the locales which will be supported by the application. 52 | | 53 | */ 54 | 55 | 'locale' => 'en', 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Encryption Key 60 | |-------------------------------------------------------------------------- 61 | | 62 | | This key is used by the Illuminate encrypter service and should be set 63 | | to a random, 32 character string, otherwise these encrypted strings 64 | | will not be safe. Please do this before deploying an application! 65 | | 66 | */ 67 | 68 | 'key' => 'umvmq4Mb88D4eMlBH2rpTYuprGLH9cwb', 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Autoloaded Service Providers 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The service providers listed here will be automatically loaded on the 76 | | request to your application. Feel free to add your own services to 77 | | this array to grant expanded functionality to your applications. 78 | | 79 | */ 80 | 81 | 'providers' => array( 82 | 83 | 'Illuminate\Foundation\Providers\ArtisanServiceProvider', 84 | 'Illuminate\Auth\AuthServiceProvider', 85 | 'Illuminate\Cache\CacheServiceProvider', 86 | 'Illuminate\Session\CommandsServiceProvider', 87 | 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider', 88 | 'Illuminate\Routing\ControllerServiceProvider', 89 | 'Illuminate\Cookie\CookieServiceProvider', 90 | 'Illuminate\Database\DatabaseServiceProvider', 91 | 'Illuminate\Encryption\EncryptionServiceProvider', 92 | 'Illuminate\Filesystem\FilesystemServiceProvider', 93 | 'Illuminate\Hashing\HashServiceProvider', 94 | 'Illuminate\Html\HtmlServiceProvider', 95 | 'Illuminate\Log\LogServiceProvider', 96 | 'Illuminate\Mail\MailServiceProvider', 97 | 'Illuminate\Database\MigrationServiceProvider', 98 | 'Illuminate\Pagination\PaginationServiceProvider', 99 | 'Illuminate\Queue\QueueServiceProvider', 100 | 'Illuminate\Redis\RedisServiceProvider', 101 | 'Illuminate\Remote\RemoteServiceProvider', 102 | 'Illuminate\Auth\Reminders\ReminderServiceProvider', 103 | 'Illuminate\Database\SeedServiceProvider', 104 | 'Illuminate\Session\SessionServiceProvider', 105 | 'Illuminate\Translation\TranslationServiceProvider', 106 | 'Illuminate\Validation\ValidationServiceProvider', 107 | 'Illuminate\View\ViewServiceProvider', 108 | 'Illuminate\Workbench\WorkbenchServiceProvider', 109 | 'Intervention\Image\ImageServiceProvider', 110 | 'Way\Generators\GeneratorsServiceProvider', 111 | 'Intervention\Image\ImageServiceProvider', 112 | ), 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Service Provider Manifest 117 | |-------------------------------------------------------------------------- 118 | | 119 | | The service provider manifest is used by Laravel to lazy load service 120 | | providers which are not needed for each request, as well to keep a 121 | | list of all of the services. Here, you may set its storage spot. 122 | | 123 | */ 124 | 125 | 'manifest' => storage_path().'/meta', 126 | 127 | /* 128 | |-------------------------------------------------------------------------- 129 | | Class Aliases 130 | |-------------------------------------------------------------------------- 131 | | 132 | | This array of class aliases will be registered when this application 133 | | is started. However, feel free to register as many as you wish as 134 | | the aliases are "lazy" loaded so they don't hinder performance. 135 | | 136 | */ 137 | 138 | 'aliases' => array( 139 | 140 | 'App' => 'Illuminate\Support\Facades\App', 141 | 'Artisan' => 'Illuminate\Support\Facades\Artisan', 142 | 'Auth' => 'Illuminate\Support\Facades\Auth', 143 | 'Blade' => 'Illuminate\Support\Facades\Blade', 144 | 'Cache' => 'Illuminate\Support\Facades\Cache', 145 | 'ClassLoader' => 'Illuminate\Support\ClassLoader', 146 | 'Config' => 'Illuminate\Support\Facades\Config', 147 | 'Controller' => 'Illuminate\Routing\Controller', 148 | 'Cookie' => 'Illuminate\Support\Facades\Cookie', 149 | 'Crypt' => 'Illuminate\Support\Facades\Crypt', 150 | 'DB' => 'Illuminate\Support\Facades\DB', 151 | 'Eloquent' => 'Illuminate\Database\Eloquent\Model', 152 | 'Event' => 'Illuminate\Support\Facades\Event', 153 | 'File' => 'Illuminate\Support\Facades\File', 154 | 'Form' => 'Illuminate\Support\Facades\Form', 155 | 'Hash' => 'Illuminate\Support\Facades\Hash', 156 | 'HTML' => 'Illuminate\Support\Facades\HTML', 157 | 'Input' => 'Illuminate\Support\Facades\Input', 158 | 'Lang' => 'Illuminate\Support\Facades\Lang', 159 | 'Log' => 'Illuminate\Support\Facades\Log', 160 | 'Mail' => 'Illuminate\Support\Facades\Mail', 161 | 'Paginator' => 'Illuminate\Support\Facades\Paginator', 162 | 'Password' => 'Illuminate\Support\Facades\Password', 163 | 'Queue' => 'Illuminate\Support\Facades\Queue', 164 | 'Redirect' => 'Illuminate\Support\Facades\Redirect', 165 | 'Redis' => 'Illuminate\Support\Facades\Redis', 166 | 'Request' => 'Illuminate\Support\Facades\Request', 167 | 'Response' => 'Illuminate\Support\Facades\Response', 168 | 'Route' => 'Illuminate\Support\Facades\Route', 169 | 'Schema' => 'Illuminate\Support\Facades\Schema', 170 | 'Seeder' => 'Illuminate\Database\Seeder', 171 | 'Session' => 'Illuminate\Support\Facades\Session', 172 | 'SSH' => 'Illuminate\Support\Facades\SSH', 173 | 'Str' => 'Illuminate\Support\Str', 174 | 'URL' => 'Illuminate\Support\Facades\URL', 175 | 'Validator' => 'Illuminate\Support\Facades\Validator', 176 | 'View' => 'Illuminate\Support\Facades\View', 177 | 'Image' => 'Intervention\Image\Facades\Image', 178 | ), 179 | 180 | ); -------------------------------------------------------------------------------- /public/assets/js/infinitescroll.js: -------------------------------------------------------------------------------- 1 | (function(e,t,n){"use strict";t.infinitescroll=function(n,r,i){this.element=t(i);if(!this._create(n,r)){this.failed=true}};t.infinitescroll.defaults={loading:{finished:n,finishedMsg:"Congratulations, you've reached the end of the internet.",img:"data:image/gif;base64,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",msg:null,msgText:"Loading the next set of posts...",selector:null,speed:"fast",start:n},state:{isDuringAjax:false,isInvalidPage:false,isDestroyed:false,isDone:false,isPaused:false,isBeyondMaxPage:false,currPage:1},debug:false,behavior:n,binder:t(e),nextSelector:"div.navigation a:first",navSelector:"div.navigation",contentSelector:null,extraScrollPx:150,itemSelector:"div.post",animate:false,pathParse:n,dataType:"html",appendCallback:true,bufferPx:40,errorCallback:function(){},infid:0,pixelsFromNavToBottom:n,path:n,prefill:false,maxPage:n};t.infinitescroll.prototype={_binding:function(t){var r=this,i=r.options;i.v="2.0b2.120520";if(!!i.behavior&&this["_binding_"+i.behavior]!==n){this["_binding_"+i.behavior].call(this);return}if(t!=="bind"&&t!=="unbind"){this._debug("Binding value "+t+" not valid");return false}if(t==="unbind"){this.options.binder.unbind("smartscroll.infscr."+r.options.infid)}else{this.options.binder[t]("smartscroll.infscr."+r.options.infid,function(){r.scroll()})}this._debug("Binding",t)},_create:function(i,s){var o=t.extend(true,{},t.infinitescroll.defaults,i);this.options=o;var u=t(e);var a=this;if(!a._validate(i)){return false}var f=t(o.nextSelector).attr("href");if(!f){this._debug("Navigation selector not found");return false}o.path=o.path||this._determinepath(f);o.contentSelector=o.contentSelector||this.element;o.loading.selector=o.loading.selector||o.contentSelector;o.loading.msg=o.loading.msg||t('
Loading...
'+o.loading.msgText+"
");(new Image).src=o.loading.img;if(o.pixelsFromNavToBottom===n){o.pixelsFromNavToBottom=t(document).height()-t(o.navSelector).offset().top;this._debug("pixelsFromNavToBottom: "+o.pixelsFromNavToBottom)}var l=this;o.loading.start=o.loading.start||function(){t(o.navSelector).hide();o.loading.msg.appendTo(o.loading.selector).show(o.loading.speed,t.proxy(function(){this.beginAjax(o)},l))};o.loading.finished=o.loading.finished||function(){if(!o.state.isBeyondMaxPage)o.loading.msg.fadeOut(o.loading.speed)};o.callback=function(e,r,i){if(!!o.behavior&&e["_callback_"+o.behavior]!==n){e["_callback_"+o.behavior].call(t(o.contentSelector)[0],r,i)}if(s){s.call(t(o.contentSelector)[0],r,o,i)}if(o.prefill){u.bind("resize.infinite-scroll",e._prefill)}};if(i.debug){if(Function.prototype.bind&&(typeof console==="object"||typeof console==="function")&&typeof console.log==="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(e){console[e]=this.call(console[e],console)},Function.prototype.bind)}}this._setup();if(o.prefill){this._prefill()}return true},_prefill:function(){function s(){return r.options.contentSelector.height()<=i.height()}var r=this;var i=t(e);this._prefill=function(){if(s()){r.scroll()}i.bind("resize.infinite-scroll",function(){if(s()){i.unbind("resize.infinite-scroll");r.scroll()}})};this._prefill()},_debug:function(){if(true!==this.options.debug){return}if(typeof console!=="undefined"&&typeof console.log==="function"){if(Array.prototype.slice.call(arguments).length===1&&typeof Array.prototype.slice.call(arguments)[0]==="string"){console.log(Array.prototype.slice.call(arguments).toString())}else{console.log(Array.prototype.slice.call(arguments))}}else if(!Function.prototype.bind&&typeof console!=="undefined"&&typeof console.log==="object"){Function.prototype.call.call(console.log,console,Array.prototype.slice.call(arguments))}},_determinepath:function(t){var r=this.options;if(!!r.behavior&&this["_determinepath_"+r.behavior]!==n){return this["_determinepath_"+r.behavior].call(this,t)}if(!!r.pathParse){this._debug("pathParse manual");return r.pathParse(t,this.options.state.currPage+1)}else if(t.match(/^(.*?)\b2\b(.*?$)/)){t=t.match(/^(.*?)\b2\b(.*?$)/).slice(1)}else if(t.match(/^(.*?)2(.*?$)/)){if(t.match(/^(.*?page=)2(\/.*|$)/)){t=t.match(/^(.*?page=)2(\/.*|$)/).slice(1);return t}t=t.match(/^(.*?)2(.*?$)/).slice(1)}else{if(t.match(/^(.*?page=)1(\/.*|$)/)){t=t.match(/^(.*?page=)1(\/.*|$)/).slice(1);return t}else{this._debug("Sorry, we couldn't parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.");r.state.isInvalidPage=true}}this._debug("determinePath",t);return t},_error:function(t){var r=this.options;if(!!r.behavior&&this["_error_"+r.behavior]!==n){this["_error_"+r.behavior].call(this,t);return}if(t!=="destroy"&&t!=="end"){t="unknown"}this._debug("Error",t);if(t==="end"||r.state.isBeyondMaxPage){this._showdonemsg()}r.state.isDone=true;r.state.currPage=1;r.state.isPaused=false;r.state.isBeyondMaxPage=false;this._binding("unbind")},_loadcallback:function(i,s,o){var u=this.options,a=this.options.callback,f=u.state.isDone?"done":!u.appendCallback?"no-append":"append",l;if(!!u.behavior&&this["_loadcallback_"+u.behavior]!==n){this["_loadcallback_"+u.behavior].call(this,i,s);return}switch(f){case"done":this._showdonemsg();return false;case"no-append":if(u.dataType==="html"){s="
"+s+"
";s=t(s).find(u.itemSelector)}break;case"append":var c=i.children();if(c.length===0){return this._error("end")}l=document.createDocumentFragment();while(i[0].firstChild){l.appendChild(i[0].firstChild)}this._debug("contentSelector",t(u.contentSelector)[0]);t(u.contentSelector)[0].appendChild(l);s=c.get();break}u.loading.finished.call(t(u.contentSelector)[0],u);if(u.animate){var h=t(e).scrollTop()+t(u.loading.msg).height()+u.extraScrollPx+"px";t("html,body").animate({scrollTop:h},800,function(){u.state.isDuringAjax=false})}if(!u.animate){u.state.isDuringAjax=false}a(this,s,o);if(u.prefill){this._prefill()}},_nearbottom:function(){var i=this.options,s=0+t(document).height()-i.binder.scrollTop()-t(e).height();if(!!i.behavior&&this["_nearbottom_"+i.behavior]!==n){return this["_nearbottom_"+i.behavior].call(this)}this._debug("math:",s,i.pixelsFromNavToBottom);return s-i.bufferPx-1&&t(n[r]).length===0){this._debug("Your "+r+" found no elements.");return false}}return true},bind:function(){this._binding("bind")},destroy:function(){this.options.state.isDestroyed=true;this.options.loading.finished();return this._error("destroy")},pause:function(){this._pausing("pause")},resume:function(){this._pausing("resume")},beginAjax:function(r){var i=this,s=r.path,o,u,a,f;r.state.currPage++;if(r.maxPage!=n&&r.state.currPage>r.maxPage){r.state.isBeyondMaxPage=true;this.destroy();return}o=t(r.contentSelector).is("table, tbody")?t(""):t("
");u=typeof s==="function"?s(r.state.currPage):s.join(r.state.currPage);i._debug("heading into ajax",u);a=r.dataType==="html"||r.dataType==="json"?r.dataType:"html+callback";if(r.appendCallback&&r.dataType==="html"){a+="+callback"}switch(a){case"html+callback":i._debug("Using HTML via .load() method");o.load(u+" "+r.itemSelector,n,function(t){i._loadcallback(o,t,u)});break;case"html":i._debug("Using "+a.toUpperCase()+" via $.ajax() method");t.ajax({url:u,dataType:r.dataType,complete:function(t,n){f=typeof t.isResolved!=="undefined"?t.isResolved():n==="success"||n==="notmodified";if(f){i._loadcallback(o,t.responseText,u)}else{i._error("end")}}});break;case"json":i._debug("Using "+a.toUpperCase()+" via $.ajax() method");t.ajax({dataType:"json",type:"GET",url:u,success:function(e,t,s){f=typeof s.isResolved!=="undefined"?s.isResolved():t==="success"||t==="notmodified";if(r.appendCallback){if(r.template!==n){var a=r.template(e);o.append(a);if(f){i._loadcallback(o,a)}else{i._error("end")}}else{i._debug("template must be defined.");i._error("end")}}else{if(f){i._loadcallback(o,e,u)}else{i._error("end")}}},error:function(){i._debug("JSON ajax request failed.");i._error("end")}});break}},retrieve:function(r){r=r||null;var i=this,s=i.options;if(!!s.behavior&&this["retrieve_"+s.behavior]!==n){this["retrieve_"+s.behavior].call(this,r);return}if(s.state.isDestroyed){this._debug("Instance is destroyed");return false}s.state.isDuringAjax=true;s.loading.start.call(t(s.contentSelector)[0],s)},scroll:function(){var t=this.options,r=t.state;if(!!t.behavior&&this["scroll_"+t.behavior]!==n){this["scroll_"+t.behavior].call(this);return}if(r.isDuringAjax||r.isInvalidPage||r.isDone||r.isDestroyed||r.isPaused){return}if(!this._nearbottom()){return}this.retrieve()},toggle:function(){this._pausing()},unbind:function(){this._binding("unbind")},update:function(n){if(t.isPlainObject(n)){this.options=t.extend(true,this.options,n)}}};t.fn.infinitescroll=function(n,r){var i=typeof n;switch(i){case"string":var s=Array.prototype.slice.call(arguments,1);this.each(function(){var e=t.data(this,"infinitescroll");if(!e){return false}if(!t.isFunction(e[n])||n.charAt(0)==="_"){return false}e[n].apply(e,s)});break;case"object":this.each(function(){var e=t.data(this,"infinitescroll");if(e){e.update(n)}else{e=new t.infinitescroll(n,r,this);if(!e.failed){t.data(this,"infinitescroll",e)}}});break}return this};var r=t.event,i;r.special.smartscroll={setup:function(){t(this).bind("scroll",r.special.smartscroll.handler)},teardown:function(){t(this).unbind("scroll",r.special.smartscroll.handler)},handler:function(e,n){var r=this,s=arguments;e.type="smartscroll";if(i){clearTimeout(i)}i=setTimeout(function(){t(r).trigger("smartscroll",s)},n==="execAsap"?0:100)}};t.fn.smartscroll=function(e){return e?this.bind("smartscroll",e):this.trigger("smartscroll",["execAsap"])}})(window,jQuery) -------------------------------------------------------------------------------- /public/assets/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); --------------------------------------------------------------------------------