commit a3320dd5981db117b013f0054a16ecd3eca3e0e1 Author: Dave Date: Mon Feb 29 15:59:36 2016 +0000 - First commit diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 00000000..7694ad78 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "./public/vendor" +} \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..af38e89a --- /dev/null +++ b/.env.example @@ -0,0 +1,24 @@ +APP_ENV=production +APP_DEBUG=true +APP_URL=http://dev.attendize.com +APP_CIPHER=rijndael-128 +APP_KEY=SomeRandomString +APP_TIMEZONE + +DB_TYPE=mysql +DB_HOST=localhost +DB_DATABASE=attendize +DB_USERNAME +DB_PASSWORD + +MAIL_DRIVER=smtp +MAIL_PORT=587 +MAIL_ENCRYPTION=tls +MAIL_HOST=smtp.mandrillapp.com +MAIL_FROM_ADDRESS=info@attendize.com +MAIL_FROM_NAME=Attendize.com +MAIL_PASSWORD=b038a015-8162-4a7a-bb8b-abb63fa7c112 +MAIL_USERNAME=dave.m.earley@gmail.com + + +LOG=single \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..176a458f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e9c3aeee --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/public/vendor +/vendor +/node_modules +.env +.idea diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..e704849e --- /dev/null +++ b/.htaccess @@ -0,0 +1 @@ +RewriteRule ^(.*)$ public/$1 [L] \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..2c4bfe01 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,140 @@ +module.exports = function(grunt) { + + //Initializing the configuration object + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + // Task configuration + less: { + development: { + options: { + compress: true, //minifying the result + }, + files: { + //compiling frontend.less into frontend.css + "./public/assets/stylesheet/application.css":"./public/assets/stylesheet/application.less", + "./public/assets/stylesheet/frontend.css":"./public/assets/stylesheet/frontend.less", + //compiling backend.less into backend.css + //"./public/assets/stylesheets/backend.css":"./app/assets/stylesheets/backend.less" + } + }, + website: { + options: { + compress: true, //minifying the result + }, + files: { + "./public/website_assets/stylesheet/main.css":"./public/website_assets/stylesheet/main.less", + } + } + }, + concat: { + options: { + separator: ';', + stripBanners: true, + + }, + js_frontend: { + src: [ + './public/vendor/jquery/jquery.js', + './public/vendor/bootstrap/dist/js/bootstrap.js', + './public/vendor/jquery-form/jquery.form.js', + './public/vendor/RRSSB/js/rrssb.js', + './public/vendor/humane-js/humane.js', + './public/vendor/jquery-backstretch/jquery.backstretch.js', + './public/assets/javascript/app-public.js' + ], + dest: './public/assets/javascript/frontend.js', + }, + js_backend: { + src: [ + './public/vendor/modernizr/modernizr.js', + './public/vendor/bootstrap/dist/js/bootstrap.js', + './public/vendor/jquery-form/jquery.form.js', + './public/vendor/humane-js/humane.js', + './public/vendor/RRSSB/js/rrssb.js', + './public/vendor/bootstrap-touchspin/dist/jquery.bootstrap-touchspin.js', + './public/vendor/curioussolutions-datetimepicker/dist/DateTimePicker.js', + './public/assets/javascript/app.js' + ], + dest: './public/assets/javascript/backend.js', + }, + }, + uglify: { + options: { + mangle: true, // Use if you want the names of your functions and variables unchanged + preserveComments: false, + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %> */', + + }, + frontend: { + files: { + './public/assets/javascript/frontend.js': './public/assets/javascript/frontend.js', + } + }, + backend: { + files: { + './public/assets/javascript/backend.js': './public/assets/javascript/backend.js', + } + }, + }, + phpunit: { + classes: { + }, + options: { + } + }, +// watch: { +// js_frontend: { +// files: [ +// //watched files +// './bower_components/jquery/jquery.js', +// './bower_components/bootstrap/dist/js/bootstrap.js', +// './app/assets/javascript/frontend.js' +// ], +// tasks: ['concat:js_frontend','uglify:frontend'], //tasks to run +// options: { +// livereload: true //reloads the browser +// } +// }, +// js_backend: { +// files: [ +// //watched files +// './bower_components/jquery/jquery.js', +// './bower_components/bootstrap/dist/js/bootstrap.js', +// './app/assets/javascript/backend.js' +// ], +// tasks: ['concat:js_backend','uglify:backend'], //tasks to run +// options: { +// livereload: true //reloads the browser +// } +// }, +// less: { +// files: ['./app/assets/stylesheets/*.less'], //watched files +// tasks: ['less'], //tasks to run +// options: { +// livereload: true //reloads the browser +// } +// }, +// tests: { +// files: ['app/controllers/*.php','app/models/*.php'], //the task will run only when you save files in this location +// tasks: ['phpunit'] +// } +// } + }); + + // Plugin loading + grunt.loadNpmTasks('grunt-contrib-concat'); + //grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + //grunt.loadNpmTasks('grunt-phpunit'); + + // Task definition + //grunt.registerTask('default', ['watch']); + grunt.registerTask('default', ['less', 'concat']); + grunt.registerTask('deploy', ['less', 'concat', 'uglify']); + grunt.registerTask('js', ['concat']); + grunt.registerTask('styles', ['concat']); + grunt.registerTask('minify', ['uglify']); + +}; \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..08db4e4e --- /dev/null +++ b/LICENSE @@ -0,0 +1,40 @@ +Attribution Assurance License +Copyright (c) 2016 by Dave Earley (dave@attendize.com) +http://www.attendize.com + +All Rights Reserved +ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license) +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the conditions below are met. +These conditions require a modest attribution to Attendize.com. The hope +is that its promotional value may help justify the thousands of dollars in +otherwise billable time invested in writing this and other freely available, +open-source software. + +1. Redistributions of source code, in whole or part and with or without +modification requires the express permission of the author and must prominently +display "Powered by Attendize" or the Attendize logo in verifiable form +with hyperlink to said site. +2. Neither the name nor any trademark of the Author may be used to +endorse or promote products derived from this software without specific +prior written permission. +3. Users are entirely responsible, to the exclusion of the Author and +any other persons, for compliance with (1) regulations set by owners or +administrators of employed equipment, (2) licensing terms of any other +software, and (3) local regulations regarding use, including those +regarding import, export, and use of encryption software. + +THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS; +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/app/Attendize/Utils.php b/app/Attendize/Utils.php new file mode 100644 index 00000000..a9494a75 --- /dev/null +++ b/app/Attendize/Utils.php @@ -0,0 +1,95 @@ +is_registered; + } + + public static function isConfirmed() + { + return Auth::check() && Auth::user()->is_confirmed; + } + + public static function isDatabaseSetup() + { + try { + if (Schema::hasTable('accounts')) { + return true; + } + } catch (\Exception $e) { + return false; + } + } + + /** + * Are we the cloud version of attendize or in dev enviornment? + * + * @return bool + */ + public static function isAttendize() { + return self::isAttendizeCloud() || self::isAttendizeDev(); + } + + /** + * Are we the cloud version of Attendize? + * + * @return bool + */ + public static function isAttendizeCloud() + { + return isset($_ENV['ATTENDIZE_CLOUD']) && $_ENV['ATTENDIZE_CLOUD'] == 'true'; + } + + /** + * Are we in a dev enviornment? + * + * @return bool + */ + public static function isAttendizeDev() + { + return isset($_ENV['ATTENDIZE_DEV']) && $_ENV['ATTENDIZE_DEV'] == 'true'; + } + + public static function isDownForMaintenance() + { + return file_exists(storage_path() . '/framework/down'); + } + + public static function isProd() + { + return App::environment() == ENV_PRODUCTION; + } + + public static function file_upload_max_size() { + static $max_size = -1; + + if ($max_size < 0) { + // Start with post_max_size. + $max_size = self::parse_size(ini_get('post_max_size')); + + // If upload_max_size is less, then reduce. Except if upload_max_size is + // zero, which indicates no limit. + $upload_max = self::parse_size(ini_get('upload_max_filesize')); + if ($upload_max > 0 && $upload_max < $max_size) { + $max_size = $upload_max; + } + } + return $max_size; + } + + public static function parse_size($size) { + $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size. + $size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size. + if ($unit) { + // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by. + return round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); + } + else { + return round($size); + } + } +} \ No newline at end of file diff --git a/app/Attendize/constants.php b/app/Attendize/constants.php new file mode 100644 index 00000000..d6a5296f --- /dev/null +++ b/app/Attendize/constants.php @@ -0,0 +1,57 @@ +orderMailer = $orderMailer; + } + + + public function handleOrder($job, $data) { + echo "Starting Job {$job->getJobId()}\n"; + + $order = Order::findOrfail($data['order_id']); + + /* + * Steps : + * 1 Notify event organiser + * 2 Order Confirmation email to buyer + * 3 Generate / Send Tickets + */ + + $data = [ + 'order' => $order, + 'event' => $order->event, + 'tickets' => $order->event->tickets, + 'attendees' => $order->attendees + ]; + + $pdf_file = storage_path().'/'.$order->order_reference; + exit($pdf_file); + + + PDF::setOutputMode('F'); // force to file + PDF::html('Public.ViewEvent.Partials.PDFTicket', $data, $pdf_file); + + + //1 + $this->orderMailer->sendOrderNotification($order); + //2 + $this->orderMailer->sendOrderConfirmation($order); + //3 + + $this->orderMailer->sendTickets($order); + + $job->delete(); + } + + public function messageAttendees($job, $data) { + + echo "Starting Job {$job->getJobId()}\n"; + + $message_object = Message::find($data['message_id']); + $event = $message_object->event; + + $attendees = ($message_object->recipients == 0) ? $event->attendees : Attendee::where('ticket_id', '=', $message_object->recipients)->where('account_id', '=', $message_object->account_id)->get(); + + $toFields = []; + foreach ($attendees as $attendee) { + $toFields[$attendee->email] = $attendee->full_name; + } + + $data = [ + 'event' => $event, + 'message_content' => $message_object->message, + 'subject' => $message_object->subject + ]; + + Mail::send('Emails.messageAttendees', $data, function($message) use ($toFields, $event, $message_object) { + $message->to($toFields) + ->from(OUTGOING_EMAIL_NOREPLY, $event->organiser->name) + ->replyTo($event->organiser->email, $event->organiser->name) + ->subject($message_object->subject); + }); + + + + $message_object->is_sent = 1; + $message_object->save(); + //$message->sent + + $job->delete(); + } + +} diff --git a/app/Attendize/mailers/AttendeeMailer.php b/app/Attendize/mailers/AttendeeMailer.php new file mode 100644 index 00000000..63a15fca --- /dev/null +++ b/app/Attendize/mailers/AttendeeMailer.php @@ -0,0 +1,48 @@ +event; + + $attendees = ($message_object->recipients == 0) + ? $event->attendees // all attendees + : Attendee::where('ticket_id', '=', $message_object->recipients)->where('account_id', '=', $message_object->account_id)->get(); + + $toFields = []; + foreach ($attendees as $attendee) { + $toFields[$attendee->email] = $attendee->full_name; + } + + $data = [ + 'event' => $event, + 'message_content' => $message_object->message, + 'subject' => $message_object->subject + ]; + + /* + * Mandril lets us send the email to multiple people at once. + */ + Mail::send('Emails.messageAttendees', $data, function($message) use ($toFields, $event, $message_object) { + $message->to($toFields) + ->from(OUTGOING_EMAIL_NOREPLY, $event->organiser->name) + ->replyTo($event->organiser->email, $event->organiser->name) + ->subject($message_object->subject); + }); + + + + $message_object->is_sent = 1; + $message_object->sent_at = Carbon::now(); + $message_object->save(); + } + +} diff --git a/app/Attendize/mailers/Mailer.php b/app/Attendize/mailers/Mailer.php new file mode 100644 index 00000000..cf56bc69 --- /dev/null +++ b/app/Attendize/mailers/Mailer.php @@ -0,0 +1,25 @@ +attach($attachment); + } + + $message + ->to($toEmail) + ->from($fromEmail, $fromName) + ->replyTo($replyEmail, $fromName) + ->subject($subject); + }); + } +} \ No newline at end of file diff --git a/app/Attendize/mailers/OrderMailer.php b/app/Attendize/mailers/OrderMailer.php new file mode 100644 index 00000000..55fdf5f8 --- /dev/null +++ b/app/Attendize/mailers/OrderMailer.php @@ -0,0 +1,35 @@ +sendTo($order->account->email, OUTGOING_EMAIL, OUTGOING_EMAIL_NAME, 'New order received on the event '. $order->event->title .' ['. $order->order_reference .']', 'Emails.OrderNotification', [ + 'order' => $order + ]); + } + + public function sendOrderConfirmation(Order $order) { + + $ticket_pdf = public_path($order->ticket_pdf_path); + + if(!file_exists($ticket_pdf)){ + $ticket_pdf = FALSE; + } + + $this->sendTo($order->email, OUTGOING_EMAIL, $order->event->organiser->name, 'Your tickets & order confirmation for the event '. $order->event->title .' ['. $order->order_reference .']', 'Emails.OrderConfirmation', [ + 'order' => $order, + 'email_logo' => $order->event->organiser->full_logo_path + ], $ticket_pdf); + } + + public function sendTickets(Order $order) { +// $this->sendTo($order->account->email, OUTGOING_EMAIL, OUTGOING_EMAIL_NAME, 'New order received on the event '. $order->event->title .' ['. $order->order_reference .']', 'Emails.OrderNotification', [ +// 'order' => $order +// ]); + } + + +} diff --git a/app/Attendize/mailers/UserMailer.php b/app/Attendize/mailers/UserMailer.php new file mode 100644 index 00000000..fda21c85 --- /dev/null +++ b/app/Attendize/mailers/UserMailer.php @@ -0,0 +1,14 @@ +attendeeMessage = $attendeeMessage; + } + + function handle(AttendeeMailer $mailer) { + Log::info(date('d m y H:i') . " - Starting Job {$this->job->getJobId()} ".__CLASS__); + + $mailer->sendMessageToAttendees($this->attendeeMessage); + + Log::info( date('d m y H:i') . " - Finished Job {$this->job->getJobId()} ".__CLASS__); + + $this->delete(); + } + +} diff --git a/app/Commands/OrderTicketsCommand.php b/app/Commands/OrderTicketsCommand.php new file mode 100644 index 00000000..fdb495eb --- /dev/null +++ b/app/Commands/OrderTicketsCommand.php @@ -0,0 +1,54 @@ +ticketOrder = $ticketOrder; + $this->sendOrderConfirmation = $sendOrderConfirmation; + } + + /** + * @param OrderMailer $mailer + */ + function handle(OrderMailer $mailer) { + + Log::info(date('d m y H:i') . " - Starting Job {$this->job->getJobId()} ".__CLASS__); + + //1 - Notify event organiser + if($this->sendOrderConfirmation) { + $mailer->sendOrderNotification($this->ticketOrder); + } + + //2 - Generate PDF Tickets + $this->ticketOrder->generatePdfTickets(); + + //3 - Send Tickets / Order confirmation + $mailer->sendOrderConfirmation($this->ticketOrder); + + Log::info(date('d m y H:i') . " - Finished Job {$this->job->getJobId()} ".__CLASS__); + + $this->delete(); + } + +} diff --git a/app/Console/Commands/Inspire.php b/app/Console/Commands/Inspire.php new file mode 100644 index 00000000..abb255d1 --- /dev/null +++ b/app/Console/Commands/Inspire.php @@ -0,0 +1,34 @@ +comment(PHP_EOL.Inspiring::quote().PHP_EOL); + } + +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 00000000..0c088c89 --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,29 @@ +command('inspire') + ->hourly(); + } + +} diff --git a/app/Events/Event.php b/app/Events/Event.php new file mode 100644 index 00000000..d59f7690 --- /dev/null +++ b/app/Events/Event.php @@ -0,0 +1,7 @@ +isHttpException($e)) { + return $this->renderHttpException($e); + } + + + if (config('app.debug')) { + return $this->renderExceptionWithWhoops($e); + } + + return parent::render($request, $e); + } + + /** + * Render an exception using Whoops. + * + * @param \Exception $e + * @return \Illuminate\Http\Response + */ + protected function renderExceptionWithWhoops(Exception $e) { + $whoops = new \Whoops\Run; + + if(Request::ajax()) { + $whoops->pushHandler(new \Whoops\Handler\JsonResponseHandler); + } else { + $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler()); + } + + return new \Illuminate\Http\Response( + $whoops->handleException($e), $e->getStatusCode(), $e->getHeaders() + ); + } + +} diff --git a/app/Handlers/Commands/.gitkeep b/app/Handlers/Commands/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/Handlers/Events/.gitkeep b/app/Handlers/Events/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/Helpers/helpers.php b/app/Helpers/helpers.php new file mode 100644 index 00000000..febaf2ec --- /dev/null +++ b/app/Helpers/helpers.php @@ -0,0 +1,33 @@ +getAuthPassword()); +}); + +/* + * Some macros and blade extensions + */ + +Form::macro('rawLabel', function($name, $value = null, $options = array()) { + $label = Form::label($name, '%s', $options); + + return sprintf($label, $value); +}); + +Form::macro('labelWithHelp', function($name, $value = null, $options = array(), $help_text) { + $label = Form::label($name, '%s', $options); + + return sprintf($label, $value) + .'' + . '' + . ''; +}); + + +Form::macro('customCheckbox', function($name, $value, $checked=FALSE, $label = FALSE, $options= []) { + +// $checkbox = Form::checkbox($name, $value = null, $checked, $options); +// $label = Form::rawLabel(); +// +// $out = '
+// +// +//
'; +// +// return $out; +}); + +Form::macro('styledFile', function($name, $multiple = FALSE) { + $out = '
+
+ + + Browse… + + + + +
+
'; + + return $out; +}); + +HTML::macro('sortable_link', function($title, $active_sort, $sort_by, $sort_order, $url_params = [], $class = '', $extra = '') { + + $sort_order = $sort_order == 'asc' ? 'desc' : 'asc'; + + $url_params = http_build_query([ + 'sort_by' => $sort_by, + 'sort_order' => $sort_order + ] + $url_params); + + $html = ""; + + $html .= ($active_sort == $sort_by) ? "$title" : $title; + + $html .= ($sort_order == 'desc') ? '' : ''; + + $html .= ''; + + return $html; +}); + + + +Blade::directive('money', function($expression) { + return ""; +}); + + diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php new file mode 100644 index 00000000..4ad5c58a --- /dev/null +++ b/app/Http/Controllers/Auth/AuthController.php @@ -0,0 +1,38 @@ +auth = $auth; + $this->registrar = $registrar; + + $this->middleware('guest', ['except' => 'getLogout']); + } + +} diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 00000000..9fe481fc --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,38 @@ +auth = $auth; + $this->passwords = $passwords; + + $this->middleware('guest'); + } + +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 00000000..27b3f452 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,11 @@ +find($event_id); + //$event = Event::scope()->join('orders', 'orders.event_id', '=', 'attendees.id')->find($event_id); + + if ($searchQuery) { + + $attendees = $event->attendees() + ->withoutCancelled() + ->join('orders', 'orders.id', '=', 'attendees.order_id') + ->where(function($query) use ($searchQuery) { + $query->where('orders.order_reference', 'like', $searchQuery . '%') + ->orWhere('attendees.first_name', 'like', $searchQuery . '%') + ->orWhere('attendees.email', 'like', $searchQuery . '%') + ->orWhere('attendees.last_name', 'like', $searchQuery . '%'); + }) + ->orderBy(($sort_by == 'order_reference' ? 'orders.' : 'attendees.') . $sort_by, $sort_order) + ->select('attendees.*', 'orders.order_reference') + ->paginate(); + } else { + $attendees = $event->attendees() + ->join('orders', 'orders.id', '=', 'attendees.order_id') + ->withoutCancelled() + ->orderBy(($sort_by == 'order_reference' ? 'orders.' : 'attendees.') . $sort_by, $sort_order) + ->select('attendees.*', 'orders.order_reference') + ->paginate(); + } + + $data = [ + 'attendees' => $attendees, + 'event' => $event, + 'sort_by' => $sort_by, + 'sort_order' => $sort_order, + 'q' => $searchQuery ? $searchQuery : '' + ]; + + + + return View::make('ManageEvent.Attendees', $data); + } + + public function showCreateAttendee($event_id) { + + $event = Event::scope()->find($event_id); + + /* + * If there are no tickets then we can't create an attendee + * @todo This is a bit hackish + */ + if($event->tickets->count() === 0) { + return ''; + } + + return View::make('ManageEvent.Modals.CreateAttendee', array( + 'modal_id' => \Input::get('modal_id'), + 'event' => $event, + 'tickets' => $event->tickets()->lists('title', 'id') + )); + } + + public function postCreateAttendee($event_id) { + + $rules = [ + 'first_name' => 'required', + 'ticket_id' => 'required|exists:tickets,id,account_id,' . \Auth::user()->account_id, + 'ticket_price' => 'numeric|required', + 'email' => 'email|required', + ]; + + $messages = [ + 'ticket_id.exists' => 'The ticket you have selected does not exist', + 'ticket_id.required' => 'The ticket field is required. ' + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $ticket_id = Input::get('ticket_id'); + $ticket_price = Input::get('ticket_price'); + $attendee_first_name = Input::get('first_name'); + $attendee_last_name = Input::get('last_name'); + $attendee_email = Input::get('email'); + $email_attendee = Input::get('email_ticket'); + + + /* + * Create the order + */ + $order = new Order; + $order->first_name = $attendee_first_name; + $order->last_name = $attendee_last_name; + $order->email = $attendee_email; + $order->order_status_id = ORDER_COMPLETE; + $order->amount = $ticket_price; + $order->account_id = Auth::user()->account_id; + $order->event_id = $event_id; + $order->save(); + + /* + * Update qty sold + */ + $ticket = Ticket::scope()->find($ticket_id); + $ticket->increment('quantity_sold'); + $ticket->increment('sales_volume', $ticket_price); + $ticket->event->increment('sales_volume', $ticket_price); + + + /* + * Insert order item + */ + $orderItem = new OrderItem; + $orderItem->title = $ticket->title; + $orderItem->quantity = 1; + $orderItem->order_id = $order->id; + $orderItem->unit_price = $ticket_price; + $orderItem->save(); + + + /* + * Update the event stats + */ + $event_stats = new EventStats; + $event_stats->updateTicketsSoldCount($event_id, 1); + $event_stats->updateTicketRevenue($ticket_id, $ticket_price); + + + /* + * Create the attendee + */ + $attendee = new Attendee; + $attendee->first_name = $attendee_first_name; + $attendee->last_name = $attendee_last_name; + $attendee->email = $attendee_email; + $attendee->event_id = $event_id; + $attendee->order_id = $order->id; + $attendee->ticket_id = $ticket_id; + $attendee->account_id = Auth::user()->account_id; + $attendee->reference = $order->order_reference . '-1'; + $attendee->save(); + + if ($email_attendee == '1') { + $this->dispatch(new OrderTicketsCommand($order, false)); + } + + Session::flash('message', 'Attendee Successfully Created'); + + return Response::json(array( + 'status' => 'success', + 'id' => $attendee->id, + 'redirectUrl' => route('showEventAttendees', array( + 'event_id' => $event_id + )) + )); + } + + public function showPrintAttendees($event_id) { + + $data['event'] = Event::scope()->find($event_id); + $data['attendees'] = $data['event']->attendees()->withoutCancelled()->orderBy('first_name')->get(); + + return View::make('ManageEvent.PrintAttendees', $data); + } + + public function showMessageAttendee($attendee_id) { + + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $data = [ + 'attendee' => $attendee, + 'event' => $attendee->event, + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.MessageAttendee', $data); + } + + public function postMessageAttendee($attendee_id) { + + $rules = [ + 'subject' => 'required', + 'message' => 'required' + ]; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $data = [ + 'attendee' => $attendee, + 'message_content' => Input::get('message'), + 'subject' => Input::get('subject'), + 'event' => $attendee->event, + 'email_logo' => $attendee->event->organiser->full_logo_path + ]; + + Mail::send('Emails.messageAttendees', $data, function($message) use ($attendee, $data) { + $message->to($attendee->email, $attendee->full_name) + ->from(OUTGOING_EMAIL_NOREPLY, $attendee->event->organiser->name) + ->replyTo($attendee->event->organiser->email, $attendee->event->organiser->name) + ->subject($data['subject']); + }); + + /* Could bcc in the above? */ + if (Input::get('send_copy') == '1') { + Mail::send('Emails.messageAttendees', $data, function($message) use ($attendee, $data) { + $message->to($attendee->event->organiser->email, $attendee->event->organiser->name) + ->from(OUTGOING_EMAIL_NOREPLY, $attendee->event->organiser->name) + ->replyTo($attendee->event->organiser->email, $attendee->event->organiser->name) + ->subject($data['subject'] . '[ORGANISER COPY]'); + }); + } + + + + + + return Response::json(array( + 'status' => 'success', + 'message' => 'Message Successfully Sent' + )); + } + + public function showMessageAttendees($event_id) { + + $data = [ + 'event' => Event::scope()->find($event_id), + 'modal_id' => Input::get('modal_id'), + 'tickets' => Event::scope()->find($event_id)->tickets()->lists('title', 'id')->toArray() + ]; + + return View::make('ManageEvent.Modals.MessageAttendees', $data); + } + + public function postMessageAttendees($event_id) { + + $rules = [ + 'subject' => 'required', + 'message' => 'required', + 'recipients' => 'required' + ]; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $message = Message::createNew(); + $message->message = Input::get('message'); + $message->subject = Input::get('subject'); + $message->recipients = Input::get('recipients'); + $message->event_id = $event_id; + $message->save(); + + /* + * Add to the queue + */ + + return Response::json(array( + 'status' => 'success', + 'message' => 'Message Successfully Sent' + )); + } + + public function showExportAttendees($event_id, $export_as = 'xls') { + + Excel::create('attendees-as-of-' . date('d-m-Y-g.i.a'), function($excel) use ($event_id) { + + $excel->setTitle('Attendees List'); + + // Chain the setters + $excel->setCreator(APP_NAME) + ->setCompany(APP_NAME); + + $excel->sheet('attendees_sheet_1', function($sheet) use ($event_id) { + + DB::connection()->setFetchMode(\PDO::FETCH_ASSOC); + $data = DB::table('attendees') + ->where('attendees.event_id', '=', $event_id) + ->where('attendees.is_cancelled', '=', 0) + ->where('attendees.account_id', '=', Auth::user()->account_id) + ->join('events', 'events.id', '=', 'attendees.event_id') + ->join('orders', 'orders.id', '=', 'attendees.order_id') + ->join('tickets', 'tickets.id', '=', 'attendees.ticket_id') + ->select( + 'attendees.first_name', 'attendees.last_name', 'attendees.email', 'attendees.reference', 'orders.order_reference', 'tickets.title', 'orders.created_at', DB::raw("(CASE WHEN attendees.has_arrived = 1 THEN 'YES' ELSE 'NO' END) AS `attendees.has_arrived`"), 'attendees.arrival_time')->get(); + //DB::raw("(CASE WHEN UNIX_TIMESTAMP(`attendees.arrival_time`) = 0 THEN '---' ELSE 'd' END) AS `attendees.arrival_time`")) + + + + + $sheet->fromArray($data); + + $sheet->row(1, array( + 'First Name', 'Last Name', 'Email', 'Ticket Reference', 'Order Reference', 'Ticket Type', 'Purchase Date', 'Has Arrived', 'Arrival Time' + )); + + // Set gray background on first row + $sheet->row(1, function($row) { + $row->setBackground('#f5f5f5'); + }); + }); + })->export($export_as); + } + + public function showEditAttendee($event_id, $attendee_id) { + + + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $data = [ + 'attendee' => $attendee, + 'event' => $attendee->event, + 'tickets' => $attendee->event->tickets->lists('title', 'id'), + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.EditAttendee', $data); + } + + public function postEditAttendee($event_id, $attendee_id) { + + $rules = [ + 'first_name' => 'required', + 'ticket_id' => 'required|exists:tickets,id,account_id,' . Auth::user()->account_id, + 'email' => 'required|email' + ]; + + $messages = [ + 'ticket_id.exists' => 'The ticket you have selected does not exist', + 'ticket_id.required' => 'The ticket field is required. ' + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $attendee->first_name = Input::get('first_name'); + $attendee->last_name = Input::get('last_name'); + $attendee->email = Input::get('email'); + $attendee->ticket_id = Input::get('ticket_id'); + $attendee->save(); + + return Response::json(array( + 'status' => 'success', + 'id' => $attendee->id, + 'message' => 'Refreshing...', + 'redirectUrl' => '' + )); + } + + public function showCancelAttendee($event_id, $attendee_id) { + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $data = [ + 'attendee' => $attendee, + 'event' => $attendee->event, + 'tickets' => $attendee->event->tickets->lists('title', 'id'), + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.CancelAttendee', $data); + } + + public function postCancelAttendee($event_id, $attendee_id) { + $attendee = Attendee::scope()->findOrFail($attendee_id); + + $attendee->ticket->decrement('quantity_sold'); + $attendee->is_cancelled = 1; + $attendee->save(); + + $data = [ + 'attendee' => $attendee, + 'email_logo' => $attendee->organiser->full_logo_path + ]; + + if (Input::get('notify_attendee') == '1') { + Mail::send('Emails.notifyCancelledAttendee', $data, function($message) use ($attendee) { + $message->to($attendee->email, $attendee->full_name) + ->from(OUTGOING_EMAIL_NOREPLY, $attendee->event->organiser->name) + ->replyTo($attendee->event->organiser->email, $attendee->event->organiser->name) + ->subject('You\'re ticket has been cancelled'); + }); + } + + \Session::flash('message', 'Successfully Cancelled Attenddee'); + + return Response::json(array( + 'status' => 'success', + 'id' => $attendee->id, + 'message' => 'Refreshing...', + 'redirectUrl' => '' + )); + } + +} diff --git a/app/Http/Controllers/EventCheckInController.php b/app/Http/Controllers/EventCheckInController.php new file mode 100644 index 00000000..848b2cc4 --- /dev/null +++ b/app/Http/Controllers/EventCheckInController.php @@ -0,0 +1,86 @@ +findOrFail($event_id); + $data['attendees'] = $data['event']->attendees; + return View::make('ManageEvent.CheckIn', $data); + } + + public function postCheckInSearch($event_id) { + + $searchQuery = Input::get('q'); + + $attendees = Attendee::scope()->withoutCancelled() + ->join('tickets', 'tickets.id', '=', 'attendees.ticket_id') + ->where(function($query) use ($event_id) { + $query->where('attendees.event_id', '=', $event_id); + })->where(function($query) use ($searchQuery) { + $query->orWhere('attendees.first_name', 'like', $searchQuery . '%') + ->orWhere(DB::raw("CONCAT_WS(' ', first_name, last_name)"), 'like', $searchQuery . '%') + //->orWhere('attendees.email', 'like', $searchQuery . '%') + ->orWhere('attendees.reference', 'like', $searchQuery . '%') + ->orWhere('attendees.last_name', 'like', $searchQuery . '%'); + }) + ->select([ + 'attendees.id', + 'attendees.first_name', + 'attendees.last_name', + 'attendees.email', + 'attendees.reference', + 'attendees.arrival_time', + 'attendees.has_arrived', + 'tickets.title as ticket' + ]) + ->orderBy('attendees.first_name', 'ASC') + ->get(); + + return Response::json($attendees); + } + + public function postCheckInAttendee() { + + $attendee_id = Input::get('attendee_id'); + $checking = Input::get('checking'); + + $attendee = Attendee::scope()->find($attendee_id); + + /* + * Ugh + */ + if ((($checking == 'in') && ($attendee->has_arrived == 1)) || (($checking == 'out') && ($attendee->has_arrived == 0))) { + return Response::json([ + 'status' => 'error', + 'message' => 'Warning: This Attendee Has Already Been Checked ' . (($checking == 'in') ? 'In (at '.$attendee->arrival_time->format('H:i A, F j').')' : 'Out').'!', + 'checked' => $checking, + 'id' => $attendee->id + ]); + } + + $attendee->has_arrived = ($checking == 'in') ? 1 : 0; + $attendee->arrival_time = Carbon::now(); + $attendee->save(); + + return Response::json([ + 'status' => 'success', + 'checked' => $checking, + 'message' => 'Attendee Successfully Checked ' . (($checking == 'in') ? 'In' : 'Out'), + 'id' => $attendee->id + ]); + } + +} diff --git a/app/Http/Controllers/EventCheckoutController.php b/app/Http/Controllers/EventCheckoutController.php new file mode 100644 index 00000000..60068487 --- /dev/null +++ b/app/Http/Controllers/EventCheckoutController.php @@ -0,0 +1,504 @@ +is_embedded = Input::get('is_embedded') == '1'; + } + + public function postValidateTickets($event_id) + { + /* + * Order expires after X min + */ + $order_expires_time = Carbon::now()->addMinutes(CHECKOUT_TIMEOUT_AFTER); + + $event = Event::findOrFail($event_id); + + $ticket_ids = Input::get('tickets'); + + /* + * Remove any tickets the user has reserved + */ + ReservedTickets::where('session_id', '=', Session::getId())->delete(); + + /* + * Go though the selected tickets and check if they're available + * , tot up the price and reserve them to prevent over selling. + */ + + $validation_rules = []; + $validation_messages = []; + $tickets = []; + $order_total = 0; + $total_ticket_quantity = 0; + $booking_fee = 0; + $organiser_booking_fee = 0; + $quantity_available_validation_rules = []; + + foreach ($ticket_ids as $ticket_id) { + + $current_ticket_quantity = (int)Input::get('ticket_' . $ticket_id); + + + if ($current_ticket_quantity < 1) { + continue; + } + + $total_ticket_quantity = $total_ticket_quantity + $current_ticket_quantity; + + $ticket = Ticket::find($ticket_id); + + $ticket_quantity_remaining = $ticket->quantity_remaining; + + /* + * @todo Check max/min per person + */ + $max_per_person = min($ticket_quantity_remaining, $ticket->max_per_person); + + $quantity_available_validation_rules['ticket_' . $ticket_id] = ['numeric', 'min:' . $ticket->min_per_person, 'max:' . $max_per_person]; + + + $quantity_available_validation_messages = [ + 'ticket_' . $ticket_id . '.max' => 'The maximum number of tickets you can register is ' . $ticket_quantity_remaining, + 'ticket_' . $ticket_id . '.min' => 'You must select at least ' . $ticket->min_per_person . ' tickets.' + ]; + + + $validator = Validator::make(['ticket_' . $ticket_id => (int)Input::get('ticket_' . $ticket_id)], $quantity_available_validation_rules, $quantity_available_validation_messages); + + if ($validator->fails()) { + return Response::json([ + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + ]); + } + + $order_total = $order_total + ($current_ticket_quantity * $ticket->price); + $booking_fee = $booking_fee + ($current_ticket_quantity * $ticket->booking_fee); + $organiser_booking_fee = $organiser_booking_fee + ($current_ticket_quantity * $ticket->organiser_booking_fee); + + $tickets[] = [ + 'ticket' => $ticket, + 'qty' => $current_ticket_quantity, + 'price' => ($current_ticket_quantity * $ticket->price), + 'booking_fee' => ($current_ticket_quantity * $ticket->booking_fee), + 'organiser_booking_fee' => ($current_ticket_quantity * $ticket->organiser_booking_fee), + 'full_price' => $ticket->price + $ticket->total_booking_fee + ]; + + /* + * Reserve the tickets in the DB + */ + $reservedTickets = new ReservedTickets; + $reservedTickets->ticket_id = $ticket_id; + $reservedTickets->event_id = $event_id; + $reservedTickets->quantity_reserved = $current_ticket_quantity; + $reservedTickets->expires = $order_expires_time; + $reservedTickets->session_id = Session::getId(); + $reservedTickets->save(); + + if ($event->ask_for_all_attendees_info) { + for ($i = 0; $i < $current_ticket_quantity; $i++) { + /* + * Create our validation rules here + */ + $validation_rules['ticket_holder_first_name.' . $i . '.' . $ticket_id] = ['required']; + $validation_rules['ticket_holder_last_name.' . $i . '.' . $ticket_id] = ['required']; + $validation_rules['ticket_holder_email.' . $i . '.' . $ticket_id] = ['required', 'email']; + + $validation_messages['ticket_holder_first_name.' . $i . '.' . $ticket_id . '.required'] = 'Ticket holder ' . ($i + 1) . '\'s first name is required'; + $validation_messages['ticket_holder_last_name.' . $i . '.' . $ticket_id . '.required'] = 'Ticket holder ' . ($i + 1) . '\'s last name is required'; + $validation_messages['ticket_holder_email.' . $i . '.' . $ticket_id . '.required'] = 'Ticket holder ' . ($i + 1) . '\'s email is required'; + $validation_messages['ticket_holder_email.' . $i . '.' . $ticket_id . '.email'] = 'Ticket holder ' . ($i + 1) . '\'s email appears to be invalid'; + } + } + } + + if (empty($tickets)) { + return Response::json(array( + 'status' => 'error', + 'message' => 'No tickets selected.' + )); + } + + /* + * @todo - Store this in something other than a session? + */ + Session::set('ticket_order_' . $event->id, [ + 'validation_rules' => $validation_rules, + 'validation_messages' => $validation_messages, + 'event_id' => $event->id, + 'tickets' => $tickets, /* probably shouldn't store the whole ticket obj in session */ + 'total_ticket_quantity' => $total_ticket_quantity, + 'order_started' => time(), + 'expires' => $order_expires_time, + 'reserved_tickets_id' => $reservedTickets->id, + 'order_total' => $order_total, + 'booking_fee' => $booking_fee, + 'organiser_booking_fee' => $organiser_booking_fee, + 'total_booking_fee' => $booking_fee + $organiser_booking_fee, + 'order_requires_payment' => (ceil($order_total) == 0) ? FALSE : TRUE, + 'account_id' => $event->account->id, + 'affiliate_referral' => Cookie::get('affiliate_' . $event_id) + ]); + + if (Request::ajax()) { + return Response::json(array( + 'status' => 'success', + 'redirectUrl' => route('showEventCheckout', [ + 'event_id' => $event_id, + 'is_embedded' => $this->is_embedded + ]) . '#order_form' + )); + } + + /* + * TODO: We should just show an enable JS message here instead + */ + return Redirect::to(route('showEventCheckout', [ + 'event_id' => $event_id + ]) . '#order_form'); + } + + public function showEventCheckout($event_id) + { + + $order_session = Session::get('ticket_order_' . $event_id); + + if (!$order_session || $order_session['expires'] < Carbon::now()) { + return Redirect::route('showEventPage', ['event_id' => $event_id]); + } + + $secondsToExpire = Carbon::now()->diffInSeconds($order_session['expires']); + + //dd($secondsToExpire); + $data = $order_session + [ + 'event' => Event::findorFail($order_session['event_id']), + 'secondsToExpire' => $secondsToExpire, + 'is_embedded' => $this->is_embedded + ]; + + if ($this->is_embedded) { + return View::make('Public.ViewEvent.Embedded.EventPageCheckout', $data); + } + return View::make('Public.ViewEvent.EventPageCheckout', $data); + } + + public function postCreateOrder($event_id) + { + + $mirror_buyer_info = (Input::get('mirror_buyer_info') == 'on') ? TRUE : FALSE; + + $event = Event::findOrFail($event_id); + + $order = new Order; + + $ticket_order = Session::get('ticket_order_' . $event_id); + + $attendee_increment = 1; + + $validation_rules = $ticket_order['validation_rules']; + $validation_messages = $ticket_order['validation_messages']; + + + if (!$mirror_buyer_info && $event->ask_for_all_attendees_info) { + $order->rules = $order->rules + $validation_rules; + $order->messages = $order->messages + $validation_messages; + } + + if (!$order->validate(Input::all())) { + return Response::json(array( + 'status' => 'error', + 'messages' => $order->errors() + )); + } + + /** + * Begin payment attempt before creating the attendees etc. + * */ + if ($ticket_order['order_requires_payment']) { + + try { + + $stripe_error = FALSE; + + Stripe::setApiKey($event->account->stripe_api_key); + + $token = Input::get('stripeToken'); + + $customer = Stripe_Customer::create(array( + 'email' => Input::get('order_email'), + 'card' => $token, + 'description' => 'Customer: ' . Input::get('order_email') + )); + + if (Utils::isAttendize()) { + $charge = Stripe_Charge::create(array( + 'customer' => $customer->id, + 'amount' => ($ticket_order['order_total'] + $ticket_order['organiser_booking_fee']) * 100, + 'currency' => $event->currency->code, + 'description' => Input::get('order_email'), + 'application_fee' => $ticket_order['booking_fee'] * 100, + 'description' => 'Order for customer: ' . Input::get('order_email') + )); + } else { + $charge = Stripe_Charge::create(array( + 'customer' => $customer->id, + 'amount' => ($ticket_order['order_total'] + $ticket_order['organiser_booking_fee']) * 100, + 'currency' => $event->currency->code, + 'description' => Input::get('order_email'), + 'description' => 'Order for customer: ' . Input::get('order_email') + )); + } + + $order->transaction_id = $charge->id; + } catch (\Stripe_CardError $e) { + // Card was declined. + $e_json = $e->getJsonBody(); + $error = $e_json['error']; + $stripe_error = $error['message']; + Log::error($e); + } catch (\Stripe_InvalidRequestError $e) { + $stripe_error = 'Whoops, something went wrong. Please try again.'; + Log::error($e); + } catch (\Stripe_AuthenticationError $e) { + $stripe_error = 'There was a problem processing your payment. Please try again'; + Log::error($e); + } catch (\Stripe_ApiConnectionError $e) { + $stripe_error = 'There was a problem processing your payment. Please try again'; + Log::error($e); + } catch (\Stripe_Error $e) { + $stripe_error = 'There was a problem processing your payment. Please try again'; + Log::error($e); + } catch (\Exception $e) { + $stripe_error = 'There was a problem processing your payment. Please try again'; + Log::error($e); + } + + if ($stripe_error) { + return Response::json(array( + 'status' => 'error', + 'message' => $stripe_error + )); + } + } + + /* + * Create the order + */ + $order->first_name = Input::get('order_first_name'); + $order->last_name = Input::get('order_last_name'); + $order->email = Input::get('order_email'); + $order->order_status_id = ORDER_COMPLETE; + $order->amount = $ticket_order['order_total']; + $order->booking_fee = $ticket_order['booking_fee']; + $order->organiser_booking_fee = $ticket_order['organiser_booking_fee']; + $order->discount = 0.00; + $order->account_id = $event->account->id; + $order->event_id = $event_id; + $order->save(); + + /* + * Update the event sales volume + */ + $event->increment('sales_volume', $order->amount); + $event->increment('organiser_fees_volume', $order->organiser_booking_fee); + + /* + * Update affiliates stats stats + */ + if ($ticket_order['affiliate_referral']) { + $affiliate = Affiliate::where('name', '=', $ticket_order['affiliate_referral'])->first(); + $affiliate->increment('sales_volume', $order->amount + $order->organiser_booking_fee); + $affiliate->increment('tickets_sold', $ticket_order['total_ticket_quantity']); + } + + /* + * Update the event stats + */ + $event_stats = EventStats::firstOrNew([ + 'event_id' => $event_id, + 'date' => DB::raw('CURDATE()') + ]); + $event_stats->increment('tickets_sold', $ticket_order['total_ticket_quantity']); + + if ($ticket_order['order_requires_payment']) { + $event_stats->increment('sales_volume', $order->amount); + $event_stats->increment('organiser_fees_volume', $order->organiser_booking_fee); + } + + /* + * Add the attendees + */ + foreach ($ticket_order['tickets'] as $attendee_details) { + + /* + * Update ticket's quantity sold + */ + $ticket = Ticket::findOrFail($attendee_details['ticket']['id']); + + /* + * Update some ticket info + */ + $ticket->increment('quantity_sold', $attendee_details['qty']); + $ticket->increment('sales_volume', ($attendee_details['ticket']['price'] * $attendee_details['qty'])); + $ticket->increment('organiser_fees_volume', ($attendee_details['ticket']['organiser_booking_fee'] * $attendee_details['qty'])); + + /* + * Insert order items (for use in generating invoices) + */ + $orderItem = new OrderItem; + $orderItem->title = $attendee_details['ticket']['title']; + $orderItem->quantity = $attendee_details['qty']; + $orderItem->order_id = $order->id; + $orderItem->unit_price = $attendee_details['ticket']['price']; + $orderItem->unit_booking_fee = $attendee_details['ticket']['booking_fee'] + $attendee_details['ticket']['organiser_booking_fee']; + $orderItem->save(); + + /* + * Create the attendees + */ + for ($i = 0; $i < $attendee_details['qty']; $i++) { + $attendee = new Attendee; + $attendee->first_name = $event->ask_for_all_attendees_info ? ($mirror_buyer_info ? $order->first_name : Input::get("ticket_holder_first_name.$i.{$attendee_details['ticket']['id']}")) : $order->first_name; + $attendee->last_name = $event->ask_for_all_attendees_info ? ($mirror_buyer_info ? $order->last_name : Input::get("ticket_holder_last_name.$i.{$attendee_details['ticket']['id']}")) : $order->last_name; + $attendee->email = $event->ask_for_all_attendees_info ? ($mirror_buyer_info ? $order->email : Input::get("ticket_holder_email.$i.{$attendee_details['ticket']['id']}")) : $order->email; + $attendee->event_id = $event_id; + $attendee->order_id = $order->id; + $attendee->ticket_id = $attendee_details['ticket']['id']; + $attendee->account_id = $event->account->id; + $attendee->reference = $order->order_reference . '-' . ($attendee_increment); + $attendee->save(); + + /* + * Queue an email to send to each attendee + */ + + + /* Keep track of total number of attendees */ + $attendee_increment++; + } + } + + /* + * Queue up some tasks - Emails to be sents, PDFs etc. + */ + $this->dispatch(new OrderTicketsCommand($order)); + + /* + * Release the reserved the tickets + */ + ReservedTickets::where('session_id', '=', Session::getId())->delete(); + + /* + * Kill the session + */ + Session::forget('ticket_order_' . $event->id); + + /* + * Queue the PDF creation jobs + */ + + return Response::json(array( + 'status' => 'success', + 'redirectUrl' => route('showOrderDetails', [ + 'is_embedded' => $this->is_embedded, + 'order_reference' => $order->order_reference + ]) + )); + } + + /** + * Show the order details page + * + * @param string $order_reference + * @return view + */ + public function showOrderDetails($order_reference) + { + + $order = Order::where('order_reference', '=', $order_reference)->first(); + + if (!$order) { + App::abort(404); + } + + $data = [ + 'order' => $order, + 'event' => $order->event, + 'tickets' => $order->event->tickets, + 'is_embedded' => $this->is_embedded + ]; + + if ($this->is_embedded) { + return View::make('Public.ViewEvent.Embedded.EventPageViewOrder', $data); + } + return View::make('Public.ViewEvent.EventPageViewOrder', $data); + } + + /** + * Output order tickets + * + * @param string $order_reference + */ + public function showOrderTickets($order_reference) + { + + $order = Order::where('order_reference', '=', $order_reference)->first(); + + if (!$order) { + App::abort(404); + } + + $data = [ + 'order' => $order, + 'event' => $order->event, + 'tickets' => $order->event->tickets, + 'attendees' => $order->attendees + ]; + + if (Input::get('download') == '1') { + return PDF::html('Public.ViewEvent.Partials.PDFTicket', $data, 'Tickets'); + } + + return View::make('Public.ViewEvent.Partials.PDFTicket', $data); + } + +} diff --git a/app/Http/Controllers/EventController.php b/app/Http/Controllers/EventController.php new file mode 100644 index 00000000..ce5a71f1 --- /dev/null +++ b/app/Http/Controllers/EventController.php @@ -0,0 +1,289 @@ + Input::get('modal_id'), + 'organisers' => Organiser::scope()->lists('name', 'id'), + 'organiser_id' => Input::get('organiser_id') ? Input::get('organiser_id') : false + ]; + + return View::make('ManageOrganiser.Modals.CreateEvent', $data); + } + + public function postCreateEvent() { + + $event = Event::createNew(); + + if (!$event->validate(Input::all())) { + return Response::json(array( + 'status' => 'error', + 'messages' => $event->errors() + )); + } + + $event->title = Input::get('title'); + $event->description = strip_tags(Input::get('description')); + $event->start_date = Input::get('start_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('start_date')) : NULL; + + /* + * Venue location info (Usually autofilled from google maps) + */ + + $is_auto_address = (trim(Input::get('place_id')) !== ''); + + if ($is_auto_address) { /* Google auto filled */ + $event->venue_name = Input::get('name'); + $event->venue_name_full = Input::get('venue_name_full'); + $event->location_lat = Input::get('lat'); + $event->location_long = Input::get('lng'); + $event->location_address = Input::get('formatted_address'); + $event->location_country = Input::get('country'); + $event->location_country_code = Input::get('country_short'); + $event->location_state = Input::get('administrative_area_level_1'); + $event->location_address_line_1 = Input::get('route'); + $event->location_address_line_2 = Input::get('locality'); + $event->location_post_code = Input::get('postal_code'); + $event->location_street_number = Input::get('street_number'); + $event->location_google_place_id = Input::get('place_id'); + $event->location_is_manual = 0; + } else { /* Manually entered */ + $event->venue_name = Input::get('location_venue_name'); + $event->location_address_line_1 = Input::get('location_address_line_1'); + $event->location_address_line_2 = Input::get('location_address_line_2'); + $event->location_state = Input::get('location_state'); + $event->location_post_code = Input::get('location_post_code'); + $event->location_is_manual = 1; + } + + + $event->end_date = Input::get('end_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('end_date')) : NULL; + + $event->currency_id = Auth::user()->account->currency_id; + //$event->timezone_id = Auth::user()->account->timezone_id; + + + if (Input::get('organiser_name')) { + + $organiser = Organiser::createNew(FALSE, FALSE, TRUE); + + $rules = array( + 'organiser_name' => array('required'), + 'organiser_email' => array('required', 'email'), + ); + $messages = array( + 'organiser_name.required' => 'You must give a name for the event organiser.' + ); + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $organiser->name = Input::get('organiser_name'); + $organiser->about = Input::get('organiser_about'); + $organiser->email = Input::get('organiser_email'); + $organiser->facebook = Input::get('organiser_facebook'); + $organiser->twitter = Input::get('organiser_twitter'); + $organiser->save(); + $event->organiser_id = $organiser->id; + } elseif (Input::get('organiser_id')) { + $event->organiser_id = Input::get('organiser_id'); + } else { /* Somethings gone horribly wrong */} + + $event->save(); + + if (Input::hasFile('event_image')) { + + $path = public_path() . '/' . EVENT_IMAGES_PATH; + $filename = 'event_image-' . md5(time() . $event->id) . '.' . strtolower(Input::file('event_image')->getClientOriginalExtension()); + + $file_full_path = $path . '/' . $filename; + + + Input::file('event_image')->move($path, $filename); + + $img = Image::make($file_full_path); + + $img->resize(800, null, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($file_full_path); + + /* Upload to s3 */ + \Storage::put(EVENT_IMAGES_PATH.'/'.$filename, file_get_contents($file_full_path)); + + + $eventImage = EventImage::createNew(); + $eventImage->image_path = EVENT_IMAGES_PATH . '/' . $filename; + $eventImage->event_id = $event->id; + $eventImage->save(); + } + + return Response::json(array( + 'status' => 'success', + 'id' => $event->id, + 'redirectUrl' => route('showEventTickets', array( + 'event_id' => $event->id, + 'first_run' => 'yup' + )) + )); + } + + public function postEditEvent($event_id) { + + $event = Event::scope()->findOrFail($event_id); + + if (!$event->validate(Input::all())) { + return Response::json(array( + 'status' => 'error', + 'messages' => $event->errors() + )); + } + + $event->is_live = Input::get('is_live'); + $event->title = Input::get('title'); + $event->description = strip_tags(Input::get('description')); + $event->start_date = Input::get('start_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('start_date')) : NULL; + + + /* + * If the google place ID is the same as before then don't update the venue + */ + if ((Input::get('place_id') !== $event->location_google_place_id) || $event->location_google_place_id == '') { + $is_auto_address = (trim(Input::get('place_id')) !== ''); + + if ($is_auto_address) { /* Google auto filled */ + $event->venue_name = Input::get('name'); + $event->venue_name_full = Input::get('venue_name_full'); + $event->location_lat = Input::get('lat'); + $event->location_long = Input::get('lng'); + $event->location_address = Input::get('formatted_address'); + $event->location_country = Input::get('country'); + $event->location_country_code = Input::get('country_short'); + $event->location_state = Input::get('administrative_area_level_1'); + $event->location_address_line_1 = Input::get('route'); + $event->location_address_line_2 = Input::get('locality'); + $event->location_post_code = Input::get('postal_code'); + $event->location_street_number = Input::get('street_number'); + $event->location_google_place_id = Input::get('place_id'); + $event->location_is_manual = 0; + } else { /* Manually entered */ + $event->venue_name = Input::get('location_venue_name'); + $event->location_address_line_1 = Input::get('location_address_line_1'); + $event->location_address_line_2 = Input::get('location_address_line_2'); + $event->location_state = Input::get('location_state'); + $event->location_post_code = Input::get('location_post_code'); + $event->location_is_manual = 1; + $event->location_google_place_id = ''; + $event->venue_name_full = ''; + $event->location_lat = ''; + $event->location_long = ''; + $event->location_address = ''; + $event->location_country = ''; + $event->location_country_code = ''; + $event->location_street_number = ''; + } + } + + + $event->end_date = Input::get('end_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('end_date')) : NULL; + + + if (Input::get('remove_current_image') == '1') { + EventImage::where('event_id', '=', $event->id)->delete(); + } + + $event->save(); + + if (Input::hasFile('event_image')) { + + $path = public_path() . '/' . EVENT_IMAGES_PATH; + $filename = 'event_image-' . md5(time() . $event->id) . '.' . strtolower(Input::file('event_image')->getClientOriginalExtension()); + + $file_full_path = $path . '/' . $filename; + + + Input::file('event_image')->move($path, $filename); + + $img = Image::make($file_full_path); + + $img->resize(800, null, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($file_full_path); + + \Storage::put(EVENT_IMAGES_PATH.'/'.$filename, file_get_contents($file_full_path)); + + EventImage::where('event_id', '=', $event->id)->delete(); + + $eventImage = EventImage::createNew(); + $eventImage->image_path = EVENT_IMAGES_PATH . '/' . $filename; + $eventImage->event_id = $event->id; + $eventImage->save(); + } + + return Response::json(array( + 'status' => 'success', + 'id' => $event->id, + 'message' => 'Event Successfully Updated', + 'redirectUrl' => '' + )); + } + + public function postUploadEventImage() { + + if (Input::hasFile('event_image')) { + + $the_file = \File::get(Input::file('event_image')->getRealPath()); + $file_name = 'event_details_image-' . md5(microtime()) . '.' . strtolower(Input::file('event_image')->getClientOriginalExtension()); + + $relative_path_to_file = EVENT_IMAGES_PATH . '/' . $file_name; + $full_path_to_file = public_path().'/'.$relative_path_to_file; + + $img = Image::make($the_file); + + $img->resize(1000, null, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($full_path_to_file); + if(\Storage::put($file_name, $the_file)) { + return Response::json([ + 'link' => '/'.$relative_path_to_file + ]); + } + + return Response::json([ + 'error' => 'There was a problem uploading your image.' + ]); + + } + + + + } + +} diff --git a/app/Http/Controllers/EventCustomizeController.php b/app/Http/Controllers/EventCustomizeController.php new file mode 100644 index 00000000..341c4d63 --- /dev/null +++ b/app/Http/Controllers/EventCustomizeController.php @@ -0,0 +1,210 @@ +getEventViewData($event_id, [ + 'available_bg_images' => $this->getAvailableBackgroundImages(), + 'available_bg_images_thumbs' => $this->getAvailableBackgroundImagesThumbs(), + 'tab' => $tab + ]); + return View::make('ManageEvent.Customize', $data); + } + + public function getAvailableBackgroundImages() { + + $images = []; + + $files = File::files(public_path() . '/' . EVENT_BG_IMAGES); + + foreach ($files as $image) { + $images[] = str_replace(public_path(), '', $image); + } + + return $images; + } + + public function getAvailableBackgroundImagesThumbs() { + + $images = []; + + $files = File::files(public_path() . '/' . EVENT_BG_IMAGES.'/thumbs'); + + foreach ($files as $image) { + $images[] = str_replace(public_path(), '', $image); + } + + return $images; + } + + public function postEditEventSocial($event_id) { + $event = Event::scope()->findOrFail($event_id); + + $rules = [ + 'social_share_text' => ['max:3000'], + 'social_show_facebook' => ['boolean'], + 'social_show_twitter' => ['boolean'], + 'social_show_linkedin' => ['boolean'], + 'social_show_email' => ['boolean'], + 'social_show_googleplus' => ['boolean'] + ]; + $messages = [ + 'social_share_text.max' => 'Please keep the shate text under 3000 characters.', + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $event->social_share_text = Input::get('social_share_text'); + $event->social_show_facebook = Input::get('social_show_facebook'); + $event->social_show_linkedin = Input::get('social_show_linkedin'); + $event->social_show_twitter = Input::get('social_show_twitter'); + $event->social_show_email = Input::get('social_show_email'); + $event->social_show_googleplus = Input::get('social_show_googleplus'); + $event->save(); + + return Response::json([ + 'status' => 'success', + 'message' => 'Social Settings Succesfully Upated', + ]); + } + + public function postEditEventFees($event_id) { + $event = Event::scope()->findOrFail($event_id); + + $rules = [ + 'organiser_fee_percentage' => ['numeric', 'between:0,100'], + 'organiser_fee_fixed' => ['numeric', 'between:0,100'] + ]; + $messages = [ + 'organiser_fee_percentage.numeric' => 'Please enter a value between 0 and 100', + 'organiser_fee_fixed.numeric' => 'Please check the format. It shoud be in the format 0.00.', + 'organiser_fee_fixed.between' => 'Please enter a value between 0 and 100.' + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $event->organiser_fee_percentage = Input::get('organiser_fee_percentage'); + $event->organiser_fee_fixed = Input::get('organiser_fee_fixed'); + $event->save(); + + return Response::json([ + 'status' => 'success', + 'message' => 'Order Page Succesfully Upated', + ]); + } + + public function postEditEventOrderPage($event_id) { + $event = Event::scope()->findOrFail($event_id); + + // Just plain text so no validation needed (hopefully) + $rules = []; + $messages = []; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $event->pre_order_display_message = trim(Input::get('pre_order_display_message')); + $event->post_order_display_message = trim(Input::get('post_order_display_message')); + $event->ask_for_all_attendees_info = (Input::get('ask_for_all_attendees_info') == 'on'); + $event->save(); + + return Response::json([ + 'status' => 'success', + 'message' => 'Order Page Succesfully Upated', + ]); + } + + public function postEditEventDesign($event_id) { + + $event = Event::scope()->findOrFail($event_id); + + $rules = [ + 'bg_image_path' => ['mimes:jpeg,jpg,png', 'max:4000'] + ]; + $messages = [ + 'bg_image_path.mimes' => 'Please ensure you are uploading an image (JPG, PNG, JPEG)', + 'bg_image_path.max' => 'Pleae ensure the image is not larger than 2.5MB' + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + + if (Input::get('bg_image_path_custom') && Input::get('bg_type') == 'image') { + $event->bg_image_path = Input::get('bg_image_path_custom'); + $event->bg_type = 'image'; + } + + if (Input::get('bg_color') && Input::get('bg_type') == 'color') { + $event->bg_color = Input::get('bg_color'); + $event->bg_type = 'color'; + } + + /* + * Not in use for now. + */ + if (Input::hasFile('bg_image_path') && Input::get('bg_type') == 'custom_image') { + + $path = public_path() . '/' . EVENT_IMAGES_PATH; + $filename = 'event_bg-'. md5($event->id) . '.' . strtolower(Input::file('bg_image_path')->getClientOriginalExtension()); + + $file_full_path = $path . '/' . $filename; + + + Input::file('bg_image_path')->move($path, $filename); + + $img = Image::make($file_full_path); + + $img->resize(1400, null, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($file_full_path, 75); + + + $event->bg_image_path = EVENT_IMAGES_PATH . '/' . $filename; + $event->bg_type = 'custom_image'; + + \Storage::put(EVENT_IMAGES_PATH.'/'.$filename, file_get_contents($file_full_path)); + } + + $event->save(); + + return Response::json([ + 'status' => 'success', + 'message' => 'Event Page Succesfully Upated', + 'runThis' => 'document.getElementById(\'previewIframe\').contentWindow.location.reload(true);' + ]); + } + +} diff --git a/app/Http/Controllers/EventDashboardController.php b/app/Http/Controllers/EventDashboardController.php new file mode 100644 index 00000000..d11c77af --- /dev/null +++ b/app/Http/Controllers/EventDashboardController.php @@ -0,0 +1,108 @@ +findOrFail($event_id); + + $num_days= 20; + + /** + * This is a fairly hackish way to get the data for the dashboard charts. I'm sure someone + * with better SQL skill could do it in one simple query. + * + * Filling in the missing days here seems to be fast(ish) (with 20 days history), but the work + * should be done in the DB + */ + $chartData = EventStats::where('event_id', '=', $event->id) + ->where('date', '>', Carbon::now()->subDays($num_days)->format('Y-m-d')) + ->get() + ->toArray(); + + $startDate = new DateTime("-$num_days days"); + $dateItter = new DatePeriod( + $startDate, new DateInterval('P1D'), $num_days + ); + + $original = $chartData; + + /* + * I have no idea what I was doing here, but it seems to work; + */ + $result = array(); + $i = 0; + foreach ($dateItter as $date) { + $views = 0; + $sales_volume = 0; + $unique_views = 0; + $tickets_sold = 0; + $organiser_fees_volume = 0; + + foreach ($original as $item) { + if ($item['date'] == $date->format('Y-m-d')) { + $views = $item['views']; + $sales_volume = $item['sales_volume']; + $organiser_fees_volume = $item['organiser_fees_volume']; + $unique_views = $item['unique_views']; + $tickets_sold = $item['tickets_sold']; + + } + $i++; + } + + $result[] = array( + "date" => $date->format('Y-m-d'), + "views" => $views, + 'unique_views' => $unique_views, + 'sales_volume' => $sales_volume + $organiser_fees_volume, + 'tickets_sold' => $tickets_sold + ); + } + + $data = [ + 'event' => $event, + 'chartData' => json_encode($result) + ]; + + return View::make('ManageEvent.Dashboard', $data); + } + + /** + * @param $chartData + * @param bool|FALSE $from_date + * @param bool|FALSE $toDate + * @return string + */ + public function generateChartJson($chartData, $from_date = FALSE, $toDate = FALSE) { + + $data = []; + + $startdate = '2014-10-1'; + $enddate = '2014-11-7'; + $timestamp = strtotime($startdate); + while ($startdate <= $enddate) { + + $startdate = date('Y-m-d', $timestamp); + + $data[] = [ + 'date' => $startdate, + 'tickets_sold' => rand(0, 7), + 'views' => rand(0, 5), + 'unique_views' => rand(0, 5) + ]; + + + $timestamp = strtotime('+1 days', strtotime($startdate)); + } + + return json_encode($data); + } + +} diff --git a/app/Http/Controllers/EventOrdersController.php b/app/Http/Controllers/EventOrdersController.php new file mode 100644 index 00000000..cd01f730 --- /dev/null +++ b/app/Http/Controllers/EventOrdersController.php @@ -0,0 +1,345 @@ +find($event_id); + + if ($searchQuery) { + + /* + * Strip the hash from the start of the search term in case people search for + * order references like '#EDGC67' + */ + if ($searchQuery[0] === '#') { + $searchQuery = str_replace('#', '', $searchQuery); + } + + $orders = $event->orders() + ->where(function ($query) use ($searchQuery) { + $query->where('order_reference', 'like', $searchQuery . '%') + ->orWhere('first_name', 'like', $searchQuery . '%') + ->orWhere('email', 'like', $searchQuery . '%') + ->orWhere('last_name', 'like', $searchQuery . '%'); + }) + ->orderBy($sort_by, $sort_order) + ->paginate(); + } else { + $orders = $event->orders()->orderBy($sort_by, $sort_order)->paginate(); + } + + $data = [ + 'orders' => $orders, + 'event' => $event, + 'sort_by' => $sort_by, + 'sort_order' => $sort_order, + 'q' => $searchQuery ? $searchQuery : '' + ]; + + return View::make('ManageEvent.Orders', $data); + } + + public function manageOrder($order_id) + { + + $data = [ + 'order' => Order::scope()->find($order_id), + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.ManageOrder', $data); + } + + public function showCancelOrder($order_id) + { + $order = Order::scope()->find($order_id); + + $data = [ + 'order' => $order, + 'event' => $order->event(), + 'attendees' => $order->attendees()->withoutCancelled()->get(), + 'modal_id' => Input::get('modal_id') + ]; + + return View::make('ManageEvent.Modals.CancelOrder', $data); + } + + /** + * @param $order_id + * @return mixed + */ + public function postCancelOrder($order_id) + { + + $rules = [ + 'refund_amount' => ['numeric'] + ]; + $messages = [ + 'refund_amount.integer' => 'Refund amount must only contain numbers.', + ]; + + $validator = Validator::make(Input::all(), $rules, $messages); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $order = Order::scope()->findOrFail($order_id); + $refund_order = (Input::get('refund_order') === 'on') ? TRUE : FALSE; + $refund_type = Input::get('refund_type'); + $refund_amount = Input::get('refund_amount'); + $attendees = Input::get('attendees'); + $error_message = FALSE; + + + if ($refund_order) { + + if (!$order->transaction_id) { + $error_message = 'Sorry, this order cannot be refunded.'; + } + + if ($order->is_refunded) { + $error_message = 'This order has already been refunded'; + } elseif ($order->amount == 0) { + $error_message = 'Nothing to refund'; + } elseif ($refund_amount > ($order->amount - $order->amount_refunded)) { + $error_message = 'The maximum amount you can refund is ' . (money($order->amount - $order->amount_refunded, $order->event->currency->code)); + } + if (!$error_message) { + try { + + Stripe::setApiKey($order->account->stripe_api_key); + $charge = Stripe_Charge::retrieve($order->transaction_id); + + + if ($refund_type === 'full') { /* Full refund */ + + $refund_amount = $order->amount - $order->amount_refunded; + $refund = $charge->refund([ + 'refund_application_fee' => floatval($order->booking_fee) > 0 ? true : false + ]); + + /* Update the event sales volume*/ + $order->event->decrement('sales_volume', $refund_amount); + + $order->is_refunded = 1; + $order->amount_refunded = $order->amount; + $order->order_status_id = ORDER_REFUNDED; + + + + } else { /* Partial refund */ + + $refund = $charge->refund([ + 'amount' => $refund_amount * 100, + 'refund_application_fee' => floatval($order->booking_fee) > 0 ? true : false + ]); + + /* Update the event sales volume*/ + $order->event->decrement('sales_volume', $refund_amount); + + $order->order_status_id = ORDER_PARTIALLY_REFUNDED; + + if (($order->organiser_amount - $order->amount_refunded) == 0) { + $order->is_refunded = 1; + $order->order_status_id = ORDER_REFUNDED; + } + + $order->is_partially_refunded = 1; + } + $order->amount_refunded = round($refund->amount_refunded / 100, 2); + $order->save(); + + + } catch (\Stripe_InvalidRequestError $e) { + Log::error($e); + $error_message = 'There has been a problem processing your refund. Please check your information and try again.'; + } catch (\Stripe_AuthenticationError $e) { + Log::error($e); + $error_message = 'There has been a problem processing your refund. Please try again.'; + } catch (\Stripe_ApiConnectionError $e) { + Log::error($e); + $error_message = 'There has been a problem processing your refund. Please try again.'; + } catch (\Stripe_Error $e) { + Log::error($e); + $error_message = 'There has been a problem processing your refund. Please try again.'; + } catch (Exception $e) { + Log::error($e); + $error_message = 'There has been a problem processing your refund. Please try again.'; + } + } + + if ($error_message) { + return Response::json([ + 'status' => 'success', + 'message' => $error_message + ]); + } + } + + /* + * Cancel the attendees + */ + if ($attendees) { + foreach ($attendees as $attendee_id) { + $attendee = Attendee::scope()->where('id', '=', $attendee_id)->first(); + $attendee->is_cancelled = 1; + $attendee->save(); + } + } + + \Session::flash('message', + (!$refund_amount && !$attendees) ? 'Nothing To Do' : "Successfully " . ($refund_order ? " Refunded Order" : " ") . ($attendees && $refund_order ? ' & ' : '') . ($attendees ? "Cancelled Attendee(s)" : "")); + + return Response::json([ + 'status' => 'success', + 'redirectUrl' => '' + ]); + } + + /** + * @param $event_id + * @param string $export_as Accepted: xls, xlsx, csv, pdf, html + */ + public function showExportOrders($event_id, $export_as = 'xls') + { + + $event = Event::scope()->findOrFail($event_id); + + Excel::create('orders-as-of-' . date('d-m-Y-g.i.a'), function ($excel) use ($event) { + + $excel->setTitle('Orders For Event: ' . $event->title); + + // Chain the setters + $excel->setCreator(APP_NAME) + ->setCompany(APP_NAME); + + $excel->sheet('orders_sheet_1', function ($sheet) use ($event) { + + \DB::connection()->setFetchMode(\PDO::FETCH_ASSOC); + $data = DB::table('orders') + ->where('orders.event_id', '=', $event->id) + ->where('orders.event_id', '=', $event->id) + ->select([ + 'orders.first_name', + 'orders.last_name', + 'orders.email', + 'orders.order_reference', + 'orders.amount', + \DB::raw("(CASE WHEN orders.is_refunded = 1 THEN 'YES' ELSE 'NO' END) AS `orders.is_refunded`"), + \DB::raw("(CASE WHEN orders.is_partially_refunded = 1 THEN 'YES' ELSE 'NO' END) AS `orders.is_partially_refunded`"), + 'orders.amount_refunded', + 'orders.created_at' + ])->get(); + //DB::raw("(CASE WHEN UNIX_TIMESTAMP(`attendees.arrival_time`) = 0 THEN '---' ELSE 'd' END) AS `attendees.arrival_time`")) + + $sheet->fromArray($data); + + $sheet->row(1, array( + 'First Name', 'Last Name', 'Email', 'Order Reference', 'Amount', 'Fully Refunded', 'Partially Refunded', 'Amount Refunded', 'Order Date' + )); + + // Set gray background on first row + $sheet->row(1, function ($row) { + $row->setBackground('#f5f5f5'); + }); + }); + })->export($export_as); + } + + public function showMessageOrder($order_id) + { + + $order = Order::scope()->findOrFail($order_id); + + $data = [ + 'order' => $order, + 'event' => $order->event, + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.MessageOrder', $data); + } + + public function postMessageOrder($order_id) + { + + $rules = [ + 'subject' => 'required|max:250', + 'message' => 'required|max:5000' + ]; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $order = Attendee::scope()->findOrFail($order_id); + + $data = [ + 'order' => $order, + 'message_content' => Input::get('message'), + 'subject' => Input::get('subject'), + 'event' => $order->event, + 'email_logo' => $order->event->organiser->full_logo_path + ]; + + Mail::send('Emails.messageOrder', $data, function ($message) use ($order, $data) { + $message->to($order->email, $order->full_name) + ->from(OUTGOING_EMAIL_NOREPLY, $order->event->organiser->name) + ->replyTo($order->event->organiser->email, $order->event->organiser->name) + ->subject($data['subject']); + }); + + /* Could bcc in the above? */ + if (Input::get('send_copy') == '1') { + Mail::send('Emails.messageOrder', $data, function ($message) use ($order, $data) { + $message->to($order->event->organiser->email) + ->from(OUTGOING_EMAIL_NOREPLY, $order->event->organiser->name) + ->replyTo($order->event->organiser->email, $order->event->organiser->name) + ->subject($data['subject'] . ' [Organiser copy]'); + }); + } + + + return Response::json(array( + 'status' => 'success', + 'message' => 'Message Successfully Sent' + )); + } + +} diff --git a/app/Http/Controllers/EventPromoteController.php b/app/Http/Controllers/EventPromoteController.php new file mode 100644 index 00000000..5524c41a --- /dev/null +++ b/app/Http/Controllers/EventPromoteController.php @@ -0,0 +1,14 @@ + Event::scope()->find($event_id) + ]; + + return View::make('ManageEvent.Promote', $data); + } + +} diff --git a/app/Http/Controllers/EventTicketQuestionsController.php b/app/Http/Controllers/EventTicketQuestionsController.php new file mode 100644 index 00000000..6c1a16d7 --- /dev/null +++ b/app/Http/Controllers/EventTicketQuestionsController.php @@ -0,0 +1,49 @@ + Event::scope()->findOrFail($event_id), + 'modal_id' => Input::get('modal_id'), + 'question_types' => QuestionType::all() + ]; + + return View::make('ManageEvent.Modals.ViewQuestions', $data); + } + + + public function postCreateQuestion($event_id) { + + $event = Event::findOrFail($event_id); + + $question = Question::createNew(FALSE, FALSE, TRUE); + $question->title = Input::get('title'); + $question->instructions = Input::get('instructions'); + $question->options = Input::get('options'); + $question->is_required = Input::get('title'); + $question->question_type_id = Input::get('question_type_id'); + $question->save(); + + + + $ticket_ids = Input::get('tickets'); + + foreach($ticket_ids as $ticket_id) { + Ticket::scope()->find($ticket_id)->questions()->attach($question->id); + } + + $event->questions()->attach($question->id); + + + return Response::json([ + 'status' => 'success', + 'message' => 'Successfully Created Question' + ]); + } + + + +} diff --git a/app/Http/Controllers/EventTicketsController.php b/app/Http/Controllers/EventTicketsController.php new file mode 100644 index 00000000..b44fcd10 --- /dev/null +++ b/app/Http/Controllers/EventTicketsController.php @@ -0,0 +1,196 @@ +findOrFail($event_id); + + $tickets = $searchQuery + ? $event->tickets()->where('title', 'like', '%' . $searchQuery . '%')->orderBy($sort_by, 'desc')->paginate(10) + : $event->tickets()->orderBy($sort_by, 'desc')->paginate(10); + + + $data = [ + 'event' => $event, + 'tickets' => $tickets, + 'sort_by' => $sort_by, + 'q' => $searchQuery ? $searchQuery : '' + ]; + + return View::make('ManageEvent.Tickets', $data); + } + + public function showEditTicket($event_id, $ticket_id) { + + $data = [ + 'event' => Event::scope()->find($event_id), + 'ticket' => Ticket::scope()->find($ticket_id), + 'modal_id' => Input::get('modal_id'), + ]; + + return View::make('ManageEvent.Modals.EditTicket', $data); + } + + public function showCreateTicket($event_id) { + return View::make('ManageEvent.Modals.CreateTicket', array( + 'modal_id' => Input::get('modal_id'), + 'event' => Event::find($event_id) + )); + } + + public function postCreateTicket($event_id) { + + $ticket = Ticket::createNew(); + + if (!$ticket->validate(Input::all())) { + + return Response::json(array( + 'status' => 'error', + 'messages' => $ticket->errors() + )); + } + + $ticket->event_id = $event_id; + $ticket->title = Input::get('title'); + $ticket->quantity_available = !Input::get('quantity_available') ? NULL : Input::get('quantity_available'); + $ticket->start_sale_date = Input::get('start_sale_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('start_sale_date')) : NULL; + $ticket->end_sale_date = Input::get('end_sale_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('end_sale_date')) : NULL; + $ticket->price = Input::get('price'); + $ticket->min_per_person = Input::get('min_per_person'); + $ticket->max_per_person = Input::get('max_per_person'); + $ticket->description = Input::get('description'); + $ticket->save(); + + \Session::flash('message', 'Successfully Created Ticket'); + + return Response::json(array( + 'status' => 'success', + 'id' => $ticket->id, + 'message' => 'Refreshing...', + 'redirectUrl' => route('showEventTickets', array( + 'event_id' => $event_id + )) + )); + } + + public function postPauseTicket() { + $ticket_id = Input::get('ticket_id'); + + $ticket = Ticket::scope()->find($ticket_id); + + $ticket->is_paused = ($ticket->is_paused == 1) ? 0 : 1; + + if ($ticket->save()) { + return Response::json([ + 'status' => 'success', + 'message' => 'Ticket Successfully Updated', + 'id' => $ticket->id + ]); + } + + Log::error('Ticket Failed to pause/resume', [ + 'ticket' => $ticket + ]); + + return Response::json([ + 'status' => 'error', + 'id' => $ticket->id, + 'message' => 'Whoops!, looks like something went wrong. Please try again.' + ]); + + } + + public function postDeleteTicket() { + + $ticket_id = Input::get('ticket_id'); + + $ticket = Ticket::scope()->find($ticket_id); + + if ($ticket->quantity_sold > 0) { + return Response::json([ + 'status' => 'error', + 'message' => 'Sorry, you can\'t delete this ticket as some have already been sold', + 'id' => $ticket->id + ]); + } + + if ($ticket->delete()) { + return Response::json([ + 'status' => 'success', + 'message' => 'Ticket Successfully Deleted', + 'id' => $ticket->id + ]); + } + + Log::error('Ticket Failed to delete', [ + 'ticket' => $ticket + ]); + + return Response::json([ + 'status' => 'error', + 'id' => $ticket->id, + 'message' => 'Whoops!, looks like something went wrong. Please try again.' + ]); + } + + public function postEditTicket($event_id, $ticket_id) { + + $ticket = Ticket::findOrFail($ticket_id); + + /* + * Override some vaidation rules + */ + $validation_rules['quantity_available'] = ['integer','min:'.($ticket->quantity_sold + $ticket->quantity_reserved)]; + $validation_messages['quantity_available.min'] = 'Quantity available can\'t be less the amount sold or reserved.'; + + $ticket->rules = $validation_rules + $ticket->rules; + $ticket->messages = $validation_messages + $ticket->messages; + + + if (!$ticket->validate(Input::all())) { + + return Response::json(array( + 'status' => 'error', + 'messages' => $ticket->errors() + )); + } + + $ticket->title = Input::get('title'); + $ticket->quantity_available = !Input::get('quantity_available') ? NULL : Input::get('quantity_available'); + $ticket->price = Input::get('price'); + $ticket->start_sale_date = Input::get('start_sale_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('start_sale_date')) : NULL; + $ticket->end_sale_date = Input::get('end_sale_date') ? Carbon::createFromFormat('d-m-Y H:i', Input::get('end_sale_date')) : NULL; + $ticket->description = Input::get('description'); + $ticket->min_per_person = Input::get('min_per_person'); + $ticket->max_per_person = Input::get('max_per_person'); + + + $ticket->save(); + + return Response::json(array( + 'status' => 'success', + 'id' => $ticket->id, + 'message' => 'Refreshing...', + 'redirectUrl' => route('showEventTickets', array( + 'event_id' => $event_id + )) + )); + } + +} diff --git a/app/Http/Controllers/EventViewController.php b/app/Http/Controllers/EventViewController.php new file mode 100644 index 00000000..eccf2d2e --- /dev/null +++ b/app/Http/Controllers/EventViewController.php @@ -0,0 +1,109 @@ +is_live) { + return View::make('Public.ViewEvent.EventNotLivePage'); + } + + $data = [ + 'event' => $event, + 'tickets' => $event->tickets()->orderBy('created_at', 'desc')->get(), + 'is_embedded' => 0 + ]; + /* + * Don't record stats if we're previewing the event page from the backend or if we own the event. + */ + if (!$preview || !Auth::check()) { + + $event_stats = new EventStats; + $event_stats->updateViewCount($event_id); + } + + /* + * See if there is an affiliate referral in the URL + */ + if ($affiliate_ref = \Input::get('ref')) { + + $affiliate_ref = preg_replace("/\W|_/", '', $affiliate_ref); + + if ($affiliate_ref) { + $affiliate = Affiliate::firstOrNew([ + 'name' => Input::get('ref'), + 'event_id' => $event_id, + 'account_id' => $event->account_id, + ]); + + ++$affiliate->visits; + + $affiliate->save(); + + Cookie::queue('affiliate_' . $event_id, $affiliate_ref, 60 * 24 * 60); + } + } + + return View::make('Public.ViewEvent.EventPage', $data); + } + + public function showEventHomePreview($event_id) + { + return showEventHome($event_id, TRUE); + } + + + public function postContactOrganiser($event_id) + { + + $rules = [ + 'name' => 'required', + 'email' => ['required', 'email'], + 'message' => ['required'] + ]; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Response::json(array( + 'status' => 'error', + 'messages' => $validator->messages()->toArray() + )); + } + + $event = Event::findOrFail($event_id); + + $data = [ + 'sender_name' => Input::get('name'), + 'sender_email' => Input::get('email'), + 'message_content' => strip_tags(Input::get('message')), + 'event' => $event + ]; + + Mail::send('Emails.messageOrganiser', $data, function ($message) use ($event, $data) { + $message->to($event->organiser->email, $event->organiser->name) + ->from(OUTGOING_EMAIL_NOREPLY, $data['sender_name']) + ->replyTo($data['sender_email'], $data['sender_name']) + ->subject('Message Regarding: ' . $event->title); + }); + + return Response::json(array( + 'status' => 'success', + 'message' => 'Message Successfully Sent' + )); + + + } +} + diff --git a/app/Http/Controllers/EventViewEmbeddedController.php b/app/Http/Controllers/EventViewEmbeddedController.php new file mode 100644 index 00000000..1ad6146c --- /dev/null +++ b/app/Http/Controllers/EventViewEmbeddedController.php @@ -0,0 +1,23 @@ + $event, + 'tickets' => $event->tickets()->orderBy('created_at', 'desc')->get(), + 'is_embedded' => '1' + ]; + + return View::make('Public.ViewEvent.Embedded.EventPage', $data); + } + +} diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php new file mode 100644 index 00000000..050a1300 --- /dev/null +++ b/app/Http/Controllers/ImageController.php @@ -0,0 +1,17 @@ +resize(320, 240); + + $img->insert('public/watermark.png'); + + $img->save('public/bar.jpg'); + } + +} diff --git a/app/Http/Controllers/InstallerController.php b/app/Http/Controllers/InstallerController.php new file mode 100644 index 00000000..bf633d54 --- /dev/null +++ b/app/Http/Controllers/InstallerController.php @@ -0,0 +1,136 @@ + 'success', + 'message' => 'Success, Your connection works!', + 'test' => 1 + ]); + } + + return Response::json([ + 'status' => 'error', + 'message' => 'Unable to connect! Please check your settings', + 'test' => 1 + ]); + } + + + $config = "APP_ENV=production\n" . + "APP_DEBUG=false\n" . + "APP_URL={$app_url}\n" . + "APP_KEY={$app_key}\n\n" . + "DB_TYPE=mysql\n" . + "DB_HOST={$database['host']}\n" . + "DB_DATABASE={$database['name']}\n" . + "DB_USERNAME={$database['username']}\n" . + "DB_PASSWORD={$database['password']}\n\n" . + "MAIL_DRIVER={$mail['driver']}\n" . + "MAIL_PORT={$mail['port']}\n" . + "MAIL_ENCRYPTION={$mail['encryption']}\n" . + "MAIL_HOST={$mail['host']}\n" . + "MAIL_USERNAME={$mail['username']}\n" . + "MAIL_FROM_NAME={$mail['from_name']}\n" . + "MAIL_FROM_ADDRESS={$mail['from_address']}\n" . + "MAIL_PASSWORD={$mail['password']}\n\n"; + + $fp = fopen(base_path()."/.env", 'w'); + fwrite($fp, $config); + fclose($fp); + + Artisan::call('migrate', array('--force' => true)); + if (Timezone::count() == 0) { + Artisan::call('db:seed', array('--force' => true)); + } + Artisan::call('optimize', array('--force' => true)); + + return Redirect::route('signup',['first_run' => 'yup']); + } + + + private function testDatabase($database) + { + Config::set('database.default', $database['type']); + Config::set("database.connections.mysql.host", $database['host']); + Config::set("database.connections.mysql.database", $database['name']); + Config::set("database.connections.mysql.username", $database['username']); + Config::set("database.connections.mysql.password", $database['password']); + + try { + DB::reconnect(); + $success = DB::connection()->getDatabaseName() ? 'yes' : 'no'; + } catch (Exception $e) { + return $e->getMessage(); + } + return $success; + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/ManageAccountController.php b/app/Http/Controllers/ManageAccountController.php new file mode 100644 index 00000000..ef66a1e6 --- /dev/null +++ b/app/Http/Controllers/ManageAccountController.php @@ -0,0 +1,160 @@ + Input::get('modal_id'), + 'account' => Account::find(Auth::user()->account_id), + 'timezones' => Timezone::lists('location', 'id'), + 'currencies' => Currency::lists('title', 'id') + ]; + + return View::make('ManageAccount.Modals.EditAccount', $data); + } + + public function showStripeReturn() { + + $error_message = "There was an error connecting your Stripe account. Please try again."; + + if (Input::get('error') || !Input::get('code')) { + //BugSnag::notifyError('Error Connecting to Stripe', Input::get('error')); + \Session::flash('message', $error_message); + + return redirect()->route('showEventsDashboard'); + } + + $request = [ + 'url' => 'https://connect.stripe.com/oauth/token', + 'params' => [ + + 'client_secret' => STRIPE_SECRET_KEY, //sk_test_iXk2Ky0DlhIcTcKMvsDa8iKI', + 'code' => Input::get('code'), + 'grant_type' => 'authorization_code' + ] + ]; + + $response = HttpClient::post($request); + + $content = $response->json(); + + if(isset($content->error) || !isset($content->access_token)) { + //BugSnag::notifyError('Error Connecting to Stripe', Input::get('error')); + \Session::flash('message', $error_message); + + return redirect()->route('showEventsDashboard'); + } + + $account = Account::find(\Auth::user()->account_id); + $account->stripe_access_token = $content->access_token; + $account->stripe_refresh_token = $content->refresh_token; + $account->stripe_publishable_key = $content->stripe_publishable_key; + $account->stripe_data_raw = json_encode($content); + $account->save(); + + \Session::flash('message', "You have successfully connected your Stripe account."); + return redirect()->route('showEventsDashboard'); + } + + public function postEditAccount() { + $account = Account::find(Auth::user()->account_id); + + if (!$account->validate(Input::all())) { + + return Response::json(array( + 'status' => 'error', + 'messages' => $account->errors() + )); + } + + $account->first_name = Input::get('first_name'); + $account->last_name = Input::get('last_name'); + $account->email = Input::get('email'); + $account->timezone_id = Input::get('timezone_id'); + $account->currency_id = Input::get('currency_id'); + $account->save(); + + return Response::json(array( + 'status' => 'success', + 'id' => $account->id, + 'message' => 'Account Successfully Updated' + )); + } + + public function postEditAccountPayment() { + $account = Account::find(Auth::user()->account_id); + + $account->stripe_publishable_key = Input::get('stripe_publishable_key'); + $account->stripe_secret_key = Input::get('stripe_secret_key'); + + $account->save(); + + return Response::json(array( + 'status' => 'success', + 'id' => $account->id, + 'message' => 'Payment Information Successfully Updated' + )); + + } + + public function postInviteUser() { + $rules = array( + 'email' => array('required', 'email', 'unique:users,email,NULL,id,account_id,'.Auth::user()->account_id), + ); + + $messages = array( + 'email.email' => 'Please enter a valid E-mail address.', + 'email.required' => 'E-mail address is required.', + 'email.unique' => 'E-mail already in use for this account.', + ); + + $validation = \Validator::make(Input::all(), $rules, $messages); + + if ($validation->fails()) { + return \Response::json([ + 'status' => 'error', + 'messages'=> $validation->messages()->toArray() + ]); + } + + $temp_password = str_random(8); + + $user = new User; + $user->email = Input::get('email'); + $user->password = \Hash::make($temp_password); + $user->account_id = Auth::user()->account_id; + $user->save(); + + $data = [ + 'user' => $user, + 'temp_password' => $temp_password, + 'inviter' => Auth::user() + ]; + + \Mail::send('Emails.inviteUser', $data, function($message) use ($data) { + $message->to($data['user']->email) + ->subject($data['inviter']->first_name.' '.$data['inviter']->last_name.' added you to an Attendize Ticketing account.'); + }); + + return Response::json([ + 'status' => 'success', + 'message'=> 'Success! '.$user->email.' has been sent further instructions.' + ]); + + } + +} diff --git a/app/Http/Controllers/MyBaseController.php b/app/Http/Controllers/MyBaseController.php new file mode 100644 index 00000000..86062f08 --- /dev/null +++ b/app/Http/Controllers/MyBaseController.php @@ -0,0 +1,42 @@ +get()); + } + + /** + * Setup the layout used by the controller. + * + * @return void + */ + protected function setupLayout() { + if (!is_null($this->layout)) { + $this->layout = View::make($this->layout); + } + } + + /** + * Returns data which is required in each view, optionally combined with additional data. + * + * @param int $event_id + * @param array $additional_data + * @return arrau + */ + public function getEventViewData($event_id, $additional_data = array()) { + return array_merge(array( + 'event' => Event::scope()->findOrFail($event_id) + ) + , $additional_data); + } + +} diff --git a/app/Http/Controllers/OrganiserController.php b/app/Http/Controllers/OrganiserController.php new file mode 100644 index 00000000..c95635da --- /dev/null +++ b/app/Http/Controllers/OrganiserController.php @@ -0,0 +1,127 @@ +where('title', 'like', '%' . $searchQuery . '%')->orderBy($sort_by, 'desc')->where('organiser_id', '=', $organiser_id)->paginate(12) + : Event::scope()->where('organiser_id', '=', $organiser_id)->orderBy($sort_by, 'desc')->paginate(12); + + + $data = [ + 'events' => $events, + 'organisers' => Organiser::scope()->orderBy('name')->get(), + 'current_organiser' => Organiser::scope()->find($organiser_id), + 'q' => $searchQuery ? $searchQuery : '', //Redundant + 'search' => [ + 'q' => $searchQuery ? $searchQuery : '', + 'sort_by' => $sort_by, + 'showPast' => Input::get('past') + ] + ]; + + return View::make('ManageEvents.OrganiserDashboard', $data); + } + + + public function showEditOrganiser($organiser_id) { + + $organiser = Organiser::scope()->findOrfail($organiser_id); + + return View::make('ManageEvents.Modals.EditOrganiser', [ + 'modal_id' => Input::get('modal_id'), + 'organiser' => $organiser + ]); + } + + public function showCreateOrganiser() { + + return View::make('ManageOrganiser.CreateOrganiser', [ + 'modal_id' => 'createOrganiser' + ]); + + return View::make('ManageEvents.Modals.CreateOrganiser', [ + 'modal_id' => Input::get('modal_id') + ]); + } + + public function postCreateOrganiser() { + $organiser = Organiser::createNew(FALSE, FALSE, TRUE); + + if (!$organiser->validate(Input::all())) { + return Response::json(array( + 'status' => 'error', + 'messages' => $organiser->errors() + )); + } + + $organiser->name = Input::get('name'); + $organiser->about = Input::get('about'); + $organiser->email = Input::get('email'); + $organiser->facebook = Input::get('facebook'); + $organiser->twitter = Input::get('twitter'); + $organiser->confirmation_key = md5(time().rand(0,999999)); + + if (Input::hasFile('organiser_logo')) { + + $path = public_path() . '/' . ORGANISER_IMAGES_PATH; + $filename = 'organiser_logo-' . $organiser->id . '.' . strtolower(Input::file('organiser_logo')->getClientOriginalExtension()); + + $file_full_path = $path . '/' . $filename; + + Input::file('organiser_logo')->move($path, $filename); + + $img = Image::make($file_full_path); + + $img->resize(250, 250, function ($constraint) { + //$constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($file_full_path); + + if(file_exists($file_full_path)) { + $organiser->logo_path = ORGANISER_IMAGES_PATH . '/' . $filename; + } + + } + $organiser->save(); + + \Session::flash('message', 'Successfully Created Organiser'); + + return Response::json(array( + 'status' => 'success', + 'message' => 'Refreshing..', + 'redirectUrl' => route('showOrganiserDashboard', [ + 'organiser_id' => $organiser->id + ]) + )); + } + + + + +} diff --git a/app/Http/Controllers/OrganiserCustomizeController.php b/app/Http/Controllers/OrganiserCustomizeController.php new file mode 100644 index 00000000..e14a587d --- /dev/null +++ b/app/Http/Controllers/OrganiserCustomizeController.php @@ -0,0 +1,88 @@ + Organiser::scope()->findOrFail($organiser_id) + ]; + + return View::make('ManageOrganiser.Customize', $data); + } + + public function postEditOrganiser($organiser_id) { + $organiser = Organiser::scope()->find($organiser_id); + + if (!$organiser->validate(Input::all())) { + return Response::json(array( + 'status' => 'error', + 'messages' => $organiser->errors() + )); + } + + $organiser->name = Input::get('name'); + $organiser->about = Input::get('about'); + $organiser->email = Input::get('email'); + $organiser->facebook = Input::get('facebook'); + $organiser->twitter = Input::get('twitter'); + + /* + * If the email has been changed the user must confirm the email. + */ + if($organiser->email !== Input::get('email')) { + $organiser->is_email_confirmed = 0; + } + + if (Input::get('remove_current_image') == '1') { + $organiser->logo_path = ''; + } + + if (Input::hasFile('organiser_logo') ) { + + $the_file = \File::get(Input::file('organiser_logo')->getRealPath()); + $file_name = '123-test-organiser_logo-' . $organiser->id . '.' . strtolower(Input::file('organiser_logo')->getClientOriginalExtension()); + + $relative_path_to_file = ORGANISER_IMAGES_PATH . '/' . $file_name; + $full_path_to_file = public_path().'/'.$relative_path_to_file; + + $img = Image::make($the_file); + + $img->resize(200, 200, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + $img->save($full_path_to_file); + + if(\Storage::put($file_name, $the_file)) { + $organiser->logo_path = $relative_path_to_file; + } + + } + + + $organiser->save(); + + Session::flash('message', 'Successfully Updated Organiser'); + + return Response::json(array( + 'status' => 'success', + 'redirectUrl' => route('showOrganiserCustomize', [ + 'organiser_id' => $organiser->id + ]) + )); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/OrganiserDashboardController.php b/app/Http/Controllers/OrganiserDashboardController.php new file mode 100644 index 00000000..e8663b24 --- /dev/null +++ b/app/Http/Controllers/OrganiserDashboardController.php @@ -0,0 +1,34 @@ +findOrFail($organiser_id); + $upcoming_events = $organiser->events()->where('end_date', '>=', Carbon::now())->get(); + + $data = [ + 'organiser' => $organiser, + 'upcoming_events' => $upcoming_events, + 'search' => [ + 'sort_by' => 's', + 'q' => '' + ], + 'q'=> 'dd' + ]; + + return View::make('ManageOrganiser.Dashboard', $data); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/OrganiserEventsController.php b/app/Http/Controllers/OrganiserEventsController.php new file mode 100644 index 00000000..670b568f --- /dev/null +++ b/app/Http/Controllers/OrganiserEventsController.php @@ -0,0 +1,39 @@ +findOrfail($organiser_id); + + $allowed_sorts = ['created_at', 'start_date', 'end_date', 'title']; + + $searchQuery = Input::get('q'); + $sort_by = (in_array(Input::get('sort_by'), $allowed_sorts) ? Input::get('sort_by') : 'start_date'); + + $events = $searchQuery + ? Event::scope()->where('title', 'like', '%' . $searchQuery . '%')->orderBy($sort_by, 'desc')->where('organiser_id', '=', $organiser_id)->paginate(12) + : Event::scope()->where('organiser_id', '=', $organiser_id)->orderBy($sort_by, 'desc')->paginate(12); + + + $data = [ + 'events' => $events, + 'organiser' => $organiser, + 'search' => [ + 'q' => $searchQuery ? $searchQuery : '', + 'sort_by' => Input::get('sort_by') ? Input::get('sort_by') : '', + 'showPast' => Input::get('past') + ] + ]; + + return View::make('ManageOrganiser.Events', $data); + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/OrganiserViewController.php b/app/Http/Controllers/OrganiserViewController.php new file mode 100644 index 00000000..e60125ff --- /dev/null +++ b/app/Http/Controllers/OrganiserViewController.php @@ -0,0 +1,27 @@ + $organiser, + 'tickets' => $organiser->events()->orderBy('created_at', 'desc')->get(), + 'is_embedded' => 0 + ]; + + return View::make('Public.ViewOrganiser.OrganiserPage', $data); + } + + public function showEventHomePreview($event_id) { + return showEventHome($event_id, TRUE); + } + +} diff --git a/app/Http/Controllers/RemindersController.php b/app/Http/Controllers/RemindersController.php new file mode 100644 index 00000000..a3e6f0b0 --- /dev/null +++ b/app/Http/Controllers/RemindersController.php @@ -0,0 +1,124 @@ +auth = $auth; + $this->passwords = $passwords; + + $this->middleware('guest'); + } + + /** + * Get the e-mail subject line to be used for the reset link email. + * + * @return string + */ + protected function getEmailSubject() + { + return isset($this->subject) ? $this->subject : 'Your Password Reset Link'; + } + + /** + * Display the password reminder view. + * + * @return Response + */ + public function getRemind() { + return \View::make('Public.LoginAndRegister.ForgotPassword'); + } + + /** + * Handle a POST request to remind a user of their password. + * + * @return Response + */ + public function postRemind(Request $request) { + + $this->validate($request, ['email' => 'required']); + + $response = $this->passwords->sendResetLink($request->only('email'), function($m) { + $m->subject($this->getEmailSubject()); + }); + + switch ($response) { + case PasswordBroker::RESET_LINK_SENT: + return redirect()->back()->with('status', trans($response)); + + case PasswordBroker::INVALID_USER: + return redirect()->back()->withErrors(['email' => trans($response)]); + } + } + + /** + * Display the password reset view for the given token. + * + * @param string $token + * @return Response + */ + public function getReset($token = null) { + if (is_null($token)) + \App::abort(404); + + return \View::make('Public.LoginAndRegister.ResetPassword')->with('token', $token); + } + + /** + * Handle a POST request to reset a user's password. + * + * @return Response + */ + public function postReset(Request $request) { + $this->validate($request, [ + 'token' => 'required', + 'email' => 'required', + 'password' => 'required|confirmed', + ]); + + $credentials = $request->only( + 'email', 'password', 'password_confirmation', 'token' + ); + + $response = $this->passwords->reset($credentials, function($user, $password) { + $user->password = bcrypt($password); + + $user->save(); + + $this->auth->login($user); + }); + + switch ($response) { + case PasswordBroker::PASSWORD_RESET: + \Session::flash('message', 'Password Successfully Reset'); + return redirect(route('login')); + + default: + return redirect()->back() + ->withInput($request->only('email')) + ->withErrors(['email' => trans($response)]); + } + } + +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 00000000..441bc1c9 --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,71 @@ + \Auth::user(), + 'modal_id' => \Input::get('modal_id') + ]; + + return \View::make('ManageUser.Modals.EditUser', $data); + } + + public function postEditUser() { + + $rules = array( + 'email' => ['required', 'email', 'exists:users,email,account_id,' . Auth::user()->account_id], + 'new_password' => ['min:5', 'confirmed', 'required_with:password'], + 'password' => 'passcheck', + 'first_name' => ['required'], + 'last_name' => ['required'] + ); + + $messages = [ + 'email.email' => 'Please enter a valid E-mail address.', + 'email.required' => 'E-mail address is required.', + 'password.passcheck' => 'This password is incorrect.', + 'email.exists' => 'This E-mail has is already in use.', + 'first_name.required' => 'Please enter your first name.' + ]; + + $validation = \Validator::make(Input::all(), $rules, $messages); + + if ($validation->fails()) { + return Response::json([ + 'status' => 'error', + 'messages' => $validation->messages()->toArray() + ]); + } + + $user = Auth::user(); + + if (Input::get('password')) { + $user->password = \Hash::make(Input::get('new_password')); + } + + $user->first_name = Input::get('first_name'); + $user->last_name = Input::get('last_name'); + + + + //$user->email = Input::get('email'); + $user->save(); + + return Response::json([ + 'status' => 'success', + 'message' => 'Successfully Edited User' + ]); + } + +} diff --git a/app/Http/Controllers/UserLoginController.php b/app/Http/Controllers/UserLoginController.php new file mode 100644 index 00000000..d4fa249d --- /dev/null +++ b/app/Http/Controllers/UserLoginController.php @@ -0,0 +1,58 @@ +auth = $auth; + $this->middleware('guest'); + } + + public function showLogin() { + + /* + * If there's an ajax request to the login page assume the person has been + * logged out and redirect them to the login page + */ + if (Request::ajax()) { + return Response::json(array( + 'status' => 'success', + 'redirectUrl' => route('login') + )); + } + + return View::make('Public.LoginAndRegister.Login'); + } + + /** + * Handle the login + * + * @return void + */ + public function postLogin() { + + $email = Input::get('email'); + $password = Input::get('password'); + + if ($this->auth->attempt(array('email' => $email, 'password' => $password), true)) { + return Redirect::to(route('showSelectOrganiser')); + } + return Redirect::to('login?failed=yup')->with('message', 'Your username/password combination was incorrect') + ->withInput(); + } + + + + +} diff --git a/app/Http/Controllers/UserLogoutController.php b/app/Http/Controllers/UserLogoutController.php new file mode 100644 index 00000000..6e885552 --- /dev/null +++ b/app/Http/Controllers/UserLogoutController.php @@ -0,0 +1,22 @@ +auth = $auth; + } + + + public function doLogout() { + $this->auth->logout(); + return \Redirect::to('/?logged_out=yup'); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/UserSignupController.php b/app/Http/Controllers/UserSignupController.php new file mode 100644 index 00000000..b20254b4 --- /dev/null +++ b/app/Http/Controllers/UserSignupController.php @@ -0,0 +1,127 @@ +auth = $auth; + $this->middleware('guest'); + } + + public function showSignup() { + + if(Account::count() > 0 && Utils::isAttendize()) { + return Redirect::route('login'); + } + + return View::make('Public.LoginAndRegister.Signup'); + } + + + /* + * Validate an email + */ + + /** + * Creates an account + * + * @return void + */ + public function postSignup() { + $rules = array( + 'email' => array('required', 'email', 'unique:users'), + 'password' => array('required', 'min:5', 'confirmed'), + 'first_name' => array('required'), + 'terms_agreed' => array('required') + ); + + $messages = array( + 'email.email' => 'Please enter a valid E-mail address.', + 'email.required' => 'E-mail address is required.', + 'password.required' => 'Password is required.', + 'password.min' => 'Your password is too short! Min 5 symbols.', + 'email.unique' => 'This E-mail has already been taken.', + 'first_name.required' => 'Please enter your first name.', + 'terms_agreed.required' => 'Please agree to our Terms of Service.' + ); + + $validation = Validator::make(Input::all(), $rules, $messages); + + if ($validation->fails()) { + return Redirect::to('signup')->withInput()->withErrors($validation); + } + + $account = new Account; + $account->email = Input::get('email'); + $account->first_name = Input::get('first_name'); + $account->last_name = Input::get('last_name'); + $account->currency_id = DEFAULT_CURRENCY; + $account->timezone_id = DEFAULT_TIMEZONE; + $account->save(); + + $user = new User; + $user->email = Input::get('email'); + $user->first_name = Input::get('first_name'); + $user->last_name = Input::get('last_name'); + $user->password = Hash::make(Input::get('password')); + $user->account_id = $account->id; + $user->is_parent = 1; + $user->is_registered = 1; + $user->save(); + + + /* + * Send a confirmation email. + * NOTE: $user->confirmation_code is generated by the model in the background + */ + \Mail::send('Emails.ConfirmEmail', ['first_name' => $user->first_name, 'confirmation_code' => $user->confirmation_code], function($message) { + $message->to(Input::get('email'), Input::get('first_name')) + ->subject('Thank you for registering for Attendize'); + }); + + \Session::flash('message', "Success! You can now login."); + + return Redirect::to('login'); + } + + function confirmEmail($confirmation_code) { + + $user = User::whereConfirmationCode($confirmation_code)->first(); + + if ( ! $user) + { + return \View::make('Public.Errors.Generic', [ + 'message' => 'The confirmation code is missing or malformed.' + ]); + } + + $user->is_confirmed = 1; + $user->confirmation_code = null; + $user->save(); + + \Session::flash('message', "Success! Your email is now verified. You can now login."); + + //$this->auth->login($user); + + return Redirect::route('login'); + } + + private function validateEmail($data) + { + $rules = [ + 'email' => 'required|email|unique:users' + ]; + + return Validator::make($data, $rules); + } + + +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php new file mode 100644 index 00000000..9eec1e98 --- /dev/null +++ b/app/Http/Kernel.php @@ -0,0 +1,34 @@ + 'App\Http\Middleware\Authenticate', + 'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth', + 'guest' => 'App\Http\Middleware\RedirectIfAuthenticated', + 'first.run' => 'App\Http\Middleware\FirstRunMiddleware' + ]; + +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 00000000..6cdaef1c --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,50 @@ +auth = $auth; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + if ($this->auth->guest()) + { + if ($request->ajax()) + { + return response('Unauthorized.', 401); + } + else + { + return redirect()->guest('login'); + } + } + + return $next($request); + } + +} diff --git a/app/Http/Middleware/FirstRunMiddleware.php b/app/Http/Middleware/FirstRunMiddleware.php new file mode 100644 index 00000000..3d1c09d8 --- /dev/null +++ b/app/Http/Middleware/FirstRunMiddleware.php @@ -0,0 +1,44 @@ +count() === 0 && !($request->route()->getName() == 'showCreateOrganiser') && !($request->route()->getName() == 'postCreateOrganiser')) { + return redirect(route('showCreateOrganiser', [ + 'first_run' => '1' + ])); + } elseif (Organiser::scope()->count() === 1 && ($request->route()->getName() == 'showSelectOrganiser')) { + return redirect(route('showOrganiserDashboard', [ + 'organiser_id' => Organiser::scope()->first()->id + ])); + } + + $response = $next($request); + + return $response; + } +} \ No newline at end of file diff --git a/app/Http/Middleware/GeneralChecks.php b/app/Http/Middleware/GeneralChecks.php new file mode 100644 index 00000000..26851223 --- /dev/null +++ b/app/Http/Middleware/GeneralChecks.php @@ -0,0 +1,27 @@ +auth = $auth; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + if ($this->auth->check()) + { + return new RedirectResponse(route('showSelectOrganiser')); + } + + return $next($request); + } + +} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 00000000..3d701ec3 --- /dev/null +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,25 @@ + 'showInstaller', + 'uses' => 'InstallerController@showInstaller' +]); +Route::post('install', [ + 'as' => 'postInstaller', + 'uses' => 'InstallerController@postInstaller' +]); + +/* + * Stripe connect return + */ +Route::any('payment/return/stripe', [ + 'as' => 'showStripeReturn', + 'uses' => 'ManageAccountController@showStripeReturn' +]); + + + +/* + * Login + */ +Route::get('/login', [ + 'as' => 'login', + 'uses' => 'UserLoginController@showLogin' +]); +Route::post('/login', 'UserLoginController@postLogin'); + +/* + * Forgot password + */ +Route::get('login/forgot-password', [ + 'as' => 'forgotPassword', + 'uses' => 'RemindersController@getRemind' +]); + +Route::post('login/forgot-password', [ + 'as' => 'postForgotPassword', + 'uses' => 'RemindersController@postRemind' +]); + +/* + * Reset Password + */ +Route::get('login/reset-password/{token}', [ + 'as' => 'showResetPassword', + 'uses' => 'RemindersController@getReset' +]); + +Route::post('login/reset-password', [ + 'as' => 'postResetPassword', + 'uses' => 'RemindersController@postReset' +]); + + +/* + * Logout + */ +Route::any('/logout', 'UserLogoutController@doLogout'); + + +/* + * Registration / Account creation + */ +Route::get('/signup', 'UserSignupController@showSignup'); +Route::post('/signup', 'UserSignupController@postSignup'); + +/* + * Confirm Email + */ +Route::get('signup/confirm_email/{confirmation_code}', [ + 'as' => 'confirmEmail', + 'uses' => 'UserSignupController@confirmEmail' +]); + +/* + * Public organiser page routes + */ +Route::group(['prefix' => 'o'], function() { + + Route::get('/{organiser_id}/{organier_slug?}', [ + 'as' => 'showOrganiserHome', + 'uses' => 'OrganiserViewController@showOrganiserHome' + ]); + +}); + + +/* + * Public event page routes + */ +Route::group(array('prefix' => 'e'), function() { + + + + /* + * Embedded events + */ + Route::get('/{event_id}/embed', [ + 'as' => 'showEmbeddedEventPage', + 'uses' => 'EventViewEmbeddedController@showEmbeddedEvent' + ]); + + Route::get('/{event_id}/{event_slug?}', [ + 'as' => 'showEventPage', + 'uses' => 'EventViewController@showEventHome' + ]); + + Route::post('/{event_id}/contact_organiser', [ + 'as' => 'postContactOrganiser', + 'uses' => 'EventViewController@postContactOrganiser' + ]); + + /* + * Used for previewing designs in the backend. Doesn't log page views etc. + */ + Route::get('/{event_id}/preview', [ + 'as' => 'showEventPagePreview', + 'uses' => 'EventViewController@showEventHomePreview' + ]); + + Route::post('{event_id}/checkout/', [ + 'as' => 'postValidateTickets', + 'uses' => 'EventCheckoutController@postValidateTickets' + ]); + + Route::get('{event_id}/checkout/create', [ + 'as' => 'showEventCheckout', + 'uses' => 'EventCheckoutController@showEventCheckout' + ]); + + Route::post('{event_id}/checkout/create', [ + 'as' => 'postCreateOrder', + 'uses' => 'EventCheckoutController@postCreateOrder' + ]); +}); + + + +/* + * View order + */ +Route::get('order/{order_reference}', [ + 'as' => 'showOrderDetails', + 'uses' => 'EventCheckoutController@showOrderDetails' +]); + +Route::get('order/{order_reference}/tickets', [ + 'as' => 'showOrderTickets', + 'uses' => 'EventCheckoutController@showOrderTickets' +]); + + +/* + * Begin logged in stuff + */ +Route::group(array('middleware' => ['auth', 'first.run']), function() { + + + + + + /* + * Edit User + */ + Route::group(['prefix' => 'user'], function() { + + Route::get('/', [ + 'as' => 'showEditUser', + 'uses' => 'UserController@showEditUser' + ]); + Route::post('/', [ + 'as' => 'postEditUser', + 'uses' => 'UserController@postEditUser' + ]); + + }); + + /* + * Manage account + */ + Route::group(array('prefix' => 'account'), function() { + + Route::get('/', [ + 'as' => 'showEditAccount', + 'uses' => 'ManageAccountController@showEditAccount' + ]); + + Route::post('/', [ + 'as' => 'postEditAccount', + 'uses' => 'ManageAccountController@postEditAccount' + ]); + Route::post('/edit_payment', [ + 'as' => 'postEditAccountPayment', + 'uses' => 'ManageAccountController@postEditAccountPayment' + ]); + + Route::post('invite_user', [ + 'as' => 'postInviteUser', + 'uses' => 'ManageAccountController@postInviteUser' + ]); + + }); + + + + Route::get('select_organiser', [ + 'as' => 'showSelectOrganiser', + 'uses' => 'OrganiserController@showSelectOragniser' + ]); + + /* + * New organiser dashboard + */ + Route::group(array('prefix' => 'organiser'), function() { + /* + * ----------- + * Organiser Dashboard + * ----------- + */ + Route::get('{organiser_id}/dashboard', [ + 'as' => 'showOrganiserDashboard', + 'uses' => 'OrganiserDashboardController@showDashboard' + ]); + + /* + * ----------- + * Organiser events + * ----------- + */ + Route::get('{organiser_id}/events', [ + 'as' => 'showOrganiserEvents', + 'uses' => 'OrganiserEventsController@showEvents' + ]); + /* + * ----------- + * Organiser events + * ----------- + */ + Route::get('{organiser_id}/customize', [ + 'as' => 'showOrganiserCustomize', + 'uses' => 'OrganiserCustomizeController@showCustomize' + ]); + }); + + /* + * Events dashboard + */ + Route::group(array('prefix' => 'events'), function() { + + + + /* + * ----------- + * Events Dashboard - Organisers + * ----------- + */ + Route::get('/organiser/create', [ + 'as' => 'showCreateOrganiser', + 'uses' => 'OrganiserController@showCreateOrganiser' + ]); + Route::post('/organiser/create', [ + 'as' => 'postCreateOrganiser', + 'uses' => 'OrganiserController@postCreateOrganiser' + ]); + Route::get('/organiser/{organiser_id}', [ + 'as' => 'showOrganiserEventsDashboard', + 'uses' => 'OrganiserController@showOrganiserDashboard' + ]); + Route::get('/organiser/{organiser_id?}', [ + 'as' => 'showSearchEventsDashboard', + 'uses' => 'OrganiserController@showOrganiserDashboard' + ]); + Route::get('/organiser/{organiser_id}/edit', [ + 'as' => 'showEditOrganiser', + 'uses' => 'OrganiserController@showEditOrganiser' + ]); + Route::post('/organiser/{organiser_id}/edit', [ + 'as' => 'postEditOrganiser', + 'uses' => 'OrganiserCustomizeController@postEditOrganiser' + ]); + + + + + + /* + * ---------- + * Create Event + * ---------- + */ + Route::get('/create', [ + 'as' => 'showCreateEvent', + 'uses' => 'EventController@showCreateEvent' + ]); + + Route::post('/create', [ + 'as' => 'postCreateEvent', + 'uses' => 'EventController@postCreateEvent' + ]); + }); + + /* + * Upoad event images + */ + Route::post('/upload_image', [ + 'as' => 'postUploadEventImage', + 'uses' => 'EventController@postUploadEventImage' + ]); + + + + /* + * Event Management Stuff + */ + Route::group(array('prefix' => 'event'), function() { + + /* + * ------- + * Dashboard + * ------- + */ + Route::get('{event_id}/dashboard/', array( + 'as' => 'showEventDashboard', + 'uses' => 'EventDashboardController@showDashboard') + ); + + Route::get('{event_id}', function($event_id) { + return Redirect::route('showEventDashboard', [ + 'event_id' => $event_id + ]); + }); + + /** + * @todo Move to a controller + */ + Route::get('{event_id}/go_live', ['as' => 'MakeEventLive', function($event_id) { + $event = \App\Models\Event::scope()->findOrFail($event_id); + $event->is_live = 1; + $event->save(); + \Session::flash('message', 'Event Successfully Made Live! You can undo this action in event settings page.'); + return Redirect::route('showEventDashboard', [ + 'event_id' => $event_id + ]); + }]); + + /* + * ------- + * Tickets + * ------- + */ + Route::get('{event_id}/tickets/', array( + 'as' => 'showEventTickets', + 'uses' => 'EventTicketsController@showTickets' + )); + Route::get('{event_id}/tickets/edit/{ticket_id}', array( + 'as' => 'showEditTicket', + 'uses' => 'EventTicketsController@showEditTicket' + )); + Route::post('{event_id}/tickets/edit/{ticket_id}', array( + 'as' => 'postEditTicket', + 'uses' => 'EventTicketsController@postEditTicket' + )); + Route::get('{event_id}/tickets/create', array( + 'as' => 'showCreateTicket', + 'uses' => 'EventTicketsController@showCreateTicket' + )); + Route::post('{event_id}/tickets/create', array( + 'as' => 'postCreateTicket', + 'uses' => 'EventTicketsController@postCreateTicket' + )); + Route::post('{event_id}/tickets/delete', array( + 'as' => 'postDeleteTicket', + 'uses' => 'EventTicketsController@postDeleteTicket' + )); + Route::post('{event_id}/tickets/pause', array( + 'as' => 'postPauseTicket', + 'uses' => 'EventTicketsController@postPauseTicket' + )); + + /* + * Ticket questions + */ + Route::get('{event_id}/tickets/questions', [ + 'as' => 'showTicketQuestions', + 'uses' => 'EventTicketQuestionsController@showQuestions' + ]); + Route::post('{event_id}/tickets/questions/create', [ + 'as' => 'postCreateQuestion', + 'uses' => 'EventTicketQuestionsController@postCreateQuestion' + ]); + + + + + /* + * ------- + * Attendees + * ------- + */ + Route::get('{event_id}/attendees/', array( + 'as' => 'showEventAttendees', + 'uses' => 'EventAttendeesController@showAttendees' + )); + + Route::get('{event_id}/attendees/message', [ + 'as' => 'showMessageAttendees', + 'uses' => 'EventAttendeesController@showMessageAttendees' + ]); + + Route::post('{event_id}/attendees/message', [ + 'as' => 'postMessageAttendees', + 'uses' => 'EventAttendeesController@postMessageAttendees' + ]); + + Route::get('{event_id}/attendees/single_message', [ + 'as' => 'showMessageAttendee', + 'uses' => 'EventAttendeesController@showMessageAttendee' + ]); + + Route::post('{event_id}/attendees/single_message', [ + 'as' => 'postMessageAttendee', + 'uses' => 'EventAttendeesController@postMessageAttendee' + ]); + + + Route::get('{event_id}/attendees/create', [ + 'as' => 'showCreateAttendee', + 'uses' => 'EventAttendeesController@showCreateAttendee' + ]); + + Route::post('{event_id}/attendees/create', [ + 'as' => 'postCreateAttendee', + 'uses' => 'EventAttendeesController@postCreateAttendee' + ]); + + Route::get('{event_id}/attendees/print', [ + 'as' => 'showPrintAttendees', + 'uses' => 'EventAttendeesController@showPrintAttendees' + ]); + + Route::get('{event_id}/attendees/export/{export_as?}', [ + 'as' => 'showExportAttendees', + 'uses' => 'EventAttendeesController@showExportAttendees' + ]); + + Route::get('{event_id}/attendees/{attendee_id}/edit', [ + 'as' => 'showEditAttendee', + 'uses' => 'EventAttendeesController@showEditAttendee' + ]); + Route::post('{event_id}/attendees/{attendee_id}/edit', [ + 'as' => 'postEditAttendee', + 'uses' => 'EventAttendeesController@postEditAttendee' + ]); + + Route::get('{event_id}/attendees/{attendee_id}/cancel', [ + 'as' => 'showCancelAttendee', + 'uses' => 'EventAttendeesController@showCancelAttendee' + ]); + Route::post('{event_id}/attendees/{attendee_id}/cancel', [ + 'as' => 'postCancelAttendee', + 'uses' => 'EventAttendeesController@postCancelAttendee' + ]); + + + /* + * ------- + * Orders + * ------- + */ + Route::get('{event_id}/orders/', array( + 'as' => 'showEventOrders', + 'uses' => 'EventOrdersController@showOrders' + )); + + Route::get('order/{order_id}', array( + 'as' => 'showManageOrder', + 'uses' => 'EventOrdersController@manageOrder' + )); + + Route::get('order/{order_id}/cancel', array( + 'as' => 'showCancelOrder', + 'uses' => 'EventOrdersController@showCancelOrder' + )); + + Route::post('order/{order_id}/cancel', array( + 'as' => 'postCancelOrder', + 'uses' => 'EventOrdersController@postCancelOrder' + )); + + Route::get('{event_id}/orders/export/{export_as?}', [ + 'as' => 'showExportOrders', + 'uses' => 'EventOrdersController@showExportOrders' + ]); + Route::get('{event_id}/orders/message', [ + 'as' => 'showMessageOrder', + 'uses' => 'EventOrdersController@showMessageOrder' + ]); + + Route::post('{event_id}/orders/message', [ + 'as' => 'postMessageOrder', + 'uses' => 'EventOrdersController@postMessageOrder' + ]); + + /* + * ------- + * Edit Event + * ------- + */ + Route::post('{event_id}/customize', array( + 'as' => 'postEditEvent', + 'uses' => 'EventController@postEditEvent' + )); + + /* + * ------- + * Customize Design etc. + * ------- + */ + Route::get('{event_id}/customize', array( + 'as' => 'showEventCustomize', + 'uses' => 'EventCustomizeController@showCustomize' + )); + + Route::get('{event_id}/customize/{tab?}', array( + 'as' => 'showEventCustomizeTab', + 'uses' => 'EventCustomizeController@showCustomize' + )); + + + + Route::post('{event_id}/customize/order_page', array( + 'as' => 'postEditEventOrderPage', + 'uses' => 'EventCustomizeController@postEditEventOrderPage' + )); + Route::post('{event_id}/customize/design', array( + 'as' => 'postEditEventDesign', + 'uses' => 'EventCustomizeController@postEditEventDesign' + )); + Route::post('{event_id}/customize/social', array( + 'as' => 'postEditEventSocial', + 'uses' => 'EventCustomizeController@postEditEventSocial' + )); + + Route::post('{event_id}/customize/fees', array( + 'as' => 'postEditEventFees', + 'uses' => 'EventCustomizeController@postEditEventFees' + )); + + + + + /* + * ------- + * Check In App + * ------- + */ + Route::get('{event_id}/check_in', array( + 'as' => 'showChechIn', + 'uses' => 'EventCheckInController@showCheckIn' + )); + Route::post('{event_id}/check_in/search', array( + 'as' => 'postCheckInSearch', + 'uses' => 'EventCheckInController@postCheckInSearch' + )); + Route::post('{event_id}/check_in/', array( + 'as' => 'postCheckInAttendee', + 'uses' => 'EventCheckInController@postCheckInAttendee' + )); + + + + + + /* + * ------- + * Promote + * ------- + */ + Route::get('{event_id}/promote', array( + 'as' => 'showEventPromote', + 'uses' => 'EventPromoteController@showPromote' + )); + }); +}); + + +Route::post('queue/push', function() { + + // set_time_limit(300); + + return Queue::marshal(); +}); + + +Route::get('/', function() { + + return Redirect::route('showSelectOrganiser'); +}); + + +Route::get('/terms_and_conditions', ['as' => 'termsAndConditions', function() { + +return 'TODO: add terms and cond'; +//return View::make('Public.Website.Terms_and_Cond'); +}]); + + diff --git a/app/Models/Account.php b/app/Models/Account.php new file mode 100644 index 00000000..9c38fa1b --- /dev/null +++ b/app/Models/Account.php @@ -0,0 +1,38 @@ + ['required'], + 'last_name' => ['required'], + 'email' => ['required', 'email'] + ]; + + protected $messages = []; + + public function users() { + return $this->hasMany('\App\Models\User'); + } + + public function orders() { + return $this->hasMany('\App\Models\Order'); + } + + public function currency() { + return $this->hasOne('\App\Models\Currency'); + } + + public function getStripeApiKeyAttribute() { + if(Utils::isAttendize()) { + return $this->stripe_access_token; + } + + return $this->stripe_secret_key; + } + + +} diff --git a/app/Models/Activity.php b/app/Models/Activity.php new file mode 100644 index 00000000..f1b22bbb --- /dev/null +++ b/app/Models/Activity.php @@ -0,0 +1,14 @@ +belongsTo('\App\Models\Order'); + } + + public function ticket() { + return $this->belongsTo('\App\Models\Ticket'); + } + + public function event() { + return $this->belongsTo('\App\Models\Event'); + } + + + public function scopeWithoutCancelled($query) { + return $query->where('attendees.is_cancelled', '=', 0); + } + + public function getFullNameAttribute() { + return $this->first_name.' '.$this->last_name; + } + +// +// public function getReferenceAttribute() { +// return $this->order->order_reference +// } + + public function getDates() { + return array('created_at', 'updated_at', 'arrival_time'); + } + + /** + * Generate a private referennce number for the attendee. Use for checking in the attendee. + */ + public static function boot() { + parent::boot(); + + static::creating(function($order) { + $order->private_reference_number = str_pad(rand(0, pow(10, 9)-1), 9, '0', STR_PAD_LEFT); + }); + } + +} diff --git a/app/Models/Currency.php b/app/Models/Currency.php new file mode 100644 index 00000000..ac0e4bc0 --- /dev/null +++ b/app/Models/Currency.php @@ -0,0 +1,23 @@ +belongsTo('\App\Models\Event'); + } + +} diff --git a/app/Models/DateFormat.php b/app/Models/DateFormat.php new file mode 100644 index 00000000..0194c94a --- /dev/null +++ b/app/Models/DateFormat.php @@ -0,0 +1,17 @@ + array('required'), + 'description' => array('required'), + 'location_venue_name' => array('required_without:venue_name_full'), + 'venue_name_full' => array('required_without:location_venue_name'), + 'start_date' => array('required'), + 'end_date' => array('required'), + 'organiser_name' => array('required_without:organiser_id'), + 'event_image' => ['mimes:jpeg,jpg,png', 'max:3000'] + ); + protected $messages = array( + 'title.required' => 'You must at least give a title for your event.', + 'organiser_name.required_without' => 'Please create an organiser or select an existing organiser.', + 'event_image.mimes' => 'Please ensure you are uploading an image (JPG, PNG, JPEG)', + 'event_image.max' => 'Pleae ensure the image is not larger then 3MB', + 'location_venue_name.required_without' => 'Please enter a venue for your event', + 'venue_name_full.required_without' => 'Please enter a venue for your event' + ); + + public function questions() { + return $this->belongsToMany('\App\Models\Question', 'event_question'); + } + + public function attendees() { + return $this->hasMany('\App\Models\Attendee'); + } + + public function images() { + return $this->hasMany('\App\Models\EventImage'); + } + public function messages() { + return $this->hasMany('\App\Models\Message')->orderBy('created_at', 'DESC'); + } + + public function tickets() { + return $this->hasMany('\App\Models\Ticket'); + } + + public function stats() { + return $this->hasMany('\App\Models\EventStats'); + } + + public function affiliates() { + return $this->hasMany('\App\Models\Affiliate'); + } + + public function orders() { + return $this->hasMany('\App\Models\Order'); + } + + public function account() { + return $this->belongsTo('\App\Models\Account'); + } + + public function currency() { + return $this->belongsTo('\App\Models\Currency'); + } + + public function organiser() { + return $this->belongsTo('\App\Models\Organiser'); + } + + /* + * Getters & Setters + */ + + public function getEmbedUrlAttribute() { + return str_replace(['http:', 'https:'], '', route('showEmbeddedEventPage', ['event' => $this->id])); + } + + public function getFixedFeeAttribute() { + return TICKET_BOOKING_FEE_FIXED + $this->organiser_fee_fixed; + } + public function getPercentageFeeAttribute() { + return TICKET_BOOKING_FEE_PERCENTAGE + $this->organiser_fee_percentage; + } + + public function getHappeningNowAttribute() { + return Carbon::now()->between($this->start_date, $this->end_date); + } + + public function getCurrencySymbolAttribute() { + return $this->currency->symbol_left; + } + public function getCurrencyCodeAttribute() { + return $this->currency->code; + } + + public function getEmbedHtmlCodeAttribute () { + return " + + "; + } + + /* + * Get a usable address for embedding Google Maps + */ + public function getMapAddressAttribute() { + + $string = $this->venue.',' + .$this->location_street_number.',' + .$this->location_address_line_1.',' + .$this->location_address_line_2.',' + .$this->location_state.',' + .$this->location_post_code.',' + .$this->location_country; + + return urlencode($string); + + } + + public function getBgImageUrlAttribute() { + return URL::to('/').'/'.$this->bg_image_path; + } + + public function getEventUrlAttribute() { + return URL::to('/').'/e/'.$this->id.'/'.Str::slug($this->title); + } + + + public function getSalesAndFeesVoulmeAttribute() { + return $this->sales_volume + $this->organiser_fees_volume; + } + + public function getDates() { + return array('created_at', 'updated_at', 'start_date', 'end_date'); + } + +} diff --git a/app/Models/EventImage.php b/app/Models/EventImage.php new file mode 100644 index 00000000..858b7da0 --- /dev/null +++ b/app/Models/EventImage.php @@ -0,0 +1,15 @@ +ticket_revenue = $ticket->ticket_revenue + $amount; + + return $ticket->save(); + } + + public function updateViewCount($event_id) { + + $stats = $this->firstOrNew([ + 'event_id' => $event_id, + 'date' => DB::raw('CURDATE()') + ]); + + $cookie_name = 'visitTrack_'.$event_id.'_'.date('dmy'); + + if(!Cookie::get($cookie_name)) { + Cookie::queue($cookie_name, true, 60 * 24 * 14); + ++$stats->unique_views; + } + + ++$stats->views; + + return $stats->save(); + } + + /* + * TODO: Missing amount? + */ + public function updateSalesVolume($event_id) { + $stats = $this->firstOrNew([ + 'event_id' => $event_id, + 'date' => DB::raw('CURDATE()') + ]); + + $stats->sales_volume = $stats->sales_volume + $amount; + + return $stats->save(); + } + + + public function updateTicketsSoldCount($event_id, $count) { + + $stats = $this->firstOrNew([ + 'event_id' => $event_id, + 'date' => DB::raw('CURDATE()') + ]); + + $stats->increment('tickets_sold', $count); + + return $stats->save(); + } + +} diff --git a/app/Models/Message.php b/app/Models/Message.php new file mode 100644 index 00000000..167b7e70 --- /dev/null +++ b/app/Models/Message.php @@ -0,0 +1,31 @@ +belongsTo('\App\Models\Event'); + } + + public function getRecipientsLabelAttribute() { + if($this->recipients == 0) { + return 'All Attendees'; + } + + $ticket = Ticket::scope()->find($this->recipients); + return 'Ticket: '.$ticket->title; + } + + public function getDates() { + return array('created_at', 'updated_at', 'sent_at'); + } + +} diff --git a/app/Models/MyBaseModel.php b/app/Models/MyBaseModel.php new file mode 100644 index 00000000..52b20eb7 --- /dev/null +++ b/app/Models/MyBaseModel.php @@ -0,0 +1,99 @@ +rules, $this->messages); + + if ($v->fails()) { + $this->errors = $v->messages(); + return false; + } + + // validation pass + return true; + } + + public function errors($returnArray = TRUE) { + return $returnArray ? $this->errors->toArray() : $this->errors; + } + + /** + * + * @param int $account_id + * @param int $user_id + * @param bool $ignore_user_id + * @return \className + */ + public static function createNew($account_id = FALSE, $user_id = FALSE, $ignore_user_id = FALSE) { + $className = get_called_class(); + $entity = new $className(); + + if (Auth::check()) { + + if (!$ignore_user_id) { + $entity->user_id = Auth::user()->id; + } + + $entity->account_id = Auth::user()->account_id; + } else if ($account_id || $user_id) { + + if ($user_id && !$ignore_user_id) { + $entity->user_id = $user_id; + } + + $entity->account_id = $account_id; + } else { + App::abort(500); + } + + return $entity; + } + + public function getFormatedDate($field, $format = 'd-m-Y H:i') { + return $this->$field === NULL ? NULL : date($format, strtotime($this->$field)); + } + + /** + * + * @param int $accountId + */ + public function scopeScope($query, $accountId = false) { + + /* + * GOD MODE - DON'T UNCOMMENT! + * returning $query before adding the account_id condition will let you + * browse all events etc. in the system. + * //return $query; + */ + + + if (!$accountId) { + $accountId = Auth::user()->account_id; + } + + $table = $this->getTable(); + + $query->where(function($query) use ($accountId, $table) { + $query->whereRaw(\DB::raw('(' . $table . '.account_id = ' . $accountId . ')')); + }); + + return $query; + } + +} diff --git a/app/Models/Order.php b/app/Models/Order.php new file mode 100644 index 00000000..8969aa71 --- /dev/null +++ b/app/Models/Order.php @@ -0,0 +1,103 @@ + ['required'], + 'order_last_name' => ['required'], + 'order_email' => ['required', 'email'], + ]; + public $messages = [ + 'order_first_name.required' => 'Please enter a valid first name', + 'order_last_name.required' => 'Please enter a valid last name', + 'order_email.email' => 'Please enter a valid email', + ]; + + public function orderItems() { + return $this->hasMany('\App\Models\OrderItem'); + } + + public function attendees() { + return $this->hasMany('\App\Models\Attendee'); + } + + public function account() { + return $this->belongsTo('\App\Models\Account'); + } + + public function event() { + return $this->belongsTo('\App\Models\Event'); + } + + public function tickets() { + return $this->hasMany('\App\Models\Ticket'); + } + + public function orderStatus() { + return $this->belongsTo('\App\Models\OrderStatus'); + } + + public function getOrganiserAmountAttribute() { + return $this->amount + $this->organiser_booking_fee; + } + + public function getTotalAmountAttribute() { + return $this->amount + $this->organiser_booking_fee + $this->booking_fee; + } + + public function getFullNameAttribute() { + return $this->first_name . ' ' . $this->last_name; + } + + /** + * Generate and save the PDF tickets + * + * @todo Move this from the order model + * @return boolean + */ + public function generatePdfTickets() { + + $data = [ + 'order' => $this, + 'event' => $this->event, + 'tickets' => $this->event->tickets, + 'attendees' => $this->attendees + ]; + + $pdf_file_path = public_path(EVENT_PDF_TICKETS_PATH) . '/' . $this->order_reference; + $pdf_file = $pdf_file_path.'.pdf'; + + if (file_exists($pdf_file)) { + return true; + } + + if(!is_dir($pdf_file_path)) { + File::makeDirectory($pdf_file_path, 0777, true, true); + } + + PDF::setOutputMode('F'); // force to file + PDF::html('Public.ViewEvent.Partials.PDFTicket', $data, $pdf_file_path); + + $this->ticket_pdf_path = EVENT_PDF_TICKETS_PATH.'/'.$this->order_reference.'.pdf'; + $this->save(); + + return file_exists($pdf_file); + } + + public static function boot() { + parent::boot(); + + static::creating(function($order) { + $order->order_reference = strtoupper(str_random(5)) . date('jn'); + }); + } + +} diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php new file mode 100644 index 00000000..b203436c --- /dev/null +++ b/app/Models/OrderItem.php @@ -0,0 +1,15 @@ + array('required'), + 'email' => array('required', 'email'), + 'organiser_logo' => ['mimes:jpeg,jpg,png', 'max:10000'] + ); + protected $messages = array( + 'name.required' => 'You must at least give a name for the event organiser.', + 'organiser_logo.max' => 'Please upload an image smaller than 10Mb', + 'organiser_logo.size' => 'Please upload an image smaller than 10Mb', + 'organiser_logo.mimes' => 'Please select a valid image type (jpeg, jpg, png)' + ); + + public function events() { + return $this->hasMany('\App\Models\Event'); + } + + public function attendees() { + return $this->hasManyThrough('\App\Models\Attendee', '\App\Models\Event'); + } + + public function getFullLogoPathAttribute() { + if($this->logo_path && (file_exists(CDN_URL_USER_ASSETS.'/'.$this->logo_path) || file_exists(public_path($this->logo_path)))) { + return CDN_URL_USER_ASSETS.'/'.$this->logo_path; + } + + return FALLBACK_ORGANISER_LOGO_URL; + } + + public function getOrganiserUrlAttribute() { + return route('showOrganiserHome', [ + 'organiser_id' => $this->id, + 'organiser_slug' => Str::slug($this->oraganiser_name) + ]); + } + + public function getOrganiserSalesVolumeAttribute() { + return $this->events->sum('sales_volume'); + } + + + public function getDailyStats() { + + + + } + + + +} diff --git a/app/Models/PaymentGateway.php b/app/Models/PaymentGateway.php new file mode 100644 index 00000000..6a220824 --- /dev/null +++ b/app/Models/PaymentGateway.php @@ -0,0 +1,14 @@ +belongsToMany('\App\Models\Event'); + } + + + public function question_types() { + return $this->hasOne('\App\Models\QuestionType'); + } + +} diff --git a/app/Models/QuestionType.php b/app/Models/QuestionType.php new file mode 100644 index 00000000..3c49d979 --- /dev/null +++ b/app/Models/QuestionType.php @@ -0,0 +1,7 @@ + array('required'), + 'price' => array('required', 'numeric', 'min:0'), + 'start_sale_date' => array('date'), + 'end_sale_date' => array('date', 'after:start_sale_date'), + 'quantity_available' => ['integer', 'min:0'] + ]; + public $messages = [ + 'price.numeric' => 'The price must be a valid number (e.g 12.50)', + 'title.required' => 'You must at least give a title for your ticket. (e.g Early Bird)', + 'quantity_available.integer' => 'Please ensure the quantity available is a number.' + ]; + + public function event() { + return $this->belongsTo('\App\Models\Event'); + } + + public function order() { + return $this->belongsToMany('\App\Models\Order'); + } + + public function questions() { + return $this->belongsToMany('\App\Models\Question', 'ticket_question'); + } + + public function reserved() { + + } + + public function scopeSoldOut($query) { + $query->where('remaining_tickets', '=', 0); + } + + /* + * Getters & Setters + */ + + public function getDates() { + return array('created_at', 'updated_at', 'start_sale_date', 'end_sale_date'); + } + + public function getQuantityRemainingAttribute() { + + if(is_null($this->quantity_available)) { + return 9999; //Better way to do this? + } + + return $this->quantity_available - ($this->quantity_sold + $this->quantity_reserved); + } + + public function getQuantityReservedAttribute() { + + $reserved_total = \DB::table('reserved_tickets') + ->where('ticket_id', $this->id) + ->where('expires', '>', \Carbon::now()) + ->sum('quantity_reserved'); + + + return $reserved_total; + } + + + public function getBookingFeeAttribute () { + return (int)ceil($this->price) === 0 ? 0 : round(($this->price * (TICKET_BOOKING_FEE_PERCENTAGE / 100)) + (TICKET_BOOKING_FEE_FIXED), 2); + } + + public function getOrganiserBookingFeeAttribute() { + return (int)ceil($this->price) === 0 ? 0 : round(($this->price * ($this->event->organiser_fee_percentage / 100)) + ($this->event->organiser_fee_fixed), 2); + } + + public function getTotalBookingFeeAttribute() { + return $this->getBookingFeeAttribute() + $this->getOrganiserBookingFeeAttribute(); + } + + public function getTotalPriceAttribute() { + return $this->getTotalBookingFeeAttribute() + $this->price; + } + + public function getTicketMaxMinRangAttribute() { + $range = []; + + for($i=$this->min_per_person; $i<=$this->max_per_person; $i++) { + $range[] = [$i => $i]; + } + + return $range; + } + + + public function isFree() { + return (int)ceil($this->price) === 0; + } + + /** + * Return the maximum figure to go to on dropdowns + * + * @return int + + public function getMaxPerPersonMaxValueAttribute() { + return $this->max_per_person === -1 ? MAX_TICKETS_PER_PERSON : $this->max_per_person; + } + */ + public function getSaleStatusAttribute() { + + if ($this->start_sale_date !== NULL) { + if ($this->start_sale_date->isFuture()) { + return TICKET_STATUS_BEFORE_SALE_DATE; + } + } + + + + if ($this->end_sale_date !== NULL) { + if ($this->end_sale_date->isPast()) { + return TICKET_STATUS_AFTER_SALE_DATE; + } + } + + if ((int)$this->quantity_available > 0) { + if ((int)$this->quantity_remaining <= 0) { + return TICKET_STATUS_SOLD_OUT; + } + } + + if($this->event->start_date->lte(\Carbon::now())) { + return TICKET_STATUS_OFF_SALE; + } + + return TICKET_STATUS_ON_SALE; + } + + + + + + +// public function setQuantityAvailableAttribute($value) { +// $this->attributes['quantity_available'] = trim($value) == '' ? -1 : $value; +// } +// +// public function setMaxPerPersonAttribute($value) { +// $this->attributes['max_per_person'] = trim($value) == '' ? -1 : $value; +// } + +} diff --git a/app/Models/TicketStatus.php b/app/Models/TicketStatus.php new file mode 100644 index 00000000..c60e4968 --- /dev/null +++ b/app/Models/TicketStatus.php @@ -0,0 +1,14 @@ +belongsTo('\App\Models\Account'); + } + + public function activity() { + return $this->hasMany('\App\Models\Activity'); + } + + /** + * Get the unique identifier for the user. + * + * @return mixed + */ + public function getAuthIdentifier() { + return $this->getKey(); + } + + /** + * Get the password for the user. + * + * @return string + */ + public function getAuthPassword() { + return $this->password; + } + + /** + * Get the e-mail address where password reminders are sent. + * + * @return string + */ + public function getReminderEmail() { + return $this->email; + } + + public function getRememberToken() { + return $this->remember_token; + } + + public function setRememberToken($value) { + $this->remember_token = $value; + } + + public function getRememberTokenName() { + return 'remember_token'; + } + + public static function boot() { + parent::boot(); + + static::creating(function($user) { + $user->confirmation_code = str_random(); + }); + } + +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 00000000..a4d90618 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,42 @@ +app->bind( + 'Illuminate\Contracts\Auth\Registrar', 'App\Services\Registrar' + ); + + } + + +} diff --git a/app/Providers/BusServiceProvider.php b/app/Providers/BusServiceProvider.php new file mode 100644 index 00000000..f0d9be6f --- /dev/null +++ b/app/Providers/BusServiceProvider.php @@ -0,0 +1,34 @@ +mapUsing(function($command) + { + return Dispatcher::simpleMapping( + $command, 'App\Commands', 'App\Handlers\Commands' + ); + }); + } + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + // + } + +} diff --git a/app/Providers/ConfigServiceProvider.php b/app/Providers/ConfigServiceProvider.php new file mode 100644 index 00000000..06e57992 --- /dev/null +++ b/app/Providers/ConfigServiceProvider.php @@ -0,0 +1,23 @@ + [ + 'EventListener', + ], + ]; + + /** + * Register any other events for your application. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + */ + public function boot(DispatcherContract $events) + { + parent::boot($events); + + // + } + +} diff --git a/app/Providers/HelpersServiceProvider.php b/app/Providers/HelpersServiceProvider.php new file mode 100644 index 00000000..8aba8832 --- /dev/null +++ b/app/Providers/HelpersServiceProvider.php @@ -0,0 +1,28 @@ +group(['namespace' => $this->namespace], function($router) + { + require app_path('Http/routes.php'); + }); + } + +} diff --git a/app/Services/Registrar.php b/app/Services/Registrar.php new file mode 100644 index 00000000..10354681 --- /dev/null +++ b/app/Services/Registrar.php @@ -0,0 +1,39 @@ + 'required|max:255', + 'email' => 'required|email|max:255|unique:users', + 'password' => 'required|confirmed|min:6', + ]); + } + + /** + * Create a new user instance after a valid registration. + * + * @param array $data + * @return User + */ + public function create(array $data) + { + return User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + ]); + } + +} diff --git a/app/User_OLD.php b/app/User_OLD.php new file mode 100644 index 00000000..2dae8479 --- /dev/null +++ b/app/User_OLD.php @@ -0,0 +1,34 @@ +make('Illuminate\Contracts\Console\Kernel'); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running. We will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 00000000..f50a3f72 --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + 'Illuminate\Contracts\Http\Kernel', + 'App\Http\Kernel' +); + +$app->singleton( + 'Illuminate\Contracts\Console\Kernel', + 'App\Console\Kernel' +); + +$app->singleton( + 'Illuminate\Contracts\Debug\ExceptionHandler', + 'App\Exceptions\Handler' +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/bootstrap/autoload.php b/bootstrap/autoload.php new file mode 100644 index 00000000..2c35b3fc --- /dev/null +++ b/bootstrap/autoload.php @@ -0,0 +1,35 @@ +" + ], + "description": "Ticketing Platform", + "license": "MIT", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "bootstrap": "~3.2.0", + "curioussolutions-datetimepicker": "~0.1.1", + "humane-js": "~3.2.0", + "chartjs": "~0.2.0", + "geocomplete": "~1.6.4", + "wysiwyg-editor": "~1.2.3", + "modernizr": "~2.8.3", + "jquery-form": "malsup/form#~3.46.0", + "RRSSB": "~1.6.0", + "jquery-backstretch": "~2.0.4", + "flot": "~0.8.3", + "morrisjs": "~0.5.1", + "jquery.countdown": "~2.0.4", + "fontawesome": "~4.2.0", + "jquery-fastLiveFilter": "awbush/jquery-fastLiveFilter#~1.0.3", + "hint.css": "~1.3.3", + "wysihtml": "~0.4.17", + "bootstrap-touchspin": "~3.0.1", + "simplemde": "~1.8.1" + }, + "resolutions": { + "jquery": ">=1.5" + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..e5270221 --- /dev/null +++ b/composer.json @@ -0,0 +1,70 @@ +{ + "name": "attendize/attendize", + "description": "A free and open-source event management and ticket selling application.", + "keywords": ["event management", "ticket selling", "tickets", "events"], + "license": "Attribution Assurance License", + "type": "project", + "authors": { + "name" : "Dave Earley", + "email": "dave@attendize.com" + }, + "homepage" : "https://www.attendize.com", + "require": { + "laravel/framework": "5.1.*", + "illuminate/html": "~5.0", + "milon/barcode": "dev-master", + "stripe/stripe-php": "1.*", + "iron-io/iron_mq": "2.*", + "intervention/image": "dev-master", + "nitmedia/wkhtml2pdf": "dev-master", + "maatwebsite/excel": "~2.0.0", + "dompdf/dompdf": "dev-master", + "laravel/socialite": "~2.0", + "filp/whoops": "~1.0", + "vinelab/http": "dev-master", + "barryvdh/laravel-debugbar": "~2.0", + "mews/purifier": "~2.0", + "league/flysystem-aws-s3-v3" : "~1.0", + "maxhoffmann/parsedown-laravel": "dev-master" + + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "phpspec/phpspec": "~2.1" + }, + + "autoload": { + "classmap": [ + "database", + "app/Http/Controllers", + "app/Models", + "app/Attendize" + ], + "psr-4": { + "App\\": "app/", + "Attendize\\": "app/Models" + } + }, + "autoload-dev": { + "classmap": [ + "tests/TestCase.php" + ] + }, + "scripts": { + "post-install-cmd": [ + "php artisan clear-compiled", + "php artisan optimize" + ], + "post-update-cmd": [ + "php artisan clear-compiled", + "php artisan optimize" + ], + "post-create-project-cmd": [ + "php -r \"copy('.env.example', '.env');\"", + "php artisan key:generate" + ] + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/config/Wkhtml2pdf.php b/config/Wkhtml2pdf.php new file mode 100644 index 00000000..59a3603e --- /dev/null +++ b/config/Wkhtml2pdf.php @@ -0,0 +1,9 @@ + false, + 'binpath' => 'lib/', + 'binfile' => 'wkhtmltopdf-amd64', + 'output_mode' => 'I' +); \ No newline at end of file diff --git a/config/app.php b/config/app.php new file mode 100644 index 00000000..8ce9bc8c --- /dev/null +++ b/config/app.php @@ -0,0 +1,221 @@ + env('APP_DEBUG', false), + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + 'url' => env('APP_URL'), + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + 'timezone' => 'UTC', + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + 'locale' => 'en', + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + 'fallback_locale' => 'en', + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + 'key' => env('APP_KEY', 'SomeRandomString'), + 'cipher' => MCRYPT_RIJNDAEL_128, + /* + |-------------------------------------------------------------------------- + | Logging Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure the log settings for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Settings: "single", "daily", "syslog", "errorlog" + | + */ + 'log' => 'daily', + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + 'providers' => [ + + /* + * Laravel Framework Service Providers... + */ + 'Illuminate\Foundation\Providers\ArtisanServiceProvider', + 'Illuminate\Auth\AuthServiceProvider', + 'Illuminate\Bus\BusServiceProvider', + 'Illuminate\Cache\CacheServiceProvider', + 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider', + 'Illuminate\Routing\ControllerServiceProvider', + 'Illuminate\Cookie\CookieServiceProvider', + 'Illuminate\Database\DatabaseServiceProvider', + 'Illuminate\Encryption\EncryptionServiceProvider', + 'Illuminate\Filesystem\FilesystemServiceProvider', + 'Illuminate\Foundation\Providers\FoundationServiceProvider', + 'Illuminate\Hashing\HashServiceProvider', + 'Illuminate\Mail\MailServiceProvider', + 'Illuminate\Pagination\PaginationServiceProvider', + 'Illuminate\Pipeline\PipelineServiceProvider', + 'Illuminate\Queue\QueueServiceProvider', + 'Illuminate\Redis\RedisServiceProvider', + 'Illuminate\Auth\Passwords\PasswordResetServiceProvider', + 'Illuminate\Session\SessionServiceProvider', + 'Illuminate\Translation\TranslationServiceProvider', + 'Illuminate\Validation\ValidationServiceProvider', + 'Illuminate\View\ViewServiceProvider', + 'Illuminate\Html\HtmlServiceProvider', + 'Illuminate\Broadcasting\BroadcastServiceProvider', +// 'Thomaswelton\LaravelGravatar\LaravelGravatarServiceProvider', +// 'Webpatser\Countries\CountriesServiceProvider', +// 'Vinelab\Http\HttpServiceProvider', +// + 'Vinelab\Http\HttpServiceProvider', + 'Milon\Barcode\BarcodeServiceProvider', + 'Intervention\Image\ImageServiceProvider', + 'Nitmedia\Wkhtml2pdf\L5Wkhtml2pdfServiceProvider', + 'Maatwebsite\Excel\ExcelServiceProvider', + 'Laravel\Socialite\SocialiteServiceProvider', +// 'Bugsnag\BugsnagLaravel\BugsnagLaravelServiceProvider::class', + 'Barryvdh\Debugbar\ServiceProvider', + 'Nitmedia\Wkhtml2pdf\L5Wkhtml2pdfServiceProvider', + 'Mews\Purifier\PurifierServiceProvider', + 'MaxHoffmann\Parsedown\ParsedownServiceProvider', + /* + * Application Service Providers... + */ + + 'App\Providers\AppServiceProvider', + 'App\Providers\BusServiceProvider', + 'App\Providers\ConfigServiceProvider', + 'App\Providers\EventServiceProvider', + 'App\Providers\RouteServiceProvider', + 'App\Providers\HelpersServiceProvider', + + + + ], + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + 'aliases' => [ + + 'App' => 'Illuminate\Support\Facades\App', + 'Artisan' => 'Illuminate\Support\Facades\Artisan', + 'Auth' => 'Illuminate\Support\Facades\Auth', + 'Blade' => 'Illuminate\Support\Facades\Blade', + 'Bus' => 'Illuminate\Support\Facades\Bus', + 'Cache' => 'Illuminate\Support\Facades\Cache', + 'Config' => 'Illuminate\Support\Facades\Config', + 'Cookie' => 'Illuminate\Support\Facades\Cookie', + 'Crypt' => 'Illuminate\Support\Facades\Crypt', + 'DB' => 'Illuminate\Support\Facades\DB', + 'Eloquent' => 'Illuminate\Database\Eloquent\Model', + /* + * changed Event alias to LaravelEvent as there was a conflict + * If something blows up it could be because of this + */ + 'LaravelEvent' => 'Illuminate\Support\Facades\Event', + 'File' => 'Illuminate\Support\Facades\File', + 'Hash' => 'Illuminate\Support\Facades\Hash', + 'Input' => 'Illuminate\Support\Facades\Input', + 'Inspiring' => 'Illuminate\Foundation\Inspiring', + 'Lang' => 'Illuminate\Support\Facades\Lang', + 'Log' => 'Illuminate\Support\Facades\Log', + 'Mail' => 'Illuminate\Support\Facades\Mail', + 'Password' => 'Illuminate\Support\Facades\Password', + 'Queue' => 'Illuminate\Support\Facades\Queue', + 'Redirect' => 'Illuminate\Support\Facades\Redirect', + 'Redis' => 'Illuminate\Support\Facades\Redis', + 'Request' => 'Illuminate\Support\Facades\Request', + 'Response' => 'Illuminate\Support\Facades\Response', + 'Route' => 'Illuminate\Support\Facades\Route', + 'Schema' => 'Illuminate\Support\Facades\Schema', + 'Session' => 'Illuminate\Support\Facades\Session', + 'Storage' => 'Illuminate\Support\Facades\Storage', + 'URL' => 'Illuminate\Support\Facades\URL', + 'Validator' => 'Illuminate\Support\Facades\Validator', + 'View' => 'Illuminate\Support\Facades\View', + 'Form' => 'Illuminate\Html\FormFacade', + 'HTML' => 'Illuminate\Html\HtmlFacade', + 'Str' => 'Illuminate\Support\Str', + 'Utils' => 'App\Attendize\Utils', + 'Countries' => 'Webpatser\Countries\CountriesFacade', + 'Carbon' => 'Carbon\Carbon', + //'PDF' => 'Barryvdh\DomPDF\Facade', + 'PDF' => 'Nitmedia\Wkhtml2pdf\Facades\Wkhtml2pdf', + 'DNS1D' => 'Milon\Barcode\Facades\DNS1DFacade', + 'DNS2D' => 'Milon\Barcode\Facades\DNS2DFacade', + 'Image' => 'Intervention\Image\Facades\Image', + 'Excel' => 'Maatwebsite\Excel\Facades\Excel', + 'Socialize' => 'Laravel\Socialite\Facades\Socialite', + 'HttpClient' => 'Vinelab\Http\Facades\Client', + //'Purifier' => 'Mews\Purifier\Facades\Purifier', + 'Purifier' => 'Mews\Purifier\Facades\Purifier', + 'Markdown' => 'MaxHoffmann\Parsedown\ParsedownFacade', + + ], +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 00000000..c3995e46 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,67 @@ + 'eloquent', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | When using the "Eloquent" authentication driver, we need to know which + | Eloquent model should be used to retrieve your users. Of course, it + | is often just the "User" model but you may use whatever you like. + | + */ + + 'model' => 'App\Models\User', + + /* + |-------------------------------------------------------------------------- + | Authentication Table + |-------------------------------------------------------------------------- + | + | When using the "Database" authentication driver, we need to know which + | table should be used to retrieve your users. We have chosen a basic + | default value but you may easily change it to any table you like. + | + */ + + 'table' => 'users', + + /* + |-------------------------------------------------------------------------- + | Password Reset Settings + |-------------------------------------------------------------------------- + | + | Here you may set the options for resetting passwords including the view + | that is your password reset e-mail. You can also set the name of the + | table that maintains all of the reset tokens for your application. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'password' => [ + 'email' => 'emails.Auth.Reminder', + 'table' => 'password_resets', + 'expire' => 60, + ], + +]; diff --git a/config/barcode.php b/config/barcode.php new file mode 100644 index 00000000..6fc229f3 --- /dev/null +++ b/config/barcode.php @@ -0,0 +1,5 @@ + public_path("/"), +]; diff --git a/config/bugsnag.php b/config/bugsnag.php new file mode 100644 index 00000000..114205a8 --- /dev/null +++ b/config/bugsnag.php @@ -0,0 +1,5 @@ + 'b663417807397a325bcd5a9b3d5048b5' +]; \ No newline at end of file diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 00000000..9ddd5f33 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,79 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc' + ], + + 'array' => [ + 'driver' => 'array' + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path().'/framework/cache', + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'servers' => [ + [ + 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100 + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => 'laravel', + +]; diff --git a/config/compile.php b/config/compile.php new file mode 100644 index 00000000..3a002fca --- /dev/null +++ b/config/compile.php @@ -0,0 +1,41 @@ + [ + + realpath(__DIR__.'/../app/Providers/AppServiceProvider.php'), + realpath(__DIR__.'/../app/Providers/BusServiceProvider.php'), + realpath(__DIR__.'/../app/Providers/ConfigServiceProvider.php'), + realpath(__DIR__.'/../app/Providers/EventServiceProvider.php'), + realpath(__DIR__.'/../app/Providers/RouteServiceProvider.php'), + + ], + + /* + |-------------------------------------------------------------------------- + | Compiled File Providers + |-------------------------------------------------------------------------- + | + | Here you may list service providers which define a "compiles" function + | that returns additional files that should be compiled, providing an + | easy way to get common files from any packages you are utilizing. + | + */ + + 'providers' => [ + // + ], + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 00000000..c47f6bc0 --- /dev/null +++ b/config/database.php @@ -0,0 +1,125 @@ + PDO::FETCH_CLASS, + + /* + |-------------------------------------------------------------------------- + | Default Database Connection Name + |-------------------------------------------------------------------------- + | + | Here you may specify which of the database connections below you wish + | to use as your default connection for all database work. Of course + | you may use many connections at once using the Database library. + | + */ + + 'default' => 'mysql', + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'database' => storage_path().'/database.sqlite', + 'prefix' => '', + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST'), + 'database' => env('DB_DATABASE'), + 'username' => env('DB_USERNAME'), + 'password' => env('DB_PASSWORD'), + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'strict' => false, + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'host' => env('DB_HOST', 'localhost'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'host' => env('DB_HOST', 'localhost'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'prefix' => '', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'cluster' => false, + + 'default' => [ + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0, + ], + + ], + +]; diff --git a/config/debugbar.php b/config/debugbar.php new file mode 100644 index 00000000..8131c837 --- /dev/null +++ b/config/debugbar.php @@ -0,0 +1,145 @@ + false, + + /* + |-------------------------------------------------------------------------- + | Storage settings + |-------------------------------------------------------------------------- + | + | DebugBar stores data for session/ajax requests. + | You can disable this, so the debugbar stores data in headers/session, + | but this can cause problems with large data collectors. + | By default, file storage (in the storage folder) is used. Redis and PDO + | can also be used. For PDO, run the package migrations first. + | + */ + 'storage' => array( + 'enabled' => true, + 'driver' => 'file', // redis, file, pdo + 'path' => storage_path() . '/debugbar', // For file driver + 'connection' => null, // Leave null for default connection (Redis/PDO) + ), + + /* + |-------------------------------------------------------------------------- + | Vendors + |-------------------------------------------------------------------------- + | + | Vendor files are included by default, but can be set to false. + | This can also be set to 'js' or 'css', to only include javascript or css vendor files. + | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) + | and for js: jquery and and highlight.js + | So if you want syntax highlighting, set it to true. + | jQuery is set to not conflict with existing jQuery scripts. + | + */ + + 'include_vendors' => true, + + /* + |-------------------------------------------------------------------------- + | Capture Ajax Requests + |-------------------------------------------------------------------------- + | + | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), + | you can use this option to disable sending the data through the headers. + | + */ + + 'capture_ajax' => true, + + /* + |-------------------------------------------------------------------------- + | DataCollectors + |-------------------------------------------------------------------------- + | + | Enable/disable DataCollectors + | + */ + + 'collectors' => array( + 'phpinfo' => true, // Php version + 'messages' => true, // Messages + 'time' => true, // Time Datalogger + 'memory' => true, // Memory usage + 'exceptions' => true, // Exception displayer + 'log' => true, // Logs from Monolog (merged in messages if enabled) + 'db' => true, // Show database (PDO) queries and bindings + 'views' => true, // Views with their data + 'route' => true, // Current route information + 'laravel' => false, // Laravel version and environment + 'events' => false, // All events fired + 'default_request' => false, // Regular or special Symfony request logger + 'symfony_request' => true, // Only one can be enabled.. + 'mail' => true, // Catch mail messages + 'logs' => true, // Add the latest log messages + 'files' => false, // Show the included files + 'config' => false, // Display config settings + 'auth' => false, // Display Laravel authentication status + 'session' => false, // Display session data in a separate tab + ), + + /* + |-------------------------------------------------------------------------- + | Extra options + |-------------------------------------------------------------------------- + | + | Configure some DataCollectors + | + */ + + 'options' => array( + 'auth' => array( + 'show_name' => false, // Also show the users name/email in the debugbar + ), + 'db' => array( + 'with_params' => true, // Render SQL with the parameters substituted + 'timeline' => false, // Add the queries to the timeline + 'backtrace' => false, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files. + 'explain' => array( // EXPERIMENTAL: Show EXPLAIN output on queries + 'enabled' => false, + 'types' => array('SELECT'), // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+ + ), + 'hints' => true, // Show hints for common mistakes + ), + 'mail' => array( + 'full_log' => false + ), + 'views' => array( + 'data' => false, //Note: Can slow down the application, because the data can be quite large.. + ), + 'route' => array( + 'label' => true // show complete route on bar + ), + 'logs' => array( + 'file' => null + ), + ), + + /* + |-------------------------------------------------------------------------- + | Inject Debugbar in Response + |-------------------------------------------------------------------------- + | + | Usually, the debugbar is added just before , by listening to the + | Response after the App is done. If you disable this, you have to add them + | in your template yourself. See http://phpdebugbar.com/docs/rendering.html + | + */ + + 'inject' => true, + +); diff --git a/config/excel.php b/config/excel.php new file mode 100644 index 00000000..24fd87e7 --- /dev/null +++ b/config/excel.php @@ -0,0 +1,683 @@ + array( + + /* + |-------------------------------------------------------------------------- + | Enable/Disable cell caching + |-------------------------------------------------------------------------- + */ + 'enable' => true, + + /* + |-------------------------------------------------------------------------- + | Caching driver + |-------------------------------------------------------------------------- + | + | Set the caching driver + | + | Available methods: + | memory|gzip|serialized|igbinary|discISAM|apc|memcache|temp|wincache|sqlite|sqlite3 + | + */ + 'driver' => 'memory', + + /* + |-------------------------------------------------------------------------- + | Cache settings + |-------------------------------------------------------------------------- + */ + 'settings' => array( + + 'memoryCacheSize' => '32MB', + 'cacheTime' => 600 + + ), + + /* + |-------------------------------------------------------------------------- + | Memcache settings + |-------------------------------------------------------------------------- + */ + 'memcache' => array( + + 'host' => 'localhost', + 'port' => 11211, + + ), + + /* + |-------------------------------------------------------------------------- + | Cache dir (for discISAM) + |-------------------------------------------------------------------------- + */ + + 'dir' => storage_path('cache') + ), + + 'properties' => array( + 'creator' => 'Maatwebsite', + 'lastModifiedBy' => 'Maatwebsite', + 'title' => 'Spreadsheet', + 'description' => 'Default spreadsheet export', + 'subject' => 'Spreadsheet export', + 'keywords' => 'maatwebsite, excel, export', + 'category' => 'Excel', + 'manager' => 'Maatwebsite', + 'company' => 'Maatwebsite', + ), + + /* + |-------------------------------------------------------------------------- + | Sheets settings + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Default page setup + |-------------------------------------------------------------------------- + */ + 'pageSetup' => array( + 'orientation' => 'portrait', + 'paperSize' => '9', + 'scale' => '100', + 'fitToPage' => false, + 'fitToHeight' => true, + 'fitToWidth' => true, + 'columnsToRepeatAtLeft' => array('', ''), + 'rowsToRepeatAtTop' => array(0, 0), + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'printArea' => null, + 'firstPageNumber' => null, + ), + ), + + /* + |-------------------------------------------------------------------------- + | Creator + |-------------------------------------------------------------------------- + | + | The default creator of a new Excel file + | + */ + + 'creator' => 'Maatwebsite', + + 'csv' => array( + /* + |-------------------------------------------------------------------------- + | Delimiter + |-------------------------------------------------------------------------- + | + | The default delimiter which will be used to read out a CSV file + | + */ + + 'delimiter' => ',', + + /* + |-------------------------------------------------------------------------- + | Enclosure + |-------------------------------------------------------------------------- + */ + + 'enclosure' => '"', + + /* + |-------------------------------------------------------------------------- + | Line endings + |-------------------------------------------------------------------------- + */ + + 'line_ending' => "\r\n" + ), + + 'export' => array( + + /* + |-------------------------------------------------------------------------- + | Autosize columns + |-------------------------------------------------------------------------- + | + | Disable/enable column autosize or set the autosizing for + | an array of columns ( array('A', 'B') ) + | + */ + 'autosize' => true, + + /* + |-------------------------------------------------------------------------- + | Autosize method + |-------------------------------------------------------------------------- + | + | --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX + | The default is based on an estimate, which does its calculation based + | on the number of characters in the cell value (applying any calculation + | and format mask, and allowing for wordwrap and rotation) and with an + | "arbitrary" adjustment based on the font (Arial, Calibri or Verdana, + | defaulting to Calibri if any other font is used) and a proportional + | adjustment for the font size. + | + | --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_EXACT + | The second method is more accurate, based on actual style formatting as + | well (bold, italic, etc), and is calculated by generating a gd2 imagettf + | bounding box and using its dimensions to determine the size; but this + | method is significantly slower, and its accuracy is still dependent on + | having the appropriate fonts installed. + | + */ + 'autosize-method' => PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX, + + /* + |-------------------------------------------------------------------------- + | Auto generate table heading + |-------------------------------------------------------------------------- + | + | If set to true, the array indices (or model attribute names) + | will automatically be used as first row (table heading) + | + */ + 'generate_heading_by_indices' => true, + + /* + |-------------------------------------------------------------------------- + | Auto set alignment on merged cells + |-------------------------------------------------------------------------- + */ + 'merged_cell_alignment' => 'left', + + /* + |-------------------------------------------------------------------------- + | Pre-calculate formulas during export + |-------------------------------------------------------------------------- + */ + 'calculate' => false, + + /* + |-------------------------------------------------------------------------- + | Include Charts during export + |-------------------------------------------------------------------------- + */ + 'includeCharts' => false, + + /* + |-------------------------------------------------------------------------- + | Default sheet settings + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Default page margin + |-------------------------------------------------------------------------- + | + | 1) When set to false, default margins will be used + | 2) It's possible to enter a single margin which will + | be used for all margins. + | 3) Alternatively you can pass an array with 4 margins + | Default order: array(top, right, bottom, left) + | + */ + 'page_margin' => false, + + /* + |-------------------------------------------------------------------------- + | Value in source array that stands for blank cell + |-------------------------------------------------------------------------- + */ + 'nullValue' => null, + + /* + |-------------------------------------------------------------------------- + | Insert array starting from this cell address as the top left coordinate + |-------------------------------------------------------------------------- + */ + 'startCell' => 'A1', + + /* + |-------------------------------------------------------------------------- + | Apply strict comparison when testing for null values in the array + |-------------------------------------------------------------------------- + */ + 'strictNullComparison' => false + ), + + /* + |-------------------------------------------------------------------------- + | Store settings + |-------------------------------------------------------------------------- + */ + + 'store' => array( + + /* + |-------------------------------------------------------------------------- + | Path + |-------------------------------------------------------------------------- + | + | The path we want to save excel file to + | + */ + 'path' => storage_path('exports'), + + /* + |-------------------------------------------------------------------------- + | Return info + |-------------------------------------------------------------------------- + | + | Whether we want to return information about the stored file or not + | + */ + 'returnInfo' => false + + ), + + /* + |-------------------------------------------------------------------------- + | PDF Settings + |-------------------------------------------------------------------------- + */ + 'pdf' => array( + + /* + |-------------------------------------------------------------------------- + | PDF Drivers + |-------------------------------------------------------------------------- + | Supported: DomPDF, tcPDF, mPDF + */ + 'driver' => 'DomPDF', + + /* + |-------------------------------------------------------------------------- + | PDF Driver settings + |-------------------------------------------------------------------------- + */ + 'drivers' => array( + + /* + |-------------------------------------------------------------------------- + | DomPDF settings + |-------------------------------------------------------------------------- + */ + 'DomPDF' => array( + 'path' => base_path('vendor/dompdf/dompdf/') + ), + + /* + |-------------------------------------------------------------------------- + | tcPDF settings + |-------------------------------------------------------------------------- + */ + 'tcPDF' => array( + 'path' => base_path('vendor/tecnick.com/tcpdf/') + ), + + /* + |-------------------------------------------------------------------------- + | mPDF settings + |-------------------------------------------------------------------------- + */ + 'mPDF' => array( + 'path' => base_path('vendor/mpdf/mpdf/') + ), + ) + ) + ), + + 'filters' => array( + /* + |-------------------------------------------------------------------------- + | Register read filters + |-------------------------------------------------------------------------- + */ + + 'registered' => array( + 'chunk' => 'Maatwebsite\Excel\Filters\ChunkReadFilter' + ), + + /* + |-------------------------------------------------------------------------- + | Enable certain filters for every file read + |-------------------------------------------------------------------------- + */ + + 'enabled' => array() + ), + + 'import' => array( + + /* + |-------------------------------------------------------------------------- + | Has heading + |-------------------------------------------------------------------------- + | + | The sheet has a heading (first) row which we can use as attribute names + | + | Options: true|false|slugged|ascii|numeric|hashed|trans|original + | + */ + + 'heading' => 'slugged', + + /* + |-------------------------------------------------------------------------- + | First Row with data or heading of data + |-------------------------------------------------------------------------- + | + | If the heading row is not the first row, or the data doesn't start + | on the first row, here you can change the start row. + | + */ + + 'startRow' => 1, + + /* + |-------------------------------------------------------------------------- + | Cell name word separator + |-------------------------------------------------------------------------- + | + | The default separator which is used for the cell names + | Note: only applies to 'heading' settings 'true' && 'slugged' + | + */ + + 'separator' => '_', + + /* + |-------------------------------------------------------------------------- + | Include Charts during import + |-------------------------------------------------------------------------- + */ + + 'includeCharts' => false, + + /* + |-------------------------------------------------------------------------- + | Sheet heading conversion + |-------------------------------------------------------------------------- + | + | Convert headings to ASCII + | Note: only applies to 'heading' settings 'true' && 'slugged' + | + */ + + 'to_ascii' => true, + + /* + |-------------------------------------------------------------------------- + | Import encoding + |-------------------------------------------------------------------------- + */ + + 'encoding' => array( + + 'input' => 'UTF-8', + 'output' => 'UTF-8' + + ), + + /* + |-------------------------------------------------------------------------- + | Calculate + |-------------------------------------------------------------------------- + | + | By default cells with formulas will be calculated. + | + */ + + 'calculate' => true, + + /* + |-------------------------------------------------------------------------- + | Ignore empty cells + |-------------------------------------------------------------------------- + | + | By default empty cells are not ignored + | + */ + + 'ignoreEmpty' => false, + + /* + |-------------------------------------------------------------------------- + | Force sheet collection + |-------------------------------------------------------------------------- + | + | For a sheet collection even when there is only 1 sheets. + | When set to false and only 1 sheet found, the parsed file will return + | a row collection instead of a sheet collection. + | When set to true, it will return a sheet collection instead. + | + */ + 'force_sheets_collection' => false, + + /* + |-------------------------------------------------------------------------- + | Date format + |-------------------------------------------------------------------------- + | + | The format dates will be parsed to + | + */ + + 'dates' => array( + + /* + |-------------------------------------------------------------------------- + | Enable/disable date formatting + |-------------------------------------------------------------------------- + */ + 'enabled' => true, + + /* + |-------------------------------------------------------------------------- + | Default date format + |-------------------------------------------------------------------------- + | + | If set to false, a carbon object will return + | + */ + 'format' => false, + + /* + |-------------------------------------------------------------------------- + | Date columns + |-------------------------------------------------------------------------- + */ + 'columns' => array() + ), + + /* + |-------------------------------------------------------------------------- + | Import sheets by config + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Example sheet + |-------------------------------------------------------------------------- + | + | Example sheet "test" will grab the firstname at cell A2 + | + */ + + 'test' => array( + + 'firstname' => 'A2' + + ) + + ) + ), + + 'views' => array( + + /* + |-------------------------------------------------------------------------- + | Styles + |-------------------------------------------------------------------------- + | + | The default styles which will be used when parsing a view + | + */ + + 'styles' => array( + + /* + |-------------------------------------------------------------------------- + | Table headings + |-------------------------------------------------------------------------- + */ + 'th' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Strong tags + |-------------------------------------------------------------------------- + */ + 'strong' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Bold tags + |-------------------------------------------------------------------------- + */ + 'b' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Italic tags + |-------------------------------------------------------------------------- + */ + 'i' => array( + 'font' => array( + 'italic' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 1 + |-------------------------------------------------------------------------- + */ + 'h1' => array( + 'font' => array( + 'bold' => true, + 'size' => 24, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 2 + |-------------------------------------------------------------------------- + */ + 'h2' => array( + 'font' => array( + 'bold' => true, + 'size' => 18, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 2 + |-------------------------------------------------------------------------- + */ + 'h3' => array( + 'font' => array( + 'bold' => true, + 'size' => 13.5, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 4 + |-------------------------------------------------------------------------- + */ + 'h4' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 5 + |-------------------------------------------------------------------------- + */ + 'h5' => array( + 'font' => array( + 'bold' => true, + 'size' => 10, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 6 + |-------------------------------------------------------------------------- + */ + 'h6' => array( + 'font' => array( + 'bold' => true, + 'size' => 7.5, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Hyperlinks + |-------------------------------------------------------------------------- + */ + 'a' => array( + 'font' => array( + 'underline' => true, + 'color' => array('argb' => 'FF0000FF'), + ) + ), + + /* + |-------------------------------------------------------------------------- + | Horizontal rules + |-------------------------------------------------------------------------- + */ + 'hr' => array( + 'borders' => array( + 'bottom' => array( + 'style' => 'thin', + 'color' => array('FF000000') + ), + ) + ) + ) + + ) + +); \ No newline at end of file diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 00000000..430c6637 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,70 @@ + 'local', + + /* + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | + */ + + 'cloud' => 's3', + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been setup for each driver as an example of the required options. + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => public_path().'/user_content', + ], + + 's3' => [ + 'driver' => 's3', + 'key' => 'your-key', + 'secret' => 'your-secret', + 'region' => 'your-region', + 'bucket' => 'your-bucket', + ], + + 'rackspace' => [ + 'driver' => 'rackspace', + 'username' => 'your-username', + 'key' => 'your-key', + 'container' => 'your-container', + 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/', + 'region' => 'IAD', + ], + + ], + +]; diff --git a/config/image.php b/config/image.php new file mode 100644 index 00000000..b106809e --- /dev/null +++ b/config/image.php @@ -0,0 +1,20 @@ + 'gd' + +); diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 00000000..a8a97f4e --- /dev/null +++ b/config/mail.php @@ -0,0 +1,124 @@ + env('MAIL_DRIVER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Mailgun mail service which will provide reliable deliveries. + | + */ + + 'host' => env('MAIL_HOST', 'smtp.mandrillapp.com'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to deliver e-mails to + | users of the application. Like the host we have set this value to + | stay compatible with the Mailgun e-mail application by default. + | + */ + + 'port' => env('MAIL_PORT', 587), + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => ['address' => env('MAIL_FROM_ADDRESS'), 'name' => env('MAIL_FROM_NAME')], + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => env('MAIL_ENCRYPTION','tls'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => 'dave.m.earley@gmail.com', + + /* + |-------------------------------------------------------------------------- + | SMTP Server Password + |-------------------------------------------------------------------------- + | + | Here you may set the password required by your SMTP server to send out + | messages from your application. This will be given to the server on + | connection so that the application will be able to send messages. + | + */ + + 'password' => 'b038a015-8162-4a7a-bb8b-abb63fa7c112', + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Mail "Pretend" + |-------------------------------------------------------------------------- + | + | When this option is enabled, e-mail will not actually be sent over the + | web and will instead be written to your application's logs files so + | you may inspect the message. This is great for local development. + | + */ + + 'pretend' => false, + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 00000000..fd16e588 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,81 @@ + env('QUEUE_DRIVER', 'sync'), + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + */ + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'expire' => 60, + ], + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'ttr' => 60, + ], + 'sqs' => [ + 'driver' => 'sqs', + 'key' => 'your-public-key', + 'secret' => 'your-secret-key', + 'queue' => 'your-queue-url', + 'region' => 'us-east-1', + ], + 'iron' => [ + 'driver' => 'iron', + 'host' => 'mq-aws-eu-west-1.iron.io', + 'token' => 'e86QTHwOmEDzqYtti9xAKSgjS7E', + 'project' => '535d254120fa16000900000a', + 'queue' => '54e8a61d8d560bccac9dc83e', + 'encrypt' => true + ], + 'redis' => [ + 'driver' => 'redis', + 'queue' => 'default', + 'expire' => 60, + ], + ], + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + 'failed' => [ + 'database' => 'mysql', 'table' => 'failed_jobs', + ], +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 00000000..dddc9866 --- /dev/null +++ b/config/services.php @@ -0,0 +1,37 @@ + [ + 'domain' => '', + 'secret' => '', + ], + + 'mandrill' => [ + 'secret' => '', + ], + + 'ses' => [ + 'key' => '', + 'secret' => '', + 'region' => 'us-east-1', + ], + + 'stripe' => [ + 'model' => 'User', + 'secret' => '', + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 00000000..47470fab --- /dev/null +++ b/config/session.php @@ -0,0 +1,153 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => 120, + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path().'/framework/sessions', + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => null, + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => 'laravel_session', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | + */ + + 'secure' => false, + +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 00000000..88fc534a --- /dev/null +++ b/config/view.php @@ -0,0 +1,33 @@ + [ + realpath(base_path('resources/views')) + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => realpath(storage_path().'/framework/views'), + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 00000000..9b1dffd9 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/database/migrations/2014_03_26_180116_create_users_table.php b/database/migrations/2014_03_26_180116_create_users_table.php new file mode 100644 index 00000000..a974a261 --- /dev/null +++ b/database/migrations/2014_03_26_180116_create_users_table.php @@ -0,0 +1,529 @@ +increments('id'); + $t->string('name'); + }); + + Schema::create('ticket_statuses', function($table) { + $table->increments('id'); + $table->text('name'); + }); + + Schema::create('reserved_tickets', function($table) { + + $table->increments('id'); + + $table->integer('ticket_id'); + $table->integer('event_id'); + $table->integer('quantity_reserved'); + $table->datetime('expires'); + $table->string('session_id', 45); + + $table->timestamps(); + }); + + Schema::create('timezones', function($t) { + $t->increments('id'); + $t->string('name'); + $t->string('location'); + }); + + Schema::create('date_formats', function($t) { + $t->increments('id'); + $t->string('format'); + $t->string('picker_format'); + $t->string('label'); + }); + + Schema::create('datetime_formats', function($t) { + $t->increments('id'); + $t->string('format'); + $t->string('label'); + }); + + // Create the `currency` table + Schema::create('currencies', function($table) { + $table->increments('id')->unsigned(); + $table->string('title', 255); + $table->string('symbol_left', 12); + $table->string('symbol_right', 12); + $table->string('code', 3); + $table->integer('decimal_place'); + $table->double('value', 15, 8); + $table->string('decimal_point', 3); + $table->string('thousand_point', 3); + $table->integer('status'); + $table->timestamps(); + }); + + + /** + * Accounts table + */ + Schema::create('accounts', function($t) { + $t->increments('id'); + + $t->string('first_name'); + $t->string('last_name'); + $t->string('email'); + + $t->unsignedInteger('timezone_id')->nullable(); + $t->unsignedInteger('date_format_id')->nullable(); + $t->unsignedInteger('datetime_format_id')->nullable(); + $t->unsignedInteger('currency_id')->nullable(); + + $t->timestamps(); + $t->softDeletes(); + + $t->string('name'); + $t->string('last_ip'); + $t->timestamp('last_login_date'); + + $t->string('address1'); + $t->string('address2'); + $t->string('city'); + $t->string('state'); + $t->string('postal_code'); + $t->unsignedInteger('country_id')->nullable(); + $t->text('email_footer'); + + $t->boolean('is_active')->default(false); + $t->boolean('is_banned')->default(false); + $t->boolean('is_beta')->default(false); + + $t->string('stripe_access_token', 55); + $t->string('stripe_refresh_token', 55); + $t->string('stripe_secret_key', 55); + $t->string('stripe_publishable_key', 55); + $t->text('stripe_data_raw', 55); + + $t->foreign('timezone_id')->references('id')->on('timezones'); + $t->foreign('date_format_id')->references('id')->on('date_formats'); + $t->foreign('datetime_format_id')->references('id')->on('date_formats'); + //$t->foreign('country_id')->references('id')->on('countries'); + $t->foreign('currency_id')->references('id')->on('currencies'); + }); + + + /* + * Users Table + */ + Schema::create('users', function($t) { + + + $t->increments('id'); + $t->unsignedInteger('account_id')->index(); + $t->timestamps(); + $t->softDeletes(); + + + $t->string('first_name'); + $t->string('last_name'); + $t->string('phone'); + $t->string('email'); + $t->string('password'); + $t->string('confirmation_code'); + $t->boolean('is_registered')->default(false); + $t->boolean('is_confirmed')->default(false); + $t->boolean('is_parent')->default(false); + $t->string('remember_token', 100)->nullable(); + + + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + }); + + Schema::create('organisers', function ($table) { + + $table->increments('id')->index(); + + $table->timestamps(); + $table->softDeletes(); + + $table->unsignedInteger('account_id')->index(); + + $table->string('name'); + $table->text('about'); + $table->string('email'); + $table->string('phone'); + $table->string('confirmation_key', 20); + $table->string('facebook'); + $table->string('twitter'); + $table->string('logo_path'); + $table->boolean('is_email_confirmed')->default(0); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + }); + + + + + Schema::create('events', function($t) { + $t->increments('id'); + + $t->string('title'); + $t->string('location'); + $t->string('bg_type', 15)->default('color'); + $t->string('bg_color')->default(EVENT_DEFAULT_BG_COLOR); + $t->string('bg_image_path'); + $t->text('description'); + + $t->dateTime('start_date')->nullable(); + $t->dateTime('end_date')->nullable(); + + $t->dateTime('on_sale_date')->nullable(); + + $t->integer('account_id')->unsigned()->index(); + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + + $t->integer('user_id')->unsigned(); + $t->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + $t->unsignedInteger('currency_id')->nullable(); + $t->foreign('currency_id')->references('id')->on('currencies'); + + $t->decimal('sales_volume', 13, 2); + $t->decimal('organiser_fees_volume', 13, 2); + $t->decimal('organiser_fee_fixed', 13, 2)->default(0); + $t->decimal('organiser_fees_percentage', 4, 3)->default(0); + $t->unsignedInteger('organiser_id'); + $t->foreign('organiser_id')->references('id')->on('organisers'); + + $t->string('venue_name'); + $t->string('venue_name_full'); + $t->string('location_address', 355); + $t->string('location_address_line_1', 355); + $t->string('location_address_line_2', 355); + $t->string('location_country'); + $t->string('location_country_code'); + $t->string('location_state'); + $t->string('location_post_code'); + $t->string('location_street_number'); + $t->string('location_lat'); + $t->string('location_long'); + $t->string('location_google_place_id'); + + + $t->unsignedInteger('ask_for_all_attendees_info')->default(0); + + $t->text('pre_order_display_message'); + + $t->text('post_order_display_message'); + + $t->text('social_share_text', 'Check Out [event_title] - [event_url]'); + $t->boolean('social_show_facebook')->default(true); + $t->boolean('social_show_linkedin')->default(true); + $t->boolean('social_show_twitter')->default(true); + $t->boolean('social_show_email')->default(true); + $t->boolean('social_show_googleplus')->default(true); + + + + $t->unsignedInteger('location_is_manual')->default(0); + + $t->boolean('is_live')->default(false); + + $t->timestamps(); + $t->softDeletes(); + }); + + + + /* + * Users table + */ + Schema::create('orders', function($t) { + $t->increments('id'); + $t->unsignedInteger('account_id')->index(); + $t->unsignedInteger('order_status_id'); + $t->timestamps(); + $t->softDeletes(); + + $t->string('first_name'); + $t->string('last_name'); + $t->string('email'); + $t->string('ticket_pdf_path', 155); + + $t->string('order_reference', 15); + $t->string('transaction_id', 50); + + $t->decimal('discount', 8, 2); + $t->decimal('booking_fee', 8, 2); + $t->decimal('organiser_booking_fee', 8, 2); + $t->date('order_date')->nullable(); + + $t->text('notes'); + $t->boolean('is_deleted')->default(0); + $t->boolean('is_cancelled')->default(0); + $t->boolean('is_partially_refunded')->default(0); + $t->boolean('is_refunded')->default(0); + + $t->decimal('amount', 13, 2); + $t->decimal('amount_refunded', 13, 2); + + $t->unsignedInteger('event_id')->index(); + $t->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $t->foreign('order_status_id')->references('id')->on('order_statuses')->onDelete('no action'); + }); + + /** + * Tickets table + */ + Schema::create('tickets', function($t) { + + $t->increments('id'); + $t->timestamps(); + $t->softDeletes(); + + + $t->unsignedInteger('edited_by_user_id')->nullable(); + $t->unsignedInteger('account_id')->index(); + $t->unsignedInteger('order_id')->nullable(); + + $t->unsignedInteger('event_id')->index(); + $t->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + + $t->string('title'); + $t->text('description'); + $t->decimal('price', 13, 2); + + $t->integer('max_per_person')->nullable()->default(null); + $t->integer('min_per_person')->nullable()->default(null); + + $t->integer('quantity_available')->nullable()->default(null); + $t->integer('quantity_sold')->default(0); + + $t->dateTime('start_sale_date')->nullable(); + $t->dateTime('end_sale_date')->nullable(); + + $t->decimal('sales_volume', 13, 2); + $t->decimal('organiser_fees_volume', 13, 2); + + $t->tinyInteger('is_paused')->default(0); + + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $t->foreign('order_id')->references('id')->on('orders'); + $t->foreign('edited_by_user_id')->references('id')->on('users'); + + $t->unsignedInteger('public_id')->index(); + + $t->unsignedInteger('user_id'); + $t->foreign('user_id')->references('id')->on('users'); + }); + + Schema::create('order_items', function($table) { + $table->increments('id'); + $table->string('title', 255); + $table->integer('quantity'); + $table->decimal('unit_price', 13, 2); + $table->decimal('unit_booking_fee', 13, 2); + $table->unsignedInteger('order_id'); + $table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade'); + $table->softDeletes(); + }); + + /* + * checkbox, multiselect, select, radio, text etc. + */ +// Schema::create('question_types', function($t) { +// $t->increments('id'); +// $t->string('name'); +// $t->boolean('allow_multiple')->default(FALSE); +// }); +// +// +// Schema::create('questions', function($t) { +// $t->timestamps(); +// $t->softDeletes(); +// +// $t->increments('id'); +// +// $t->string('title', 255); +// $t->text('instructions'); +// $t->text('options'); +// +// +// $t->unsignedInteger('question_type_id'); +// $t->unsignedInteger('account_id')->index(); +// +// $t->tinyInteger('is_required')->default(0); +// +// +// /* +// * If multi select - have question options +// */ +// $t->foreign('question_type_id')->references('id')->on('question_types'); +// $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); +// +// }); +// +// /** +// * Related to each question , can have one or many +// * Whats you name etc? +// * +// */ +// Schema::create('question_options', function($t) { +// $t->increments('id'); +// $t->string('name'); +// $t->integer('question_id')->unsigned()->index(); +// $t->foreign('question_id')->references('id')->on('questions')->onDelete('cascade'); +// }); +// +// +// Schema::create('answers', function($t) { +// $t->increments('id'); +// +// +// $t->integer('question_id')->unsigned()->index(); +// $t->foreign('question_id')->references('id')->on('questions')->onDelete('cascade'); +// +// $t->integer('ticket_id')->unsigned()->index(); +// $t->foreign('ticket_id')->references('id')->on('tickets')->onDelete('cascade'); +// +// $t->text('answer'); +// }); +// +// +// +// +// /** +// * Tickets / Questions pivot table +// */ +// Schema::create('event_question', function($t) { +// $t->increments('id'); +// $t->integer('event_id')->unsigned()->index(); +// $t->foreign('event_id')->references('id')->on('event')->onDelete('cascade'); +// $t->integer('question_id')->unsigned()->index(); +// $t->foreign('question_id')->references('id')->on('questions')->onDelete('cascade'); +// }); +// + + + /** + * Tickets / Orders pivot table + */ + Schema::create('ticket_order', function($t) { + $t->increments('id'); + $t->integer('order_id')->unsigned()->index(); + $t->foreign('order_id')->references('id')->on('orders')->onDelete('cascade'); + $t->integer('ticket_id')->unsigned()->index(); + $t->foreign('ticket_id')->references('id')->on('users')->onDelete('cascade'); + }); + + /** + * Tickets / Questions pivot table + */ +// Schema::create('ticket_question', function($t) { +// $t->increments('id'); +// $t->integer('ticket_id')->unsigned()->index(); +// $t->foreign('ticket_id')->references('id')->on('tickets')->onDelete('cascade'); +// $t->integer('question_id')->unsigned()->index(); +// $t->foreign('question_id')->references('id')->on('questions')->onDelete('cascade'); +// }); + + + + + Schema::create('event_stats', function($table) { + $table->increments('id')->index(); + $table->date('date'); + $table->integer('views')->default(0); + $table->integer('unique_views')->default(0); + $table->integer('tickets_sold')->default(0); + + $table->decimal('sales_volume', 13, 2); + $table->decimal('organiser_fees_volume', 13, 2); + + $table->unsignedInteger('event_id')->index(); + + $table->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + }); + + + Schema::create('attendees', function($t) { + $t->increments('id'); + $t->unsignedInteger('order_id')->index(); + $t->unsignedInteger('event_id')->index(); + $t->unsignedInteger('ticket_id')->index(); + + $t->string('first_name'); + $t->string('last_name'); + $t->string('email'); + + $t->string('reference', 20); + $t->integer('private_reference_number')->index(); + + $t->timestamps(); + $t->softDeletes(); + + $t->boolean('is_cancelled')->default(FALSE); + $t->boolean('has_arrived')->default(FALSE); + $t->dateTime('arrival_time'); + + $t->unsignedInteger('account_id')->index(); + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + + + $t->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + $t->foreign('ticket_id')->references('id')->on('tickets')->onDelete('cascade'); + $t->foreign('order_id')->references('id')->on('orders')->onDelete('cascade'); + }); + + Schema::create('messages', function($table) { + $table->increments('id'); + $table->text('message'); + $table->string('subject'); + $table->integer('recipients'); //ticket_id or 0 for all + $table->unsignedInteger('account_id')->index(); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('event_id'); + $table->unsignedInteger('is_sent', 0); + $table->dateTime('sent_at'); + $table->timestamps(); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('no action'); + }); + + + + Schema::create('event_images', function($t) { + + $t->increments('id'); + $t->string('image_path'); + $t->timestamps(); + + $t->unsignedInteger('event_id'); + $t->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + + $t->unsignedInteger('account_id'); + $t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + + $t->unsignedInteger('user_id'); + $t->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() { + Schema::drop('users'); + } + +} diff --git a/database/migrations/2014_04_08_232044_setup_countries_table.php b/database/migrations/2014_04_08_232044_setup_countries_table.php new file mode 100644 index 00000000..5a459875 --- /dev/null +++ b/database/migrations/2014_04_08_232044_setup_countries_table.php @@ -0,0 +1,45 @@ +integer('id')->index(); + $table->string('capital', 255)->nullable(); + $table->string('citizenship', 255)->nullable(); + $table->string('country_code', 3)->default(''); + $table->string('currency', 255)->nullable(); + $table->string('currency_code', 255)->nullable(); + $table->string('currency_sub_unit', 255)->nullable(); + $table->string('full_name', 255)->nullable(); + $table->string('iso_3166_2', 2)->default(''); + $table->string('iso_3166_3', 3)->default(''); + $table->string('name', 255)->default(''); + $table->string('region_code', 3)->default(''); + $table->string('sub_region_code', 3)->default(''); + $table->boolean('eea')->default(0); + + $table->primary('id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('Countries'); + } + +} diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php new file mode 100644 index 00000000..679df38f --- /dev/null +++ b/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -0,0 +1,33 @@ +string('email')->index(); + $table->string('token')->index(); + $table->timestamp('created_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('password_resets'); + } + +} diff --git a/database/migrations/2014_11_07_132018_add_affiliates_table.php b/database/migrations/2014_11_07_132018_add_affiliates_table.php new file mode 100644 index 00000000..18b5e2f9 --- /dev/null +++ b/database/migrations/2014_11_07_132018_add_affiliates_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->string('name', 125); + $table->integer('visits'); + $table->integer('tickets_sold'); + $table->decimal('sales_volume', 10, 2); + $table->timestamp('last_visit'); + $table->unsignedInteger('account_id')->index(); + $table->unsignedInteger('event_id'); + $table->timestamps(); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('event_id')->references('id')->on('events')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() { + // + } + +} diff --git a/database/migrations/2014_11_17_011806_create_failed_jobs_table.php b/database/migrations/2014_11_17_011806_create_failed_jobs_table.php new file mode 100644 index 00000000..61efc17d --- /dev/null +++ b/database/migrations/2014_11_17_011806_create_failed_jobs_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->text('connection'); + $table->text('queue'); + $table->text('payload'); + $table->timestamp('failed_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('failed_jobs'); + } + +} diff --git a/database/seeds/.gitkeep b/database/seeds/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/database/seeds/ConstantsSeeder.php b/database/seeds/ConstantsSeeder.php new file mode 100644 index 00000000..4668f306 --- /dev/null +++ b/database/seeds/ConstantsSeeder.php @@ -0,0 +1,538 @@ + 1, + 'name' => 'Completed' + ], + [ + 'id' => 2, + 'name' => 'Refunded' + ], + [ + 'id' => 3, + 'name' => 'Partially Refunded' + ], + [ + 'id' => 4, + 'name' => 'Cancelled' + ], + ]; + + DB::table('order_statuses')->insert($order_statuses); + + $ticket_statuses = [ + [ + 'id' => 1, + 'name' => 'Sold Out' + ], + [ + 'id' => 2, + 'name' => 'Sales Have Ended' + ], + [ + 'id' => 3, + 'name' => 'Not On Sale Yet' + ], + [ + 'id' => 4, + 'name' => 'On Sale' + ], + [ + 'id' => 5, + 'name' => 'On Sale' + ] + ]; + + DB::table('ticket_statuses')->insert($ticket_statuses); + + + /* + * Question Types + */ + $question_types = [ + [ + 'id' => 0, + 'name' => 'Single-line text box', + 'allow_multiple' => 0 + ], + [ + 'id' => 1, + 'name' => 'Multi-line text box', + 'allow_multiple' => 0 + ], + [ + 'id' => 2, + 'name' => 'Drop down options (single section)', + 'allow_multiple' => 0 + ], + [ + 'id' => 3, + 'name' => 'Drop down options (multiple section)', + 'allow_multiple' => 1 + ], + [ + 'id' => 4, + 'name' => 'Check Boxes', + 'allow_multiple' => 1 + ] + + ]; + + //DB::table('question_types')->insert($question_types); + + + + $currencies = array( + array( + 'id' => 1, + 'title' => 'U.S. Dollar', + 'symbol_left' => '$', + 'symbol_right' => '', + 'code' => 'USD', + 'decimal_place' => 2, + 'value' => 1.00000000, + 'decimal_point' => '.', + 'thousand_point' => ',', + 'status' => 1, + 'created_at' => '2013-11-29 19:51:38', + 'updated_at' => '2013-11-29 19:51:38', + ), + array( + 'id' => 2, + 'title' => 'Euro', + 'symbol_left' => '€', + 'symbol_right' => '', + 'code' => 'EUR', + 'decimal_place' => 2, + 'value' => 0.74970001, + 'decimal_point' => '.', + 'thousand_point' => ',', + 'status' => 1, + 'created_at' => '2013-11-29 19:51:38', + 'updated_at' => '2013-11-29 19:51:38', + ), + array( + 'id' => 3, + 'title' => 'Pound Sterling', + 'symbol_left' => '£', + 'symbol_right' => '', + 'code' => 'GBP', + 'decimal_place' => 2, + 'value' => 0.62220001, + 'decimal_point' => '.', + 'thousand_point' => ',', + 'status' => 1, + 'created_at' => '2013-11-29 19:51:38', + 'updated_at' => '2013-11-29 19:51:38', + ), + array( + 'id' => 4, + 'title' => 'Australian Dollar', + 'symbol_left' => '$', + 'symbol_right' => '', + 'code' => 'AUD', + 'decimal_place' => 2, + 'value' => 0.94790000, + 'decimal_point' => '.', + 'thousand_point' => ',', + 'status' => 1, + 'created_at' => '2013-11-29 19:51:38', + 'updated_at' => '2013-11-29 19:51:38', + ), + array( + 'id' => 5, + 'title' => 'Canadian Dollar', + 'symbol_left' => '$', + 'symbol_right' => '', + 'code' => 'CAD', + 'decimal_place' => 2, + 'value' => 0.98500001, + 'decimal_point' => '.', + 'thousand_point' => ',', + 'status' => 1, + 'created_at' => '2013-11-29 19:51:38', + 'updated_at' => '2013-11-29 19:51:38', + ) +// array( +// 'id' => 6, +// 'title' => 'Czech Koruna', +// 'symbol_left' => '', +// 'symbol_right' => 'Kč', +// 'code' => 'CZK', +// 'decimal_place' => 2, +// 'value' => 19.16900063, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 7, +// 'title' => 'Danish Krone', +// 'symbol_left' => 'kr', +// 'symbol_right' => '', +// 'code' => 'DKK', +// 'decimal_place' => 2, +// 'value' => 5.59420013, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 8, +// 'title' => 'Hong Kong Dollar', +// 'symbol_left' => '$', +// 'symbol_right' => '', +// 'code' => 'HKD', +// 'decimal_place' => 2, +// 'value' => 7.75290012, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 9, +// 'title' => 'Hungarian Forint', +// 'symbol_left' => 'Ft', +// 'symbol_right' => '', +// 'code' => 'HUF', +// 'decimal_place' => 2, +// 'value' => 221.27000427, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 10, +// 'title' => 'Israeli New Sheqel', +// 'symbol_left' => '?', +// 'symbol_right' => '', +// 'code' => 'ILS', +// 'decimal_place' => 2, +// 'value' => 3.73559999, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 11, +// 'title' => 'Japanese Yen', +// 'symbol_left' => '¥', +// 'symbol_right' => '', +// 'code' => 'JPY', +// 'decimal_place' => 2, +// 'value' => 88.76499939, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 12, +// 'title' => 'Mexican Peso', +// 'symbol_left' => '$', +// 'symbol_right' => '', +// 'code' => 'MXN', +// 'decimal_place' => 2, +// 'value' => 12.63899994, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 13, +// 'title' => 'Norwegian Krone', +// 'symbol_left' => 'kr', +// 'symbol_right' => '', +// 'code' => 'NOK', +// 'decimal_place' => 2, +// 'value' => 5.52229977, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 14, +// 'title' => 'New Zealand Dollar', +// 'symbol_left' => '$', +// 'symbol_right' => '', +// 'code' => 'NZD', +// 'decimal_place' => 2, +// 'value' => 1.18970001, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 15, +// 'title' => 'Philippine Peso', +// 'symbol_left' => 'Php', +// 'symbol_right' => '', +// 'code' => 'PHP', +// 'decimal_place' => 2, +// 'value' => 40.58000183, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 16, +// 'title' => 'Polish Zloty', +// 'symbol_left' => '', +// 'symbol_right' => 'zł', +// 'code' => 'PLN', +// 'decimal_place' => 2, +// 'value' => 3.08590007, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 17, +// 'title' => 'Singapore Dollar', +// 'symbol_left' => '$', +// 'symbol_right' => '', +// 'code' => 'SGD', +// 'decimal_place' => 2, +// 'value' => 1.22560000, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 18, +// 'title' => 'Swedish Krona', +// 'symbol_left' => 'kr', +// 'symbol_right' => '', +// 'code' => 'SEK', +// 'decimal_place' => 2, +// 'value' => 6.45870018, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 19, +// 'title' => 'Swiss Franc', +// 'symbol_left' => 'CHF', +// 'symbol_right' => '', +// 'code' => 'CHF', +// 'decimal_place' => 2, +// 'value' => 0.92259997, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 20, +// 'title' => 'Taiwan New Dollar', +// 'symbol_left' => 'NT$', +// 'symbol_right' => '', +// 'code' => 'TWD', +// 'decimal_place' => 2, +// 'value' => 28.95199966, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ), +// array( +// 'id' => 21, +// 'title' => 'Thai Baht', +// 'symbol_left' => '฿', +// 'symbol_right' => '', +// 'code' => 'THB', +// 'decimal_place' => 2, +// 'value' => 30.09499931, +// 'decimal_point' => '.', +// 'thousand_point' => ',', +// 'status' => 1, +// 'created_at' => '2013-11-29 19:51:38', +// 'updated_at' => '2013-11-29 19:51:38', +// ) + ); + + DB::table('currencies')->insert($currencies); + + \App\Models\DateTimeFormat::create(array('format' => 'd/M/Y g:i a', 'label' => '10/Mar/2013')); + \App\Models\DateTimeFormat::create(array('format' => 'd-M-Yk g:i a', 'label' => '10-Mar-2013')); + \App\Models\DateTimeFormat::create(array('format' => 'd/F/Y g:i a', 'label' => '10/March/2013')); + \App\Models\DateTimeFormat::create(array('format' => 'd-F-Y g:i a', 'label' => '10-March-2013')); + \App\Models\DateTimeFormat::create(array('format' => 'M j, Y g:i a', 'label' => 'Mar 10, 2013 6:15 pm')); + \App\Models\DateTimeFormat::create(array('format' => 'F j, Y g:i a', 'label' => 'March 10, 2013 6:15 pm')); + \App\Models\DateTimeFormat::create(array('format' => 'D M jS, Y g:ia', 'label' => 'Mon March 10th, 2013 6:15 pm')); + + \App\Models\DateFormat::create(array('format' => 'd/M/Y', 'picker_format' => 'dd/M/yyyy', 'label' => '10/Mar/2013')); + \App\Models\DateFormat::create(array('format' => 'd-M-Y', 'picker_format' => 'dd-M-yyyy', 'label' => '10-Mar-2013')); + \App\Models\DateFormat::create(array('format' => 'd/F/Y', 'picker_format' => 'dd/MM/yyyy', 'label' => '10/March/2013')); + \App\Models\DateFormat::create(array('format' => 'd-F-Y', 'picker_format' => 'dd-MM-yyyy', 'label' => '10-March-2013')); + \App\Models\DateFormat::create(array('format' => 'M j, Y', 'picker_format' => 'M d, yyyy', 'label' => 'Mar 10, 2013')); + \App\Models\DateFormat::create(array('format' => 'F j, Y', 'picker_format' => 'MM d, yyyy', 'label' => 'March 10, 2013')); + \App\Models\DateFormat::create(array('format' => 'D M j, Y', 'picker_format' => 'D MM d, yyyy', 'label' => 'Mon March 10, 2013')); + + /* + d, dd: Numeric date, no leading zero and leading zero, respectively. Eg, 5, 05. + D, DD: Abbreviated and full weekday names, respectively. Eg, Mon, Monday. + m, mm: Numeric month, no leading zero and leading zero, respectively. Eg, 7, 07. + M, MM: Abbreviated and full month names, respectively. Eg, Jan, January + yy, yyyy: 2- and 4-digit years, respectively. Eg, 12, 2012.) + */ + + + + $timezones = array( + 'Pacific/Midway' => "(GMT-11:00) Midway Island", + 'US/Samoa' => "(GMT-11:00) Samoa", + 'US/Hawaii' => "(GMT-10:00) Hawaii", + 'US/Alaska' => "(GMT-09:00) Alaska", + 'US/Pacific' => "(GMT-08:00) Pacific Time (US & Canada)", + 'America/Tijuana' => "(GMT-08:00) Tijuana", + 'US/Arizona' => "(GMT-07:00) Arizona", + 'US/Mountain' => "(GMT-07:00) Mountain Time (US & Canada)", + 'America/Chihuahua' => "(GMT-07:00) Chihuahua", + 'America/Mazatlan' => "(GMT-07:00) Mazatlan", + 'America/Mexico_City' => "(GMT-06:00) Mexico City", + 'America/Monterrey' => "(GMT-06:00) Monterrey", + 'Canada/Saskatchewan' => "(GMT-06:00) Saskatchewan", + 'US/Central' => "(GMT-06:00) Central Time (US & Canada)", + 'US/Eastern' => "(GMT-05:00) Eastern Time (US & Canada)", + 'US/East-Indiana' => "(GMT-05:00) Indiana (East)", + 'America/Bogota' => "(GMT-05:00) Bogota", + 'America/Lima' => "(GMT-05:00) Lima", + 'America/Caracas' => "(GMT-04:30) Caracas", + 'Canada/Atlantic' => "(GMT-04:00) Atlantic Time (Canada)", + 'America/La_Paz' => "(GMT-04:00) La Paz", + 'America/Santiago' => "(GMT-04:00) Santiago", + 'Canada/Newfoundland' => "(GMT-03:30) Newfoundland", + 'America/Buenos_Aires' => "(GMT-03:00) Buenos Aires", + 'Greenland' => "(GMT-03:00) Greenland", + 'Atlantic/Stanley' => "(GMT-02:00) Stanley", + 'Atlantic/Azores' => "(GMT-01:00) Azores", + 'Atlantic/Cape_Verde' => "(GMT-01:00) Cape Verde Is.", + 'Africa/Casablanca' => "(GMT) Casablanca", + 'Europe/Dublin' => "(GMT) Dublin", + 'Europe/Lisbon' => "(GMT) Lisbon", + 'Europe/London' => "(GMT) London", + 'Africa/Monrovia' => "(GMT) Monrovia", + 'Europe/Amsterdam' => "(GMT+01:00) Amsterdam", + 'Europe/Belgrade' => "(GMT+01:00) Belgrade", + 'Europe/Berlin' => "(GMT+01:00) Berlin", + 'Europe/Bratislava' => "(GMT+01:00) Bratislava", + 'Europe/Brussels' => "(GMT+01:00) Brussels", + 'Europe/Budapest' => "(GMT+01:00) Budapest", + 'Europe/Copenhagen' => "(GMT+01:00) Copenhagen", + 'Europe/Ljubljana' => "(GMT+01:00) Ljubljana", + 'Europe/Madrid' => "(GMT+01:00) Madrid", + 'Europe/Paris' => "(GMT+01:00) Paris", + 'Europe/Prague' => "(GMT+01:00) Prague", + 'Europe/Rome' => "(GMT+01:00) Rome", + 'Europe/Sarajevo' => "(GMT+01:00) Sarajevo", + 'Europe/Skopje' => "(GMT+01:00) Skopje", + 'Europe/Stockholm' => "(GMT+01:00) Stockholm", + 'Europe/Vienna' => "(GMT+01:00) Vienna", + 'Europe/Warsaw' => "(GMT+01:00) Warsaw", + 'Europe/Zagreb' => "(GMT+01:00) Zagreb", + 'Europe/Athens' => "(GMT+02:00) Athens", + 'Europe/Bucharest' => "(GMT+02:00) Bucharest", + 'Africa/Cairo' => "(GMT+02:00) Cairo", + 'Africa/Harare' => "(GMT+02:00) Harare", + 'Europe/Helsinki' => "(GMT+02:00) Helsinki", + 'Europe/Istanbul' => "(GMT+02:00) Istanbul", + 'Asia/Jerusalem' => "(GMT+02:00) Jerusalem", + 'Europe/Kiev' => "(GMT+02:00) Kyiv", + 'Europe/Minsk' => "(GMT+02:00) Minsk", + 'Europe/Riga' => "(GMT+02:00) Riga", + 'Europe/Sofia' => "(GMT+02:00) Sofia", + 'Europe/Tallinn' => "(GMT+02:00) Tallinn", + 'Europe/Vilnius' => "(GMT+02:00) Vilnius", + 'Asia/Baghdad' => "(GMT+03:00) Baghdad", + 'Asia/Kuwait' => "(GMT+03:00) Kuwait", + 'Africa/Nairobi' => "(GMT+03:00) Nairobi", + 'Asia/Riyadh' => "(GMT+03:00) Riyadh", + 'Asia/Tehran' => "(GMT+03:30) Tehran", + 'Europe/Moscow' => "(GMT+04:00) Moscow", + 'Asia/Baku' => "(GMT+04:00) Baku", + 'Europe/Volgograd' => "(GMT+04:00) Volgograd", + 'Asia/Muscat' => "(GMT+04:00) Muscat", + 'Asia/Tbilisi' => "(GMT+04:00) Tbilisi", + 'Asia/Yerevan' => "(GMT+04:00) Yerevan", + 'Asia/Kabul' => "(GMT+04:30) Kabul", + 'Asia/Karachi' => "(GMT+05:00) Karachi", + 'Asia/Tashkent' => "(GMT+05:00) Tashkent", + 'Asia/Kolkata' => "(GMT+05:30) Kolkata", + 'Asia/Kathmandu' => "(GMT+05:45) Kathmandu", + 'Asia/Yekaterinburg' => "(GMT+06:00) Ekaterinburg", + 'Asia/Almaty' => "(GMT+06:00) Almaty", + 'Asia/Dhaka' => "(GMT+06:00) Dhaka", + 'Asia/Novosibirsk' => "(GMT+07:00) Novosibirsk", + 'Asia/Bangkok' => "(GMT+07:00) Bangkok", + 'Asia/Jakarta' => "(GMT+07:00) Jakarta", + 'Asia/Krasnoyarsk' => "(GMT+08:00) Krasnoyarsk", + 'Asia/Chongqing' => "(GMT+08:00) Chongqing", + 'Asia/Hong_Kong' => "(GMT+08:00) Hong Kong", + 'Asia/Kuala_Lumpur' => "(GMT+08:00) Kuala Lumpur", + 'Australia/Perth' => "(GMT+08:00) Perth", + 'Asia/Singapore' => "(GMT+08:00) Singapore", + 'Asia/Taipei' => "(GMT+08:00) Taipei", + 'Asia/Ulaanbaatar' => "(GMT+08:00) Ulaan Bataar", + 'Asia/Urumqi' => "(GMT+08:00) Urumqi", + 'Asia/Irkutsk' => "(GMT+09:00) Irkutsk", + 'Asia/Seoul' => "(GMT+09:00) Seoul", + 'Asia/Tokyo' => "(GMT+09:00) Tokyo", + 'Australia/Adelaide' => "(GMT+09:30) Adelaide", + 'Australia/Darwin' => "(GMT+09:30) Darwin", + 'Asia/Yakutsk' => "(GMT+10:00) Yakutsk", + 'Australia/Brisbane' => "(GMT+10:00) Brisbane", + 'Australia/Canberra' => "(GMT+10:00) Canberra", + 'Pacific/Guam' => "(GMT+10:00) Guam", + 'Australia/Hobart' => "(GMT+10:00) Hobart", + 'Australia/Melbourne' => "(GMT+10:00) Melbourne", + 'Pacific/Port_Moresby' => "(GMT+10:00) Port Moresby", + 'Australia/Sydney' => "(GMT+10:00) Sydney", + 'Asia/Vladivostok' => "(GMT+11:00) Vladivostok", + 'Asia/Magadan' => "(GMT+12:00) Magadan", + 'Pacific/Auckland' => "(GMT+12:00) Auckland", + 'Pacific/Fiji' => "(GMT+12:00) Fiji", + ); + + foreach ($timezones as $name => $location) { + \App\Models\Timezone::create(array('name' => $name, 'location' => $location)); + } + } + +} diff --git a/database/seeds/CountriesSeeder.php b/database/seeds/CountriesSeeder.php new file mode 100644 index 00000000..cf52bd96 --- /dev/null +++ b/database/seeds/CountriesSeeder.php @@ -0,0 +1,39 @@ +delete(); + + //Get all of the countries + $countries = json_decode(file_get_contents(database_path().'/seeds/countries.json'), true); + foreach ($countries as $countryId => $country){ + DB::table('Countries')->insert(array( + 'id' => $countryId, + 'capital' => ((isset($country['capital'])) ? $country['capital'] : null), + 'citizenship' => ((isset($country['citizenship'])) ? $country['citizenship'] : null), + 'country_code' => $country['country-code'], + 'currency' => ((isset($country['currency'])) ? $country['currency'] : null), + 'currency_code' => ((isset($country['currency_code'])) ? $country['currency_code'] : null), + 'currency_sub_unit' => ((isset($country['currency_sub_unit'])) ? $country['currency_sub_unit'] : null), + 'full_name' => ((isset($country['full_name'])) ? $country['full_name'] : null), + 'iso_3166_2' => $country['iso_3166_2'], + 'iso_3166_3' => $country['iso_3166_3'], + 'name' => $country['name'], + 'region_code' => $country['region-code'], + 'sub_region_code' => $country['sub-region-code'], + 'eea' => (bool)$country['eea'] + )); + } + } +} \ No newline at end of file diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php new file mode 100644 index 00000000..a310855c --- /dev/null +++ b/database/seeds/DatabaseSeeder.php @@ -0,0 +1,24 @@ +call('ConstantsSeeder'); + $this->command->info('Seeded the constants!'); + + // $this->call('UserTableSeeder'); + $this->call('CountriesSeeder'); + $this->command->info('Seeded the countries!'); + } + +} diff --git a/database/seeds/countries.json b/database/seeds/countries.json new file mode 100644 index 00000000..86b45f2e --- /dev/null +++ b/database/seeds/countries.json @@ -0,0 +1,4451 @@ +{ + "4":{ + "capital":"Kabul", + "citizenship":"Afghan", + "country-code":"004", + "currency":"afghani", + "currency_code":"AFN", + "currency_sub_unit":"pul", + "full_name":"Islamic Republic of Afghanistan", + "iso_3166_2":"AF", + "iso_3166_3":"AFG", + "name":"Afghanistan", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"93", + "currency_symbol":"\u060b", + "flag":"AF.png" + }, + "8":{ + "capital":"Tirana", + "citizenship":"Albanian", + "country-code":"008", + "currency":"lek", + "currency_code":"ALL", + "currency_sub_unit":"(qindar (pl. qindarka))", + "full_name":"Republic of Albania", + "iso_3166_2":"AL", + "iso_3166_3":"ALB", + "name":"Albania", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"355", + "currency_symbol":"Lek", + "flag":"AL.png" + }, + "10":{ + "capital":"Antartica", + "citizenship":"of Antartica", + "country-code":"010", + "currency":"", + "currency_code":"", + "currency_sub_unit":"", + "full_name":"Antarctica", + "iso_3166_2":"AQ", + "iso_3166_3":"ATA", + "name":"Antarctica", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"672", + "currency_symbol":"", + "flag":"AQ.png" + }, + "12":{ + "capital":"Algiers", + "citizenship":"Algerian", + "country-code":"012", + "currency":"Algerian dinar", + "currency_code":"DZD", + "currency_sub_unit":"centime", + "full_name":"People\u2019s Democratic Republic of Algeria", + "iso_3166_2":"DZ", + "iso_3166_3":"DZA", + "name":"Algeria", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"213", + "currency_symbol":"DZD", + "flag":"DZ.png" + }, + "16":{ + "capital":"Pago Pago", + "citizenship":"American Samoan", + "country-code":"016", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Territory of American", + "iso_3166_2":"AS", + "iso_3166_3":"ASM", + "name":"American Samoa", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"AS.png" + }, + "20":{ + "capital":"Andorra la Vella", + "citizenship":"Andorran", + "country-code":"020", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Principality of Andorra", + "iso_3166_2":"AD", + "iso_3166_3":"AND", + "name":"Andorra", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"376", + "currency_symbol":"\u20ac", + "flag":"AD.png" + }, + "24":{ + "capital":"Luanda", + "citizenship":"Angolan", + "country-code":"024", + "currency":"kwanza", + "currency_code":"AOA", + "currency_sub_unit":"c\u00eantimo", + "full_name":"Republic of Angola", + "iso_3166_2":"AO", + "iso_3166_3":"AGO", + "name":"Angola", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"244", + "currency_symbol":"Kz", + "flag":"AO.png" + }, + "28":{ + "capital":"St John\u2019s", + "citizenship":"of Antigua and Barbuda", + "country-code":"028", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Antigua and Barbuda", + "iso_3166_2":"AG", + "iso_3166_3":"ATG", + "name":"Antigua and Barbuda", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"AG.png" + }, + "31":{ + "capital":"Baku", + "citizenship":"Azerbaijani", + "country-code":"031", + "currency":"Azerbaijani manat", + "currency_code":"AZN", + "currency_sub_unit":"kepik (inv.)", + "full_name":"Republic of Azerbaijan", + "iso_3166_2":"AZ", + "iso_3166_3":"AZE", + "name":"Azerbaijan", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"994", + "currency_symbol":"\u043c\u0430\u043d", + "flag":"AZ.png" + }, + "32":{ + "capital":"Buenos Aires", + "citizenship":"Argentinian", + "country-code":"032", + "currency":"Argentine peso", + "currency_code":"ARS", + "currency_sub_unit":"centavo", + "full_name":"Argentine Republic", + "iso_3166_2":"AR", + "iso_3166_3":"ARG", + "name":"Argentina", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"54", + "currency_symbol":"$", + "flag":"AR.png" + }, + "36":{ + "capital":"Canberra", + "citizenship":"Australian", + "country-code":"036", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Commonwealth of Australia", + "iso_3166_2":"AU", + "iso_3166_3":"AUS", + "name":"Australia", + "region-code":"009", + "sub-region-code":"053", + "eea":false, + "calling_code":"61", + "currency_symbol":"$", + "flag":"AU.png" + }, + "40":{ + "capital":"Vienna", + "citizenship":"Austrian", + "country-code":"040", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Austria", + "iso_3166_2":"AT", + "iso_3166_3":"AUT", + "name":"Austria", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"43", + "currency_symbol":"\u20ac", + "flag":"AT.png" + }, + "44":{ + "capital":"Nassau", + "citizenship":"Bahamian", + "country-code":"044", + "currency":"Bahamian dollar", + "currency_code":"BSD", + "currency_sub_unit":"cent", + "full_name":"Commonwealth of the Bahamas", + "iso_3166_2":"BS", + "iso_3166_3":"BHS", + "name":"Bahamas", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"BS.png" + }, + "48":{ + "capital":"Manama", + "citizenship":"Bahraini", + "country-code":"048", + "currency":"Bahraini dinar", + "currency_code":"BHD", + "currency_sub_unit":"fils (inv.)", + "full_name":"Kingdom of Bahrain", + "iso_3166_2":"BH", + "iso_3166_3":"BHR", + "name":"Bahrain", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"973", + "currency_symbol":"BHD", + "flag":"BH.png" + }, + "50":{ + "capital":"Dhaka", + "citizenship":"Bangladeshi", + "country-code":"050", + "currency":"taka (inv.)", + "currency_code":"BDT", + "currency_sub_unit":"poisha (inv.)", + "full_name":"People\u2019s Republic of Bangladesh", + "iso_3166_2":"BD", + "iso_3166_3":"BGD", + "name":"Bangladesh", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"880", + "currency_symbol":"BDT", + "flag":"BD.png" + }, + "51":{ + "capital":"Yerevan", + "citizenship":"Armenian", + "country-code":"051", + "currency":"dram (inv.)", + "currency_code":"AMD", + "currency_sub_unit":"luma", + "full_name":"Republic of Armenia", + "iso_3166_2":"AM", + "iso_3166_3":"ARM", + "name":"Armenia", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"374", + "currency_symbol":"AMD", + "flag":"AM.png" + }, + "52":{ + "capital":"Bridgetown", + "citizenship":"Barbadian", + "country-code":"052", + "currency":"Barbados dollar", + "currency_code":"BBD", + "currency_sub_unit":"cent", + "full_name":"Barbados", + "iso_3166_2":"BB", + "iso_3166_3":"BRB", + "name":"Barbados", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"BB.png" + }, + "56":{ + "capital":"Brussels", + "citizenship":"Belgian", + "country-code":"056", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Kingdom of Belgium", + "iso_3166_2":"BE", + "iso_3166_3":"BEL", + "name":"Belgium", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"32", + "currency_symbol":"\u20ac", + "flag":"BE.png" + }, + "60":{ + "capital":"Hamilton", + "citizenship":"Bermudian", + "country-code":"060", + "currency":"Bermuda dollar", + "currency_code":"BMD", + "currency_sub_unit":"cent", + "full_name":"Bermuda", + "iso_3166_2":"BM", + "iso_3166_3":"BMU", + "name":"Bermuda", + "region-code":"019", + "sub-region-code":"021", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"BM.png" + }, + "64":{ + "capital":"Thimphu", + "citizenship":"Bhutanese", + "country-code":"064", + "currency":"ngultrum (inv.)", + "currency_code":"BTN", + "currency_sub_unit":"chhetrum (inv.)", + "full_name":"Kingdom of Bhutan", + "iso_3166_2":"BT", + "iso_3166_3":"BTN", + "name":"Bhutan", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"975", + "currency_symbol":"BTN", + "flag":"BT.png" + }, + "68":{ + "capital":"Sucre (BO1)", + "citizenship":"Bolivian", + "country-code":"068", + "currency":"boliviano", + "currency_code":"BOB", + "currency_sub_unit":"centavo", + "full_name":"Plurinational State of Bolivia", + "iso_3166_2":"BO", + "iso_3166_3":"BOL", + "name":"Bolivia, Plurinational State of", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"591", + "currency_symbol":"$b", + "flag":"BO.png" + }, + "70":{ + "capital":"Sarajevo", + "citizenship":"of Bosnia and Herzegovina", + "country-code":"070", + "currency":"convertible mark", + "currency_code":"BAM", + "currency_sub_unit":"fening", + "full_name":"Bosnia and Herzegovina", + "iso_3166_2":"BA", + "iso_3166_3":"BIH", + "name":"Bosnia and Herzegovina", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"387", + "currency_symbol":"KM", + "flag":"BA.png" + }, + "72":{ + "capital":"Gaborone", + "citizenship":"Botswanan", + "country-code":"072", + "currency":"pula (inv.)", + "currency_code":"BWP", + "currency_sub_unit":"thebe (inv.)", + "full_name":"Republic of Botswana", + "iso_3166_2":"BW", + "iso_3166_3":"BWA", + "name":"Botswana", + "region-code":"002", + "sub-region-code":"018", + "eea":false, + "calling_code":"267", + "currency_symbol":"P", + "flag":"BW.png" + }, + "74":{ + "capital":"Bouvet island", + "citizenship":"of Bouvet island", + "country-code":"074", + "currency":"", + "currency_code":"", + "currency_sub_unit":"", + "full_name":"Bouvet Island", + "iso_3166_2":"BV", + "iso_3166_3":"BVT", + "name":"Bouvet Island", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"47", + "currency_symbol":"kr", + "flag":"BV.png" + }, + "76":{ + "capital":"Brasilia", + "citizenship":"Brazilian", + "country-code":"076", + "currency":"real (pl. reais)", + "currency_code":"BRL", + "currency_sub_unit":"centavo", + "full_name":"Federative Republic of Brazil", + "iso_3166_2":"BR", + "iso_3166_3":"BRA", + "name":"Brazil", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"55", + "currency_symbol":"R$", + "flag":"BR.png" + }, + "84":{ + "capital":"Belmopan", + "citizenship":"Belizean", + "country-code":"084", + "currency":"Belize dollar", + "currency_code":"BZD", + "currency_sub_unit":"cent", + "full_name":"Belize", + "iso_3166_2":"BZ", + "iso_3166_3":"BLZ", + "name":"Belize", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"501", + "currency_symbol":"BZ$", + "flag":"BZ.png" + }, + "86":{ + "capital":"Diego Garcia", + "citizenship":"Changosian", + "country-code":"086", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"British Indian Ocean Territory", + "iso_3166_2":"IO", + "iso_3166_3":"IOT", + "name":"British Indian Ocean Territory", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"246", + "currency_symbol":"$", + "flag":"IO.png" + }, + "90":{ + "capital":"Honiara", + "citizenship":"Solomon Islander", + "country-code":"090", + "currency":"Solomon Islands dollar", + "currency_code":"SBD", + "currency_sub_unit":"cent", + "full_name":"Solomon Islands", + "iso_3166_2":"SB", + "iso_3166_3":"SLB", + "name":"Solomon Islands", + "region-code":"009", + "sub-region-code":"054", + "eea":false, + "calling_code":"677", + "currency_symbol":"$", + "flag":"SB.png" + }, + "92":{ + "capital":"Road Town", + "citizenship":"British Virgin Islander;", + "country-code":"092", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"British Virgin Islands", + "iso_3166_2":"VG", + "iso_3166_3":"VGB", + "name":"Virgin Islands, British", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"VG.png" + }, + "96":{ + "capital":"Bandar Seri Begawan", + "citizenship":"Bruneian", + "country-code":"096", + "currency":"Brunei dollar", + "currency_code":"BND", + "currency_sub_unit":"sen (inv.)", + "full_name":"Brunei Darussalam", + "iso_3166_2":"BN", + "iso_3166_3":"BRN", + "name":"Brunei Darussalam", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"673", + "currency_symbol":"$", + "flag":"BN.png" + }, + "100":{ + "capital":"Sofia", + "citizenship":"Bulgarian", + "country-code":"100", + "currency":"lev (pl. leva)", + "currency_code":"BGN", + "currency_sub_unit":"stotinka", + "full_name":"Republic of Bulgaria", + "iso_3166_2":"BG", + "iso_3166_3":"BGR", + "name":"Bulgaria", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"359", + "currency_symbol":"\u043b\u0432", + "flag":"BG.png" + }, + "104":{ + "capital":"Yangon", + "citizenship":"Burmese", + "country-code":"104", + "currency":"kyat", + "currency_code":"MMK", + "currency_sub_unit":"pya", + "full_name":"Union of Myanmar\/", + "iso_3166_2":"MM", + "iso_3166_3":"MMR", + "name":"Myanmar", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"95", + "currency_symbol":"K", + "flag":"MM.png" + }, + "108":{ + "capital":"Bujumbura", + "citizenship":"Burundian", + "country-code":"108", + "currency":"Burundi franc", + "currency_code":"BIF", + "currency_sub_unit":"centime", + "full_name":"Republic of Burundi", + "iso_3166_2":"BI", + "iso_3166_3":"BDI", + "name":"Burundi", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"257", + "currency_symbol":"BIF", + "flag":"BI.png" + }, + "112":{ + "capital":"Minsk", + "citizenship":"Belarusian", + "country-code":"112", + "currency":"Belarusian rouble", + "currency_code":"BYR", + "currency_sub_unit":"kopek", + "full_name":"Republic of Belarus", + "iso_3166_2":"BY", + "iso_3166_3":"BLR", + "name":"Belarus", + "region-code":"150", + "sub-region-code":"151", + "eea":false, + "calling_code":"375", + "currency_symbol":"p.", + "flag":"BY.png" + }, + "116":{ + "capital":"Phnom Penh", + "citizenship":"Cambodian", + "country-code":"116", + "currency":"riel", + "currency_code":"KHR", + "currency_sub_unit":"sen (inv.)", + "full_name":"Kingdom of Cambodia", + "iso_3166_2":"KH", + "iso_3166_3":"KHM", + "name":"Cambodia", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"855", + "currency_symbol":"\u17db", + "flag":"KH.png" + }, + "120":{ + "capital":"Yaound\u00e9", + "citizenship":"Cameroonian", + "country-code":"120", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Republic of Cameroon", + "iso_3166_2":"CM", + "iso_3166_3":"CMR", + "name":"Cameroon", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"237", + "currency_symbol":"FCF", + "flag":"CM.png" + }, + "124":{ + "capital":"Ottawa", + "citizenship":"Canadian", + "country-code":"124", + "currency":"Canadian dollar", + "currency_code":"CAD", + "currency_sub_unit":"cent", + "full_name":"Canada", + "iso_3166_2":"CA", + "iso_3166_3":"CAN", + "name":"Canada", + "region-code":"019", + "sub-region-code":"021", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"CA.png" + }, + "132":{ + "capital":"Praia", + "citizenship":"Cape Verdean", + "country-code":"132", + "currency":"Cape Verde escudo", + "currency_code":"CVE", + "currency_sub_unit":"centavo", + "full_name":"Republic of Cape Verde", + "iso_3166_2":"CV", + "iso_3166_3":"CPV", + "name":"Cape Verde", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"238", + "currency_symbol":"CVE", + "flag":"CV.png" + }, + "136":{ + "capital":"George Town", + "citizenship":"Caymanian", + "country-code":"136", + "currency":"Cayman Islands dollar", + "currency_code":"KYD", + "currency_sub_unit":"cent", + "full_name":"Cayman Islands", + "iso_3166_2":"KY", + "iso_3166_3":"CYM", + "name":"Cayman Islands", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"KY.png" + }, + "140":{ + "capital":"Bangui", + "citizenship":"Central African", + "country-code":"140", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Central African Republic", + "iso_3166_2":"CF", + "iso_3166_3":"CAF", + "name":"Central African Republic", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"236", + "currency_symbol":"FCF", + "flag":"CF.png" + }, + "144":{ + "capital":"Colombo", + "citizenship":"Sri Lankan", + "country-code":"144", + "currency":"Sri Lankan rupee", + "currency_code":"LKR", + "currency_sub_unit":"cent", + "full_name":"Democratic Socialist Republic of Sri Lanka", + "iso_3166_2":"LK", + "iso_3166_3":"LKA", + "name":"Sri Lanka", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"94", + "currency_symbol":"\u20a8", + "flag":"LK.png" + }, + "148":{ + "capital":"N\u2019Djamena", + "citizenship":"Chadian", + "country-code":"148", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Republic of Chad", + "iso_3166_2":"TD", + "iso_3166_3":"TCD", + "name":"Chad", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"235", + "currency_symbol":"XAF", + "flag":"TD.png" + }, + "152":{ + "capital":"Santiago", + "citizenship":"Chilean", + "country-code":"152", + "currency":"Chilean peso", + "currency_code":"CLP", + "currency_sub_unit":"centavo", + "full_name":"Republic of Chile", + "iso_3166_2":"CL", + "iso_3166_3":"CHL", + "name":"Chile", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"56", + "currency_symbol":"CLP", + "flag":"CL.png" + }, + "156":{ + "capital":"Beijing", + "citizenship":"Chinese", + "country-code":"156", + "currency":"renminbi-yuan (inv.)", + "currency_code":"CNY", + "currency_sub_unit":"jiao (10)", + "full_name":"People\u2019s Republic of China", + "iso_3166_2":"CN", + "iso_3166_3":"CHN", + "name":"China", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"86", + "currency_symbol":"\u00a5", + "flag":"CN.png" + }, + "158":{ + "capital":"Taipei", + "citizenship":"Taiwanese", + "country-code":"158", + "currency":"new Taiwan dollar", + "currency_code":"TWD", + "currency_sub_unit":"fen (inv.)", + "full_name":"Republic of China, Taiwan (TW1)", + "iso_3166_2":"TW", + "iso_3166_3":"TWN", + "name":"Taiwan, Province of China", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"886", + "currency_symbol":"NT$", + "flag":"TW.png" + }, + "162":{ + "capital":"Flying Fish Cove", + "citizenship":"Christmas Islander", + "country-code":"162", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Christmas Island Territory", + "iso_3166_2":"CX", + "iso_3166_3":"CXR", + "name":"Christmas Island", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"61", + "currency_symbol":"$", + "flag":"CX.png" + }, + "166":{ + "capital":"Bantam", + "citizenship":"Cocos Islander", + "country-code":"166", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Territory of Cocos (Keeling) Islands", + "iso_3166_2":"CC", + "iso_3166_3":"CCK", + "name":"Cocos (Keeling) Islands", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"61", + "currency_symbol":"$", + "flag":"CC.png" + }, + "170":{ + "capital":"Santa Fe de Bogot\u00e1", + "citizenship":"Colombian", + "country-code":"170", + "currency":"Colombian peso", + "currency_code":"COP", + "currency_sub_unit":"centavo", + "full_name":"Republic of Colombia", + "iso_3166_2":"CO", + "iso_3166_3":"COL", + "name":"Colombia", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"57", + "currency_symbol":"$", + "flag":"CO.png" + }, + "174":{ + "capital":"Moroni", + "citizenship":"Comorian", + "country-code":"174", + "currency":"Comorian franc", + "currency_code":"KMF", + "currency_sub_unit":"", + "full_name":"Union of the Comoros", + "iso_3166_2":"KM", + "iso_3166_3":"COM", + "name":"Comoros", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"269", + "currency_symbol":"KMF", + "flag":"KM.png" + }, + "175":{ + "capital":"Mamoudzou", + "citizenship":"Mahorais", + "country-code":"175", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Departmental Collectivity of Mayotte", + "iso_3166_2":"YT", + "iso_3166_3":"MYT", + "name":"Mayotte", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"262", + "currency_symbol":"\u20ac", + "flag":"YT.png" + }, + "178":{ + "capital":"Brazzaville", + "citizenship":"Congolese", + "country-code":"178", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Republic of the Congo", + "iso_3166_2":"CG", + "iso_3166_3":"COG", + "name":"Congo", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"242", + "currency_symbol":"FCF", + "flag":"CG.png" + }, + "180":{ + "capital":"Kinshasa", + "citizenship":"Congolese", + "country-code":"180", + "currency":"Congolese franc", + "currency_code":"CDF", + "currency_sub_unit":"centime", + "full_name":"Democratic Republic of the Congo", + "iso_3166_2":"CD", + "iso_3166_3":"COD", + "name":"Congo, the Democratic Republic of the", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"243", + "currency_symbol":"CDF", + "flag":"CD.png" + }, + "184":{ + "capital":"Avarua", + "citizenship":"Cook Islander", + "country-code":"184", + "currency":"New Zealand dollar", + "currency_code":"NZD", + "currency_sub_unit":"cent", + "full_name":"Cook Islands", + "iso_3166_2":"CK", + "iso_3166_3":"COK", + "name":"Cook Islands", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"682", + "currency_symbol":"$", + "flag":"CK.png" + }, + "188":{ + "capital":"San Jos\u00e9", + "citizenship":"Costa Rican", + "country-code":"188", + "currency":"Costa Rican col\u00f3n (pl. colones)", + "currency_code":"CRC", + "currency_sub_unit":"c\u00e9ntimo", + "full_name":"Republic of Costa Rica", + "iso_3166_2":"CR", + "iso_3166_3":"CRI", + "name":"Costa Rica", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"506", + "currency_symbol":"\u20a1", + "flag":"CR.png" + }, + "191":{ + "capital":"Zagreb", + "citizenship":"Croatian", + "country-code":"191", + "currency":"kuna (inv.)", + "currency_code":"HRK", + "currency_sub_unit":"lipa (inv.)", + "full_name":"Republic of Croatia", + "iso_3166_2":"HR", + "iso_3166_3":"HRV", + "name":"Croatia", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"385", + "currency_symbol":"kn", + "flag":"HR.png" + }, + "192":{ + "capital":"Havana", + "citizenship":"Cuban", + "country-code":"192", + "currency":"Cuban peso", + "currency_code":"CUP", + "currency_sub_unit":"centavo", + "full_name":"Republic of Cuba", + "iso_3166_2":"CU", + "iso_3166_3":"CUB", + "name":"Cuba", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"53", + "currency_symbol":"\u20b1", + "flag":"CU.png" + }, + "196":{ + "capital":"Nicosia", + "citizenship":"Cypriot", + "country-code":"196", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Cyprus", + "iso_3166_2":"CY", + "iso_3166_3":"CYP", + "name":"Cyprus", + "region-code":"142", + "sub-region-code":"145", + "eea":true, + "calling_code":"357", + "currency_symbol":"CYP", + "flag":"CY.png" + }, + "203":{ + "capital":"Prague", + "citizenship":"Czech", + "country-code":"203", + "currency":"Czech koruna (pl. koruny)", + "currency_code":"CZK", + "currency_sub_unit":"hal\u00e9r", + "full_name":"Czech Republic", + "iso_3166_2":"CZ", + "iso_3166_3":"CZE", + "name":"Czech Republic", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"420", + "currency_symbol":"K\u010d", + "flag":"CZ.png" + }, + "204":{ + "capital":"Porto Novo (BJ1)", + "citizenship":"Beninese", + "country-code":"204", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of Benin", + "iso_3166_2":"BJ", + "iso_3166_3":"BEN", + "name":"Benin", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"229", + "currency_symbol":"XOF", + "flag":"BJ.png" + }, + "208":{ + "capital":"Copenhagen", + "citizenship":"Danish", + "country-code":"208", + "currency":"Danish krone", + "currency_code":"DKK", + "currency_sub_unit":"\u00f8re (inv.)", + "full_name":"Kingdom of Denmark", + "iso_3166_2":"DK", + "iso_3166_3":"DNK", + "name":"Denmark", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"45", + "currency_symbol":"kr", + "flag":"DK.png" + }, + "212":{ + "capital":"Roseau", + "citizenship":"Dominican", + "country-code":"212", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Commonwealth of Dominica", + "iso_3166_2":"DM", + "iso_3166_3":"DMA", + "name":"Dominica", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"DM.png" + }, + "214":{ + "capital":"Santo Domingo", + "citizenship":"Dominican", + "country-code":"214", + "currency":"Dominican peso", + "currency_code":"DOP", + "currency_sub_unit":"centavo", + "full_name":"Dominican Republic", + "iso_3166_2":"DO", + "iso_3166_3":"DOM", + "name":"Dominican Republic", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"RD$", + "flag":"DO.png" + }, + "218":{ + "capital":"Quito", + "citizenship":"Ecuadorian", + "country-code":"218", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Republic of Ecuador", + "iso_3166_2":"EC", + "iso_3166_3":"ECU", + "name":"Ecuador", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"593", + "currency_symbol":"$", + "flag":"EC.png" + }, + "222":{ + "capital":"San Salvador", + "citizenship":"Salvadoran", + "country-code":"222", + "currency":"Salvadorian col\u00f3n (pl. colones)", + "currency_code":"SVC", + "currency_sub_unit":"centavo", + "full_name":"Republic of El Salvador", + "iso_3166_2":"SV", + "iso_3166_3":"SLV", + "name":"El Salvador", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"503", + "currency_symbol":"$", + "flag":"SV.png" + }, + "226":{ + "capital":"Malabo", + "citizenship":"Equatorial Guinean", + "country-code":"226", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Republic of Equatorial Guinea", + "iso_3166_2":"GQ", + "iso_3166_3":"GNQ", + "name":"Equatorial Guinea", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"240", + "currency_symbol":"FCF", + "flag":"GQ.png" + }, + "231":{ + "capital":"Addis Ababa", + "citizenship":"Ethiopian", + "country-code":"231", + "currency":"birr (inv.)", + "currency_code":"ETB", + "currency_sub_unit":"cent", + "full_name":"Federal Democratic Republic of Ethiopia", + "iso_3166_2":"ET", + "iso_3166_3":"ETH", + "name":"Ethiopia", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"251", + "currency_symbol":"ETB", + "flag":"ET.png" + }, + "232":{ + "capital":"Asmara", + "citizenship":"Eritrean", + "country-code":"232", + "currency":"nakfa", + "currency_code":"ERN", + "currency_sub_unit":"cent", + "full_name":"State of Eritrea", + "iso_3166_2":"ER", + "iso_3166_3":"ERI", + "name":"Eritrea", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"291", + "currency_symbol":"Nfk", + "flag":"ER.png" + }, + "233":{ + "capital":"Tallinn", + "citizenship":"Estonian", + "country-code":"233", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Estonia", + "iso_3166_2":"EE", + "iso_3166_3":"EST", + "name":"Estonia", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"372", + "currency_symbol":"kr", + "flag":"EE.png" + }, + "234":{ + "capital":"T\u00f3rshavn", + "citizenship":"Faeroese", + "country-code":"234", + "currency":"Danish krone", + "currency_code":"DKK", + "currency_sub_unit":"\u00f8re (inv.)", + "full_name":"Faeroe Islands", + "iso_3166_2":"FO", + "iso_3166_3":"FRO", + "name":"Faroe Islands", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"298", + "currency_symbol":"kr", + "flag":"FO.png" + }, + "238":{ + "capital":"Stanley", + "citizenship":"Falkland Islander", + "country-code":"238", + "currency":"Falkland Islands pound", + "currency_code":"FKP", + "currency_sub_unit":"new penny", + "full_name":"Falkland Islands", + "iso_3166_2":"FK", + "iso_3166_3":"FLK", + "name":"Falkland Islands (Malvinas)", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"500", + "currency_symbol":"\u00a3", + "flag":"FK.png" + }, + "239":{ + "capital":"King Edward Point (Grytviken)", + "citizenship":"of South Georgia and the South Sandwich Islands", + "country-code":"239", + "currency":"", + "currency_code":"", + "currency_sub_unit":"", + "full_name":"South Georgia and the South Sandwich Islands", + "iso_3166_2":"GS", + "iso_3166_3":"SGS", + "name":"South Georgia and the South Sandwich Islands", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"44", + "currency_symbol":"\u00a3", + "flag":"GS.png" + }, + "242":{ + "capital":"Suva", + "citizenship":"Fijian", + "country-code":"242", + "currency":"Fiji dollar", + "currency_code":"FJD", + "currency_sub_unit":"cent", + "full_name":"Republic of Fiji", + "iso_3166_2":"FJ", + "iso_3166_3":"FJI", + "name":"Fiji", + "region-code":"009", + "sub-region-code":"054", + "eea":false, + "calling_code":"679", + "currency_symbol":"$", + "flag":"FJ.png" + }, + "246":{ + "capital":"Helsinki", + "citizenship":"Finnish", + "country-code":"246", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Finland", + "iso_3166_2":"FI", + "iso_3166_3":"FIN", + "name":"Finland", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"358", + "currency_symbol":"\u20ac", + "flag":"FI.png" + }, + "248":{ + "capital":"Mariehamn", + "citizenship":"\u00c5land Islander", + "country-code":"248", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"\u00c5land Islands", + "iso_3166_2":"AX", + "iso_3166_3":"ALA", + "name":"\u00c5land Islands", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"358" + }, + "250":{ + "capital":"Paris", + "citizenship":"French", + "country-code":"250", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"French Republic", + "iso_3166_2":"FR", + "iso_3166_3":"FRA", + "name":"France", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"33", + "currency_symbol":"\u20ac", + "flag":"FR.png" + }, + "254":{ + "capital":"Cayenne", + "citizenship":"Guianese", + "country-code":"254", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"French Guiana", + "iso_3166_2":"GF", + "iso_3166_3":"GUF", + "name":"French Guiana", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"594", + "currency_symbol":"\u20ac", + "flag":"GF.png" + }, + "258":{ + "capital":"Papeete", + "citizenship":"Polynesian", + "country-code":"258", + "currency":"CFP franc", + "currency_code":"XPF", + "currency_sub_unit":"centime", + "full_name":"French Polynesia", + "iso_3166_2":"PF", + "iso_3166_3":"PYF", + "name":"French Polynesia", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"689", + "currency_symbol":"XPF", + "flag":"PF.png" + }, + "260":{ + "capital":"Port-aux-Francais", + "citizenship":"of French Southern and Antarctic Lands", + "country-code":"260", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"French Southern and Antarctic Lands", + "iso_3166_2":"TF", + "iso_3166_3":"ATF", + "name":"French Southern Territories", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"33", + "currency_symbol":"\u20ac", + "flag":"TF.png" + }, + "262":{ + "capital":"Djibouti", + "citizenship":"Djiboutian", + "country-code":"262", + "currency":"Djibouti franc", + "currency_code":"DJF", + "currency_sub_unit":"", + "full_name":"Republic of Djibouti", + "iso_3166_2":"DJ", + "iso_3166_3":"DJI", + "name":"Djibouti", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"253", + "currency_symbol":"DJF", + "flag":"DJ.png" + }, + "266":{ + "capital":"Libreville", + "citizenship":"Gabonese", + "country-code":"266", + "currency":"CFA franc (BEAC)", + "currency_code":"XAF", + "currency_sub_unit":"centime", + "full_name":"Gabonese Republic", + "iso_3166_2":"GA", + "iso_3166_3":"GAB", + "name":"Gabon", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"241", + "currency_symbol":"FCF", + "flag":"GA.png" + }, + "268":{ + "capital":"Tbilisi", + "citizenship":"Georgian", + "country-code":"268", + "currency":"lari", + "currency_code":"GEL", + "currency_sub_unit":"tetri (inv.)", + "full_name":"Georgia", + "iso_3166_2":"GE", + "iso_3166_3":"GEO", + "name":"Georgia", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"995", + "currency_symbol":"GEL", + "flag":"GE.png" + }, + "270":{ + "capital":"Banjul", + "citizenship":"Gambian", + "country-code":"270", + "currency":"dalasi (inv.)", + "currency_code":"GMD", + "currency_sub_unit":"butut", + "full_name":"Republic of the Gambia", + "iso_3166_2":"GM", + "iso_3166_3":"GMB", + "name":"Gambia", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"220", + "currency_symbol":"D", + "flag":"GM.png" + }, + "275":{ + "citizenship":"Palestinian", + "country-code":"275", + "iso_3166_2":"PS", + "iso_3166_3":"PSE", + "name":"Palestinian Territory, Occupied", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"970", + "currency_symbol":"\u20aa", + "flag":"PS.png" + }, + "276":{ + "capital":"Berlin", + "citizenship":"German", + "country-code":"276", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Federal Republic of Germany", + "iso_3166_2":"DE", + "iso_3166_3":"DEU", + "name":"Germany", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"49", + "currency_symbol":"\u20ac", + "flag":"DE.png" + }, + "288":{ + "capital":"Accra", + "citizenship":"Ghanaian", + "country-code":"288", + "currency":"Ghana cedi", + "currency_code":"GHS", + "currency_sub_unit":"pesewa", + "full_name":"Republic of Ghana", + "iso_3166_2":"GH", + "iso_3166_3":"GHA", + "name":"Ghana", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"233", + "currency_symbol":"\u00a2", + "flag":"GH.png" + }, + "292":{ + "capital":"Gibraltar", + "citizenship":"Gibraltarian", + "country-code":"292", + "currency":"Gibraltar pound", + "currency_code":"GIP", + "currency_sub_unit":"penny", + "full_name":"Gibraltar", + "iso_3166_2":"GI", + "iso_3166_3":"GIB", + "name":"Gibraltar", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"350", + "currency_symbol":"\u00a3", + "flag":"GI.png" + }, + "296":{ + "capital":"Tarawa", + "citizenship":"Kiribatian", + "country-code":"296", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Republic of Kiribati", + "iso_3166_2":"KI", + "iso_3166_3":"KIR", + "name":"Kiribati", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"686", + "currency_symbol":"$", + "flag":"KI.png" + }, + "300":{ + "capital":"Athens", + "citizenship":"Greek", + "country-code":"300", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Hellenic Republic", + "iso_3166_2":"GR", + "iso_3166_3":"GRC", + "name":"Greece", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"30", + "currency_symbol":"\u20ac", + "flag":"GR.png" + }, + "304":{ + "capital":"Nuuk", + "citizenship":"Greenlander", + "country-code":"304", + "currency":"Danish krone", + "currency_code":"DKK", + "currency_sub_unit":"\u00f8re (inv.)", + "full_name":"Greenland", + "iso_3166_2":"GL", + "iso_3166_3":"GRL", + "name":"Greenland", + "region-code":"019", + "sub-region-code":"021", + "eea":false, + "calling_code":"299", + "currency_symbol":"kr", + "flag":"GL.png" + }, + "308":{ + "capital":"St George\u2019s", + "citizenship":"Grenadian", + "country-code":"308", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Grenada", + "iso_3166_2":"GD", + "iso_3166_3":"GRD", + "name":"Grenada", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"GD.png" + }, + "312":{ + "capital":"Basse Terre", + "citizenship":"Guadeloupean", + "country-code":"312", + "currency":"euro", + "currency_code":"EUR ", + "currency_sub_unit":"cent", + "full_name":"Guadeloupe", + "iso_3166_2":"GP", + "iso_3166_3":"GLP", + "name":"Guadeloupe", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"590", + "currency_symbol":"\u20ac", + "flag":"GP.png" + }, + "316":{ + "capital":"Aga\u00f1a (Hag\u00e5t\u00f1a)", + "citizenship":"Guamanian", + "country-code":"316", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Territory of Guam", + "iso_3166_2":"GU", + "iso_3166_3":"GUM", + "name":"Guam", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"GU.png" + }, + "320":{ + "capital":"Guatemala City", + "citizenship":"Guatemalan", + "country-code":"320", + "currency":"quetzal (pl. quetzales)", + "currency_code":"GTQ", + "currency_sub_unit":"centavo", + "full_name":"Republic of Guatemala", + "iso_3166_2":"GT", + "iso_3166_3":"GTM", + "name":"Guatemala", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"502", + "currency_symbol":"Q", + "flag":"GT.png" + }, + "324":{ + "capital":"Conakry", + "citizenship":"Guinean", + "country-code":"324", + "currency":"Guinean franc", + "currency_code":"GNF", + "currency_sub_unit":"", + "full_name":"Republic of Guinea", + "iso_3166_2":"GN", + "iso_3166_3":"GIN", + "name":"Guinea", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"224", + "currency_symbol":"GNF", + "flag":"GN.png" + }, + "328":{ + "capital":"Georgetown", + "citizenship":"Guyanese", + "country-code":"328", + "currency":"Guyana dollar", + "currency_code":"GYD", + "currency_sub_unit":"cent", + "full_name":"Cooperative Republic of Guyana", + "iso_3166_2":"GY", + "iso_3166_3":"GUY", + "name":"Guyana", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"592", + "currency_symbol":"$", + "flag":"GY.png" + }, + "332":{ + "capital":"Port-au-Prince", + "citizenship":"Haitian", + "country-code":"332", + "currency":"gourde", + "currency_code":"HTG", + "currency_sub_unit":"centime", + "full_name":"Republic of Haiti", + "iso_3166_2":"HT", + "iso_3166_3":"HTI", + "name":"Haiti", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"509", + "currency_symbol":"G", + "flag":"HT.png" + }, + "334":{ + "capital":"Territory of Heard Island and McDonald Islands", + "citizenship":"of Territory of Heard Island and McDonald Islands", + "country-code":"334", + "currency":"", + "currency_code":"", + "currency_sub_unit":"", + "full_name":"Territory of Heard Island and McDonald Islands", + "iso_3166_2":"HM", + "iso_3166_3":"HMD", + "name":"Heard Island and McDonald Islands", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"61", + "currency_symbol":"$", + "flag":"HM.png" + }, + "336":{ + "capital":"Vatican City", + "citizenship":"of the Holy See\/of the Vatican", + "country-code":"336", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"the Holy See\/ Vatican City State", + "iso_3166_2":"VA", + "iso_3166_3":"VAT", + "name":"Holy See (Vatican City State)", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"39", + "currency_symbol":"\u20ac", + "flag":"VA.png" + }, + "340":{ + "capital":"Tegucigalpa", + "citizenship":"Honduran", + "country-code":"340", + "currency":"lempira", + "currency_code":"HNL", + "currency_sub_unit":"centavo", + "full_name":"Republic of Honduras", + "iso_3166_2":"HN", + "iso_3166_3":"HND", + "name":"Honduras", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"504", + "currency_symbol":"L", + "flag":"HN.png" + }, + "344":{ + "capital":"(HK3)", + "citizenship":"Hong Kong Chinese", + "country-code":"344", + "currency":"Hong Kong dollar", + "currency_code":"HKD", + "currency_sub_unit":"cent", + "full_name":"Hong Kong Special Administrative Region of the People\u2019s Republic of China (HK2)", + "iso_3166_2":"HK", + "iso_3166_3":"HKG", + "name":"Hong Kong", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"852", + "currency_symbol":"$", + "flag":"HK.png" + }, + "348":{ + "capital":"Budapest", + "citizenship":"Hungarian", + "country-code":"348", + "currency":"forint (inv.)", + "currency_code":"HUF", + "currency_sub_unit":"(fill\u00e9r (inv.))", + "full_name":"Republic of Hungary", + "iso_3166_2":"HU", + "iso_3166_3":"HUN", + "name":"Hungary", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"36", + "currency_symbol":"Ft", + "flag":"HU.png" + }, + "352":{ + "capital":"Reykjavik", + "citizenship":"Icelander", + "country-code":"352", + "currency":"kr\u00f3na (pl. kr\u00f3nur)", + "currency_code":"ISK", + "currency_sub_unit":"", + "full_name":"Republic of Iceland", + "iso_3166_2":"IS", + "iso_3166_3":"ISL", + "name":"Iceland", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"354", + "currency_symbol":"kr", + "flag":"IS.png" + }, + "356":{ + "capital":"New Delhi", + "citizenship":"Indian", + "country-code":"356", + "currency":"Indian rupee", + "currency_code":"INR", + "currency_sub_unit":"paisa", + "full_name":"Republic of India", + "iso_3166_2":"IN", + "iso_3166_3":"IND", + "name":"India", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"91", + "currency_symbol":"\u20b9", + "flag":"IN.png" + }, + "360":{ + "capital":"Jakarta", + "citizenship":"Indonesian", + "country-code":"360", + "currency":"Indonesian rupiah (inv.)", + "currency_code":"IDR", + "currency_sub_unit":"sen (inv.)", + "full_name":"Republic of Indonesia", + "iso_3166_2":"ID", + "iso_3166_3":"IDN", + "name":"Indonesia", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"62", + "currency_symbol":"Rp", + "flag":"ID.png" + }, + "364":{ + "capital":"Tehran", + "citizenship":"Iranian", + "country-code":"364", + "currency":"Iranian rial", + "currency_code":"IRR", + "currency_sub_unit":"(dinar) (IR1)", + "full_name":"Islamic Republic of Iran", + "iso_3166_2":"IR", + "iso_3166_3":"IRN", + "name":"Iran, Islamic Republic of", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"98", + "currency_symbol":"\ufdfc", + "flag":"IR.png" + }, + "368":{ + "capital":"Baghdad", + "citizenship":"Iraqi", + "country-code":"368", + "currency":"Iraqi dinar", + "currency_code":"IQD", + "currency_sub_unit":"fils (inv.)", + "full_name":"Republic of Iraq", + "iso_3166_2":"IQ", + "iso_3166_3":"IRQ", + "name":"Iraq", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"964", + "currency_symbol":"IQD", + "flag":"IQ.png" + }, + "372":{ + "capital":"Dublin", + "citizenship":"Irish", + "country-code":"372", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Ireland (IE1)", + "iso_3166_2":"IE", + "iso_3166_3":"IRL", + "name":"Ireland", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"353", + "currency_symbol":"\u20ac", + "flag":"IE.png" + }, + "376":{ + "capital":"(IL1)", + "citizenship":"Israeli", + "country-code":"376", + "currency":"shekel", + "currency_code":"ILS", + "currency_sub_unit":"agora", + "full_name":"State of Israel", + "iso_3166_2":"IL", + "iso_3166_3":"ISR", + "name":"Israel", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"972", + "currency_symbol":"\u20aa", + "flag":"IL.png" + }, + "380":{ + "capital":"Rome", + "citizenship":"Italian", + "country-code":"380", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Italian Republic", + "iso_3166_2":"IT", + "iso_3166_3":"ITA", + "name":"Italy", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"39", + "currency_symbol":"\u20ac", + "flag":"IT.png" + }, + "384":{ + "capital":"Yamoussoukro (CI1)", + "citizenship":"Ivorian", + "country-code":"384", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of C\u00f4te d\u2019Ivoire", + "iso_3166_2":"CI", + "iso_3166_3":"CIV", + "name":"C\u00f4te d'Ivoire", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"225", + "currency_symbol":"XOF", + "flag":"CI.png" + }, + "388":{ + "capital":"Kingston", + "citizenship":"Jamaican", + "country-code":"388", + "currency":"Jamaica dollar", + "currency_code":"JMD", + "currency_sub_unit":"cent", + "full_name":"Jamaica", + "iso_3166_2":"JM", + "iso_3166_3":"JAM", + "name":"Jamaica", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"JM.png" + }, + "392":{ + "capital":"Tokyo", + "citizenship":"Japanese", + "country-code":"392", + "currency":"yen (inv.)", + "currency_code":"JPY", + "currency_sub_unit":"(sen (inv.)) (JP1)", + "full_name":"Japan", + "iso_3166_2":"JP", + "iso_3166_3":"JPN", + "name":"Japan", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"81", + "currency_symbol":"\u00a5", + "flag":"JP.png" + }, + "398":{ + "capital":"Astana", + "citizenship":"Kazakh", + "country-code":"398", + "currency":"tenge (inv.)", + "currency_code":"KZT", + "currency_sub_unit":"tiyn", + "full_name":"Republic of Kazakhstan", + "iso_3166_2":"KZ", + "iso_3166_3":"KAZ", + "name":"Kazakhstan", + "region-code":"142", + "sub-region-code":"143", + "eea":false, + "calling_code":"7", + "currency_symbol":"\u043b\u0432", + "flag":"KZ.png" + }, + "400":{ + "capital":"Amman", + "citizenship":"Jordanian", + "country-code":"400", + "currency":"Jordanian dinar", + "currency_code":"JOD", + "currency_sub_unit":"100 qirsh", + "full_name":"Hashemite Kingdom of Jordan", + "iso_3166_2":"JO", + "iso_3166_3":"JOR", + "name":"Jordan", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"962", + "currency_symbol":"JOD", + "flag":"JO.png" + }, + "404":{ + "capital":"Nairobi", + "citizenship":"Kenyan", + "country-code":"404", + "currency":"Kenyan shilling", + "currency_code":"KES", + "currency_sub_unit":"cent", + "full_name":"Republic of Kenya", + "iso_3166_2":"KE", + "iso_3166_3":"KEN", + "name":"Kenya", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"254", + "currency_symbol":"KES", + "flag":"KE.png" + }, + "408":{ + "capital":"Pyongyang", + "citizenship":"North Korean", + "country-code":"408", + "currency":"North Korean won (inv.)", + "currency_code":"KPW", + "currency_sub_unit":"chun (inv.)", + "full_name":"Democratic People\u2019s Republic of Korea", + "iso_3166_2":"KP", + "iso_3166_3":"PRK", + "name":"Korea, Democratic People's Republic of", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"850", + "currency_symbol":"\u20a9", + "flag":"KP.png" + }, + "410":{ + "capital":"Seoul", + "citizenship":"South Korean", + "country-code":"410", + "currency":"South Korean won (inv.)", + "currency_code":"KRW", + "currency_sub_unit":"(chun (inv.))", + "full_name":"Republic of Korea", + "iso_3166_2":"KR", + "iso_3166_3":"KOR", + "name":"Korea, Republic of", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"82", + "currency_symbol":"\u20a9", + "flag":"KR.png" + }, + "414":{ + "capital":"Kuwait City", + "citizenship":"Kuwaiti", + "country-code":"414", + "currency":"Kuwaiti dinar", + "currency_code":"KWD", + "currency_sub_unit":"fils (inv.)", + "full_name":"State of Kuwait", + "iso_3166_2":"KW", + "iso_3166_3":"KWT", + "name":"Kuwait", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"965", + "currency_symbol":"KWD", + "flag":"KW.png" + }, + "417":{ + "capital":"Bishkek", + "citizenship":"Kyrgyz", + "country-code":"417", + "currency":"som", + "currency_code":"KGS", + "currency_sub_unit":"tyiyn", + "full_name":"Kyrgyz Republic", + "iso_3166_2":"KG", + "iso_3166_3":"KGZ", + "name":"Kyrgyzstan", + "region-code":"142", + "sub-region-code":"143", + "eea":false, + "calling_code":"996", + "currency_symbol":"\u043b\u0432", + "flag":"KG.png" + }, + "418":{ + "capital":"Vientiane", + "citizenship":"Lao", + "country-code":"418", + "currency":"kip (inv.)", + "currency_code":"LAK", + "currency_sub_unit":"(at (inv.))", + "full_name":"Lao People\u2019s Democratic Republic", + "iso_3166_2":"LA", + "iso_3166_3":"LAO", + "name":"Lao People's Democratic Republic", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"856", + "currency_symbol":"\u20ad", + "flag":"LA.png" + }, + "422":{ + "capital":"Beirut", + "citizenship":"Lebanese", + "country-code":"422", + "currency":"Lebanese pound", + "currency_code":"LBP", + "currency_sub_unit":"(piastre)", + "full_name":"Lebanese Republic", + "iso_3166_2":"LB", + "iso_3166_3":"LBN", + "name":"Lebanon", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"961", + "currency_symbol":"\u00a3", + "flag":"LB.png" + }, + "426":{ + "capital":"Maseru", + "citizenship":"Basotho", + "country-code":"426", + "currency":"loti (pl. maloti)", + "currency_code":"LSL", + "currency_sub_unit":"sente", + "full_name":"Kingdom of Lesotho", + "iso_3166_2":"LS", + "iso_3166_3":"LSO", + "name":"Lesotho", + "region-code":"002", + "sub-region-code":"018", + "eea":false, + "calling_code":"266", + "currency_symbol":"L", + "flag":"LS.png" + }, + "428":{ + "capital":"Riga", + "citizenship":"Latvian", + "country-code":"428", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Latvia", + "iso_3166_2":"LV", + "iso_3166_3":"LVA", + "name":"Latvia", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"371", + "currency_symbol":"Ls", + "flag":"LV.png" + }, + "430":{ + "capital":"Monrovia", + "citizenship":"Liberian", + "country-code":"430", + "currency":"Liberian dollar", + "currency_code":"LRD", + "currency_sub_unit":"cent", + "full_name":"Republic of Liberia", + "iso_3166_2":"LR", + "iso_3166_3":"LBR", + "name":"Liberia", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"231", + "currency_symbol":"$", + "flag":"LR.png" + }, + "434":{ + "capital":"Tripoli", + "citizenship":"Libyan", + "country-code":"434", + "currency":"Libyan dinar", + "currency_code":"LYD", + "currency_sub_unit":"dirham", + "full_name":"Socialist People\u2019s Libyan Arab Jamahiriya", + "iso_3166_2":"LY", + "iso_3166_3":"LBY", + "name":"Libya", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"218", + "currency_symbol":"LYD", + "flag":"LY.png" + }, + "438":{ + "capital":"Vaduz", + "citizenship":"Liechtensteiner", + "country-code":"438", + "currency":"Swiss franc", + "currency_code":"CHF", + "currency_sub_unit":"centime", + "full_name":"Principality of Liechtenstein", + "iso_3166_2":"LI", + "iso_3166_3":"LIE", + "name":"Liechtenstein", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"423", + "currency_symbol":"CHF", + "flag":"LI.png" + }, + "440":{ + "capital":"Vilnius", + "citizenship":"Lithuanian", + "country-code":"440", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Lithuania", + "iso_3166_2":"LT", + "iso_3166_3":"LTU", + "name":"Lithuania", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"370", + "currency_symbol":"Lt", + "flag":"LT.png" + }, + "442":{ + "capital":"Luxembourg", + "citizenship":"Luxembourger", + "country-code":"442", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Grand Duchy of Luxembourg", + "iso_3166_2":"LU", + "iso_3166_3":"LUX", + "name":"Luxembourg", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"352", + "currency_symbol":"\u20ac", + "flag":"LU.png" + }, + "446":{ + "capital":"Macao (MO3)", + "citizenship":"Macanese", + "country-code":"446", + "currency":"pataca", + "currency_code":"MOP", + "currency_sub_unit":"avo", + "full_name":"Macao Special Administrative Region of the People\u2019s Republic of China (MO2)", + "iso_3166_2":"MO", + "iso_3166_3":"MAC", + "name":"Macao", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"853", + "currency_symbol":"MOP", + "flag":"MO.png" + }, + "450":{ + "capital":"Antananarivo", + "citizenship":"Malagasy", + "country-code":"450", + "currency":"ariary", + "currency_code":"MGA", + "currency_sub_unit":"iraimbilanja (inv.)", + "full_name":"Republic of Madagascar", + "iso_3166_2":"MG", + "iso_3166_3":"MDG", + "name":"Madagascar", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"261", + "currency_symbol":"MGA", + "flag":"MG.png" + }, + "454":{ + "capital":"Lilongwe", + "citizenship":"Malawian", + "country-code":"454", + "currency":"Malawian kwacha (inv.)", + "currency_code":"MWK", + "currency_sub_unit":"tambala (inv.)", + "full_name":"Republic of Malawi", + "iso_3166_2":"MW", + "iso_3166_3":"MWI", + "name":"Malawi", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"265", + "currency_symbol":"MK", + "flag":"MW.png" + }, + "458":{ + "capital":"Kuala Lumpur (MY1)", + "citizenship":"Malaysian", + "country-code":"458", + "currency":"ringgit (inv.)", + "currency_code":"MYR", + "currency_sub_unit":"sen (inv.)", + "full_name":"Malaysia", + "iso_3166_2":"MY", + "iso_3166_3":"MYS", + "name":"Malaysia", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"60", + "currency_symbol":"RM", + "flag":"MY.png" + }, + "462":{ + "capital":"Mal\u00e9", + "citizenship":"Maldivian", + "country-code":"462", + "currency":"rufiyaa", + "currency_code":"MVR", + "currency_sub_unit":"laari (inv.)", + "full_name":"Republic of Maldives", + "iso_3166_2":"MV", + "iso_3166_3":"MDV", + "name":"Maldives", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"960", + "currency_symbol":"Rf", + "flag":"MV.png" + }, + "466":{ + "capital":"Bamako", + "citizenship":"Malian", + "country-code":"466", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of Mali", + "iso_3166_2":"ML", + "iso_3166_3":"MLI", + "name":"Mali", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"223", + "currency_symbol":"XOF", + "flag":"ML.png" + }, + "470":{ + "capital":"Valletta", + "citizenship":"Maltese", + "country-code":"470", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Malta", + "iso_3166_2":"MT", + "iso_3166_3":"MLT", + "name":"Malta", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"356", + "currency_symbol":"MTL", + "flag":"MT.png" + }, + "474":{ + "capital":"Fort-de-France", + "citizenship":"Martinican", + "country-code":"474", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Martinique", + "iso_3166_2":"MQ", + "iso_3166_3":"MTQ", + "name":"Martinique", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"596", + "currency_symbol":"\u20ac", + "flag":"MQ.png" + }, + "478":{ + "capital":"Nouakchott", + "citizenship":"Mauritanian", + "country-code":"478", + "currency":"ouguiya", + "currency_code":"MRO", + "currency_sub_unit":"khoum", + "full_name":"Islamic Republic of Mauritania", + "iso_3166_2":"MR", + "iso_3166_3":"MRT", + "name":"Mauritania", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"222", + "currency_symbol":"UM", + "flag":"MR.png" + }, + "480":{ + "capital":"Port Louis", + "citizenship":"Mauritian", + "country-code":"480", + "currency":"Mauritian rupee", + "currency_code":"MUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Mauritius", + "iso_3166_2":"MU", + "iso_3166_3":"MUS", + "name":"Mauritius", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"230", + "currency_symbol":"\u20a8", + "flag":"MU.png" + }, + "484":{ + "capital":"Mexico City", + "citizenship":"Mexican", + "country-code":"484", + "currency":"Mexican peso", + "currency_code":"MXN", + "currency_sub_unit":"centavo", + "full_name":"United Mexican States", + "iso_3166_2":"MX", + "iso_3166_3":"MEX", + "name":"Mexico", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"52", + "currency_symbol":"$", + "flag":"MX.png" + }, + "492":{ + "capital":"Monaco", + "citizenship":"Monegasque", + "country-code":"492", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Principality of Monaco", + "iso_3166_2":"MC", + "iso_3166_3":"MCO", + "name":"Monaco", + "region-code":"150", + "sub-region-code":"155", + "eea":false, + "calling_code":"377", + "currency_symbol":"\u20ac", + "flag":"MC.png" + }, + "496":{ + "capital":"Ulan Bator", + "citizenship":"Mongolian", + "country-code":"496", + "currency":"tugrik", + "currency_code":"MNT", + "currency_sub_unit":"m\u00f6ng\u00f6 (inv.)", + "full_name":"Mongolia", + "iso_3166_2":"MN", + "iso_3166_3":"MNG", + "name":"Mongolia", + "region-code":"142", + "sub-region-code":"030", + "eea":false, + "calling_code":"976", + "currency_symbol":"\u20ae", + "flag":"MN.png" + }, + "498":{ + "capital":"Chisinau", + "citizenship":"Moldovan", + "country-code":"498", + "currency":"Moldovan leu (pl. lei)", + "currency_code":"MDL", + "currency_sub_unit":"ban", + "full_name":"Republic of Moldova", + "iso_3166_2":"MD", + "iso_3166_3":"MDA", + "name":"Moldova, Republic of", + "region-code":"150", + "sub-region-code":"151", + "eea":false, + "calling_code":"373", + "currency_symbol":"MDL", + "flag":"MD.png" + }, + "499":{ + "capital":"Podgorica", + "citizenship":"Montenegrin", + "country-code":"499", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Montenegro", + "iso_3166_2":"ME", + "iso_3166_3":"MNE", + "name":"Montenegro", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"382" + }, + "500":{ + "capital":"Plymouth (MS2)", + "citizenship":"Montserratian", + "country-code":"500", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Montserrat", + "iso_3166_2":"MS", + "iso_3166_3":"MSR", + "name":"Montserrat", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"MS.png" + }, + "504":{ + "capital":"Rabat", + "citizenship":"Moroccan", + "country-code":"504", + "currency":"Moroccan dirham", + "currency_code":"MAD", + "currency_sub_unit":"centime", + "full_name":"Kingdom of Morocco", + "iso_3166_2":"MA", + "iso_3166_3":"MAR", + "name":"Morocco", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"212", + "currency_symbol":"MAD", + "flag":"MA.png" + }, + "508":{ + "capital":"Maputo", + "citizenship":"Mozambican", + "country-code":"508", + "currency":"metical", + "currency_code":"MZN", + "currency_sub_unit":"centavo", + "full_name":"Republic of Mozambique", + "iso_3166_2":"MZ", + "iso_3166_3":"MOZ", + "name":"Mozambique", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"258", + "currency_symbol":"MT", + "flag":"MZ.png" + }, + "512":{ + "capital":"Muscat", + "citizenship":"Omani", + "country-code":"512", + "currency":"Omani rial", + "currency_code":"OMR", + "currency_sub_unit":"baiza", + "full_name":"Sultanate of Oman", + "iso_3166_2":"OM", + "iso_3166_3":"OMN", + "name":"Oman", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"968", + "currency_symbol":"\ufdfc", + "flag":"OM.png" + }, + "516":{ + "capital":"Windhoek", + "citizenship":"Namibian", + "country-code":"516", + "currency":"Namibian dollar", + "currency_code":"NAD", + "currency_sub_unit":"cent", + "full_name":"Republic of Namibia", + "iso_3166_2":"NA", + "iso_3166_3":"NAM", + "name":"Namibia", + "region-code":"002", + "sub-region-code":"018", + "eea":false, + "calling_code":"264", + "currency_symbol":"$", + "flag":"NA.png" + }, + "520":{ + "capital":"Yaren", + "citizenship":"Nauruan", + "country-code":"520", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Republic of Nauru", + "iso_3166_2":"NR", + "iso_3166_3":"NRU", + "name":"Nauru", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"674", + "currency_symbol":"$", + "flag":"NR.png" + }, + "524":{ + "capital":"Kathmandu", + "citizenship":"Nepalese", + "country-code":"524", + "currency":"Nepalese rupee", + "currency_code":"NPR", + "currency_sub_unit":"paisa (inv.)", + "full_name":"Nepal", + "iso_3166_2":"NP", + "iso_3166_3":"NPL", + "name":"Nepal", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"977", + "currency_symbol":"\u20a8", + "flag":"NP.png" + }, + "528":{ + "capital":"Amsterdam (NL2)", + "citizenship":"Dutch", + "country-code":"528", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Kingdom of the Netherlands", + "iso_3166_2":"NL", + "iso_3166_3":"NLD", + "name":"Netherlands", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"31", + "currency_symbol":"\u20ac", + "flag":"NL.png" + }, + "531":{ + "capital":"Willemstad", + "citizenship":"Cura\u00e7aoan", + "country-code":"531", + "currency":"Netherlands Antillean guilder (CW1)", + "currency_code":"ANG", + "currency_sub_unit":"cent", + "full_name":"Cura\u00e7ao", + "iso_3166_2":"CW", + "iso_3166_3":"CUW", + "name":"Cura\u00e7ao", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"599" + }, + "533":{ + "capital":"Oranjestad", + "citizenship":"Aruban", + "country-code":"533", + "currency":"Aruban guilder", + "currency_code":"AWG", + "currency_sub_unit":"cent", + "full_name":"Aruba", + "iso_3166_2":"AW", + "iso_3166_3":"ABW", + "name":"Aruba", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"297", + "currency_symbol":"\u0192", + "flag":"AW.png" + }, + "534":{ + "capital":"Philipsburg", + "citizenship":"Sint Maartener", + "country-code":"534", + "currency":"Netherlands Antillean guilder (SX1)", + "currency_code":"ANG", + "currency_sub_unit":"cent", + "full_name":"Sint Maarten", + "iso_3166_2":"SX", + "iso_3166_3":"SXM", + "name":"Sint Maarten (Dutch part)", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"721" + }, + "535":{ + "citizenship":"of Bonaire, Sint Eustatius and Saba", + "country-code":"535", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "iso_3166_2":"BQ", + "iso_3166_3":"BES", + "name":"Bonaire, Sint Eustatius and Saba", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"599" + }, + "540":{ + "capital":"Noum\u00e9a", + "citizenship":"New Caledonian", + "country-code":"540", + "currency":"CFP franc", + "currency_code":"XPF", + "currency_sub_unit":"centime", + "full_name":"New Caledonia", + "iso_3166_2":"NC", + "iso_3166_3":"NCL", + "name":"New Caledonia", + "region-code":"009", + "sub-region-code":"054", + "eea":false, + "calling_code":"687", + "currency_symbol":"XPF", + "flag":"NC.png" + }, + "548":{ + "capital":"Port Vila", + "citizenship":"Vanuatuan", + "country-code":"548", + "currency":"vatu (inv.)", + "currency_code":"VUV", + "currency_sub_unit":"", + "full_name":"Republic of Vanuatu", + "iso_3166_2":"VU", + "iso_3166_3":"VUT", + "name":"Vanuatu", + "region-code":"009", + "sub-region-code":"054", + "eea":false, + "calling_code":"678", + "currency_symbol":"Vt", + "flag":"VU.png" + }, + "554":{ + "capital":"Wellington", + "citizenship":"New Zealander", + "country-code":"554", + "currency":"New Zealand dollar", + "currency_code":"NZD", + "currency_sub_unit":"cent", + "full_name":"New Zealand", + "iso_3166_2":"NZ", + "iso_3166_3":"NZL", + "name":"New Zealand", + "region-code":"009", + "sub-region-code":"053", + "eea":false, + "calling_code":"64", + "currency_symbol":"$", + "flag":"NZ.png" + }, + "558":{ + "capital":"Managua", + "citizenship":"Nicaraguan", + "country-code":"558", + "currency":"c\u00f3rdoba oro", + "currency_code":"NIO", + "currency_sub_unit":"centavo", + "full_name":"Republic of Nicaragua", + "iso_3166_2":"NI", + "iso_3166_3":"NIC", + "name":"Nicaragua", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"505", + "currency_symbol":"C$", + "flag":"NI.png" + }, + "562":{ + "capital":"Niamey", + "citizenship":"Nigerien", + "country-code":"562", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of Niger", + "iso_3166_2":"NE", + "iso_3166_3":"NER", + "name":"Niger", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"227", + "currency_symbol":"XOF", + "flag":"NE.png" + }, + "566":{ + "capital":"Abuja", + "citizenship":"Nigerian", + "country-code":"566", + "currency":"naira (inv.)", + "currency_code":"NGN", + "currency_sub_unit":"kobo (inv.)", + "full_name":"Federal Republic of Nigeria", + "iso_3166_2":"NG", + "iso_3166_3":"NGA", + "name":"Nigeria", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"234", + "currency_symbol":"\u20a6", + "flag":"NG.png" + }, + "570":{ + "capital":"Alofi", + "citizenship":"Niuean", + "country-code":"570", + "currency":"New Zealand dollar", + "currency_code":"NZD", + "currency_sub_unit":"cent", + "full_name":"Niue", + "iso_3166_2":"NU", + "iso_3166_3":"NIU", + "name":"Niue", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"683", + "currency_symbol":"$", + "flag":"NU.png" + }, + "574":{ + "capital":"Kingston", + "citizenship":"Norfolk Islander", + "country-code":"574", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Territory of Norfolk Island", + "iso_3166_2":"NF", + "iso_3166_3":"NFK", + "name":"Norfolk Island", + "region-code":"009", + "sub-region-code":"053", + "eea":false, + "calling_code":"672", + "currency_symbol":"$", + "flag":"NF.png" + }, + "578":{ + "capital":"Oslo", + "citizenship":"Norwegian", + "country-code":"578", + "currency":"Norwegian krone (pl. kroner)", + "currency_code":"NOK", + "currency_sub_unit":"\u00f8re (inv.)", + "full_name":"Kingdom of Norway", + "iso_3166_2":"NO", + "iso_3166_3":"NOR", + "name":"Norway", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"47", + "currency_symbol":"kr", + "flag":"NO.png" + }, + "580":{ + "capital":"Saipan", + "citizenship":"Northern Mariana Islander", + "country-code":"580", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Commonwealth of the Northern Mariana Islands", + "iso_3166_2":"MP", + "iso_3166_3":"MNP", + "name":"Northern Mariana Islands", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"MP.png" + }, + "581":{ + "capital":"United States Minor Outlying Islands", + "citizenship":"of United States Minor Outlying Islands", + "country-code":"581", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"United States Minor Outlying Islands", + "iso_3166_2":"UM", + "iso_3166_3":"UMI", + "name":"United States Minor Outlying Islands", + "region-code":"", + "sub-region-code":"", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"UM.png" + }, + "583":{ + "capital":"Palikir", + "citizenship":"Micronesian", + "country-code":"583", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Federated States of Micronesia", + "iso_3166_2":"FM", + "iso_3166_3":"FSM", + "name":"Micronesia, Federated States of", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"691", + "currency_symbol":"$", + "flag":"FM.png" + }, + "584":{ + "capital":"Majuro", + "citizenship":"Marshallese", + "country-code":"584", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Republic of the Marshall Islands", + "iso_3166_2":"MH", + "iso_3166_3":"MHL", + "name":"Marshall Islands", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"692", + "currency_symbol":"$", + "flag":"MH.png" + }, + "585":{ + "capital":"Melekeok", + "citizenship":"Palauan", + "country-code":"585", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Republic of Palau", + "iso_3166_2":"PW", + "iso_3166_3":"PLW", + "name":"Palau", + "region-code":"009", + "sub-region-code":"057", + "eea":false, + "calling_code":"680", + "currency_symbol":"$", + "flag":"PW.png" + }, + "586":{ + "capital":"Islamabad", + "citizenship":"Pakistani", + "country-code":"586", + "currency":"Pakistani rupee", + "currency_code":"PKR", + "currency_sub_unit":"paisa", + "full_name":"Islamic Republic of Pakistan", + "iso_3166_2":"PK", + "iso_3166_3":"PAK", + "name":"Pakistan", + "region-code":"142", + "sub-region-code":"034", + "eea":false, + "calling_code":"92", + "currency_symbol":"\u20a8", + "flag":"PK.png" + }, + "591":{ + "capital":"Panama City", + "citizenship":"Panamanian", + "country-code":"591", + "currency":"balboa", + "currency_code":"PAB", + "currency_sub_unit":"cent\u00e9simo", + "full_name":"Republic of Panama", + "iso_3166_2":"PA", + "iso_3166_3":"PAN", + "name":"Panama", + "region-code":"019", + "sub-region-code":"013", + "eea":false, + "calling_code":"507", + "currency_symbol":"B\/.", + "flag":"PA.png" + }, + "598":{ + "capital":"Port Moresby", + "citizenship":"Papua New Guinean", + "country-code":"598", + "currency":"kina (inv.)", + "currency_code":"PGK", + "currency_sub_unit":"toea (inv.)", + "full_name":"Independent State of Papua New Guinea", + "iso_3166_2":"PG", + "iso_3166_3":"PNG", + "name":"Papua New Guinea", + "region-code":"009", + "sub-region-code":"054", + "eea":false, + "calling_code":"675", + "currency_symbol":"PGK", + "flag":"PG.png" + }, + "600":{ + "capital":"Asunci\u00f3n", + "citizenship":"Paraguayan", + "country-code":"600", + "currency":"guaran\u00ed", + "currency_code":"PYG", + "currency_sub_unit":"c\u00e9ntimo", + "full_name":"Republic of Paraguay", + "iso_3166_2":"PY", + "iso_3166_3":"PRY", + "name":"Paraguay", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"595", + "currency_symbol":"Gs", + "flag":"PY.png" + }, + "604":{ + "capital":"Lima", + "citizenship":"Peruvian", + "country-code":"604", + "currency":"new sol", + "currency_code":"PEN", + "currency_sub_unit":"c\u00e9ntimo", + "full_name":"Republic of Peru", + "iso_3166_2":"PE", + "iso_3166_3":"PER", + "name":"Peru", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"51", + "currency_symbol":"S\/.", + "flag":"PE.png" + }, + "608":{ + "capital":"Manila", + "citizenship":"Filipino", + "country-code":"608", + "currency":"Philippine peso", + "currency_code":"PHP", + "currency_sub_unit":"centavo", + "full_name":"Republic of the Philippines", + "iso_3166_2":"PH", + "iso_3166_3":"PHL", + "name":"Philippines", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"63", + "currency_symbol":"Php", + "flag":"PH.png" + }, + "612":{ + "capital":"Adamstown", + "citizenship":"Pitcairner", + "country-code":"612", + "currency":"New Zealand dollar", + "currency_code":"NZD", + "currency_sub_unit":"cent", + "full_name":"Pitcairn Islands", + "iso_3166_2":"PN", + "iso_3166_3":"PCN", + "name":"Pitcairn", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"649", + "currency_symbol":"$", + "flag":"PN.png" + }, + "616":{ + "capital":"Warsaw", + "citizenship":"Polish", + "country-code":"616", + "currency":"zloty", + "currency_code":"PLN", + "currency_sub_unit":"grosz (pl. groszy)", + "full_name":"Republic of Poland", + "iso_3166_2":"PL", + "iso_3166_3":"POL", + "name":"Poland", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"48", + "currency_symbol":"z\u0142", + "flag":"PL.png" + }, + "620":{ + "capital":"Lisbon", + "citizenship":"Portuguese", + "country-code":"620", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Portuguese Republic", + "iso_3166_2":"PT", + "iso_3166_3":"PRT", + "name":"Portugal", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"351", + "currency_symbol":"\u20ac", + "flag":"PT.png" + }, + "624":{ + "capital":"Bissau", + "citizenship":"Guinea-Bissau national", + "country-code":"624", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of Guinea-Bissau", + "iso_3166_2":"GW", + "iso_3166_3":"GNB", + "name":"Guinea-Bissau", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"245", + "currency_symbol":"XOF", + "flag":"GW.png" + }, + "626":{ + "capital":"Dili", + "citizenship":"East Timorese", + "country-code":"626", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Democratic Republic of East Timor", + "iso_3166_2":"TL", + "iso_3166_3":"TLS", + "name":"Timor-Leste", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"670", + "currency_symbol":"$", + "flag":"TL.png" + }, + "630":{ + "capital":"San Juan", + "citizenship":"Puerto Rican", + "country-code":"630", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Commonwealth of Puerto Rico", + "iso_3166_2":"PR", + "iso_3166_3":"PRI", + "name":"Puerto Rico", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"PR.png" + }, + "634":{ + "capital":"Doha", + "citizenship":"Qatari", + "country-code":"634", + "currency":"Qatari riyal", + "currency_code":"QAR", + "currency_sub_unit":"dirham", + "full_name":"State of Qatar", + "iso_3166_2":"QA", + "iso_3166_3":"QAT", + "name":"Qatar", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"974", + "currency_symbol":"\ufdfc", + "flag":"QA.png" + }, + "638":{ + "capital":"Saint-Denis", + "citizenship":"Reunionese", + "country-code":"638", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"R\u00e9union", + "iso_3166_2":"RE", + "iso_3166_3":"REU", + "name":"R\u00e9union", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"262", + "currency_symbol":"\u20ac", + "flag":"RE.png" + }, + "642":{ + "capital":"Bucharest", + "citizenship":"Romanian", + "country-code":"642", + "currency":"Romanian leu (pl. lei)", + "currency_code":"RON", + "currency_sub_unit":"ban (pl. bani)", + "full_name":"Romania", + "iso_3166_2":"RO", + "iso_3166_3":"ROU", + "name":"Romania", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"40", + "currency_symbol":"lei", + "flag":"RO.png" + }, + "643":{ + "capital":"Moscow", + "citizenship":"Russian", + "country-code":"643", + "currency":"Russian rouble", + "currency_code":"RUB", + "currency_sub_unit":"kopek", + "full_name":"Russian Federation", + "iso_3166_2":"RU", + "iso_3166_3":"RUS", + "name":"Russian Federation", + "region-code":"150", + "sub-region-code":"151", + "eea":false, + "calling_code":"7", + "currency_symbol":"\u0440\u0443\u0431", + "flag":"RU.png" + }, + "646":{ + "capital":"Kigali", + "citizenship":"Rwandan; Rwandese", + "country-code":"646", + "currency":"Rwandese franc", + "currency_code":"RWF", + "currency_sub_unit":"centime", + "full_name":"Republic of Rwanda", + "iso_3166_2":"RW", + "iso_3166_3":"RWA", + "name":"Rwanda", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"250", + "currency_symbol":"RWF", + "flag":"RW.png" + }, + "652":{ + "capital":"Gustavia", + "citizenship":"of Saint Barth\u00e9lemy", + "country-code":"652", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Collectivity of Saint Barth\u00e9lemy", + "iso_3166_2":"BL", + "iso_3166_3":"BLM", + "name":"Saint Barth\u00e9lemy", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"590" + }, + "654":{ + "capital":"Jamestown", + "citizenship":"Saint Helenian", + "country-code":"654", + "currency":"Saint Helena pound", + "currency_code":"SHP", + "currency_sub_unit":"penny", + "full_name":"Saint Helena, Ascension and Tristan da Cunha", + "iso_3166_2":"SH", + "iso_3166_3":"SHN", + "name":"Saint Helena, Ascension and Tristan da Cunha", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"290", + "currency_symbol":"\u00a3", + "flag":"SH.png" + }, + "659":{ + "capital":"Basseterre", + "citizenship":"Kittsian; Nevisian", + "country-code":"659", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Federation of Saint Kitts and Nevis", + "iso_3166_2":"KN", + "iso_3166_3":"KNA", + "name":"Saint Kitts and Nevis", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"KN.png" + }, + "660":{ + "capital":"The Valley", + "citizenship":"Anguillan", + "country-code":"660", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Anguilla", + "iso_3166_2":"AI", + "iso_3166_3":"AIA", + "name":"Anguilla", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"AI.png" + }, + "662":{ + "capital":"Castries", + "citizenship":"Saint Lucian", + "country-code":"662", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Saint Lucia", + "iso_3166_2":"LC", + "iso_3166_3":"LCA", + "name":"Saint Lucia", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"LC.png" + }, + "663":{ + "capital":"Marigot", + "citizenship":"of Saint Martin", + "country-code":"663", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Collectivity of Saint Martin", + "iso_3166_2":"MF", + "iso_3166_3":"MAF", + "name":"Saint Martin (French part)", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"590" + }, + "666":{ + "capital":"Saint-Pierre", + "citizenship":"St-Pierrais; Miquelonnais", + "country-code":"666", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Territorial Collectivity of Saint Pierre and Miquelon", + "iso_3166_2":"PM", + "iso_3166_3":"SPM", + "name":"Saint Pierre and Miquelon", + "region-code":"019", + "sub-region-code":"021", + "eea":false, + "calling_code":"508", + "currency_symbol":"\u20ac", + "flag":"PM.png" + }, + "670":{ + "capital":"Kingstown", + "citizenship":"Vincentian", + "country-code":"670", + "currency":"East Caribbean dollar", + "currency_code":"XCD", + "currency_sub_unit":"cent", + "full_name":"Saint Vincent and the Grenadines", + "iso_3166_2":"VC", + "iso_3166_3":"VCT", + "name":"Saint Vincent and the Grenadines", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"VC.png" + }, + "674":{ + "capital":"San Marino", + "citizenship":"San Marinese", + "country-code":"674", + "currency":"euro", + "currency_code":"EUR ", + "currency_sub_unit":"cent", + "full_name":"Republic of San Marino", + "iso_3166_2":"SM", + "iso_3166_3":"SMR", + "name":"San Marino", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"378", + "currency_symbol":"\u20ac", + "flag":"SM.png" + }, + "678":{ + "capital":"S\u00e3o Tom\u00e9", + "citizenship":"S\u00e3o Tom\u00e9an", + "country-code":"678", + "currency":"dobra", + "currency_code":"STD", + "currency_sub_unit":"centavo", + "full_name":"Democratic Republic of S\u00e3o Tom\u00e9 and Pr\u00edncipe", + "iso_3166_2":"ST", + "iso_3166_3":"STP", + "name":"Sao Tome and Principe", + "region-code":"002", + "sub-region-code":"017", + "eea":false, + "calling_code":"239", + "currency_symbol":"Db", + "flag":"ST.png" + }, + "682":{ + "capital":"Riyadh", + "citizenship":"Saudi Arabian", + "country-code":"682", + "currency":"riyal", + "currency_code":"SAR", + "currency_sub_unit":"halala", + "full_name":"Kingdom of Saudi Arabia", + "iso_3166_2":"SA", + "iso_3166_3":"SAU", + "name":"Saudi Arabia", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"966", + "currency_symbol":"\ufdfc", + "flag":"SA.png" + }, + "686":{ + "capital":"Dakar", + "citizenship":"Senegalese", + "country-code":"686", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Republic of Senegal", + "iso_3166_2":"SN", + "iso_3166_3":"SEN", + "name":"Senegal", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"221", + "currency_symbol":"XOF", + "flag":"SN.png" + }, + "688":{ + "capital":"Belgrade", + "citizenship":"Serb", + "country-code":"688", + "currency":"Serbian dinar", + "currency_code":"RSD", + "currency_sub_unit":"para (inv.)", + "full_name":"Republic of Serbia", + "iso_3166_2":"RS", + "iso_3166_3":"SRB", + "name":"Serbia", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"381" + }, + "690":{ + "capital":"Victoria", + "citizenship":"Seychellois", + "country-code":"690", + "currency":"Seychelles rupee", + "currency_code":"SCR", + "currency_sub_unit":"cent", + "full_name":"Republic of Seychelles", + "iso_3166_2":"SC", + "iso_3166_3":"SYC", + "name":"Seychelles", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"248", + "currency_symbol":"\u20a8", + "flag":"SC.png" + }, + "694":{ + "capital":"Freetown", + "citizenship":"Sierra Leonean", + "country-code":"694", + "currency":"leone", + "currency_code":"SLL", + "currency_sub_unit":"cent", + "full_name":"Republic of Sierra Leone", + "iso_3166_2":"SL", + "iso_3166_3":"SLE", + "name":"Sierra Leone", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"232", + "currency_symbol":"Le", + "flag":"SL.png" + }, + "702":{ + "capital":"Singapore", + "citizenship":"Singaporean", + "country-code":"702", + "currency":"Singapore dollar", + "currency_code":"SGD", + "currency_sub_unit":"cent", + "full_name":"Republic of Singapore", + "iso_3166_2":"SG", + "iso_3166_3":"SGP", + "name":"Singapore", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"65", + "currency_symbol":"$", + "flag":"SG.png" + }, + "703":{ + "capital":"Bratislava", + "citizenship":"Slovak", + "country-code":"703", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Slovak Republic", + "iso_3166_2":"SK", + "iso_3166_3":"SVK", + "name":"Slovakia", + "region-code":"150", + "sub-region-code":"151", + "eea":true, + "calling_code":"421", + "currency_symbol":"Sk", + "flag":"SK.png" + }, + "704":{ + "capital":"Hanoi", + "citizenship":"Vietnamese", + "country-code":"704", + "currency":"dong", + "currency_code":"VND", + "currency_sub_unit":"(10 h\u00e0o", + "full_name":"Socialist Republic of Vietnam", + "iso_3166_2":"VN", + "iso_3166_3":"VNM", + "name":"Viet Nam", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"84", + "currency_symbol":"\u20ab", + "flag":"VN.png" + }, + "705":{ + "capital":"Ljubljana", + "citizenship":"Slovene", + "country-code":"705", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Republic of Slovenia", + "iso_3166_2":"SI", + "iso_3166_3":"SVN", + "name":"Slovenia", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"386", + "currency_symbol":"\u20ac", + "flag":"SI.png" + }, + "706":{ + "capital":"Mogadishu", + "citizenship":"Somali", + "country-code":"706", + "currency":"Somali shilling", + "currency_code":"SOS", + "currency_sub_unit":"cent", + "full_name":"Somali Republic", + "iso_3166_2":"SO", + "iso_3166_3":"SOM", + "name":"Somalia", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"252", + "currency_symbol":"S", + "flag":"SO.png" + }, + "710":{ + "capital":"Pretoria (ZA1)", + "citizenship":"South African", + "country-code":"710", + "currency":"rand", + "currency_code":"ZAR", + "currency_sub_unit":"cent", + "full_name":"Republic of South Africa", + "iso_3166_2":"ZA", + "iso_3166_3":"ZAF", + "name":"South Africa", + "region-code":"002", + "sub-region-code":"018", + "eea":false, + "calling_code":"27", + "currency_symbol":"R", + "flag":"ZA.png" + }, + "716":{ + "capital":"Harare", + "citizenship":"Zimbabwean", + "country-code":"716", + "currency":"Zimbabwe dollar (ZW1)", + "currency_code":"ZWL", + "currency_sub_unit":"cent", + "full_name":"Republic of Zimbabwe", + "iso_3166_2":"ZW", + "iso_3166_3":"ZWE", + "name":"Zimbabwe", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"263", + "currency_symbol":"Z$", + "flag":"ZW.png" + }, + "724":{ + "capital":"Madrid", + "citizenship":"Spaniard", + "country-code":"724", + "currency":"euro", + "currency_code":"EUR", + "currency_sub_unit":"cent", + "full_name":"Kingdom of Spain", + "iso_3166_2":"ES", + "iso_3166_3":"ESP", + "name":"Spain", + "region-code":"150", + "sub-region-code":"039", + "eea":true, + "calling_code":"34", + "currency_symbol":"\u20ac", + "flag":"ES.png" + }, + "728":{ + "capital":"Juba", + "citizenship":"South Sudanese", + "country-code":"728", + "currency":"South Sudanese pound", + "currency_code":"SSP", + "currency_sub_unit":"piaster", + "full_name":"Republic of South Sudan", + "iso_3166_2":"SS", + "iso_3166_3":"SSD", + "name":"South Sudan", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"211" + }, + "729":{ + "capital":"Khartoum", + "citizenship":"Sudanese", + "country-code":"729", + "currency":"Sudanese pound", + "currency_code":"SDG", + "currency_sub_unit":"piastre", + "full_name":"Republic of the Sudan", + "iso_3166_2":"SD", + "iso_3166_3":"SDN", + "name":"Sudan", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"249" + }, + "732":{ + "capital":"Al aaiun", + "citizenship":"Sahrawi", + "country-code":"732", + "currency":"Moroccan dirham", + "currency_code":"MAD", + "currency_sub_unit":"centime", + "full_name":"Western Sahara", + "iso_3166_2":"EH", + "iso_3166_3":"ESH", + "name":"Western Sahara", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"212", + "currency_symbol":"MAD", + "flag":"EH.png" + }, + "740":{ + "capital":"Paramaribo", + "citizenship":"Surinamese", + "country-code":"740", + "currency":"Surinamese dollar", + "currency_code":"SRD", + "currency_sub_unit":"cent", + "full_name":"Republic of Suriname", + "iso_3166_2":"SR", + "iso_3166_3":"SUR", + "name":"Suriname", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"597", + "currency_symbol":"$", + "flag":"SR.png" + }, + "744":{ + "capital":"Longyearbyen", + "citizenship":"of Svalbard", + "country-code":"744", + "currency":"Norwegian krone (pl. kroner)", + "currency_code":"NOK", + "currency_sub_unit":"\u00f8re (inv.)", + "full_name":"Svalbard and Jan Mayen", + "iso_3166_2":"SJ", + "iso_3166_3":"SJM", + "name":"Svalbard and Jan Mayen", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"47", + "currency_symbol":"kr", + "flag":"SJ.png" + }, + "748":{ + "capital":"Mbabane", + "citizenship":"Swazi", + "country-code":"748", + "currency":"lilangeni", + "currency_code":"SZL", + "currency_sub_unit":"cent", + "full_name":"Kingdom of Swaziland", + "iso_3166_2":"SZ", + "iso_3166_3":"SWZ", + "name":"Swaziland", + "region-code":"002", + "sub-region-code":"018", + "eea":false, + "calling_code":"268", + "currency_symbol":"SZL", + "flag":"SZ.png" + }, + "752":{ + "capital":"Stockholm", + "citizenship":"Swedish", + "country-code":"752", + "currency":"krona (pl. kronor)", + "currency_code":"SEK", + "currency_sub_unit":"\u00f6re (inv.)", + "full_name":"Kingdom of Sweden", + "iso_3166_2":"SE", + "iso_3166_3":"SWE", + "name":"Sweden", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"46", + "currency_symbol":"kr", + "flag":"SE.png" + }, + "756":{ + "capital":"Berne", + "citizenship":"Swiss", + "country-code":"756", + "currency":"Swiss franc", + "currency_code":"CHF", + "currency_sub_unit":"centime", + "full_name":"Swiss Confederation", + "iso_3166_2":"CH", + "iso_3166_3":"CHE", + "name":"Switzerland", + "region-code":"150", + "sub-region-code":"155", + "eea":true, + "calling_code":"41", + "currency_symbol":"CHF", + "flag":"CH.png" + }, + "760":{ + "capital":"Damascus", + "citizenship":"Syrian", + "country-code":"760", + "currency":"Syrian pound", + "currency_code":"SYP", + "currency_sub_unit":"piastre", + "full_name":"Syrian Arab Republic", + "iso_3166_2":"SY", + "iso_3166_3":"SYR", + "name":"Syrian Arab Republic", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"963", + "currency_symbol":"\u00a3", + "flag":"SY.png" + }, + "762":{ + "capital":"Dushanbe", + "citizenship":"Tajik", + "country-code":"762", + "currency":"somoni", + "currency_code":"TJS", + "currency_sub_unit":"diram", + "full_name":"Republic of Tajikistan", + "iso_3166_2":"TJ", + "iso_3166_3":"TJK", + "name":"Tajikistan", + "region-code":"142", + "sub-region-code":"143", + "eea":false, + "calling_code":"992", + "currency_symbol":"TJS", + "flag":"TJ.png" + }, + "764":{ + "capital":"Bangkok", + "citizenship":"Thai", + "country-code":"764", + "currency":"baht (inv.)", + "currency_code":"THB", + "currency_sub_unit":"satang (inv.)", + "full_name":"Kingdom of Thailand", + "iso_3166_2":"TH", + "iso_3166_3":"THA", + "name":"Thailand", + "region-code":"142", + "sub-region-code":"035", + "eea":false, + "calling_code":"66", + "currency_symbol":"\u0e3f", + "flag":"TH.png" + }, + "768":{ + "capital":"Lom\u00e9", + "citizenship":"Togolese", + "country-code":"768", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Togolese Republic", + "iso_3166_2":"TG", + "iso_3166_3":"TGO", + "name":"Togo", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"228", + "currency_symbol":"XOF", + "flag":"TG.png" + }, + "772":{ + "capital":"(TK2)", + "citizenship":"Tokelauan", + "country-code":"772", + "currency":"New Zealand dollar", + "currency_code":"NZD", + "currency_sub_unit":"cent", + "full_name":"Tokelau", + "iso_3166_2":"TK", + "iso_3166_3":"TKL", + "name":"Tokelau", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"690", + "currency_symbol":"$", + "flag":"TK.png" + }, + "776":{ + "capital":"Nuku\u2019alofa", + "citizenship":"Tongan", + "country-code":"776", + "currency":"pa\u2019anga (inv.)", + "currency_code":"TOP", + "currency_sub_unit":"seniti (inv.)", + "full_name":"Kingdom of Tonga", + "iso_3166_2":"TO", + "iso_3166_3":"TON", + "name":"Tonga", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"676", + "currency_symbol":"T$", + "flag":"TO.png" + }, + "780":{ + "capital":"Port of Spain", + "citizenship":"Trinidadian; Tobagonian", + "country-code":"780", + "currency":"Trinidad and Tobago dollar", + "currency_code":"TTD", + "currency_sub_unit":"cent", + "full_name":"Republic of Trinidad and Tobago", + "iso_3166_2":"TT", + "iso_3166_3":"TTO", + "name":"Trinidad and Tobago", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"TT$", + "flag":"TT.png" + }, + "784":{ + "capital":"Abu Dhabi", + "citizenship":"Emirian", + "country-code":"784", + "currency":"UAE dirham", + "currency_code":"AED", + "currency_sub_unit":"fils (inv.)", + "full_name":"United Arab Emirates", + "iso_3166_2":"AE", + "iso_3166_3":"ARE", + "name":"United Arab Emirates", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"971", + "currency_symbol":"AED", + "flag":"AE.png" + }, + "788":{ + "capital":"Tunis", + "citizenship":"Tunisian", + "country-code":"788", + "currency":"Tunisian dinar", + "currency_code":"TND", + "currency_sub_unit":"millime", + "full_name":"Republic of Tunisia", + "iso_3166_2":"TN", + "iso_3166_3":"TUN", + "name":"Tunisia", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"216", + "currency_symbol":"TND", + "flag":"TN.png" + }, + "792":{ + "capital":"Ankara", + "citizenship":"Turk", + "country-code":"792", + "currency":"Turkish lira (inv.)", + "currency_code":"TRY", + "currency_sub_unit":"kurus (inv.)", + "full_name":"Republic of Turkey", + "iso_3166_2":"TR", + "iso_3166_3":"TUR", + "name":"Turkey", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"90", + "currency_symbol":"YTL", + "flag":"TR.png" + }, + "795":{ + "capital":"Ashgabat", + "citizenship":"Turkmen", + "country-code":"795", + "currency":"Turkmen manat (inv.)", + "currency_code":"TMT", + "currency_sub_unit":"tenge (inv.)", + "full_name":"Turkmenistan", + "iso_3166_2":"TM", + "iso_3166_3":"TKM", + "name":"Turkmenistan", + "region-code":"142", + "sub-region-code":"143", + "eea":false, + "calling_code":"993", + "currency_symbol":"m", + "flag":"TM.png" + }, + "796":{ + "capital":"Cockburn Town", + "citizenship":"Turks and Caicos Islander", + "country-code":"796", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"Turks and Caicos Islands", + "iso_3166_2":"TC", + "iso_3166_3":"TCA", + "name":"Turks and Caicos Islands", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"TC.png" + }, + "798":{ + "capital":"Funafuti", + "citizenship":"Tuvaluan", + "country-code":"798", + "currency":"Australian dollar", + "currency_code":"AUD", + "currency_sub_unit":"cent", + "full_name":"Tuvalu", + "iso_3166_2":"TV", + "iso_3166_3":"TUV", + "name":"Tuvalu", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"688", + "currency_symbol":"$", + "flag":"TV.png" + }, + "800":{ + "capital":"Kampala", + "citizenship":"Ugandan", + "country-code":"800", + "currency":"Uganda shilling", + "currency_code":"UGX", + "currency_sub_unit":"cent", + "full_name":"Republic of Uganda", + "iso_3166_2":"UG", + "iso_3166_3":"UGA", + "name":"Uganda", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"256", + "currency_symbol":"UGX", + "flag":"UG.png" + }, + "804":{ + "capital":"Kiev", + "citizenship":"Ukrainian", + "country-code":"804", + "currency":"hryvnia", + "currency_code":"UAH", + "currency_sub_unit":"kopiyka", + "full_name":"Ukraine", + "iso_3166_2":"UA", + "iso_3166_3":"UKR", + "name":"Ukraine", + "region-code":"150", + "sub-region-code":"151", + "eea":false, + "calling_code":"380", + "currency_symbol":"\u20b4", + "flag":"UA.png" + }, + "807":{ + "capital":"Skopje", + "citizenship":"of the former Yugoslav Republic of Macedonia", + "country-code":"807", + "currency":"denar (pl. denars)", + "currency_code":"MKD", + "currency_sub_unit":"deni (inv.)", + "full_name":"the former Yugoslav Republic of Macedonia", + "iso_3166_2":"MK", + "iso_3166_3":"MKD", + "name":"Macedonia, the former Yugoslav Republic of", + "region-code":"150", + "sub-region-code":"039", + "eea":false, + "calling_code":"389", + "currency_symbol":"\u0434\u0435\u043d", + "flag":"MK.png" + }, + "818":{ + "capital":"Cairo", + "citizenship":"Egyptian", + "country-code":"818", + "currency":"Egyptian pound", + "currency_code":"EGP", + "currency_sub_unit":"piastre", + "full_name":"Arab Republic of Egypt", + "iso_3166_2":"EG", + "iso_3166_3":"EGY", + "name":"Egypt", + "region-code":"002", + "sub-region-code":"015", + "eea":false, + "calling_code":"20", + "currency_symbol":"\u00a3", + "flag":"EG.png" + }, + "826":{ + "capital":"London", + "citizenship":"British", + "country-code":"826", + "currency":"pound sterling", + "currency_code":"GBP", + "currency_sub_unit":"penny (pl. pence)", + "full_name":"United Kingdom of Great Britain and Northern Ireland", + "iso_3166_2":"GB", + "iso_3166_3":"GBR", + "name":"United Kingdom", + "region-code":"150", + "sub-region-code":"154", + "eea":true, + "calling_code":"44", + "currency_symbol":"\u00a3", + "flag":"GB.png" + }, + "831":{ + "capital":"St Peter Port", + "citizenship":"of Guernsey", + "country-code":"831", + "currency":"Guernsey pound (GG2)", + "currency_code":"GGP (GG2)", + "currency_sub_unit":"penny (pl. pence)", + "full_name":"Bailiwick of Guernsey", + "iso_3166_2":"GG", + "iso_3166_3":"GGY", + "name":"Guernsey", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"44" + }, + "832":{ + "capital":"St Helier", + "citizenship":"of Jersey", + "country-code":"832", + "currency":"Jersey pound (JE2)", + "currency_code":"JEP (JE2)", + "currency_sub_unit":"penny (pl. pence)", + "full_name":"Bailiwick of Jersey", + "iso_3166_2":"JE", + "iso_3166_3":"JEY", + "name":"Jersey", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"44" + }, + "833":{ + "capital":"Douglas", + "citizenship":"Manxman; Manxwoman", + "country-code":"833", + "currency":"Manx pound (IM2)", + "currency_code":"IMP (IM2)", + "currency_sub_unit":"penny (pl. pence)", + "full_name":"Isle of Man", + "iso_3166_2":"IM", + "iso_3166_3":"IMN", + "name":"Isle of Man", + "region-code":"150", + "sub-region-code":"154", + "eea":false, + "calling_code":"44" + }, + "834":{ + "capital":"Dodoma (TZ1)", + "citizenship":"Tanzanian", + "country-code":"834", + "currency":"Tanzanian shilling", + "currency_code":"TZS", + "currency_sub_unit":"cent", + "full_name":"United Republic of Tanzania", + "iso_3166_2":"TZ", + "iso_3166_3":"TZA", + "name":"Tanzania, United Republic of", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"255", + "currency_symbol":"TZS", + "flag":"TZ.png" + }, + "840":{ + "capital":"Washington DC", + "citizenship":"American", + "country-code":"840", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"United States of America", + "iso_3166_2":"US", + "iso_3166_3":"USA", + "name":"United States", + "region-code":"019", + "sub-region-code":"021", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"US.png" + }, + "850":{ + "capital":"Charlotte Amalie", + "citizenship":"US Virgin Islander", + "country-code":"850", + "currency":"US dollar", + "currency_code":"USD", + "currency_sub_unit":"cent", + "full_name":"United States Virgin Islands", + "iso_3166_2":"VI", + "iso_3166_3":"VIR", + "name":"Virgin Islands, U.S.", + "region-code":"019", + "sub-region-code":"029", + "eea":false, + "calling_code":"1", + "currency_symbol":"$", + "flag":"VI.png" + }, + "854":{ + "capital":"Ouagadougou", + "citizenship":"Burkinabe", + "country-code":"854", + "currency":"CFA franc (BCEAO)", + "currency_code":"XOF", + "currency_sub_unit":"centime", + "full_name":"Burkina Faso", + "iso_3166_2":"BF", + "iso_3166_3":"BFA", + "name":"Burkina Faso", + "region-code":"002", + "sub-region-code":"011", + "eea":false, + "calling_code":"226", + "currency_symbol":"XOF", + "flag":"BF.png" + }, + "858":{ + "capital":"Montevideo", + "citizenship":"Uruguayan", + "country-code":"858", + "currency":"Uruguayan peso", + "currency_code":"UYU", + "currency_sub_unit":"cent\u00e9simo", + "full_name":"Eastern Republic of Uruguay", + "iso_3166_2":"UY", + "iso_3166_3":"URY", + "name":"Uruguay", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"598", + "currency_symbol":"$U", + "flag":"UY.png" + }, + "860":{ + "capital":"Tashkent", + "citizenship":"Uzbek", + "country-code":"860", + "currency":"sum (inv.)", + "currency_code":"UZS", + "currency_sub_unit":"tiyin (inv.)", + "full_name":"Republic of Uzbekistan", + "iso_3166_2":"UZ", + "iso_3166_3":"UZB", + "name":"Uzbekistan", + "region-code":"142", + "sub-region-code":"143", + "eea":false, + "calling_code":"998", + "currency_symbol":"\u043b\u0432", + "flag":"UZ.png" + }, + "862":{ + "capital":"Caracas", + "citizenship":"Venezuelan", + "country-code":"862", + "currency":"bol\u00edvar fuerte (pl. bol\u00edvares fuertes)", + "currency_code":"VEF", + "currency_sub_unit":"c\u00e9ntimo", + "full_name":"Bolivarian Republic of Venezuela", + "iso_3166_2":"VE", + "iso_3166_3":"VEN", + "name":"Venezuela, Bolivarian Republic of", + "region-code":"019", + "sub-region-code":"005", + "eea":false, + "calling_code":"58", + "currency_symbol":"Bs", + "flag":"VE.png" + }, + "876":{ + "capital":"Mata-Utu", + "citizenship":"Wallisian; Futunan; Wallis and Futuna Islander", + "country-code":"876", + "currency":"CFP franc", + "currency_code":"XPF", + "currency_sub_unit":"centime", + "full_name":"Wallis and Futuna", + "iso_3166_2":"WF", + "iso_3166_3":"WLF", + "name":"Wallis and Futuna", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"681", + "currency_symbol":"XPF", + "flag":"WF.png" + }, + "882":{ + "capital":"Apia", + "citizenship":"Samoan", + "country-code":"882", + "currency":"tala (inv.)", + "currency_code":"WST", + "currency_sub_unit":"sene (inv.)", + "full_name":"Independent State of Samoa", + "iso_3166_2":"WS", + "iso_3166_3":"WSM", + "name":"Samoa", + "region-code":"009", + "sub-region-code":"061", + "eea":false, + "calling_code":"685", + "currency_symbol":"WS$", + "flag":"WS.png" + }, + "887":{ + "capital":"San\u2019a", + "citizenship":"Yemenite", + "country-code":"887", + "currency":"Yemeni rial", + "currency_code":"YER", + "currency_sub_unit":"fils (inv.)", + "full_name":"Republic of Yemen", + "iso_3166_2":"YE", + "iso_3166_3":"YEM", + "name":"Yemen", + "region-code":"142", + "sub-region-code":"145", + "eea":false, + "calling_code":"967", + "currency_symbol":"\ufdfc", + "flag":"YE.png" + }, + "894":{ + "capital":"Lusaka", + "citizenship":"Zambian", + "country-code":"894", + "currency":"Zambian kwacha (inv.)", + "currency_code":"ZMW", + "currency_sub_unit":"ngwee (inv.)", + "full_name":"Republic of Zambia", + "iso_3166_2":"ZM", + "iso_3166_3":"ZMB", + "name":"Zambia", + "region-code":"002", + "sub-region-code":"014", + "eea":false, + "calling_code":"260", + "currency_symbol":"ZK", + "flag":"ZM.png" + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..aabff497 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "Attendize", + "version": "1.0.0", + "description": "Attendize Ticketing Platform", + "main": "public/index.php", + "dependencies": { + "less": "~1.7.0" + }, + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-less": "^0.12.0", + "grunt-contrib-uglify": "^0.6.0", + "grunt-contrib-watch": "^0.6.1", + "gulp": "^3.8.8", + "laravel-elixir": "*" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://david4ie@bitbucket.org/david4ie/attendize.git" + }, + "author": "Dave Earley", + "license": "ISC" +} diff --git a/phpspec.yml b/phpspec.yml new file mode 100644 index 00000000..eb57939e --- /dev/null +++ b/phpspec.yml @@ -0,0 +1,5 @@ +suites: + main: + namespace: App + psr4_prefix: App + src_path: app \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..08522be9 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,22 @@ + + + + + ./tests/ + + + + + + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 00000000..77827ae7 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,15 @@ + + + Options -MultiViews + + + RewriteEngine On + + # Redirect Trailing Slashes... + RewriteRule ^(.*)/$ /$1 [L,R=301] + + # Handle Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/assets/images/LOGO-NO-TEXT-300x300.png b/public/assets/images/LOGO-NO-TEXT-300x300.png new file mode 100644 index 00000000..e9b0ac3f Binary files /dev/null and b/public/assets/images/LOGO-NO-TEXT-300x300.png differ diff --git a/public/assets/images/loading-bubbles.svg b/public/assets/images/loading-bubbles.svg new file mode 100644 index 00000000..63e29125 --- /dev/null +++ b/public/assets/images/loading-bubbles.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/public/assets/images/logo-100x100-darkBg.png b/public/assets/images/logo-100x100-darkBg.png new file mode 100644 index 00000000..61d168e6 Binary files /dev/null and b/public/assets/images/logo-100x100-darkBg.png differ diff --git a/public/assets/images/logo-100x100-lightBg.png b/public/assets/images/logo-100x100-lightBg.png new file mode 100644 index 00000000..98160ba9 Binary files /dev/null and b/public/assets/images/logo-100x100-lightBg.png differ diff --git a/public/assets/images/logo-300x150-darkBg.png b/public/assets/images/logo-300x150-darkBg.png new file mode 100644 index 00000000..805ec486 Binary files /dev/null and b/public/assets/images/logo-300x150-darkBg.png differ diff --git a/public/assets/images/logo-300x300.png b/public/assets/images/logo-300x300.png new file mode 100644 index 00000000..5eb9ae5a Binary files /dev/null and b/public/assets/images/logo-300x300.png differ diff --git a/public/assets/images/logo-email.png b/public/assets/images/logo-email.png new file mode 100644 index 00000000..9ca696f0 Binary files /dev/null and b/public/assets/images/logo-email.png differ diff --git a/public/assets/images/logo-white-50.png b/public/assets/images/logo-white-50.png new file mode 100644 index 00000000..7d81bcc4 Binary files /dev/null and b/public/assets/images/logo-white-50.png differ diff --git a/public/assets/images/logo-white-65.png b/public/assets/images/logo-white-65.png new file mode 100644 index 00000000..ae238e64 Binary files /dev/null and b/public/assets/images/logo-white-65.png differ diff --git a/public/assets/images/logo.svg b/public/assets/images/logo.svg new file mode 100644 index 00000000..4b95869c --- /dev/null +++ b/public/assets/images/logo.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/images/logo_ai.svg b/public/assets/images/logo_ai.svg new file mode 100644 index 00000000..c62fdd46 --- /dev/null +++ b/public/assets/images/logo_ai.svg @@ -0,0 +1,152 @@ + + + + diff --git a/public/assets/images/powered-by-stripe.png b/public/assets/images/powered-by-stripe.png new file mode 100644 index 00000000..d3ee3183 Binary files /dev/null and b/public/assets/images/powered-by-stripe.png differ diff --git a/public/assets/images/public/EventPage/backgrounds/1.jpg b/public/assets/images/public/EventPage/backgrounds/1.jpg new file mode 100644 index 00000000..bdfa7f12 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/1.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/10.jpg b/public/assets/images/public/EventPage/backgrounds/10.jpg new file mode 100644 index 00000000..a8ce94be Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/10.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/11.jpg b/public/assets/images/public/EventPage/backgrounds/11.jpg new file mode 100644 index 00000000..69a28673 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/11.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/12.jpg b/public/assets/images/public/EventPage/backgrounds/12.jpg new file mode 100644 index 00000000..d953551a Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/12.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/13.jpg b/public/assets/images/public/EventPage/backgrounds/13.jpg new file mode 100644 index 00000000..1a6299e4 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/13.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/14.jpg b/public/assets/images/public/EventPage/backgrounds/14.jpg new file mode 100644 index 00000000..8f191686 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/14.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/15.jpg b/public/assets/images/public/EventPage/backgrounds/15.jpg new file mode 100644 index 00000000..91234e42 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/15.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/16.jpg b/public/assets/images/public/EventPage/backgrounds/16.jpg new file mode 100644 index 00000000..f7f1c3ab Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/16.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/17.jpg b/public/assets/images/public/EventPage/backgrounds/17.jpg new file mode 100644 index 00000000..176ddb22 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/17.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/18.jpg b/public/assets/images/public/EventPage/backgrounds/18.jpg new file mode 100644 index 00000000..2bd309b9 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/18.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/19.jpg b/public/assets/images/public/EventPage/backgrounds/19.jpg new file mode 100644 index 00000000..25c1fafe Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/19.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/2.jpg b/public/assets/images/public/EventPage/backgrounds/2.jpg new file mode 100644 index 00000000..8f929b6b Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/2.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/20.jpg b/public/assets/images/public/EventPage/backgrounds/20.jpg new file mode 100644 index 00000000..f62bb338 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/20.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/21.jpg b/public/assets/images/public/EventPage/backgrounds/21.jpg new file mode 100644 index 00000000..57f5b438 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/21.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/22.jpg b/public/assets/images/public/EventPage/backgrounds/22.jpg new file mode 100644 index 00000000..c7f6b17e Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/22.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/23.jpg b/public/assets/images/public/EventPage/backgrounds/23.jpg new file mode 100644 index 00000000..2ad2c438 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/23.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/24.jpg b/public/assets/images/public/EventPage/backgrounds/24.jpg new file mode 100644 index 00000000..be345ea8 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/24.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/25.jpg b/public/assets/images/public/EventPage/backgrounds/25.jpg new file mode 100644 index 00000000..abb4fbdf Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/25.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/26.jpg b/public/assets/images/public/EventPage/backgrounds/26.jpg new file mode 100644 index 00000000..4bc08d91 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/26.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/27.jpg b/public/assets/images/public/EventPage/backgrounds/27.jpg new file mode 100644 index 00000000..8956afcd Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/27.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/28.jpg b/public/assets/images/public/EventPage/backgrounds/28.jpg new file mode 100644 index 00000000..37a77394 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/28.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/29.jpg b/public/assets/images/public/EventPage/backgrounds/29.jpg new file mode 100644 index 00000000..376a5911 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/29.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/3.jpg b/public/assets/images/public/EventPage/backgrounds/3.jpg new file mode 100644 index 00000000..30243f84 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/3.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/30.jpg b/public/assets/images/public/EventPage/backgrounds/30.jpg new file mode 100644 index 00000000..9b9a9a59 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/30.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/31.jpg b/public/assets/images/public/EventPage/backgrounds/31.jpg new file mode 100644 index 00000000..a5f6f2d7 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/31.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/32.jpg b/public/assets/images/public/EventPage/backgrounds/32.jpg new file mode 100644 index 00000000..b7c124a8 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/32.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/33.jpg b/public/assets/images/public/EventPage/backgrounds/33.jpg new file mode 100644 index 00000000..ba109d5b Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/33.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/34.jpg b/public/assets/images/public/EventPage/backgrounds/34.jpg new file mode 100644 index 00000000..d6399926 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/34.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/35.jpg b/public/assets/images/public/EventPage/backgrounds/35.jpg new file mode 100644 index 00000000..829655da Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/35.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/36.jpg b/public/assets/images/public/EventPage/backgrounds/36.jpg new file mode 100644 index 00000000..12a8d75e Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/36.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/37.jpg b/public/assets/images/public/EventPage/backgrounds/37.jpg new file mode 100644 index 00000000..0cb6e9aa Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/37.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/38.jpg b/public/assets/images/public/EventPage/backgrounds/38.jpg new file mode 100644 index 00000000..2fbe778b Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/38.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/39.jpg b/public/assets/images/public/EventPage/backgrounds/39.jpg new file mode 100644 index 00000000..ffc3c870 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/39.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/4.jpg b/public/assets/images/public/EventPage/backgrounds/4.jpg new file mode 100644 index 00000000..e639ffaa Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/4.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/5.jpg b/public/assets/images/public/EventPage/backgrounds/5.jpg new file mode 100644 index 00000000..b295e926 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/5.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/6.jpg b/public/assets/images/public/EventPage/backgrounds/6.jpg new file mode 100644 index 00000000..f53c0c42 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/6.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/7.jpg b/public/assets/images/public/EventPage/backgrounds/7.jpg new file mode 100644 index 00000000..71bd69de Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/7.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/8.jpg b/public/assets/images/public/EventPage/backgrounds/8.jpg new file mode 100644 index 00000000..19ddc22a Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/8.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/9.jpg b/public/assets/images/public/EventPage/backgrounds/9.jpg new file mode 100644 index 00000000..64f74bf5 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/9.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/1.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/1.jpg new file mode 100644 index 00000000..b106592c Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/1.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/10.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/10.jpg new file mode 100644 index 00000000..d554dd37 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/10.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/11.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/11.jpg new file mode 100644 index 00000000..cace60ab Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/11.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/12.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/12.jpg new file mode 100644 index 00000000..694227a8 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/12.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/13.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/13.jpg new file mode 100644 index 00000000..003007f3 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/13.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/14.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/14.jpg new file mode 100644 index 00000000..68232e43 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/14.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/15.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/15.jpg new file mode 100644 index 00000000..4e506d12 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/15.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/16.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/16.jpg new file mode 100644 index 00000000..44acbc25 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/16.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/17.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/17.jpg new file mode 100644 index 00000000..2c40528c Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/17.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/18.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/18.jpg new file mode 100644 index 00000000..47f5bb61 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/18.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/19.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/19.jpg new file mode 100644 index 00000000..11a78088 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/19.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/2.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/2.jpg new file mode 100644 index 00000000..4685901b Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/2.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/20.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/20.jpg new file mode 100644 index 00000000..ae836a21 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/20.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/21.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/21.jpg new file mode 100644 index 00000000..7dfdefc6 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/21.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/22.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/22.jpg new file mode 100644 index 00000000..1c9e0321 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/22.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/23.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/23.jpg new file mode 100644 index 00000000..b6ec8100 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/23.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/24.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/24.jpg new file mode 100644 index 00000000..2cc585a9 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/24.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/25.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/25.jpg new file mode 100644 index 00000000..2e54615e Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/25.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/26.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/26.jpg new file mode 100644 index 00000000..46b4cecc Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/26.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/27.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/27.jpg new file mode 100644 index 00000000..7265aae5 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/27.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/28.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/28.jpg new file mode 100644 index 00000000..f04f3926 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/28.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/29.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/29.jpg new file mode 100644 index 00000000..4ccd709e Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/29.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/3.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/3.jpg new file mode 100644 index 00000000..28fc72f7 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/3.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/30.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/30.jpg new file mode 100644 index 00000000..fc97c9a9 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/30.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/31.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/31.jpg new file mode 100644 index 00000000..5e118d3a Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/31.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/32.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/32.jpg new file mode 100644 index 00000000..a565d1e8 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/32.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/33.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/33.jpg new file mode 100644 index 00000000..1d958eb8 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/33.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/34.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/34.jpg new file mode 100644 index 00000000..c1abab09 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/34.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/35.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/35.jpg new file mode 100644 index 00000000..4f5e4b35 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/35.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/36.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/36.jpg new file mode 100644 index 00000000..003b624d Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/36.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/37.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/37.jpg new file mode 100644 index 00000000..1c28d005 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/37.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/38.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/38.jpg new file mode 100644 index 00000000..bdd193ee Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/38.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/39.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/39.jpg new file mode 100644 index 00000000..309aaa04 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/39.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/4.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/4.jpg new file mode 100644 index 00000000..073e5fbe Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/4.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/5.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/5.jpg new file mode 100644 index 00000000..6d6bb694 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/5.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/6.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/6.jpg new file mode 100644 index 00000000..46d62e70 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/6.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/7.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/7.jpg new file mode 100644 index 00000000..d727d1c7 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/7.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/8.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/8.jpg new file mode 100644 index 00000000..1121eca2 Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/8.jpg differ diff --git a/public/assets/images/public/EventPage/backgrounds/thumbs/9.jpg b/public/assets/images/public/EventPage/backgrounds/thumbs/9.jpg new file mode 100644 index 00000000..2b2cddaa Binary files /dev/null and b/public/assets/images/public/EventPage/backgrounds/thumbs/9.jpg differ diff --git a/public/assets/images/public/EventPage/credit-card-logos.png b/public/assets/images/public/EventPage/credit-card-logos.png new file mode 100644 index 00000000..2718d08e Binary files /dev/null and b/public/assets/images/public/EventPage/credit-card-logos.png differ diff --git a/public/assets/images/stripe-connect-blue.png b/public/assets/images/stripe-connect-blue.png new file mode 100644 index 00000000..0dd25939 Binary files /dev/null and b/public/assets/images/stripe-connect-blue.png differ diff --git a/public/assets/images/touch/apple-touch-icon-114x114.png b/public/assets/images/touch/apple-touch-icon-114x114.png new file mode 100644 index 00000000..6500f89c Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-114x114.png differ diff --git a/public/assets/images/touch/apple-touch-icon-120x120.png b/public/assets/images/touch/apple-touch-icon-120x120.png new file mode 100644 index 00000000..1bfc5cb6 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-120x120.png differ diff --git a/public/assets/images/touch/apple-touch-icon-144x144.png b/public/assets/images/touch/apple-touch-icon-144x144.png new file mode 100644 index 00000000..0d4fb5f3 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-144x144.png differ diff --git a/public/assets/images/touch/apple-touch-icon-152x152.png b/public/assets/images/touch/apple-touch-icon-152x152.png new file mode 100644 index 00000000..14a70418 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-152x152.png differ diff --git a/public/assets/images/touch/apple-touch-icon-180x180.png b/public/assets/images/touch/apple-touch-icon-180x180.png new file mode 100644 index 00000000..f2b698b8 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-180x180.png differ diff --git a/public/assets/images/touch/apple-touch-icon-57x57.png b/public/assets/images/touch/apple-touch-icon-57x57.png new file mode 100644 index 00000000..18640d55 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-57x57.png differ diff --git a/public/assets/images/touch/apple-touch-icon-60x60.png b/public/assets/images/touch/apple-touch-icon-60x60.png new file mode 100644 index 00000000..1ac344d2 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-60x60.png differ diff --git a/public/assets/images/touch/apple-touch-icon-72x72.png b/public/assets/images/touch/apple-touch-icon-72x72.png new file mode 100644 index 00000000..631a99a1 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-72x72.png differ diff --git a/public/assets/images/touch/apple-touch-icon-76x76.png b/public/assets/images/touch/apple-touch-icon-76x76.png new file mode 100644 index 00000000..5bc14ab7 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-76x76.png differ diff --git a/public/assets/images/touch/apple-touch-icon-precomposed.png b/public/assets/images/touch/apple-touch-icon-precomposed.png new file mode 100644 index 00000000..036b4578 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon-precomposed.png differ diff --git a/public/assets/images/touch/apple-touch-icon.png b/public/assets/images/touch/apple-touch-icon.png new file mode 100644 index 00000000..f2b698b8 Binary files /dev/null and b/public/assets/images/touch/apple-touch-icon.png differ diff --git a/public/assets/images/touch/browserconfig.xml b/public/assets/images/touch/browserconfig.xml new file mode 100644 index 00000000..c353dc4a --- /dev/null +++ b/public/assets/images/touch/browserconfig.xml @@ -0,0 +1,12 @@ + + + + + + + + + #ffffff + + + diff --git a/public/assets/images/touch/favicon-160x160.png b/public/assets/images/touch/favicon-160x160.png new file mode 100644 index 00000000..9ba968e5 Binary files /dev/null and b/public/assets/images/touch/favicon-160x160.png differ diff --git a/public/assets/images/touch/favicon-16x16.png b/public/assets/images/touch/favicon-16x16.png new file mode 100644 index 00000000..cdb11868 Binary files /dev/null and b/public/assets/images/touch/favicon-16x16.png differ diff --git a/public/assets/images/touch/favicon-192x192.png b/public/assets/images/touch/favicon-192x192.png new file mode 100644 index 00000000..791baea8 Binary files /dev/null and b/public/assets/images/touch/favicon-192x192.png differ diff --git a/public/assets/images/touch/favicon-32x32.png b/public/assets/images/touch/favicon-32x32.png new file mode 100644 index 00000000..731ebb13 Binary files /dev/null and b/public/assets/images/touch/favicon-32x32.png differ diff --git a/public/assets/images/touch/favicon-96x96.png b/public/assets/images/touch/favicon-96x96.png new file mode 100644 index 00000000..9c191c6f Binary files /dev/null and b/public/assets/images/touch/favicon-96x96.png differ diff --git a/public/assets/images/touch/favicon.ico b/public/assets/images/touch/favicon.ico new file mode 100644 index 00000000..2b8458be Binary files /dev/null and b/public/assets/images/touch/favicon.ico differ diff --git a/public/assets/images/touch/mstile-144x144.png b/public/assets/images/touch/mstile-144x144.png new file mode 100644 index 00000000..608cd869 Binary files /dev/null and b/public/assets/images/touch/mstile-144x144.png differ diff --git a/public/assets/images/touch/mstile-150x150.png b/public/assets/images/touch/mstile-150x150.png new file mode 100644 index 00000000..13b38ebb Binary files /dev/null and b/public/assets/images/touch/mstile-150x150.png differ diff --git a/public/assets/images/touch/mstile-310x150.png b/public/assets/images/touch/mstile-310x150.png new file mode 100644 index 00000000..29c1477d Binary files /dev/null and b/public/assets/images/touch/mstile-310x150.png differ diff --git a/public/assets/images/touch/mstile-310x310.png b/public/assets/images/touch/mstile-310x310.png new file mode 100644 index 00000000..15db45d0 Binary files /dev/null and b/public/assets/images/touch/mstile-310x310.png differ diff --git a/public/assets/images/touch/mstile-70x70.png b/public/assets/images/touch/mstile-70x70.png new file mode 100644 index 00000000..4b3419c8 Binary files /dev/null and b/public/assets/images/touch/mstile-70x70.png differ diff --git a/public/assets/javascript/app-public.js b/public/assets/javascript/app-public.js new file mode 100644 index 00000000..85146c33 --- /dev/null +++ b/public/assets/javascript/app-public.js @@ -0,0 +1,331 @@ +$(function() { + $('form.ajax').on('submit', function(e) { + e.preventDefault(); + e.stopImmediatePropagation(); + + var $form = + $(this), + $submitButton = $form.find('input[type=submit]'), + ajaxFormConf = { + delegation: true, + beforeSerialize: function(jqForm, options) { + window.doSubmit = true; + clearFormErrors(jqForm[0]); + toggleSubmitDisabled($submitButton); + }, + beforeSubmit: function() { + $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + return window.doSubmit; + }, + error: function(data, statusText, xhr, $form) { + $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + showMessage('Whoops!, it looks like something went wrong on our servers.\n\ + Please try again, or contact support if the problem persists.'); + }, + success: function(data, statusText, xhr, $form) { + + if (data.message) { + showMessage(data.message); + } + switch (data.status) { + case 'success': + + if (data.redirectUrl) { + window.location = data.redirectUrl; + } + + var $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + break; + + case 'error': + if (data.messages) { + $.each(data.messages, function(index, error) { + + /* + * use the class as the selector if the input name is an array. + */ + var selector = (index.indexOf(".") >= 0) ? '.' + index.replace(/\./g, "\\.") : ':input[name=' + index + ']'; + + $(selector, $form) + .after('
' + error + '
') + .parent() + .addClass('has-error'); + }); + } + + + var $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + + break; + + default: + break; + } + }, + dataType: 'json' + }; + + toggleSubmitDisabled($submitButton); + + if ($form.hasClass('payment-form')) { + + clearFormErrors($('.payment-form')); + + Stripe.setPublishableKey($form.data('stripe-pub-key')); + + var + noErrors = true, + $cardNumber = $('.card-number'), + $cardName = $('.card-name'), + $cvcNumber = $('.card-cvc'), + $expiryMonth = $('.card-expiry-month'), + $expiryYear = $('.card-expiry-year'); + + if (!Stripe.validateCardNumber($cardNumber.val())) { + showFormError($cardNumber, 'The credit card number appears to be invalid.'); + noErrors = false; + } + + if (!Stripe.validateCVC($cvcNumber.val())) { + showFormError($cvcNumber, 'The CVC number appears to be invalid.'); + noErrors = false; + } + + if (!Stripe.validateExpiry($expiryMonth.val(), $expiryYear.val())) { + showFormError($expiryMonth, 'The expiration date appears to be invalid.'); + showFormError($expiryYear, ''); + noErrors = false; + } + + if (noErrors) { + Stripe.card.createToken({ + name: $cardName.val(), + number: $cardNumber.val(), + cvc: $cvcNumber.val(), + exp_month: $expiryMonth.val(), + exp_year: $expiryYear.val() + }, + function(status, response) { + + if (response.error) { + clearFormErrors($('.payment-form')); + showFormError($('*[data-stripe=' + response.error.param + ']', $('.payment-form')), response.error.message); + toggleSubmitDisabled($submitButton); + } else { + var token = response.id; + $form.append($('').val(token)); + $form.ajaxSubmit(ajaxFormConf); + } + + }); + } else { + showMessage('Please check your card details and try again.'); + toggleSubmitDisabled($submitButton); + } + + } else { + $form.ajaxSubmit(ajaxFormConf); + } + }); + + $('a').smoothScroll({ + offset: -60 + }); + + /* Scroll to top */ + $(window).scroll(function() { + if ($(this).scrollTop() > 100) { + $('.totop').fadeIn(); + } else { + $('.totop').fadeOut(); + } + }); + + $('#organiserHead').on('click', function(e) { + e.stopImmediatePropagation(); + $('#organiser')[0].scrollIntoView(); + }); + + $('#contact_organiser').on('click', function(e) { + e.preventDefault(); + $('.contact_form').slideToggle(); + }); + + $('#mirror_buyer_info').change(function(e) { + + if ($(this).prop('checked')) { + $('.ticket_holders_details').slideUp(); + } else { + $('.ticket_holders_details').slideDown(); + } + + $('.ticket_holder_first_name').val($('#order_first_name').val()); + $('.ticket_holder_last_name').val($('#order_last_name').val()); + $('.ticket_holder_email').val($('#order_email').val()); + }); + +}); + + +/** + * Toggle a submit button disabled/enabled - duh! + * + * @param element $submitButton + * @returns void + */ +function toggleSubmitDisabled($submitButton) { + + if ($submitButton.hasClass('disabled')) { + $submitButton.attr('disabled', false) + .removeClass('disabled') + .val($submitButton.data('original-text')); + return; + } + + $submitButton.data('original-text', $submitButton.val()) + .attr('disabled', true) + .addClass('disabled') + .val('Just a second...'); +} + +/** + * Clears given form of any error classes / messages + * + * @param {Element} $form + * @returns {void} + */ +function clearFormErrors($form) { + $($form) + .find('.error.help-block') + .remove(); + $($form).find(':input') + .parent() + .removeClass('has-error'); +} + +function showFormError($formElement, message) { + $formElement.after('
' + message + '
') + .parent() + .addClass('has-error'); +} + +/** + * Shows users a message. + * Currently uses humane.js + * + * @param string message + * @returns void + */ +function showMessage(message) { + humane.log(message, { + timeout: 2500 + }); +} + +function hideMessage() { + humane.remove(); +} + +/** + * Counts down to the given number of seconds + * + * @param element $element + * @param int seconds + * @returns void + */ +function setCountdown($element, seconds) { + + var endTime, mins, msLeft, time, twoMinWarningShown = false; + + function twoDigits(n) + { + return (n <= 9 ? "0" + n : n); + } + + function updateTimer() + { + msLeft = endTime - (+new Date); + if (msLeft < 1000) { + alert("You have run out of time! You will have to restart the order process."); + location.reload(); + } else { + + if (msLeft < 120000 && !twoMinWarningShown) { + showMessage("You only have 2 minutes left to complete this order!"); + twoMinWarningShown = true; + } + + time = new Date(msLeft); + mins = time.getUTCMinutes(); + $element.html('' + mins + ' minutes and ' + twoDigits(time.getUTCSeconds()) + ' seconds'); + setTimeout(updateTimer, time.getUTCMilliseconds() + 500); + } + } + + endTime = (+new Date) + 1000 * seconds + 500; + updateTimer(); +} + + +/*! + * Smooth Scroll - v1.4.13 - 2013-11-02 + * https://github.com/kswedberg/jquery-smooth-scroll + * Copyright (c) 2013 Karl Swedberg + * Licensed MIT (https://github.com/kswedberg/jquery-smooth-scroll/blob/master/LICENSE-MIT) + */ +(function(t) { + function e(t) { + return t.replace(/(:|\.)/g, "\\$1") + } + var l = "1.4.13", o = {}, s = {exclude: [], excludeWithin: [], offset: 0, direction: "top", scrollElement: null, scrollTarget: null, beforeScroll: function() { + }, afterScroll: function() { + }, easing: "swing", speed: 400, autoCoefficent: 2, preventDefault: !0}, n = function(e) { + var l = [], o = !1, s = e.dir && "left" == e.dir ? "scrollLeft" : "scrollTop"; + return this.each(function() { + if (this != document && this != window) { + var e = t(this); + e[s]() > 0 ? l.push(this) : (e[s](1), o = e[s]() > 0, o && l.push(this), e[s](0)) + } + }), l.length || this.each(function() { + "BODY" === this.nodeName && (l = [this]) + }), "first" === e.el && l.length > 1 && (l = [l[0]]), l + }; + t.fn.extend({scrollable: function(t) { + var e = n.call(this, {dir: t}); + return this.pushStack(e) + }, firstScrollable: function(t) { + var e = n.call(this, {el: "first", dir: t}); + return this.pushStack(e) + }, smoothScroll: function(l, o) { + if (l = l || {}, "options" === l) + return o ? this.each(function() { + var e = t(this), l = t.extend(e.data("ssOpts") || {}, o); + t(this).data("ssOpts", l) + }) : this.first().data("ssOpts"); + var s = t.extend({}, t.fn.smoothScroll.defaults, l), n = t.smoothScroll.filterPath(location.pathname); + return this.unbind("click.smoothscroll").bind("click.smoothscroll", function(l) { + var o = this, r = t(this), i = t.extend({}, s, r.data("ssOpts") || {}), c = s.exclude, a = i.excludeWithin, f = 0, h = 0, u = !0, d = {}, p = location.hostname === o.hostname || !o.hostname, m = i.scrollTarget || (t.smoothScroll.filterPath(o.pathname) || n) === n, S = e(o.hash); + if (i.scrollTarget || p && m && S) { + for (; u && c.length > f; ) + r.is(e(c[f++])) && (u = !1); + for (; u && a.length > h; ) + r.closest(a[h++]).length && (u = !1) + } else + u = !1; + u && (i.preventDefault && l.preventDefault(), t.extend(d, i, {scrollTarget: i.scrollTarget || S, link: o}), t.smoothScroll(d)) + }), this + }}), t.smoothScroll = function(e, l) { + if ("options" === e && "object" == typeof l) + return t.extend(o, l); + var s, n, r, i, c = 0, a = "offset", f = "scrollTop", h = {}, u = {}; + "number" == typeof e ? (s = t.extend({link: null}, t.fn.smoothScroll.defaults, o), r = e) : (s = t.extend({link: null}, t.fn.smoothScroll.defaults, e || {}, o), s.scrollElement && (a = "position", "static" == s.scrollElement.css("position") && s.scrollElement.css("position", "relative"))), f = "left" == s.direction ? "scrollLeft" : f, s.scrollElement ? (n = s.scrollElement, /^(?:HTML|BODY)$/.test(n[0].nodeName) || (c = n[f]())) : n = t("html, body").firstScrollable(s.direction), s.beforeScroll.call(n, s), r = "number" == typeof e ? e : l || t(s.scrollTarget)[a]() && t(s.scrollTarget)[a]()[s.direction] || 0, h[f] = r + c + s.offset, i = s.speed, "auto" === i && (i = h[f] || n.scrollTop(), i /= s.autoCoefficent), u = {duration: i, easing: s.easing, complete: function() { + s.afterScroll.call(s.link, s) + }}, s.step && (u.step = s.step), n.length ? n.stop().animate(h, u) : s.afterScroll.call(s.link, s) + }, t.smoothScroll.version = l, t.smoothScroll.filterPath = function(t) { + return t.replace(/^\//, "").replace(/(?:index|default).[a-zA-Z]{3,4}$/, "").replace(/\/$/, "") + }, t.fn.smoothScroll.defaults = s +})(jQuery); \ No newline at end of file diff --git a/public/assets/javascript/app.js b/public/assets/javascript/app.js new file mode 100644 index 00000000..3eeb4545 --- /dev/null +++ b/public/assets/javascript/app.js @@ -0,0 +1,442 @@ + + +window.Attendize = { + DateFormat: 'dd-MM-yyyy', + DateTimeFormat: 'dd-MM-yyyy hh:mm:ss', + GenericErrorMessage: 'Whoops!, An unknown error has occurred.' + + 'Please try again or contact support if the problem persists. ' +}; + +$(function () { + + /* + * -------------------------- + * Set up all our required plugins + * -------------------------- + */ + + /* Datepciker */ + $(document).ajaxComplete(function () { + $('#DatePicker').remove(); + var $div = $("
", {id: "DatePicker"}); + $("body").append($div); + $div.DateTimePicker({ + dateTimeFormat: window.Attendize.DateTimeFormat + }); + + }); + + /* Responsive sidebar */ + $(document.body).on('click', '.toggleSidebar', function (e) { + $('html').toggleClass('sidebar-open-ltr'); + e.preventDefault(); + }); + + /* Scroll to top */ + $(window).scroll(function () { + if ($(this).scrollTop() > 100) { + $('.totop').fadeIn(); + } else { + $('.totop').fadeOut(); + } + }); + + $(".totop").click(function () { + $("html, body").animate({ + scrollTop: 0 + }, 200); + }); + + + /* + * -------------------- + * Ajaxify those forms + * -------------------- + * + * All forms with the 'ajax' class will automatically handle showing errors etc. + * + */ + $('form.ajax').ajaxForm({ + delegation: true, + beforeSubmit: function (formData, jqForm, options) { + + $(jqForm[0]) + .find('.error.help-block') + .remove(); + $(jqForm[0]).find('.has-error') + .removeClass('has-error'); + + var $submitButton = $(jqForm[0]).find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + + + }, + uploadProgress: function (event, position, total, percentComplete) { + $('.uploadProgress').show().html('Uploading Images - ' + percentComplete + '% Complete... '); + }, + error: function (data, statusText, xhr, $form) { + + showMessage('Whoops!, it looks like something went wrong on our servers.\n\ + Please try again, or contact support if the problem persists.'); + + var $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + + $('.uploadProgress').hide(); + }, + success: function (data, statusText, xhr, $form) { + + switch (data.status) { + case 'success': + + if ($form.hasClass('reset')) { + $form.resetForm(); + } + + if ($form.hasClass('closeModalAfter')) { + $('.modal, .modal-backdrop').fadeOut().remove(); + } + + var $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + + if (typeof data.message !== 'undefined') { + showMessage(data.message); + } + + if (typeof data.runThis !== 'undefined') { + eval(data.runThis); + } + + if (typeof data.redirectUrl !== 'undefined') { + window.location = data.redirectUrl; + } + + break; + + case 'error': + $.each(data.messages, function (index, error) { + var $input = $(':input[name=' + index + ']', $form); + + if ($input.prop('type') === 'file') { + $('#input-' + $input.prop('name')).append('
' + error + '
') + .parent() + .addClass('has-error'); + } else { + $input.after('
' + error + '
') + .parent() + .addClass('has-error'); + } + + }); + + var $submitButton = $form.find('input[type=submit]'); + toggleSubmitDisabled($submitButton); + + break; + + default: + break; + } + + $('.uploadProgress').hide(); + }, + dataType: 'json' + }); + + + /* + * -------------------- + * Create a simple way to show remote dynamic modals from the frontend + * -------------------- + * + * E.g : + * + * Click For Modal + * + * + */ + $(document.body).on('click', '.loadModal, [data-invoke~=modal]', function (e) { + + var loadUrl = $(this).data('href'), + modalId = $(this).data('modal-id'), + cacheResult = $(this).data('cache') === 'on' ? true : false; + + // $('#' + modalId).remove(); + $('.modal').remove(); + $('html').addClass('working'); + + /* + * Hopefully this message will rarely show + */ + setTimeout(function () { + //showMessage('One second...'); #far to annoying + }, 750); + + $.ajax({ + url: loadUrl, + data: {'modal_id': modalId}, + localCache: cacheResult, + dataType: 'html', + success: function (data) { + hideMessage(); + + //history.pushState({}, '', window.location + '#' + modalId); + + $('body').append(data); + + var $modal = $('#' + modalId); + + $modal.modal({ + 'backdrop': 'static' + }); + + $modal.modal('show'); + + $modal.on('hidden.bs.modal', function (e) { + // window + location.hash = ''; + }); + + $('html').removeClass('working'); + } + }).done().fail(function (data) { + $('html').removeClass('working'); + showMessage('Whoops!, something has gone wrong.

' + data.status + ' ' + data.statusText); + }); + + e.preventDefault(); + }); + + /* + * ------------------------------------------------------------ + * A slightly hackish way to close modals on back button press. + * ------------------------------------------------------------ + */ + $(window).on('hashchange', function (e) { + $('.modal').modal('hide'); + }); + + + /* + * ------------------------------------------------------------- + * Simple way for any type of object to be deleted. + * ------------------------------------------------------------- + * + * E.g markup: + * + * Delete This Object + * + * + */ + $('.deleteThis').on('click', function (e) { + + /* + * Confirm if the user wants to delete this object + */ + if ($(this).data('confirm-delete') !== 'yes') { + $(this).data('original-text', $(this).html()).html('Click To Confirm?').data('confirm-delete', 'yes'); + + var that = $(this); + setTimeout(function () { + that.data('confirm-delete', 'no').html(that.data('original-text')); + }, 2000); + + return; + } + + var deleteId = $(this).data('id'), + deleteType = $(this).data('type'), + route = $(this).data('route'); + + $.post(route, deleteType + '_id=' + deleteId) + .done(function (data) { + + if (typeof data.message !== 'undefined') { + showMessage(data.message); + } + + switch (data.status) { + case 'success': + $('#' + deleteType + '_' + deleteId).fadeOut(); + break; + case 'error': + /* Error */ + break; + + default: + break; + } + }).fail(function (data) { + showMessage(Attendize.GenericErrorMessages); + }); + + e.preventDefault(); + }); + + + $(document.body).on('click', '.pauseTicketSales', function (e) { + + var ticketId = $(this).data('id'), + route = $(this).data('route'); + + $.post(route, 'ticket_id=' + ticketId) + .done(function (data) { + + if (typeof data.message !== 'undefined') { + showMessage(data.message); + } + + switch (data.status) { + case 'success': + setTimeout(function () { + document.location.reload(); + }, 300); + break; + case 'error': + /* Error */ + break; + + default: + break; + } + }).fail(function (data) { + showMessage(Attendize.GenericErrorMessages); + }); + + + e.preventDefault(); + }); + + /** + * Toggle checkboxes + */ + + + + + $(document.body).on('click', '.check-all', function (e) { + var toggleClass = $(this).data('check-class'); + $('.' + toggleClass).each(function () { + this.checked = $(this).checked; + }); + }); + + + /* + * ------------------------------------------------------------ + * Toggle hidden content when a.show-more-content is clicked + * ------------------------------------------------------------ + */ + $(document.body).on('click', '.show-more-options', function (e) { + + var toggleClass = !$(this).data('toggle-class') + ? '.more-options' + : $(this).data('toggle-class'); + + + if ($(this).hasClass('toggled')) { + $(this).html($(this) + .data('original-text')); + + } else { + + if (!$(this).data('original-text')) { + $(this).data('original-text', $(this).html()); + } + $(this).html(!$(this).data('show-less-text') ? 'Show Less' : $(this).data('show-less-text')); + } + + $(this).toggleClass('toggled'); + + /* + * ? + */ + if ($(this).data('clear-field')) { + $($(this).data('clear-field')).val(''); + } + + $(toggleClass).slideToggle(); + e.preventDefault(); + }); + + + /* + * Sort by trigger + */ + $('select[name=sort_by_select]').on('change', function () { + $('input[name=sort_by]').val($(this).val()).closest('form').submit(); + }); + + /** + * Custom file inputs + */ + $(document).on('change', '.btn-file :file', function () { + var input = $(this), + numFiles = input.get(0).files ? input.get(0).files.length : 1, + label = input.val().replace(/\\/g, '/').replace(/.*\//, ''); + + input.trigger('fileselect', [ + numFiles, + label + ]); + }); + + $(document.body).on('fileselect', '.btn-file :file', function (event, numFiles, label) { + var input = $(this).parents('.input-group').find(':text'), + log = numFiles > 1 ? numFiles + ' files selected' : label; + if (input.length) { + input.val(log); + } else { + if (log) { + console.log(log); + } + + } + }); + +}); + + +/** + * + * @param elm $submitButton + * @returns void + */ +function toggleSubmitDisabled($submitButton) { + + if ($submitButton.hasClass('disabled')) { + $submitButton.attr('disabled', false) + .removeClass('disabled') + .val($submitButton.data('original-text')); + return; + } + + $submitButton.data('original-text', $submitButton.val()) + .attr('disabled', true) + .addClass('disabled') + .val('Working...'); +} + +/** + * Shows users a message. + * Currently uses humane.js + * + * @param string message + * @returns void + */ +function showMessage(message) { + humane.log(message, { + timeout: 3500 + }); +} + +function showHelp(message) { + humane.log(message, { + timeout: 12000 + }); +} + +function hideMessage() { + humane.remove(); +} diff --git a/public/assets/javascript/backend.js b/public/assets/javascript/backend.js new file mode 100644 index 00000000..bf0d42ee --- /dev/null +++ b/public/assets/javascript/backend.js @@ -0,0 +1,8001 @@ +/*! + * Modernizr v2.8.3 + * www.modernizr.com + * + * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton + * Available under the BSD and MIT licenses: www.modernizr.com/license/ + */ + +/* + * Modernizr tests which native CSS3 and HTML5 features are available in + * the current UA and makes the results available to you in two ways: + * as properties on a global Modernizr object, and as classes on the + * element. This information allows you to progressively enhance + * your pages with a granular level of control over the experience. + * + * Modernizr has an optional (not included) conditional resource loader + * called Modernizr.load(), based on Yepnope.js (yepnopejs.com). + * To get a build that includes Modernizr.load(), as well as choosing + * which tests to include, go to www.modernizr.com/download/ + * + * Authors Faruk Ates, Paul Irish, Alex Sexton + * Contributors Ryan Seddon, Ben Alman + */ + +window.Modernizr = (function( window, document, undefined ) { + + var version = '2.8.3', + + Modernizr = {}, + + /*>>cssclasses*/ + // option for enabling the HTML classes to be added + enableClasses = true, + /*>>cssclasses*/ + + docElement = document.documentElement, + + /** + * Create our "modernizr" element that we do most feature tests on. + */ + mod = 'modernizr', + modElem = document.createElement(mod), + mStyle = modElem.style, + + /** + * Create the input element for various Web Forms feature tests. + */ + inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ , + + /*>>smile*/ + smile = ':)', + /*>>smile*/ + + toString = {}.toString, + + // TODO :: make the prefixes more granular + /*>>prefixes*/ + // List of property values to set for css tests. See ticket #21 + prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), + /*>>prefixes*/ + + /*>>domprefixes*/ + // Following spec is to expose vendor-specific style properties as: + // elem.style.WebkitBorderRadius + // and the following would be incorrect: + // elem.style.webkitBorderRadius + + // Webkit ghosts their properties in lowercase but Opera & Moz do not. + // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ + // erik.eae.net/archives/2008/03/10/21.48.10/ + + // More here: github.com/Modernizr/Modernizr/issues/issue/21 + omPrefixes = 'Webkit Moz O ms', + + cssomPrefixes = omPrefixes.split(' '), + + domPrefixes = omPrefixes.toLowerCase().split(' '), + /*>>domprefixes*/ + + /*>>ns*/ + ns = {'svg': 'http://www.w3.org/2000/svg'}, + /*>>ns*/ + + tests = {}, + inputs = {}, + attrs = {}, + + classes = [], + + slice = classes.slice, + + featureName, // used in testing loop + + + /*>>teststyles*/ + // Inject element with style element and some CSS rules + injectElementWithStyles = function( rule, callback, nodes, testnames ) { + + var style, ret, node, docOverflow, + div = document.createElement('div'), + // After page load injecting a fake body doesn't work so check if body exists + body = document.body, + // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it. + fakeBody = body || document.createElement('body'); + + if ( parseInt(nodes, 10) ) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while ( nodes-- ) { + node = document.createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + // '].join(''); + div.id = mod; + // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (body ? div : fakeBody).innerHTML += style; + fakeBody.appendChild(div); + if ( !body ) { + //avoid crashing IE8, if background image is used + fakeBody.style.background = ''; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + fakeBody.style.overflow = 'hidden'; + docOverflow = docElement.style.overflow; + docElement.style.overflow = 'hidden'; + docElement.appendChild(fakeBody); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if ( !body ) { + fakeBody.parentNode.removeChild(fakeBody); + docElement.style.overflow = docOverflow; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + + }, + /*>>teststyles*/ + + /*>>mq*/ + // adapted from matchMedia polyfill + // by Scott Jehl and Paul Irish + // gist.github.com/786768 + testMediaQuery = function( mq ) { + + var matchMedia = window.matchMedia || window.msMatchMedia; + if ( matchMedia ) { + return matchMedia(mq) && matchMedia(mq).matches || false; + } + + var bool; + + injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) { + bool = (window.getComputedStyle ? + getComputedStyle(node, null) : + node.currentStyle)['position'] == 'absolute'; + }); + + return bool; + + }, + /*>>mq*/ + + + /*>>hasevent*/ + // + // isEventSupported determines if a given element supports the given event + // kangax.github.com/iseventsupported/ + // + // The following results are known incorrects: + // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative + // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333 + // ... + isEventSupported = (function() { + + var TAGNAMES = { + 'select': 'input', 'change': 'input', + 'submit': 'form', 'reset': 'form', + 'error': 'img', 'load': 'img', 'abort': 'img' + }; + + function isEventSupported( eventName, element ) { + + element = element || document.createElement(TAGNAMES[eventName] || 'div'); + eventName = 'on' + eventName; + + // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those + var isSupported = eventName in element; + + if ( !isSupported ) { + // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element + if ( !element.setAttribute ) { + element = document.createElement('div'); + } + if ( element.setAttribute && element.removeAttribute ) { + element.setAttribute(eventName, ''); + isSupported = is(element[eventName], 'function'); + + // If property was created, "remove it" (by setting value to `undefined`) + if ( !is(element[eventName], 'undefined') ) { + element[eventName] = undefined; + } + element.removeAttribute(eventName); + } + } + + element = null; + return isSupported; + } + return isEventSupported; + })(), + /*>>hasevent*/ + + // TODO :: Add flag for hasownprop ? didn't last time + + // hasOwnProperty shim by kangax needed for Safari 2.0 support + _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp; + + if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) { + hasOwnProp = function (object, property) { + return _hasOwnProperty.call(object, property); + }; + } + else { + hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ + return ((property in object) && is(object.constructor.prototype[property], 'undefined')); + }; + } + + // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js + // es5.github.com/#x15.3.4.5 + + if (!Function.prototype.bind) { + Function.prototype.bind = function bind(that) { + + var target = this; + + if (typeof target != "function") { + throw new TypeError(); + } + + var args = slice.call(arguments, 1), + bound = function () { + + if (this instanceof bound) { + + var F = function(){}; + F.prototype = target.prototype; + var self = new F(); + + var result = target.apply( + self, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return self; + + } else { + + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + + } + + }; + + return bound; + }; + } + + /** + * setCss applies given styles to the Modernizr DOM node. + */ + function setCss( str ) { + mStyle.cssText = str; + } + + /** + * setCssAll extrapolates all vendor-specific css strings. + */ + function setCssAll( str1, str2 ) { + return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); + } + + /** + * is returns a boolean for if typeof obj is exactly type. + */ + function is( obj, type ) { + return typeof obj === type; + } + + /** + * contains returns a boolean for if substr is found within str. + */ + function contains( str, substr ) { + return !!~('' + str).indexOf(substr); + } + + /*>>testprop*/ + + // testProps is a generic CSS / DOM property test. + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + // Because the testing of the CSS property names (with "-", as + // opposed to the camelCase DOM properties) is non-portable and + // non-standard but works in WebKit and IE (but not Gecko or Opera), + // we explicitly reject properties with dashes so that authors + // developing in WebKit or IE first don't end up with + // browser-specific content by accident. + + function testProps( props, prefixed ) { + for ( var i in props ) { + var prop = props[i]; + if ( !contains(prop, "-") && mStyle[prop] !== undefined ) { + return prefixed == 'pfx' ? prop : true; + } + } + return false; + } + /*>>testprop*/ + + // TODO :: add testDOMProps + /** + * testDOMProps is a generic DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + */ + function testDOMProps( props, obj, elem ) { + for ( var i in props ) { + var item = obj[props[i]]; + if ( item !== undefined) { + + // return the property name as a string + if (elem === false) return props[i]; + + // let's bind a function + if (is(item, 'function')){ + // default to autobind unless override + return item.bind(elem || obj); + } + + // return the unbound function or obj or value + return item; + } + } + return false; + } + + /*>>testallprops*/ + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + */ + function testPropsAll( prop, prefixed, elem ) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); + + // did they call .prefixed('boxSizing') or are we just testing a prop? + if(is(prefixed, "string") || is(prefixed, "undefined")) { + return testProps(props, prefixed); + + // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) + } else { + props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); + return testDOMProps(props, prefixed, elem); + } + } + /*>>testallprops*/ + + + /** + * Tests + * ----- + */ + + // The *new* flexbox + // dev.w3.org/csswg/css3-flexbox + + tests['flexbox'] = function() { + return testPropsAll('flexWrap'); + }; + + // The *old* flexbox + // www.w3.org/TR/2009/WD-css3-flexbox-20090723/ + + tests['flexboxlegacy'] = function() { + return testPropsAll('boxDirection'); + }; + + // On the S60 and BB Storm, getContext exists, but always returns undefined + // so we actually have to call getContext() to verify + // github.com/Modernizr/Modernizr/issues/issue/97/ + + tests['canvas'] = function() { + var elem = document.createElement('canvas'); + return !!(elem.getContext && elem.getContext('2d')); + }; + + tests['canvastext'] = function() { + return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function')); + }; + + // webk.it/70117 is tracking a legit WebGL feature detect proposal + + // We do a soft detect which may false positive in order to avoid + // an expensive context creation: bugzil.la/732441 + + tests['webgl'] = function() { + return !!window.WebGLRenderingContext; + }; + + /* + * The Modernizr.touch test only indicates if the browser supports + * touch events, which does not necessarily reflect a touchscreen + * device, as evidenced by tablets running Windows 7 or, alas, + * the Palm Pre / WebOS (touch) phones. + * + * Additionally, Chrome (desktop) used to lie about its support on this, + * but that has since been rectified: crbug.com/36415 + * + * We also test for Firefox 4 Multitouch Support. + * + * For more info, see: modernizr.github.com/Modernizr/touch.html + */ + + tests['touch'] = function() { + var bool; + + if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { + bool = true; + } else { + injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) { + bool = node.offsetTop === 9; + }); + } + + return bool; + }; + + + // geolocation is often considered a trivial feature detect... + // Turns out, it's quite tricky to get right: + // + // Using !!navigator.geolocation does two things we don't want. It: + // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513 + // 2. Disables page caching in WebKit: webk.it/43956 + // + // Meanwhile, in Firefox < 8, an about:config setting could expose + // a false positive that would throw an exception: bugzil.la/688158 + + tests['geolocation'] = function() { + return 'geolocation' in navigator; + }; + + + tests['postmessage'] = function() { + return !!window.postMessage; + }; + + + // Chrome incognito mode used to throw an exception when using openDatabase + // It doesn't anymore. + tests['websqldatabase'] = function() { + return !!window.openDatabase; + }; + + // Vendors had inconsistent prefixing with the experimental Indexed DB: + // - Webkit's implementation is accessible through webkitIndexedDB + // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB + // For speed, we don't test the legacy (and beta-only) indexedDB + tests['indexedDB'] = function() { + return !!testPropsAll("indexedDB", window); + }; + + // documentMode logic from YUI to filter out IE8 Compat Mode + // which false positives. + tests['hashchange'] = function() { + return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7); + }; + + // Per 1.6: + // This used to be Modernizr.historymanagement but the longer + // name has been deprecated in favor of a shorter and property-matching one. + // The old API is still available in 1.6, but as of 2.0 will throw a warning, + // and in the first release thereafter disappear entirely. + tests['history'] = function() { + return !!(window.history && history.pushState); + }; + + tests['draganddrop'] = function() { + var div = document.createElement('div'); + return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); + }; + + // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10 + // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17. + // FF10 still uses prefixes, so check for it until then. + // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/ + tests['websockets'] = function() { + return 'WebSocket' in window || 'MozWebSocket' in window; + }; + + + // css-tricks.com/rgba-browser-support/ + tests['rgba'] = function() { + // Set an rgba() color and check the returned value + + setCss('background-color:rgba(150,255,150,.5)'); + + return contains(mStyle.backgroundColor, 'rgba'); + }; + + tests['hsla'] = function() { + // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally, + // except IE9 who retains it as hsla + + setCss('background-color:hsla(120,40%,100%,.5)'); + + return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla'); + }; + + tests['multiplebgs'] = function() { + // Setting multiple images AND a color on the background shorthand property + // and then querying the style.background property value for the number of + // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! + + setCss('background:url(https://),url(https://),red url(https://)'); + + // If the UA supports multiple backgrounds, there should be three occurrences + // of the string "url(" in the return value for elemStyle.background + + return (/(url\s*\(.*?){3}/).test(mStyle.background); + }; + + + + // this will false positive in Opera Mini + // github.com/Modernizr/Modernizr/issues/396 + + tests['backgroundsize'] = function() { + return testPropsAll('backgroundSize'); + }; + + tests['borderimage'] = function() { + return testPropsAll('borderImage'); + }; + + + // Super comprehensive table about all the unique implementations of + // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance + + tests['borderradius'] = function() { + return testPropsAll('borderRadius'); + }; + + // WebOS unfortunately false positives on this test. + tests['boxshadow'] = function() { + return testPropsAll('boxShadow'); + }; + + // FF3.0 will false positive on this test + tests['textshadow'] = function() { + return document.createElement('div').style.textShadow === ''; + }; + + + tests['opacity'] = function() { + // Browsers that actually have CSS Opacity implemented have done so + // according to spec, which means their return values are within the + // range of [0.0,1.0] - including the leading zero. + + setCssAll('opacity:.55'); + + // The non-literal . in this regex is intentional: + // German Chrome returns this value as 0,55 + // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 + return (/^0.55$/).test(mStyle.opacity); + }; + + + // Note, Android < 4 will pass this test, but can only animate + // a single property at a time + // goo.gl/v3V4Gp + tests['cssanimations'] = function() { + return testPropsAll('animationName'); + }; + + + tests['csscolumns'] = function() { + return testPropsAll('columnCount'); + }; + + + tests['cssgradients'] = function() { + /** + * For CSS Gradients syntax, please see: + * webkit.org/blog/175/introducing-css-gradients/ + * developer.mozilla.org/en/CSS/-moz-linear-gradient + * developer.mozilla.org/en/CSS/-moz-radial-gradient + * dev.w3.org/csswg/css3-images/#gradients- + */ + + var str1 = 'background-image:', + str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', + str3 = 'linear-gradient(left top,#9f9, white);'; + + setCss( + // legacy webkit syntax (FIXME: remove when syntax not in use anymore) + (str1 + '-webkit- '.split(' ').join(str2 + str1) + + // standard syntax // trailing 'background-image:' + prefixes.join(str3 + str1)).slice(0, -str1.length) + ); + + return contains(mStyle.backgroundImage, 'gradient'); + }; + + + tests['cssreflections'] = function() { + return testPropsAll('boxReflect'); + }; + + + tests['csstransforms'] = function() { + return !!testPropsAll('transform'); + }; + + + tests['csstransforms3d'] = function() { + + var ret = !!testPropsAll('perspective'); + + // Webkit's 3D transforms are passed off to the browser's own graphics renderer. + // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in + // some conditions. As a result, Webkit typically recognizes the syntax but + // will sometimes throw a false positive, thus we must do a more thorough check: + if ( ret && 'webkitPerspective' in docElement.style ) { + + // Webkit allows this media query to succeed only if the feature is enabled. + // `@media (transform-3d),(-webkit-transform-3d){ ... }` + injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { + ret = node.offsetLeft === 9 && node.offsetHeight === 3; + }); + } + return ret; + }; + + + tests['csstransitions'] = function() { + return testPropsAll('transition'); + }; + + + /*>>fontface*/ + // @font-face detection routine by Diego Perini + // javascript.nwbox.com/CSSSupport/ + + // false positives: + // WebOS github.com/Modernizr/Modernizr/issues/342 + // WP7 github.com/Modernizr/Modernizr/issues/538 + tests['fontface'] = function() { + var bool; + + injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) { + var style = document.getElementById('smodernizr'), + sheet = style.sheet || style.styleSheet, + cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; + + bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; + }); + + return bool; + }; + /*>>fontface*/ + + // CSS generated content detection + tests['generatedcontent'] = function() { + var bool; + + injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) { + bool = node.offsetHeight >= 3; + }); + + return bool; + }; + + + + // These tests evaluate support of the video/audio elements, as well as + // testing what types of content they support. + // + // We're using the Boolean constructor here, so that we can extend the value + // e.g. Modernizr.video // true + // Modernizr.video.ogg // 'probably' + // + // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 + // thx to NielsLeenheer and zcorpan + + // Note: in some older browsers, "no" was a return value instead of empty string. + // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 + // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 + + tests['video'] = function() { + var elem = document.createElement('video'), + bool = false; + + // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,''); + + // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 + bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,''); + + bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,''); + } + + } catch(e) { } + + return bool; + }; + + tests['audio'] = function() { + var elem = document.createElement('audio'), + bool = false; + + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,''); + bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,''); + + // Mimetypes accepted: + // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements + // bit.ly/iphoneoscodecs + bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,''); + bool.m4a = ( elem.canPlayType('audio/x-m4a;') || + elem.canPlayType('audio/aac;')) .replace(/^no$/,''); + } + } catch(e) { } + + return bool; + }; + + + // In FF4, if disabled, window.localStorage should === null. + + // Normally, we could not test that directly and need to do a + // `('localStorage' in window) && ` test first because otherwise Firefox will + // throw bugzil.la/365772 if cookies are disabled + + // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem + // will throw the exception: + // QUOTA_EXCEEDED_ERRROR DOM Exception 22. + // Peculiarly, getItem and removeItem calls do not throw. + + // Because we are forced to try/catch this, we'll go aggressive. + + // Just FWIW: IE8 Compat mode supports these features completely: + // www.quirksmode.org/dom/html5.html + // But IE8 doesn't support either with local files + + tests['localstorage'] = function() { + try { + localStorage.setItem(mod, mod); + localStorage.removeItem(mod); + return true; + } catch(e) { + return false; + } + }; + + tests['sessionstorage'] = function() { + try { + sessionStorage.setItem(mod, mod); + sessionStorage.removeItem(mod); + return true; + } catch(e) { + return false; + } + }; + + + tests['webworkers'] = function() { + return !!window.Worker; + }; + + + tests['applicationcache'] = function() { + return !!window.applicationCache; + }; + + + // Thanks to Erik Dahlstrom + tests['svg'] = function() { + return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect; + }; + + // specifically for SVG inline in HTML, not within XHTML + // test page: paulirish.com/demo/inline-svg + tests['inlinesvg'] = function() { + var div = document.createElement('div'); + div.innerHTML = ''; + return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; + }; + + // SVG SMIL animation + tests['smil'] = function() { + return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate'))); + }; + + // This test is only for clip paths in SVG proper, not clip paths on HTML content + // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg + + // However read the comments to dig into applying SVG clippaths to HTML content here: + // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491 + tests['svgclippaths'] = function() { + return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath'))); + }; + + /*>>webforms*/ + // input features and input types go directly onto the ret object, bypassing the tests loop. + // Hold this guy to execute in a moment. + function webforms() { + /*>>input*/ + // Run through HTML5's new input attributes to see if the UA understands any. + // We're using f which is the element created early on + // Mike Taylr has created a comprehensive resource for testing these attributes + // when applied to all input types: + // miketaylr.com/code/input-type-attr.html + // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary + + // Only input placeholder is tested while textarea's placeholder is not. + // Currently Safari 4 and Opera 11 have support only for the input placeholder + // Both tests are available in feature-detects/forms-placeholder.js + Modernizr['input'] = (function( props ) { + for ( var i = 0, len = props.length; i < len; i++ ) { + attrs[ props[i] ] = !!(props[i] in inputElem); + } + if (attrs.list){ + // safari false positive's on datalist: webk.it/74252 + // see also github.com/Modernizr/Modernizr/issues/146 + attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement); + } + return attrs; + })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); + /*>>input*/ + + /*>>inputtypes*/ + // Run through HTML5's new input types to see if the UA understands any. + // This is put behind the tests runloop because it doesn't return a + // true/false like all the other tests; instead, it returns an object + // containing each input type with its corresponding true/false value + + // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/ + Modernizr['inputtypes'] = (function(props) { + + for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) { + + inputElem.setAttribute('type', inputElemType = props[i]); + bool = inputElem.type !== 'text'; + + // We first check to see if the type we give it sticks.. + // If the type does, we feed it a textual value, which shouldn't be valid. + // If the value doesn't stick, we know there's input sanitization which infers a custom UI + if ( bool ) { + + inputElem.value = smile; + inputElem.style.cssText = 'position:absolute;visibility:hidden;'; + + if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) { + + docElement.appendChild(inputElem); + defaultView = document.defaultView; + + // Safari 2-4 allows the smiley as a value, despite making a slider + bool = defaultView.getComputedStyle && + defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && + // Mobile android web browser has false positive, so must + // check the height to see if the widget is actually there. + (inputElem.offsetHeight !== 0); + + docElement.removeChild(inputElem); + + } else if ( /^(search|tel)$/.test(inputElemType) ){ + // Spec doesn't define any special parsing or detectable UI + // behaviors so we pass these through as true + + // Interestingly, opera fails the earlier test, so it doesn't + // even make it here. + + } else if ( /^(url|email)$/.test(inputElemType) ) { + // Real url and email support comes with prebaked validation. + bool = inputElem.checkValidity && inputElem.checkValidity() === false; + + } else { + // If the upgraded input compontent rejects the :) text, we got a winner + bool = inputElem.value != smile; + } + } + + inputs[ props[i] ] = !!bool; + } + return inputs; + })('search tel url email datetime date month week time datetime-local number range color'.split(' ')); + /*>>inputtypes*/ + } + /*>>webforms*/ + + + // End of test definitions + // ----------------------- + + + + // Run through all tests and detect their support in the current UA. + // todo: hypothetically we could be doing an array of tests and use a basic loop here. + for ( var feature in tests ) { + if ( hasOwnProp(tests, feature) ) { + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + featureName = feature.toLowerCase(); + Modernizr[featureName] = tests[feature](); + + classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); + } + } + + /*>>webforms*/ + // input tests need to run. + Modernizr.input || webforms(); + /*>>webforms*/ + + + /** + * addTest allows the user to define their own feature tests + * the result will be added onto the Modernizr object, + * as well as an appropriate className set on the html element + * + * @param feature - String naming the feature + * @param test - Function returning true if feature is supported, false if not + */ + Modernizr.addTest = function ( feature, test ) { + if ( typeof feature == 'object' ) { + for ( var key in feature ) { + if ( hasOwnProp( feature, key ) ) { + Modernizr.addTest( key, feature[ key ] ); + } + } + } else { + + feature = feature.toLowerCase(); + + if ( Modernizr[feature] !== undefined ) { + // we're going to quit if you're trying to overwrite an existing test + // if we were to allow it, we'd do this: + // var re = new RegExp("\\b(no-)?" + feature + "\\b"); + // docElement.className = docElement.className.replace( re, '' ); + // but, no rly, stuff 'em. + return Modernizr; + } + + test = typeof test == 'function' ? test() : test; + + if (typeof enableClasses !== "undefined" && enableClasses) { + docElement.className += ' ' + (test ? '' : 'no-') + feature; + } + Modernizr[feature] = test; + + } + + return Modernizr; // allow chaining. + }; + + + // Reset modElem.cssText to nothing to reduce memory footprint. + setCss(''); + modElem = inputElem = null; + + /*>>shiv*/ + /** + * @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed + */ + ;(function(window, document) { + /*jshint evil:true */ + /** version */ + var version = '3.7.0'; + + /** Preset options */ + var options = window.html5 || {}; + + /** Used to skip problem elements */ + var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; + + /** Not all elements can be cloned in IE **/ + var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; + + /** Detect whether the browser supports default html5 styles */ + var supportsHtml5Styles; + + /** Name of the expando, to work with multiple documents or to re-shiv one document */ + var expando = '_html5shiv'; + + /** The id for the the documents expando */ + var expanID = 0; + + /** Cached data for each document */ + var expandoData = {}; + + /** Detect whether the browser supports unknown elements */ + var supportsUnknownElements; + + (function() { + try { + var a = document.createElement('a'); + a.innerHTML = ''; + //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles + supportsHtml5Styles = ('hidden' in a); + + supportsUnknownElements = a.childNodes.length == 1 || (function() { + // assign a false positive if unable to shiv + (document.createElement)('a'); + var frag = document.createDocumentFragment(); + return ( + typeof frag.cloneNode == 'undefined' || + typeof frag.createDocumentFragment == 'undefined' || + typeof frag.createElement == 'undefined' + ); + }()); + } catch(e) { + // assign a false positive if detection fails => unable to shiv + supportsHtml5Styles = true; + supportsUnknownElements = true; + } + + }()); + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a style sheet with the given CSS text and adds it to the document. + * @private + * @param {Document} ownerDocument The document. + * @param {String} cssText The CSS text. + * @returns {StyleSheet} The style element. + */ + function addStyleSheet(ownerDocument, cssText) { + var p = ownerDocument.createElement('p'), + parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; + + p.innerHTML = 'x'; + return parent.insertBefore(p.lastChild, parent.firstChild); + } + + /** + * Returns the value of `html5.elements` as an array. + * @private + * @returns {Array} An array of shived element node names. + */ + function getElements() { + var elements = html5.elements; + return typeof elements == 'string' ? elements.split(' ') : elements; + } + + /** + * Returns the data associated to the given document + * @private + * @param {Document} ownerDocument The document. + * @returns {Object} An object of data. + */ + function getExpandoData(ownerDocument) { + var data = expandoData[ownerDocument[expando]]; + if (!data) { + data = {}; + expanID++; + ownerDocument[expando] = expanID; + expandoData[expanID] = data; + } + return data; + } + + /** + * returns a shived element for the given nodeName and document + * @memberOf html5 + * @param {String} nodeName name of the element + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived element. + */ + function createElement(nodeName, ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createElement(nodeName); + } + if (!data) { + data = getExpandoData(ownerDocument); + } + var node; + + if (data.cache[nodeName]) { + node = data.cache[nodeName].cloneNode(); + } else if (saveClones.test(nodeName)) { + node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); + } else { + node = data.createElem(nodeName); + } + + // Avoid adding some elements to fragments in IE < 9 because + // * Attributes like `name` or `type` cannot be set/changed once an element + // is inserted into a document/fragment + // * Link elements with `src` attributes that are inaccessible, as with + // a 403 response, will cause the tab/window to crash + // * Script elements appended to fragments will execute when their `src` + // or `text` property is set + return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; + } + + /** + * returns a shived DocumentFragment for the given document + * @memberOf html5 + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived DocumentFragment. + */ + function createDocumentFragment(ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createDocumentFragment(); + } + data = data || getExpandoData(ownerDocument); + var clone = data.frag.cloneNode(), + i = 0, + elems = getElements(), + l = elems.length; + for(;i>shiv*/ + + // Assign private properties to the return object with prefix + Modernizr._version = version; + + // expose these for the plugin API. Look in the source for how to join() them against your input + /*>>prefixes*/ + Modernizr._prefixes = prefixes; + /*>>prefixes*/ + /*>>domprefixes*/ + Modernizr._domPrefixes = domPrefixes; + Modernizr._cssomPrefixes = cssomPrefixes; + /*>>domprefixes*/ + + /*>>mq*/ + // Modernizr.mq tests a given media query, live against the current state of the window + // A few important notes: + // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false + // * A max-width or orientation query will be evaluated against the current state, which may change later. + // * You must specify values. Eg. If you are testing support for the min-width media query use: + // Modernizr.mq('(min-width:0)') + // usage: + // Modernizr.mq('only screen and (max-width:768)') + Modernizr.mq = testMediaQuery; + /*>>mq*/ + + /*>>hasevent*/ + // Modernizr.hasEvent() detects support for a given event, with an optional element to test on + // Modernizr.hasEvent('gesturestart', elem) + Modernizr.hasEvent = isEventSupported; + /*>>hasevent*/ + + /*>>testprop*/ + // Modernizr.testProp() investigates whether a given style property is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testProp('pointerEvents') + Modernizr.testProp = function(prop){ + return testProps([prop]); + }; + /*>>testprop*/ + + /*>>testallprops*/ + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + Modernizr.testAllProps = testPropsAll; + /*>>testallprops*/ + + + /*>>teststyles*/ + // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards + // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... }) + Modernizr.testStyles = injectElementWithStyles; + /*>>teststyles*/ + + + /*>>prefixed*/ + // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input + // Modernizr.prefixed('boxSizing') // 'MozBoxSizing' + + // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style. + // Return values will also be the camelCase variant, if you need to translate that to hypenated style use: + // + // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-'); + + // If you're trying to ascertain which transition end event to bind to, you might do something like... + // + // var transEndEventNames = { + // 'WebkitTransition' : 'webkitTransitionEnd', + // 'MozTransition' : 'transitionend', + // 'OTransition' : 'oTransitionEnd', + // 'msTransition' : 'MSTransitionEnd', + // 'transition' : 'transitionend' + // }, + // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; + + Modernizr.prefixed = function(prop, obj, elem){ + if(!obj) { + return testPropsAll(prop, 'pfx'); + } else { + // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' + return testPropsAll(prop, obj, elem); + } + }; + /*>>prefixed*/ + + + /*>>cssclasses*/ + // Remove "no-js" class from element, if it exists: + docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') + + + // Add the new classes to the element. + (enableClasses ? ' js ' + classes.join(' ') : ''); + /*>>cssclasses*/ + + return Modernizr; + +})(this, this.document); +;/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } + +/* ======================================================================== + * Bootstrap: transition.js v3.2.0 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.2.0 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.2.0' + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.2.0 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.2.0' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + $el[val](data[state] == null ? this.options[state] : data[state]) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.2.0 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.2.0' + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true + } + + Carousel.prototype.keydown = function (e) { + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.2.0 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.2.0' + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + Plugin.call(actives, 'hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') option = !option + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var href + var $this = $(this) + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.2.0 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.2.0' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $(' +
+ + +
+ {!! Form::model($event, array('url' => route('postEditEventFees', ['event_id' => $event->id]), 'class' => 'ajax')) !!} +

+ Organiser Fees +

+ +
+ These are optional fees you can include in the cost of each ticket. This charge will appear on buyer's invoices as 'BOOKING FEES'. +
+ +
+ {!! Form::label('organiser_fee_percentage', 'Service Fee Percentage', array('class'=>'control-label required')) !!} + {!! Form::text('organiser_fee_percentage', $event->organiser_fee_percentage, [ + 'class' => 'form-control', + 'placeholder' => '0' + ]) !!} +
+ e.g: enter 3.5 for 3.5% +
+
+
+ {!! Form::label('organiser_fee_fixed', 'Service Fee Fixed Price', array('class'=>'control-label required')) !!} + {!! Form::text('organiser_fee_fixed', null, [ + 'class' => 'form-control', + 'placeholder' => '0.00' + ]) !!} +
+ e.g: enter 1.25 for {{$event->currency_symbol}}1.25 +
+
+ + {!!Form::close()!!} +
+
+

+ Social Settings +

+ +
+
+ {!! Form::label('event_page_show_map', 'Show map on event page?', array('id' => 'customcheckbox', 'class'=>'control-label')) !!} + {!! Form::checkbox('event_page_show_map', 1, false) !!} +
+
+
+ {!! Form::label('event_page_show_social_share', 'Show social share buttons?', array('class'=>'control-label')) !!} + {!! Form::checkbox('event_page_show_social_share', 1, false) !!} +
+ +
+ +
+ {!! Form::model($event, array('url' => route('postEditEventOrderPage', ['event_id' => $event->id]), 'class' => 'ajax ')) !!} +

+ Order Page Settings +

+ +
+
+ +
+
+ If checked, the buyer will be asked for details of each attendee; as opposed to just + himself. +
+
+ + +
+ {!! Form::label('pre_order_display_message', 'Message to display to attendees before they complete their order.', array('class'=>'control-label ')) !!} + + {!! Form::textarea('pre_order_display_message', $event->pre_order_display_message, [ + 'class' => 'form-control', + 'rows' => 4 + ]) !!} +
+ This message will be displayed to attendees immediately before they finalize their order. +
+ +
+
+ {!! Form::label('post_order_display_message', 'Message to display to attendees before after they have completed their order.', array('class'=>'control-label ')) !!} + + {!! Form::textarea('post_order_display_message', $event->post_order_display_message, [ + 'class' => 'form-control', + 'rows' => 4 + ]) !!} +
+ This message will be displayed to attendees once they have successfully completed the + checkout process. +
+
+ + + + {!!Form::close()!!} + +
+ +
+ +
+
+

+ HTML Embed Code +

+ +
+
+

+ Instructions +

+ +

+ Simply copy and paste the HTML provided onto your website where you would like the + widget to appear and the widget will appear. +

+ +

+ Feel free to contact + us if you require any further information regarding using the HTML embed code. +

+ +
+ Embed Preview +
+ +
+ {!! $event->embed_html_code !!} +
+ +
+
+ + +
+ + + + + +@stop + + diff --git a/resources/views/ManageEvent/Dashboard.blade.php b/resources/views/ManageEvent/Dashboard.blade.php new file mode 100644 index 00000000..09adc5fe --- /dev/null +++ b/resources/views/ManageEvent/Dashboard.blade.php @@ -0,0 +1,476 @@ +@extends('Shared.Layouts.Master') + +@section('title') + @parent + Dashboard +@stop + + +@section('top_nav') + @include('ManageEvent.Partials.TopNav') +@stop + +@section('page_title') + + Event Dashboard +@stop + +@section('page_header') + + @stop + + @section('menu') + @include('ManageEvent.Partials.Sidebar') + @stop + + @section('head') + + + + + + + + + + + + + @stop + + @section('content') + + + + +
+
+ +
+
+
+

+
+ {{money($event->sales_volume + $event->organiser_fees_volume, $event->currency->code)}} +
+ +

+ + Sales Volume + +
+
+
+
+

+ {{$event->orders->count()}} +

+ + Orders + +
+
+
+
+

+ {{$event->tickets->sum('quantity_sold')}} +

+ + Tickets Sold + +
+
+ + +
+
+

+ {{$event->stats->sum('views')}} +

+ + Event Views + +
+
+ +
+
+

+ 0 +

+ + Facebook Shares + +
+
+ + +
+ + +
+
+
+
+

+ Tickets Sold + + {{$event->tickets->sum('quantity_sold')}} Total + +

+
+
+
+
+
+
+
+ +
+
+
+
+

+ Event Page Visits + + {{$event->stats->sum('views')}} Total + +

+
+
+
+
+
+
+
+
+
+
+
+

+ Ticket Sales Volume + + {{money($event->sales_volume + $event->organiser_fees_volume, $event->currency->code)}} + Total + +

+
+
+
+
+
+
+
+
+
+ +
+
+
+ + +
+ + @if($event->happening_now) + This event is on now + @else + + @endif +
+ +
+
+ +
+

+ + Event URL +

+
+ +
+ {!!Form::input('text', '', $event->event_url, ['class' => 'form-control', 'onclick' => 'this.select();'])!!} +
+ +
+ + + + +
+
+
+ + +
+ + +@stop diff --git a/resources/views/ManageEvent/Emails/MessageAttendees.php b/resources/views/ManageEvent/Emails/MessageAttendees.php new file mode 100644 index 00000000..d68dcf86 --- /dev/null +++ b/resources/views/ManageEvent/Emails/MessageAttendees.php @@ -0,0 +1,3 @@ +Hello {{$attendess->first_name}}, + +You've received a message from $event->organo \ No newline at end of file diff --git a/resources/views/ManageEvent/Modals/CancelAttendee.blade.php b/resources/views/ManageEvent/Modals/CancelAttendee.blade.php new file mode 100644 index 00000000..6085b299 --- /dev/null +++ b/resources/views/ManageEvent/Modals/CancelAttendee.blade.php @@ -0,0 +1,40 @@ +