├── .gitignore ├── LICENSE ├── README.md ├── app.dockerfile ├── application ├── .babelrc ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── app │ ├── Article.php │ ├── Category.php │ ├── Comment.php │ ├── Console │ │ ├── Commands │ │ │ ├── BlogInstall.php │ │ │ └── CreateAdmin.php │ │ └── Kernel.php │ ├── Discussion.php │ ├── Exceptions │ │ ├── Handler.php │ │ └── UploadException.php │ ├── Follower.php │ ├── Helpers.php │ ├── Http │ │ ├── Controllers │ │ │ ├── Api │ │ │ │ ├── ApiController.php │ │ │ │ ├── ArticleController.php │ │ │ │ ├── CategoryController.php │ │ │ │ ├── CommentController.php │ │ │ │ ├── DiscussionController.php │ │ │ │ ├── HomeController.php │ │ │ │ ├── LinkController.php │ │ │ │ ├── MeController.php │ │ │ │ ├── SystemController.php │ │ │ │ ├── TagController.php │ │ │ │ ├── UploadController.php │ │ │ │ ├── UserController.php │ │ │ │ └── VisitorController.php │ │ │ ├── ArticleController.php │ │ │ ├── Auth │ │ │ │ ├── AuthController.php │ │ │ │ ├── ForgotPasswordController.php │ │ │ │ ├── LoginController.php │ │ │ │ ├── RegisterController.php │ │ │ │ └── ResetPasswordController.php │ │ │ ├── CategoryController.php │ │ │ ├── Controller.php │ │ │ ├── DiscussionController.php │ │ │ ├── HomeController.php │ │ │ ├── LinkController.php │ │ │ ├── SettingController.php │ │ │ ├── TagController.php │ │ │ └── UserController.php │ │ ├── Kernel.php │ │ ├── Middleware │ │ │ ├── EncryptCookies.php │ │ │ ├── MustBeAdmin.php │ │ │ ├── RedirectIfAuthenticated.php │ │ │ ├── TrimStrings.php │ │ │ └── VerifyCsrfToken.php │ │ └── Requests │ │ │ ├── ArticleRequest.php │ │ │ ├── CategoryRequest.php │ │ │ ├── CommentRequest.php │ │ │ ├── DiscussionRequest.php │ │ │ ├── ImageRequest.php │ │ │ ├── LinkRequest.php │ │ │ ├── TagRequest.php │ │ │ └── UserRequest.php │ ├── Link.php │ ├── Notifications │ │ ├── FollowedUser.php │ │ ├── GotVote.php │ │ ├── MentionedUser.php │ │ └── ReceivedComment.php │ ├── Policies │ │ ├── CommentPolicy.php │ │ ├── DiscussionPolicy.php │ │ └── UserPolicy.php │ ├── Providers │ │ ├── AppServiceProvider.php │ │ ├── AuthServiceProvider.php │ │ ├── BroadcastServiceProvider.php │ │ ├── EventServiceProvider.php │ │ └── RouteServiceProvider.php │ ├── Repositories │ │ ├── ArticleRepository.php │ │ ├── BaseRepository.php │ │ ├── CategoryRepository.php │ │ ├── CommentRepository.php │ │ ├── DiscussionRepository.php │ │ ├── LinkRepository.php │ │ ├── TagRepository.php │ │ ├── UserRepository.php │ │ └── VisitorRepository.php │ ├── Scopes │ │ ├── DraftScope.php │ │ └── StatusScope.php │ ├── Support │ │ ├── Response.php │ │ └── Transform.php │ ├── Tag.php │ ├── Tools │ │ ├── FileManager │ │ │ ├── BaseManager.php │ │ │ └── UpyunManager.php │ │ ├── IP.php │ │ ├── Markdowner.php │ │ └── Mention.php │ ├── Traits │ │ └── FollowTrait.php │ ├── Transformers │ │ ├── ArticleTransformer.php │ │ ├── CategoryTransformer.php │ │ ├── CommentTransformer.php │ │ ├── DiscussionTransformer.php │ │ ├── EmptyTransformer.php │ │ ├── LinkTransformer.php │ │ ├── TagTransformer.php │ │ ├── UserTransformer.php │ │ └── VisitorTransformer.php │ ├── User.php │ └── Visitor.php ├── artisan ├── bootstrap │ ├── app.php │ ├── autoload.php │ └── cache │ │ └── .gitignore ├── composer.json ├── composer.lock ├── composer_install.sh ├── config │ ├── app.php │ ├── auth.php │ ├── blog.php │ ├── broadcasting.php │ ├── cache.php │ ├── compile.php │ ├── database.php │ ├── filesystems.php │ ├── image.php │ ├── mail.php │ ├── queue.php │ ├── services.php │ ├── session.php │ └── view.php ├── database │ ├── .gitignore │ ├── factories │ │ └── ModelFactory.php │ ├── migrations │ │ ├── .gitkeep │ │ ├── 2014_10_12_000000_create_users_table.php │ │ ├── 2014_10_12_100000_create_password_resets_table.php │ │ ├── 2016_09_02_065857_create_articles_table.php │ │ ├── 2016_09_02_065920_create_tags_table.php │ │ ├── 2016_09_02_065952_create_visitors_table.php │ │ ├── 2016_09_02_070119_create_categories_table.php │ │ ├── 2016_09_02_070132_create_discussions_table.php │ │ ├── 2016_09_02_070151_create_comments_table.php │ │ ├── 2016_09_13_022056_create_links_table.php │ │ ├── 2016_11_11_163610_create_taggables_table.php │ │ ├── 2016_12_11_153312_create_followers_table.php │ │ ├── 2016_12_12_171655_create_notifications_table.php │ │ ├── 2016_12_12_205419_create_failed_jobs_table.php │ │ └── 2017_04_14_013622_create_votes_table.php │ └── seeds │ │ ├── .gitkeep │ │ ├── ArticlesTableSeeder.php │ │ ├── CategoriesTableSeeder.php │ │ ├── CommentsTableSeeder.php │ │ ├── DatabaseSeeder.php │ │ ├── DiscussionsTableSeeder.php │ │ ├── LinksTableSeeder.php │ │ ├── TagsTableSeeder.php │ │ ├── UsersTableSeeder.php │ │ └── VisitorsTableSeeder.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── public │ ├── .gitignore │ ├── .htaccess │ ├── favicon.ico │ ├── images │ │ ├── default.png │ │ └── favicon.ico │ ├── index.php │ └── robots.txt ├── resources │ ├── assets │ │ ├── js │ │ │ ├── App.vue │ │ │ ├── app.js │ │ │ ├── bootstrap.js │ │ │ ├── components │ │ │ │ ├── AvatarUpload.vue │ │ │ │ ├── Chartjs.vue │ │ │ │ ├── Comment.vue │ │ │ │ ├── Cropper.vue │ │ │ │ ├── Parse.vue │ │ │ │ ├── Textarea.vue │ │ │ │ ├── VoteButton.vue │ │ │ │ ├── dashboard │ │ │ │ │ ├── CustomAction.vue │ │ │ │ │ ├── Form.vue │ │ │ │ │ ├── Modal.vue │ │ │ │ │ ├── Table.vue │ │ │ │ │ ├── TablePagination.vue │ │ │ │ │ ├── TablePaginationMixin.vue │ │ │ │ │ └── particals │ │ │ │ │ │ ├── FooterBar.vue │ │ │ │ │ │ ├── Navbar.vue │ │ │ │ │ │ └── Sidebar.vue │ │ │ │ └── passport │ │ │ │ │ ├── AuthorizedClients.vue │ │ │ │ │ ├── Clients.vue │ │ │ │ │ └── PersonalAccessTokens.vue │ │ │ ├── config │ │ │ │ ├── base.js │ │ │ │ ├── helper.js │ │ │ │ ├── menu.js │ │ │ │ └── toastr.js │ │ │ ├── home.js │ │ │ ├── lang │ │ │ │ ├── en │ │ │ │ │ ├── form.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── page.js │ │ │ │ │ ├── sidebar.js │ │ │ │ │ └── table.js │ │ │ │ ├── index.js │ │ │ │ └── zh_cn │ │ │ │ │ ├── form.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── page.js │ │ │ │ │ ├── sidebar.js │ │ │ │ │ └── table.js │ │ │ ├── plugins │ │ │ │ └── http │ │ │ │ │ └── index.js │ │ │ ├── routes.js │ │ │ ├── vendor │ │ │ │ ├── github_emoji.js │ │ │ │ ├── highlight.min.js │ │ │ │ └── select2.min.js │ │ │ ├── views │ │ │ │ ├── Dashboard.vue │ │ │ │ ├── Parent.vue │ │ │ │ └── dashboard │ │ │ │ │ ├── File.vue │ │ │ │ │ ├── Home.vue │ │ │ │ │ ├── System.vue │ │ │ │ │ ├── Visitor.vue │ │ │ │ │ ├── article │ │ │ │ │ ├── Article.vue │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ ├── Form.vue │ │ │ │ │ └── FormMixin.vue │ │ │ │ │ ├── category │ │ │ │ │ ├── Category.vue │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ └── Form.vue │ │ │ │ │ ├── comment │ │ │ │ │ ├── Comment.vue │ │ │ │ │ └── Edit.vue │ │ │ │ │ ├── discussion │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Discussion.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ └── Form.vue │ │ │ │ │ ├── link │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ ├── Form.vue │ │ │ │ │ └── Link.vue │ │ │ │ │ ├── tag │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ ├── Form.vue │ │ │ │ │ └── Tag.vue │ │ │ │ │ └── user │ │ │ │ │ ├── Create.vue │ │ │ │ │ ├── Edit.vue │ │ │ │ │ ├── Form.vue │ │ │ │ │ └── User.vue │ │ │ └── vuex │ │ │ │ ├── actions.js │ │ │ │ ├── mutations.js │ │ │ │ └── store.js │ │ └── sass │ │ │ ├── _form.scss │ │ │ ├── _ibox.scss │ │ │ ├── _navbar.scss │ │ │ ├── _pagination.scss │ │ │ ├── _toastr.scss │ │ │ ├── _togglebutton.scss │ │ │ ├── app.scss │ │ │ ├── comment.scss │ │ │ ├── home.scss │ │ │ ├── markdown.scss │ │ │ ├── public.scss │ │ │ ├── styles.scss │ │ │ ├── variables.scss │ │ │ └── vendor │ │ │ ├── highlight.min.css │ │ │ ├── select2.min.css │ │ │ ├── simplemde.min.css │ │ │ └── toastr.min.css │ ├── lang │ │ ├── en │ │ │ ├── auth.php │ │ │ ├── blog.php │ │ │ ├── pagination.php │ │ │ ├── passwords.php │ │ │ └── validation.php │ │ └── zh_cn │ │ │ ├── auth.php │ │ │ ├── blog.php │ │ │ ├── pagination.php │ │ │ ├── passwords.php │ │ │ └── validation.php │ └── views │ │ ├── article │ │ ├── index.blade.php │ │ └── show.blade.php │ │ ├── auth │ │ ├── github_register.blade.php │ │ ├── login.blade.php │ │ ├── passwords │ │ │ ├── email.blade.php │ │ │ └── reset.blade.php │ │ └── register.blade.php │ │ ├── category │ │ ├── index.blade.php │ │ └── show.blade.php │ │ ├── dashboard │ │ └── index.blade.php │ │ ├── discussion │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ ├── index.blade.php │ │ └── show.blade.php │ │ ├── errors │ │ ├── 403.blade.php │ │ ├── 404.blade.php │ │ └── 503.blade.php │ │ ├── layouts │ │ └── app.blade.php │ │ ├── link │ │ └── index.blade.php │ │ ├── mail │ │ ├── followed │ │ │ └── user.blade.php │ │ ├── mention │ │ │ └── user.blade.php │ │ └── receive │ │ │ └── comment.blade.php │ │ ├── notifications │ │ ├── followed-user.blade.php │ │ ├── got-vote.blade.php │ │ ├── mentioned-user.blade.php │ │ └── received-comment.blade.php │ │ ├── pagination │ │ └── default.blade.php │ │ ├── particals │ │ ├── footer.blade.php │ │ ├── jumbotron.blade.php │ │ └── navbar.blade.php │ │ ├── search.blade.php │ │ ├── setting │ │ ├── binding.blade.php │ │ ├── index.blade.php │ │ ├── notification.blade.php │ │ └── particals │ │ │ └── sidebar.blade.php │ │ ├── tag │ │ ├── index.blade.php │ │ └── show.blade.php │ │ ├── user │ │ ├── comments.blade.php │ │ ├── discussions.blade.php │ │ ├── following.blade.php │ │ ├── index.blade.php │ │ ├── notifications.blade.php │ │ ├── particals │ │ │ ├── comments.blade.php │ │ │ ├── discussions.blade.php │ │ │ └── info.blade.php │ │ └── profile.blade.php │ │ ├── vendor │ │ └── notifications │ │ │ ├── email-plain.blade.php │ │ │ └── email.blade.php │ │ └── widgets │ │ └── article.blade.php ├── routes │ ├── api.php │ ├── channels.php │ ├── console.php │ └── web.php ├── server.php ├── storage │ ├── app │ │ ├── .gitignore │ │ └── public │ │ │ └── .gitignore │ ├── framework │ │ ├── .gitignore │ │ ├── cache │ │ │ └── .gitignore │ │ ├── sessions │ │ │ └── .gitignore │ │ └── views │ │ │ └── .gitignore │ ├── logs │ │ └── .gitignore │ ├── oauth-private.key │ └── oauth-public.key ├── tests │ ├── CreatesApplication.php │ ├── Feature │ │ ├── Api │ │ │ ├── ArticleApiTest.php │ │ │ └── UserApiTest.php │ │ └── ExampleTest.php │ ├── TestCase.php │ └── Unit │ │ └── ExampleTest.php ├── webpack.mix.js └── yarn.lock ├── docker-compose.yml ├── vhost.conf └── web.dockerfile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gliterd Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app.dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.1.12-fpm 2 | 3 | RUN apt-get update && apt-get install -y libmcrypt-dev \ 4 | mysql-client libmagickwand-dev --no-install-recommends curl nano \ 5 | && pecl install imagick \ 6 | && docker-php-ext-enable imagick \ 7 | && docker-php-ext-install \ 8 | zip xml gd \ 9 | mcrypt pdo_mysql 10 | -------------------------------------------------------------------------------- /application/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"] 3 | } -------------------------------------------------------------------------------- /application/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false 10 | 11 | [*.{vue,js,scss}] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 2 15 | end_of_line = lf 16 | insert_final_newline = true 17 | trim_trailing_whitespace = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /application/.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | APP_KEY= 3 | APP_DEBUG=true 4 | APP_LOG_LEVEL=debug 5 | APP_URL=http://localhost 6 | 7 | DB_CONNECTION=mysql 8 | DB_HOST=127.0.0.1 9 | DB_PORT=3306 10 | DB_DATABASE=homestead 11 | DB_USERNAME=homestead 12 | DB_PASSWORD=secret 13 | 14 | BROADCAST_DRIVER=log 15 | CACHE_DRIVER=file 16 | SESSION_DRIVER=file 17 | QUEUE_DRIVER=sync 18 | 19 | REDIS_HOST=127.0.0.1 20 | REDIS_PASSWORD=null 21 | REDIS_PORT=6379 22 | 23 | MAIL_DRIVER=smtp 24 | MAIL_HOST= 25 | MAIL_PORT= 26 | MAIL_USERNAME= 27 | MAIL_PASSWORD= 28 | MAIL_ENCRYPTION= 29 | MAIL_FROM=Example 30 | MAIL_NAME=Example 31 | 32 | PUSHER_APP_ID= 33 | PUSHER_APP_KEY= 34 | PUSHER_APP_SECRET= 35 | 36 | GITHUB_CLIENT_ID= 37 | GITHUB_CLIENT_SECRET= 38 | GITHUB_REDIRECT= 39 | 40 | YOUDAO_APP_KEY= 41 | YOUDAO_APP_SECRET= 42 | -------------------------------------------------------------------------------- /application/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | -------------------------------------------------------------------------------- /application/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/storage 3 | /vendor 4 | /.idea 5 | Homestead.json 6 | Homestead.yaml 7 | .env 8 | /public/css 9 | /public/js 10 | /public/fonts 11 | /public/mix-manifest.json 12 | npm-debug.log -------------------------------------------------------------------------------- /application/app/Category.php: -------------------------------------------------------------------------------- 1 | hasMany(Article::Class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /application/app/Comment.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 40 | } 41 | 42 | /** 43 | * Get all of the owning commentable models. 44 | * 45 | * @return \Illuminate\Database\Eloquent\Relations\morphTo 46 | */ 47 | public function commentable() 48 | { 49 | return $this->morphTo(); 50 | } 51 | 52 | /** 53 | * Set the content Attribute. 54 | * 55 | * @param $value 56 | */ 57 | public function setContentAttribute($value) 58 | { 59 | $data = [ 60 | 'raw' => $value, 61 | 'html' => (new Markdowner)->convertMarkdownToHtml($value) 62 | ]; 63 | 64 | $this->attributes['content'] = json_encode($data); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /application/app/Console/Commands/BlogInstall.php: -------------------------------------------------------------------------------- 1 | execShellWithPrettyPrint('php artisan key:generate'); 39 | $this->execShellWithPrettyPrint('php artisan migrate --seed'); 40 | $this->execShellWithPrettyPrint('php artisan passport:install'); 41 | $this->execShellWithPrettyPrint('php artisan storage:link'); 42 | } 43 | 44 | /** 45 | * Exec sheel with pretty print. 46 | * 47 | * @param string $command 48 | * @return mixed 49 | */ 50 | public function execShellWithPrettyPrint($command) 51 | { 52 | $this->info('---'); 53 | $this->info($command); 54 | $output = shell_exec($command); 55 | $this->info($output); 56 | $this->info('---'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /application/app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire') 29 | // ->hourly(); 30 | } 31 | 32 | /** 33 | * Register the Closure based commands for the application. 34 | * 35 | * @return void 36 | */ 37 | protected function commands() 38 | { 39 | require base_path('routes/console.php'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /application/app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 60 | return response()->json(['error' => 'Unauthenticated.'], 401); 61 | } 62 | 63 | return redirect()->guest('login'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /application/app/Exceptions/UploadException.php: -------------------------------------------------------------------------------- 1 | translate($slug); 54 | } 55 | } 56 | 57 | if(!function_exists('lang')) { 58 | /** 59 | * Trans for getting the language. 60 | * 61 | * @param string $text 62 | * @param array $parameters 63 | * @return string 64 | */ 65 | function lang($text, $parameters = []) 66 | { 67 | return trans('blog.'.$text, $parameters); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Api/ApiController.php: -------------------------------------------------------------------------------- 1 | response = new Response(response(), new Transform($manager)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Api/HomeController.php: -------------------------------------------------------------------------------- 1 | user = $user; 26 | $this->visitor = $visitor; 27 | $this->article = $article; 28 | $this->comment = $comment; 29 | } 30 | 31 | public function statistics() 32 | { 33 | $users = $this->user->getNumber(); 34 | $visitors = (int) $this->visitor->getAllClicks(); 35 | $articles = $this->article->getNumber(); 36 | $comments = $this->comment->getNumber(); 37 | 38 | $data = compact('users', 'visitors', 'articles', 'comments'); 39 | 40 | return $this->response->json($data); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Api/MeController.php: -------------------------------------------------------------------------------- 1 | comment = $comment; 17 | } 18 | 19 | /** 20 | * post up vote the comment by user. 21 | * 22 | * @param Request $request 23 | * @param string $type 24 | * 25 | * @return mixed 26 | */ 27 | public function postVoteComment(Request $request, $type) 28 | { 29 | $this->validate($request, [ 30 | 'id' => 'required|exists:comments,id', 31 | ]); 32 | 33 | ($type == 'up') 34 | ? $this->comment->toggleVote($request->id) 35 | : $this->comment->toggleVote($request->id, false); 36 | 37 | return $this->response->withNoContent(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Api/SystemController.php: -------------------------------------------------------------------------------- 1 | getPdo(); 23 | 24 | $version = $pdo->query('select version()')->fetchColumn(); 25 | 26 | $data = [ 27 | 'server' => $_SERVER['SERVER_SOFTWARE'], 28 | 'http_host' => $_SERVER['HTTP_HOST'], 29 | 'remote_host' => isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : $_SERVER['REMOTE_ADDR'], 30 | 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 31 | 'php' => phpversion(), 32 | 'sapi_name' => php_sapi_name(), 33 | 'extensions' => implode(", ", get_loaded_extensions()), 34 | 'db_connection' => isset($_SERVER['DB_CONNECTION']) ? $_SERVER['DB_CONNECTION'] : 'Secret', 35 | 'db_database' => isset($_SERVER['DB_DATABASE']) ? $_SERVER['DB_DATABASE'] : 'Secret', 36 | 'db_version' => $version, 37 | ]; 38 | 39 | return $this->response->json($data); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Api/VisitorController.php: -------------------------------------------------------------------------------- 1 | visitor = $visitor; 16 | } 17 | 18 | /** 19 | * Display a listing of the resource. 20 | * 21 | * @return \Illuminate\Http\JsonResponse 22 | */ 23 | public function index() 24 | { 25 | return $this->response->collection($this->visitor->page()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/ArticleController.php: -------------------------------------------------------------------------------- 1 | article = $article; 16 | } 17 | 18 | /** 19 | * Display the articles resource. 20 | * 21 | * @return mixed 22 | */ 23 | public function index() 24 | { 25 | $articles = $this->article->page(config('blog.article.number'), config('blog.article.sort'), config('blog.article.sortColumn')); 26 | 27 | return view('article.index', compact('articles')); 28 | } 29 | 30 | /** 31 | * Display the article resource by article slug. 32 | * 33 | * @param string $slug 34 | * @return mixed 35 | */ 36 | public function show($slug) 37 | { 38 | $article = $this->article->getBySlug($slug); 39 | 40 | // $article->content = collect(json_decode($article->content))->get('html'); 41 | 42 | return view('article.show', compact('article')); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | middleware('guest', ['except' => 'logout']); 39 | } 40 | 41 | /** 42 | * Log the user out of the application. 43 | * 44 | * @param \Illuminate\Http\Request $request 45 | * @return \Illuminate\Http\Response 46 | */ 47 | public function logout(Request $request) 48 | { 49 | $this->guard()->logout(); 50 | 51 | $request->session()->flush(); 52 | 53 | $request->session()->regenerate(); 54 | 55 | return redirect('/login'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/CategoryController.php: -------------------------------------------------------------------------------- 1 | category = $category; 16 | } 17 | 18 | /** 19 | * Display the categories resource. 20 | * 21 | * @return mixed 22 | */ 23 | public function index() 24 | { 25 | $categories = $this->category->all(); 26 | 27 | return view('category.index', compact('categories')); 28 | } 29 | 30 | /** 31 | * Display the category resource by category name. 32 | * 33 | * @param string $category 34 | * @return mixed 35 | */ 36 | public function show($category) 37 | { 38 | if (!$category = $this->category->getByName($category)) abort(404); 39 | 40 | $articles = $category->articles; 41 | 42 | return view('category.show', compact('category', 'articles')); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | article = $article; 16 | } 17 | 18 | /** 19 | * Display the dashboard page. 20 | * 21 | * @return mixed 22 | */ 23 | public function dashboard() 24 | { 25 | return view('dashboard.index'); 26 | } 27 | 28 | /** 29 | * Search the article by keyword. 30 | * 31 | * @param Request $request 32 | * @return mixed 33 | */ 34 | public function search(Request $request) 35 | { 36 | $key = trim($request->get('q')); 37 | 38 | $articles = $this->article->search($key); 39 | 40 | return view('search', compact('articles')); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/LinkController.php: -------------------------------------------------------------------------------- 1 | link = $link; 16 | } 17 | 18 | /** 19 | * Display the link resource. 20 | * 21 | * @return mix 22 | */ 23 | public function index() 24 | { 25 | $links = $this->link->page(); 26 | 27 | return view('link.index', compact('links')); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/SettingController.php: -------------------------------------------------------------------------------- 1 | user = $user; 15 | } 16 | 17 | /** 18 | * Display the current user setting list. 19 | * 20 | * @return \Illuminate\Http\Response 21 | */ 22 | public function index() 23 | { 24 | return view('setting.index'); 25 | } 26 | 27 | /** 28 | * Display the notification page for setting. 29 | * 30 | * @return \Illuminate\Http\Response 31 | */ 32 | public function notification() 33 | { 34 | return view('setting.notification'); 35 | } 36 | 37 | /** 38 | * Set the email notification. 39 | * 40 | * @param Request $request [description] 41 | * @return Redirect 42 | */ 43 | public function setNotification(Request $request) 44 | { 45 | $input = [ 46 | 'email_notify_enabled' => $request->get('email_notify_enabled') ? 'yes' : 'no' 47 | ]; 48 | 49 | $this->user->update(\Auth::id(), $input); 50 | 51 | return redirect()->back(); 52 | } 53 | 54 | /** 55 | * Display the bindings page. 56 | * 57 | * @return \Illuminate\Http\Response 58 | */ 59 | public function binding() 60 | { 61 | return view('setting.binding'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /application/app/Http/Controllers/TagController.php: -------------------------------------------------------------------------------- 1 | tag = $tag; 16 | } 17 | 18 | /** 19 | * Display the tag resource. 20 | * 21 | * @return mixed 22 | */ 23 | public function index() 24 | { 25 | $tags = $this->tag->all(); 26 | 27 | return view('tag.index', compact('tags')); 28 | } 29 | 30 | /** 31 | * Display the articles and discussions by the tag. 32 | * 33 | * @param string $tag 34 | * @return mixed 35 | */ 36 | public function show($tag) 37 | { 38 | $tag = $this->tag->getByName($tag); 39 | 40 | if (!$tag) abort(404); 41 | 42 | $articles = $tag->articles; 43 | $discussions = $tag->discussions; 44 | 45 | return view('tag.show', compact('tag', 'articles', 'discussions')); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /application/app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | is_admin) { 21 | abort(404); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /application/app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 21 | return redirect('/'); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /application/app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 'required|min:3', 28 | 'subtitle' => 'required|min:3', 29 | 'content' => 'required', 30 | 'category_id' => 'required', 31 | 'published_at' => 'required', 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /application/app/Http/Requests/CategoryRequest.php: -------------------------------------------------------------------------------- 1 | 'required|min:2', 28 | 'path' => 'required', 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /application/app/Http/Requests/CommentRequest.php: -------------------------------------------------------------------------------- 1 | 'required' 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /application/app/Http/Requests/DiscussionRequest.php: -------------------------------------------------------------------------------- 1 | 'required|min:2', 28 | 'content' => 'required', 29 | 'tags' => 'required', 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/app/Http/Requests/ImageRequest.php: -------------------------------------------------------------------------------- 1 | 'image|mimes:jpeg,jpg,png,gif' 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /application/app/Http/Requests/LinkRequest.php: -------------------------------------------------------------------------------- 1 | 'required|min:2', 28 | 'link' => 'required|min:2' 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /application/app/Http/Requests/TagRequest.php: -------------------------------------------------------------------------------- 1 | 'required|unique:tags', 28 | 'title' => 'required', 29 | 'meta_description' => 'required' 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/app/Http/Requests/UserRequest.php: -------------------------------------------------------------------------------- 1 | 'required|unique:users', 28 | 'email' => 'required|email|unique:users', 29 | 'password_confirmation' => 'required', 30 | 'password' => 'required|confirmed', 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /application/app/Link.php: -------------------------------------------------------------------------------- 1 | user = $user; 27 | } 28 | 29 | /** 30 | * Get the notification's delivery channels. 31 | * 32 | * @param mixed $notifiable 33 | * @return array 34 | */ 35 | public function via($notifiable) 36 | { 37 | return ['database', 'mail']; 38 | } 39 | 40 | /** 41 | * Get the mail representation of the notification. 42 | * 43 | * @param mixed $notifiable 44 | * @return \Illuminate\Notifications\Messages\MailMessage 45 | */ 46 | public function toMail($notifiable) 47 | { 48 | $message = lang('Followed Content', [ 'user' => $this->user->name]); 49 | 50 | $data = [ 51 | 'username' => $notifiable->name, 52 | 'message' => $message, 53 | 'url' => url('user', [ 'username' => $this->user->name ]) 54 | ]; 55 | 56 | return (new MailMessage) 57 | ->subject(lang('Someone Followed')) 58 | ->markdown('mail.followed.user', $data); 59 | } 60 | 61 | /** 62 | * Get the array representation of the notification. 63 | * 64 | * @param mixed $notifiable 65 | * @return array 66 | */ 67 | public function toArray($notifiable) 68 | { 69 | return $this->user->toArray(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /application/app/Notifications/GotVote.php: -------------------------------------------------------------------------------- 1 | vote_type = $vote_type; 28 | $this->user = $user; 29 | $this->comment = $comment; 30 | } 31 | 32 | /** 33 | * Get the notification's delivery channels. 34 | * 35 | * @param mixed $notifiable 36 | * @return array 37 | */ 38 | public function via($notifiable) 39 | { 40 | return ['database']; 41 | } 42 | 43 | /** 44 | * Get the mail representation of the notification. 45 | * 46 | * @param mixed $notifiable 47 | * @return \Illuminate\Notifications\Messages\MailMessage 48 | */ 49 | public function toMail($notifiable) 50 | { 51 | return (new MailMessage) 52 | ->line('The introduction to the notification.') 53 | ->action('Notification Action', url('/')) 54 | ->line('Thank you for using our application!'); 55 | } 56 | 57 | /** 58 | * Get the array representation of the notification. 59 | * 60 | * @param mixed $notifiable 61 | * @return array 62 | */ 63 | public function toArray($notifiable) 64 | { 65 | return array( 66 | 'comment_id' => $this->comment->id, 67 | 'issuer_id' => $this->user->id, 68 | 'commentable_id' => $this->comment->commentable_id, 69 | 'vote_type' => $this->vote_type 70 | ); 71 | } 72 | } -------------------------------------------------------------------------------- /application/app/Policies/CommentPolicy.php: -------------------------------------------------------------------------------- 1 | is_admin || $comment->user_id === $user->id; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /application/app/Policies/DiscussionPolicy.php: -------------------------------------------------------------------------------- 1 | is_admin || $user->id === $discussion->user_id; 23 | } 24 | 25 | /** 26 | * Determine whether the user can delete the discussion. 27 | * 28 | * @param \App\User $user 29 | * @param \App\Discussion $discussion 30 | * @return mixed 31 | */ 32 | public function delete(User $user, Discussion $discussion) 33 | { 34 | return $user->is_admin; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /application/app/Policies/UserPolicy.php: -------------------------------------------------------------------------------- 1 | id === $user->id; 22 | } 23 | 24 | /** 25 | * Determine whether the current user can delete the user. 26 | * 27 | * @param \App\User $currentUser 28 | * @param \App\User $user 29 | * @return mixed 30 | */ 31 | public function delete(User $currentUser, User $user) 32 | { 33 | return $currentUser->is_admin; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /application/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | Discussion::class, 26 | 'articles' => Article::class, 27 | ]); 28 | } 29 | 30 | /** 31 | * Register any application services. 32 | * 33 | * @return void 34 | */ 35 | public function register() 36 | { 37 | $this->app->singleton('uploader', function ($app) { 38 | $config = config('filesystems.default', 'public'); 39 | 40 | if ($config == 'upyun') { 41 | return new UpyunManager(); 42 | } 43 | 44 | return new BaseManager(); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /application/app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | \App\Policies\UserPolicy::class, 19 | \App\Comment::class => \App\Policies\CommentPolicy::class, 20 | \App\Discussion::class => \App\Policies\DiscussionPolicy::class, 21 | ]; 22 | 23 | /** 24 | * Register any authentication / authorization services. 25 | * 26 | * @return void 27 | */ 28 | public function boot() 29 | { 30 | $this->registerPolicies(); 31 | 32 | Passport::routes(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /application/app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'App\Listeners\EventListener', 18 | ], 19 | ]; 20 | 21 | /** 22 | * Register any events for your application. 23 | * 24 | * @return void 25 | */ 26 | public function boot() 27 | { 28 | parent::boot(); 29 | 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 37 | 38 | $this->mapWebRoutes(); 39 | 40 | Route::post('/oauth/token', [ 41 | 'uses' => '\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken', 42 | ]); 43 | } 44 | 45 | /** 46 | * Define the "web" routes for the application. 47 | * 48 | * These routes all receive session state, CSRF protection, etc. 49 | * 50 | * @return void 51 | */ 52 | protected function mapWebRoutes() 53 | { 54 | Route::middleware('web') 55 | ->namespace($this->namespace) 56 | ->group(base_path('routes/web.php')); 57 | } 58 | 59 | /** 60 | * Define the "api" routes for the application. 61 | * 62 | * These routes are typically stateless. 63 | * 64 | * @return void 65 | */ 66 | protected function mapApiRoutes() 67 | { 68 | Route::prefix('api') 69 | ->middleware('api') 70 | ->namespace($this->namespace) 71 | ->group(base_path('routes/api.php')); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /application/app/Repositories/CategoryRepository.php: -------------------------------------------------------------------------------- 1 | model = $category; 16 | } 17 | 18 | /** 19 | * Get record by the name. 20 | * 21 | * @param string $name 22 | * @return collection 23 | */ 24 | public function getByName($name) 25 | { 26 | return $this->model->where('name', $name)->first(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /application/app/Repositories/LinkRepository.php: -------------------------------------------------------------------------------- 1 | model = $link; 17 | } 18 | 19 | /** 20 | * Get number of the records 21 | * 22 | * @param int $number 23 | * @param string $sort 24 | * @param string $sortColumn 25 | * @return Paginate 26 | */ 27 | public function page($number = 10, $sort = 'desc', $sortColumn = 'created_at') 28 | { 29 | $this->model = $this->checkAuthScope(); 30 | 31 | return $this->model->orderBy($sortColumn, $sort)->paginate($number); 32 | } 33 | 34 | /** 35 | * Get the article record without draft scope. 36 | * 37 | * @param int $id 38 | * @return mixed 39 | */ 40 | public function getById($id) 41 | { 42 | return $this->model->withoutGlobalScope(StatusScope::class)->findOrFail($id); 43 | } 44 | 45 | /** 46 | * Update the article record without draft scope. 47 | * 48 | * @param int $id 49 | * @param array $input 50 | * @return boolean 51 | */ 52 | public function update($id, $input) 53 | { 54 | $this->model = $this->model->withoutGlobalScope(StatusScope::class)->findOrFail($id); 55 | 56 | return $this->save($this->model, $input); 57 | } 58 | 59 | /** 60 | * Check the auth and the model without global scope when user is the admin. 61 | * 62 | * @return Model 63 | */ 64 | public function checkAuthScope() 65 | { 66 | if (auth()->check() && auth()->user()->is_admin) { 67 | $this->model = $this->model->withoutGlobalScope(StatusScope::class); 68 | } 69 | 70 | return $this->model; 71 | } 72 | 73 | /** 74 | * Delete the draft article. 75 | * 76 | * @param int $id 77 | * @return boolean 78 | */ 79 | public function destroy($id) 80 | { 81 | return $this->getById($id)->delete(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /application/app/Repositories/TagRepository.php: -------------------------------------------------------------------------------- 1 | model = $tag; 16 | } 17 | 18 | /** 19 | * Get record by the name. 20 | * 21 | * @param string $name 22 | * @return collection 23 | */ 24 | public function getByName($name) 25 | { 26 | return $this->model->where('tag', $name)->first(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /application/app/Repositories/VisitorRepository.php: -------------------------------------------------------------------------------- 1 | model = $visitor->with('article'); 30 | 31 | $this->ip = $ip; 32 | } 33 | 34 | /** 35 | * Update or create the record of visitors table 36 | * 37 | * @param $article_id 38 | */ 39 | public function log($article_id) 40 | { 41 | $ip = $this->ip->get(); 42 | 43 | if ($this->hasArticleIp($article_id, $ip)) { 44 | 45 | $this->model->where('article_id', $article_id) 46 | ->where('ip', $ip) 47 | ->increment('clicks'); 48 | 49 | } else { 50 | $data = [ 51 | 'ip' => $ip, 52 | 'article_id' => $article_id, 53 | 'clicks' => 1 54 | ]; 55 | $this->model->firstOrCreate( $data ); 56 | } 57 | } 58 | 59 | /** 60 | * Check the record by article id and ip if it exists. 61 | * 62 | * @param $article_id 63 | * @param $ip 64 | * @return bool 65 | */ 66 | public function hasArticleIp($article_id, $ip) 67 | { 68 | return $this->model 69 | ->where('article_id', $article_id) 70 | ->where('ip', $ip) 71 | ->count() ? true : false; 72 | } 73 | 74 | /** 75 | * Get all the clicks. 76 | * 77 | * @return int 78 | */ 79 | public function getAllClicks() 80 | { 81 | return $this->model->sum('clicks'); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /application/app/Scopes/DraftScope.php: -------------------------------------------------------------------------------- 1 | where('is_draft', 0); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /application/app/Scopes/StatusScope.php: -------------------------------------------------------------------------------- 1 | where('status', 1); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /application/app/Tag.php: -------------------------------------------------------------------------------- 1 | morphedByMany(Article::class, 'taggable'); 36 | } 37 | 38 | /** 39 | * Get all of the discussions that are assigned this tag. 40 | * 41 | * @return \Illuminate\Database\Eloquent\Relations\MorpheBymany 42 | */ 43 | public function discussions() 44 | { 45 | return $this->morphedByMany(Discussion::class, 'taggable'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /application/app/Tools/IP.php: -------------------------------------------------------------------------------- 1 | request = $request; 21 | } 22 | 23 | /** 24 | * Get the client ip. 25 | * 26 | * @return mixed|string 27 | */ 28 | public function get() 29 | { 30 | $ip = $this->request->getClientIp(); 31 | 32 | if($ip == '::1') { 33 | $ip = '127.0.0.1'; 34 | } 35 | 36 | return $ip; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /application/app/Tools/Markdowner.php: -------------------------------------------------------------------------------- 1 | htmlConverter = new HtmlConverter(); 26 | 27 | $this->markdownConverter = new Parsedown(); 28 | } 29 | 30 | /** 31 | * Convert Markdown To Html. 32 | * 33 | * @param $markdown 34 | * @return string 35 | */ 36 | public function convertMarkdownToHtml($markdown) 37 | { 38 | return $this->markdownConverter 39 | ->setBreaksEnabled(true) 40 | ->text($markdown); 41 | } 42 | 43 | /** 44 | * Convert Html To Markdown. 45 | * 46 | * @param $html 47 | * @return string 48 | */ 49 | public function convertHtmlToMarkdown($html) 50 | { 51 | return $this->htmlConverter->convert($html); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /application/app/Tools/Mention.php: -------------------------------------------------------------------------------- 1 | content, $atlist_tmp); 25 | 26 | $usernames = []; 27 | 28 | foreach ($atlist_tmp[2] as $k=>$v) { 29 | if ($atlist_tmp[1][$k] || strlen($v) >25) { 30 | continue; 31 | } 32 | $usernames[] = $v; 33 | } 34 | return array_unique($usernames); 35 | } 36 | 37 | /** 38 | * Replace the `@{user}` by the user link 39 | */ 40 | public function replace() 41 | { 42 | $this->content_parsed = $this->content; 43 | 44 | foreach ($this->users as $user) { 45 | $search = '@' . $user->name; 46 | 47 | $place = '[' . $search . '](' . url('user', $user->name) . ')'; 48 | 49 | $this->content_parsed = str_replace($search, $place, $this->content_parsed); 50 | } 51 | } 52 | 53 | /** 54 | * Parse the `@` mention user in content. 55 | * 56 | * @param string $content 57 | * @return string 58 | */ 59 | public function parse($content) 60 | { 61 | $this->content = $content; 62 | 63 | $this->usernames = $this->getMentionedUsername(); 64 | 65 | count($this->usernames) > 0 && $this->users = User::whereIn('name', $this->usernames)->get(); 66 | 67 | $this->replace(); 68 | 69 | return $this->content_parsed; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /application/app/Traits/FollowTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | namespace App\Traits; 11 | 12 | /** 13 | * Trait FollowTrait. 14 | */ 15 | trait FollowTrait 16 | { 17 | /** 18 | * Follow a user or users. 19 | * 20 | * @param int|array $user 21 | * 22 | * @return int 23 | */ 24 | public function follow($user) 25 | { 26 | return $this->followings()->sync((array)$user, false); 27 | } 28 | 29 | /** 30 | * Unfollow a user or users. 31 | * 32 | * @param int|array $user 33 | * 34 | * @return int 35 | */ 36 | public function unfollow($user) 37 | { 38 | return $this->followings()->detach((array)$user); 39 | } 40 | 41 | /** 42 | * Check if user is following given user. 43 | * 44 | * @param $user 45 | * 46 | * @return bool 47 | */ 48 | public function isFollowing($user) 49 | { 50 | return $this->followings->contains($user); 51 | } 52 | 53 | /** 54 | * Check if user is followed by given user. 55 | * 56 | * @param $user 57 | * 58 | * @return bool 59 | */ 60 | public function isFollowedBy($user) 61 | { 62 | return $this->followers->contains($user); 63 | } 64 | 65 | /** 66 | * Return user followers. 67 | * 68 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 69 | */ 70 | public function followers() 71 | { 72 | return $this->belongsToMany(__CLASS__, 'followers', 'follow_id', 'user_id')->withTimestamps(); 73 | } 74 | 75 | /** 76 | * Return user following users. 77 | * 78 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 79 | */ 80 | public function followings() 81 | { 82 | return $this->belongsToMany(__CLASS__, 'followers', 'user_id', 'follow_id')->withTimestamps(); 83 | } 84 | } -------------------------------------------------------------------------------- /application/app/Transformers/ArticleTransformer.php: -------------------------------------------------------------------------------- 1 | $article->id, 19 | 'title' => $article->title, 20 | 'subtitle' => $article->subtitle, 21 | 'user' => $article->user, 22 | 'slug' => $article->slug, 23 | 'content' => $article->content['raw'], 24 | 'page_image' => $article->page_image, 25 | 'meta_description' => $article->meta_description, 26 | 'is_original' => $article->is_original, 27 | 'is_draft' => $article->is_draft, 28 | 'visitors' => $article->view_count, 29 | 'published_at' => $article->published_at->diffForHumans(), 30 | 'published_time' => $article->published_at->toDateTimeString(), 31 | ]; 32 | } 33 | 34 | /** 35 | * Include Category 36 | * 37 | * @param Article $article 38 | * @return \League\Fractal\Resource\Collection 39 | */ 40 | public function includeCategory(Article $article) 41 | { 42 | $category = $article->category; 43 | 44 | return $this->item($category, new CategoryTransformer); 45 | } 46 | 47 | /** 48 | * Include Tags 49 | * 50 | * @param Article $article 51 | * @return \League\Fractal\Resource\Collection 52 | */ 53 | public function includeTags(Article $article) 54 | { 55 | $tags = $article->tags; 56 | 57 | return $this->collection($tags, new TagTransformer); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /application/app/Transformers/CategoryTransformer.php: -------------------------------------------------------------------------------- 1 | $category->id, 14 | 'name' => $category->name, 15 | 'path' => $category->path, 16 | 'description' => $category->description, 17 | 'status' => $category->status, 18 | 'created_at' => $category->created_at->toDateTimeString(), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application/app/Transformers/CommentTransformer.php: -------------------------------------------------------------------------------- 1 | content); 16 | 17 | return [ 18 | 'id' => $comment->id, 19 | 'user_id' => $comment->user_id, 20 | 'username' => isset($comment->user) ? $comment->user->name : 'Null', 21 | 'avatar' => isset($comment->user) ? $comment->user->avatar : config('blog.default_avatar'), 22 | 'commentable' => isset($comment->commentable) ? $comment->commentable->title : 'Be Forbidden!', 23 | 'type' => $comment->commentable_type, 24 | 'content_raw' => $content->raw, 25 | 'created_at' => $comment->created_at->diffForHumans(), 26 | 'is_voted' => auth()->guard('api')->id() ? $comment->isVotedBy(auth()->guard('api')->id()) : false, 27 | 'is_up_voted' => auth()->guard('api')->id() ? auth()->guard('api')->user()->hasUpVoted($comment) : false, 28 | 'is_down_voted' => auth()->guard('api')->id() ? auth()->guard('api')->user()->hasDownVoted($comment) : false, 29 | 'vote_count' => $comment->countUpVoters(), 30 | ]; 31 | } 32 | 33 | /** 34 | * Include User 35 | * 36 | * @param Comment $comment 37 | * @return \League\Fractal\Resource\Collection 38 | */ 39 | public function includeUser(Comment $comment) 40 | { 41 | $user = $comment->user; 42 | 43 | return $this->item($user, new UserTransformer); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /application/app/Transformers/EmptyTransformer.php: -------------------------------------------------------------------------------- 1 | $link->id, 14 | 'name' => $link->name, 15 | 'image' => $link->image, 16 | 'link' => $link->link, 17 | 'status' => (bool) $link->status, 18 | 'created_at' => $link->created_at->toDateTimeString(), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application/app/Transformers/TagTransformer.php: -------------------------------------------------------------------------------- 1 | $tag->id, 14 | 'tag' => $tag->tag, 15 | 'title' => $tag->title, 16 | 'meta_description' => $tag->meta_description, 17 | 'status' => $tag->status, 18 | 'created_at' => $tag->created_at->toDateTimeString(), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application/app/Transformers/UserTransformer.php: -------------------------------------------------------------------------------- 1 | $user->id, 14 | 'avatar' => $user->avatar, 15 | 'name' => $user->name, 16 | 'status' => $user->status, 17 | 'email' => $user->email, 18 | 'nickname' => $user->nickname, 19 | 'is_admin' => $user->is_admin, 20 | 'github_name' => $user->github_name, 21 | 'website' => $user->website, 22 | 'description' => $user->description, 23 | 'created_at' => $user->created_at->toDateTimeString(), 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /application/app/Transformers/VisitorTransformer.php: -------------------------------------------------------------------------------- 1 | $visitor->id, 14 | 'article' => [ 'title' => isset($visitor->article) ? $visitor->article->title : 'null' ], 15 | 'ip' => $visitor->ip, 16 | 'country' => $visitor->country, 17 | 'clicks' => $visitor->clicks, 18 | 'created_at' => $visitor->created_at->toDateTimeString(), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application/app/Visitor.php: -------------------------------------------------------------------------------- 1 | belongsTo(Article::class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /application/artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 32 | 33 | $status = $kernel->handle( 34 | $input = new Symfony\Component\Console\Input\ArgvInput, 35 | new Symfony\Component\Console\Output\ConsoleOutput 36 | ); 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Shutdown The Application 41 | |-------------------------------------------------------------------------- 42 | | 43 | | Once Artisan has finished running. We will fire off the shutdown events 44 | | so that any final work may be done by the application before we shut 45 | | down the process. This is the last thing to happen to the request. 46 | | 47 | */ 48 | 49 | $kernel->terminate($input, $status); 50 | 51 | exit($status); 52 | -------------------------------------------------------------------------------- /application/bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /application/bootstrap/autoload.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | // 40 | ], 41 | ], 42 | 43 | 'redis' => [ 44 | 'driver' => 'redis', 45 | 'connection' => 'default', 46 | ], 47 | 48 | 'log' => [ 49 | 'driver' => 'log', 50 | ], 51 | 52 | 'null' => [ 53 | 'driver' => 'null', 54 | ], 55 | 56 | ], 57 | 58 | ]; -------------------------------------------------------------------------------- /application/config/compile.php: -------------------------------------------------------------------------------- 1 | [ 17 | // 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled File Providers 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may list service providers which define a "compiles" function 26 | | that returns additional files that should be compiled, providing an 27 | | easy way to get common files from any packages you are utilizing. 28 | | 29 | */ 30 | 31 | 'providers' => [ 32 | // 33 | ], 34 | 35 | ]; 36 | -------------------------------------------------------------------------------- /application/config/image.php: -------------------------------------------------------------------------------- 1 | 'gd' 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /application/config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | ], 21 | 22 | 'ses' => [ 23 | 'key' => env('SES_KEY'), 24 | 'secret' => env('SES_SECRET'), 25 | 'region' => 'us-east-1', 26 | ], 27 | 28 | 'sparkpost' => [ 29 | 'secret' => env('SPARKPOST_SECRET'), 30 | ], 31 | 32 | 'stripe' => [ 33 | 'model' => App\User::class, 34 | 'key' => env('STRIPE_KEY'), 35 | 'secret' => env('STRIPE_SECRET'), 36 | ], 37 | 38 | // Third-party login configuration 39 | 'github' => [ 40 | 'client_id' => env('GITHUB_CLIENT_ID'), 41 | 'client_secret' => env('GITHUB_CLIENT_SECRET'), 42 | 'redirect' => env('GITHUB_REDIRECT'), 43 | ], 44 | 45 | // Translation 46 | 'youdao' => [ 47 | 'appKey' => env('YOUDAO_APP_KEY'), 48 | 'appSecret' => env('YOUDAO_APP_SECRET'), 49 | ], 50 | 51 | ]; 52 | -------------------------------------------------------------------------------- /application/config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | realpath(base_path('resources/views')), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => realpath(storage_path('framework/views')), 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /application/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /application/database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /application/database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name')->unique(); 19 | $table->string('nickname')->nullable(); 20 | $table->text('avatar')->nullable(); 21 | $table->string('email')->unique(); 22 | $table->string('confirm_code', 64)->unique()->nullable(); 23 | $table->tinyInteger('status')->default(false); 24 | $table->boolean('is_admin')->default(false); 25 | $table->string('password'); 26 | $table->string('github_id')->nullable(); 27 | $table->string('github_name')->nullable(); 28 | $table->string('github_url')->nullable(); 29 | $table->string('weibo_name')->nullable(); 30 | $table->string('weibo_link')->nullable(); 31 | $table->string('website')->nullable(); 32 | $table->string('description')->nullable(); 33 | $table->enum('email_notify_enabled', ['yes', 'no'])->default('yes')->index(); 34 | $table->rememberToken(); 35 | $table->timestamps(); 36 | $table->softDeletes(); 37 | }); 38 | } 39 | 40 | /** 41 | * Reverse the migrations. 42 | * 43 | * @return void 44 | */ 45 | public function down() 46 | { 47 | Schema::drop('users'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /application/database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token')->index(); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::drop('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_065857_create_articles_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('category_id')->unsigned(); 19 | $table->integer('user_id')->unsigned(); 20 | $table->integer('last_user_id')->unsigned(); 21 | $table->string('slug')->unique(); 22 | $table->string('title'); 23 | $table->string('subtitle'); 24 | // $table->json('content'); 25 | $table->text('content'); 26 | $table->string('page_image')->nullable(); 27 | $table->string('meta_description')->nullable(); 28 | $table->boolean('is_original')->default(false); 29 | $table->boolean('is_draft')->default(false); 30 | $table->integer('view_count')->unsigned()->default(0)->index(); 31 | $table->timestamp('published_at')->nullable(); 32 | $table->timestamps(); 33 | $table->softDeletes(); 34 | }); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function down() 43 | { 44 | Schema::dropIfExists('articles'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_065920_create_tags_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('tag')->unique(); 19 | $table->string('title'); 20 | $table->string('meta_description'); 21 | $table->timestamps(); 22 | $table->softDeletes(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('tags'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_065952_create_visitors_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('article_id')->unsigned(); 19 | $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade'); 20 | $table->string('ip', 32); 21 | $table->string('country')->nullable(); 22 | $table->integer('clicks')->unsigned()->default(1); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('visitors'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_070119_create_categories_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->tinyInteger('parent_id')->unsigned()->default(0); 19 | $table->string('name'); 20 | $table->string('path'); 21 | $table->string('description')->nullable(); 22 | $table->string('image_url')->nullable(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('categories'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_070132_create_discussions_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned(); 19 | $table->integer('last_user_id')->unsigned(); 20 | $table->string('title'); 21 | // $table->json('content'); 22 | $table->text('content'); 23 | $table->tinyInteger('status')->default(false); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('discussions'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_02_070151_create_comments_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned(); 19 | $table->integer('commentable_id')->unsigned(); 20 | $table->char('commentable_type'); 21 | // $table->json('content'); 22 | $table->text('content'); 23 | $table->timestamps(); 24 | $table->softDeletes(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('comments'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /application/database/migrations/2016_09_13_022056_create_links_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name')->index(); 19 | $table->string('link')->index(); 20 | $table->text('image')->nullable(); 21 | $table->boolean('status')->default(true); 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('links'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /application/database/migrations/2016_11_11_163610_create_taggables_table.php: -------------------------------------------------------------------------------- 1 | integer('tag_id')->unsigned()->index(); 18 | $table->integer('taggable_id')->unsigned()->index(); 19 | $table->string('taggable_type')->index(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('taggables'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /application/database/migrations/2016_12_11_153312_create_followers_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->unsignedInteger('user_id'); 19 | $table->unsignedInteger('follow_id'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('followers'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /application/database/migrations/2016_12_12_171655_create_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 17 | $table->string('type'); 18 | $table->morphs('notifiable'); 19 | $table->text('data'); 20 | $table->timestamp('read_at')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('notifications'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /application/database/migrations/2016_12_12_205419_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->text('connection'); 19 | $table->text('queue'); 20 | $table->longText('payload'); 21 | $table->longText('exception'); 22 | $table->timestamp('failed_at')->useCurrent(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('failed_jobs'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /application/database/migrations/2017_04_14_013622_create_votes_table.php: -------------------------------------------------------------------------------- 1 | unsignedInteger('user_id'); 17 | $table->unsignedInteger('votable_id'); 18 | $table->string('votable_type')->index(); 19 | $table->enum('type', ['up_vote', 'down_vote'])->default('up_vote'); 20 | 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('votes'); 33 | } 34 | } -------------------------------------------------------------------------------- /application/database/seeds/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /application/database/seeds/ArticlesTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/database/seeds/CategoriesTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/database/seeds/CommentsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/database/seeds/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call(UsersTableSeeder::class); 15 | $this->call(CategoriesTableSeeder::class); 16 | $this->call(ArticlesTableSeeder::class); 17 | $this->call(DiscussionsTableSeeder::class); 18 | $this->call(CommentsTableSeeder::class); 19 | $this->call(TagsTableSeeder::class); 20 | $this->call(LinksTableSeeder::class); 21 | $this->call(VisitorsTableSeeder::class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /application/database/seeds/DiscussionsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/database/seeds/LinksTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /application/database/seeds/TagsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /application/database/seeds/UsersTableSeeder.php: -------------------------------------------------------------------------------- 1 | 'admin', 19 | 'email' => 'admin@pigjian.com', 20 | 'password' => Hash::make('admin'), 21 | 'status' => true, 22 | 'is_admin' => true, 23 | 'confirm_code' => str_random(64), 24 | 'created_at' => Carbon::now(), 25 | 'updated_at' => Carbon::now() 26 | ] 27 | ]; 28 | 29 | DB::table('users')->insert($users); 30 | 31 | factory(User::class, 10)->create(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /application/database/seeds/VisitorsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.15.2", 14 | "babel-preset-es2015": "^6.24.1", 15 | "babel-preset-stage-2": "^6.24.1", 16 | "bootstrap-sass": "^3.3.7", 17 | "chart.js": "^2.4.0", 18 | "cropperjs": "^1.0.0-rc.1", 19 | "cross-env": "^5.0.5", 20 | "emojione": "^2.2.7", 21 | "fine-uploader": "^5.14.2-rc1", 22 | "ionicons": "^2.0.1", 23 | "jquery": "^3.1.0", 24 | "laravel-mix": "^1.4.0", 25 | "lodash": "^4.16.2", 26 | "marked": "^0.3.6", 27 | "path-to-regexp": "^1.7.0", 28 | "simplemde": "^1.11.2", 29 | "social-share.js": "^1.0.15", 30 | "sweetalert": "^1.1.3", 31 | "toastr": "^2.1.2", 32 | "v-textcomplete": "^0.1.2", 33 | "vue": "^2.1.6", 34 | "vue-datepicker": "^1.3.0", 35 | "vue-i18n": "v6.0.0-alpha.5", 36 | "vue-multiselect": "^2.0.0-beta.15", 37 | "vue-router": "^2.1.1", 38 | "vuex": "^2.1.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /application/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests 14 | 15 | 16 | 17 | 18 | ./app 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /application/public/.gitignore: -------------------------------------------------------------------------------- 1 | /uploads/avatar 2 | /uploads/links -------------------------------------------------------------------------------- /application/public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Redirect Trailing Slashes If Not A Folder... 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteRule ^(.*)/$ /$1 [L,R=301] 11 | 12 | # Handle Front Controller... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteRule ^ index.php [L] 16 | 17 | # Handle Authorization Header 18 | RewriteCond %{HTTP:Authorization} . 19 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 20 | 21 | -------------------------------------------------------------------------------- /application/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gliterd/docker-laravel-vuejs/7c0edead165ad15af9f1636548c5865181af2b65/application/public/favicon.ico -------------------------------------------------------------------------------- /application/public/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gliterd/docker-laravel-vuejs/7c0edead165ad15af9f1636548c5865181af2b65/application/public/images/default.png -------------------------------------------------------------------------------- /application/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gliterd/docker-laravel-vuejs/7c0edead165ad15af9f1636548c5865181af2b65/application/public/images/favicon.ico -------------------------------------------------------------------------------- /application/public/index.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | /* 10 | |-------------------------------------------------------------------------- 11 | | Register The Auto Loader 12 | |-------------------------------------------------------------------------- 13 | | 14 | | Composer provides a convenient, automatically generated class loader for 15 | | our application. We just need to utilize it! We'll simply require it 16 | | into the script here so that we don't have to worry about manual 17 | | loading any of our classes later on. It feels nice to relax. 18 | | 19 | */ 20 | 21 | require __DIR__.'/../bootstrap/autoload.php'; 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Turn On The Lights 26 | |-------------------------------------------------------------------------- 27 | | 28 | | We need to illuminate PHP development, so let us turn on the lights. 29 | | This bootstraps the framework and gets it ready for use, then it 30 | | will load up this application so that we can run it and send 31 | | the responses back to the browser and delight our users. 32 | | 33 | */ 34 | 35 | $app = require_once __DIR__.'/../bootstrap/app.php'; 36 | 37 | /* 38 | |-------------------------------------------------------------------------- 39 | | Run The Application 40 | |-------------------------------------------------------------------------- 41 | | 42 | | Once we have the application, we can handle the incoming request 43 | | through the kernel, and send the associated response back to 44 | | the client's browser allowing them to enjoy the creative 45 | | and wonderful application we have prepared for them. 46 | | 47 | */ 48 | 49 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 50 | 51 | $response = $kernel->handle( 52 | $request = Illuminate\Http\Request::capture() 53 | ); 54 | 55 | $response->send(); 56 | 57 | $kernel->terminate($request, $response); 58 | -------------------------------------------------------------------------------- /application/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /application/resources/assets/js/App.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/assets/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * First we will load all of this project's JavaScript dependencies which 4 | * include Vue and Vue Resource. This gives a great starting point for 5 | * building robust, powerful web applications using Vue and Laravel. 6 | */ 7 | 8 | require('./bootstrap'); 9 | 10 | import httpPlugin from 'plugins/http'; 11 | import VueRouter from 'vue-router'; 12 | import store from './vuex/store.js'; 13 | import VueI18n from 'vue-i18n'; 14 | import 'vue-multiselect/dist/vue-multiselect.min.css'; 15 | 16 | import routes from './routes.js'; 17 | import locales from 'lang'; 18 | 19 | import App from './App.vue'; 20 | 21 | window.toastr = require('toastr/build/toastr.min.js'); 22 | window.innerHeight = 800; 23 | 24 | window.toastr.options = { 25 | positionClass: "toast-bottom-right", 26 | showDuration: "300", 27 | hideDuration: "1000", 28 | timeOut: "5000", 29 | extendedTimeOut: "1000", 30 | showEasing: "swing", 31 | hideEasing: "linear", 32 | showMethod: "fadeIn", 33 | hideMethod: "fadeOut" 34 | }; 35 | 36 | Vue.use(httpPlugin); 37 | Vue.use(VueI18n); 38 | Vue.use(VueRouter); 39 | 40 | Vue.config.lang = window.Language; 41 | 42 | const i18n = new VueI18n({ 43 | locale: Vue.config.lang, 44 | messages: locales 45 | }) 46 | 47 | Vue.component( 48 | 'vue-table-pagination', 49 | require('components/dashboard/TablePagination.vue') 50 | ); 51 | 52 | Vue.component( 53 | 'vue-table', 54 | require('components/dashboard/Table.vue') 55 | ); 56 | 57 | Vue.component( 58 | 'vue-form', 59 | require('components/dashboard/Form.vue') 60 | ); 61 | 62 | Vue.component( 63 | 'chart', 64 | require('./components/Chartjs.vue') 65 | ); 66 | 67 | const router = new VueRouter({ 68 | mode: 'history', 69 | base: __dirname, 70 | linkActiveClass: 'active', 71 | routes: routes 72 | }); 73 | 74 | new Vue({ 75 | router, 76 | store, 77 | i18n, 78 | render: (h) => h(App) 79 | }).$mount('#app'); 80 | -------------------------------------------------------------------------------- /application/resources/assets/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | 2 | window._ = require('lodash'); 3 | 4 | /** 5 | * We'll load jQuery and the Bootstrap jQuery plugin which provides support 6 | * for JavaScript based Bootstrap features such as modals and tabs. This 7 | * code may be modified to fit the specific needs of your application. 8 | */ 9 | 10 | window.$ = window.jQuery = require('jquery'); 11 | require('bootstrap-sass'); 12 | window.swal = require('sweetalert'); 13 | 14 | /** 15 | * Vue is a modern JavaScript library for building interactive web interfaces 16 | * using reactive data binding and reusable components. Vue's API is clean 17 | * and simple, leaving you to focus on building your next great project. 18 | */ 19 | 20 | window.Vue = require('vue'); 21 | 22 | /** 23 | * Echo exposes an expressive API for subscribing to channels and listening 24 | * for events that are broadcast by Laravel. Echo and event broadcasting 25 | * allows your team to easily build robust real-time web applications. 26 | */ 27 | 28 | // import Echo from "laravel-echo" 29 | 30 | // window.Echo = new Echo({ 31 | // broadcaster: 'pusher', 32 | // key: 'your-pusher-key' 33 | // }); 34 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/Chartjs.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 54 | 55 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/Parse.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/Textarea.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/dashboard/Form.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/dashboard/TablePagination.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 42 | 43 | 52 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/dashboard/particals/FooterBar.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/assets/js/components/dashboard/particals/Navbar.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 36 | 37 | 69 | -------------------------------------------------------------------------------- /application/resources/assets/js/config/base.js: -------------------------------------------------------------------------------- 1 | export const apiUrl = '/api/' 2 | -------------------------------------------------------------------------------- /application/resources/assets/js/config/helper.js: -------------------------------------------------------------------------------- 1 | export function stack_error(response) { 2 | if (typeof response.data == 'string') { 3 | toastr.error(response.status + ' ' + response.statusText) 4 | } else { 5 | let data = response.data 6 | let content = ''; 7 | 8 | Object.keys(data).map(function(key, index) { 9 | let value = data[key]; 10 | 11 | content += '' + value[0] + '
'; 12 | }); 13 | 14 | swal({ 15 | title: "Error Text!", 16 | type: 'error', 17 | text: content, 18 | html: true 19 | }); 20 | } 21 | } -------------------------------------------------------------------------------- /application/resources/assets/js/config/menu.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | label: 'sidebar.dashboard', 4 | icon : 'ion-ios-speedometer', 5 | uri : '/dashboard/home' 6 | }, 7 | { 8 | label: 'sidebar.user', 9 | icon : 'ion-person-stalker', 10 | uri : '/dashboard/users' 11 | }, 12 | { 13 | label: 'sidebar.article', 14 | icon : 'ion-ios-book', 15 | uri : '/dashboard/articles' 16 | }, 17 | { 18 | label: 'sidebar.discussion', 19 | icon : 'ion-help-circled', 20 | uri : '/dashboard/discussions' 21 | }, 22 | { 23 | label: 'sidebar.comment', 24 | icon : 'ion-chatbubble-working', 25 | uri : '/dashboard/comments' 26 | }, 27 | { 28 | label: 'sidebar.file', 29 | icon : 'ion-ios-folder', 30 | uri : '/dashboard/files' 31 | }, 32 | { 33 | label: 'sidebar.tag', 34 | icon : 'ion-ios-pricetags', 35 | uri : '/dashboard/tags' 36 | }, 37 | { 38 | label: 'sidebar.category', 39 | icon : 'ion-ios-list', 40 | uri : '/dashboard/categories' 41 | }, 42 | { 43 | label: 'sidebar.link', 44 | icon : 'ion-ios-world', 45 | uri : '/dashboard/links' 46 | }, 47 | { 48 | label: 'sidebar.visitor', 49 | icon : 'ion-chatbubble-working', 50 | uri : '/dashboard/visitors' 51 | }, 52 | { 53 | label: 'sidebar.system', 54 | icon : 'ion-gear-b', 55 | uri : '/dashboard/system' 56 | } 57 | ] -------------------------------------------------------------------------------- /application/resources/assets/js/config/toastr.js: -------------------------------------------------------------------------------- 1 | export default { 2 | positionClass: "toast-bottom-right", 3 | showDuration: "300", 4 | hideDuration: "1000", 5 | timeOut: "5000", 6 | extendedTimeOut: "1000", 7 | showEasing: "swing", 8 | hideEasing: "linear", 9 | showMethod: "fadeIn", 10 | hideMethod: "fadeOut" 11 | } -------------------------------------------------------------------------------- /application/resources/assets/js/home.js: -------------------------------------------------------------------------------- 1 | window.$ = window.jQuery = require('jquery'); 2 | window.swal = require('sweetalert'); 3 | window.Vue = require('vue'); 4 | 5 | import VueI18n from 'vue-i18n'; 6 | import locales from 'lang'; 7 | import httpPlugin from 'plugins/http'; 8 | 9 | require('bootstrap-sass'); 10 | require('social-share.js/dist/js/social-share.min.js'); 11 | require('vendor/select2.min.js'); 12 | window.marked = require('marked'); 13 | window.hljs = require('vendor/highlight.min.js'); 14 | window.toastr = require('toastr/build/toastr.min.js'); 15 | 16 | Vue.use(VueI18n); 17 | Vue.use(httpPlugin); 18 | 19 | Vue.config.lang = window.Language; 20 | 21 | const i18n = new VueI18n({ 22 | locale: Vue.config.lang, 23 | messages: locales 24 | }) 25 | 26 | Vue.component('comment', require('components/Comment.vue')); 27 | 28 | Vue.component('parse', require('components/Parse.vue')); 29 | 30 | Vue.component('parse-textarea', require('components/Textarea.vue')); 31 | 32 | Vue.component('avatar', require('components/AvatarUpload.vue')); 33 | 34 | new Vue({ 35 | i18n: i18n, 36 | }).$mount('#app'); -------------------------------------------------------------------------------- /application/resources/assets/js/lang/en/index.js: -------------------------------------------------------------------------------- 1 | import sidebar from './sidebar' 2 | import page from './page' 3 | import table from './table' 4 | import form from './form' 5 | 6 | export default { 7 | sidebar, 8 | page, 9 | table, 10 | form 11 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/en/page.js: -------------------------------------------------------------------------------- 1 | export default { 2 | users: 'Users', 3 | visitors: 'Visitors', 4 | articles: 'Articles', 5 | comments: 'Comments', 6 | discussions: 'Discussions', 7 | files: 'Files', 8 | tags: 'Tags', 9 | categories: 'Categories', 10 | links: 'Links', 11 | systems: 'Systems', 12 | user_num: 'Users', 13 | view_num: 'Views', 14 | article_num: 'Articles', 15 | comment_num: 'Comments', 16 | all: 'All', 17 | admin: 'Admin', 18 | create: 'Create', 19 | system: 'System', 20 | database: 'Database', 21 | key: 'Setting', 22 | value: 'Value', 23 | server: 'Web Server', 24 | domain: 'Domain', 25 | version: 'Version', 26 | extension: 'Extension', 27 | driver: 'Driver', 28 | nothing: 'No Content' 29 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/en/sidebar.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: 'Dashboard', 3 | user: 'Users', 4 | article: 'Articles', 5 | discussion: 'Discussion', 6 | comment: 'Comments', 7 | tag: 'Tags', 8 | file: 'Files', 9 | category: 'Categories', 10 | link: 'Links', 11 | visitor: 'Visitors', 12 | system: 'Systems' 13 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/en/table.js: -------------------------------------------------------------------------------- 1 | export default { 2 | id: 'ID', 3 | avatar: 'Avatar', 4 | username: 'User Name', 5 | email: 'E-Mail Address', 6 | status: 'Status', 7 | title: 'Title', 8 | subtitle: 'Sub Title', 9 | created_at: 'Created At', 10 | published_at: 'Published At', 11 | comment_type: 'Commentable Type', 12 | comment_title: 'Commentable Title', 13 | tag: 'Tag', 14 | meta_description: 'Meta Description', 15 | name: 'Name', 16 | type: 'Type', 17 | date: 'Date Time', 18 | size: 'Size', 19 | action: 'Actions', 20 | path: 'Path', 21 | image: 'Image', 22 | link: 'Link', 23 | enabled: 'Enabled', 24 | article_title: 'Article Title', 25 | click_num: 'Clicks Num', 26 | ip: 'IP', 27 | new_folder: 'New Folder', 28 | upload: 'Uplaod' 29 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/index.js: -------------------------------------------------------------------------------- 1 | import en from './en' 2 | import zh_cn from './zh_cn' 3 | 4 | export default { 5 | en, 6 | zh_cn 7 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/zh_cn/form.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'name': '用户名', 3 | 'email': '邮箱', 4 | 'password': '密码', 5 | 'confirm_password': '确认密码', 6 | 'category': '分类', 7 | 'select_option': '选择选项', 8 | 'title': '标题', 9 | 'subtitle': '副标题', 10 | 'page_image': '页面图像', 11 | 'content': '内容', 12 | 'content_notice': '请输入内容', 13 | 'tag': '标签', 14 | 'select_tag': '选择标签', 15 | 'select_category': '选择分类', 16 | 'meta_description': '主要描述', 17 | 'datetime': '日期时间', 18 | 'is_draft': '是否草稿?', 19 | 'is_original': '是否原创?', 20 | 'status': '状态', 21 | 'discussions': '讨论', 22 | 'articles': '文章', 23 | 'folder_name': '文件夹名', 24 | 'file': '文件', 25 | 'file_name': '文件名', 26 | 'cancel': '取消', 27 | 'ok': '确认', 28 | 'category_name': '分类名字', 29 | 'description': '描述', 30 | 'path': '路径', 31 | 'link_name': '链接名', 32 | 'link': '链接', 33 | 'image': '图像', 34 | 'is_enable': '是否开启', 35 | 'yes': '是', 36 | 'no': '否', 37 | 'create_user': '创建用户', 38 | 'edit_user': '修改用户', 39 | 'back': '返回', 40 | 'nickname': '昵称', 41 | 'website': '网站地址', 42 | 'create_article': '创建文章', 43 | 'edit_article': '修改文章', 44 | 'create_discussion': '创建讨论', 45 | 'edit_discussion': '修改讨论', 46 | 'edit_comment': '修改评论', 47 | 'submit_comment': '发布评论', 48 | 'create_tag': '创建标签', 49 | 'edit_tag': '修改标签', 50 | 'create_folder': '创建文件夹', 51 | 'upload_file': '上传文件', 52 | 'create_category': '创建分类', 53 | 'edit_category': '修改分类', 54 | 'category_description': '请输入分类描述', 55 | 'create_link': '创建友链', 56 | 'edit_link': '修改友链', 57 | 'create': '创建', 58 | 'edit': '修改', 59 | 'crop_avatar': '裁剪头像', 60 | 'modify_avatar': '修改头像', 61 | 'content_placeholder': '请输入{type}内容。', 62 | 'article': '文章', 63 | 'discussion': '讨论', 64 | 'comment': '评论', 65 | 'published_at': '发布时间?', 66 | 67 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/zh_cn/index.js: -------------------------------------------------------------------------------- 1 | import sidebar from './sidebar' 2 | import page from './page' 3 | import table from './table' 4 | import form from './form' 5 | 6 | export default { 7 | sidebar, 8 | page, 9 | table, 10 | form 11 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/zh_cn/page.js: -------------------------------------------------------------------------------- 1 | export default { 2 | users: '用户列表', 3 | visitors: '访问列表', 4 | articles: '文章列表', 5 | comments: '评论列表', 6 | discussions: '讨论列表', 7 | files: '文件列表', 8 | tags: '标签列表', 9 | categories: '分类列表', 10 | links: '友链列表', 11 | systems: '系统设置', 12 | user_num: '用户数', 13 | view_num: '访问数', 14 | article_num: '文章数', 15 | comment_num: '评论数', 16 | all: '全部', 17 | admin: '管理员', 18 | create: '创建', 19 | system: '系统', 20 | database: '数据库', 21 | key: '设置', 22 | value: '值', 23 | server: '网站服务器', 24 | domain: '域名', 25 | version: '版本', 26 | extension: '扩展', 27 | driver: '驱动', 28 | nothing: 'No Content' 29 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/zh_cn/sidebar.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: '面板', 3 | user: '用户管理', 4 | article: '文章管理', 5 | discussion: '讨论管理', 6 | comment: '评论管理', 7 | tag: '标签管理', 8 | file: '文件管理', 9 | category: '分类管理', 10 | link: '友链管理', 11 | visitor: '访问列表', 12 | system: '系统配置' 13 | } -------------------------------------------------------------------------------- /application/resources/assets/js/lang/zh_cn/table.js: -------------------------------------------------------------------------------- 1 | export default { 2 | id: 'ID', 3 | avatar: '头像', 4 | username: '用户名', 5 | email: '邮箱地址', 6 | status: '状态', 7 | title: '标题', 8 | subtitle: '副标题', 9 | created_at: '创建时间', 10 | published_at: '发布时间', 11 | comment_type: '评论类型', 12 | comment_title: '评论标题', 13 | tag: '标签', 14 | meta_description: '描述', 15 | name: '名字', 16 | type: '类型', 17 | date: '日期', 18 | size: '大小', 19 | action: '操作', 20 | path: '路径', 21 | image: '图片', 22 | link: '链接', 23 | enabled: '是否启用', 24 | article_title: '文章标题', 25 | click_num: '点击次数', 26 | ip: 'IP', 27 | new_folder: '创建文件夹', 28 | upload: '上传图片' 29 | } -------------------------------------------------------------------------------- /application/resources/assets/js/plugins/http/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { apiUrl } from 'config/base' 3 | 4 | /** 5 | * Create Axios 6 | */ 7 | export const http = axios.create({ 8 | baseURL: apiUrl, 9 | }) 10 | 11 | /** 12 | * We'll load the axios HTTP library which allows us to easily issue requests 13 | * to our Laravel back-end. This library automatically handles sending the 14 | * CSRF token as a header based on the value of the "XSRF" token cookie. 15 | */ 16 | http.defaults.headers.common = { 17 | 'X-CSRF-TOKEN': window.Laravel.csrfToken, 18 | 'X-Requested-With': 'XMLHttpRequest' 19 | }; 20 | 21 | /** 22 | * Handle all error messages. 23 | */ 24 | http.interceptors.response.use(function (response) { 25 | return response; 26 | }, function (error) { 27 | const { response } = error 28 | 29 | if ([401].indexOf(response.status) >= 0) { 30 | if (response.status == 401 && response.data.error.message != 'Unauthorized') { 31 | return Promise.reject(response); 32 | } 33 | window.location = '/login'; 34 | } 35 | 36 | return Promise.reject(error); 37 | }); 38 | 39 | export default function install(Vue) { 40 | Object.defineProperty(Vue.prototype, '$http', { 41 | get() { 42 | return http 43 | }, 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/Parent.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/Visitor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 47 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/article/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/article/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 30 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/category/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/category/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/discussion/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/discussion/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/link/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/link/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/tag/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/tag/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/user/Create.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /application/resources/assets/js/views/dashboard/user/Edit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /application/resources/assets/js/vuex/actions.js: -------------------------------------------------------------------------------- 1 | export const toggle = ({ commit }) => commit('toggle') 2 | -------------------------------------------------------------------------------- /application/resources/assets/js/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | export const toggle = state => { 2 | return state.sidebar.opened = !state.sidebar.opened 3 | } -------------------------------------------------------------------------------- /application/resources/assets/js/vuex/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import * as actions from './actions.js'; 4 | import * as mutations from './mutations.js'; 5 | 6 | Vue.use(Vuex); 7 | 8 | const state = { 9 | sidebar: { 10 | opened: false 11 | } 12 | }; 13 | 14 | export default new Vuex.Store({ 15 | state, 16 | actions, 17 | mutations 18 | }); 19 | -------------------------------------------------------------------------------- /application/resources/assets/sass/_form.scss: -------------------------------------------------------------------------------- 1 | .form-control, .form-control:focus, input{ 2 | border-width: 2px; 3 | -webkit-box-shadow: none; 4 | box-shadow: none; 5 | outline: 0; 6 | } 7 | 8 | legend { 9 | border: none; 10 | } 11 | 12 | .well { 13 | border: none; 14 | box-shadow: none; 15 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/_ibox.scss: -------------------------------------------------------------------------------- 1 | .ibox { 2 | clear: both; 3 | margin-bottom: 25px; 4 | margin-top: 0; 5 | padding: 0; 6 | } 7 | .ibox-title { 8 | -moz-border-bottom-colors: none; 9 | -moz-border-left-colors: none; 10 | -moz-border-right-colors: none; 11 | -moz-border-top-colors: none; 12 | background-color: #ffffff; 13 | border-color: #e7eaec; 14 | border-image: none; 15 | border-style: solid solid none; 16 | border-width: 2px 0 0; 17 | color: inherit; 18 | margin-bottom: 0; 19 | padding: 15px 15px 7px; 20 | min-height: 48px; 21 | } 22 | .ibox-default { 23 | border-color: #e7eaec; 24 | } 25 | .ibox-success { 26 | border-color: #3498db; 27 | } 28 | .ibox-success { 29 | border-color: #1abc9c; 30 | } 31 | .ibox-warning { 32 | border-color: #f1c40f; 33 | } 34 | .ibox-danger { 35 | border-color: #e74c3c; 36 | } 37 | .ibox-content { 38 | clear: both; 39 | background-color: #ffffff; 40 | color: inherit; 41 | padding: 15px 20px 20px 20px; 42 | border-color: #e7eaec; 43 | border-image: none; 44 | border-style: solid solid none; 45 | border-width: 1px 0; 46 | } 47 | .no-padding { 48 | padding: 0; 49 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/_navbar.scss: -------------------------------------------------------------------------------- 1 | @media (min-width: 768px) { 2 | .navbar-nav > li > a { 3 | padding-top: 17px; 4 | padding-bottom: 14px; 5 | } 6 | } 7 | 8 | .navbar-collapse { 9 | border: none; 10 | } 11 | 12 | .notification { 13 | .ion-android-notifications { 14 | position: relative; 15 | font-size: 2.5rem; 16 | color: #fff; 17 | vertical-align: bottom; 18 | 19 | .new { 20 | position: absolute; 21 | top: -5px; 22 | right: -5px; 23 | width: 10px; 24 | height: 10px; 25 | border-radius: 50%; 26 | background-color: #d9534f; 27 | display: none; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/_toastr.scss: -------------------------------------------------------------------------------- 1 | .toast-success { 2 | background-color: #1abc9c; 3 | } 4 | .toast-error { 5 | background-color: #e74c3c; 6 | } 7 | .toast-info { 8 | background-color: #597289; 9 | } 10 | .toast-warning { 11 | background-color: #f1c40f; 12 | } 13 | #toast-container > div { 14 | opacity: 0.9; 15 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90); 16 | filter: alpha(opacity=90); 17 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/app.scss: -------------------------------------------------------------------------------- 1 | 2 | // Public 3 | @import "public"; 4 | 5 | //Simplemde 6 | @import "vendor/simplemde.min"; 7 | 8 | @import "_form"; 9 | @import "_navbar"; 10 | @import "_ibox"; 11 | @import "_toastr"; 12 | @import "_togglebutton"; 13 | 14 | .width-5-percent { 15 | width: 5%; 16 | } 17 | .width-10-percent { 18 | width: 10%; 19 | } 20 | .no-margin { 21 | margin: 0 !important; 22 | } 23 | .multiselect__content { 24 | z-index: 2; 25 | } 26 | .avatar { 27 | width: 50px; 28 | margin: 0 auto; 29 | } 30 | .editor-toolbar.fullscreen { 31 | z-index: 1000 !important; 32 | } 33 | .CodeMirror-fullscreen { 34 | z-index: 1000 !important; 35 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/comment.scss: -------------------------------------------------------------------------------- 1 | .comment { 2 | 3 | .heading { 4 | padding: 10px 20px; 5 | background: #ECF0F1; 6 | 7 | a { 8 | color: #7F8C8D; 9 | } 10 | } 11 | .box-body { 12 | border: 1px solid #ECF0F1; 13 | border-radius: 5px; 14 | background-color: #fff; 15 | color: #7F8C8D; 16 | } 17 | .media { 18 | padding-top: 20px; 19 | } 20 | .media-left:hover img, .media-right:hover img{ 21 | -webkit-transform: rotateZ(360deg); 22 | -moz-transform: rotateZ(360deg); 23 | transform: rotateZ(360deg); 24 | } 25 | .img-circle { 26 | width: 64px; 27 | height: 64px; 28 | -webkit-transition: transition .6s ease-in; 29 | -moz-transition: transition .6s ease-in; 30 | transition: transform .6s ease-in; 31 | } 32 | .comment-editor { 33 | margin-top: 40px; 34 | } 35 | .footing { 36 | padding: 10px 20px; 37 | border-top: 1px dashed #e1e1e1; 38 | } 39 | .downvoted { 40 | background: #fff; 41 | opacity: .3; 42 | } 43 | .downvoted:hover { 44 | opacity: 1; 45 | } 46 | 47 | i { 48 | margin-right: 5px; 49 | } 50 | .operate { 51 | font-size: 17px; 52 | a { 53 | margin-right: 5px; 54 | text-decoration: none; 55 | } 56 | } 57 | .none { 58 | color: #c5c5c5; 59 | font-size: 16px; 60 | } 61 | } 62 | @media screen and (max-width: 767px) { 63 | .comment .media-left { 64 | display: none; 65 | } 66 | .own-avatar { 67 | display: none; 68 | } 69 | } 70 | .comment-body { 71 | padding: 30px 50px; 72 | color: #34495e; 73 | display: grid; 74 | 75 | a { 76 | color: #1abc9c; 77 | } 78 | } -------------------------------------------------------------------------------- /application/resources/assets/sass/home.scss: -------------------------------------------------------------------------------- 1 | 2 | // Public 3 | @import "public"; 4 | 5 | // Share.js 6 | @import "node_modules/social-share.js/src/css/share.scss"; 7 | 8 | // Select 2 9 | @import "vendor/select2.min"; 10 | 11 | // Markdown 12 | @import "markdown"; 13 | @import "vendor/highlight.min"; 14 | 15 | @import "_form"; 16 | @import "_navbar"; 17 | @import "_pagination"; 18 | @import "_togglebutton"; 19 | @import "_toastr"; 20 | @import "styles"; 21 | @import "comment"; 22 | -------------------------------------------------------------------------------- /application/resources/assets/sass/public.scss: -------------------------------------------------------------------------------- 1 | 2 | // Fonts 3 | // @import url(https://fonts.googleapis.com/css?family=Raleway:400); 4 | 5 | // Variables 6 | @import "variables"; 7 | 8 | // Bootstrap 9 | @import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; 10 | 11 | // Ionicons 12 | @import 'node_modules/ionicons/scss/ionicons'; 13 | 14 | //Sweetalert 15 | @import "node_modules/sweetalert/dev/sweetalert.scss"; 16 | 17 | // Toastr 18 | @import "vendor/toastr.min"; 19 | -------------------------------------------------------------------------------- /application/resources/assets/sass/vendor/highlight.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-formula{color:#c678dd}.hljs-section,.hljs-name,.hljs-selector-tag,.hljs-deletion,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta-string{color:#98c379}.hljs-built_in,.hljs-class .hljs-title{color:#e6c07b}.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-number{color:#d19a66}.hljs-symbol,.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#61aeee}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}.hljs-link{text-decoration:underline} -------------------------------------------------------------------------------- /application/resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /application/resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /application/resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Passwords must be at least six characters and match the confirmation.', 17 | 'reset' => 'Your password has been reset!', 18 | 'sent' => 'We have e-mailed your password reset link!', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that e-mail address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /application/resources/lang/zh_cn/auth.php: -------------------------------------------------------------------------------- 1 | '用户名/密码不匹配.', 17 | 'throttle' => '登录尝试次数过多。 请在以下时间后重试 :seconds 秒.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /application/resources/lang/zh_cn/pagination.php: -------------------------------------------------------------------------------- 1 | '« 上一页', 17 | 'next' => '下一页 »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /application/resources/lang/zh_cn/passwords.php: -------------------------------------------------------------------------------- 1 | '密码必须至少为六个字符,并与确认密码相匹配.', 17 | 'reset' => '您的密码已重置!', 18 | 'sent' => '我们已通过电子邮件发送您的密码重置链接!', 19 | 'token' => '此密码重置令牌无效.', 20 | 'user' => "我们找不到具有该电子邮件地址的用户.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /application/resources/views/article/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

{{ config('blog.article.title') }}

6 | 7 |
{{ config('blog.article.description') }}
8 | @endcomponent 9 | 10 | @include('widgets.article') 11 | 12 | {{ $articles->links('pagination.default') }} 13 | 14 | @endsection -------------------------------------------------------------------------------- /application/resources/views/category/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

{{ lang('Categories') }}

6 | 7 |
{{ lang('Categories Meta') }}
8 | @endcomponent 9 | 10 |
11 |
12 |
13 |
    14 | @forelse($categories as $category) 15 |
  • 16 | {{ $category->articles->count() }} 17 | {{ $category->name }} 18 |
  • 19 | @empty 20 |
  • {{ lang('Nothing') }}
  • 21 | @endforelse 22 |
23 |
24 |
25 |
26 | @endsection -------------------------------------------------------------------------------- /application/resources/views/category/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

{{ $category->name }}

6 | 7 |
{{ lang('Category Meta') }}
8 | @endcomponent 9 | 10 |
11 |
12 |
13 |
    14 | @forelse($articles as $article) 15 |
  • 16 | {{ $article->comments->count() }} 17 | {{ $article->title }} 18 |
  • 19 | @empty 20 |
  • {{ lang('Nothing') }}
  • 21 | @endforelse 22 |
23 |
24 |
25 |
26 | @endsection -------------------------------------------------------------------------------- /application/resources/views/dashboard/index.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{ config('app.name') }} Dashboard 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | @if(config('blog.google.open')) 38 | 47 | @endif 48 | 49 | 50 | -------------------------------------------------------------------------------- /application/resources/views/errors/403.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden. 5 | 6 | 7 | 8 | 39 | 40 | 41 |
42 |
43 |
403 Forbidden.
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /application/resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 Not Found. 5 | 6 | 7 | 8 | 39 | 40 | 41 |
42 |
43 |
404 Not Found.
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /application/resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Be right back. 5 | 6 | 7 | 8 | 39 | 40 | 41 |
42 |
43 |
Be right back.
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /application/resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @yield('title', config('app.name')) 16 | 17 | 18 | 19 | 20 | 27 | 28 | @yield('styles') 29 | 30 | 31 |
32 | @include('particals.navbar') 33 | 34 |
35 | @yield('content') 36 |
37 | 38 | @include('particals.footer') 39 |
40 | 41 | 42 | 43 | 44 | @yield('scripts') 45 | 46 | 51 | 52 | @if(config('blog.google.open')) 53 | 62 | @endif 63 | 64 | 65 | -------------------------------------------------------------------------------- /application/resources/views/link/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

{{ lang('Links') }}

6 | @endcomponent 7 | 8 |
9 |
10 |
11 | 18 |
19 |
20 |
21 | @endsection 22 | -------------------------------------------------------------------------------- /application/resources/views/mail/followed/user.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | **{{ lang('Dear') }} {{ $username }}:** 3 | 4 | {{ $message }} 5 | 6 | {{ lang('View') }} 7 | 8 | Thanks,
9 | {{ config('app.name') }} 10 | @endcomponent 11 | -------------------------------------------------------------------------------- /application/resources/views/mail/mention/user.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | **{{ lang('Dear') }} {{ $username }}:** 3 | 4 | {{ $message }} 5 | 6 | {{ $content }} 7 | 8 | @component('mail::button', ['url' => $url]) 9 | {{ lang('View') }} 10 | @endcomponent 11 | 12 | Thanks,
13 | {{ config('app.name') }} 14 | @endcomponent 15 | -------------------------------------------------------------------------------- /application/resources/views/mail/receive/comment.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | **{{ lang('Dear') }} {{ $username }}:** 3 | 4 | {{ $message }} 5 | 6 | {{ $content }} 7 | 8 | @component('mail::button', ['url' => $url]) 9 | {{ lang('View') }} 10 | @endcomponent 11 | 12 | Thanks,
13 | {{ config('app.name') }} 14 | @endcomponent 15 | -------------------------------------------------------------------------------- /application/resources/views/notifications/followed-user.blade.php: -------------------------------------------------------------------------------- 1 | data['id']); 3 | ?> 4 | 5 |
  • 6 | @if ($user) 7 | {{ $user->name }} {{ lang('Followed') }} 8 | @else 9 | {{ lang('User') }} {{ lang('Deleted') }} 10 | @endif 11 |
  • 12 | -------------------------------------------------------------------------------- /application/resources/views/notifications/got-vote.blade.php: -------------------------------------------------------------------------------- 1 | data['comment_id']); 3 | $user = App\User::find($notification->data['issuer_id']); 4 | 5 | $commentable_id = $notification->data['commentable_id']; 6 | 7 | if ($comment && $comment->commentable) { 8 | switch($comment->commentable_type) { 9 | case 'articles': 10 | $article = App\Article::find($commentable_id); 11 | $url = url($article->slug); 12 | break; 13 | case 'discussions': 14 | $discussion = App\Discussion::find($commentable_id); 15 | $url = url('discussion', ['id' => $discussion->id]); 16 | break; 17 | } 18 | } 19 | 20 | if($notification->data['vote_type'] == 'up_vote') 21 | { 22 | $iconRead = 'ion-ios-arrow-thin-up'; 23 | $iconNotRead = 'ion-arrow-up-a'; 24 | $action = lang('Likes'); // Likes your comment 25 | } 26 | else 27 | { 28 | $iconRead = 'ion-ios-arrow-thin-down'; 29 | $iconNotRead = 'ion-arrow-down-a'; 30 | $action = lang('Dislikes'); // Dislikes your comment 31 | } 32 | 33 | ?> 34 |
  • 35 | @if ($comment && $comment->commentable && $user) 36 | 37 | {{ $user->name }} {{ $action }} 38 | {{ $comment->commentable->title }} 39 | @else 40 | {{ lang('Deleted') }} 41 | @endif 42 |
  • -------------------------------------------------------------------------------- /application/resources/views/notifications/mentioned-user.blade.php: -------------------------------------------------------------------------------- 1 | data['id']); 3 | $type = $notification->data['commentable_type'] === 'articles' ? lang('Article') : lang('Discussion'); 4 | $commentable_id = $notification->data['commentable_id']; 5 | 6 | if ($comment && $comment->commentable) { 7 | switch($comment->commentable_type) { 8 | case 'articles': 9 | $article = App\Article::find($commentable_id); 10 | $url = url($article->slug); 11 | break; 12 | case 'discussions': 13 | $discussion = App\Discussion::find($commentable_id); 14 | $url = url('discussion', ['id' => $discussion->id]); 15 | break; 16 | } 17 | } else { 18 | $message = lang('Be Banned Comment'); 19 | } 20 | 21 | ?> 22 | 23 |
  • 24 | @if ($comment && $comment->commentable) 25 | {{ $comment->user->name }} 26 | {{ lang('Mentioned') }} {{ lang('In') }} 27 | {{ $comment->commentable->title }} 28 | @elseif ($comment && !$comment->commentable) 29 | {{ strtolower($type) }} {{ $message }} 30 | @else 31 | {{ strtolower($type) }} {{ lang('Deleted') }} 32 | @endif 33 |
  • 34 | -------------------------------------------------------------------------------- /application/resources/views/notifications/received-comment.blade.php: -------------------------------------------------------------------------------- 1 | data['id']); 3 | $type = $notification->data['commentable_type'] === 'articles' ? lang('Articles') : lang('Discussions'); 4 | $commentable_id = $notification->data['commentable_id']; 5 | 6 | if ($comment && $comment->commentable) { 7 | switch($comment->commentable_type) { 8 | case 'articles': 9 | $article = App\Article::find($commentable_id); 10 | $url = url($article->slug); 11 | break; 12 | case 'discussions': 13 | $discussion = App\Discussion::find($commentable_id); 14 | $url = url('discussion', ['id' => $discussion->id]); 15 | break; 16 | } 17 | } else { 18 | $message = lang('Be Banned Comment'); 19 | } 20 | 21 | ?> 22 | 23 |
  • 24 | @if ($comment && $comment->commentable) 25 | 26 | {{ $comment->user->name }} 27 | {{ lang('Commented') }} {{ $type }} 28 | {{ $comment->commentable->title }} 29 | @elseif ($comment && !$comment->commentable) 30 | {{ strtolower($type) }} {{ $message }} 31 | @else 32 | {{ strtolower($type) }} {{ lang('Deleted') }} 33 | @endif 34 |
  • 35 | -------------------------------------------------------------------------------- /application/resources/views/pagination/default.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 |
    3 | 39 |
    40 | @endif 41 | -------------------------------------------------------------------------------- /application/resources/views/particals/footer.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/views/particals/jumbotron.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | {{ $slot }} 6 |
    7 |
    8 |
    9 |
    -------------------------------------------------------------------------------- /application/resources/views/search.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

    {{ request()->get('q') }}

    6 | 7 |
    what you want to search.
    8 | @endcomponent 9 | 10 | @include('widgets.article') 11 | 12 | @endsection -------------------------------------------------------------------------------- /application/resources/views/setting/binding.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
    5 |
    6 |
    7 | @include('setting.particals.sidebar') 8 |
    9 | 10 |
    11 |
    12 |
    {{ lang('Account Binding') }}
    13 | 14 |
    15 |
    16 |
    17 | 18 |
    19 | @if(!Auth::user()->github_id && config('services.github.client_id')) 20 | 21 | Github 22 | 23 | @else 24 | 27 | @endif 28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 | @endsection -------------------------------------------------------------------------------- /application/resources/views/setting/notification.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
    5 |
    6 |
    7 | @include('setting.particals.sidebar') 8 |
    9 | 10 |
    11 |
    12 |
    {{ lang('Notification Setting') }}
    13 | 14 |
    15 |
    16 | {{ csrf_field() }} 17 | 18 |
    19 | 20 |
    21 |
    22 | 26 |
    27 |
    28 |
    29 | 30 |
    31 |
    32 | 33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    41 | @endsection -------------------------------------------------------------------------------- /application/resources/views/setting/particals/sidebar.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    {{ lang('Settings') }}
    3 | 4 |
    5 | 6 | {{ lang('Account Setting') }} 7 | 8 | @if(config('blog.mail_notification')) 9 | 10 | {{ lang('Notification Setting') }} 11 | 12 | @endif 13 | 14 | {{ lang('Account Binding') }} 15 | 16 |
    17 |
    -------------------------------------------------------------------------------- /application/resources/views/tag/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

    {{ lang('Tags') }}

    6 | 7 |
    {{ lang('Tags Meta') }}
    8 | @endcomponent 9 | 10 |
    11 |
    12 | @forelse($tags as $tag) 13 |
    14 |
    15 |
    16 |

    17 | {{ $tag->tag }} 18 |

    19 |
    20 |
    21 | {{ $tag->meta_description }} 22 |
    23 |
    24 |
    25 | @empty 26 |

    {{ lang('Nothing') }}

    27 | @endforelse 28 |
    29 |
    30 | @endsection 31 | -------------------------------------------------------------------------------- /application/resources/views/tag/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @component('particals.jumbotron') 5 |

    {{ $tag->tag }}

    6 | 7 |
    {{ lang('Tag Meta') }}
    8 | @endcomponent 9 | 10 |
    11 |
    12 |
    13 |
    14 |
    {{ lang('For Articles') }} ( {{ $articles->count() }} )
    15 |
      16 | @forelse($articles as $article) 17 |
    • 18 | {{ $article->comments->count() }} 19 | {{ $article->title }} 20 |
    • 21 | @empty 22 |
    • {{ lang('Nothing') }}
    • 23 | @endforelse 24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    {{ lang('For Discussions') }} ( {{ $discussions->count() }} )
    30 |
      31 | @forelse($discussions as $discussion) 32 |
    • 33 | {{ $discussion->comments->count() }} 34 | {{ $discussion->title }} 35 |
    • 36 | @empty 37 |
    • {{ lang('Nothing') }}
    • 38 | @endforelse 39 |
    40 |
    41 |
    42 |
    43 |
    44 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/comments.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @include('user.particals.info') 5 | 6 |
    7 |
    8 |
    9 |
    10 |
    {{ lang('Your Comments') }} ( {{ $comments->count() }} )
    11 | 12 | @include('user.particals.comments') 13 | 14 |
    15 |
    16 |
    17 |
    18 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/discussions.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @include('user.particals.info') 5 | 6 |
    7 |
    8 |
    9 |
    10 |
    {{ lang('Your Discussions') }} ( {{ $discussions->count() }} )
    11 | 12 | @include('user.particals.discussions') 13 | 14 |
    15 |
    16 |
    17 |
    18 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/following.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @include('user.particals.info') 5 | 6 |
    7 |
    8 |
    9 |
    10 |
    {{ lang('Your Followings') }} ( {{ $followings->count() }} )
    11 | 27 |
    28 |
    29 |
    30 |
    31 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | @include('user.particals.info') 5 |
    6 |
    7 |
    8 |
    9 |
    {{ lang('Recent Discussions') }}
    10 | 11 | @include('user.particals.discussions') 12 | 13 |
    14 |
    15 |
    16 |
    17 |
    {{ lang('Recent Comments') }}
    18 | 19 | @include('user.particals.comments') 20 | 21 |
    22 |
    23 |
    24 |
    25 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/notifications.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
    5 |
    6 |
    7 |
    8 |
    9 | 10 | {{ Auth::user()->unreadNotifications->count() }} 11 | {{ lang('New Notification') }} 12 | 13 | 15 | {{ lang('Mark As Read') }} 16 | 17 |
    18 | {{ csrf_field() }} 19 |
    20 |
    21 |
    22 |
      23 | @foreach(Auth::user()->notifications as $notification) 24 | @include('notifications.'. snake_case(class_basename($notification->type), '-')) 25 | @endforeach 26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 | @endsection -------------------------------------------------------------------------------- /application/resources/views/user/particals/comments.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/views/user/particals/discussions.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/resources/views/vendor/notifications/email-plain.blade.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 16 | }); -------------------------------------------------------------------------------- /application/routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | }); 19 | -------------------------------------------------------------------------------- /application/server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /application/storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /application/storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /application/storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | schedule-* 4 | compiled.php 5 | services.json 6 | events.scanned.php 7 | routes.scanned.php 8 | down 9 | -------------------------------------------------------------------------------- /application/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /application/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /application/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /application/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /application/storage/oauth-public.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq68vCnWbXg3Z8VTWsWgl 3 | ac5NnIHw49nSGOvWUXWsH3nKS7WpSRMzQd3P6+rDMLJkQxhmr2bbycmho4Gj5jk/ 4 | QghFTuoo25/5lgERAtfmi+a1cxa9f9YY0PFH7Wiv+BGGp5XG3XWPTZhUTIF0tMYQ 5 | ECq7pNYtwGfJ5L0ONShhGHtMCDnhkbKGU/dlJusTZQOwSVPTLZ7wmnZ6xycOazx7 6 | bOZ8twYn4iAtX81/yeuwAD/JQQK58s0B6SoBcTQWvYKdH1ix63UVX10mTTsL2ba3 7 | e9OfxvO9kbgONl2YMHEZv929g1jRaZ2lqdvqVdGMtjV0t7oF4BliCYY/I0JIu7is 8 | mxyC93Njea/ENmEdASa1KhKlq3e1BP4zWROft0cYbWqJaVYsajlNhTrud5J4ibgZ 9 | sqX+6tSaREIRGoSMz9cbNlS/TKhcdXJjsZPBZXec85+rWuA/KBnvS6H4rhXqkt7v 10 | MFk+zZqreYQnogx2IQdPp54eu5yAHA8CkJMOggbDkqUZshIdNclMPcfz0953rN18 11 | D3Xyey3KfdXjwM89VB5qktcjiSPh1a9h/XW2e+TxqtPGgI4/9sFXT/3kz8Oiobze 12 | 61DQ31JkYlx+FzEm2fzC4rD4jihj30SfRuhbXoxFllRuDIP0AGHl3uFD5FAc/e0j 13 | CkzAd/kY2Pd+PBnqmBj4ELMCAwEAAQ== 14 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /application/tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } -------------------------------------------------------------------------------- /application/tests/Feature/Api/ArticleApiTest.php: -------------------------------------------------------------------------------- 1 | actingAsAdmin()->get('api/article'); 13 | 14 | $response->assertStatus(200); 15 | $response->assertJsonStructure([ 16 | 'data' => ['*' => ['id', 'title', 'subtitle', 'user', 'slug', 'content', 'page_image', 'meta_description', 'is_original', 'is_draft', 'visitors', 'published_at', 'published_time']], 17 | 'meta' => ['pagination' => []], 18 | ]); 19 | } 20 | 21 | /** @test */ 22 | public function it_shows_a_article() 23 | { 24 | $article = factory(\App\Article::class, 1)->create()->first(); 25 | $response = $this->actingAsAdmin()->get(route('api.article.edit', $article->id)); 26 | 27 | $response->assertStatus(200); 28 | } 29 | 30 | /** @test */ 31 | public function it_store_a_article() 32 | { 33 | $article = factory(\App\Article::class)->make(); 34 | 35 | $data = array_merge($article->toArray(), [ 'tags' => '[1, 2]' ]); 36 | 37 | $response = $this->actingAsAdmin()->post(route('api.article.store', $data)); 38 | 39 | $response->assertStatus(204); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /application/tests/Feature/Api/UserApiTest.php: -------------------------------------------------------------------------------- 1 | actingAsAdmin(); 13 | $response = $this->get('/api/user'); 14 | 15 | $response->assertStatus(200); 16 | $response->assertJsonStructure([ 17 | 'data' => ['*' => ['id', 'name', 'avatar', 'status', 'email', 'nickname', 'is_admin', 'github_name', 'website', 'description', 'created_at']], 18 | 'meta' => ['pagination' => []], 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application/tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 17 | 18 | $response->assertStatus(200); 19 | } 20 | } -------------------------------------------------------------------------------- /application/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | create(), 17 | ['user', 'article'] 18 | ); 19 | 20 | return $this; 21 | } 22 | } -------------------------------------------------------------------------------- /application/tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 19 | } 20 | } -------------------------------------------------------------------------------- /application/webpack.mix.js: -------------------------------------------------------------------------------- 1 | const { mix } = require('laravel-mix'); 2 | const path = require('path'); 3 | 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | Mix Asset Management 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Mix provides a clean, fluent API for defining some Webpack build steps 10 | | for your Laravel application. By default, we are compiling the Sass 11 | | file for the application as well as bundling up all the JS files. 12 | | 13 | */ 14 | 15 | mix.webpackConfig({ 16 | output: { 17 | publicPath: "/", 18 | chunkFilename: 'js/[name].[chunkhash].js' 19 | }, 20 | resolve: { 21 | alias: { 22 | 'components': 'assets/js/components', 23 | 'config': 'assets/js/config', 24 | 'lang': 'assets/js/lang', 25 | 'plugins': 'assets/js/plugins', 26 | 'vendor': 'assets/js/vendor', 27 | 'views': 'assets/js/views', 28 | 'dashboard': 'assets/js/views/dashboard', 29 | }, 30 | modules: [ 31 | 'node_modules', 32 | path.resolve(__dirname, "resources") 33 | ] 34 | }, 35 | }) 36 | 37 | mix.js('resources/assets/js/app.js', 'public/js') 38 | .sass('resources/assets/sass/app.scss', 'public/css') 39 | .js('resources/assets/js/home.js', 'public/js') 40 | .sass('resources/assets/sass/home.scss', 'public/css') 41 | .version(); 42 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | # The Application 5 | app: 6 | build: 7 | context: ./ 8 | dockerfile: app.dockerfile 9 | working_dir: /var/www 10 | volumes: 11 | - ./application/:/var/www 12 | environment: 13 | - "DB_PORT=3306" 14 | - "host=localhost" 15 | 16 | # The Web Server 17 | web: 18 | build: 19 | context: ./ 20 | dockerfile: web.dockerfile 21 | working_dir: /var/www 22 | volumes_from: 23 | - app 24 | links: 25 | - app 26 | ports: 27 | - 8091:80 28 | 29 | # The Database 30 | database: 31 | image: mysql:5.7 32 | volumes: 33 | - db:/var/lib/mysql 34 | environment: 35 | - "MYSQL_DATABASE=laravel" 36 | - "MYSQL_USER=laravel" 37 | - "MYSQL_PASSWORD=laravel" 38 | - "MYSQL_ROOT_PASSWORD=secret" 39 | ports: 40 | - "33062:3306" 41 | 42 | volumes: 43 | db: 44 | -------------------------------------------------------------------------------- /vhost.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | index index.php index.html; 4 | server_name marammat.dev; 5 | root /var/www/public; 6 | 7 | location / { 8 | try_files $uri /index.php?$args; 9 | } 10 | 11 | location ~ \.php$ { 12 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 13 | fastcgi_pass app:9000; 14 | fastcgi_index index.php; 15 | include fastcgi_params; 16 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 17 | fastcgi_param PATH_INFO $fastcgi_path_info; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web.dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.10 2 | 3 | ADD vhost.conf /etc/nginx/conf.d/default.conf 4 | --------------------------------------------------------------------------------