diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php new file mode 100644 index 0000000..d975afc --- /dev/null +++ b/app/Console/Commands/Import.php @@ -0,0 +1,221 @@ +option('file')) AND is_null($this->option('dir'))) + abort(500,'Missing filename, please use --file= OR --dir='); + + Log::info('Processing: '.($this->option('file') ? $this->option('file') : $this->option('dir'))); + + $files = []; + + if ($this->option('dir')) + { + // Remove our trailing slash from the directory. + $dir = preg_replace('/\/$/','',$this->option('dir')); + + // Exclude . & .. from the path. + $files = array_diff(scandir($dir),array('.','..')); + + // Determine if our dir is releative to where we store data + $dir = preg_replace('/^\//','',str_replace(config('photo.dir'),'',$dir)); + + // Add our path + if ($dir) + array_walk($files,function(&$value,$key,$path='') { + if ($path) { + $value = sprintf('%s/%s',$path,$value); + } + },$dir); + } + elseif ($this->option('file')) + { + $files = array($this->option('file')); + } + + // Show a progress bar + $bar = $this->output->createProgressBar(count($files)); + $bar->setFormat("%current%/%max% [%bar%] %percent:3s%% (%memory%) (%remaining%) "); + + $tags = NULL; + $t = $p = array(); + + // Tags + if ($this->option('tags')) + { + $tags = explode(',',$this->option('tags')); + $t = Tag::whereIn('tag',$tags)->get(); + } + + // People + if ($this->option('people')) + { + $tags = explode(',',$this->option('people')); + $p = Person::whereIn('tag',$tags)->get(); + } + + $c = 0; + foreach ($files as $file) + { + $bar->advance(); + + if (preg_match('/@__thumb/',$file) OR preg_match('/\/._/',$file)) + { + $this->warn(sprintf('Ignoring file [%s]',$file)); + continue; + } + + if (! in_array(strtolower(pathinfo($file,PATHINFO_EXTENSION)),config('photo.import.accepted'))) + { + $this->warn(sprintf('Ignoring [%s]',$file)); + continue; + } + + if ($this->option('verbose')) + $this->info(sprintf('Processing file [%s]',$file)); + + $c++; + + $po = Photo::where('filename',$file)->first(); + + if (is_null($po)) + { + $po = new Photo; + $po->filename = $file; + } + + $po->date_taken = strtotime($po->io('exif:DateTime')); + $po->subsectime = $po->io('exif:SubSecTimeOriginal'); + + $po->signature = $po->io()->getImageSignature(); + + $po->make = $po->io('exif:Make'); + $po->model = $po->io('exif:Model'); + + $po->height = $po->io()->getImageheight(); + $po->width = $po->io()->getImageWidth(); + $po->orientation = $po->io()->getImageOrientation(); + + $po->gps_lat = $po->gps(preg_split('/,\s?/',$po->io()->getImageProperty('exif:GPSLatitude')),$po->io()->getImageProperty('exif:GPSLatitudeRef')); + $po->gps_lon = $po->gps(preg_split('/,\s?/',$po->io()->getImageProperty('exif:GPSLongitude')),$po->io()->getImageProperty('exif:GPSLongitudeRef')); + + try { + $po->thumbnail = exif_thumbnail($po->file_path()); + } catch (Exception $e) { + // @todo Couldnt get the thumbnail, so we should create one. + } + + // If this is a duplicate + $x = $po->list_duplicate()->get(); + if (count($x)) { + $skip = FALSE; + + foreach ($x as $o) { + // We'll only ignore based on the same signature. + if ($po->signature != $o->signature AND ! $po->exists) + continue; + + if ($this->option('ignoredupe')) + { + $skip = TRUE; + $this->warn(sprintf("Ignoring file [%s], it's the same as [%s] with id %s",$po->file_path(),$o->filename,$o->id)); + break; + + } + elseif ($this->option('deletedupe') AND ($po->filename != $o->filename)) + { + $skip = TRUE; + $this->error(sprintf("Deleting file [%s], it's the same as [%s] with id %s and signature [%s]\n",$po->file_path(),$o->filename,$o->id,$po->signature)); + unlink($po->file_path()); + } + } + + if ($skip) + continue; + + $po->duplicate = '1'; + $this->warn(sprintf('Image [%s] marked as a duplicate',$file)); + } + + if ($po->exists) + $this->warn(sprintf('Image [%s] already in DB: %s',$file,$po->id)); + + $po->save(); + + if ($po->wasRecentlyCreated) + $this->info(sprintf('Image [%s] stored in DB: %s',$file,$po->id)); + + // Record our tags + foreach ($t as $o) + if (! (new PhotoTag)->where('tag_id','=',$o->id)->where('photo_id','=',$po->id)->count()) + { + $x = new PhotoTag; + $x->tag_id = $o->id; + $x->photo_id = $po->id; + $x->save(); + } + + // Record our people + foreach ($p as $o) + if (! (new PhotoPerson)->where('people_id','=',$o->id)->where('photo_id','=',$po->id)->count()) + { + $x = new PhotoPerson; + $x->people_id = $o->id; + $x->photo_id = $po->id; + $x->save(); + } + } + + $bar->finish(); + + return $this->info(sprintf('Images processed: %s',$c)); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 71c519d..6ad3780 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -13,7 +13,7 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ - // Commands\Inspire::class, + Commands\Import::class, ]; /** diff --git a/app/Http/Controllers/PhotoController.php b/app/Http/Controllers/PhotoController.php new file mode 100644 index 0000000..687a5e2 --- /dev/null +++ b/app/Http/Controllers/PhotoController.php @@ -0,0 +1,31 @@ +middleware('guest'); + } + + public function info($id) + { + return view('photo.view', ['photo'=> Photo::findOrFail($id)]); + } + + public function thumbnail($id) + { + return (new Response(Photo::findOrFail($id)->thumbnail()))->header('Content-Type','image/jpg'); + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 1ad3549..8fa3ec9 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,5 +1,7 @@ where('id', '[0-9]+');; +Route::get('/thumbnail/{id}', 'PhotoController@thumbnail')->where('id', '[0-9]+');; diff --git a/app/Model/Person.php b/app/Model/Person.php new file mode 100644 index 0000000..1dc0167 --- /dev/null +++ b/app/Model/Person.php @@ -0,0 +1,9 @@ +180, + 6=>90, + 8=>-90, + ]; + + /** + * Determine the new name for the image + */ + public function file_path($short=FALSE,$new=FALSE) { + $file = $this->filename; + + if ($new) + $file = sprintf('%s.%s',((is_null($this->date_taken) OR ! $this->date_taken) + ? sprintf('UNKNOWN/%07s',$this->file_path_id()) + : sprintf('%s_%03s',date('Y/m/d-His',$this->date_taken),$this->subsectime).($this->subsectime ? '' : sprintf('-%05s',$this->id))),$this->type()); + + return (($short OR preg_match('/^\//',$file)) ? '' : config('photo.dir').DIRECTORY_SEPARATOR).$file; + } + + public function gps(array $coordinate,$hemisphere) { + if (! $coordinate OR ! $hemisphere) + return NULL; + + for ($i=0; $i<3; $i++) { + $part = explode('/', $coordinate[$i]); + + if (count($part) == 1) + $coordinate[$i] = $part[0]; + + elseif (count($part) == 2) + $coordinate[$i] = floatval($part[0])/floatval($part[1]); + + else + $coordinate[$i] = 0; + } + + list($degrees, $minutes, $seconds) = $coordinate; + + $sign = ($hemisphere == 'W' || $hemisphere == 'S') ? -1 : 1; + + return round($sign*($degrees+$minutes/60+$seconds/3600),$degrees > 100 ? 3 : 4); + } + + /** + * Return an Imagick object or attribute + * + */ + public function io($attr=NULL) { + if (is_null($this->_io)) + $this->_io = new \Imagick($this->file_path()); + + return is_null($attr) ? $this->_io : $this->_io->getImageProperty($attr); + } + + /** + * Rotate the image + * + */ + private function rotate(\Imagick $imo) + { + if (array_key_exists($this->orientation,$this->_rotate)) + $imo->rotateImage(new \ImagickPixel('none'),$this->_rotate[$this->orientation]); + + return $imo->getImageBlob(); + } + + /** + * Return the image's thumbnail + * + */ + public function thumbnail($rotate=TRUE) + { + if (! $this->thumbnail) + return NULL; + + if (! $rotate OR ! array_key_exists($this->orientation,$this->_rotate) OR ! extension_loaded('imagick')) + return $this->thumbnail; + + $imo = new \Imagick(); + $imo->readImageBlob($this->thumbnail); + + return $this->rotate($imo); + } + + public function list_duplicate() { + $po = DB::table('photo'); + + if ($this->id) + $po->where('id','!=',$this->id); + + // Ignore photo's pending removal. + $po->where(function($query) { + $query->where('remove','!=',TRUE) + ->orWhere('remove','=',NULL); + }); + + // Where the signature is the same + $po->where(function($query) { + $query->where('signature','=',$this->signature); + + // Or they have the same time taken with the same camera + if ($this->date_taken AND ($this->model OR $this->make)) { + $query->orWhere(function($query) { + $query->where('date_taken','=',$this->date_taken ? $this->date_taken : NULL); + $query->where('subsectime','=',$this->subsectime ? $this->subsectime : NULL); + + if (! is_null($this->model)) + $query->where('model','=',$this->model); + + if (! is_null($this->make)) + $query->where('make','=',$this->make); + + }); + } + }); + + return $po; + } +} diff --git a/app/Model/PhotoPerson.php b/app/Model/PhotoPerson.php new file mode 100644 index 0000000..a84f6bb --- /dev/null +++ b/app/Model/PhotoPerson.php @@ -0,0 +1,9 @@ + diff --git a/application/classes/Model/Photo.php b/application/classes/Model/Photo.php index 10ebca5..2aa8b61 100644 --- a/application/classes/Model/Photo.php +++ b/application/classes/Model/Photo.php @@ -185,6 +185,8 @@ class Model_Photo extends ORM { return join('|',array_keys($result)); } + +/* private function rotate(Imagick $imo) { switch ($this->orientation) { case 3: $imo->rotateImage(new ImagickPixel('none'),180); @@ -195,6 +197,7 @@ class Model_Photo extends ORM { break; } } +*/ public function rotation() { switch ($this->orientation) { @@ -211,6 +214,7 @@ class Model_Photo extends ORM { return sprintf('%s...%s',substr($signature,0,$chars),substr($signature,-1*$chars)); } +/* public function thumbnail($rotate=TRUE) { if (! $this->thumbnail) return NULL; @@ -224,6 +228,7 @@ class Model_Photo extends ORM { return $imo->getImageBlob(); } +*/ public function thumbnail_sig() { return md5($this->thumbnail()).':'.strlen($this->thumbnail()); diff --git a/application/classes/Model/Photo/People.php b/application/classes/Model/Photo/People.php deleted file mode 100644 index 273da9c..0000000 --- a/application/classes/Model/Photo/People.php +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/application/classes/Model/Photo/Tag.php b/application/classes/Model/Photo/Tag.php deleted file mode 100644 index 4e5b5d6..0000000 --- a/application/classes/Model/Photo/Tag.php +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/application/classes/Model/Tags.php b/application/classes/Model/Tags.php deleted file mode 100644 index 4f109f3..0000000 --- a/application/classes/Model/Tags.php +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/application/classes/Task/Photo/Import.php b/application/classes/Task/Photo/Import.php deleted file mode 100644 index 4c07036..0000000 --- a/application/classes/Task/Photo/Import.php +++ /dev/null @@ -1,186 +0,0 @@ -NULL, // Directory of Photos to Import - 'file'=>NULL, // Photo File to Import - 'tags'=>NULL, - 'people'=>NULL, - 'ignoredupe'=>FALSE, // Skip duplicate photos - 'deletedupe'=>FALSE, // Skip duplicate photos - 'verbose'=>FALSE, // Photo File to Import - ); - - private function _adddir(&$value,$key,$path='') { - if ($path) - $value = sprintf('%s/%s',$path,$value); - } - - protected function _execute(array $params) { - $tags = NULL; - $t = $p = array(); - - if (is_null($params['file']) AND is_null($params['dir']) OR ($params['file'] AND $params['dir'])) - throw new Kohana_Exception('Missing filename, please use --file= OR --dir='); - - if ($params['dir']) { - $files = array_diff(scandir($params['dir']),array('.','..')); - - array_walk($files,'static::_adddir',$params['dir']); - - } else - $files = array($params['file']); - - // Tags - if ($params['tags']) { - $tags = explode(',',$params['tags']); - $t = ORM::factory('Tags')->where('tag','IN',$tags)->find_all(); - } - - // People - if ($params['people']) { - $tags = explode(',',$params['people']); - $p = ORM::factory('People')->where('tag','IN',$tags)->find_all(); - } - - $c = 0; - foreach ($files as $file) { - if ($params['verbose']) - printf("Processing file [%s]\n",$file); - - if (preg_match('/@__thumb/',$file) OR preg_match('/\/._/',$file)) { - $this->writelog(sprintf("Ignoring file [%s]\n",$file)); - continue; - } - - if (! in_array(strtolower(pathinfo($file,PATHINFO_EXTENSION)),$this->_accepted)) { - $this->writelog(sprintf("Ignoring file [%s]\n",$file)); - continue; - } - - $c++; - - $po = ORM::factory('Photo',array('filename'=>$file)); - - if (! $po->loaded()) - $po->filename = realpath($file); - - $po->date_taken = $this->dbcol(strtotime($po->io('exif:DateTime'))); - $po->signature = $this->dbcol($po->io()->getImageSignature()); - $po->make = $this->dbcol($po->io('exif:Make')); - $po->model = $this->dbcol($po->io('exif:Model')); - $po->height = $this->dbcol($po->io()->getImageheight()); - $po->width = $this->dbcol($po->io()->getImageWidth()); - $po->orientation = $this->dbcol($po->io()->getImageOrientation()); - $po->subsectime = $this->dbcol($po->io('exif:SubSecTimeOriginal')); - - $po->gps_lat = $this->dbcol($po->gps(preg_split('/,\s?/',$po->io()->getImageProperty('exif:GPSLatitude')),$po->io()->getImageProperty('exif:GPSLatitudeRef'))); - $po->gps_lon = $this->dbcol($po->gps(preg_split('/,\s?/',$po->io()->getImageProperty('exif:GPSLongitude')),$po->io()->getImageProperty('exif:GPSLongitudeRef'))); - - try { - $po->thumbnail = $this->dbcol(exif_thumbnail($po->filename)); - } catch (Exception $e) { - } - - switch ($params['verbose']) { - case 1: - print_r($po->what_changed()); - break; - - case 2: - print_r($po->io()->getImageProperties()); - break; - } - - $x = $po->list_duplicate()->find_all(); - if (count($x)) { - $skip = FALSE; - - foreach ($x as $o) { - # We'll only ignore based on the same signature. - if ($params['ignoredupe'] AND ($po->signature == $o->signature)) { - $skip = TRUE; - $this->writelog(sprintf("Ignore file [%s], it's the same as [%s (%s)]\n",$po->filename,$o->id,$o->file_path())); - break; - - } elseif ($params['deletedupe'] AND ($po->signature == $o->signature) AND ($po->filename != $o->filename)) { - $skip = TRUE; - $this->writelog(sprintf("Delete file [%s], it's the same as [%s (%s)] with signature [%s]\n",$po->filename,$o->id,$o->file_path(),$po->signature)); - unlink($file); - } - } - - unset($x); - - if ($skip) - continue; - else - $po->duplicate = '1'; - } - - if (! $po->changed()) - $this->writelog(sprintf("Image [%s] already in DB: %s\n",$file,$po->id)); - - $po->save(); - - if ($po->saved()) - $this->writelog(sprintf("Image [%s] stored in DB: %s\n",$file,$po->id)); - - // Record our tags - foreach ($t as $o) { - $x = ORM::factory('Photo_Tag')->where('tag_id','=',$o->id)->where('photo_id','=',$po->id)->find(); - $x->tag_id = $o->id; - $x->photo_id = $po->id; - $x->save(); - } - - // Record our people - foreach ($p as $o) { - $x = ORM::factory('Photo_People',array('people_id'=>$o->id,'photo_id'=>$po->id)); - $x->people_id = $o->id; - $x->photo_id = $po->id; - $x->save(); - } - - unset($po); - unset($x); - unset($o); - } - - if ($c > 1) - return sprintf("Images processed: %s\n",$c); - } - - // Force the return of a string or NULL - private function dbcol($val,$noval=NULL) { - return $val ? (string)$val : $noval; - } - - private function writelog($msg) { - if (! $this->_log) - return; - - static $fh = NULL; - - if (is_null($fh)) - $fh = fopen($this->_log,'a+'); - - fwrite($fh,$msg); - } -} -?> diff --git a/composer.json b/composer.json index 4943e17..f551198 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,9 @@ "mockery/mockery": "0.9.*", "phpunit/phpunit": "~4.0", "symfony/css-selector": "2.8.*|3.0.*", - "symfony/dom-crawler": "2.8.*|3.0.*" + "symfony/dom-crawler": "2.8.*|3.0.*", + "xethron/migrations-generator": "dev-l5", + "way/generators": "dev-feature/laravel-five-stable" }, "autoload": { "classmap": [ @@ -46,5 +48,11 @@ }, "config": { "preferred-install": "dist" + }, + "repositories": { + "repo-name": { + "type": "git", + "url": "git@github.com:jamisonvalenta/Laravel-4-Generators.git" + } } } diff --git a/composer.lock b/composer.lock index f777581..0ac80b8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c284a9c122da36c99a8b6597bdce7aa6", - "content-hash": "8b1485987e7c5949da82435d403e52e8", + "hash": "06f9a4d19eda3cf3c92386077c05117c", + "content-hash": "e32614d49ea2fa40cc882537b7125da4", "packages": [ { "name": "classpreloader/classpreloader", @@ -1780,6 +1780,354 @@ } ], "packages-dev": [ + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2015-08-31 12:32:49" + }, + { + "name": "doctrine/cache", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2015-12-31 16:37:02" + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2015-04-14 22:21:58" + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2015-12-25 13:18:31" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2016-01-05 22:11:12" + }, { "name": "doctrine/instantiator", "version": "1.0.5", @@ -1834,6 +2182,60 @@ ], "time": "2015-06-14 21:17:01" }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, { "name": "fzaninotto/faker", "version": "v1.6.0", @@ -3101,6 +3503,45 @@ "homepage": "https://symfony.com", "time": "2016-06-14 11:18:07" }, + { + "name": "way/generators", + "version": "dev-feature/laravel-five-stable", + "source": { + "type": "git", + "url": "git@github.com:jamisonvalenta/Laravel-4-Generators.git", + "reference": "a358bb44f517e2b3c1fd4f848f2646857c75b3a4" + }, + "require": { + "illuminate/support": "~5.0", + "php": ">=5.4.0" + }, + "require-dev": { + "behat/behat": "~2.5.1", + "behat/mink": "~1.5.0", + "behat/mink-extension": "~1.2.0", + "behat/mink-goutte-driver": "~1.0.9", + "behat/mink-selenium2-driver": "~1.1.1", + "phpspec/phpspec": "~2.0", + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "autoload": { + "psr-0": { + "Way\\Generators": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeffrey Way", + "email": "jeffrey@jeffrey-way.com" + } + ], + "description": "Rapidly generate resources, migrations, models, and much more.", + "time": "2015-02-17 06:26:25" + }, { "name": "webmozart/assert", "version": "1.0.2", @@ -3149,11 +3590,66 @@ "validate" ], "time": "2015-08-24 13:29:44" + }, + { + "name": "xethron/migrations-generator", + "version": "dev-l5", + "source": { + "type": "git", + "url": "https://github.com/Xethron/migrations-generator.git", + "reference": "e5e86efb5731e6859ea0d96a806159f9f2c26ce1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Xethron/migrations-generator/zipball/e5e86efb5731e6859ea0d96a806159f9f2c26ce1", + "reference": "e5e86efb5731e6859ea0d96a806159f9f2c26ce1", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.4", + "illuminate/support": ">=4.1", + "php": ">=5.4.0", + "way/generators": "dev-feature/laravel-five-stable" + }, + "require-dev": { + "illuminate/cache": ">=4.1.0", + "illuminate/console": ">=4.1.0", + "mockery/mockery": ">=0.9.0", + "phpunit/phpunit": ">=4.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Xethron\\MigrationsGenerator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Breytenbach", + "email": "bernhard@coffeecode.co.za" + } + ], + "description": "Generates Laravel Migrations from an existing database", + "keywords": [ + "artisan", + "generator", + "laravel", + "migration", + "migrations" + ], + "time": "2015-02-23 05:43:08" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "xethron/migrations-generator": 20, + "way/generators": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/config/app.php b/config/app.php index 7c1987c..7c0bb8e 100644 --- a/config/app.php +++ b/config/app.php @@ -52,7 +52,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => 'Australia/Melbourne', /* |-------------------------------------------------------------------------- @@ -158,6 +158,8 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, + Way\Generators\GeneratorsServiceProvider::class, + Xethron\MigrationsGenerator\MigrationsGeneratorServiceProvider::class, ], /* diff --git a/config/photo.php b/config/photo.php new file mode 100644 index 0000000..7506f2b --- /dev/null +++ b/config/photo.php @@ -0,0 +1,9 @@ +'/var/www/sites/co.dlcm.p/store', + 'import'=>[ + 'accepted'=>['jpg'], + 'log'=>'/tmp/import.log', + ], +]; diff --git a/database/migrations/2016_06_03_055603_create_photo_table.php b/database/migrations/2016_06_03_055603_create_photo_table.php new file mode 100644 index 0000000..aebad5e --- /dev/null +++ b/database/migrations/2016_06_03_055603_create_photo_table.php @@ -0,0 +1,48 @@ +bigInteger('id', true); + $table->timestamps(); + $table->integer('date_taken')->nullable(); + $table->smallInteger('subsectime')->nullable(); + $table->string('filename', 128); + $table->string('signature', 64)->nullable(); + $table->string('make', 32)->nullable(); + $table->string('model', 32)->nullable(); + $table->integer('height')->nullable(); + $table->integer('width')->nullable(); + $table->integer('orientation')->nullable(); + $table->float('gps_lat', 10, 0)->nullable(); + $table->float('gps_lon', 10, 0)->nullable(); + $table->binary('thumbnail', 65535)->nullable(); + $table->boolean('duplicate')->nullable(); + $table->boolean('remove')->nullable(); + $table->boolean('flag')->nullable(); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('photo'); + } + +} diff --git a/database/migrations/2016_06_03_055604_create_people_table.php b/database/migrations/2016_06_03_055604_create_people_table.php new file mode 100644 index 0000000..617dd70 --- /dev/null +++ b/database/migrations/2016_06_03_055604_create_people_table.php @@ -0,0 +1,35 @@ +integer('id', true); + $table->string('tag', 16)->unique('tag_UNIQUE'); + $table->string('name', 64)->nullable(); + $table->integer('date_birth')->nullable(); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('people'); + } + +} diff --git a/database/migrations/2016_06_03_055604_create_photo_people_table.php b/database/migrations/2016_06_03_055604_create_photo_people_table.php new file mode 100644 index 0000000..4109647 --- /dev/null +++ b/database/migrations/2016_06_03_055604_create_photo_people_table.php @@ -0,0 +1,36 @@ +bigInteger('id', true); + $table->timestamps(); + $table->integer('people_id'); + $table->bigInteger('photo_id')->index('fk_pp_ph_idx'); + $table->unique(['people_id','photo_id'], 'UNIQUE'); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('photo_people'); + } + +} diff --git a/database/migrations/2016_06_03_055604_create_photo_tag_table.php b/database/migrations/2016_06_03_055604_create_photo_tag_table.php new file mode 100644 index 0000000..08153d9 --- /dev/null +++ b/database/migrations/2016_06_03_055604_create_photo_tag_table.php @@ -0,0 +1,36 @@ +bigInteger('id', true); + $table->timestamps(); + $table->bigInteger('photo_id'); + $table->bigInteger('tag_id')->index('pt_t_idx'); + $table->unique(['photo_id','tag_id'], 'UNIQUE'); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('photo_tag'); + } + +} diff --git a/database/migrations/2016_06_03_055604_create_tags_table.php b/database/migrations/2016_06_03_055604_create_tags_table.php new file mode 100644 index 0000000..5189df5 --- /dev/null +++ b/database/migrations/2016_06_03_055604_create_tags_table.php @@ -0,0 +1,34 @@ +bigInteger('id', true); + $table->string('tag', 16)->unique('tag_UNIQUE'); + $table->string('description', 45)->nullable(); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('tags'); + } + +} diff --git a/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_people_table.php b/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_people_table.php new file mode 100644 index 0000000..942bb80 --- /dev/null +++ b/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_people_table.php @@ -0,0 +1,37 @@ +foreign('people_id', 'fk_pp_p')->references('id')->on('people')->onUpdate('NO ACTION')->onDelete('NO ACTION'); + $table->foreign('photo_id', 'fk_pp_ph')->references('id')->on('photo')->onUpdate('NO ACTION')->onDelete('NO ACTION'); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('photo_people', function(Blueprint $table) + { + $table->dropForeign('fk_pp_p'); + $table->dropForeign('fk_pp_ph'); + }); + } + +} diff --git a/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_tag_table.php b/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_tag_table.php new file mode 100644 index 0000000..60a8191 --- /dev/null +++ b/database/migrations/2016_06_03_055605_add_foreign_keys_to_photo_tag_table.php @@ -0,0 +1,37 @@ +foreign('photo_id', 'fk_pt_p')->references('id')->on('photo')->onUpdate('NO ACTION')->onDelete('NO ACTION'); + $table->foreign('tag_id', 'fk_pt_t')->references('id')->on('tags')->onUpdate('NO ACTION')->onDelete('NO ACTION'); + }); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('photo_tag', function(Blueprint $table) + { + $table->dropForeign('fk_pt_p'); + $table->dropForeign('fk_pt_t'); + }); + } + +} diff --git a/resources/views/auth/emails/password.blade.php b/resources/views/auth/emails/password.blade.php new file mode 100644 index 0000000..1b53830 --- /dev/null +++ b/resources/views/auth/emails/password.blade.php @@ -0,0 +1 @@ +Click here to reset your password: {{ $link }} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..a1ad8cf --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,66 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
Login
+
+
+ {{ csrf_field() }} + +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + Forgot Your Password? +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/email.blade.php b/resources/views/auth/passwords/email.blade.php new file mode 100644 index 0000000..33df8e3 --- /dev/null +++ b/resources/views/auth/passwords/email.blade.php @@ -0,0 +1,47 @@ +@extends('layouts.app') + + +@section('content') +
+
+
+
+
Reset Password
+
+ @if (session('status')) +
+ {{ session('status') }} +
+ @endif + +
+ {{ csrf_field() }} + +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php new file mode 100644 index 0000000..64e8297 --- /dev/null +++ b/resources/views/auth/passwords/reset.blade.php @@ -0,0 +1,70 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
Reset Password
+ +
+
+ {{ csrf_field() }} + + + +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+ +
+ + + @if ($errors->has('password_confirmation')) + + {{ $errors->first('password_confirmation') }} + + @endif +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000..4705d6e --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,82 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
Register
+
+
+ {{ csrf_field() }} + +
+ + +
+ + + @if ($errors->has('name')) + + {{ $errors->first('name') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password_confirmation')) + + {{ $errors->first('password_confirmation') }} + + @endif +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/common/errors.blade.php b/resources/views/common/errors.blade.php new file mode 100644 index 0000000..da2afaa --- /dev/null +++ b/resources/views/common/errors.blade.php @@ -0,0 +1,16 @@ + + +@if (count($errors) > 0) + +
+ Whoops! Something went wrong! + +

+ + +
+@endif diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..d909843 --- /dev/null +++ b/resources/views/layouts/app.blade.php @@ -0,0 +1,82 @@ + + + + + + + + Laravel + + + + + + + + {{-- --}} + + + + + + + @yield('content') + + + + + {{-- --}} + + diff --git a/resources/views/photo/view.blade.php b/resources/views/photo/view.blade.php new file mode 100644 index 0000000..e5ebfd8 --- /dev/null +++ b/resources/views/photo/view.blade.php @@ -0,0 +1,17 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
Photo id; ?>
+ +
+ +
+
+
+
+
+@endsection diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index 87710ac..06e2217 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -1,45 +1,17 @@ - - - - Laravel +@extends('layouts.app') - +@section('content') +
+
+
+
+
Welcome
- - - -
-
-
Laravel 5
+
+ Your Application's Landing Page. +
- - +
+
+@endsection