├── public ├── favicon.ico ├── robots.txt ├── images │ ├── hero.jpg │ ├── hero_image.png │ ├── slack-button.png │ ├── default-avatar.png │ ├── wang-xi-349509.jpg │ └── social │ │ ├── google-share.jpg │ │ ├── twitter-share.jpg │ │ └── facebook-share.jpg ├── .htaccess ├── web.config └── index.php ├── database ├── .gitignore ├── factories │ ├── RoleFactory.php │ ├── PromotionFactory.php │ ├── PostFactory.php │ └── UserFactory.php ├── seeds │ ├── DatabaseSeeder.php │ ├── UserTableSeeder.php │ ├── RoleSeeder.php │ └── ProfileKeysTableSeeder.php └── migrations │ ├── 2018_02_08_221421_create_roles_table.php │ ├── 2018_02_08_224146_add_location_to_user_table.php │ ├── 2018_02_04_144227_add_post_type_to_posts_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2018_02_03_154644_add_slack_id_to_user_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2018_03_04_133849_create_profile_keys_table.php │ ├── 2018_02_04_192742_create_social_links_table.php │ ├── 2018_02_03_143413_create_posts_table.php │ ├── 2018_02_04_204455_create_comments_table.php │ ├── 2018_02_08_221628_role_user.php │ ├── 2018_02_04_191245_create_profiles_table.php │ ├── 2018_03_04_195413_create_promotions_table.php │ ├── 2018_03_04_133955_remove_profiles_columns_and_add_key_pairs.php │ └── 2018_02_04_183153_add_slack_fields_to_user_table.php ├── resources ├── assets │ ├── js │ │ ├── backend │ │ │ ├── vuex │ │ │ │ └── index.js │ │ │ ├── helpers │ │ │ │ └── index.js │ │ │ ├── app │ │ │ │ ├── index.js │ │ │ │ ├── routes.js │ │ │ │ ├── home │ │ │ │ │ ├── components │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── Dashboard.vue │ │ │ │ │ └── routes │ │ │ │ │ │ └── index.js │ │ │ │ └── blog │ │ │ │ │ ├── index.js │ │ │ │ │ ├── components │ │ │ │ │ ├── index.js │ │ │ │ │ ├── PostEdit.vue │ │ │ │ │ ├── PostCreate.vue │ │ │ │ │ └── PostShow.vue │ │ │ │ │ └── routes │ │ │ │ │ └── index.js │ │ │ ├── router │ │ │ │ └── index.js │ │ │ ├── components │ │ │ │ └── App.vue │ │ │ └── backend.js │ │ ├── components │ │ │ ├── SiteFooter.vue │ │ │ └── Dropdown.vue │ │ ├── frontend │ │ │ └── app.js │ │ └── bootstrap.js │ ├── sass │ │ ├── components │ │ │ ├── _homepage.scss │ │ │ ├── _nav.scss │ │ │ ├── _links.scss │ │ │ ├── _ratios.scss │ │ │ └── _forms.scss │ │ ├── _variables.scss │ │ └── app.scss │ └── img │ │ └── default-avatar.png ├── views │ ├── backend │ │ ├── dashboard.blade.php │ │ ├── master.blade.php │ │ └── partials │ │ │ └── nav.blade.php │ ├── frontend │ │ ├── partials │ │ │ ├── header.blade.php │ │ │ ├── footer.blade.php │ │ │ ├── nav.blade.php │ │ │ ├── metadata.blade.php │ │ │ └── logo.blade.php │ │ ├── home │ │ │ ├── partials │ │ │ │ ├── sponsor.blade.php │ │ │ │ ├── latest-resources.blade.php │ │ │ │ ├── hero.blade.php │ │ │ │ ├── latest-interviews.blade.php │ │ │ │ ├── introduction.blade.php │ │ │ │ └── latest-posts.blade.php │ │ │ └── index.blade.php │ │ ├── profile.blade.php │ │ ├── master.blade.php │ │ └── posts │ │ │ ├── index.blade.php │ │ │ └── show.blade.php │ └── auth │ │ ├── login.blade.php │ │ └── passwords │ │ ├── reset.blade.php │ │ └── email.blade.php └── lang │ └── en │ ├── pagination.php │ ├── auth.php │ └── passwords.php ├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── debugbar │ └── .gitignore ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── cache │ └── .gitignore │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── laraveluk ├── tests ├── .DS_Store ├── CreatesApplication.php ├── Unit │ ├── UserTest.php │ └── PostTest.php ├── TestCase.php └── Feature │ ├── ViewPostsTest.php │ └── PromotionsTest.php ├── .gitattributes ├── .env.travis ├── app ├── Enums │ └── ProfileKeys.php ├── Models │ ├── Promotion.php │ ├── ProfileKey.php │ ├── Role.php │ ├── SocialLinks.php │ ├── Comment.php │ ├── Profile.php │ ├── Post.php │ └── User.php ├── Http │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── TrimStrings.php │ │ ├── TrustProxies.php │ │ └── RedirectIfAuthenticated.php │ ├── Controllers │ │ ├── MemberDashboardController.php │ │ ├── Controller.php │ │ ├── ProfileShowController.php │ │ ├── Blog │ │ │ ├── CommentController.php │ │ │ ├── PostTagController.php │ │ │ └── PostController.php │ │ ├── Api │ │ │ ├── UtilController.php │ │ │ ├── UserController.php │ │ │ ├── PromotionsController.php │ │ │ ├── ProfileController.php │ │ │ └── PostController.php │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── ResetPasswordController.php │ │ │ ├── SlackLoginController.php │ │ │ └── RegisterController.php │ │ └── PagesController.php │ └── Kernel.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AppServiceProvider.php │ ├── EventServiceProvider.php │ ├── AuthServiceProvider.php │ └── RouteServiceProvider.php ├── Policies │ ├── PromotionPolicy.php │ └── PostPolicy.php ├── Console │ └── Kernel.php └── Exceptions │ └── Handler.php ├── .gitignore ├── .editorconfig ├── .travis.yml ├── routes ├── channels.php ├── console.php ├── web.php └── api.php ├── config ├── hashing.php ├── slack.php ├── laraveluk.php ├── view.php ├── passwordreset.php ├── services.php ├── broadcasting.php ├── taggable.php ├── logging.php ├── filesystems.php ├── queue.php ├── cache.php ├── auth.php └── mail.php ├── deploy ├── server.php ├── .env.example ├── webpack.mix.js ├── phpunit.xml ├── package.json ├── artisan ├── composer.json └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /resources/assets/js/backend/vuex/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /resources/assets/js/backend/helpers/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /laraveluk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/laraveluk -------------------------------------------------------------------------------- /resources/assets/js/backend/app/index.js: -------------------------------------------------------------------------------- 1 | export { default as routes } from './routes' -------------------------------------------------------------------------------- /tests/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/tests/.DS_Store -------------------------------------------------------------------------------- /public/images/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/hero.jpg -------------------------------------------------------------------------------- /public/images/hero_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/hero_image.png -------------------------------------------------------------------------------- /public/images/slack-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/slack-button.png -------------------------------------------------------------------------------- /resources/assets/sass/components/_homepage.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | background-image: url("../images/hero.jpg"); 3 | } 4 | -------------------------------------------------------------------------------- /public/images/default-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/default-avatar.png -------------------------------------------------------------------------------- /public/images/wang-xi-349509.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/wang-xi-349509.jpg -------------------------------------------------------------------------------- /public/images/social/google-share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/social/google-share.jpg -------------------------------------------------------------------------------- /public/images/social/twitter-share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/social/twitter-share.jpg -------------------------------------------------------------------------------- /resources/views/backend/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('backend.master') 2 | 3 | @section('content') 4 | 5 | @stop 6 | -------------------------------------------------------------------------------- /public/images/social/facebook-share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/public/images/social/facebook-share.jpg -------------------------------------------------------------------------------- /resources/assets/img/default-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorotwell/website/HEAD/resources/assets/img/default-avatar.png -------------------------------------------------------------------------------- /resources/assets/sass/components/_nav.scss: -------------------------------------------------------------------------------- 1 | @screen mobile { 2 | #mainNavTogger:checked + nav { 3 | @apply .visible; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/routes.js: -------------------------------------------------------------------------------- 1 | import dashboard from './home/routes' 2 | import blog from './blog/routes' 3 | 4 | export default [...dashboard, ...blog] -------------------------------------------------------------------------------- /resources/assets/sass/components/_links.scss: -------------------------------------------------------------------------------- 1 | a { 2 | &.red-link { 3 | @apply .text-red; 4 | } 5 | &:hover { 6 | @apply .no-underline; 7 | } 8 | } -------------------------------------------------------------------------------- /resources/views/frontend/partials/header.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/home/components/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export const Dashboard = Vue.component('dashboard', require('./Dashboard.vue')) -------------------------------------------------------------------------------- /.env.travis: -------------------------------------------------------------------------------- 1 | APP_ENV=testing 2 | APP_KEY=EDkdQGJyGlZwu9MN 3 | 4 | DB_CONNECTION=testing 5 | DB_TEST_USERNAME=root 6 | DB_TEST_PASSWORD= 7 | 8 | CACHE_DRIVER=array 9 | SESSION_DRIVER=array 10 | QUEUE_DRIVER=sync -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export const PostCreate = Vue.component('PostCreate', require('./PostCreate')) 4 | export const PostEdit = Vue.component('PostEdit', require('./PostEdit')) 5 | -------------------------------------------------------------------------------- /database/factories/RoleFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Models\Role::class, function (Faker $faker) { 6 | return [ 7 | 'name' => $faker->name 8 | ]; 9 | }); 10 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/home/routes/index.js: -------------------------------------------------------------------------------- 1 | import { Dashboard } from '../components/index' 2 | 3 | export default [ 4 | { 5 | path: '/', 6 | component: Dashboard, 7 | name: 'dashboard', 8 | } 9 | ] -------------------------------------------------------------------------------- /resources/assets/js/backend/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import Router from 'vue-router' 4 | import { routes as routes } from '../app/index' 5 | 6 | Vue.use(Router) 7 | 8 | const router = new Router({ 9 | routes: routes 10 | }) 11 | 12 | export default router -------------------------------------------------------------------------------- /app/Enums/ProfileKeys.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 |
7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | /.idea 7 | /.vagrant 8 | Homestead.json 9 | Homestead.yaml 10 | npm-debug.log 11 | yarn-error.log 12 | .env 13 | .DS_Store 14 | /public/test-coverage 15 | /public/js/* 16 | /public/css/* 17 | /public/mix-manifest.json 18 | 19 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/components/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export const PostShow = Vue.component('post-show', require('./PostShow.vue')) 4 | export const PostCreate = Vue.component('post-create', require('./PostCreate.vue')) 5 | export const PostEdit = Vue.component('post-edit', require('./PostEdit.vue')) 6 | -------------------------------------------------------------------------------- /app/Models/Promotion.php: -------------------------------------------------------------------------------- 1 | call(UsersTableSeeder::class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/assets/js/backend/components/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /resources/assets/sass/components/_ratios.scss: -------------------------------------------------------------------------------- 1 | .ratio-3\/4 { 2 | position: relative; 3 | &:before { 4 | display: block; 5 | content: ""; 6 | width: 100%; 7 | padding-top: (3 / 4) * 100%; 8 | } 9 | > .inner { 10 | position: absolute; 11 | top: 0; 12 | right: 0; 13 | bottom: 0; 14 | left: 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/factories/PromotionFactory.php: -------------------------------------------------------------------------------- 1 | define(\App\Models\Promotion::class, function (Faker $faker) { 6 | return [ 7 | 'name' => $faker->name, 8 | 'url' => $faker->url, 9 | 'description' => $faker->text(140), 10 | 'published' => true, 11 | ]; 12 | }); 13 | -------------------------------------------------------------------------------- /app/Models/ProfileKey.php: -------------------------------------------------------------------------------- 1 | hasMany('App\Profile'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # PHP PSR-2 Coding Standards 5 | # http://www.php-fig.org/psr/psr-2/ 6 | 7 | root = true 8 | 9 | [*.php] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = space 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | 6 | services: 7 | - mysql 8 | 9 | before_script: 10 | - cp .env.travis .env 11 | - mysql -e 'create database website_test;' 12 | - composer self-update 13 | - composer install --no-interaction 14 | - php artisan key:generate 15 | - php artisan migrate 16 | - yarn 17 | - yarn prod 18 | 19 | script: 20 | - vendor/bin/phpunit 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 12 | } 13 | 14 | public function dashboard() 15 | { 16 | return view('backend.dashboard'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | belongsToMany(User::class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /resources/assets/js/components/SiteFooter.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | -------------------------------------------------------------------------------- /database/factories/PostFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Models\Post::class, function (Faker $faker) { 6 | $title = $faker->sentence; 7 | return [ 8 | 'title' => $title, 9 | 'slug' => str_slug($title), // to be replaced with Sluggable Fake 10 | 'body' => $faker->paragraph, 11 | 'user_id' => factory('App\Models\User')->create()->id, 12 | 'post_type' => 'post' 13 | ]; 14 | }); 15 | -------------------------------------------------------------------------------- /database/seeds/UserTableSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 16 | 'name' => 'John', 17 | 'email' => 'john@doe.com', 18 | 'password' => bcrypt('secret') 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | create([ 15 | 'name' => 'Administrator', 16 | ]); 17 | 18 | factory(App\Models\Role::class)->create([ 19 | 'name' => 'Member', 20 | ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/routes/index.js: -------------------------------------------------------------------------------- 1 | import { PostShow, PostCreate, PostEdit } from '../components/index' 2 | 3 | export default [ 4 | { 5 | path: '/blog/create', 6 | component: PostCreate, 7 | name: 'post-create' 8 | }, 9 | { 10 | path: '/blog/edit/:slug', 11 | component: PostEdit, 12 | name: 'post-edit' 13 | }, 14 | { 15 | path: '/blog/:slug', 16 | component: PostShow, 17 | name: 'post-show' 18 | } 19 | ] 20 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 | 12 | 21 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 20 | 21 | Hash::setRounds(4); 22 | 23 | return $app; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Unit/UserTest.php: -------------------------------------------------------------------------------- 1 | create(); 15 | $posts = factory('App\Models\Post', 10)->create(['user_id' => $user->id]); 16 | 17 | $this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $user->posts); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/assets/js/backend/backend.js: -------------------------------------------------------------------------------- 1 | import router from './router/index' 2 | import store from './vuex/index' 3 | 4 | window._ = require('lodash'); 5 | window.Vue = require('vue'); 6 | window.axios = require('axios') 7 | 8 | Vue.component('app', require('./components/App')); 9 | Vue.component('navbar', require('../components/Navbar')); 10 | Vue.component('dropdown', require('../components/Dropdown')); 11 | Vue.component('site-footer', require('../components/SiteFooter')); 12 | 13 | const app = new Vue({ 14 | router: router, 15 | store: store, 16 | el: '#app' 17 | }); 18 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 16 | }); 17 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 17 | ]; 18 | -------------------------------------------------------------------------------- /app/Models/SocialLinks.php: -------------------------------------------------------------------------------- 1 | belongsTo(Profile::class, 'profile_id'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/sponsor.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Special Thanks To Our Sponsors

3 |
4 |

5 | Many thanks to SnapShooter.io for sponsoring the hosting of our website. SnapShooter helps you to schedule frequent customizable DigitalOcean backups of your droplets and volumes. 6 |

7 |
8 |
9 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | })->describe('Display an inspiring quote'); 19 | -------------------------------------------------------------------------------- /app/Http/Controllers/ProfileShowController.php: -------------------------------------------------------------------------------- 1 | findOrFail($id)->toArray(); 21 | 22 | return view('frontend.profile', compact('user')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DEPLOY_SOURCE=$1 3 | 4 | if [[ "$DEPLOY_SOURCE" = "github" ]]; then 5 | # activate maintenance mode 6 | php artisan down 7 | 8 | # update source code 9 | git pull 10 | echo "Codebase updated" 11 | 12 | # update PHP dependencies 13 | export COMPOSER_HOME='/tmp/composer' 14 | composer install --no-interaction --no-dev --prefer-dist 15 | 16 | else 17 | # Install frontend assets 18 | yarn 19 | 20 | yarn run production 21 | fi 22 | 23 | # update database 24 | php artisan migrate --force 25 | 26 | # stop maintenance mode 27 | php artisan up 28 | 29 | echo "Deploy script completed" 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 21 | return redirect('/'); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /resources/views/frontend/profile.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('title_and_meta') 4 | Profile - {{ $user['name'] }} 5 | 6 | 7 | 8 | 9 | @stop 10 | 11 | @section('content') 12 | @include('frontend.partials.header') 13 | 14 |

{{ $user['name'] }}

15 | 16 |
17 |         {{ print_r($user) }}
18 |     
19 | 20 | 21 | @stop 22 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Handle Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/latest-resources.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Resources

3 |
4 |
5 | 6 |

Watch this space for a list of helpful resources and links shared by our members.

7 |
8 |
9 |
10 | Coming soon. 11 |
12 |
13 | -------------------------------------------------------------------------------- /config/slack.php: -------------------------------------------------------------------------------- 1 | env('SLACK_SIGNUP_URL'), 22 | 'legacy_token' => env('SLACK_LEGACY_TOKEN') 23 | ]; -------------------------------------------------------------------------------- /app/Policies/PromotionPolicy.php: -------------------------------------------------------------------------------- 1 | id == $promotion->user_id) { 16 | return true; 17 | } 18 | 19 | return false; 20 | } 21 | 22 | public function delete(User $user, Promotion $promotion) 23 | { 24 | if($user->id == $promotion->user_id) { 25 | return true; 26 | } 27 | 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/views/frontend/master.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('frontend.partials.metadata') 5 | 13 | 14 | 15 | 16 |
17 | @yield('content') 18 |
19 | 20 | 21 | @yield('js') 22 | 23 | @include('frontend.partials.footer') 24 | 25 | 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/hero.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |

Welcome to Laravel UK

7 |

The most exciting developer group in the United Kingdom

8 | @guest 9 | 10 | 11 | 12 | @endguest 13 |
14 |
15 | -------------------------------------------------------------------------------- /database/migrations/2018_02_08_221421_create_roles_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->text('name'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('roles'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/assets/js/frontend/app.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * First we will load all of this project's JavaScript dependencies which 4 | * includes Vue and other libraries. It is a great starting point when 5 | * building robust, powerful web applications using Vue and Laravel. 6 | */ 7 | 8 | require('../bootstrap'); 9 | 10 | window.Vue = require('vue'); 11 | 12 | /** 13 | * Next, we will create a fresh Vue application instance and attach it to 14 | * the page. Then, you may begin adding components to this application 15 | * or customize the JavaScript scaffolding to fit your unique needs. 16 | */ 17 | 18 | Vue.component('Dropdown', require('../components/Dropdown')); 19 | Vue.component('Navbar', require('../components/Navbar')); 20 | 21 | const app = new Vue({ 22 | el: '#app' 23 | }); 24 | -------------------------------------------------------------------------------- /config/laraveluk.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'name' => 'Laravel UK', 8 | 'url' => 'https://laravelphp.uk', 9 | 'description' => 'Welcome to the new LaravelUK website and thanks for taking a look.', 10 | 'keywords' => 'laravel, larvel UK, UK Laravel, UK, php, framework, web, artisans', 11 | 'admin_role_name' => 'Administrator' 12 | ], 13 | 'social' => [ 14 | 'twitter_url' => 'https://twitter.com/UKLaravel', 15 | 'twitter_handle' => '@UKLaravel', 16 | 'facebook_url' => '', 17 | 'slack_url' => 'https://laraveluk.signup.team/' 18 | ] 19 | ]; 20 | -------------------------------------------------------------------------------- /app/Models/Comment.php: -------------------------------------------------------------------------------- 1 | morphTo(); 20 | } 21 | 22 | /** 23 | * A comment belongs to a user 24 | * 25 | * @return Illuminate\Database\Eloquent\Relations\BelongsTo 26 | */ 27 | public function author() 28 | { 29 | return $this->belongsTo(User::class, 'user_id'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Controllers/Blog/CommentController.php: -------------------------------------------------------------------------------- 1 | comments()->create([ 23 | 'body' => $request->body, 24 | 'user_id' => auth()->user()->id 25 | ]); 26 | 27 | return back(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/migrations/2018_02_08_224146_add_location_to_user_table.php: -------------------------------------------------------------------------------- 1 | string('location')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | $table->dropColumn('location'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Models/Profile.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, 'user_id'); 26 | } 27 | 28 | /** 29 | * A profile belongs to a profile key 30 | * 31 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 32 | */ 33 | public function profile_key() { 34 | return $this->belongsTo('App\ProfileKey'); 35 | } 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Models\User::class, function (Faker $faker) { 17 | return [ 18 | 'name' => $faker->name, 19 | 'email' => $faker->unique()->safeEmail, 20 | 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 21 | 'remember_token' => str_random(10), 22 | ]; 23 | }); 24 | -------------------------------------------------------------------------------- /database/migrations/2018_02_04_144227_add_post_type_to_posts_table.php: -------------------------------------------------------------------------------- 1 | string('post_type')->index()->default('post'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('posts', function (Blueprint $table) { 29 | $table->dropColumn('post_type'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/seeds/ProfileKeysTableSeeder.php: -------------------------------------------------------------------------------- 1 | "first_name", 16 | 'type' => \App\Enums\ProfileKeys::TEXT 17 | ]); 18 | 19 | \App\Models\ProfileKey::create([ 20 | 'name' => "last_name", 21 | 'type' => \App\Enums\ProfileKeys::TEXT 22 | ]); 23 | 24 | \App\Models\ProfileKey::create([ 25 | 'name' => "age", 26 | 'type' => \App\Enums\ProfileKeys::NUMBER 27 | ]); 28 | 29 | \App\Models\ProfileKey::create([ 30 | 'name' => "Bio", 31 | 'type' => \App\Enums\ProfileKeys::TEXTAREA 32 | ]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 17 | // add your listeners (aka providers) here 18 | 'SocialiteProviders\Slack\SlackExtendSocialite@handle', 19 | ], 20 | ]; 21 | 22 | /** 23 | * Register any events for your application. 24 | * 25 | * @return void 26 | */ 27 | public function boot() 28 | { 29 | parent::boot(); 30 | 31 | // 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 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::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_LOG_LEVEL=debug 6 | APP_URL=http://localhost 7 | 8 | DB_CONNECTION=mysql 9 | DB_HOST=127.0.0.1 10 | DB_PORT=3306 11 | DB_DATABASE=homestead 12 | DB_USERNAME=homestead 13 | DB_PASSWORD=secret 14 | 15 | DB_TEST_HOST= 16 | DB_TEST_DATABASE= 17 | DB_TEST_USERNAME= 18 | DB_TEST_PASSWORD= 19 | 20 | BROADCAST_DRIVER=log 21 | CACHE_DRIVER=file 22 | SESSION_DRIVER=file 23 | SESSION_LIFETIME=120 24 | QUEUE_DRIVER=sync 25 | 26 | REDIS_HOST=127.0.0.1 27 | REDIS_PASSWORD=null 28 | REDIS_PORT=6379 29 | 30 | MAIL_DRIVER=smtp 31 | MAIL_HOST=smtp.mailtrap.io 32 | MAIL_PORT=2525 33 | MAIL_USERNAME=null 34 | MAIL_PASSWORD=null 35 | MAIL_ENCRYPTION=null 36 | 37 | PUSHER_APP_ID= 38 | PUSHER_APP_KEY= 39 | PUSHER_APP_SECRET= 40 | PUSHER_APP_CLUSTER=mt1 41 | 42 | SLACK_CLIENT_ID= 43 | SLACK_CLIENT_SECRET= 44 | SLACK_REDIRECT_CALLBACK_URL= 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/UtilController.php: -------------------------------------------------------------------------------- 1 | getContent(); 14 | $githubHash = $request->header('X-Hub-Signature'); 15 | 16 | $localToken = config('app.deploy_secret'); 17 | $localHash = 'sha1=' . hash_hmac('sha1', $githubPayload, $localToken, false); 18 | 19 | if (hash_equals($githubHash, $localHash)) { 20 | $root_path = base_path(); 21 | $process = new Process('cd ' . $root_path . '; ./deploy github'); 22 | $process->run(function ($type, $buffer) { 23 | echo $buffer; 24 | }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | PostPolicy::class, 22 | Promotion::class => PromotionPolicy::class 23 | ]; 24 | 25 | /** 26 | * Register any authentication / authorization services. 27 | * 28 | * @return void 29 | */ 30 | public function boot() 31 | { 32 | $this->registerPolicies(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2018_02_03_154644_add_slack_id_to_user_table.php: -------------------------------------------------------------------------------- 1 | string('slack_id')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | if (Schema::hasColumn('users', 'slack_id')) { 29 | Schema::table('users', function (Blueprint $table) { 30 | $table->dropColumn('slack_id'); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->string('password'); 21 | $table->rememberToken(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('users'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2018_03_04_133849_create_profile_keys_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 50); 19 | $table->tinyInteger('type'); // Text / Number / TextArea etc (See App\Enums\ProfileKeys) 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('profile_keys'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2018_02_04_192742_create_social_links_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('profile_id')->unsigned()->index(); 19 | $table->string('url'); 20 | $table->string('icon')->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('social_links'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2018_02_03_143413_create_posts_table.php: -------------------------------------------------------------------------------- 1 | gincrements('id'); 18 | $table->string('title'); 19 | $table->string('slug'); 20 | $table->longtext('body'); 21 | $table->integer('user_id')->unsigned()->index(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('posts'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2018_02_04_204455_create_comments_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->index(); 19 | $table->text('body'); 20 | $table->integer('commentable_id')->index(); 21 | $table->string('commentable_type'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('comments'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Http/Controllers/Blog/PostTagController.php: -------------------------------------------------------------------------------- 1 | get(); 20 | $title = "Posts Tagged: {$tag}"; 21 | 22 | $metadata = [ 23 | 'title' => 'Laravel UK | Articles by ' . $tag . ' tag', 24 | 'description' => 'A comprehensive list of our latets news, events, tutorials and interviews marked as ' . $tag, 25 | 'keywords' => config('site.keywords') . $tag 26 | ]; 27 | 28 | return \view('frontend.posts.index', compact('posts', 'title', 'metadata')); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/assets/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | 2 | window._ = require('lodash'); 3 | 4 | /** 5 | * We'll load the axios HTTP library which allows us to easily issue requests 6 | * to our Laravel back-end. This library automatically handles sending the 7 | * CSRF token as a header based on the value of the "XSRF" token cookie. 8 | */ 9 | 10 | window.axios = require('axios'); 11 | 12 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 13 | 14 | /** 15 | * Next we will register the CSRF Token as a common header with Axios so that 16 | * all outgoing HTTP requests automatically have it attached. This is just 17 | * a simple convenience so we don't have to attach every token manually. 18 | */ 19 | 20 | let token = document.head.querySelector('meta[name="csrf-token"]'); 21 | 22 | if (token) { 23 | window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; 24 | } else { 25 | console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); 26 | } -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | create(); 16 | $this->actingAs($user); 17 | return $this; 18 | } 19 | 20 | // Hat tip, @adamwathan. 21 | protected function disableExceptionHandling() 22 | { 23 | $this->oldExceptionHandler = $this->app->make(ExceptionHandler::class); 24 | $this->app->instance(ExceptionHandler::class, new class extends Handler { 25 | public function __construct() {} 26 | public function report(\Exception $e) {} 27 | public function render($request, \Exception $e) { 28 | throw $e; 29 | } 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/latest-interviews.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Interviews

3 |
4 |
5 | 6 | Simon Bennet 7 |

4th February 2018

8 |

9 | Laravel UK recently had a chat with one of us, Simon Bennet, creator of Snapshooter.io. 10 |

11 |
12 |
13 |
14 | More interviews coming soon. 15 |
16 |
17 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire') 28 | // ->hourly(); 29 | } 30 | 31 | /** 32 | * Register the commands for the application. 33 | * 34 | * @return void 35 | */ 36 | protected function commands() 37 | { 38 | $this->load(__DIR__.'/Commands'); 39 | 40 | require base_path('routes/console.php'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /resources/assets/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Body 3 | $body-bg: #f5f8fa; 4 | 5 | // Borders 6 | $laravel-border-color: darken($body-bg, 10%); 7 | $list-group-border: $laravel-border-color; 8 | $navbar-default-border: $laravel-border-color; 9 | $panel-default-border: $laravel-border-color; 10 | $panel-inner-border: $laravel-border-color; 11 | 12 | // Brands 13 | $brand-primary: #3097D1; 14 | $brand-info: #8eb4cb; 15 | $brand-success: #2ab27b; 16 | $brand-warning: #cbb956; 17 | $brand-danger: #bf5329; 18 | 19 | // Typography 20 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 21 | $font-family-sans-serif: "Raleway", sans-serif; 22 | $font-size-base: 14px; 23 | $line-height-base: 1.6; 24 | $text-color: #636b6f; 25 | 26 | // Navbar 27 | $navbar-default-bg: #fff; 28 | 29 | // Buttons 30 | $btn-default-color: $text-color; 31 | 32 | // Inputs 33 | $input-border: lighten($text-color, 40%); 34 | $input-border-focus: lighten($brand-primary, 25%); 35 | $input-color-placeholder: lighten($text-color, 30%); 36 | 37 | // Panels 38 | $panel-default-heading-bg: #fff; 39 | -------------------------------------------------------------------------------- /database/migrations/2018_02_08_221628_role_user.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned()->index(); 19 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 20 | $table->integer('role_id')->unsigned()->index(); 21 | $table->foreign('role_id')->references('id')->on('roles')->onDelete('no action'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('role_user'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Policies/PostPolicy.php: -------------------------------------------------------------------------------- 1 | hasRole('Admin')) { 17 | return true; 18 | } 19 | } 20 | 21 | /** 22 | * Determine whether the user can update the post. 23 | * 24 | * @param \App\Models\User $user 25 | * @param \App\Models\Post $post 26 | * @return mixed 27 | */ 28 | public function update(User $user, Post $post) 29 | { 30 | return $user->id === $post->user_id; 31 | } 32 | 33 | /** 34 | * Determine whether the user can delete the post. 35 | * 36 | * @param \App\Models\User $user 37 | * @param \App\Models\Post $post 38 | * @return mixed 39 | */ 40 | public function delete(User $user, Post $post) 41 | { 42 | return $user->id === $post->user_id; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | let tailwindcss = require('tailwindcss'); 3 | 4 | let mix = require('laravel-mix'); 5 | 6 | /* 7 | |-------------------------------------------------------------------------- 8 | | Mix Asset Management 9 | |-------------------------------------------------------------------------- 10 | | 11 | | Mix provides a clean, fluent API for defining some Webpack build steps 12 | | for your Laravel application. By default, we are compiling the Sass 13 | | file for the application as well as bundling up all the JS files. 14 | | 15 | */ 16 | 17 | // Frontend assets 18 | mix.js('resources/assets/js/frontend/app.js', 'public/js/') 19 | .sass('resources/assets/sass/app.scss', 'public/css/'); 20 | 21 | //Backend assets 22 | mix.js('resources/assets/js/backend/backend.js', 'public/js/'); 23 | 24 | mix.options({ 25 | processCssUrls: false, 26 | postCss: [tailwindcss('./tailwind.js')], 27 | }); 28 | 29 | if (mix.inProduction) { 30 | mix.version(); 31 | } 32 | 33 | mix.browserSync({ 34 | proxy: process.env.APP_URL, 35 | open: false 36 | }); 37 | -------------------------------------------------------------------------------- /database/migrations/2018_02_04_191245_create_profiles_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned()->index(); 19 | $table->string('past')->nullable(); 20 | $table->string('present')->nullable(); 21 | $table->string('future')->nullable(); 22 | $table->string('hobbies')->nullable(); 23 | $table->timestamp('deleted_at')->nullable(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('profiles'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | middleware('guest')->except('logout'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('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 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/migrations/2018_03_04_195413_create_promotions_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->unsignedInteger('user_id'); 19 | $table->string('name'); 20 | $table->string('description', 140); 21 | $table->string('url'); 22 | $table->boolean('published')->default(true); 23 | $table->softDeletes(); 24 | $table->timestamps(); 25 | 26 | 27 | $table->foreign('user_id')->references('id')->on('users'); 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | * 34 | * @return void 35 | */ 36 | public function down() 37 | { 38 | Schema::dropIfExists('promotions'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /config/passwordreset.php: -------------------------------------------------------------------------------- 1 | App\Models\User::class, 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Search field 17 | |-------------------------------------------------------------------------- 18 | | 19 | | If no ID field is provided when the command is called the command will 20 | | prompt for a user. This is the field that should be used to search. 21 | | 22 | */ 23 | 'search_field' => 'name', 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Password field 28 | |-------------------------------------------------------------------------- 29 | | 30 | | This is the name of the password field that this command should reset. 31 | | 32 | */ 33 | 'password_field' => 'password', 34 | 35 | ]; 36 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/UserController.php: -------------------------------------------------------------------------------- 1 | update($request->only('name', 'email', 'location')); 27 | 28 | if (Auth::user()) { 29 | Log::debug(Auth::user()->name . " updated profile information for {$user->name}"); 30 | } 31 | 32 | return $user; 33 | } 34 | 35 | public function resetPassword(Request $request, $user_id) 36 | { 37 | $user = User::findOrFail($user_id); 38 | 39 | $exitCode = Artisan::call('user:resetpassword', [ 40 | 'user' => $user->id, 41 | '--force' => true 42 | ]); 43 | 44 | return $user; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /resources/views/backend/master.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{-- Add title and meta description --}} 10 | @yield('title_and_meta') 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 27 | 28 | 29 |
30 | @yield('content') 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/Feature 14 | 15 | 16 | 17 | ./tests/Unit 18 | 19 | 20 | 21 | 22 | ./app 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /resources/assets/sass/components/_forms.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * A place to add form based styles 3 | */ 4 | label { 5 | @apply .block .text-grey-darker .text-sm .font-bold .mb-2; 6 | sup { 7 | @apply .text-red .font-bold; 8 | } 9 | } 10 | p.info { 11 | @apply .block .text-xs .text-grey-dark .font-normal .pb-4 .tracking-wide; 12 | } 13 | input, textarea { 14 | @apply .appearance-none .bg-grey-lighter .border .border-grey .rounded .w-full .p-4 .text-grey-darker; 15 | } 16 | 17 | select { 18 | @apply .block .appearance-none .w-full .bg-grey-lighter .border .border-grey .p-4 .rounded .cursor-pointer; 19 | transition: border-color 800ms; 20 | &:hover { 21 | @apply .border-grey-dark; 22 | } 23 | } 24 | 25 | button { 26 | @apply .my-3 .bg-blue-light .text-white .font-bold .p-4 .rounded; 27 | transition: background-color 800ms; 28 | &:hover { 29 | @apply .bg-blue-dark; 30 | } 31 | &:disabled { 32 | @apply .bg-grey-light .cursor-not-allowed; 33 | } 34 | } 35 | 36 | .editr--toolbar { 37 | @apply .bg-grey-lighter .border .border-grey; 38 | } -------------------------------------------------------------------------------- /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 | 'slack' => [ 39 | 'client_id' => env('SLACK_CLIENT_ID'), 40 | 'client_secret' => env('SLACK_CLIENT_SECRET'), 41 | 'redirect' => env('SLACK_REDIRECT_CALLBACK_URL'), 42 | ], 43 | ]; 44 | -------------------------------------------------------------------------------- /database/migrations/2018_03_04_133955_remove_profiles_columns_and_add_key_pairs.php: -------------------------------------------------------------------------------- 1 | dropColumn(['past','present','future','hobbies']); 18 | $table->integer('profile_key_id')->unsigned()->after('user_id'); 19 | $table->text('value')->after('profile_key_id'); 20 | $table->foreign('profile_key_id')->references('id')->on('profile_keys')->onDelete('cascade'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::table('profiles', function (Blueprint $table) { 32 | $table->dropForeign(['profile_key_id']); 33 | $table->dropColumn(['profile_key_id','value']); 34 | $table->string('past'); 35 | $table->string('present'); 36 | $table->string('future'); 37 | $table->string('hobbies'); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | name('home'); 5 | Route::get('/about', 'PagesController@aboutUs')->name('about'); 6 | Route::get('/laravel-people', 'PagesController@laravelPeople')->name('laravelpeeps'); 7 | 8 | /** Dashboard Routes **/ 9 | Route::get('/members', 'MemberDashboardController@dashboard')->name('dashboard'); 10 | 11 | /** Authentication Routes */ 12 | Auth::routes(); 13 | Route::get('login/slack', 'Auth\SlackLoginController@redirectToProvider')->name('login.slack'); 14 | Route::get('login/slack/callback', 'Auth\SlackLoginController@handleProviderCallback'); 15 | Route::get('logout', 'Auth\LoginController@logout')->name('logout'); 16 | 17 | /** Blog Routes */ 18 | Route::prefix('blog')->namespace('Blog')->group(function () { 19 | Route::get('/tag/{tag}', 'PostTagController@show')->name('frontend.posts.tags.show'); 20 | Route::get('/{type?}', 'PostController@index')->name('frontend.posts.index'); 21 | Route::get('/{type}/{post}', 'PostController@show')->name('frontend.posts.show'); 22 | /** Comment Routes */ 23 | Route::post('/{type}/{post}/comment', 'CommentController@store')->name('frontend.comments.store'); 24 | }); 25 | 26 | /** Profile Show Route */ 27 | Route::get('profiles/{id}', 'ProfileShowController')->name('profile'); 28 | 29 | /** Redirects*/ 30 | Route::redirect('tutorials', '/blog/tutorials'); 31 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | name('deploy'); 16 | 17 | Route::middleware(['web', 'auth', 'api'])->group(function () { 18 | Route::resource('user', 'UserController', [ 19 | 'only' => [ 20 | 'index', 21 | 'show', 22 | 'update', 23 | ] 24 | ]); 25 | 26 | Route::resource('post', 'PostController', [ 27 | 'only' => [ 28 | 'index', 29 | 'show', 30 | 'store', 31 | 'update', 32 | 'destroy' 33 | ] 34 | ]); 35 | 36 | Route::resource('profile', 'ProfileController', [ 37 | 'only' => [ 38 | 'show', 39 | 'update', 40 | 'destroy' 41 | ] 42 | ]); 43 | 44 | Route::group(['prefix' => 'promotions'], function() { 45 | Route::post('/store', 'PromotionsController@store'); 46 | Route::get('/get/{promotion}', 'PromotionsController@get'); 47 | Route::put('/update/{promotion}', 'PromotionsController@update'); 48 | Route::delete('/delete/{promotion}', 'PromotionsController@delete'); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /database/migrations/2018_02_04_183153_add_slack_fields_to_user_table.php: -------------------------------------------------------------------------------- 1 | string('avatar')->nullable(); 18 | $table->string('nickname')->nullable(); 19 | $table->string('slug')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | if (Schema::hasColumn('users', 'avatar')) { 31 | Schema::table('users', function (Blueprint $table) { 32 | $table->dropColumn('avatar'); 33 | }); 34 | } 35 | 36 | if (Schema::hasColumn('users', 'nickname')) { 37 | Schema::table('users', function (Blueprint $table) { 38 | $table->dropColumn('nickname'); 39 | }); 40 | } 41 | 42 | if (Schema::hasColumn('users', 'slug')) { 43 | Schema::table('users', function (Blueprint $table) { 44 | $table->dropColumn('slug'); 45 | }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.17", 14 | "bootstrap-sass": "^3.3.7", 15 | "browser-sync": "^2.23.6", 16 | "browser-sync-webpack-plugin": "^2.0.1", 17 | "cross-env": "^5.1", 18 | "jquery": "^3.2", 19 | "laravel-mix": "^1.0", 20 | "lodash": "^4.17.5", 21 | "tailwindcss": "^0.4.1", 22 | "vue": "^2.5.7" 23 | }, 24 | "dependencies": { 25 | "animate.css": "^3.6.1", 26 | "dotenv": "^5.0.0", 27 | "vue-pagination-2": "^1.3.1", 28 | "vue-router": "^3.0.1", 29 | "vue2-editor": "^2.4.2", 30 | "vuex": "^3.0.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/PagesController.php: -------------------------------------------------------------------------------- 1 | get()->sortByDesc('created_at')->take(5); 13 | 14 | return view('frontend.home.index', compact('posts')); 15 | } 16 | 17 | public function aboutUs() 18 | { 19 | $metadata = [ 20 | 'title' => 'Laravel UK / About Us', 21 | 'description' => 'The most exciting coding community in the UK', 22 | 'keywords' => config('site.keywords') . ', Laravel, LaravelUK, United Kingdom, coding, programming, community, Laracon, Slack, About Us, Ian Landsman, Laura Elizabeth, Matt Stauffer, Taylor Otwell, Jeffery Way, Evan You, Chris Fidao, Adam Watham, Mohamed Said, Michael Dyrynda, Steve Schoger, Jacob Bennett' 23 | ]; 24 | 25 | return view('frontend.about', compact('metadata')); 26 | } 27 | 28 | public function laravelPeople() 29 | { 30 | $metadata = [ 31 | 'title' => 'Laravel UK / Laravel People', 32 | 'description' => 'These wonderful folk contribute so much to the Laravel community.', 33 | 'keywords' => config('site.keywords') . ', laravel people, people, wonderful, folk, Eric L. Barnes, Michael Dyrynda, Ian Landsman, Laura Elizabeth, Matt Stauffer, taylor otwell, Jeffery Way, Evan You, Chris Fidao, Adam Watham, Mohamed Said, Michael Dyrynda, Steve Schoger, Jacob Bennett' 34 | ]; 35 | 36 | return view('frontend.laravel_people', compact('metadata')); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /resources/views/backend/partials/nav.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
7 |
8 | LaravelUK 9 |
10 |
11 |
12 | 13 |
14 | 20 |
21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/broadcasting.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 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'encrypted' => true, 41 | ], 42 | ], 43 | 44 | 'redis' => [ 45 | 'driver' => 'redis', 46 | 'connection' => 'default', 47 | ], 48 | 49 | 'log' => [ 50 | 'driver' => 'log', 51 | ], 52 | 53 | 'null' => [ 54 | 'driver' => 'null', 55 | ], 56 | 57 | ], 58 | 59 | ]; 60 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /app/Http/Controllers/Blog/PostController.php: -------------------------------------------------------------------------------- 1 | get()->sortByDesc('created_at'); 20 | $metadata_title = 'Laravel UK / News, Interviews & Articles'; 21 | } else { 22 | $posts = Post::with('author', 'tags')->where('post_type', $postType)->get()->sortByDesc('created_at'); 23 | $metadata_title = 'Laravel UK | ' . title_case($postType); 24 | } 25 | 26 | $metadata = [ 27 | 'title' => $metadata_title, 28 | 'description' => 'A comprehensive list of our latets news, events, tutorials and interviews', 29 | 'keywords' => config('site.keywords') . ', news, articles, tutorials, interviews, event, events' 30 | ]; 31 | 32 | return view('frontend.posts.index', compact('posts', 'metadata')); 33 | } 34 | 35 | /** 36 | * Display the specified resource. 37 | * 38 | * @param \App\Models\Post $post 39 | * @return \Illuminate\Http\Response 40 | */ 41 | public function show($postType, Post $post) 42 | { 43 | $metadata = [ 44 | 'title' => (strtolower($postType) !== 'post') ? str_singular(title_case($postType)) . ' | ' . $post->title : $post->title, 45 | 'description' => $post->excerpt, 46 | 'keywords' => config('site.keywords') . ', ' . $post->author->name 47 | ]; 48 | 49 | return view('frontend.posts.show', compact('post', 'metadata')); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /resources/views/frontend/home/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('title_and_meta') 4 | Welcome To LaravelUK Community - The most exciting Laravel community in the UK 5 | 6 | 7 | 8 | 9 | @stop 10 | 11 | @section('content') 12 | 13 | @include('frontend.home.partials.hero') 14 | 15 | 16 | 17 | @include('frontend.home.partials.introduction') 18 | 19 | 20 |
21 |
22 | 23 | @include('frontend.home.partials.sponsor') 24 | 25 | 26 |
27 | 28 | @include('frontend.home.partials.latest-posts') 29 | 30 | 31 | 32 | @include('frontend.home.partials.latest-interviews') 33 | 34 | 35 | 36 | @include('frontend.home.partials.latest-resources') 37 | 38 |
39 |
40 |
41 | @endsection 42 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 37 | 38 | $this->mapWebRoutes(); 39 | } 40 | 41 | /** 42 | * Define the "web" routes for the application. 43 | * 44 | * These routes all receive session state, CSRF protection, etc. 45 | * 46 | * @return void 47 | */ 48 | protected function mapWebRoutes() 49 | { 50 | Route::middleware('web') 51 | ->namespace($this->namespace) 52 | ->group(base_path('routes/web.php')); 53 | } 54 | 55 | /** 56 | * Define the "api" routes for the application. 57 | * 58 | * These routes all receive session state, CSRF protection, etc. 59 | * and provide an API for the various Vue components to use 60 | * 61 | * @return void 62 | */ 63 | protected function mapApiRoutes() 64 | { 65 | Route::prefix('api') 66 | ->name('api.') 67 | // ->middleware('api') 68 | ->namespace($this->namespace.'\Api') 69 | ->group(base_path('routes/api.php')); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/SlackLoginController.php: -------------------------------------------------------------------------------- 1 | redirect(); 21 | } 22 | 23 | /** 24 | * Obtain the user information from Slack. 25 | * 26 | * @return \Illuminate\Http\Response 27 | */ 28 | public function handleProviderCallback() 29 | { 30 | $slackUser = Socialite::driver('slack')->user(); 31 | 32 | if ($user = User::where('slack_id', $slackUser->getId())->first()) { // User already exists, so sign them back in 33 | 34 | $user->update([ 35 | 'name' => $slackUser->getName(), 36 | 'email' => $slackUser->getEmail(), 37 | 'avatar' => $slackUser->getAvatar(), 38 | 'nickname' => $slackUser->getNickname() 39 | ]); 40 | 41 | Log::debug("{$slackUser->getName()} logged in with Slack"); 42 | } else { // User does not exist, so register them 43 | 44 | $user = User::create([ 45 | 'slack_id' => $slackUser->getId(), 46 | 'name' => $slackUser->getName(), 47 | 'email' => $slackUser->getEmail(), 48 | 'avatar' => $slackUser->getAvatar(), 49 | 'nickname' => $slackUser->getNickname(), 50 | 'password' => bcrypt(str_random(16)), 51 | ]); 52 | 53 | Log::debug("{$slackUser->getName()} registered with Slack"); 54 | } 55 | 56 | Auth::login($user); 57 | 58 | return redirect()->route('home'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Models/Post.php: -------------------------------------------------------------------------------- 1 | [ 27 | 'source' => 'title' 28 | ] 29 | ]; 30 | } 31 | 32 | /** 33 | * Set the Route's Key Name 34 | * 35 | * @return string 36 | */ 37 | public function getRouteKeyName() 38 | { 39 | return 'slug'; 40 | } 41 | 42 | /** 43 | * Get the URL attribute (helper) 44 | * 45 | * @return string 46 | */ 47 | public function getUrlAttribute() 48 | { 49 | return route('frontend.posts.show', [$this->post_type, $this]); 50 | } 51 | 52 | /** 53 | * Get the Excerpt attribute (helper) 54 | * 55 | * @return string 56 | */ 57 | public function getExcerptAttribute() 58 | { 59 | return str_limit(strip_tags($this->body), 140); 60 | } 61 | 62 | /** 63 | * A post belongs to a user 64 | * 65 | * @return Illuminate\Database\Eloquent\Relations\BelongsTo 66 | */ 67 | public function author() 68 | { 69 | return $this->belongsTo(\App\Models\User::class, 'user_id'); 70 | } 71 | 72 | /** 73 | * A post has and belongs to many comments 74 | * 75 | * @return Illuminate\Database\Eloquent\Relations\MorphMany 76 | */ 77 | public function comments() 78 | { 79 | return $this->morphMany(\App\Models\Comment::class, 'commentable'); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/taggable.php: -------------------------------------------------------------------------------- 1 | ',;', 10 | 11 | /** 12 | * Character used to delimit tag lists returned in the 13 | * tagList, tagListNormalized, etc. attributes. 14 | */ 15 | 'glue' => ',', 16 | 17 | /** 18 | * Method used to "normalize" tag names. Can either be a global function name, 19 | * a closure function, or a callable, e.g. ['Classname', 'method']. 20 | */ 21 | 'normalizer' => 'mb_strtolower', 22 | 23 | /** 24 | * The database connection to use for the Tag model and associated tables. 25 | * By default, we use the default database connection, but this can be defined 26 | * so that all the tag-related tables are stored in a different connection. 27 | */ 28 | 'connection' => null, 29 | 30 | /** 31 | * How to handle passing empty values to the scope queries. When set to false, 32 | * the scope queries will return no models. When set to true, passing an empty 33 | * value to the scope queries will throw an exception instead. 34 | */ 35 | 'throwEmptyExceptions' => false, 36 | 37 | /** 38 | * If you want to be able to find all the models that share a tag, you will need 39 | * to define the inverse relations here. The array keys are the relation names 40 | * you would use to access them (e.g. "posts") and the values are the qualified 41 | * class names of the models that are taggable (e.g. "\App\Post). e.g. with 42 | * the following configuration: 43 | * 44 | * 'taggedModels' => [ 45 | * 'posts' => \App\Post::class 46 | * ] 47 | * 48 | * You will be able to do: 49 | * 50 | * $posts = Tag::findByName('Apple')->posts; 51 | * 52 | * to get a collection of all the Posts that are tagged "Apple". 53 | */ 54 | 55 | 'taggedModels' => [], 56 | ]; 57 | -------------------------------------------------------------------------------- /tests/Unit/PostTest.php: -------------------------------------------------------------------------------- 1 | post = factory(Post::class)->make(); 22 | } 23 | 24 | /** @test */ 25 | public function it_has_a_title() 26 | { 27 | $this->assertInternalType("string", $this->post->title); 28 | $this->assertNotNull($this->post->title); 29 | } 30 | 31 | /** @test */ 32 | public function it_has_a_slug() 33 | { 34 | $this->assertInternalType("string", $this->post->slug); 35 | $this->assertNotNull($this->post->slug); 36 | } 37 | 38 | /** @test */ 39 | public function it_has_a_body() 40 | { 41 | $this->assertInternalType("string", $this->post->body); 42 | $this->assertNotNull($this->post->body); 43 | } 44 | 45 | /** @test */ 46 | public function it_has_a_user_id() 47 | { 48 | $this->assertInternalType("int", $this->post->user_id); 49 | $this->assertNotNull($this->post->user_id); 50 | } 51 | 52 | /** @test */ 53 | public function it_has_an_author() 54 | { 55 | $this->assertInstanceOf(User::class, $this->post->author); 56 | } 57 | 58 | /** @test */ 59 | public function it_can_tell_you_its_url() 60 | { 61 | $post = factory('App\Models\Post')->create(); 62 | $url = route('frontend.posts.show', [$post->post_type, $post]); 63 | $this->assertEquals($url, $post->url); 64 | } 65 | 66 | /** @test */ 67 | public function it_can_provide_an_excerpt() 68 | { 69 | $post = factory('App\Models\Post')->create(); 70 | $excerpt = str_limit($post->body, 140); 71 | $this->assertEquals($excerpt, $post->excerpt); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "description": "The Laravel Framework.", 4 | "keywords": ["framework", "laravel"], 5 | "license": "MIT", 6 | "type": "project", 7 | "require": { 8 | "php": ">=7.0.0", 9 | "cviebrock/eloquent-sluggable": "^4.3", 10 | "cviebrock/eloquent-taggable": "^3.3", 11 | "fideloper/proxy": "~4.0", 12 | "joepriest/passwordreset": "^1.2.1", 13 | "laravel/framework": "5.6.*", 14 | "laravel/passport": "^4.0", 15 | "laravel/socialite": "^3.0", 16 | "laravel/tinker": "~1.0", 17 | "mashape/unirest-php": "^3.0", 18 | "socialiteproviders/slack": "^3.0" 19 | }, 20 | "require-dev": { 21 | "barryvdh/laravel-debugbar": "^3.1", 22 | "filp/whoops": "~2.0", 23 | "fzaninotto/faker": "~1.4", 24 | "mockery/mockery": "~1.0", 25 | "phpunit/phpunit": "~6.0", 26 | "symfony/thanks": "^1.0" 27 | }, 28 | "autoload": { 29 | "classmap": [ 30 | "database/seeds", 31 | "database/factories" 32 | ], 33 | "psr-4": { 34 | "App\\": "app/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "Tests\\": "tests/" 40 | } 41 | }, 42 | "extra": { 43 | "laravel": { 44 | "dont-discover": [ 45 | "laravel/socialite" 46 | ] 47 | } 48 | }, 49 | "scripts": { 50 | "post-root-package-install": [ 51 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 52 | ], 53 | "post-create-project-cmd": [ 54 | "@php artisan key:generate" 55 | ], 56 | "post-autoload-dump": [ 57 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 58 | "@php artisan package:discover" 59 | ] 60 | }, 61 | "config": { 62 | "preferred-install": "dist", 63 | "sort-packages": true, 64 | "optimize-autoloader": true 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | define('LARAVEL_START', microtime(true)); 11 | 12 | /* 13 | |-------------------------------------------------------------------------- 14 | | Register The Auto Loader 15 | |-------------------------------------------------------------------------- 16 | | 17 | | Composer provides a convenient, automatically generated class loader for 18 | | our application. We just need to utilize it! We'll simply require it 19 | | into the script here so that we don't have to worry about manual 20 | | loading any of our classes later on. It feels great to relax. 21 | | 22 | */ 23 | 24 | require __DIR__.'/../vendor/autoload.php'; 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Turn On The Lights 29 | |-------------------------------------------------------------------------- 30 | | 31 | | We need to illuminate PHP development, so let us turn on the lights. 32 | | This bootstraps the framework and gets it ready for use, then it 33 | | will load up this application so that we can run it and send 34 | | the responses back to the browser and delight our users. 35 | | 36 | */ 37 | 38 | $app = require_once __DIR__.'/../bootstrap/app.php'; 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Run The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once we have the application, we can handle the incoming request 46 | | through the kernel, and send the associated response back to 47 | | the client's browser allowing them to enjoy the creative 48 | | and wonderful application we have prepared for them. 49 | | 50 | */ 51 | 52 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 53 | 54 | $response = $kernel->handle( 55 | $request = Illuminate\Http\Request::capture() 56 | ); 57 | 58 | $response->send(); 59 | 60 | $kernel->terminate($request, $response); 61 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Log Channels 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Here you may configure the log channels for your application. Out of 20 | | the box, Laravel uses the Monolog PHP logging library. This gives 21 | | you a variety of powerful log handlers / formatters to utilize. 22 | | 23 | | Available Drivers: "single", "daily", "slack", "syslog", 24 | | "errorlog", "custom", "stack" 25 | | 26 | */ 27 | 'channels' => [ 28 | 'stack' => [ 29 | 'driver' => 'stack', 30 | 'channels' => ['single'], 31 | ], 32 | 'single' => [ 33 | 'driver' => 'single', 34 | 'path' => storage_path('logs/laravel.log'), 35 | 'level' => 'debug', 36 | ], 37 | 'daily' => [ 38 | 'driver' => 'daily', 39 | 'path' => storage_path('logs/laravel.log'), 40 | 'level' => 'debug', 41 | 'days' => 7, 42 | ], 43 | 'slack' => [ 44 | 'driver' => 'slack', 45 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 46 | 'username' => 'Laravel Log', 47 | 'emoji' => ':boom:', 48 | 'level' => 'critical', 49 | ], 50 | 'syslog' => [ 51 | 'driver' => 'syslog', 52 | 'level' => 'debug', 53 | ], 54 | 'errorlog' => [ 55 | 'driver' => 'errorlog', 56 | 'level' => 'debug', 57 | ], 58 | ], 59 | ]; 60 | -------------------------------------------------------------------------------- /resources/assets/sass/app.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * This injects Tailwind's base styles, which is a combination of 3 | * Normalize.css and some additional base styles. 4 | * 5 | * You can see the styles here: 6 | * https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css 7 | */ 8 | @import url('https://fonts.googleapis.com/css?family=Noto+Sans:400,700'); 9 | @import "../../../node_modules/animate.css/animate"; 10 | 11 | @tailwind preflight; 12 | 13 | /** 14 | * Custom elements added here 15 | * 16 | * Example: 17 | * 18 | * .btn { ... } 19 | * .form-input { ... } 20 | * 21 | * Or if using a preprocessor: 22 | * 23 | * @import "components/buttons"; 24 | * @import "components/forms"; 25 | */ 26 | body, html { 27 | height: 100%; 28 | box-sizing: border-box; 29 | -webkit-font-smoothing: antialiased; 30 | -moz-osx-font-smoothing: grayscale; 31 | @media only screen and (-webkit-min-device-pixel-ratio: 1.25), 32 | only screen and (min-device-pixel-ratio: 1.25), 33 | only screen and (min-resolution: 200dpi), 34 | only screen and (min-resolution: 1.25dppx) { 35 | -webkit-font-smoothing: subpixel-antialiased; 36 | } 37 | } 38 | 39 | 40 | input, 41 | select, 42 | select:focus, 43 | textarea { 44 | font-size: 16px; //-- this will prevent the input boxes from scaling/zooming on focus 45 | } 46 | 47 | 48 | ::-webkit-input-placeholder { 49 | /* Chrome/Opera/Safari */ 50 | @apply .text-grey .text-sm; 51 | letter-spacing: 0.04rem; 52 | } 53 | ::-moz-placeholder { 54 | /* Firefox 19+ */ 55 | @apply .text-grey .text-sm; 56 | letter-spacing: 0.04rem; 57 | } 58 | :-ms-input-placeholder { 59 | /* IE 10+ */ 60 | @apply .text-grey .text-sm; 61 | letter-spacing: 0.04rem; 62 | } 63 | :-moz-placeholder { 64 | /* Firefox 18- */ 65 | @apply .text-grey .text-sm; 66 | letter-spacing: 0.04rem; 67 | } 68 | 69 | @import "components/links"; 70 | @import "components/ratios"; 71 | @import "components/nav"; 72 | @import "components/forms"; 73 | @import "components/homepage"; 74 | 75 | /** 76 | * Tailwind's utility classes injected here. Generated based on your 77 | * config file. 78 | */ 79 | @tailwind utilities; 80 | 81 | /** 82 | * Add custom utilities not covered by Tailwind 83 | */ 84 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 31 | \App\Http\Middleware\EncryptCookies::class, 32 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 33 | \Illuminate\Session\Middleware\StartSession::class, 34 | // \Illuminate\Session\Middleware\AuthenticateSession::class, 35 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 36 | \App\Http\Middleware\VerifyCsrfToken::class, 37 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 38 | ], 39 | 40 | 'api' => [ 41 | 'throttle:60,1', 42 | 'bindings', 43 | ], 44 | ]; 45 | 46 | /** 47 | * The application's route middleware. 48 | * 49 | * These middleware may be assigned to groups or used individually. 50 | * 51 | * @var array 52 | */ 53 | protected $routeMiddleware = [ 54 | 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 55 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 56 | 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 57 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 58 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 59 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 60 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 61 | ]; 62 | } 63 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Cloud Filesystem Disk 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Many applications store files both locally and in the cloud. For this 24 | | reason, you may specify a default "cloud" driver here. This driver 25 | | will be bound as the Cloud disk implementation in the container. 26 | | 27 | */ 28 | 29 | 'cloud' => env('FILESYSTEM_CLOUD', 's3'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Filesystem Disks 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may configure as many filesystem "disks" as you wish, and you 37 | | may even configure multiple disks of the same driver. Defaults have 38 | | been setup for each driver as an example of the required options. 39 | | 40 | | Supported Drivers: "local", "ftp", "s3", "rackspace" 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'url' => env('APP_URL').'/storage', 55 | 'visibility' => 'public', 56 | ], 57 | 58 | 's3' => [ 59 | 'driver' => 's3', 60 | 'key' => env('AWS_ACCESS_KEY_ID'), 61 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 62 | 'region' => env('AWS_DEFAULT_REGION'), 63 | 'bucket' => env('AWS_BUCKET'), 64 | ], 65 | 66 | ], 67 | 68 | ]; 69 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegisterController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 41 | } 42 | 43 | /** 44 | * Get a validator for an incoming registration request. 45 | * 46 | * @param array $data 47 | * @return \Illuminate\Contracts\Validation\Validator 48 | */ 49 | protected function validator(array $data) 50 | { 51 | return Validator::make($data, [ 52 | 'name' => 'required|string|max:255', 53 | 'email' => 'required|string|email|max:255|unique:users', 54 | 'location' => 'required|string|max:255', 55 | 'password' => 'required|string|min:6|confirmed', 56 | ]); 57 | } 58 | 59 | /** 60 | * Create a new user instance after a valid registration. 61 | * 62 | * @param array $data 63 | * @return \App\Models\User 64 | */ 65 | protected function create(array $data) 66 | { 67 | $role = Role::whereName('Member')->first(); 68 | 69 | $user = User::create([ 70 | 'name' => $data['name'], 71 | 'email' => $data['email'], 72 | 'location' => $data['location'], 73 | 'password' => bcrypt($data['password']), 74 | ]); 75 | 76 | $user->assignRole($role); 77 | $user->attemptSlackNotification(); 78 | 79 | return $user; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/introduction.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

5 | Hello and welcome to Laravel UK community. 6 |

7 |

8 | If you are already a member of the LaravelUK Slack community, then you will know what an amazing place it is to be. If you haven’t joined us yet, then you really should. 9 |

10 |

11 | The skill levels range from learner to decades of experience in PHP and many other languages; from junior developers to business owners, entrepreneurs, conference speakers and book authors. 12 | 13 | All you need to do is sign up on our website and you'll receive a Slack invitation in your email inbox. 14 |

15 |

16 | See you soon 😁 17 |

18 |
19 | 25 |
26 |
27 | -------------------------------------------------------------------------------- /tests/Feature/ViewPostsTest.php: -------------------------------------------------------------------------------- 1 | create(); 16 | // When we visit the posts index 17 | $this->call('GET', route('frontend.posts.index')) 18 | // It should load successfully 19 | ->assertStatus(200) 20 | // And we should see a post title and excerpt for the first post 21 | ->assertSee($posts->first()->title) 22 | ->assertSee($posts->first()->excerpt) 23 | // and the same for the last post 24 | ->assertSee($posts->last()->title) 25 | ->assertSee($posts->last()->excerpt); 26 | } 27 | 28 | /** @test */ 29 | public function any_user_may_view_a_blog_post() 30 | { 31 | // Given that there is a post in the database 32 | $post = factory('App\Models\Post')->create(); 33 | // When we visit that post's URL 34 | $this->call('GET', route('frontend.posts.show', [$post->post_type, $post])) 35 | // It should load successfully 36 | ->assertStatus(200) 37 | // and we should at a minimum see the title, date, body and author name. 38 | ->assertSee($post->title) 39 | ->assertSee($post->created_at->format('jS F Y')) 40 | ->assertSee($post->body) 41 | ->assertSee($post->author->name); 42 | } 43 | 44 | /** @test */ 45 | public function any_blog_post_gets_its_own_dynamic_metadata_information() 46 | { 47 | // Given that there is a post in the database 48 | $post = factory('App\Models\Post')->create(); 49 | 50 | $title = $post->title; 51 | $excerpt = $post->excerpt; 52 | // When we visit that post's URL 53 | $this->call('GET', route('frontend.posts.show', [$post->post_type, $post])) 54 | // It should load successfully 55 | ->assertStatus(200) 56 | // and check that we have the metadata title set to the post title. 57 | ->assertSee('' . $title . '') 58 | // and the post excerpt / facebook information 59 | ->assertSee('') 60 | ->assertSee(''); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /resources/views/frontend/partials/nav.blade.php: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /resources/views/frontend/home/partials/latest-posts.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Recent Posts

3 |
4 | @foreach ($posts as $post) 5 | @if($loop->last) 6 |
7 | @else 8 |
9 | @endif 10 | 11 | @if ($post->author) 12 | 13 | {{$post->author->name}}'s Avatar 14 | 15 | @endif 16 | 17 | 18 |
19 |

20 | 21 | {{ str_limit($post->title, 40) }} 22 | 23 |

24 |

{{$post->created_at->format('jS F Y')}}

25 | 26 | @if ($post->tags) 27 | @foreach ($post->tags as $tag) 28 | 29 | @if (!$loop->last) 30 | {{$tag}}, 31 | @else 32 | {{$tag}} 33 | @endif 34 | 35 | @endforeach 36 | @endif 37 | 38 |
39 | 40 |
41 | @endforeach 42 |
43 | @if(!$posts->count()) 44 |
45 | We haven't made any posts yet - check back soon! 46 |
47 | @else 48 |
49 | Read more posts from our blog. 50 |
51 | @endif 52 |
53 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/PromotionsController.php: -------------------------------------------------------------------------------- 1 | validateRequest($request); 20 | 21 | $promotion = auth()->user()->promotions()->create([ 22 | 'user_id' => auth()->user()->id, 23 | 'name' => $request->get('name'), 24 | 'description' => $request->get('description'), 25 | 'url' => $request->get("url"), 26 | 'published' => $request->get('published') 27 | ]); 28 | 29 | return $promotion; 30 | } 31 | 32 | /** 33 | * Allow a promotion to be updated. 34 | * 35 | * @param Request $request 36 | * @param Promotion $promotion 37 | * @return null|static 38 | */ 39 | public function update(Request $request, Promotion $promotion) 40 | { 41 | $this->validateRequest($request); 42 | $this->authorize('update', $promotion); 43 | 44 | $promotion->update([ 45 | 'name' => $request->get('name'), 46 | 'description' => $request->get('description'), 47 | 'url' => $request->get("url"), 48 | 'published' => $request->get('published') 49 | ]); 50 | 51 | return $promotion->fresh(); 52 | } 53 | 54 | /** 55 | * Allow information on a promotion to be fetched 56 | * 57 | * @param Promotion $promotion 58 | * @return Promotion 59 | */ 60 | public function get(Promotion $promotion) 61 | { 62 | return $promotion; 63 | } 64 | 65 | /** 66 | * Allow a promotion to be deleted 67 | * 68 | * @param Promotion $promotion 69 | */ 70 | public function delete(Promotion $promotion) 71 | { 72 | $this->authorize('delete', $promotion); 73 | 74 | $promotion->delete(); 75 | 76 | return; 77 | } 78 | 79 | /** 80 | * Validation for requests 81 | * 82 | * @param Request $request 83 | * @return array 84 | */ 85 | private function validateRequest(Request $request) 86 | { 87 | return $this->validate($request, [ 88 | 'name' => 'required|max:40', 89 | 'description' => 'required|max:140', 90 | 'url' => 'required|url', 91 | 'published' => 'required|boolean' 92 | ]); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_DRIVER', 'sync'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Queue Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may configure the connection information for each server that 26 | | is used by your application. A default configuration has been added 27 | | for each back-end shipped with Laravel. You are free to add more. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'table' => 'jobs', 40 | 'queue' => 'default', 41 | 'retry_after' => 90, 42 | ], 43 | 44 | 'beanstalkd' => [ 45 | 'driver' => 'beanstalkd', 46 | 'host' => 'localhost', 47 | 'queue' => 'default', 48 | 'retry_after' => 90, 49 | ], 50 | 51 | 'sqs' => [ 52 | 'driver' => 'sqs', 53 | 'key' => env('SQS_KEY', 'your-public-key'), 54 | 'secret' => env('SQS_SECRET', 'your-secret-key'), 55 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 56 | 'queue' => env('SQS_QUEUE', 'your-queue-name'), 57 | 'region' => env('SQS_REGION', 'us-east-1'), 58 | ], 59 | 60 | 'redis' => [ 61 | 'driver' => 'redis', 62 | 'connection' => 'default', 63 | 'queue' => 'default', 64 | 'retry_after' => 90, 65 | ], 66 | 67 | ], 68 | 69 | /* 70 | |-------------------------------------------------------------------------- 71 | | Failed Queue Jobs 72 | |-------------------------------------------------------------------------- 73 | | 74 | | These options configure the behavior of failed queue job logging so you 75 | | can control which database and table are used to store the jobs that 76 | | have failed. You may change them to any database / table you wish. 77 | | 78 | */ 79 | 80 | 'failed' => [ 81 | 'database' => env('DB_CONNECTION', 'mysql'), 82 | 'table' => 'failed_jobs', 83 | ], 84 | 85 | ]; 86 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/ProfileController.php: -------------------------------------------------------------------------------- 1 | profiles; 24 | } 25 | 26 | /** 27 | * Update the specified resource in storage. 28 | * 29 | * @param \Illuminate\Http\Request $request 30 | * @param int $user_id 31 | * @return \Illuminate\Http\Response 32 | */ 33 | public function update(Request $request, $user_id) 34 | { 35 | if ($user_id != auth()->id()) { 36 | return response([ 37 | 'status' => 'error' 38 | ]); 39 | } 40 | 41 | $errors = []; 42 | $user = User::findOrFail($user_id); // Check user exists 43 | 44 | foreach ($request->except(['_method']) as $key => $value) { 45 | 46 | // Check the key exists in the profile_keys table 47 | $pk = ProfileKey::where('name', $key)->first(); 48 | 49 | if ($pk) { 50 | 51 | // Check whether we are updating that key or if we should create a new one. 52 | $profile = Profile::where('user_id', $user->id)->where('profile_key_id', $pk->id)->first(); 53 | 54 | if (!$profile) { 55 | $profile = new Profile(); 56 | $profile->user_id = $user_id; 57 | $profile->profile_key_id = $pk->id; 58 | } 59 | 60 | // Create the profile line in the profiles table 61 | $this->createResource($profile, $value); 62 | 63 | } else { 64 | // Build a list of errors 65 | $errors[$key] = $key . " does not exist in the profile keys table"; 66 | } 67 | } 68 | 69 | return response([ 70 | 'status' => 'ok', 71 | 'user_profile' => $user->profiles()->get(), 72 | 'errors' => $errors 73 | ]); 74 | } 75 | 76 | private function createResource(Profile $profile, $value) { 77 | $profile->value = $value; 78 | $profile->save(); 79 | } 80 | 81 | /** 82 | * Remove the specified resource from storage. 83 | * 84 | * @param Profile $profile 85 | * @return \Illuminate\Http\Response 86 | * @throws \Exception 87 | */ 88 | public function destroy(Profile $profile) 89 | { 90 | if ($profile->user_id != auth()->id()) { 91 | return response([ 92 | 'status' => 'error' 93 | ]); 94 | } 95 | 96 | if (auth()->user()) { 97 | Log::debug(auth()->user()->name . " deleted profile line {$profile->id}"); 98 | } 99 | 100 | $profile->delete(); 101 | 102 | return response([ 103 | 'status' => 'deleted' 104 | ]); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | */ 30 | 31 | 'stores' => [ 32 | 33 | 'apc' => [ 34 | 'driver' => 'apc', 35 | ], 36 | 37 | 'array' => [ 38 | 'driver' => 'array', 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'table' => 'cache', 44 | 'connection' => null, 45 | ], 46 | 47 | 'file' => [ 48 | 'driver' => 'file', 49 | 'path' => storage_path('framework/cache/data'), 50 | ], 51 | 52 | 'memcached' => [ 53 | 'driver' => 'memcached', 54 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 55 | 'sasl' => [ 56 | env('MEMCACHED_USERNAME'), 57 | env('MEMCACHED_PASSWORD'), 58 | ], 59 | 'options' => [ 60 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 61 | ], 62 | 'servers' => [ 63 | [ 64 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 65 | 'port' => env('MEMCACHED_PORT', 11211), 66 | 'weight' => 100, 67 | ], 68 | ], 69 | ], 70 | 71 | 'redis' => [ 72 | 'driver' => 'redis', 73 | 'connection' => 'default', 74 | ], 75 | 76 | ], 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Cache Key Prefix 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When utilizing a RAM based store such as APC or Memcached, there might 84 | | be other applications utilizing the same cache. So, we'll specify a 85 | | value to get prefixed to all our keys so we can avoid collisions. 86 | | 87 | */ 88 | 89 | 'prefix' => env( 90 | 'CACHE_PREFIX', 91 | str_slug(env('APP_NAME', 'laravel'), '_').'_cache' 92 | ), 93 | 94 | ]; 95 | -------------------------------------------------------------------------------- /tests/Feature/PromotionsTest.php: -------------------------------------------------------------------------------- 1 | disableExceptionHandling(); 18 | } 19 | 20 | /** @test */ 21 | public function a_user_can_create_a_promotion() 22 | { 23 | $this->signIn(); 24 | 25 | $response = $this->post('/api/promotions/store', [ 26 | 'name' => 'My Project', 27 | 'description' => 'This is my awesome project', 28 | 'url' => 'http://github.com', 29 | 'published' => true, 30 | ]); 31 | 32 | $response->assertStatus(201); 33 | $this->assertDatabaseHas('promotions', [ 34 | 'name' => 'My Project', 35 | 'description' => 'This is my awesome project', 36 | 'url' => 'http://github.com', 37 | 'published' => true, 38 | ]); 39 | } 40 | 41 | /** @test */ 42 | public function a_user_can_read_their_promotion() 43 | { 44 | $user = factory('App\Models\User')->create(); 45 | $this->signIn($user); 46 | 47 | $promotion = factory('App\Models\Promotion')->create([ 48 | 'user_id' => $user->id 49 | ]); 50 | 51 | $response = $this->get('/api/promotions/get/' . $promotion->id); 52 | 53 | $response->assertStatus(200); 54 | $response->assertSee($promotion->name); 55 | } 56 | 57 | /** @test */ 58 | public function a_user_can_update_their_promotion() 59 | { 60 | $user = factory('App\Models\User')->create(); 61 | $this->signIn($user); 62 | 63 | $promotion = factory('App\Models\Promotion')->create([ 64 | 'user_id' => $user->id 65 | ]); 66 | 67 | $response = $this->put('/api/promotions/update/' . $promotion->id, [ 68 | 'name' => 'My Project', 69 | 'description' => 'This is my awesome project', 70 | 'url' => 'http://github.com', 71 | 'published' => true, 72 | ]); 73 | 74 | $response->assertStatus(200); 75 | $this->assertEquals('My Project', $promotion->fresh()->name); 76 | $this->assertEquals('This is my awesome project', $promotion->fresh()->description); 77 | $this->assertEquals('http://github.com', $promotion->fresh()->url); 78 | } 79 | 80 | /** @test */ 81 | public function a_user_can_delete_their_promotion() 82 | { 83 | $user = factory('App\Models\User')->create(); 84 | $this->signIn($user); 85 | 86 | $promotion = factory('App\Models\Promotion')->create([ 87 | 'user_id' => $user->id 88 | ]); 89 | 90 | $response = $this->delete('/api/promotions/delete/' . $promotion->id); 91 | 92 | $response->assertStatus(200); 93 | $this->assertNotNull($promotion->fresh()->deleted_at); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/components/PostEdit.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 80 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/components/PostCreate.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 84 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('content') 4 | 5 | @include('frontend.partials.header') 6 | 7 |
8 |
9 |
10 |
11 | {{ csrf_field() }} 12 |
13 |
Login with Slack
14 | 15 | 17 | 18 |
19 | 20 |
21 |
25 |
26 |
27 | 28 | @if ($errors->has('email')) 29 | 30 | {{ $errors->first('email') }} 31 | 32 | @endif 33 |
34 |
35 |
36 |
37 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 | 52 |
53 |
54 |
If login in with Slack, make sure you are not currently switched to another slack team in the browser.
55 |
56 |
57 |
58 |
59 | @endsection 60 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | hasMany(Post::class); 38 | } 39 | 40 | /** 41 | * A user has many promotions 42 | * 43 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 44 | */ 45 | public function promotions() 46 | { 47 | return $this->hasMany(Promotion::class); 48 | } 49 | 50 | /** 51 | * A user has many profile key/pairs 52 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 53 | */ 54 | public function profiles() { 55 | return $this->hasMany(Profile::class); 56 | } 57 | 58 | /** 59 | * User is linked to a role 60 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 61 | */ 62 | public function roles() 63 | { 64 | return $this->belongsToMany(Role::class)->withTimestamps(); 65 | } 66 | 67 | /** 68 | * Check User role 69 | * 70 | * @param string $name 71 | * @return bool 72 | */ 73 | public function hasRole($name) 74 | { 75 | foreach ($this->roles as $role) { 76 | if ($role->name == $name) { 77 | return true; 78 | } 79 | } 80 | 81 | return false; 82 | } 83 | 84 | /** 85 | * Assign user to role 86 | * 87 | * @param App\Models\Role $role 88 | */ 89 | public function assignRole(Role $role) 90 | { 91 | $this->roles()->attach($role); 92 | } 93 | 94 | /** 95 | * Remove user role 96 | * 97 | * @param App\Models\Role $role 98 | * @return int 99 | */ 100 | public function removeRole($role) 101 | { 102 | return $this->roles()->detach($role); 103 | } 104 | 105 | /** 106 | * Get the avatar attribute 107 | * 108 | * @param string $avatar 109 | * @return string 110 | */ 111 | public function getAvatarAttribute($avatar) 112 | { 113 | if (is_null($avatar) || empty($avatar)) { 114 | return asset('/images/default-avatar.png'); 115 | } else { 116 | return $avatar; 117 | } 118 | } 119 | 120 | /** 121 | * Attempt the slack notification email 122 | * 123 | * @return Unirest\Request 124 | */ 125 | public function attemptSlackNotification() 126 | { 127 | $token = config('slack.legacy_token'); 128 | \Unirest\Request::post(config('slack.invitation_url'), [], ['token' => $token, 'email' => $this->email]); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /resources/views/frontend/partials/metadata.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {!! isset($metadata['title']) ? $metadata['title'] : config('laraveluk.site.name') !!} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/reset.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
Reset Password
9 | 10 |
11 |
12 | {{ csrf_field() }} 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 | 21 | 22 | @if ($errors->has('email')) 23 | 24 | {{ $errors->first('email') }} 25 | 26 | @endif 27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 | 35 | 36 | @if ($errors->has('password')) 37 | 38 | {{ $errors->first('password') }} 39 | 40 | @endif 41 |
42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | @if ($errors->has('password_confirmation')) 50 | 51 | {{ $errors->first('password_confirmation') }} 52 | 53 | @endif 54 |
55 |
56 | 57 |
58 |
59 | 62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | @endsection 71 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'passport', 46 | 'provider' => 'users', 47 | ], 48 | ], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | User Providers 53 | |-------------------------------------------------------------------------- 54 | | 55 | | All authentication drivers have a user provider. This defines how the 56 | | users are actually retrieved out of your database or other storage 57 | | mechanisms used by this application to persist your user's data. 58 | | 59 | | If you have multiple user tables or models you may configure multiple 60 | | sources which represent each model / table. These sources may then 61 | | be assigned to any extra authentication guards you have defined. 62 | | 63 | | Supported: "database", "eloquent" 64 | | 65 | */ 66 | 67 | 'providers' => [ 68 | 'users' => [ 69 | 'driver' => 'eloquent', 70 | 'model' => App\Models\User::class, 71 | ], 72 | 73 | // 'users' => [ 74 | // 'driver' => 'database', 75 | // 'table' => 'users', 76 | // ], 77 | ], 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Resetting Passwords 82 | |-------------------------------------------------------------------------- 83 | | 84 | | You may specify multiple password reset configurations if you have more 85 | | than one user table or model in the application and you want to have 86 | | separate password reset settings based on the specific user types. 87 | | 88 | | The expire time is the number of minutes that the reset token should be 89 | | considered valid. This security feature keeps tokens short-lived so 90 | | they have less time to be guessed. You may change this as needed. 91 | | 92 | */ 93 | 94 | 'passwords' => [ 95 | 'users' => [ 96 | 'provider' => 'users', 97 | 'table' => 'password_resets', 98 | 'expire' => 60, 99 | ], 100 | ], 101 | 102 | ]; 103 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/PostController.php: -------------------------------------------------------------------------------- 1 | page; 20 | 21 | $posts = Post::with('author')->orderBy('created_at', 'DESC'); 22 | 23 | if (!$page) { 24 | $posts = $posts->get(); 25 | } else { 26 | $posts = $posts->take(10)->offset($page * 10)->get(); 27 | } 28 | 29 | return response()->json([ 30 | 'posts' => $posts 31 | ]); 32 | } 33 | 34 | /** 35 | * Display a post 36 | * 37 | * @param App\Models\Post $post 38 | * @return Illuminate\Http\Response 39 | */ 40 | public function show(Post $post) 41 | { 42 | $post = $post->load('author'); 43 | 44 | return response()->json([ 45 | 'post' => $post 46 | ]); 47 | } 48 | 49 | /** 50 | * Store a newly created resource in storage. 51 | * 52 | * @param \Illuminate\Http\Request $request 53 | * @return \Illuminate\Http\Response 54 | */ 55 | public function store(Request $request) 56 | { 57 | $post = new Post; 58 | $post->title = $request->data['title']; 59 | $post->body = $request->data['body']; 60 | $post->post_type = $request->data['post_type'] ?? 'post'; 61 | $post->user_id = auth()->id(); 62 | $post->save(); 63 | 64 | // Only allow administrators to save tags. 65 | if (auth()->user()->hasRole(config('laraveluk.site.admin_role_name'))) { 66 | // Only Store tags if they have been passed through 67 | if ($request->data['tags']) { 68 | $post->tag($request->data['tags']); 69 | } 70 | } 71 | 72 | if (auth()->user()) { 73 | Log::debug(auth()->user()->name . " created post {$post->id}"); 74 | } 75 | 76 | return response()->json([ 77 | 'post' => $post 78 | ]); 79 | } 80 | 81 | /** 82 | * Update the specified resource in storage. 83 | * 84 | * @param \Illuminate\Http\Request $request 85 | * @param Post $post 86 | * @return \Illuminate\Http\Response 87 | */ 88 | public function update(Request $request, Post $post) 89 | { 90 | $post->update([ 91 | 'title' => $request->title ?? $post->title, 92 | 'body' => $request->body ?? $post->body, 93 | ]); 94 | 95 | if (auth()->user()) { 96 | Log::debug(auth()->user()->name . " updated post {$post->id}"); 97 | } 98 | 99 | return response()->json([ 100 | 'post' => $post 101 | ]); 102 | } 103 | 104 | /** 105 | * Remove the specified resource from storage. 106 | * 107 | * @param Post $post 108 | * @return \Illuminate\Http\Response 109 | * @throws \Exception 110 | */ 111 | public function destroy(Post $post) 112 | { 113 | if ($post->user_id != auth()->id()) { 114 | return response([ 115 | 'status' => 'error' 116 | ]); 117 | } 118 | 119 | if (auth()->user()) { 120 | Log::debug(auth()->user()->name . " deleted post {$post->id}"); 121 | } 122 | 123 | $post->delete(); 124 | 125 | return response([ 126 | 'status' => 'deleted' 127 | ]); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LaravelUK Website Development 2 | 3 | ### Introduction 4 | 5 | Thank you for your interest in contributing to the LaravelUK website project! 6 | 7 | This project is not just a platform for building the LaravelUK website, but also a platform to help other members learn technologies that they are not familiar with; irrespective of their level of experience. 8 | 9 | These guidelines ensure that developers are able to quickly give feedback on your contribution and how to make it better. Most probably you might have to go back and change a few things, but it will be in the interest of making this process better for everyone. Please read and understand the contribution guide before creating an issue or pull request.. 10 | 11 | Happy contributing! 12 | 13 | [![Build Status](https://travis-ci.org/laraveluk/website.svg?branch=master)](https://travis-ci.org/laraveluk/website) 14 | 15 | ### Framework Libraries ### 16 | The following framework and libraries will be used in the project 17 | Laravel 18 | Vue.js 19 | Vuex 20 | VueRouter 21 | Tailwind CSS (You can write vanilla CSS if you want. We will apply these within the build) 22 | 23 | ### Contribution Instructions 24 | If you are new to contibuting to open source projects, please follow the steps below 25 | - Fork the repository 26 | - Clone the forked repository to your local system 27 | - Add a git remote to the original repository. Example `git remote add upstream https://github.com/laraveluk/website.git` 28 | - Check `git remote -v` to ensure you can see both remotes 29 | - Create a feature branch in which to make your changes `git checkout -b ` 30 | - Make changes and commit to the branch 31 | - Push the branch to Github 32 | - Submit Pull request 33 | 34 | ### Etiquette 35 | As this is a community project, selected reviewers will go through your code to ascertain if the quality is sufficient for it to be merged. You are however free to choose any member of the community to review your code however only specifically assigned members can merge your Pull Requests. Please be considerate towards reviewers/maintainers when raising issues or presenting pull requests. Let's show the world that LaravelUK members are civilized and great people to work with. 36 | 37 | ## Submission Requirements 38 | Before submitting a pull request: 39 | - Check the codebase to ensure that your feature doesn't already exist. 40 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 41 | - Send coherent history - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate - commits while developing, please [squash] (http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) them before submitting. 42 | - It is important to add tests to your functionality, even if it is a very simple feature. If it is a UI change, it will be helpful to add a simple screenshot with a brief note so others will know what has changed 43 | - If you want to do more than one thing, send multiple pull requests. 44 | 45 | ### Feedback Policy 46 | 47 | We will strive for a "Zero Pull Request Pending" policy. This means, that if the pull request is good, it will be merged as soon as review is completed and if it does not meet the requirements, the contributor will be advised to make changes and re-submit. 48 | 49 | 50 | ### Coding Standards 51 | As this is a Laravel application, we will be following the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard. 52 | - Interfaces should NOT be suffixed with Interface, Traits should NOT be suffixed with Trait 53 | - Class and method names must be descriptive enough with type-hints since we are not enforcing docblocks 54 | - Ensure that you submit tests that have minimal 100% coverage. -------------------------------------------------------------------------------- /resources/views/auth/passwords/email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('content') 4 | 5 | @include('frontend.partials.header') 6 | 7 |
8 |
9 |
10 | {{ csrf_field() }} 11 |
12 | 13 |
14 |
15 | RESET PASSWORD 16 |
17 |

18 | 19 |
20 | 23 | 24 | 25 | @if ($errors->has('email')) 26 | 27 | {{ $errors->first('email') }} 28 | 29 | @endif 30 |
31 |
32 | 33 |
34 | 37 | 38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | @extends('frontend.master') 56 | 57 | @section('content') 58 |
59 |
60 |
61 |
62 |
Reset Password
63 | 64 |
65 | @if (session('status')) 66 |
67 | {{ session('status') }} 68 |
69 | @endif 70 | 71 |
72 | {{ csrf_field() }} 73 | 74 |
75 | 76 | 77 |
78 | 79 | 80 | @if ($errors->has('email')) 81 | 82 | {{ $errors->first('email') }} 83 | 84 | @endif 85 |
86 |
87 | 88 |
89 |
90 | 93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | @endsection 102 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/blog/components/PostShow.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 87 | -------------------------------------------------------------------------------- /resources/views/frontend/posts/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | @section('content') 4 | @include('frontend.partials.header') 5 | 30 |
31 | @unless($posts->isEmpty()) 32 | @foreach($posts as $post) 33 |
34 |
35 |

36 | @switch($post->post_type) 37 | @case('post') 38 | News 39 | @break 40 | @case('tutorials') 41 | Tutorial 42 | @break 43 | @case('events') 44 | Event 45 | @break 46 | @case('interviews') 47 | Interview 48 | @break 49 | @default 50 | News 51 | @break 52 | @endswitch 53 |

54 |
55 | 57 |

{!! $post->excerpt !!}

58 |
59 |
60 | {{-- Avatar of Jonathan Reinink --}} 61 |
62 |

{{ $post->author->name }}

63 |

{{ $post->created_at->format('jS F Y') }}

64 |
65 |
66 |
67 |
68 | @endforeach 69 | @else 70 |
71 |
72 |

73 | @if (request()->type) 74 | There are no {{request()->type}} yet. Check back soon! 75 | @else 76 | There are no posts yet. 77 | @endif 78 |

79 |
80 |
81 | @endunless 82 |
83 | @endsection 84 | -------------------------------------------------------------------------------- /resources/views/frontend/partials/logo.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/assets/js/backend/app/home/components/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 88 | 89 | 92 | -------------------------------------------------------------------------------- /resources/views/frontend/posts/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('frontend.master') 2 | 3 | 4 | @section('content') 5 | @include('frontend.partials.header') 6 |
7 |
8 |

{{$post->title}}

9 |
10 |
11 |
12 |
13 | {{-- Sunset in the mountains --}} 14 |
15 |
Posted by {{$post->author->name}} - {{ $post->created_at->format('jS F Y') }}
16 |

17 | {!! nl2br($post->body) !!} 18 |

19 | @can('update', $post) 20 | 22 | Edit 23 | 24 | @endcan 25 | @foreach ($post->tags as $tag) 26 | 27 | {{$tag->name}} 28 | 29 | @endforeach 30 |
31 |

32 |
33 |
34 | 35 | @foreach ($post->comments as $comment) 36 |
37 |
38 |
39 |
40 |
41 | Avatar of {{$comment->author->name}} 42 |
43 |
44 |

{{$comment->author->name}}

45 |

{{$comment->created_at->format('jS M Y H:i')}}

46 |
47 |
48 |
49 | {{$comment->body}} 50 |
51 |
52 |
53 |
54 | @endforeach 55 | @if (auth()->check()) 56 |
57 |
58 | {{csrf_field()}} 59 |
60 |
Leave a Comment
61 |
62 | 63 |
64 |
65 | 68 |
69 |
70 |
71 |
72 | @else 73 |
74 |
75 |
Log in or sign up to leave a comment.
76 |
77 |

Sign In With

78 | 79 | 80 | 81 |
82 |
83 |
84 | @endif 85 |
86 | @endsection 87 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_DRIVER', 'smtp'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | SMTP Host Address 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may provide the host address of the SMTP server used by your 27 | | applications. A default option is provided that is compatible with 28 | | the Mailgun mail service which will provide reliable deliveries. 29 | | 30 | */ 31 | 32 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | SMTP Host Port 37 | |-------------------------------------------------------------------------- 38 | | 39 | | This is the SMTP port used by your application to deliver e-mails to 40 | | users of the application. Like the host we have set this value to 41 | | stay compatible with the Mailgun e-mail application by default. 42 | | 43 | */ 44 | 45 | 'port' => env('MAIL_PORT', 587), 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Global "From" Address 50 | |-------------------------------------------------------------------------- 51 | | 52 | | You may wish for all e-mails sent by your application to be sent from 53 | | the same address. Here, you may specify a name and address that is 54 | | used globally for all e-mails that are sent by your application. 55 | | 56 | */ 57 | 58 | 'from' => [ 59 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 60 | 'name' => env('MAIL_FROM_NAME', 'Example'), 61 | ], 62 | 63 | /* 64 | |-------------------------------------------------------------------------- 65 | | E-Mail Encryption Protocol 66 | |-------------------------------------------------------------------------- 67 | | 68 | | Here you may specify the encryption protocol that should be used when 69 | | the application send e-mail messages. A sensible default using the 70 | | transport layer security protocol should provide great security. 71 | | 72 | */ 73 | 74 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | SMTP Server Username 79 | |-------------------------------------------------------------------------- 80 | | 81 | | If your SMTP server requires a username for authentication, you should 82 | | set it here. This will get used to authenticate with your server on 83 | | connection. You may also set the "password" value below this one. 84 | | 85 | */ 86 | 87 | 'username' => env('MAIL_USERNAME'), 88 | 89 | 'password' => env('MAIL_PASSWORD'), 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Sendmail System Path 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When using the "sendmail" driver to send e-mails, we will need to know 97 | | the path to where Sendmail lives on this server. A default path has 98 | | been provided here, which will work well on most of your systems. 99 | | 100 | */ 101 | 102 | 'sendmail' => '/usr/sbin/sendmail -bs', 103 | 104 | /* 105 | |-------------------------------------------------------------------------- 106 | | Markdown Mail Settings 107 | |-------------------------------------------------------------------------- 108 | | 109 | | If you are using Markdown based email rendering, you may configure your 110 | | theme and component paths here, allowing you to customize the design 111 | | of the emails. Or, you may simply stick with the Laravel defaults! 112 | | 113 | */ 114 | 115 | 'markdown' => [ 116 | 'theme' => 'default', 117 | 118 | 'paths' => [ 119 | resource_path('views/vendor/mail'), 120 | ], 121 | ], 122 | 123 | ]; 124 | --------------------------------------------------------------------------------