From 6aa01f339faac085ba21ed8ed24cc156d5c59c29 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 4 Feb 2018 17:13:44 +0800 Subject: [PATCH 01/68] Refactor counter Changes are made to follow design pattern of laravel. * Rename PostViewEvent to ViewedEvent * Add new Counter stuff(Contract, ServiceProvider, Command, etc) * Add multiple driver support of Counter(Default set to "cache.store") --- app/Console/Commands/SaveCounter.php | 151 +++++++++++++ app/Console/Commands/SavePostViewCount.php | 126 ----------- app/Console/Kernel.php | 5 +- .../{PostViewEvent.php => ViewedEvent.php} | 17 +- .../Controllers/Frontend/PostController.php | 4 +- app/Indigo/Contracts/Counter.php | 47 ++++ app/Indigo/Contracts/Viewable.php | 20 ++ app/Indigo/Tools/Counter.php | 201 ++++++++++++++++++ app/Listeners/PostViewEventListener.php | 38 ---- app/Listeners/ViewedEventListener.php | 38 ++++ app/Models/Post.php | 5 +- app/Models/Viewable.php | 26 +++ app/Providers/CounterServiceProvider.php | 39 ++++ app/Providers/EventServiceProvider.php | 4 +- config/app.php | 1 + config/indigo.php | 16 ++ 16 files changed, 557 insertions(+), 181 deletions(-) create mode 100644 app/Console/Commands/SaveCounter.php delete mode 100644 app/Console/Commands/SavePostViewCount.php rename app/Events/{PostViewEvent.php => ViewedEvent.php} (68%) create mode 100644 app/Indigo/Contracts/Counter.php create mode 100644 app/Indigo/Contracts/Viewable.php create mode 100644 app/Indigo/Tools/Counter.php delete mode 100644 app/Listeners/PostViewEventListener.php create mode 100644 app/Listeners/ViewedEventListener.php create mode 100644 app/Models/Viewable.php create mode 100644 app/Providers/CounterServiceProvider.php create mode 100644 config/indigo.php diff --git a/app/Console/Commands/SaveCounter.php b/app/Console/Commands/SaveCounter.php new file mode 100644 index 0000000..b3fd233 --- /dev/null +++ b/app/Console/Commands/SaveCounter.php @@ -0,0 +1,151 @@ +counter = $counter; + } + + /** + * Execute the console command. + */ + public function handle() + { + $viewableArray = $this->argument('viewable'); + + // Validate arguments + $this->validate($viewableArray); + + $this->iterateViewable(function ($viewable) { + $stats = $this->getStats($viewable); + + $this->updateCount($viewable, (array)$stats); + + $this->resetStats($viewable); + }); + } + + /** + * @param $viewableArray + */ + private function validate($viewableArray) + { + foreach ($viewableArray as $className) { + $instance = resolve($className); + if (!$instance instanceof Viewable || !$instance instanceof Model) { + $this->error("{$className} is not implement with " . Viewable::class); + exit; + } + $this->setViewableMaps($className, $instance); + } + } + + /** + * @param $name + * @param $instance + * @return $this + */ + private function setViewableMaps($name, $instance) + { + $this->viewableMaps[$name] = $instance; + + return $this; + } + + /** + * @param \Closure $callback + */ + private function iterateViewable(Closure $callback) + { + foreach ($this->viewableMaps as $instance) { + $callback($instance); + } + } + + /** + * @param $key + * @return mixed + */ + private function getStats($key) + { + return $this->counter->getAll($key); + } + + /** + * @param \App\Indigo\Contracts\Viewable|\Illuminate\Database\Eloquent\Model $viewable + * @param array $data + */ + private function updateCount($viewable, array $data = []) + { + $tableData = []; + + foreach ($data as $identifier => $increment) { + $viewable->newQuery()->where($viewable->getKeyName(), $identifier)->increment($viewable->getCountField(), + $increment); + array_push($tableData, [$identifier, $increment]); + } + + $this->success(get_class($viewable), $tableData); + } + + /** + * @param $className + * @param $tableData + */ + private function success($className, $tableData) + { + $this->info("{$className} save count successfully at " . Carbon::now()->toDateTimeString()); + $this->table(['ID', 'Increment'], $tableData); + } + + /** + * @param $viewable + * @return bool + */ + private function resetStats($viewable) + { + return $this->counter->resetAll($viewable); + } +} diff --git a/app/Console/Commands/SavePostViewCount.php b/app/Console/Commands/SavePostViewCount.php deleted file mode 100644 index af25cd7..0000000 --- a/app/Console/Commands/SavePostViewCount.php +++ /dev/null @@ -1,126 +0,0 @@ -postRepo = $postRepo; - - $this->cacheKeyPrefix = config('blog.counter.cache_key'); - } - - /** - * Execute the console command. - * - * @return mixed - */ - public function handle() - { - $data = $this->savePostViewCount(); - - $this->info("Post view_count save successfully at " . Carbon::now()->toDateTimeString()); - $this->table(['Post ID', 'Increase Count'], $data); - } - - /** - * Retrieve all post view_count from cache and save into DB - * - * @return array - */ - protected function savePostViewCount() - { - $results = []; - - // Retrieve all post id - $this->postRepo - ->getModel() - ->select('id') - ->chunk(100, function ($posts) use (&$results) { - foreach ($posts as $post) { - if ($count = $this->getCacheCount($post->id)) { - $post->increment('view_count', $count); - - array_push($results, [$post->id, $count]); - - $this->flushCache($this->cacheKey($post->id)); - } - } - }); - - return $results; - } - - /** - * Get post view_count from cache - * - * @param $id - * @return mixed - */ - protected function getCacheCount($id) - { - return Cache::get($this->cacheKey($id)); - } - - /** - * Get cache key - * - * @param $id - * @return string - */ - protected function cacheKey($id) - { - return $this->cacheKeyPrefix . $id; - } - - /** - * Flush post view_count in cache by key - * - * @param $key - * @return bool - */ - protected function flushCache($key) - { - return Cache::forget($key); - } -} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 7164635..d3fae5c 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,7 +2,7 @@ namespace App\Console; -use App\Console\Commands\SavePostViewCount; +use App\Console\Commands\SaveCounter; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -14,7 +14,6 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ - SavePostViewCount::class ]; /** @@ -27,7 +26,7 @@ protected function schedule(Schedule $schedule) { // $schedule->command('inspire') // ->hourly(); - $schedule->command('view_count:save')->hourly()->appendOutputTo(storage_path() . '/logs/cron.log'); + $schedule->command(SaveCounter::class)->hourly()->appendOutputTo(storage_path() . '/logs/counter.log'); } /** diff --git a/app/Events/PostViewEvent.php b/app/Events/ViewedEvent.php similarity index 68% rename from app/Events/PostViewEvent.php rename to app/Events/ViewedEvent.php index 5761509..80b9549 100644 --- a/app/Events/PostViewEvent.php +++ b/app/Events/ViewedEvent.php @@ -2,6 +2,7 @@ namespace App\Events; +use App\Indigo\Contracts\Viewable; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; @@ -11,25 +12,25 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast; /** - * Class PostViewEvent + * Class ViewedEvent * @package App\Events */ -class PostViewEvent +class ViewedEvent { use Dispatchable, InteractsWithSockets, SerializesModels; /** - * @var + * @var \App\Indigo\Contracts\Viewable */ - public $postId; + public $viewable; /** - * PostViewEvent constructor. - * @param $postId + * ViewedEvent constructor. + * @param \App\Indigo\Contracts\Viewable $viewable */ - public function __construct($postId) + public function __construct(Viewable $viewable) { - $this->postId = $postId; + $this->viewable = $viewable; } /** diff --git a/app/Http/Controllers/Frontend/PostController.php b/app/Http/Controllers/Frontend/PostController.php index 9fa3f54..8388521 100644 --- a/app/Http/Controllers/Frontend/PostController.php +++ b/app/Http/Controllers/Frontend/PostController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Frontend; -use App\Events\PostViewEvent; +use App\Events\ViewedEvent; use App\Repositories\Contracts\PostRepository; /** @@ -52,7 +52,7 @@ public function show($slug) $previous = $this->postRepository->previous($post); $next = $this->postRepository->next($post); - event(new PostViewEvent($post->id)); + event(new ViewedEvent($post)); return view('posts.show', compact('post', 'previous', 'next')); } diff --git a/app/Indigo/Contracts/Counter.php b/app/Indigo/Contracts/Counter.php new file mode 100644 index 0000000..c1e9db6 --- /dev/null +++ b/app/Indigo/Contracts/Counter.php @@ -0,0 +1,47 @@ +app = $app; + $this->setCacheStore($store); + } + + /** + * @param $store + */ + private function setCacheStore($store) + { + $this->cacheStore = $store; + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + * @param int $value + * @return int|bool + */ + public function increment(Viewable $viewable, $value = 1) + { + $count = $this->get($viewable); + + return tap((int)$count + $value, function ($newValue) use ($viewable) { + $this->put($viewable, $newValue); + }); + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + * @param int $default + * @return int + */ + public function get(Viewable $viewable, $default = 0) + { + list($key, $identifier) = $this->parser($viewable); + + $stats = tap($this->getStats($key), function ($value) use ($key) { + $this->setStatsData($key, $value); + }); + + return $stats[$identifier] ?? $default; + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + * @return array + */ + protected function parser(Viewable $viewable) + { + return [ + $this->getKey($viewable), + $viewable->getIdentifier() + ]; + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + * @return string + */ + protected function getKey(Viewable $viewable) + { + return get_class($viewable); + } + + /** + * @param $key + * @return array|mixed + */ + private function getStats($key) + { + $stats = $this->getStatsData($key) ?: $this->getAll($key); + + return $stats ?: $this->initializeStats(); + } + + /** + * @param $key + * @param null $default + * @return mixed|null + */ + private function getStatsData($key, $default = null) + { + return $this->statsData[$key] ?? $default; + } + + /** + * @param \App\Indigo\Contracts\Viewable|string $viewable + * @return mixed + */ + public function getAll($viewable) + { + $key = $this->parseKey($viewable); + + return $this->getCacheRepository()->get($key); + } + + /** + * @param $viewable + * @return string + */ + private function parseKey($viewable): string + { + return is_string($viewable) ? $viewable : $this->getKey($viewable); + } + + /** + * @return \Illuminate\Contracts\Cache\Repository + */ + protected function getCacheRepository() + { + return $this->app['cache']->store($this->cacheStore); + } + + /** + * @return array + */ + private function initializeStats() + { + return []; + } + + /** + * @param $key + * @param $value + * @return $this + */ + private function setStatsData($key, $value) + { + $this->statsData[$key] = $value; + + return $this; + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + * @param $value + */ + public function put(Viewable $viewable, $value) + { + list($key, $identifier) = $this->parser($viewable); + + $stats = $this->getStats($key); + + $stats[$identifier] = $value; + + $this->setStatsData($key, $stats); + + $this->getCacheRepository()->forever($key, $stats); + } + + /** + * @param \App\Indigo\Contracts\Viewable $viewable + */ + public function reset(Viewable $viewable) + { + $this->put($viewable, 0); + } + + /** + * @param \App\Indigo\Contracts\Viewable|string $viewable + * @return bool + */ + public function resetAll($viewable) + { + $key = $this->parseKey($viewable); + + return $this->getCacheRepository()->forget($key); + } +} \ No newline at end of file diff --git a/app/Listeners/PostViewEventListener.php b/app/Listeners/PostViewEventListener.php deleted file mode 100644 index 49deefb..0000000 --- a/app/Listeners/PostViewEventListener.php +++ /dev/null @@ -1,38 +0,0 @@ -counter = $postViewCounter; - } - - /** - * Handle the event. - * - * @param PostViewEvent $event - * @return void - */ - public function handle(PostViewEvent $event) - { - $this->counter->run($event->postId); - } -} diff --git a/app/Listeners/ViewedEventListener.php b/app/Listeners/ViewedEventListener.php new file mode 100644 index 0000000..2166e87 --- /dev/null +++ b/app/Listeners/ViewedEventListener.php @@ -0,0 +1,38 @@ +counter = $counter; + } + + /** + * Handle the event. + * + * @param \App\Events\ViewedEvent $event + * @return void + */ + public function handle(ViewedEvent $event) + { + $this->counter->increment($event->viewable); + } +} diff --git a/app/Models/Post.php b/app/Models/Post.php index 1f19313..b02a332 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Indigo\Contracts\Viewable as ViewableContract; use App\Presenters\PostPresenter; use App\Scopes\PublishedScope; use Illuminate\Database\Eloquent\Model; @@ -15,9 +16,9 @@ * Class Post * @package App\Models */ -class Post extends Model implements Markdownable +class Post extends Model implements Markdownable, ViewableContract { - use PresentableTrait, SoftDeletes; + use PresentableTrait, SoftDeletes, Viewable; /** * Is draft status */ diff --git a/app/Models/Viewable.php b/app/Models/Viewable.php new file mode 100644 index 0000000..78d707e --- /dev/null +++ b/app/Models/Viewable.php @@ -0,0 +1,26 @@ +getKey(); + } +} \ No newline at end of file diff --git a/app/Providers/CounterServiceProvider.php b/app/Providers/CounterServiceProvider.php new file mode 100644 index 0000000..7c79dfc --- /dev/null +++ b/app/Providers/CounterServiceProvider.php @@ -0,0 +1,39 @@ +app->singleton(\App\Indigo\Contracts\Counter::class, function ($app) { + $config = $this->getConfig(); + return new Counter($app, $config['cache_store']); + }); + } + + /** + * @return mixed + */ + protected function getConfig() + { + return $this->app['config']['indigo.counter']; + } +} \ No newline at end of file diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index f18e4c8..7a87b77 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -17,8 +17,8 @@ class EventServiceProvider extends ServiceProvider // 'App\Events\Event' => [ // 'App\Listeners\EventListener', // ], - 'App\Events\PostViewEvent' => [ - 'App\Listeners\PostViewEventListener', + 'App\Events\ViewedEvent' => [ + 'App\Listeners\ViewedEventListener', ], ]; diff --git a/config/app.php b/config/app.php index c2e1d39..17a5493 100644 --- a/config/app.php +++ b/config/app.php @@ -177,6 +177,7 @@ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\ComposerServiceProvider::class, + \App\Providers\CounterServiceProvider::class, // Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider::class, // Zizaco\Entrust\EntrustServiceProvider::class, diff --git a/config/indigo.php b/config/indigo.php new file mode 100644 index 0000000..1888f5f --- /dev/null +++ b/config/indigo.php @@ -0,0 +1,16 @@ + [ + /* + |-------------------------------------------------------------------------- + | Default Cache Store of Counter + |-------------------------------------------------------------------------- + | + | With referenced to cache.php + | Set "null" to use cache's default store. + | + */ + 'cache_store' => env('COUNTER_DEFAULT_STORE', null) + ] +]; \ No newline at end of file From 7b6dbc4e81354e34865627b86ced0fd168e1d244 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 4 Feb 2018 17:23:49 +0800 Subject: [PATCH 02/68] Remove ungly repository cache --- app/Contracts/ContentableInterface.php | 24 -- .../Contracts/Helpers/CacheableInterface.php | 71 ---- .../Eloquent/Traits/Cacheable.php | 313 ------------------ app/Services/CacheHelper.php | 161 --------- config/blog.php | 4 - 5 files changed, 573 deletions(-) delete mode 100644 app/Contracts/ContentableInterface.php delete mode 100644 app/Repositories/Contracts/Helpers/CacheableInterface.php delete mode 100644 app/Repositories/Eloquent/Traits/Cacheable.php delete mode 100644 app/Services/CacheHelper.php diff --git a/app/Contracts/ContentableInterface.php b/app/Contracts/ContentableInterface.php deleted file mode 100644 index 6623270..0000000 --- a/app/Contracts/ContentableInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -cache = $cache; - - return $this; - } - - /** - * @param bool $status - * @return $this - */ - public function setForever($status = true) - { - $this->forever = $status; - - return $this; - } - - /** - * @return CacheHelper - */ - protected function getCacheHelper() - { - return new CacheHelper(); - } - - /** - * @param bool $status - * @return $this - */ - public function skipCache($status = true) - { - $this->skipCache = $status; - - return $this; - } - - /** - * @param null $perPage - * @param array $columns - * @return mixed - */ - public function paginate($perPage = null, $columns = ['*']) - { - $table = $this->getModelTable(); - - $key = $this->getCacheHelper()->keyPaginate($table); - - array_push($this->tags, $this->getCacheHelper()->tagPaginate($table)); - - return $this->getIfCacheable(__FUNCTION__, func_get_args(), $key); - } - - /** - * @param $method - * @param $args - * @param $key - * @param boolean $ignoreUriQuery - * @return mixed - */ - private function getIfCacheable($method, $args, $key = null, $ignoreUriQuery = true) - { - if (!$this->isAllowedCache()) { - return call_user_func_array([$this, 'parent::' . $method], $args); - } - - $key = $key ?: $this->getCacheKey($method, $args, $ignoreUriQuery); - - $result = $this->remember($key, - function () use ($method, $args) { - return call_user_func_array([$this, 'parent::' . $method], $args); - }); - - // $this->resetModel(); - $this->resetScope(); - - return $result; - } - - /** - * @return bool - */ - public function isAllowedCache() - { - return config('blog.cache.enable', true) && !$this->isSkippedCache() && !isAdmin(); - } - - /** - * @return bool - */ - public function isSkippedCache() - { - return $this->skipCache; - } - - /** - * @param $key - * @param Closure $callback - * @param null $minutes - * @return mixed - */ - public function remember($key, Closure $callback, $minutes = null) - { - $minutes = $minutes ?: $this->getCacheMinutes(); - - $cache = $this->getCacheRepositoryWithTags(); - - if ($this->forever) { - return $cache->rememberForever($key, $callback); - } - return $cache->remember($key, $minutes, $callback); - } - - /** - * @return mixed - */ - public function getCacheMinutes() - { - return $this->cacheMinutes ?: config('blog.cache.minutes', 30); - } - - /** - * @param $minutes - * @return $this - */ - public function setCacheMinutes($minutes) - { - $this->cacheMinutes = $minutes; - - return $this; - } - - /** - * @return CacheRepository|\Illuminate\Foundation\Application|mixed - */ - public function getCacheRepositoryWithTags() - { - $cache = $this->getCacheRepository(); - - try { - $tags = array_merge($this->tags, [$this->getModelTable()]); - - return $cache->tags($tags); - } catch (\BadMethodCallException $exception) { - // Not support tags - // throw $exception; - return $cache; - } - } - - /** - * @return CacheRepository|\Illuminate\Foundation\Application|mixed - */ - public function getCacheRepository() - { - return $this->cache ?: app('cache.store'); - } - - /** - * @param $method - * @param null $args - * @param boolean $ignoreUriQuery - * @return string - */ - public function getCacheKey($method, $args = null, $ignoreUriQuery = true) - { - $relationsNameOnly = $this->parseRelations(); - - $query = $this->parseQuery($ignoreUriQuery); - - $key = sprintf('%s:%s-%s', $this->getModelTable(), $method, - md5(serialize($args) . serialize($relationsNameOnly) . $query)); - - return $key; - } - - /** - * @return array|null - */ - protected function parseRelations() - { - if (empty($this->relations)) { - return null; - } - - $names = []; - foreach ($this->relations as $name => $constraints) { - if (is_numeric($name)) { - $name = $constraints; - } - array_push($names, $name); - } - - sort($names); - - return $names; - } - - /** - * @param $ignore - * @return null|string - */ - protected function parseQuery($ignore) - { - if ($ignore) { - return ''; - } - - return request()->getQueryString(); - } - - /** - * @param $id - * @param array $columns - * @return mixed - */ - public function find($id, $columns = ['*']) - { - $id = (int)$id; - - $key = $this->getCacheHelper()->keyFind($this->getModelTable(), $id); - - return $this->setForever()->getIfCacheable(__FUNCTION__, [$id, $columns], $key); - } - - /** - * @param array $columns - * @return mixed - */ - public function all($columns = ['*']) - { - $key = $this->getCacheHelper()->keyAll($this->getModelTable()); - - return $this->getIfCacheable(__FUNCTION__, func_get_args(), $key); - } - - /** - * @param $field - * @param $value - * @param array $columns - * @return mixed - */ - public function findBy($field, $value, $columns = ['*']) - { - $key = $this->getCacheHelper()->keySlug($this->getModelTable(), $value); - - return $this->setForever()->getIfCacheable(__FUNCTION__, func_get_args(), $key); - } - // - // /** - // * @param $field - // * @param $value - // * @param array $columns - // * @return mixed - // */ - // public function findAllBy($field, $value, $columns = ['*']) - // { - // return $this->getIfCacheable(__FUNCTION__, func_get_args()); - // } - // - // /** - // * @param array $where - // * @param array $columns - // * @return mixed - // */ - // public function findWhere(array $where, $columns = ['*']) - // { - // return $this->getIfCacheable(__FUNCTION__, func_get_args()); - // } -} diff --git a/app/Services/CacheHelper.php b/app/Services/CacheHelper.php deleted file mode 100644 index dbb2d5b..0000000 --- a/app/Services/CacheHelper.php +++ /dev/null @@ -1,161 +0,0 @@ -isAllowCache()) { - return; - } - - $tags = $this->tagPaginate($model->getTable()); - - Cache::tags($tags)->flush(); - } - - /** - * @return mixed - */ - public function isAllowCache() - { - return config('blog.cache.enable'); - } - - /** - * @param $table - * @return string - */ - public function tagPaginate($table) - { - return $table . '-paginate'; - } - - /** - * @param Model $model - */ - public function flushEntity(Model $model) - { - if (!$this->isAllowCache()) { - return; - } - - $table = $model->getTable(); - $key = $this->keySlug($table, $model->slug); - - Cache::tags($table)->forget($key); - } - - /** - * @param $table - * @param $slug - * @return string - */ - public function keySlug($table, $slug) - { - return sprintf(self::KEY_FORMAT, $table, md5($table . ':' . $slug)); - } - - /** - * @param Model $model - */ - public function flushList(Model $model) - { - if (!$this->isAllowCache()) { - return; - } - - $table = $model->getTable(); - $key = $this->keyAll($table); - - Cache::tags($table)->forget($key); - } - - /** - * @param $table - * @return string - */ - public function keyAll($table) - { - return sprintf(self::KEY_FORMAT, $table, 'all'); - } - - /** - * Set forever cache of content. - * - * @param ContentableInterface $contentable - * @return mixed - */ - public function cacheContent(ContentableInterface $contentable) - { - // return Cache::rememberForever($this->getContentCacheKey($contentable), - // function () use ($contentable) { - // return MarkDownParser::md2html($contentable->getRawContent()); - // }); - } - - /** - * Get content cache key. - * - * @param ContentableInterface $contentable - * @return string - */ - protected function getContentCacheKey(ContentableInterface $contentable) - { - // return sprintf('contents:%s', $contentable->getContentId()); - } - - /** - * Forget cache key of content. - * - * @param ContentableInterface $contentable - */ - public function flushContent(ContentableInterface $contentable) - { - Cache::forget($this->getContentCacheKey($contentable)); - } - - /** - * @param $table - * @param $id - * @return string - */ - public function keyFind($table, $id) - { - return sprintf(self::KEY_FORMAT, $table, $id); - } - - /** - * @param $table - * @return string - */ - public function keyPaginate($table) - { - return sprintf(self::KEY_FORMAT, $table, 'paginate-' . request()->input('page', 1)); - } - - /** - * @return string - */ - public static function keySiteSettings() - { - return 'site.settings'; - } -} \ No newline at end of file diff --git a/config/blog.php b/config/blog.php index 7ab6b4d..dfee492 100644 --- a/config/blog.php +++ b/config/blog.php @@ -8,10 +8,6 @@ 'timeout' => 3600, // 1h 'key' => 'post_viewed', ], - // 'cache' => [ - // 'enable' => env('ENABLE_DATA_CACHE', true), - // 'minutes' => 60, - // ], 'posts' => [ 'per_page' => 5, ], From 2bba51e385e882b2ac7da0d1306f3274010f6795 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 4 Feb 2018 17:25:32 +0800 Subject: [PATCH 03/68] Remove old Counter --- app/Indigo/Post/PostViewCounter.php | 190 ---------------------------- config/blog.php | 7 - 2 files changed, 197 deletions(-) delete mode 100644 app/Indigo/Post/PostViewCounter.php diff --git a/app/Indigo/Post/PostViewCounter.php b/app/Indigo/Post/PostViewCounter.php deleted file mode 100644 index 95aa950..0000000 --- a/app/Indigo/Post/PostViewCounter.php +++ /dev/null @@ -1,190 +0,0 @@ -request = $request; - $this->setConfig(); - } - - /** - * Prepare property - */ - protected function setConfig() - { - // TODO what if config is hack? - $config = $this->getDefaultConfig(); - - $this->timeout = $config['timeout']; - $this->key = $config['key']; - $this->cacheKey = $config['cache_key']; - $this->strict_mode = $config['strict_mode']; - } - - /** - * Get default config - * - * @return mixed - */ - protected function getDefaultConfig() - { - return config('blog.counter'); - } - - /** - * Reset timeout - * - * @param $timeout - */ - public function setTimeout($timeout) - { - $this->timeout = $timeout; - } - - /** - * Determinate if enable strict mode - */ - public function enableStrictMode() - { - $this->strict_mode = true; - } - - /** - * Determinate if disable strict mode - */ - public function disableStrictMode() - { - $this->strict_mode = false; - } - - /** - * Main - * - * @param $id - */ - public function run($id) - { - $this->id = $id; - - if ($this->strict_mode) { - if (!$this->isPostViewed() || $this->isLastViewOutdated()) { - $this->createSession(); - - $this->increaseCacheRecord(); - } - } else { - $this->increaseCacheRecord(); - } - } - - /** - * Determinate if post is have been viewed - * - * @return bool - */ - protected function isPostViewed() - { - return $this->request->session()->has($this->getPostSessionKey()); - } - - /** - * Get post's session key - * - * @return string - */ - protected function getPostSessionKey() - { - return $this->key . '.' . $this->id; - } - - /** - * Determinate if post view record is outdated - * - * @return bool - */ - protected function isLastViewOutdated() - { - $lastView = $this->request->session()->get($this->getPostSessionKey()); - - return ($lastView + $this->timeout) < $this->currentTime(); - } - - /** - * Return a unix timestamp - * - * @return int - */ - private function currentTime() - { - return time(); - } - - /** - * Create post viewed session - */ - protected function createSession() - { - $this->request->session()->put($this->getPostSessionKey(), $this->currentTime()); - } - - /** - * Save view record into cache - */ - protected function increaseCacheRecord() - { - Cache::increment($this->getPostCacheKey()); - } - - /** - * Return post's cache key - * - * @return string - */ - protected function getPostCacheKey() - { - return $this->cacheKey . $this->id; - } -} diff --git a/config/blog.php b/config/blog.php index dfee492..aebce4c 100644 --- a/config/blog.php +++ b/config/blog.php @@ -1,13 +1,6 @@ [ - 'strict_mode' => env('COUNT_STRICT_MODE', false), - 'cache_key' => 'post_viewed_count:', - // require strict_mode to be set - 'timeout' => 3600, // 1h - 'key' => 'post_viewed', - ], 'posts' => [ 'per_page' => 5, ], From b51efbacb8db3adabe9d4a4999c81f8644b994f9 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 4 Feb 2018 17:32:00 +0800 Subject: [PATCH 04/68] Update app's config to indigo.php --- app/Http/Middleware/VisitorMiddleware.php | 2 +- app/Http/ViewComposers/CommentComposer.php | 4 ++-- app/Repositories/Eloquent/BaseRepository.php | 2 +- .../Eloquent/PostRepositoryEloquent.php | 2 +- config/blog.php | 24 ------------------- config/indigo.php | 20 ++++++++++++++++ .../views/partials/google_analytics.blade.php | 4 ++-- 7 files changed, 27 insertions(+), 31 deletions(-) delete mode 100644 config/blog.php diff --git a/app/Http/Middleware/VisitorMiddleware.php b/app/Http/Middleware/VisitorMiddleware.php index a70f713..e2ce3d7 100644 --- a/app/Http/Middleware/VisitorMiddleware.php +++ b/app/Http/Middleware/VisitorMiddleware.php @@ -46,6 +46,6 @@ public function handle($request, Closure $next) */ protected function allowLog() { - return config('blog.log.visitor', false); + return config('indigo.log.visitor', false); } } diff --git a/app/Http/ViewComposers/CommentComposer.php b/app/Http/ViewComposers/CommentComposer.php index c9f9854..6f342ac 100644 --- a/app/Http/ViewComposers/CommentComposer.php +++ b/app/Http/ViewComposers/CommentComposer.php @@ -17,12 +17,12 @@ class CommentComposer */ public function compose(View $view) { - $commentDriver = config('blog.comment.driver'); + $commentDriver = config('indigo.comment.driver'); // Disqus if (($commentDriver == 'disqus')) { - if (!$shortName = config('blog.comment.disqus.short_name')) { + if (!$shortName = config('indigo.comment.disqus.short_name')) { throw new \Exception('Please set disqus short name.'); } diff --git a/app/Repositories/Eloquent/BaseRepository.php b/app/Repositories/Eloquent/BaseRepository.php index 7a9d347..6127671 100644 --- a/app/Repositories/Eloquent/BaseRepository.php +++ b/app/Repositories/Eloquent/BaseRepository.php @@ -194,7 +194,7 @@ public function paginate($perPage = null, $columns = ['*']) */ public function getDefaultPerPage() { - return config('blog.repository.pagination.per_page'); + return config('indigo.repository.pagination.per_page'); } /** diff --git a/app/Repositories/Eloquent/PostRepositoryEloquent.php b/app/Repositories/Eloquent/PostRepositoryEloquent.php index 606fd69..4822bd3 100644 --- a/app/Repositories/Eloquent/PostRepositoryEloquent.php +++ b/app/Repositories/Eloquent/PostRepositoryEloquent.php @@ -285,7 +285,7 @@ protected function paginateOfPostRelated(Model $model, $relation = 'posts') */ public function getDefaultPerPage() { - return config('blog.posts.per_page'); + return config('indigo.posts.per_page'); } /** diff --git a/config/blog.php b/config/blog.php deleted file mode 100644 index aebce4c..0000000 --- a/config/blog.php +++ /dev/null @@ -1,24 +0,0 @@ - [ - 'per_page' => 5, - ], - 'analytics' => [ - 'google_trace_id' => env('GOOGLE_ANALYTICS_ID'), - ], - 'log' => [ - 'visitor' => env('ENABLE_VISITOR_LOG', false), - ], - 'comment' => [ - 'driver' => env('COMMENT_DRIVER', 'null'), // Supported: "null", "disqus" - 'disqus' => [ - 'short_name' => env('DISQUS_SHORT_NAME'), - ] - ], - 'repository' => [ - 'pagination' => [ - 'per_page' => env('REPOSITORY_DEFAULT_PER_PAGE', 15) - ] - ] -]; diff --git a/config/indigo.php b/config/indigo.php index 1888f5f..fd85407 100644 --- a/config/indigo.php +++ b/config/indigo.php @@ -12,5 +12,25 @@ | */ 'cache_store' => env('COUNTER_DEFAULT_STORE', null) + ], + 'posts' => [ + 'per_page' => 5, + ], + 'analytics' => [ + 'google_trace_id' => env('GOOGLE_ANALYTICS_ID'), + ], + 'log' => [ + 'visitor' => env('ENABLE_VISITOR_LOG', false), + ], + 'comment' => [ + 'driver' => env('COMMENT_DRIVER', 'null'), // Supported: "null", "disqus" + 'disqus' => [ + 'short_name' => env('DISQUS_SHORT_NAME'), + ] + ], + 'repository' => [ + 'pagination' => [ + 'per_page' => env('REPOSITORY_DEFAULT_PER_PAGE', 15) + ] ] ]; \ No newline at end of file diff --git a/resources/views/partials/google_analytics.blade.php b/resources/views/partials/google_analytics.blade.php index b695df6..da86f4b 100644 --- a/resources/views/partials/google_analytics.blade.php +++ b/resources/views/partials/google_analytics.blade.php @@ -1,11 +1,11 @@ -@if(config('blog.analytics.google_trace_id')) +@if(config('indigo.analytics.google_trace_id')) From 94a67f5f3989180d42450ce09565a21436919ba2 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 5 Feb 2018 21:58:16 +0800 Subject: [PATCH 05/68] Update route:logs with http basic auth --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index 8de9e29..0596baf 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,7 +11,7 @@ | */ -Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index'); +Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index')->middleware('auth.basic'); Auth::routes(); From cee91751871a5c307712c00f52d5e3e7295776fe Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 5 Feb 2018 23:30:03 +0800 Subject: [PATCH 06/68] Add spatie/laravel-backup and related schedule --- .env.example | 7 +- app/Console/Kernel.php | 3 + composer.json | 1 + composer.lock | 171 ++++++++++++++++++++++++++++++++++++++- config/backup.php | 178 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 config/backup.php diff --git a/.env.example b/.env.example index 2affc75..8ad6154 100644 --- a/.env.example +++ b/.env.example @@ -44,4 +44,9 @@ ENABLE_VISITOR_LOG= COMMENT_DRIVER= DISQUS_SHORT_NAME= -FILESYSTEM_DRIVER= \ No newline at end of file +FILESYSTEM_DRIVER= + +ADMIN_EMAIL= + +MAIL_FROM_ADDRESS= +MAIL_FROM_NAME= \ No newline at end of file diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d3fae5c..4ae67f7 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -27,6 +27,9 @@ protected function schedule(Schedule $schedule) // $schedule->command('inspire') // ->hourly(); $schedule->command(SaveCounter::class)->hourly()->appendOutputTo(storage_path() . '/logs/counter.log'); + $schedule->command('backup:clean')->dailyAt('01:00'); + $schedule->command('backup:run')->dailyAt('02:00'); + $schedule->command('backup:monitor')->twiceMonthly(); } /** diff --git a/composer.json b/composer.json index edb1fe8..a2b73c1 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "league/html-to-markdown": "^4.4", "predis/predis": "^1.1", "rap2hpoutre/laravel-log-viewer": "^0.10.0", + "spatie/laravel-backup": "^5.1", "thomaswelton/laravel-gravatar": "^1.1", "zizaco/entrust": "^1.8" }, diff --git a/composer.lock b/composer.lock index 85769fa..82586db 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "bfe2581c56167a54155046b9f9cd6d39", + "content-hash": "5230c896c234134c315b2c1f0462a555", "packages": [ { "name": "dnoegel/php-xdg-base-dir", @@ -2277,6 +2277,175 @@ ], "time": "2017-06-29T11:18:01+00:00" }, + { + "name": "spatie/db-dumper", + "version": "2.8.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/db-dumper.git", + "reference": "d7c0d4b747f913b3c82caae0421021757e76e523" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/spatie/db-dumper/d7c0d4b747f913b3c82caae0421021757e76e523.zip", + "reference": "d7c0d4b747f913b3c82caae0421021757e76e523", + "shasum": "" + }, + "require": { + "php": "^7.0", + "symfony/process": "^3.0|^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\DbDumper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Dump databases", + "homepage": "https://github.com/spatie/db-dumper", + "keywords": [ + "database", + "db-dumper", + "dump", + "mysqldump", + "spatie" + ], + "time": "2018-01-20T14:47:45+00:00" + }, + { + "name": "spatie/laravel-backup", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-backup.git", + "reference": "3d66bda66530d42eddfb3faa0f60f3e9ae8a7f08" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/spatie/laravel-backup/3d66bda66530d42eddfb3faa0f60f3e9ae8a7f08.zip", + "reference": "3d66bda66530d42eddfb3faa0f60f3e9ae8a7f08", + "shasum": "" + }, + "require": { + "illuminate/console": "~5.5.0", + "illuminate/contracts": "~5.5.0", + "illuminate/events": "~5.5.0", + "illuminate/filesystem": "~5.5.0", + "illuminate/notifications": "~5.5.0", + "illuminate/support": "~5.5.0", + "league/flysystem": "^1.0.27", + "php": "^7.0", + "spatie/db-dumper": "^2.7", + "spatie/temporary-directory": "^1.1", + "symfony/finder": "^3.3" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "~3.5.0", + "phpunit/phpunit": "^6.2" + }, + "suggest": { + "guzzlehttp/guzzle": "Allows notifications to be sent via Slack" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Backup\\BackupServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\Backup\\": "src" + }, + "files": [ + "src/Helpers/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A Laravel 5 package to backup your application", + "homepage": "https://github.com/spatie/laravel-backup", + "keywords": [ + "backup", + "database", + "laravel-backup", + "spatie" + ], + "time": "2018-01-20T14:31:30+00:00" + }, + { + "name": "spatie/temporary-directory", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/temporary-directory.git", + "reference": "e3da5b7a00c6610bc0b18480815fe09adf73383b" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/spatie/temporary-directory/e3da5b7a00c6610bc0b18480815fe09adf73383b.zip", + "reference": "e3da5b7a00c6610bc0b18480815fe09adf73383b", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\TemporaryDirectory\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "spatie", + "temporary-directory" + ], + "time": "2017-09-11T08:51:13+00:00" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.0.2", diff --git a/config/backup.php b/config/backup.php new file mode 100644 index 0000000..9df21ea --- /dev/null +++ b/config/backup.php @@ -0,0 +1,178 @@ + [ + + /* + * The name of this application. You can use this name to monitor + * the backups. + */ + 'name' => config('app.name'), + + 'source' => [ + + 'files' => [ + + /* + * The list of directories and files that will be included in the backup. + */ + 'include' => [ + storage_path('app'), + ], + + /* + * These directories and files will be excluded from the backup. + * + * Directories used by the backup process will automatically be excluded. + */ + 'exclude' => [ + storage_path('app/backup-temp'), + storage_path('app/' . config('app.name')), + storage_path('app/.gitignore'), + ], + + /* + * Determines if symlinks should be followed. + */ + 'followLinks' => false, + ], + + /* + * The names of the connections to the databases that should be backed up + * MySQL, PostgreSQL, SQLite and Mongo databases are supported. + */ + 'databases' => [ + 'mysql', + ], + ], + + /* + * The database dump can be gzipped to decrease diskspace usage. + */ + 'gzip_database_dump' => true, + + 'destination' => [ + + /* + * The filename prefix used for the backup zip file. + */ + 'filename_prefix' => 'backup_', + + /* + * The disk names on which the backups will be stored. + */ + 'disks' => [ + 'local', + ], + ], + ], + + /* + * You can get notified when specific events occur. Out of the box you can use 'mail' and 'slack'. + * For Slack you need to install guzzlehttp/guzzle. + * + * You can also use your own notification classes, just make sure the class is named after one of + * the `Spatie\Backup\Events` classes. + */ + 'notifications' => [ + + 'notifications' => [ + \Spatie\Backup\Notifications\Notifications\BackupHasFailed::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFound::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\CleanupHasFailed::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\BackupWasSuccessful::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFound::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessful::class => ['mail'], + ], + + /* + * Here you can specify the notifiable to which the notifications should be sent. The default + * notifiable will use the variables specified in this config file. + */ + 'notifiable' => \Spatie\Backup\Notifications\Notifiable::class, + + 'mail' => [ + 'to' => env('ADMIN_EMAIL'), + ], + + 'slack' => [ + 'webhook_url' => '', + + /* + * If this is set to null the default channel of the webhook will be used. + */ + 'channel' => null, + ], + ], + + /* + * Here you can specify which backups should be monitored. + * If a backup does not meet the specified requirements the + * UnHealthyBackupWasFound event will be fired. + */ + 'monitorBackups' => [ + [ + 'name' => config('app.name'), + 'disks' => ['local'], + 'newestBackupsShouldNotBeOlderThanDays' => 1, + 'storageUsedMayNotBeHigherThanMegabytes' => 5000, + ], + + /* + [ + 'name' => 'name of the second app', + 'disks' => ['local', 's3'], + 'newestBackupsShouldNotBeOlderThanDays' => 1, + 'storageUsedMayNotBeHigherThanMegabytes' => 5000, + ], + */ + ], + + 'cleanup' => [ + /* + * The strategy that will be used to cleanup old backups. The default strategy + * will keep all backups for a certain amount of days. After that period only + * a daily backup will be kept. After that period only weekly backups will + * be kept and so on. + * + * No matter how you configure it the default strategy will never + * delete the newest backup. + */ + 'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class, + + 'defaultStrategy' => [ + + /* + * The number of days for which backups must be kept. + */ + 'keepAllBackupsForDays' => 7, + + /* + * The number of days for which daily backups must be kept. + */ + 'keepDailyBackupsForDays' => 16, + + /* + * The number of weeks for which one weekly backup must be kept. + */ + 'keepWeeklyBackupsForWeeks' => 8, + + /* + * The number of months for which one monthly backup must be kept. + */ + 'keepMonthlyBackupsForMonths' => 4, + + /* + * The number of years for which one yearly backup must be kept. + */ + 'keepYearlyBackupsForYears' => 2, + + /* + * After cleaning up the backups remove the oldest backup until + * this amount of megabytes has been reached. + */ + 'deleteOldestBackupsWhenUsingMoreMegabytesThan' => 5000, + ], + ], +]; From 0ce91e02a96b054920259c7a2364512359327867 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Wed, 7 Feb 2018 22:01:26 +0800 Subject: [PATCH 07/68] Add nao-pon/flysystem-google-drive package --- composer.json | 1 + composer.lock | 467 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 467 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a2b73c1..c46b200 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "laravel/framework": "5.5.*", "laravel/tinker": "~1.0", "league/html-to-markdown": "^4.4", + "nao-pon/flysystem-google-drive": "^1.1", "predis/predis": "^1.1", "rap2hpoutre/laravel-log-viewer": "^0.10.0", "spatie/laravel-backup": "^5.1", diff --git a/composer.lock b/composer.lock index 82586db..fa99aff 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "5230c896c234134c315b2c1f0462a555", + "content-hash": "7c895b0b0f9a2da2b894d924cb4711f6", "packages": [ { "name": "dnoegel/php-xdg-base-dir", @@ -668,6 +668,195 @@ ], "time": "2017-06-15T17:19:42+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/firebase/php-jwt/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e.zip", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": " 4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "time": "2017-06-27T22:17:23+00:00" + }, + { + "name": "google/apiclient", + "version": "v2.2.1", + "source": { + "type": "git", + "url": "https://github.com/google/google-api-php-client.git", + "reference": "b69b8ac4bf6501793c389d4e013a79d09c85c5f2" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/google/google-api-php-client/b69b8ac4bf6501793c389d4e013a79d09c85c5f2.zip", + "reference": "b69b8ac4bf6501793c389d4e013a79d09c85c5f2", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", + "google/apiclient-services": "~0.13", + "google/auth": "^1.0", + "guzzlehttp/guzzle": "~5.3.1|~6.0", + "guzzlehttp/psr7": "^1.2", + "monolog/monolog": "^1.17", + "php": ">=5.4", + "phpseclib/phpseclib": "~0.3.10|~2.0" + }, + "require-dev": { + "cache/filesystem-adapter": "^0.3.2", + "phpunit/phpunit": "~4", + "squizlabs/php_codesniffer": "~2.3", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" + }, + "suggest": { + "cache/filesystem-adapter": "For caching certs and tokens (using Google_Client::setCache)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Google_": "src/" + }, + "classmap": [ + "src/Google/Service/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Client library for Google APIs", + "homepage": "http://developers.google.com/api-client-library/php", + "keywords": [ + "google" + ], + "time": "2017-11-03T01:19:53+00:00" + }, + { + "name": "google/apiclient-services", + "version": "v0.45", + "source": { + "type": "git", + "url": "https://github.com/google/google-api-php-client-services.git", + "reference": "35909fbc909bf9f9e0d36dd05b62f4b1d5ffe3ae" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/google/google-api-php-client-services/35909fbc909bf9f9e0d36dd05b62f4b1d5ffe3ae.zip", + "reference": "35909fbc909bf9f9e0d36dd05b62f4b1d5ffe3ae", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-0": { + "Google_Service_": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Client library for Google APIs", + "homepage": "http://developers.google.com/api-client-library/php", + "keywords": [ + "google" + ], + "time": "2018-02-03T00:23:12+00:00" + }, + { + "name": "google/auth", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/google/google-auth-library-php.git", + "reference": "da0062d279c9459350808a4fb63dbc08b90d6b90" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/google/google-auth-library-php/da0062d279c9459350808a4fb63dbc08b90d6b90.zip", + "reference": "da0062d279c9459350808a4fb63dbc08b90d6b90", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", + "guzzlehttp/guzzle": "~5.3.1|~6.0", + "guzzlehttp/psr7": "~1.2", + "php": ">=5.4", + "psr/cache": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^1.11", + "guzzlehttp/promises": "0.1.1|^1.3", + "phpunit/phpunit": "^4.8.36|^5.7", + "sebastian/comparator": ">=1.2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Auth\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google Auth Library for PHP", + "homepage": "http://github.com/google/google-auth-library-php", + "keywords": [ + "Authentication", + "google", + "oauth2" + ], + "time": "2018-01-24T18:28:42+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "6.3.0", @@ -1436,6 +1625,54 @@ ], "time": "2018-01-27T16:03:56+00:00" }, + { + "name": "league/flysystem-cached-adapter", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-cached-adapter.git", + "reference": "93eec679c57f8510816577f361988b10b5b57351" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/thephpleague/flysystem-cached-adapter/93eec679c57f8510816577f361988b10b5b57351.zip", + "reference": "93eec679c57f8510816577f361988b10b5b57351", + "shasum": "" + }, + "require": { + "league/flysystem": "~1.0", + "psr/cache": "^1.0.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0@dev", + "mockery/mockery": "~0.9", + "phpspec/phpspec": "~2.1", + "phpunit/phpunit": "~4.1", + "predis/predis": "~1.0", + "tedivm/stash": "~0.12" + }, + "suggest": { + "ext-phpredis": "Pure C implemented extension for PHP" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Cached\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "frankdejonge", + "email": "info@frenky.net" + } + ], + "description": "An adapter decorator to enable meta-data caching.", + "time": "2017-03-20T09:59:34+00:00" + }, { "name": "league/html-to-markdown", "version": "4.6.2", @@ -1674,6 +1911,96 @@ ], "time": "2017-01-23T04:29:33+00:00" }, + { + "name": "nao-pon/flysystem-cached-extra", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/nao-pon/flysystem-cached-extra.git", + "reference": "7e5310984a3ec09d743467ed6d83fb63856e7d0b" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/nao-pon/flysystem-cached-extra/7e5310984a3ec09d743467ed6d83fb63856e7d0b.zip", + "reference": "7e5310984a3ec09d743467ed6d83fb63856e7d0b", + "shasum": "" + }, + "require": { + "league/flysystem-cached-adapter": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hypweb\\Flysystem\\Cached\\Extra\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Naoki Sawada", + "email": "hypweb@gmail.com" + } + ], + "description": "Extra traits for Flysystem cached adapter", + "time": "2016-03-21T08:19:52+00:00" + }, + { + "name": "nao-pon/flysystem-google-drive", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/nao-pon/flysystem-google-drive.git", + "reference": "3b38384854be414061224d8e37bec9ae8a967d52" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/nao-pon/flysystem-google-drive/3b38384854be414061224d8e37bec9ae8a967d52.zip", + "reference": "3b38384854be414061224d8e37bec9ae8a967d52", + "shasum": "" + }, + "require": { + "google/apiclient": "^2.0", + "league/flysystem": "~1.0", + "nao-pon/flysystem-cached-extra": "~1.0", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-api_v2": "1.0.x-dev", + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hypweb\\Flysystem\\GoogleDrive\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Naoki Sawada", + "email": "hypweb@gmail.com" + } + ], + "description": "Flysystem adapter for Google Drive", + "time": "2017-08-18T08:45:18+00:00" + }, { "name": "nesbot/carbon", "version": "1.22.1", @@ -1826,6 +2153,98 @@ ], "time": "2017-09-27T21:40:39+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "2.0.9", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "c9a3fe35e20eb6eeaca716d6a23cde03f52d1558" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/phpseclib/phpseclib/c9a3fe35e20eb6eeaca716d6a23cde03f52d1558.zip", + "reference": "c9a3fe35e20eb6eeaca716d6a23cde03f52d1558", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "~4.0", + "sami/sami": "~2.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "time": "2017-11-29T06:38:08+00:00" + }, { "name": "predis/predis", "version": "v1.1.1", @@ -1876,6 +2295,52 @@ ], "time": "2016-06-16T16:22:20+00:00" }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://files.phpcomposer.com/files/php-fig/cache/d11b50ad223250cf17b86e38383413f5a6764bf8.zip", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, { "name": "psr/container", "version": "1.0.0", From 9d1169b1f1227b68a7d8d28b5118604f3544e7d2 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Wed, 7 Feb 2018 22:35:27 +0800 Subject: [PATCH 08/68] Add google drive filesystem driver --- app/Providers/GoogleDriveServiceProvider.php | 40 ++++++++++++++++++++ config/app.php | 1 + config/filesystems.php | 8 ++++ 3 files changed, 49 insertions(+) create mode 100644 app/Providers/GoogleDriveServiceProvider.php diff --git a/app/Providers/GoogleDriveServiceProvider.php b/app/Providers/GoogleDriveServiceProvider.php new file mode 100644 index 0000000..6d847e1 --- /dev/null +++ b/app/Providers/GoogleDriveServiceProvider.php @@ -0,0 +1,40 @@ +setClientId($config['client_id']); + $client->setClientSecret($config['client_secret']); + $client->refreshToken($config['refresh_token']); + + $adapter = new GoogleDriveAdapter(new \Google_Service_Drive($client), $config['folder_id']); + + return new Filesystem($adapter); + }); + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + // + } +} diff --git a/config/app.php b/config/app.php index 17a5493..cf679a0 100644 --- a/config/app.php +++ b/config/app.php @@ -178,6 +178,7 @@ App\Providers\RouteServiceProvider::class, App\Providers\ComposerServiceProvider::class, \App\Providers\CounterServiceProvider::class, + \App\Providers\GoogleDriveServiceProvider::class, // Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider::class, // Zizaco\Entrust\EntrustServiceProvider::class, diff --git a/config/filesystems.php b/config/filesystems.php index 9568e02..bf22939 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -63,6 +63,14 @@ 'bucket' => env('AWS_BUCKET'), ], + 'google' => [ + 'driver' => 'google', + 'client_id' => env('GOOGLE_DRIVE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_DRIVE_CLIENT_SECRET'), + 'refresh_token' => env('GOOGLE_DRIVE_REFRESH_TOKEN'), + 'folder_id' => env('GOOGLE_DRIVE_FOLDER_ID'), + ], + ], ]; From ee74d93075ab5ffd81c1d756f7cb622a2bcb6fc7 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Thu, 8 Feb 2018 17:44:20 +0800 Subject: [PATCH 09/68] Add humanlized listContents --- .../FlysystemAdapter/GoogleDriveAdapter.php | 83 +++++++++++++++++++ app/Providers/GoogleDriveServiceProvider.php | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php new file mode 100644 index 0000000..7c56b98 --- /dev/null +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -0,0 +1,83 @@ +humanContents(parent::listContents($dirname, $recursive)); + } + + /** + * @param $listContents + * @return array + */ + protected function humanContents($listContents) + { + $dirMaps = $this->prepareMaps($listContents); + + return array_map(function ($meta) use ($dirMaps) { + $pathArray = explode('/', $meta['path']); + array_pop($pathArray); + $humanDirArray = []; + foreach ($pathArray as $fileIdOfDir) { + array_push($humanDirArray, $dirMaps[$fileIdOfDir]); + } + + $basename = $this->formatBaseName($meta['filename'], $meta['extension']); + $meta['path'] = ltrim(implode('/', $humanDirArray) . '/' . $basename, '/'); + + return $meta; + }, $listContents); + } + + /** + * @param $listContents + * @return array + */ + protected function prepareMaps($listContents) + { + $maps = []; + + foreach ($listContents as $meta) { + if ($meta['type'] != 'dir') { + continue; + } + + if ($offset = strrpos($meta['path'], '/')) { + $fileId = substr($meta['path'], $offset + 1); + } else { + $fileId = $meta['path']; + } + + $maps[$fileId] = $meta['filename']; + } + + return $maps; + } + + /** + * @param $filename + * @param string $extension + * @return string + */ + protected function formatBaseName($filename, $extension = '') + { + return rtrim($filename . '.' . $extension, '.'); + } +} \ No newline at end of file diff --git a/app/Providers/GoogleDriveServiceProvider.php b/app/Providers/GoogleDriveServiceProvider.php index 6d847e1..9e46aa3 100644 --- a/app/Providers/GoogleDriveServiceProvider.php +++ b/app/Providers/GoogleDriveServiceProvider.php @@ -2,7 +2,7 @@ namespace App\Providers; -use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter; +use App\Indigo\FlysystemAdapter\GoogleDriveAdapter; use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; use League\Flysystem\Filesystem; From d14e5a51fbcbc370c6db03975571c6845d050794 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Fri, 9 Feb 2018 00:32:04 +0800 Subject: [PATCH 10/68] Add humanlized exists and strength logic --- .../FlysystemAdapter/GoogleDriveAdapter.php | 174 ++++++++++++++++-- 1 file changed, 155 insertions(+), 19 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index 7c56b98..38e575c 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -11,48 +11,143 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter { /** - * List contents of a directory. + * A multidimensional array to store human_path with file_id. + * e.g. + * [ + * root => [ + * foo/bar/file => foo_file_id/bar_file_id/file_id + * ] + * ] + * + * @var array + */ + protected $pathMap = []; + /** + * A multidimensional array to store list contents. + * + * @var array + */ + protected $contents = []; + + /** + * Check whether a file exists. + * + * @param string $path * + * @return array|bool|null + */ + public function has($path) + { + $path = $this->pathToId($path); + + return $path ? parent::has($path) : false; + } + + /** + * @param $humanPath + * @return bool + */ + public function pathToId($humanPath) + { + return $this->getPathMap('', true)[$humanPath] ?? false; + } + + /** * @param string $dirname * @param bool $recursive - * - * @return array + * @return mixed */ - public function listContents($dirname = '', $recursive = false) + public function getPathMap($dirname = '', $recursive = false) { - return $this->humanContents(parent::listContents($dirname, $recursive)); + return $this->pathMap[$this->formatDirname($dirname)] ?? $this->createPathMap($dirname, $recursive); } /** - * @param $listContents - * @return array + * @param $dirname + * @return string + */ + public function formatDirname($dirname) + { + return $dirname ?: 'default'; + } + + /** + * @param string $dirname + * @param bool $recursive + * @return mixed */ - protected function humanContents($listContents) + private function createPathMap($dirname = '', $recursive = false) { - $dirMaps = $this->prepareMaps($listContents); + $this->initializePathMap($dirname); - return array_map(function ($meta) use ($dirMaps) { + $listContents = $this->retrieveListsContents($dirname, $recursive); + + $dirMap = $this->prepareMap($listContents); + + foreach ($listContents as $meta) { $pathArray = explode('/', $meta['path']); array_pop($pathArray); $humanDirArray = []; foreach ($pathArray as $fileIdOfDir) { - array_push($humanDirArray, $dirMaps[$fileIdOfDir]); + array_push($humanDirArray, $dirMap[$fileIdOfDir]); } $basename = $this->formatBaseName($meta['filename'], $meta['extension']); - $meta['path'] = ltrim(implode('/', $humanDirArray) . '/' . $basename, '/'); + $humanPath = ltrim(implode('/', $humanDirArray) . '/' . $basename, '/'); - return $meta; - }, $listContents); + $this->setPathMap($dirname, $humanPath, $meta['path']); + } + + return $this->getPathMap($dirname); + } + + /** + * @param $dirname + */ + private function initializePathMap($dirname) + { + $this->pathMap[$this->formatDirname($dirname)] = []; + } + + /** + * @param string $dirname + * @param bool $recursive + * @return mixed + */ + public function retrieveListsContents($dirname = '', $recursive = false) + { + return $this->contents[$this->formatDirname($dirname)] ?? tap($this->originListContents($dirname, $recursive), + function ($data) use ($dirname) { + $this->setListContents($dirname, $data); + }); + } + + /** + * @param string $dirname + * @param bool $recursive + * @return mixed + */ + public function originListContents($dirname = '', $recursive = false) + { + return parent::listContents($dirname, $recursive); + } + + /** + * @param $dirname + * @param $data + */ + private function setListContents($dirname, $data) + { + $this->contents[$this->formatDirname($dirname)] = $data; } /** * @param $listContents * @return array */ - protected function prepareMaps($listContents) + private function prepareMap($listContents) { - $maps = []; + $map = []; foreach ($listContents as $meta) { if ($meta['type'] != 'dir') { @@ -65,10 +160,10 @@ protected function prepareMaps($listContents) $fileId = $meta['path']; } - $maps[$fileId] = $meta['filename']; + $map[$fileId] = $meta['filename']; } - return $maps; + return $map; } /** @@ -76,8 +171,49 @@ protected function prepareMaps($listContents) * @param string $extension * @return string */ - protected function formatBaseName($filename, $extension = '') + public function formatBaseName($filename, $extension = '') { return rtrim($filename . '.' . $extension, '.'); } + + /** + * @param $dirname + * @param $humanPath + * @param $fileId + */ + private function setPathMap($dirname, $humanPath, $fileId) + { + $this->pathMap[$this->formatDirname($dirname)][$humanPath] = $fileId; + } + + /** + * List contents of a directory. + * + * @param string $dirname + * @param bool $recursive + * + * @return array + */ + public function listContents($dirname = '', $recursive = false) + { + return $this->humanContents($dirname, $recursive); + } + + /** + * @param string $dirname + * @param bool $recursive + * @return array + */ + public function humanContents($dirname = '', $recursive = false) + { + $listContents = $this->retrieveListsContents($dirname, $recursive); + + $reversedPathMap = array_flip($this->getPathMap($dirname, $recursive)); + + return array_map(function ($meta) use ($reversedPathMap) { + $meta['path'] = $reversedPathMap[$meta['path']]; + + return $meta; + }, $listContents); + } } \ No newline at end of file From 310a932cbfb11219fc30ec5948a6c781a9247c07 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Fri, 9 Feb 2018 16:06:01 +0800 Subject: [PATCH 11/68] Fix critical bug created by previous commit --- .../FlysystemAdapter/GoogleDriveAdapter.php | 132 +++++++++++------- 1 file changed, 81 insertions(+), 51 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index 38e575c..ce8a67a 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -11,23 +11,25 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter { /** - * A multidimensional array to store human_path with file_id. - * e.g. - * [ - * root => [ - * foo/bar/file => foo_file_id/bar_file_id/file_id - * ] - * ] + * An array to store human_path with file_id. + * + * e.g. foo/bar/file => foo_file_id/bar_file_id/file_id * * @var array */ protected $pathMap = []; /** - * A multidimensional array to store list contents. + * An array to store all list contents. * * @var array */ protected $contents = []; + /** + * Determine if path map is created. + * + * @var bool + */ + protected $isPathMapCreated = false; /** * Check whether a file exists. @@ -49,40 +51,41 @@ public function has($path) */ public function pathToId($humanPath) { - return $this->getPathMap('', true)[$humanPath] ?? false; + return $this->getPathMap()[$humanPath] ?? false; } /** - * @param string $dirname - * @param bool $recursive - * @return mixed + * @param bool $force + * @return array|mixed */ - public function getPathMap($dirname = '', $recursive = false) + public function getPathMap($force = false) { - return $this->pathMap[$this->formatDirname($dirname)] ?? $this->createPathMap($dirname, $recursive); + return (!$this->isPathMapCreated || $force) ? tap($this->createPathMap(), function () { + $this->pathMapCreated(); + }) : $this->pathMap; } /** - * @param $dirname - * @return string + * @param $data + * @return $this */ - public function formatDirname($dirname) + protected function setPathMap($data) { - return $dirname ?: 'default'; + $this->pathMap = $data; + + return $this; } /** - * @param string $dirname - * @param bool $recursive * @return mixed */ - private function createPathMap($dirname = '', $recursive = false) + protected function createPathMap() { - $this->initializePathMap($dirname); + $listContents = $this->retrieveAllListContents(); - $listContents = $this->retrieveListsContents($dirname, $recursive); + $dirMap = $this->generateDirMap($listContents); - $dirMap = $this->prepareMap($listContents); + $data = []; foreach ($listContents as $meta) { $pathArray = explode('/', $meta['path']); @@ -95,31 +98,30 @@ private function createPathMap($dirname = '', $recursive = false) $basename = $this->formatBaseName($meta['filename'], $meta['extension']); $humanPath = ltrim(implode('/', $humanDirArray) . '/' . $basename, '/'); - $this->setPathMap($dirname, $humanPath, $meta['path']); + $data[$humanPath] = $meta['path']; } - return $this->getPathMap($dirname); + return tap($data, function ($data) { + $this->setPathMap($data); + }); } /** - * @param $dirname + * @return mixed */ - private function initializePathMap($dirname) + public function retrieveAllListContents() { - $this->pathMap[$this->formatDirname($dirname)] = []; + return $this->contents ?: tap($this->allListContents(), function ($data) { + $this->setListContents($data); + }); } /** - * @param string $dirname - * @param bool $recursive * @return mixed */ - public function retrieveListsContents($dirname = '', $recursive = false) + public function allListContents() { - return $this->contents[$this->formatDirname($dirname)] ?? tap($this->originListContents($dirname, $recursive), - function ($data) use ($dirname) { - $this->setListContents($dirname, $data); - }); + return $this->originListContents('', true); } /** @@ -133,19 +135,18 @@ public function originListContents($dirname = '', $recursive = false) } /** - * @param $dirname * @param $data */ - private function setListContents($dirname, $data) + protected function setListContents($data) { - $this->contents[$this->formatDirname($dirname)] = $data; + $this->contents = $data; } /** * @param $listContents * @return array */ - private function prepareMap($listContents) + private function generateDirMap($listContents) { $map = []; @@ -177,13 +178,11 @@ public function formatBaseName($filename, $extension = '') } /** - * @param $dirname - * @param $humanPath - * @param $fileId + * @param bool $status */ - private function setPathMap($dirname, $humanPath, $fileId) + protected function pathMapCreated($status = true) { - $this->pathMap[$this->formatDirname($dirname)][$humanPath] = $fileId; + $this->isPathMapCreated = $status; } /** @@ -196,19 +195,19 @@ private function setPathMap($dirname, $humanPath, $fileId) */ public function listContents($dirname = '', $recursive = false) { - return $this->humanContents($dirname, $recursive); + return $this->humanListContents($dirname, $recursive); } /** - * @param string $dirname + * @param string $directory * @param bool $recursive * @return array */ - public function humanContents($dirname = '', $recursive = false) + public function humanListContents($directory = '', $recursive = false) { - $listContents = $this->retrieveListsContents($dirname, $recursive); + $listContents = $this->filter($this->retrieveAllListContents(), $directory, $recursive); - $reversedPathMap = array_flip($this->getPathMap($dirname, $recursive)); + $reversedPathMap = array_flip($this->getPathMap()); return array_map(function ($meta) use ($reversedPathMap) { $meta['path'] = $reversedPathMap[$meta['path']]; @@ -216,4 +215,35 @@ public function humanContents($dirname = '', $recursive = false) return $meta; }, $listContents); } -} \ No newline at end of file + + /** + * @param $contents + * @param $directory + * @param bool $recursive + * @return array + */ + private function filter($contents, $directory, $recursive = false) + { + $directoryLength = strlen($directory); + + $reversedPathMap = array_flip($this->getPathMap()); + + return array_filter($contents, + function ($meta) use ($recursive, $directory, $directoryLength, $reversedPathMap) { + $humanPath = $reversedPathMap[$meta['path']]; + + if ($directoryLength == 0) { + // Base 'root' folder + return $recursive ? true : (substr_count($humanPath, '/') == 0 ? true : false); + } + + if (substr($humanPath, 0, $directoryLength) === (string)$directory) { + $separatorCount = substr_count(substr($humanPath, $directoryLength), '/'); + + return (!$recursive && ($separatorCount == 1)) || ($recursive && $separatorCount != 0); + } + + return false; + }); + } +} From cd7e1a8a465f3ee32d275237134930e365cdc6e5 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Fri, 9 Feb 2018 17:12:05 +0800 Subject: [PATCH 12/68] Update implementation of League\Flysystem\ReadInterface --- .../FlysystemAdapter/GoogleDriveAdapter.php | 97 ++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index ce8a67a..8173871 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -40,9 +40,102 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter */ public function has($path) { - $path = $this->pathToId($path); + return $this->callParentMethod($path); + } + + /** + * @param $humanPath + * @return array|bool|null + */ + protected function callParentMethod($humanPath) + { + $method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; + + return ($fileId = $this->pathToId($humanPath)) ? parent::{$method}($fileId) : false; + } + + /** + * Read a file. + * + * @param string $path + * + * @return array|false + */ + public function read($path) + { + return $this->callParentMethod($path); + } + + /** + * Read a file as a stream. + * + * @param string $path + * + * @return array|false + */ + public function readStream($path) + { + return $this->callParentMethod($path); + } - return $path ? parent::has($path) : false; + /** + * Get all the meta data of a file or directory. + * + * @param string $path + * + * @return array|false + */ + public function getMetadata($path) + { + return $this->callParentMethod($path); + } + + /** + * Get all the meta data of a file or directory. + * + * @param string $path + * + * @return array|false + */ + public function getSize($path) + { + return $this->callParentMethod($path); + } + + /** + * Get the mimetype of a file. + * + * @param string $path + * + * @return array|false + */ + public function getMimetype($path) + { + return $this->callParentMethod($path); + } + + /** + * Get the timestamp of a file. + * + * @param string $path + * + * @return array|false + */ + public function getTimestamp($path) + { + return $this->callParentMethod($path); + } + + /** + * Get the visibility of a file. + * + * @param string $path + * + * @return array|false + */ + public function getVisibility($path) + { + return $this->callParentMethod($path); } /** From 8a7a4cb1a276e76b3a43d00273c2c826a5c74254 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 11 Feb 2018 22:22:38 +0800 Subject: [PATCH 13/68] Fix bug of getMetadata --- .../FlysystemAdapter/GoogleDriveAdapter.php | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index 8173871..a36f7d0 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -90,42 +90,6 @@ public function getMetadata($path) return $this->callParentMethod($path); } - /** - * Get all the meta data of a file or directory. - * - * @param string $path - * - * @return array|false - */ - public function getSize($path) - { - return $this->callParentMethod($path); - } - - /** - * Get the mimetype of a file. - * - * @param string $path - * - * @return array|false - */ - public function getMimetype($path) - { - return $this->callParentMethod($path); - } - - /** - * Get the timestamp of a file. - * - * @param string $path - * - * @return array|false - */ - public function getTimestamp($path) - { - return $this->callParentMethod($path); - } - /** * Get the visibility of a file. * From 574e0979838b78748b2c4e4138c665e464d669a1 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Sun, 11 Feb 2018 23:48:09 +0800 Subject: [PATCH 14/68] Update GoogleDriveAdapter 'get' related methods with override --- .../FlysystemAdapter/GoogleDriveAdapter.php | 237 ++++++++++++++---- 1 file changed, 184 insertions(+), 53 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index a36f7d0..3f31f51 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -3,6 +3,7 @@ namespace App\Indigo\FlysystemAdapter; use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter as BaseGoogleDriveAdapter; +use League\Flysystem\Config; /** * Class GoogleDriveAdapter @@ -31,29 +32,6 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter */ protected $isPathMapCreated = false; - /** - * Check whether a file exists. - * - * @param string $path - * - * @return array|bool|null - */ - public function has($path) - { - return $this->callParentMethod($path); - } - - /** - * @param $humanPath - * @return array|bool|null - */ - protected function callParentMethod($humanPath) - { - $method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; - - return ($fileId = $this->pathToId($humanPath)) ? parent::{$method}($fileId) : false; - } - /** * Read a file. * @@ -67,39 +45,15 @@ public function read($path) } /** - * Read a file as a stream. - * - * @param string $path - * - * @return array|false - */ - public function readStream($path) - { - return $this->callParentMethod($path); - } - - /** - * Get all the meta data of a file or directory. - * - * @param string $path - * - * @return array|false + * @param $humanPath + * @param array $args + * @return array|bool|null */ - public function getMetadata($path) + protected function callParentMethod($humanPath, ...$args) { - return $this->callParentMethod($path); - } + $method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; - /** - * Get the visibility of a file. - * - * @param string $path - * - * @return array|false - */ - public function getVisibility($path) - { - return $this->callParentMethod($path); + return ($fileId = $this->pathToId($humanPath)) ? parent::{$method}($fileId, ...$args) : false; } /** @@ -242,6 +196,42 @@ protected function pathMapCreated($status = true) $this->isPathMapCreated = $status; } + /** + * Read a file as a stream. + * + * @param string $path + * + * @return array|false + */ + public function readStream($path) + { + return $this->callParentMethod($path); + } + + /** + * Get all the meta data of a file or directory. + * + * @param string $path + * + * @return array|false + */ + public function getMetadata($path) + { + return $this->callParentMethod($path); + } + + /** + * Get the visibility of a file. + * + * @param string $path + * + * @return array|false + */ + public function getVisibility($path) + { + return $this->callParentMethod($path); + } + /** * List contents of a directory. * @@ -303,4 +293,145 @@ function ($meta) use ($recursive, $directory, $directoryLength, $reversedPathMap return false; }); } + + /** + * Write a new file. + * + * @param string $path + * @param string $contents + * @param Config $config + * Config object + * + * @return array|false false on failure file meta data on success + */ + public function write($path, $contents, Config $config) + { + if ($this->has($path)) { + // e.g. file_id_1/file_id_2 + return $this->updateContent($path, $contents, $config); + } + + return $this->createContent($path, $contents, $config); + } + + /** + * Check whether a file exists. + * + * @param string $path + * + * @return array|bool|null + */ + public function has($path) + { + return $this->callParentMethod($path); + } + + /** + * @param $humanPath + * @param $contents + * @param $config + * @return array|false + */ + private function updateContent($humanPath, $contents, $config) + { + return parent::write($this->pathToId($humanPath), $contents, $config); + } + + /** + * @param $humanPath + * @param $contents + * @param $config + * @return array|false + */ + private function createContent($humanPath, $contents, $config) + { + $path = $this->getNewFilename($humanPath); + + return parent::write($path, $contents, $config); + } + + /** + * @param $humanPath + * @return string + */ + public function getNewFilename($humanPath): string + { + if (($offset = strrpos($humanPath, '/')) !== false) { + // e.g. file_id_1/foo.txt + $path = $this->pathToId(substr($humanPath, 0, $offset)) . '/' . substr($humanPath, $offset + 1); + } else { + // e.g. foo.txt + $path = $humanPath; + } + return $path; + } + + /** + * Rename a file. + * + * @param string $from + * @param string $to + * + * @return bool + */ + public function rename($from, $to) + { + return parent::rename($this->pathToId($from), $this->getNewFilename($to)); + } + + /** + * Copy a file. + * + * @param string $from + * @param string $to + * + * @return bool + */ + public function copy($from, $to) + { + return parent::copy($this->pathToId($from), $this->getNewFilename($to)); + } + + /** + * Delete a file. + * + * @param string $humanPath + * + * @return bool + */ + public function delete($humanPath) + { + return $this->callParentMethod($humanPath); + } + + /** + * Create a directory. + * + * @param string $dirname + * directory name + * @param Config $config + * + * @return array|false + */ + public function createDir($dirname, Config $config) + { + if ($this->has($dirname)) { + return false; + } + + return parent::createDir($this->getNewFilename($dirname), $config); + } + + /** + * Set the visibility for a file. + * + * @param string $humanPath + * @param string $visibility + * + * @return array|false file meta data + */ + public function setVisibility($humanPath, $visibility) + { + return $this->callParentMethod($humanPath, $visibility); + } } From 5cb5908a547520f192312a8a39fa8a7b0d095847 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 12:43:02 +0800 Subject: [PATCH 15/68] Update logic of distinguishing path between file and dir --- .../FlysystemAdapter/GoogleDriveAdapter.php | 212 ++++++++++++++---- 1 file changed, 170 insertions(+), 42 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index 3f31f51..5a98db0 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -2,8 +2,10 @@ namespace App\Indigo\FlysystemAdapter; +use Exception; use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter as BaseGoogleDriveAdapter; use League\Flysystem\Config; +use League\Flysystem\FileNotFoundException; /** * Class GoogleDriveAdapter @@ -11,6 +13,18 @@ */ class GoogleDriveAdapter extends BaseGoogleDriveAdapter { + /** + * + */ + const TYPE_ALL = 'all'; + /** + * + */ + const TYPE_FILE = 'file'; + /** + * + */ + const TYPE_DIR = 'dir'; /** * An array to store human_path with file_id. * @@ -18,7 +32,15 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter * * @var array */ - protected $pathMap = []; + protected $filePathMap = []; + /** + * An array to store human_path with file_id. + * + * e.g. foo/bar/file => foo_file_id/bar_file_id + * + * @var array + */ + protected $dirPathMap = []; /** * An array to store all list contents. * @@ -41,50 +63,56 @@ class GoogleDriveAdapter extends BaseGoogleDriveAdapter */ public function read($path) { - return $this->callParentMethod($path); + return $this->parsePathAndCallParent($path, self::TYPE_FILE); } /** * @param $humanPath + * @param string $type * @param array $args * @return array|bool|null */ - protected function callParentMethod($humanPath, ...$args) + protected function parsePathAndCallParent($humanPath, $type = self::TYPE_ALL, ...$args) { $method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; - return ($fileId = $this->pathToId($humanPath)) ? parent::{$method}($fileId, ...$args) : false; + return ($fileId = $this->pathToId($humanPath, $type)) ? parent::{$method}($fileId, ...$args) : false; } /** * @param $humanPath + * @param string $type * @return bool */ - public function pathToId($humanPath) + public function pathToId($humanPath, $type = self::TYPE_ALL) { - return $this->getPathMap()[$humanPath] ?? false; + return $this->getPathMap($type)[$humanPath] ?? false; } /** + * @param string $type * @param bool $force * @return array|mixed */ - public function getPathMap($force = false) + public function getPathMap($type = self::TYPE_ALL, $force = false) { - return (!$this->isPathMapCreated || $force) ? tap($this->createPathMap(), function () { - $this->pathMapCreated(); - }) : $this->pathMap; - } + if (!$this->isPathMapCreated || $force) { + list($dirPathMap, $filePathMap) = $this->createPathMap(); + } else { + $dirPathMap = $this->getDirPathMap(); + $filePathMap = $this->getFilePathMap(); + } - /** - * @param $data - * @return $this - */ - protected function setPathMap($data) - { - $this->pathMap = $data; + switch ($type) { + case self::TYPE_ALL: + return array_merge($dirPathMap, $filePathMap); + case self::TYPE_FILE: + return $filePathMap; + case self::TYPE_DIR: + return $dirPathMap; + } - return $this; + return []; } /** @@ -96,7 +124,8 @@ protected function createPathMap() $dirMap = $this->generateDirMap($listContents); - $data = []; + $dirPathMap = []; + $filePathMap = []; foreach ($listContents as $meta) { $pathArray = explode('/', $meta['path']); @@ -109,11 +138,17 @@ protected function createPathMap() $basename = $this->formatBaseName($meta['filename'], $meta['extension']); $humanPath = ltrim(implode('/', $humanDirArray) . '/' . $basename, '/'); - $data[$humanPath] = $meta['path']; + if ($meta['type'] == self::TYPE_FILE) { + $filePathMap[$humanPath] = $meta['path']; + } else { + $dirPathMap[$humanPath] = $meta['path']; + } } - return tap($data, function ($data) { - $this->setPathMap($data); + return tap([$dirPathMap, $filePathMap], function () use ($dirPathMap, $filePathMap) { + $this->setDirPathMap($dirPathMap); + $this->setFilePathMap($filePathMap); + $this->pathMapCreated(); }); } @@ -188,6 +223,22 @@ public function formatBaseName($filename, $extension = '') return rtrim($filename . '.' . $extension, '.'); } + /** + * @param array $data + */ + private function setDirPathMap(array $data) + { + $this->dirPathMap = $data; + } + + /** + * @param array $data + */ + private function setFilePathMap(array $data) + { + $this->filePathMap = $data; + } + /** * @param bool $status */ @@ -196,6 +247,22 @@ protected function pathMapCreated($status = true) $this->isPathMapCreated = $status; } + /** + * @return array + */ + public function getDirPathMap(): array + { + return $this->dirPathMap; + } + + /** + * @return array + */ + public function getFilePathMap(): array + { + return $this->filePathMap; + } + /** * Read a file as a stream. * @@ -205,7 +272,7 @@ protected function pathMapCreated($status = true) */ public function readStream($path) { - return $this->callParentMethod($path); + return $this->parsePathAndCallParent($path, self::TYPE_FILE); } /** @@ -217,7 +284,7 @@ public function readStream($path) */ public function getMetadata($path) { - return $this->callParentMethod($path); + return $this->parsePathAndCallParent($path); } /** @@ -229,7 +296,7 @@ public function getMetadata($path) */ public function getVisibility($path) { - return $this->callParentMethod($path); + return $this->parsePathAndCallParent($path); } /** @@ -303,10 +370,17 @@ function ($meta) use ($recursive, $directory, $directoryLength, $reversedPathMap * Config object * * @return array|false false on failure file meta data on success + * @throws \Exception */ public function write($path, $contents, Config $config) { - if ($this->has($path)) { + // Since there no distinction between file and dir, so must check type + // before write or update a file. + if ($this->existsDir($path)) { + throw new Exception("$path is a directory"); + } + + if ($this->existsFile($path)) { // e.g. file_id_1/file_id_2 return $this->updateContent($path, $contents, $config); } @@ -315,15 +389,21 @@ public function write($path, $contents, Config $config) } /** - * Check whether a file exists. - * - * @param string $path - * - * @return array|bool|null + * @param $humanPath + * @return bool */ - public function has($path) + private function existsDir($humanPath) { - return $this->callParentMethod($path); + return array_key_exists($humanPath, $this->getDirPathMap()); + } + + /** + * @param $humanPath + * @return bool + */ + private function existsFile($humanPath) + { + return array_key_exists($humanPath, $this->getFilePathMap()); } /** @@ -334,7 +414,7 @@ public function has($path) */ private function updateContent($humanPath, $contents, $config) { - return parent::write($this->pathToId($humanPath), $contents, $config); + return parent::write($this->pathToId($humanPath, self::TYPE_FILE), $contents, $config); } /** @@ -356,9 +436,11 @@ private function createContent($humanPath, $contents, $config) */ public function getNewFilename($humanPath): string { + // TODO foo_exists/bar_not_exists/file will be not created. if (($offset = strrpos($humanPath, '/')) !== false) { // e.g. file_id_1/foo.txt - $path = $this->pathToId(substr($humanPath, 0, $offset)) . '/' . substr($humanPath, $offset + 1); + $path = $this->pathToId(substr($humanPath, 0, $offset), self::TYPE_DIR) . '/' . substr($humanPath, + $offset + 1); } else { // e.g. foo.txt $path = $humanPath; @@ -366,6 +448,19 @@ public function getNewFilename($humanPath): string return $path; } + /** + * Check whether a file or a directory exists. + * + * @param string $path + * + * @param string $type + * @return array|bool|null + */ + public function has($path, $type = self::TYPE_ALL) + { + return $this->parsePathAndCallParent($path, $type); + } + /** * Rename a file. * @@ -393,15 +488,45 @@ public function copy($from, $to) } /** - * Delete a file. + * Delete a directory. * - * @param string $humanPath + * @param string $dirname + * + * @return bool + * @throws \League\Flysystem\FileNotFoundException + */ + public function deleteDir($dirname) + { + return $this->delete($dirname, self::TYPE_DIR); + } + + /** + * Delete a file (or a directory). * + * @param string $humanPath + * @param string $type * @return bool + * @throws \League\Flysystem\FileNotFoundException */ - public function delete($humanPath) + public function delete($humanPath, $type = self::TYPE_FILE) { - return $this->callParentMethod($humanPath); + $this->assertPresent($humanPath, $type); + + return $this->parsePathAndCallParent($humanPath, $type); + } + + /** + * @param $humanPath + * @param $type + * @return void + * @throws \League\Flysystem\FileNotFoundException + */ + protected function assertPresent($humanPath, $type) + { + if ((($type == self::TYPE_FILE) && !$this->existsFile($humanPath)) + || (($type == self::TYPE_DIR) && !$this->existsDir($humanPath))) { + throw new FileNotFoundException($humanPath); + } } /** @@ -415,10 +540,13 @@ public function delete($humanPath) */ public function createDir($dirname, Config $config) { - if ($this->has($dirname)) { + // Since there no distinction between file and dir, we prevent from + // creating the same name of dir(file) by default. + if ($this->existsDir($dirname) || $this->existsFile($dirname)) { return false; } + // TODO create parent dir recursively, maybe google api is supported? return parent::createDir($this->getNewFilename($dirname), $config); } @@ -432,6 +560,6 @@ public function createDir($dirname, Config $config) */ public function setVisibility($humanPath, $visibility) { - return $this->callParentMethod($humanPath, $visibility); + return $this->parsePathAndCallParent($humanPath, self::TYPE_ALL, $visibility); } } From 69396b4304036c110a1bdee03330fe3cb92fcc88 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 15:52:19 +0800 Subject: [PATCH 16/68] Update handler with making un-existing directories recursively --- app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php index 5a98db0..5906a23 100644 --- a/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php +++ b/app/Indigo/FlysystemAdapter/GoogleDriveAdapter.php @@ -422,6 +422,7 @@ private function updateContent($humanPath, $contents, $config) * @param $contents * @param $config * @return array|false + * @throws \Exception */ private function createContent($humanPath, $contents, $config) { @@ -433,14 +434,18 @@ private function createContent($humanPath, $contents, $config) /** * @param $humanPath * @return string + * @throws \Exception */ public function getNewFilename($humanPath): string { - // TODO foo_exists/bar_not_exists/file will be not created. if (($offset = strrpos($humanPath, '/')) !== false) { + $dirPath = substr($humanPath, 0, $offset); + if (!$dirId = $this->pathToId($dirPath, self::TYPE_DIR)) { + // TODO foo_exists/bar_not_exists/file will be not created. + throw new Exception("Directory {$dirPath} is not existing, please create it at first."); + } // e.g. file_id_1/foo.txt - $path = $this->pathToId(substr($humanPath, 0, $offset), self::TYPE_DIR) . '/' . substr($humanPath, - $offset + 1); + $path = $dirId . '/' . substr($humanPath, $offset + 1); } else { // e.g. foo.txt $path = $humanPath; @@ -468,6 +473,7 @@ public function has($path, $type = self::TYPE_ALL) * @param string $to * * @return bool + * @throws \Exception */ public function rename($from, $to) { @@ -481,6 +487,7 @@ public function rename($from, $to) * @param string $to * * @return bool + * @throws \Exception */ public function copy($from, $to) { @@ -537,6 +544,7 @@ protected function assertPresent($humanPath, $type) * @param Config $config * * @return array|false + * @throws \Exception */ public function createDir($dirname, Config $config) { From 9088c541b2506867604651461beb336ff8fd20c0 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 16:10:35 +0800 Subject: [PATCH 17/68] Add google disk to store backup --- config/backup.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/config/backup.php b/config/backup.php index 9df21ea..784fcf2 100644 --- a/config/backup.php +++ b/config/backup.php @@ -64,6 +64,7 @@ */ 'disks' => [ 'local', + 'google' ], ], ], @@ -78,12 +79,12 @@ 'notifications' => [ 'notifications' => [ - \Spatie\Backup\Notifications\Notifications\BackupHasFailed::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFound::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\CleanupHasFailed::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\BackupWasSuccessful::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFound::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessful::class => ['mail'], + \Spatie\Backup\Notifications\Notifications\BackupHasFailed::class => ['mail', 'slack'], + \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFound::class => ['mail', 'slack'], + \Spatie\Backup\Notifications\Notifications\CleanupHasFailed::class => ['mail', 'slack'], + \Spatie\Backup\Notifications\Notifications\BackupWasSuccessful::class => ['slack'], + \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFound::class => ['slack'], + \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessful::class => ['slack'], ], /* @@ -97,12 +98,12 @@ ], 'slack' => [ - 'webhook_url' => '', + 'webhook_url' => env('SLACK_MSG_URL'), /* * If this is set to null the default channel of the webhook will be used. */ - 'channel' => null, + 'channel' => env('SLACK_MSG_CHANNEL'), ], ], @@ -114,7 +115,7 @@ 'monitorBackups' => [ [ 'name' => config('app.name'), - 'disks' => ['local'], + 'disks' => ['local', 'google'], 'newestBackupsShouldNotBeOlderThanDays' => 1, 'storageUsedMayNotBeHigherThanMegabytes' => 5000, ], From 375d43b2d697940c730b07e7b2ea9d43438d2fd6 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 21:50:48 +0800 Subject: [PATCH 18/68] Add ide-helper config --- config/ide-helper.php | 179 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 config/ide-helper.php diff --git a/config/ide-helper.php b/config/ide-helper.php new file mode 100644 index 0000000..8423331 --- /dev/null +++ b/config/ide-helper.php @@ -0,0 +1,179 @@ + '_ide_helper', + 'format' => 'php', + + /* + |-------------------------------------------------------------------------- + | Fluent helpers + |-------------------------------------------------------------------------- + | + | Set to true to generate commonly used Fluent methods + | + */ + + 'include_fluent' => true, + + /* + |-------------------------------------------------------------------------- + | Write Model Magic methods + |-------------------------------------------------------------------------- + | + | Set to false to disable write magic methods of model + | + */ + + 'write_model_magic_where' => true, + + /* + |-------------------------------------------------------------------------- + | Helper files to include + |-------------------------------------------------------------------------- + | + | Include helper files. By default not included, but can be toggled with the + | -- helpers (-H) option. Extra helper files can be included. + | + */ + + 'include_helpers' => false, + + 'helper_files' => array( + base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + ), + + /* + |-------------------------------------------------------------------------- + | Model locations to include + |-------------------------------------------------------------------------- + | + | Define in which directories the ide-helper:models command should look + | for models. + | + */ + + 'model_locations' => array( + 'app/Models', + ), + + + /* + |-------------------------------------------------------------------------- + | Extra classes + |-------------------------------------------------------------------------- + | + | These implementations are not really extended, but called with magic functions + | + */ + + 'extra' => array( + 'Eloquent' => array('Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'), + 'Session' => array('Illuminate\Session\Store'), + ), + + 'magic' => array( + 'Log' => array( + 'debug' => 'Monolog\Logger::addDebug', + 'info' => 'Monolog\Logger::addInfo', + 'notice' => 'Monolog\Logger::addNotice', + 'warning' => 'Monolog\Logger::addWarning', + 'error' => 'Monolog\Logger::addError', + 'critical' => 'Monolog\Logger::addCritical', + 'alert' => 'Monolog\Logger::addAlert', + 'emergency' => 'Monolog\Logger::addEmergency', + ) + ), + + /* + |-------------------------------------------------------------------------- + | Interface implementations + |-------------------------------------------------------------------------- + | + | These interfaces will be replaced with the implementing class. Some interfaces + | are detected by the helpers, others can be listed below. + | + */ + + 'interfaces' => array( + + ), + + /* + |-------------------------------------------------------------------------- + | Support for custom DB types + |-------------------------------------------------------------------------- + | + | This setting allow you to map any custom database type (that you may have + | created using CREATE TYPE statement or imported using database plugin + | / extension to a Doctrine type. + | + | Each key in this array is a name of the Doctrine2 DBAL Platform. Currently valid names are: + | 'postgresql', 'db2', 'drizzle', 'mysql', 'oracle', 'sqlanywhere', 'sqlite', 'mssql' + | + | This name is returned by getName() method of the specific Doctrine/DBAL/Platforms/AbstractPlatform descendant + | + | The value of the array is an array of type mappings. Key is the name of the custom type, + | (for example, "jsonb" from Postgres 9.4) and the value is the name of the corresponding Doctrine2 type (in + | our case it is 'json_array'. Doctrine types are listed here: + | http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html + | + | So to support jsonb in your models when working with Postgres, just add the following entry to the array below: + | + | "postgresql" => array( + | "jsonb" => "json_array", + | ), + | + */ + 'custom_db_types' => array( + + ), + + /* + |-------------------------------------------------------------------------- + | Support for camel cased models + |-------------------------------------------------------------------------- + | + | There are some Laravel packages (such as Eloquence) that allow for accessing + | Eloquent model properties via camel case, instead of snake case. + | + | Enabling this option will support these packages by saving all model + | properties as camel case, instead of snake case. + | + | For example, normally you would see this: + | + | * @property \Carbon\Carbon $created_at + | * @property \Carbon\Carbon $updated_at + | + | With this enabled, the properties will be this: + | + | * @property \Carbon\Carbon $createdAt + | * @property \Carbon\Carbon $updatedAt + | + | Note, it is currently an all-or-nothing option. + | + */ + 'model_camel_case_properties' => false, + + /* + |-------------------------------------------------------------------------- + | Property Casts + |-------------------------------------------------------------------------- + | + | Cast the given "real type" to the given "type". + | + */ + 'type_overrides' => array( + 'integer' => 'int', + 'boolean' => 'bool', + ), +); From 55cd2c0bb2dcfb79a6e91176c7495c81ead4a451 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 22:19:57 +0800 Subject: [PATCH 19/68] Add migration, seeder, factory, model of Page --- app/Models/Page.php | 47 +++++++++++++++++++ database/factories/PageFactory.php | 20 ++++++++ .../2018_02_12_213601_create_pages_table.php | 41 ++++++++++++++++ database/seeds/PagesTableSeeder.php | 23 +++++++++ 4 files changed, 131 insertions(+) create mode 100644 app/Models/Page.php create mode 100644 database/factories/PageFactory.php create mode 100644 database/migrations/2018_02_12_213601_create_pages_table.php create mode 100644 database/seeds/PagesTableSeeder.php diff --git a/app/Models/Page.php b/app/Models/Page.php new file mode 100644 index 0000000..76a52a2 --- /dev/null +++ b/app/Models/Page.php @@ -0,0 +1,47 @@ + 'boolean' + ]; + + /** + * @return string + */ + public function getMarkdownContent() + { + // TODO: Implement getMarkdownContent() method. + } +} diff --git a/database/factories/PageFactory.php b/database/factories/PageFactory.php new file mode 100644 index 0000000..2683990 --- /dev/null +++ b/database/factories/PageFactory.php @@ -0,0 +1,20 @@ +define(App\Models\Page::class, function (Faker $faker) { + $title = $faker->unique()->sentence(mt_rand(3, 6)); + + return [ + 'title' => $title, + 'user_id' => \App\Models\User::inRandomOrder()->first()->id, + 'description' => $faker->sentence(10), + 'slug' => str_slug($title), + 'content_id' => function () { + return factory(\App\Models\Content::class)->create()->id; + }, + 'view_count' => mt_rand(0, 10000), + 'is_draft' => $faker->boolean, + 'deleted_at' => $faker->optional(0.3)->dateTime() + ]; +}); diff --git a/database/migrations/2018_02_12_213601_create_pages_table.php b/database/migrations/2018_02_12_213601_create_pages_table.php new file mode 100644 index 0000000..973695f --- /dev/null +++ b/database/migrations/2018_02_12_213601_create_pages_table.php @@ -0,0 +1,41 @@ +increments('id'); + $table->integer('user_id')->unsigned()->index(); + $table->foreign('user_id')->references('id')->on('users'); + $table->string('title'); + $table->string('description')->nullable(); + $table->string('slug'); + $table->integer('content_id')->unsigned()->index(); + $table->foreign('content_id')->references('id')->on('contents'); + $table->integer('view_count')->unsigned()->default(0); + $table->boolean('is_draft')->default(false); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('pages'); + } +} diff --git a/database/seeds/PagesTableSeeder.php b/database/seeds/PagesTableSeeder.php new file mode 100644 index 0000000..bc2a766 --- /dev/null +++ b/database/seeds/PagesTableSeeder.php @@ -0,0 +1,23 @@ +create([ + 'title' => 'about', + 'slug' => 'about', + 'is_draft' => false, + 'deleted_at' => null + ]); + + factory(\App\Models\Page::class, 5)->create(); + } +} From 7df6bd71df845ba64c01edc415b850de06d08aa8 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Mon, 12 Feb 2018 22:34:51 +0800 Subject: [PATCH 20/68] Add repository of Page --- app/Providers/RepositoryServiceProvider.php | 1 + app/Repositories/Contracts/PageRepository.php | 14 +++++++++++ .../Eloquent/PageRepositoryEloquent.php | 24 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 app/Repositories/Contracts/PageRepository.php create mode 100644 app/Repositories/Eloquent/PageRepositoryEloquent.php diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index 3ce802d..d87e72c 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -22,6 +22,7 @@ class RepositoryServiceProvider extends ServiceProvider \App\Repositories\Contracts\PostRepository::class => \App\Repositories\Eloquent\PostRepositoryEloquent::class, \App\Repositories\Contracts\VisitorRepository::class => \App\Repositories\Eloquent\VisitorRepositoryEloquent::class, \App\Repositories\Contracts\SettingRepository::class => \App\Repositories\Eloquent\SettingRepositoryEloquent::class, + \App\Repositories\Contracts\PageRepository::class => \App\Repositories\Eloquent\PageRepositoryEloquent::class, ]; /** diff --git a/app/Repositories/Contracts/PageRepository.php b/app/Repositories/Contracts/PageRepository.php new file mode 100644 index 0000000..eaf550f --- /dev/null +++ b/app/Repositories/Contracts/PageRepository.php @@ -0,0 +1,14 @@ + Date: Mon, 12 Feb 2018 23:04:31 +0800 Subject: [PATCH 21/68] Add controller, formRequest of Page --- .../Controllers/Backend/PageController.php | 144 ++++++++++++++++++ .../Controllers/Frontend/PageController.php | 41 +++++ app/Http/Requests/StoreUpdatePageRequest.php | 52 +++++++ 3 files changed, 237 insertions(+) create mode 100644 app/Http/Controllers/Backend/PageController.php create mode 100644 app/Http/Controllers/Frontend/PageController.php create mode 100644 app/Http/Requests/StoreUpdatePageRequest.php diff --git a/app/Http/Controllers/Backend/PageController.php b/app/Http/Controllers/Backend/PageController.php new file mode 100644 index 0000000..e883740 --- /dev/null +++ b/app/Http/Controllers/Backend/PageController.php @@ -0,0 +1,144 @@ +pageRepository = $pageRepository; + } + + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function index(Request $request) + { + // TODO to be refactor + if ($request->has('trash')) { + $this->pageRepository->onlyTrashed(); + } + + $pages = $this->pageRepository->paginate(); + + return view('admin.pages.index', compact('pages')); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + return view('admin.pages.create'); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\StoreUpdatePageRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(StoreUpdatePageRequest $request) + { + $page = $this->pageRepository->create($request->all()); + + return $this->successCreated($page); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function show($id) + { + $page = $this->pageRepository->find($id); + + return response($page); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function edit($id) + { + $page = $this->pageRepository->find($id); + + return view('admin.pages.edit', compact('page')); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\StoreUpdatePageRequest $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function update(StoreUpdatePageRequest $request, $id) + { + $page = $this->pageRepository->update($request->all(), $id); + + return $this->successCreated($page); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function destroy($id) + { + $this->pageRepository->delete($id); + + return $this->successDeleted(); + } + + /** + * @param $id + * @return \Illuminate\Http\JsonResponse + */ + public function restore($id) + { + $this->pageRepository->restore($id); + + return $this->successNoContent(); + } + + /** + * @param $id + * @return \Illuminate\Http\JsonResponse + */ + public function forceDelete($id) + { + $this->pageRepository->forceDelete($id); + + return $this->successDeleted(); + } +} diff --git a/app/Http/Controllers/Frontend/PageController.php b/app/Http/Controllers/Frontend/PageController.php new file mode 100644 index 0000000..fd83270 --- /dev/null +++ b/app/Http/Controllers/Frontend/PageController.php @@ -0,0 +1,41 @@ +pageRepository = $pageRepository; + $this->disableApiResource($this->pageRepository); + } + + /** + * @param $slug + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function show($slug) + { + $page = $this->pageRepository->getBySlug($slug); + + event(new ViewedEvent($page)); + + return view('pages.show', compact('page')); + } +} diff --git a/app/Http/Requests/StoreUpdatePageRequest.php b/app/Http/Requests/StoreUpdatePageRequest.php new file mode 100644 index 0000000..1778b63 --- /dev/null +++ b/app/Http/Requests/StoreUpdatePageRequest.php @@ -0,0 +1,52 @@ + 'required', + 'slug' => 'unique:pages', + 'body' => 'required', + ]; + + switch ($this->method()) { + case 'PUT': + case 'PATCH': + $rules = array_merge($rules, [ + 'slug' => [ + Rule::unique('pages')->ignore($this->route('page')) + ] + ]); + break; + case 'POST': + default: + } + + return $rules; + } +} From fe1835ba4c6de38b994e0b526599017a8f20353b Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 22:00:22 +0800 Subject: [PATCH 22/68] Add unique index to page's slug --- database/migrations/2018_02_12_213601_create_pages_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2018_02_12_213601_create_pages_table.php b/database/migrations/2018_02_12_213601_create_pages_table.php index 973695f..2a67fa1 100644 --- a/database/migrations/2018_02_12_213601_create_pages_table.php +++ b/database/migrations/2018_02_12_213601_create_pages_table.php @@ -19,7 +19,7 @@ public function up() $table->foreign('user_id')->references('id')->on('users'); $table->string('title'); $table->string('description')->nullable(); - $table->string('slug'); + $table->string('slug')->unique(); $table->integer('content_id')->unsigned()->index(); $table->foreign('content_id')->references('id')->on('contents'); $table->integer('view_count')->unsigned()->default(0); From d4b88252713c1e0348560c4b0301c4c2a222cf6f Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 22:03:43 +0800 Subject: [PATCH 23/68] Update repository of Page --- app/Models/Content.php | 12 +++ app/Models/Page.php | 45 +++++++- app/Models/User.php | 12 +++ .../Eloquent/PageRepositoryEloquent.php | 101 +++++++++++++++++- .../Eloquent/PostRepositoryEloquent.php | 9 +- .../Eloquent/Traits/FieldsHandler.php | 16 +++ 6 files changed, 184 insertions(+), 11 deletions(-) diff --git a/app/Models/Content.php b/app/Models/Content.php index 83ed778..4961c84 100644 --- a/app/Models/Content.php +++ b/app/Models/Content.php @@ -4,6 +4,10 @@ use Illuminate\Database\Eloquent\Model; +/** + * Class Content + * @package App\Models + */ class Content extends Model { /** @@ -18,4 +22,12 @@ public function post() { return $this->hasOne(Post::class); } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasOne + */ + public function page() + { + return $this->hasOne(Page::class); + } } diff --git a/app/Models/Page.php b/app/Models/Page.php index 76a52a2..4fb101e 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Indigo\Contracts\Markdownable; +use Indigo\Tools\MarkDownParser; /** * Class Page @@ -14,7 +15,6 @@ class Page extends Model implements Markdownable, ViewableContract { use SoftDeletes, Viewable; - /** * @var array */ @@ -42,6 +42,47 @@ class Page extends Model implements Markdownable, ViewableContract */ public function getMarkdownContent() { - // TODO: Implement getMarkdownContent() method. + return $this->getRawContentAttribute(); + } + + /** + * @return mixed + */ + public function getRawContentAttribute() + { + return $this->content()->getResults()->body; + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function content() + { + return $this->belongsTo(Content::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function author() + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * @return array|\Illuminate\Contracts\Translation\Translator|null|string + */ + public function getHumanStatusAttribute() + { + return $this->getAttribute('is_draft') ? trans('not_show') : trans('show'); + } + + /** + * @return string + */ + public function getContentAttribute() + { + // TODO cache + return MarkDownParser::md2html($this); } } diff --git a/app/Models/User.php b/app/Models/User.php index ac7cbdc..6f1e2f8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,10 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Zizaco\Entrust\Traits\EntrustUserTrait; +/** + * Class User + * @package App\Models + */ class User extends Authenticatable { use Notifiable, EntrustUserTrait; @@ -36,6 +40,14 @@ public function posts() return $this->hasMany(Post::class); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function pages() + { + return $this->hasMany(Page::class); + } + /** * TODO condition should be compared with role * diff --git a/app/Repositories/Eloquent/PageRepositoryEloquent.php b/app/Repositories/Eloquent/PageRepositoryEloquent.php index 5fa248b..3b5325d 100644 --- a/app/Repositories/Eloquent/PageRepositoryEloquent.php +++ b/app/Repositories/Eloquent/PageRepositoryEloquent.php @@ -2,9 +2,15 @@ namespace App\Repositories\Eloquent; +use App\Models\Content; use App\Models\Page; use App\Repositories\Contracts\PageRepository; +use App\Repositories\Eloquent\Traits\FieldsHandler; use App\Repositories\Eloquent\Traits\Slugable; +use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; /** * Class PageRepositoryEloquent @@ -12,7 +18,23 @@ */ class PageRepositoryEloquent extends BaseRepository implements PageRepository { - use Slugable; + use Slugable, FieldsHandler; + /** + * @var \Illuminate\Database\Eloquent\Model + */ + protected $contentModel; + + /** + * BaseRepository constructor. + * @param \Illuminate\Container\Container $app + * @throws \App\Repositories\Exceptions\RepositoryException + */ + public function __construct(Container $app) + { + parent::__construct($app); + + $this->contentModel = $app->make($this->contentModel()); + } /** * @return string @@ -21,4 +43,81 @@ public function model() { return Page::class; } + + /** + * @return string + */ + public function contentModel() + { + return Content::class; + } + + /** + * @param array $attributes + * @return mixed + * @throws \App\Repositories\Exceptions\RepositoryException + * @throws \Exception + * @throws \Throwable + */ + public function create(array $attributes) + { + $attributes = $this->preHandleData($attributes); + + $model = DB::transaction(function () use ($attributes) { + return tap($this->getNewModelInstance($attributes), function (Model $instance) use ($attributes) { + $instance->setAttribute('content_id', $this->contentModel->create($attributes)->getKey()); + $instance->setAttribute('user_id', Auth::id()); + $instance->save(); + }); + }); + + return $this->parseResult($model); + } + + /** + * @param null $perPage + * @param array $columns + * @return mixed + * @throws \App\Repositories\Exceptions\RepositoryException + */ + public function paginate($perPage = null, $columns = ['*']) + { + $this->with('author'); + + return parent::paginate($perPage, $columns); + } + + /** + * @param array $attributes + * @return array + */ + protected function preHandleData(array $attributes) + { + $attributes['slug'] = $this->autoSlug($attributes['slug'], $attributes['title']); + + return $this->handle($attributes); + } + + /** + * @param array $attributes + * @param $id + * @return mixed + * @throws \App\Repositories\Exceptions\RepositoryException + * @throws \Exception + * @throws \Throwable + */ + public function update(array $attributes, $id) + { + $attributes = $this->preHandleData($attributes); + + $model = DB::transaction(function () use ($attributes, $id) { + return tap($this->tempDisableApiResource(function () use ($attributes, $id) { + return parent::update($attributes, $id); + }), function (Page $page) use ($attributes) { + $page->content()->update($attributes); + }); + }); + + return $this->parseResult($model); + } } \ No newline at end of file diff --git a/app/Repositories/Eloquent/PostRepositoryEloquent.php b/app/Repositories/Eloquent/PostRepositoryEloquent.php index 4822bd3..6547b38 100644 --- a/app/Repositories/Eloquent/PostRepositoryEloquent.php +++ b/app/Repositories/Eloquent/PostRepositoryEloquent.php @@ -115,14 +115,7 @@ protected function preHandleData(array $attributes) { $attributes['slug'] = $this->autoSlug($attributes['slug'], $attributes['title']); - foreach ($attributes as $field => &$value) { - if (method_exists($this, $method = 'handle' . studly_case($field))) { - // Note that the parameters for call_user_func() are not passed by reference. - $value = call_user_func([$this, $method], $value); - } - } - - return $attributes; + return $this->handle($attributes); } /** diff --git a/app/Repositories/Eloquent/Traits/FieldsHandler.php b/app/Repositories/Eloquent/Traits/FieldsHandler.php index 943d7c9..ab488c3 100644 --- a/app/Repositories/Eloquent/Traits/FieldsHandler.php +++ b/app/Repositories/Eloquent/Traits/FieldsHandler.php @@ -28,4 +28,20 @@ public function handleIsDraft($value) { return empty($value) ? Post::IS_NOT_DRAFT : Post::IS_DRAFT; } + + /** + * @param array $attributes + * @return array + */ + public function handle(array $attributes) + { + foreach ($attributes as $field => &$value) { + if (method_exists($this, $method = 'handle' . studly_case($field))) { + // Note that the parameters for call_user_func() are not passed by reference. + $value = call_user_func([$this, $method], $value); + } + } + + return $attributes; + } } \ No newline at end of file From b7c64e94701a4d53411a51d634d067cd26e98ae9 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 22:04:04 +0800 Subject: [PATCH 24/68] Add views of Page --- resources/assets/sass/modules/article.scss | 4 + resources/views/admin/pages/_form.blade.php | 93 +++++++++++++++++++ resources/views/admin/pages/create.blade.php | 18 ++++ resources/views/admin/pages/edit.blade.php | 15 +++ resources/views/admin/pages/index.blade.php | 25 +++++ .../views/admin/partials/sidenav.blade.php | 1 + resources/views/pages/show.blade.php | 50 ++++++++++ resources/views/partials/navbar.blade.php | 2 +- resources/views/partials/sidenav.blade.php | 2 +- 9 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 resources/views/admin/pages/_form.blade.php create mode 100644 resources/views/admin/pages/create.blade.php create mode 100644 resources/views/admin/pages/edit.blade.php create mode 100644 resources/views/admin/pages/index.blade.php create mode 100644 resources/views/pages/show.blade.php diff --git a/resources/assets/sass/modules/article.scss b/resources/assets/sass/modules/article.scss index 2490f21..34a66d1 100644 --- a/resources/assets/sass/modules/article.scss +++ b/resources/assets/sass/modules/article.scss @@ -51,6 +51,10 @@ .card-panel { padding-bottom: 0; + &.page-panel { + padding-bottom: 24px !important; + } + .post-content { img { max-width: 100%; diff --git a/resources/views/admin/pages/_form.blade.php b/resources/views/admin/pages/_form.blade.php new file mode 100644 index 0000000..7bb2aff --- /dev/null +++ b/resources/views/admin/pages/_form.blade.php @@ -0,0 +1,93 @@ +
+
+ + +
+
+ id))readonly="readonly"@endif> + +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+
+
+ +@push('css') + + +@endpush + +@push('js') + + +@endpush \ No newline at end of file diff --git a/resources/views/admin/pages/create.blade.php b/resources/views/admin/pages/create.blade.php new file mode 100644 index 0000000..5d5d708 --- /dev/null +++ b/resources/views/admin/pages/create.blade.php @@ -0,0 +1,18 @@ +@extends('admin.layouts.app') + +@section('title') + Pages | @parent +@endsection + +@section('content') + + @component('admin.components.form_create', [ + 'formAction' => route('admin.pages.store'), + 'redirectUrl' => route('admin.pages.index') + ]) + + @include('admin.pages._form') + + @endcomponent + +@endsection \ No newline at end of file diff --git a/resources/views/admin/pages/edit.blade.php b/resources/views/admin/pages/edit.blade.php new file mode 100644 index 0000000..76af6c9 --- /dev/null +++ b/resources/views/admin/pages/edit.blade.php @@ -0,0 +1,15 @@ +@extends('admin.layouts.app') + +@section('title') + Pages | @parent +@endsection + +@section('content') + + @component('admin.components.form_update', ['formAction' => route('admin.pages.update', $page->id)]) + + @include('admin.pages._form') + + @endcomponent + +@endsection \ No newline at end of file diff --git a/resources/views/admin/pages/index.blade.php b/resources/views/admin/pages/index.blade.php new file mode 100644 index 0000000..5825fa7 --- /dev/null +++ b/resources/views/admin/pages/index.blade.php @@ -0,0 +1,25 @@ +@extends('admin.layouts.app') + +@section('title') + Pages | @parent +@endsection + +@section('content') + + @component('admin.components.table', [ + 'paginator' => $pages, + 'columns' => [ + 'id' => 'ID', + 'title' => 'Title', + 'author.name' => 'Author', + 'human_status' => 'Status', + 'created_at' => 'Created At' + ], + 'hrefCreate' => route('admin.pages.create'), + 'hrefShow' => route('admin.pages.show', ':id'), + 'hrefEdit' => route('admin.pages.edit', ':id'), + 'hrefDestroy' => route('admin.pages.destroy', ':id'), + ]) + @endcomponent + +@endsection diff --git a/resources/views/admin/partials/sidenav.blade.php b/resources/views/admin/partials/sidenav.blade.php index c31c2a4..079f464 100644 --- a/resources/views/admin/partials/sidenav.blade.php +++ b/resources/views/admin/partials/sidenav.blade.php @@ -40,5 +40,6 @@
  • library_booksCategories
  • loyaltyTags
  • insert_drive_filePosts
  • +
  • notePages
  • settingsSettings
  • diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php new file mode 100644 index 0000000..40d9997 --- /dev/null +++ b/resources/views/pages/show.blade.php @@ -0,0 +1,50 @@ +@extends('layouts.base') + +@section('title') +{{ $page->title }} | @parent +@endsection + +@section('keywords'){{ $page->title }}@endsection +@section('description'){{ $page->description ?: $page->title }}@endsection + +@section('content') + @component('components.header') +
    +
    +

    {{ $page->title }}

    +
    +
    + @endcomponent + +
    +
    + + @if($page->description) +
    +
    +
    + {{ $page->description }} +
    +
    +
    + @endif + +
    +
    + + {{-- TODO when use api, content should be parse first, use transformer or parse markdown before store--}} +
    + {!! $page->content !!} +
    + +
    + +
    + +
    + @include('partials.comment') +
    + +
    +
    +@endsection diff --git a/resources/views/partials/navbar.blade.php b/resources/views/partials/navbar.blade.php index 505444d..8b7766a 100644 --- a/resources/views/partials/navbar.blade.php +++ b/resources/views/partials/navbar.blade.php @@ -18,7 +18,7 @@ -
  • About
  • +
  • About
  • Github
  • diff --git a/resources/views/partials/sidenav.blade.php b/resources/views/partials/sidenav.blade.php index 7aa2720..e0ee2f5 100644 --- a/resources/views/partials/sidenav.blade.php +++ b/resources/views/partials/sidenav.blade.php @@ -38,7 +38,7 @@
  • {{--
  • Subheader
  • --}}
  • cloudBlog Introduce
  • -
  • personAbout
  • +
  • personAbout
  • codeGithub
  • From 16d96faf575a25ca8f06ca27e6c3deeb76a606a7 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 22:04:17 +0800 Subject: [PATCH 25/68] Add routes of Page --- routes/web.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/routes/web.php b/routes/web.php index 0596baf..ef37fd8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -34,6 +34,11 @@ Route::post('slug', 'SlugController@translate')->name('slug.translate'); Route::post('image', 'UploadController@uploadImage')->name('upload.image'); }); + + // Page + Route::post('pages/{id}/restore', 'PageController@restore')->name('pages.restore'); + Route::post('pages/{id}/force-delete', 'PageController@forceDelete')->name('pages.force-delete'); + Route::resource('pages', 'PageController'); }); Route::group(['namespace' => 'Frontend'], function () { @@ -42,4 +47,7 @@ Route::resource('articles', 'PostController', ['only' => ['show'], 'middleware' => 'visitor']); Route::resource('categories', 'CategoryController', ['only' => ['show']]); Route::resource('tags', 'TagController', ['only' => ['show']]); + + // Page + Route::get('{slug}', 'PageController@show')->name('pages.show'); }); From d105d02c43bea9a1c1c52937d2b7e6100ee8d607 Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 22:25:05 +0800 Subject: [PATCH 26/68] Fix sidenav trigger menu --- resources/assets/sass/modules/sidenav.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/assets/sass/modules/sidenav.scss b/resources/assets/sass/modules/sidenav.scss index bd1b274..a75a257 100644 --- a/resources/assets/sass/modules/sidenav.scss +++ b/resources/assets/sass/modules/sidenav.scss @@ -1,6 +1,9 @@ -.menu-on-large { - position: absolute; - left: -20%; +nav a.sidenav-trigger { + &.menu-on-large { + position: absolute; + left: -20%; + display: block; + } } .sidenav { From 5ea7e272ac1f8d8bca13fbc1ae1d015204af008e Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 23:13:15 +0800 Subject: [PATCH 27/68] Add some HasPublishedStatus(Time) contracts to Page and Post --- .../Controllers/Backend/PageController.php | 2 ++ .../Controllers/Backend/PostController.php | 2 +- app/Indigo/Contracts/HasPublishedTime.php | 11 ++++++++++ app/Models/Page.php | 13 +++++++++++ app/Models/Post.php | 3 ++- .../Contracts/Helpers/HasPublishedStatus.php | 11 ++++++++++ app/Repositories/Contracts/PageRepository.php | 3 ++- app/Repositories/Contracts/PostRepository.php | 8 ++----- .../Eloquent/PageRepositoryEloquent.php | 3 ++- .../Eloquent/PostRepositoryEloquent.php | 14 ++---------- .../Eloquent/Traits/FieldsHandler.php | 2 ++ .../Eloquent/Traits/HasPublishedStatus.php | 22 +++++++++++++++++++ app/Scopes/PublishedScope.php | 8 +++++-- 13 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 app/Indigo/Contracts/HasPublishedTime.php create mode 100644 app/Repositories/Contracts/Helpers/HasPublishedStatus.php create mode 100644 app/Repositories/Eloquent/Traits/HasPublishedStatus.php diff --git a/app/Http/Controllers/Backend/PageController.php b/app/Http/Controllers/Backend/PageController.php index e883740..d54f109 100644 --- a/app/Http/Controllers/Backend/PageController.php +++ b/app/Http/Controllers/Backend/PageController.php @@ -24,6 +24,8 @@ class PageController extends BackendController public function __construct(PageRepository $pageRepository) { $this->pageRepository = $pageRepository; + + $this->pageRepository->ignorePublishedStatusMode(); } /** diff --git a/app/Http/Controllers/Backend/PostController.php b/app/Http/Controllers/Backend/PostController.php index 065e7ba..54d6f0a 100644 --- a/app/Http/Controllers/Backend/PostController.php +++ b/app/Http/Controllers/Backend/PostController.php @@ -42,7 +42,7 @@ public function __construct( $this->categoryRepository = $categoryRepository; $this->tagRepository = $tagRepository; - $this->postRepository->adminMode(); + $this->postRepository->ignorePublishedStatusMode(); } /** diff --git a/app/Indigo/Contracts/HasPublishedTime.php b/app/Indigo/Contracts/HasPublishedTime.php new file mode 100644 index 0000000..470906e --- /dev/null +++ b/app/Indigo/Contracts/HasPublishedTime.php @@ -0,0 +1,11 @@ + 'boolean' ]; + /** + * The "booting" method of the model. + * + * @return void + */ + protected static function boot() + { + parent::boot(); + + static::addGlobalScope(new PublishedScope); + } + /** * @return string */ diff --git a/app/Models/Post.php b/app/Models/Post.php index b02a332..d8e9809 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Indigo\Contracts\HasPublishedTime; use App\Indigo\Contracts\Viewable as ViewableContract; use App\Presenters\PostPresenter; use App\Scopes\PublishedScope; @@ -16,7 +17,7 @@ * Class Post * @package App\Models */ -class Post extends Model implements Markdownable, ViewableContract +class Post extends Model implements Markdownable, ViewableContract, HasPublishedTime { use PresentableTrait, SoftDeletes, Viewable; /** diff --git a/app/Repositories/Contracts/Helpers/HasPublishedStatus.php b/app/Repositories/Contracts/Helpers/HasPublishedStatus.php new file mode 100644 index 0000000..b8d273b --- /dev/null +++ b/app/Repositories/Contracts/Helpers/HasPublishedStatus.php @@ -0,0 +1,11 @@ +with(['category', 'author'])->orderBy('id', 'desc')->paginate($perPage ?: $this->getDefaultPerPage(), $columns); } - - /** - * @return $this - */ - public function adminMode() - { - $this->model = $this->model->withoutGlobalScope(PublishedScope::class); - - return $this; - } } diff --git a/app/Repositories/Eloquent/Traits/FieldsHandler.php b/app/Repositories/Eloquent/Traits/FieldsHandler.php index ab488c3..e527c40 100644 --- a/app/Repositories/Eloquent/Traits/FieldsHandler.php +++ b/app/Repositories/Eloquent/Traits/FieldsHandler.php @@ -26,6 +26,8 @@ public function handlePublishedAt($value) */ public function handleIsDraft($value) { + // TODO when switch to 'publish', this attribute is not contained in the post data. Should be fix in + // frontend and update this method. return empty($value) ? Post::IS_NOT_DRAFT : Post::IS_DRAFT; } diff --git a/app/Repositories/Eloquent/Traits/HasPublishedStatus.php b/app/Repositories/Eloquent/Traits/HasPublishedStatus.php new file mode 100644 index 0000000..b5618be --- /dev/null +++ b/app/Repositories/Eloquent/Traits/HasPublishedStatus.php @@ -0,0 +1,22 @@ +model = $this->model->withoutGlobalScope(PublishedScope::class); + + return $this; + } +} \ No newline at end of file diff --git a/app/Scopes/PublishedScope.php b/app/Scopes/PublishedScope.php index 543bda5..a82cc28 100644 --- a/app/Scopes/PublishedScope.php +++ b/app/Scopes/PublishedScope.php @@ -2,6 +2,7 @@ namespace App\Scopes; +use App\Indigo\Contracts\HasPublishedTime; use App\Models\Post; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; @@ -21,7 +22,10 @@ class PublishedScope implements Scope */ public function apply(Builder $builder, Model $model) { - $builder->where('published_at', '<=', Carbon::now()->toDateTimeString()) - ->where('is_draft', '=', Post::IS_NOT_DRAFT); + if ($model instanceof HasPublishedTime) { + $builder = $builder->where('published_at', '<=', Carbon::now()->toDateTimeString()); + } + + $builder->where('is_draft', '=', Post::IS_NOT_DRAFT); } } \ No newline at end of file From 19cccde49dccb81b78a9860b8d8f6600ad06757a Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Tue, 13 Feb 2018 23:33:00 +0800 Subject: [PATCH 28/68] Add PHPDoc @see tag to traits which implement interface --- app/Models/Viewable.php | 2 ++ app/Repositories/Eloquent/Traits/ApiResource.php | 1 + app/Repositories/Eloquent/Traits/HasCriteria.php | 7 +++++++ app/Repositories/Eloquent/Traits/HasPost.php | 1 + app/Repositories/Eloquent/Traits/HasPublishedStatus.php | 1 + app/Repositories/Eloquent/Traits/Slugable.php | 1 + 6 files changed, 13 insertions(+) diff --git a/app/Models/Viewable.php b/app/Models/Viewable.php index 78d707e..77c784b 100644 --- a/app/Models/Viewable.php +++ b/app/Models/Viewable.php @@ -10,6 +10,7 @@ trait Viewable { /** * @return string + * @see \App\Indigo\Contracts\Viewable::getCountField() */ public function getCountField() { @@ -18,6 +19,7 @@ public function getCountField() /** * @return mixed + * @see \App\Indigo\Contracts\Viewable::getIdentifier() */ public function getIdentifier() { diff --git a/app/Repositories/Eloquent/Traits/ApiResource.php b/app/Repositories/Eloquent/Traits/ApiResource.php index 98a0c71..237eff2 100644 --- a/app/Repositories/Eloquent/Traits/ApiResource.php +++ b/app/Repositories/Eloquent/Traits/ApiResource.php @@ -72,6 +72,7 @@ protected function tempDisableApiResource($callback) /** * @param bool $switch * @return $this + * @see \App\Repositories\Contracts\Helpers\ApiResourceInterface::useResource() */ public function useResource($switch = true) { diff --git a/app/Repositories/Eloquent/Traits/HasCriteria.php b/app/Repositories/Eloquent/Traits/HasCriteria.php index 336d30a..9705734 100644 --- a/app/Repositories/Eloquent/Traits/HasCriteria.php +++ b/app/Repositories/Eloquent/Traits/HasCriteria.php @@ -24,6 +24,7 @@ trait HasCriteria * @param $criteria * @return mixed * @throws \App\Repositories\Exceptions\RepositoryException + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::pushCriteria() */ public function pushCriteria($criteria) { @@ -55,6 +56,7 @@ protected function parseCriteria($criteria) /** * @param $criteria * @return mixed + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::popCriteria() */ public function popCriteria($criteria) { @@ -73,6 +75,7 @@ public function popCriteria($criteria) /** * @param bool $status * @return mixed + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::skipCriteria() */ public function skipCriteria($status = true) { @@ -85,6 +88,7 @@ public function skipCriteria($status = true) * @param $criteria * @return mixed * @throws \App\Repositories\Exceptions\RepositoryException + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::useCriteria() */ public function useCriteria($criteria) { @@ -97,6 +101,7 @@ public function useCriteria($criteria) /** * @return mixed + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::applyCriteria() */ public function applyCriteria() { @@ -115,6 +120,7 @@ public function applyCriteria() /** * @return array + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::getCriteria() */ public function getCriteria() { @@ -123,6 +129,7 @@ public function getCriteria() /** * @return $this + * @see \App\Repositories\Contracts\Helpers\HasCriteriaInterface::resetCriteria() */ public function resetCriteria() { diff --git a/app/Repositories/Eloquent/Traits/HasPost.php b/app/Repositories/Eloquent/Traits/HasPost.php index d4c693e..20dae9a 100644 --- a/app/Repositories/Eloquent/Traits/HasPost.php +++ b/app/Repositories/Eloquent/Traits/HasPost.php @@ -11,6 +11,7 @@ trait HasPost /** * @param array $columns * @return mixed + * @see \App\Repositories\Contracts\Helpers\HasPostInterface::getResultsHavePosts() */ public function getResultsHavePosts($columns = ['*']) { diff --git a/app/Repositories/Eloquent/Traits/HasPublishedStatus.php b/app/Repositories/Eloquent/Traits/HasPublishedStatus.php index b5618be..75fdf5c 100644 --- a/app/Repositories/Eloquent/Traits/HasPublishedStatus.php +++ b/app/Repositories/Eloquent/Traits/HasPublishedStatus.php @@ -11,6 +11,7 @@ trait HasPublishedStatus { /** + * @see \App\Repositories\Contracts\Helpers\HasPublishedStatus::ignorePublishedStatusMode() * @return $this */ public function ignorePublishedStatusMode() diff --git a/app/Repositories/Eloquent/Traits/Slugable.php b/app/Repositories/Eloquent/Traits/Slugable.php index 1e9e0b1..dcffbe9 100644 --- a/app/Repositories/Eloquent/Traits/Slugable.php +++ b/app/Repositories/Eloquent/Traits/Slugable.php @@ -63,6 +63,7 @@ protected function uniqueChar() * @param $slug * @return mixed * @throws \App\Repositories\Exceptions\RepositoryException + * @see \App\Repositories\Contracts\Helpers\SlugableInterface::getBySlug() */ public function getBySlug($slug) { From 9edd09a9f669f7595bca5a0e4323ad6d0a53d1ba Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Wed, 14 Feb 2018 10:31:02 +0800 Subject: [PATCH 29/68] Update post-main padding-top --- resources/assets/sass/modules/article.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/assets/sass/modules/article.scss b/resources/assets/sass/modules/article.scss index 34a66d1..256d13c 100644 --- a/resources/assets/sass/modules/article.scss +++ b/resources/assets/sass/modules/article.scss @@ -43,6 +43,8 @@ // Post related .post-main { + padding-top: 1rem; + .post-desc { } From 139c0c26b2c17046fd7035b126d3c3d439ad809a Mon Sep 17 00:00:00 2001 From: Miles Pong Date: Wed, 14 Feb 2018 17:02:19 +0800 Subject: [PATCH 30/68] Update namespace and add Article model --- app/Console/Commands/SaveCounter.php | 10 +- app/Events/ViewedEvent.php | 12 +-- app/Indigo/Contracts/Counter.php | 14 +-- app/Indigo/Contracts/HasPublishedTime.php | 2 +- app/Indigo/Contracts/Viewable.php | 2 +- .../FlysystemAdapter/GoogleDriveAdapter.php | 2 +- app/Indigo/Models/Article.php | 102 ++++++++++++++++++ app/Indigo/Tools/Counter.php | 22 ++-- app/Listeners/ViewedEventListener.php | 6 +- app/Models/Page.php | 53 +-------- app/Models/Post.php | 69 +----------- app/Models/Viewable.php | 28 ----- app/Providers/CounterServiceProvider.php | 4 +- app/Providers/GoogleDriveServiceProvider.php | 2 +- .../Eloquent/Traits/FieldsHandler.php | 5 +- app/Scopes/PublishedScope.php | 6 +- database/seeds/PagesTableSeeder.php | 2 +- resources/assets/sass/modules/article.scss | 2 +- resources/views/pages/show.blade.php | 2 +- resources/views/posts/show.blade.php | 2 +- 20 files changed, 154 insertions(+), 193 deletions(-) create mode 100644 app/Indigo/Models/Article.php delete mode 100644 app/Models/Viewable.php diff --git a/app/Console/Commands/SaveCounter.php b/app/Console/Commands/SaveCounter.php index b3fd233..63b19c7 100644 --- a/app/Console/Commands/SaveCounter.php +++ b/app/Console/Commands/SaveCounter.php @@ -2,12 +2,12 @@ namespace App\Console\Commands; -use App\Indigo\Contracts\Counter; -use App\Indigo\Contracts\Viewable; use Carbon\Carbon; use Closure; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Model; +use Indigo\Contracts\Counter; +use Indigo\Contracts\Viewable; /** * Class SaveCounter @@ -28,7 +28,7 @@ class SaveCounter extends Command */ protected $description = 'Save posts view count into database and flush cache'; /** - * @var \App\Indigo\Contracts\Counter + * @var \Indigo\Contracts\Counter */ private $counter; /** @@ -39,7 +39,7 @@ class SaveCounter extends Command /** * Create a new command instance. * - * @param \App\Indigo\Contracts\Counter $counter + * @param \Indigo\Contracts\Counter $counter */ public function __construct(Counter $counter) { @@ -114,7 +114,7 @@ private function getStats($key) } /** - * @param \App\Indigo\Contracts\Viewable|\Illuminate\Database\Eloquent\Model $viewable + * @param \Indigo\Contracts\Viewable|\Illuminate\Database\Eloquent\Model $viewable * @param array $data */ private function updateCount($viewable, array $data = []) diff --git a/app/Events/ViewedEvent.php b/app/Events/ViewedEvent.php index 80b9549..3dbff8d 100644 --- a/app/Events/ViewedEvent.php +++ b/app/Events/ViewedEvent.php @@ -2,14 +2,12 @@ namespace App\Events; -use App\Indigo\Contracts\Viewable; use Illuminate\Broadcasting\Channel; -use Illuminate\Queue\SerializesModels; +use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Foundation\Events\Dispatchable; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Queue\SerializesModels; +use Indigo\Contracts\Viewable; /** * Class ViewedEvent @@ -20,13 +18,13 @@ class ViewedEvent use Dispatchable, InteractsWithSockets, SerializesModels; /** - * @var \App\Indigo\Contracts\Viewable + * @var \Indigo\Contracts\Viewable */ public $viewable; /** * ViewedEvent constructor. - * @param \App\Indigo\Contracts\Viewable $viewable + * @param \Indigo\Contracts\Viewable $viewable */ public function __construct(Viewable $viewable) { diff --git a/app/Indigo/Contracts/Counter.php b/app/Indigo/Contracts/Counter.php index c1e9db6..4b02501 100644 --- a/app/Indigo/Contracts/Counter.php +++ b/app/Indigo/Contracts/Counter.php @@ -1,6 +1,6 @@ getRawContentAttribute(); + } + + /** + * @return mixed + */ + abstract public function getRawContentAttribute(); + + /** + * @return string + */ + public function getCountField() + { + return 'view_count'; + } + + /** + * @return mixed + */ + public function getIdentifier() + { + return $this->getKey(); + } + + /** + * @param $value + */ + public function setIsDraftAttribute($value) + { + $this->attributes['is_draft'] = !empty($value) ? self::IS_DRAFT : self::IS_NOT_DRAFT; + } + + /** + * @param $value + * @return bool + */ + public function getIsDraftAttribute($value) + { + return (boolean)$value; + } + + /** + * @return array|\Illuminate\Contracts\Translation\Translator|null|string + */ + public function getHumanStatusAttribute() + { + return $this->getAttribute('is_draft') ? trans('not_show') : trans('show'); + } + + /** + * @return string + */ + public function getHtmlContentAttribute() + { + // TODO cache + return MarkDownParser::md2html($this); + } +} \ No newline at end of file diff --git a/app/Indigo/Tools/Counter.php b/app/Indigo/Tools/Counter.php index c4ed4a8..7e03aac 100644 --- a/app/Indigo/Tools/Counter.php +++ b/app/Indigo/Tools/Counter.php @@ -1,9 +1,9 @@ 'boolean' - ]; - - /** - * The "booting" method of the model. - * - * @return void - */ - protected static function boot() - { - parent::boot(); - - static::addGlobalScope(new PublishedScope); - } - - /** - * @return string - */ - public function getMarkdownContent() - { - return $this->getRawContentAttribute(); - } /** * @return mixed @@ -81,21 +49,4 @@ public function author() { return $this->belongsTo(User::class, 'user_id'); } - - /** - * @return array|\Illuminate\Contracts\Translation\Translator|null|string - */ - public function getHumanStatusAttribute() - { - return $this->getAttribute('is_draft') ? trans('not_show') : trans('show'); - } - - /** - * @return string - */ - public function getContentAttribute() - { - // TODO cache - return MarkDownParser::md2html($this); - } } diff --git a/app/Models/Post.php b/app/Models/Post.php index d8e9809..fa090df 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -2,36 +2,19 @@ namespace App\Models; -use App\Indigo\Contracts\HasPublishedTime; -use App\Indigo\Contracts\Viewable as ViewableContract; use App\Presenters\PostPresenter; -use App\Scopes\PublishedScope; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Storage; -use Indigo\Contracts\Markdownable; -use Indigo\Tools\MarkDownParser; +use Indigo\Contracts\HasPublishedTime; +use Indigo\Models\Article as ArticleModel; use Laracasts\Presenter\PresentableTrait; /** * Class Post * @package App\Models */ -class Post extends Model implements Markdownable, ViewableContract, HasPublishedTime +class Post extends ArticleModel implements HasPublishedTime { - use PresentableTrait, SoftDeletes, Viewable; - /** - * Is draft status - */ - const IS_DRAFT = 1; - /** - * Is not draft status - */ - const IS_NOT_DRAFT = 0; - /** - * Cache key prefix of post's content - */ - const CONTENT_CACHE_KEY_PREFIX = 'contents:'; + use PresentableTrait; /** * @var string */ @@ -53,24 +36,6 @@ class Post extends Model implements Markdownable, ViewableContract, HasPublished * @var array */ protected $dates = ['published_at', 'deleted_at']; - /** - * @var array - */ - protected $casts = [ - 'is_draft' => 'boolean' - ]; - - /** - * The "booting" method of the model. - * - * @return void - */ - protected static function boot() - { - parent::boot(); - - static::addGlobalScope(new PublishedScope); - } /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -96,24 +61,6 @@ public function tags() return $this->morphToMany(Tag::class, 'taggable'); } - /** - * @param $name - * @return mixed - */ - public function getConst($name) - { - return constant("self::{$name}"); - } - - /** - * @return string - */ - public function getContentAttribute() - { - // TODO cache - return MarkDownParser::md2html($this); - } - /** * @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $published @@ -167,14 +114,6 @@ public function scopeLatestPublished($query) return $query->orderByDesc('published_at'); } - /** - * @return mixed - */ - public function getMarkdownContent() - { - return $this->getRawContentAttribute(); - } - /** * @return mixed */ diff --git a/app/Models/Viewable.php b/app/Models/Viewable.php deleted file mode 100644 index 77c784b..0000000 --- a/app/Models/Viewable.php +++ /dev/null @@ -1,28 +0,0 @@ -getKey(); - } -} \ No newline at end of file diff --git a/app/Providers/CounterServiceProvider.php b/app/Providers/CounterServiceProvider.php index 7c79dfc..7063d33 100644 --- a/app/Providers/CounterServiceProvider.php +++ b/app/Providers/CounterServiceProvider.php @@ -2,8 +2,8 @@ namespace App\Providers; -use App\Indigo\Tools\Counter; use Barryvdh\Debugbar\ServiceProvider; +use Indigo\Tools\Counter; /** * Class CounterServiceProvider @@ -23,7 +23,7 @@ public function boot() */ public function register() { - $this->app->singleton(\App\Indigo\Contracts\Counter::class, function ($app) { + $this->app->singleton(\Indigo\Contracts\Counter::class, function ($app) { $config = $this->getConfig(); return new Counter($app, $config['cache_store']); }); diff --git a/app/Providers/GoogleDriveServiceProvider.php b/app/Providers/GoogleDriveServiceProvider.php index 9e46aa3..7572e51 100644 --- a/app/Providers/GoogleDriveServiceProvider.php +++ b/app/Providers/GoogleDriveServiceProvider.php @@ -2,9 +2,9 @@ namespace App\Providers; -use App\Indigo\FlysystemAdapter\GoogleDriveAdapter; use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; +use Indigo\FlysystemAdapter\GoogleDriveAdapter; use League\Flysystem\Filesystem; class GoogleDriveServiceProvider extends ServiceProvider diff --git a/app/Repositories/Eloquent/Traits/FieldsHandler.php b/app/Repositories/Eloquent/Traits/FieldsHandler.php index e527c40..74f4e59 100644 --- a/app/Repositories/Eloquent/Traits/FieldsHandler.php +++ b/app/Repositories/Eloquent/Traits/FieldsHandler.php @@ -2,7 +2,6 @@ namespace App\Repositories\Eloquent\Traits; -use App\Models\Post; use Carbon\Carbon; /** @@ -22,13 +21,13 @@ public function handlePublishedAt($value) /** * @param $value - * @return int + * @return bool */ public function handleIsDraft($value) { // TODO when switch to 'publish', this attribute is not contained in the post data. Should be fix in // frontend and update this method. - return empty($value) ? Post::IS_NOT_DRAFT : Post::IS_DRAFT; + return !empty($value); } /** diff --git a/app/Scopes/PublishedScope.php b/app/Scopes/PublishedScope.php index a82cc28..e579a88 100644 --- a/app/Scopes/PublishedScope.php +++ b/app/Scopes/PublishedScope.php @@ -2,12 +2,12 @@ namespace App\Scopes; -use App\Indigo\Contracts\HasPublishedTime; -use App\Models\Post; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; +use Indigo\Contracts\HasPublishedTime; +use Indigo\Models\Article; /** * Class PublishedScope @@ -26,6 +26,6 @@ public function apply(Builder $builder, Model $model) $builder = $builder->where('published_at', '<=', Carbon::now()->toDateTimeString()); } - $builder->where('is_draft', '=', Post::IS_NOT_DRAFT); + $builder->where('is_draft', '=', Article::IS_NOT_DRAFT); } } \ No newline at end of file diff --git a/database/seeds/PagesTableSeeder.php b/database/seeds/PagesTableSeeder.php index bc2a766..7e03a22 100644 --- a/database/seeds/PagesTableSeeder.php +++ b/database/seeds/PagesTableSeeder.php @@ -12,7 +12,7 @@ class PagesTableSeeder extends Seeder public function run() { factory(\App\Models\Page::class)->create([ - 'title' => 'about', + 'title' => 'About', 'slug' => 'about', 'is_draft' => false, 'deleted_at' => null diff --git a/resources/assets/sass/modules/article.scss b/resources/assets/sass/modules/article.scss index 256d13c..45542da 100644 --- a/resources/assets/sass/modules/article.scss +++ b/resources/assets/sass/modules/article.scss @@ -44,7 +44,7 @@ // Post related .post-main { padding-top: 1rem; - + .post-desc { } diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php index 40d9997..c6af9bb 100644 --- a/resources/views/pages/show.blade.php +++ b/resources/views/pages/show.blade.php @@ -34,7 +34,7 @@ {{-- TODO when use api, content should be parse first, use transformer or parse markdown before store--}}
    - {!! $page->content !!} + {!! $page->htmlContent !!}
    diff --git a/resources/views/posts/show.blade.php b/resources/views/posts/show.blade.php index 4f5bbbc..ae555e5 100644 --- a/resources/views/posts/show.blade.php +++ b/resources/views/posts/show.blade.php @@ -33,7 +33,7 @@ {{-- TODO when use api, content should be parse first, use transformer or parse markdown before store--}}
    - {!! $post->content !!} + {!! $post->htmlContent !!}