├── .nvmrc ├── config ├── .gitkeep ├── logging.php ├── social.php ├── auth.php ├── mail.php ├── app.php ├── services.php └── database.php ├── app ├── Handlers │ ├── Events │ │ ├── .gitkeep │ │ └── SlackSubscriber.php │ └── Commands │ │ └── .gitkeep ├── Events │ └── Event.php ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── Api │ │ │ ├── MeController.php │ │ │ ├── BiosController.php │ │ │ ├── TalksController.php │ │ │ ├── UserBiosController.php │ │ │ └── UserTalksController.php │ │ ├── HomeController.php │ │ ├── RejectionController.php │ │ ├── SubmissionReactionsController.php │ │ ├── AcceptancesController.php │ │ ├── DashboardController.php │ │ └── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── ResetPasswordController.php │ │ │ └── LoginController.php │ ├── Requests │ │ ├── Request.php │ │ ├── SpeakerSearchRequest.php │ │ ├── SaveTalkRequest.php │ │ └── Auth │ │ │ ├── LoginFormRequest.php │ │ │ └── RegisterFormRequest.php │ └── Middleware │ │ └── Social.php ├── Exceptions │ ├── InvalidAddressGeocodingException.php │ └── ValidationException.php ├── Models │ ├── TalkReaction.php │ ├── UserSocial.php │ ├── TightenSlack.php │ ├── Bio.php │ └── UuidBase.php ├── helpers.php ├── Collections │ └── TalksCollection.php ├── Policies │ └── ConferencePolicy.php ├── Filament │ └── Resources │ │ ├── ConferenceResource │ │ └── Pages │ │ │ ├── ListConferences.php │ │ │ └── EditConference.php │ │ └── ConferenceIssueResource │ │ └── Pages │ │ ├── ListConferenceIssues.php │ │ └── ViewConferenceIssue.php ├── Services │ ├── FakeCaptcha.php │ ├── Currency.php │ ├── Geocoder │ │ └── Geocoder.php │ └── CreateBioForm.php ├── Casts │ ├── Coordinates.php │ └── Url.php ├── Providers │ ├── CaptchaServiceProvider.php │ ├── BusServiceProvider.php │ ├── ConfigServiceProvider.php │ └── EventServiceProvider.php ├── CallingAllPapers │ └── Client.php ├── macros.php ├── Mail │ └── ContactRequest.php ├── Console │ └── Commands │ │ ├── VerifyConferenceImporterHeartbeat.php │ │ └── SendNotificationForOpenCFPs.php ├── Notifications │ ├── ConferenceImporterInactive.php │ ├── NewConference.php │ ├── ConferenceImporterError.php │ ├── NewUser.php │ ├── ConferenceIssueReported.php │ └── CFPsAreOpen.php ├── ApiResources │ ├── Me.php │ ├── Bio.php │ └── Conference.php └── Transformers │ └── TalkForConferenceTransformer.php ├── database ├── migrations │ ├── .gitkeep │ ├── 2024_07_02_000000_rename_password_resets_table.php │ ├── 2019_04_19_142914_create_password_resets_table.php │ ├── 2019_04_19_142914_create_oauth_personal_access_clients_table.php │ ├── 2022_08_25_164159_add_soft_deletes_to_conferences_table.php │ ├── 2021_11_10_115054_add_reason_to_rejections_table.php │ ├── 2019_04_19_142914_create_favorites_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_bios_table.php │ ├── 2021_11_10_115106_add_reason_to_acceptances_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_talks_table.php │ ├── 2020_01_09_104729_add_featured_column_to_users_table.php │ ├── 2021_03_29_153621_add_provider_to_oauth_clients_table.php │ ├── 2020_01_09_122305_add_featured_column_to_conferences_table.php │ ├── 2022_07_21_193843_add_speaker_packages_column_to_conferences_table.php │ ├── 2024_12_20_115249_add_location_name_to_conferences_table.php │ ├── 2022_11_11_132039_add_rejected_at_column_to_conferences_table.php │ ├── 2019_04_19_142914_create_oauth_refresh_tokens_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_conferences_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_users_social_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_talk_revisions_table.php │ ├── 2021_10_13_143629_add_rejection_id_to_submissions_table.php │ ├── 2019_04_19_142914_create_users_social_table.php │ ├── 2021_11_22_161401_add_has_cfp_column_to_conferences_table.php │ ├── 2022_05_25_101856_create_talk_reactions_table.php │ ├── 2019_04_19_142914_create_dismissed_conferences_table.php │ ├── 2019_08_20_094313_extend_cap_id_field_by_four_characters_for_the_year.php │ ├── 2019_04_19_142914_create_talks_table.php │ ├── 2019_04_19_142914_create_bios_table.php │ ├── 2019_04_19_142914_create_failed_jobs_table.php │ ├── 2019_04_19_142914_create_oauth_auth_codes_table.php │ ├── 2019_04_19_142914_create_acceptances_table.php │ ├── 2019_10_25_152352_add_conference_location.php │ ├── 2019_04_19_142915_add_foreign_keys_to_favorites_table.php │ ├── 2019_04_19_142914_create_oauth_access_tokens_table.php │ ├── 2019_04_19_142914_create_oauth_clients_table.php │ ├── 2019_04_19_142914_create_submissions_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_acceptances_table.php │ ├── 2019_04_19_142915_add_foreign_keys_to_dismissed_conferences_table.php │ ├── 2022_08_23_145646_create_conference_issues_table.php │ ├── 2020_10_30_131213_rename_featured_columns_to_is_featured.php │ ├── 2021_10_28_000000_add_uuid_to_failed_jobs_table.php │ ├── 2019_04_19_142914_create_talk_revisions_table.php │ ├── 2023_03_31_000348_add_admin_note_column_to_conference_issues_table.php │ ├── 2019_05_09_222406_drop_joindin_add_cap.php │ ├── 2019_04_19_142915_add_foreign_keys_to_submissions_table.php │ ├── 2021_10_13_142431_create_rejections_table.php │ └── 2019_04_19_142914_create_conferences_table.php ├── seeders │ ├── .gitkeep │ ├── UsersSeeder.php │ ├── AcceptanceSeeder.php │ ├── SubmissionsSeeder.php │ ├── RejectionSeeder.php │ ├── ConferencesSeeder.php │ ├── DatabaseSeeder.php │ └── BiosSeeder.php ├── .gitignore └── factories │ ├── UserSocialFactory.php │ ├── RejectionFactory.php │ ├── AcceptanceFactory.php │ ├── BioFactory.php │ ├── TalkRevisionFactory.php │ └── ConferenceIssueFactory.php ├── .gitattributes ├── bootstrap ├── cache │ └── .gitignore └── providers.php ├── storage ├── logs │ └── .gitignore ├── app │ ├── .gitignore │ └── private │ │ └── .gitignore ├── .gitignore └── framework │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── views │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── public ├── robots.txt ├── favicon.ico ├── img │ ├── missing.png │ ├── placekitten.jpg │ ├── pronto-logo.png │ ├── joindin-button.png │ ├── symposium-logo.png │ ├── symposium-banner.png │ ├── symposium-screenshot.png │ └── profile_pictures │ │ ├── missing.png │ │ └── hires │ │ └── missing.png ├── mix-manifest.json ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ └── vendor │ │ └── bootstrap-sass │ │ └── bootstrap │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff ├── index.php └── .htaccess ├── symposium-banner.png ├── tests ├── stubs │ └── test.jpg ├── Api │ ├── JsonApiTest.php │ ├── ApiTestCase.php │ └── MeApiTest.php ├── Unit │ ├── SpeakerPackageTest.php │ ├── UserTest.php │ ├── TalkRevisionTest.php │ ├── ContactRequestTest.php │ └── Notifications │ │ ├── ConferenceImporterErrorTest.php │ │ ├── ConferenceImporterInactiveTest.php │ │ ├── CFPsAreOpenTest.php │ │ ├── NewConferenceTest.php │ │ └── ConferenceIssueReportedTest.php ├── TestCase.php └── Feature │ ├── VerifyConferenceImporterHeartbeatTest.php │ ├── HomeTest.php │ ├── ConferenceIssuesTest.php │ └── SyncCallingAllPapersEventsTest.php ├── resources ├── views │ ├── bios │ │ ├── listing-empty.blade.php │ │ ├── index.blade.php │ │ ├── show-public.blade.php │ │ ├── form.blade.php │ │ ├── listing.blade.php │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ └── show.blade.php │ ├── talks │ │ ├── listing-empty.blade.php │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ ├── show-public.blade.php │ │ └── listing.blade.php │ ├── auth │ │ ├── emails │ │ │ └── password.blade.php │ │ ├── login.blade.php │ │ ├── passwords │ │ │ ├── email.blade.php │ │ │ └── reset.blade.php │ │ └── no-signups.blade.php │ ├── components │ │ ├── notice │ │ │ ├── info.blade.php │ │ │ ├── error.blade.php │ │ │ ├── success.blade.php │ │ │ └── index.blade.php │ │ ├── alert │ │ │ ├── warning.blade.php │ │ │ └── index.blade.php │ │ ├── tag │ │ │ ├── success.blade.php │ │ │ └── index.blade.php │ │ ├── heading │ │ │ └── list-item.blade.php │ │ ├── input │ │ │ ├── upload.blade.php │ │ │ ├── date.blade.php │ │ │ ├── group.blade.php │ │ │ ├── textarea.blade.php │ │ │ ├── radios.blade.php │ │ │ ├── toggle.blade.php │ │ │ └── text.blade.php │ │ ├── menu │ │ │ ├── item.blade.php │ │ │ └── index.blade.php │ │ ├── nav-item.blade.php │ │ ├── button │ │ │ ├── icon.blade.php │ │ │ ├── secondary.blade.php │ │ │ ├── primary.blade.php │ │ │ ├── mobile-nav.blade.php │ │ │ └── base.blade.php │ │ ├── form.blade.php │ │ ├── info.blade.php │ │ ├── mobile-nav-item.blade.php │ │ ├── modal │ │ │ └── add-talk-reaction.blade.php │ │ ├── listing.blade.php │ │ ├── panel │ │ │ └── index.blade.php │ │ └── side-menu.blade.php │ ├── conferences │ │ ├── listing-empty.blade.php │ │ ├── showPublic.blade.php │ │ ├── create.blade.php │ │ ├── show.blade.php │ │ ├── issues │ │ │ └── create.blade.php │ │ └── edit.blade.php │ ├── partials │ │ ├── authentication-errors.blade.php │ │ ├── flash-messages.blade.php │ │ └── footer.blade.php │ ├── emails │ │ ├── open-cfps.blade.php │ │ ├── newsignup.blade.php │ │ └── public-profile-contact.blade.php │ ├── account │ │ ├── oauth-settings.blade.php │ │ └── confirm-delete.blade.php │ └── layouts │ │ └── index.blade.php ├── svg │ ├── arrow-thick-left.svg │ ├── folder.svg │ ├── trash.svg │ ├── plus.svg │ ├── compose.svg │ ├── folder-outline.svg │ ├── star-full.svg │ ├── close.svg │ ├── flag.svg │ ├── clipboard.svg │ ├── link.svg │ ├── star-empty.svg │ ├── logo.svg │ ├── dropdown.svg │ ├── map-pin.svg │ ├── calendar.svg │ ├── chevron-left.svg │ ├── chevron-right.svg │ ├── rectangle.svg │ ├── check-circle.svg │ ├── user-group.svg │ ├── github.svg │ ├── circle.svg │ ├── dots.svg │ ├── icon_find.svg │ ├── icon_versioncontrol.svg │ ├── icon_trackconferences.svg │ └── icon_tracktalks.svg ├── css │ ├── calendar.css │ ├── app.css │ └── mobile-menu-button.css └── js │ ├── components │ ├── ModalToggle.vue │ ├── CfpFields.vue │ ├── CurrencySelection.vue │ ├── LoadingSpinner.vue │ ├── MenuToggle.vue │ └── UpdateQueryString.vue │ └── directives │ └── Dismiss.js ├── .github ├── screenshots │ ├── talks-page.png │ ├── conference-list.png │ ├── symposium-calendar.png │ └── symposium-dashboard.png └── workflows │ └── lint.yml ├── phpspec.yml ├── postcss.config.cjs ├── .editorconfig ├── lang └── en │ └── conference.php ├── artisan ├── .gitignore ├── routes ├── console.php └── api.php ├── vite.config.js ├── google-guide.md ├── LICENSE.md └── .env.example /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.20.3 2 | -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Handlers/Events/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/seeders/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /app/Handlers/Commands/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !private/ 3 | !.gitignore -------------------------------------------------------------------------------- /storage/app/private/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/.gitignore: -------------------------------------------------------------------------------- 1 | laravel.log 2 | database.sqlite 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /app/Events/Event.php: -------------------------------------------------------------------------------- 1 | 2 | No bios yet. 3 | 4 | -------------------------------------------------------------------------------- /resources/views/talks/listing-empty.blade.php: -------------------------------------------------------------------------------- 1 | 2 | No talks yet. 3 | 4 | -------------------------------------------------------------------------------- /public/img/joindin-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/img/joindin-button.png -------------------------------------------------------------------------------- /public/img/symposium-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/img/symposium-logo.png -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /public/img/symposium-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/img/symposium-banner.png -------------------------------------------------------------------------------- /.github/screenshots/talks-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/.github/screenshots/talks-page.png -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | [ 6 | // 7 | ], 8 | 9 | ]; 10 | -------------------------------------------------------------------------------- /resources/views/auth/emails/password.blade.php: -------------------------------------------------------------------------------- 1 | Click here to reset your password: {{ url('password/reset/' . $token) }} 2 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /.github/screenshots/conference-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/.github/screenshots/conference-list.png -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | suites: 2 | main: 3 | namespace: Symposium 4 | psr4_prefix: Symposium 5 | src_path: app 6 | -------------------------------------------------------------------------------- /public/img/profile_pictures/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/img/profile_pictures/missing.png -------------------------------------------------------------------------------- /resources/views/components/alert/warning.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/notice/error.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/notice/success.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/tag/success.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/conferences/listing-empty.blade.php: -------------------------------------------------------------------------------- 1 | 2 | No conferences match this filter 3 | 4 | -------------------------------------------------------------------------------- /.github/screenshots/symposium-calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/.github/screenshots/symposium-calendar.png -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.github/screenshots/symposium-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/.github/screenshots/symposium-dashboard.png -------------------------------------------------------------------------------- /resources/svg/arrow-thick-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/img/profile_pictures/hires/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/img/profile_pictures/hires/missing.png -------------------------------------------------------------------------------- /resources/views/components/alert/index.blade.php: -------------------------------------------------------------------------------- 1 |
class([ 2 | 'mb-5 p-4 rounded' 3 | ]) }}> 4 | {{ $slot }} 5 |
6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /resources/svg/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/Http/Requests/Request.php: -------------------------------------------------------------------------------- 1 | [ 6 | 7 | 'github' => [ 8 | 'name' => 'GitHub', 9 | ], 10 | 11 | ], 12 | 13 | ]; 14 | -------------------------------------------------------------------------------- /resources/svg/compose.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/folder-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /resources/svg/star-full.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventyio/symposium/HEAD/public/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Coding Standards 2 | 3 | on: [push] 4 | 5 | permissions: 6 | contents: write 7 | 8 | jobs: 9 | lint: 10 | uses: laravel/.github/.github/workflows/coding-standards.yml@main 11 | -------------------------------------------------------------------------------- /resources/views/components/heading/list-item.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'href', 3 | ]) 4 | 5 |

6 | 7 | {{ $slot }} 8 | 9 |

10 | -------------------------------------------------------------------------------- /resources/views/partials/authentication-errors.blade.php: -------------------------------------------------------------------------------- 1 | @if ($errors->count() > 0) 2 |
3 | @foreach ($errors->all() as $error) 4 | {{ $error }}
5 | @endforeach 6 |
7 | @endif 8 | -------------------------------------------------------------------------------- /resources/svg/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'api' => [ 7 | 'driver' => 'passport', 8 | 'provider' => 'users', 9 | 'hash' => false, 10 | ], 11 | ], 12 | 13 | ]; 14 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/components/tag/index.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 'font-medium leading-4 rounded-lg text-xs whitespace-nowrap', 4 | 'style' => 'padding: 2px 10px;' 5 | ]) }} 6 | > 7 | {{ $slot }} 8 | 9 | -------------------------------------------------------------------------------- /resources/views/emails/open-cfps.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | The following CFPs are now open: 3 | 4 | @foreach ($conferences as $conference) 5 | - [{{ $conference->title }}]({{ $conference->cfp_url ?: $conference->url }}) 6 | @endforeach 7 | @endcomponent 8 | -------------------------------------------------------------------------------- /resources/views/emails/newsignup.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

New User Signup

8 | 9 |
10 | New user signed up: {{ $email }} 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /app/Models/TalkReaction.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | @isset($help) 13 | {{ $help }} 14 | @endisset 15 | -------------------------------------------------------------------------------- /lang/en/conference.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'duplicate' => 'This is a duplicate conference', 6 | 'incorrect' => 'This conference has missing or incorrect information', 7 | 'spam' => 'This is a spam listing', 8 | 'other' => 'Other', 9 | ], 10 | ]; 11 | -------------------------------------------------------------------------------- /database/factories/UserSocialFactory.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/star-empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/account/oauth-settings.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'OAuth settings']) 2 | 3 | @section('content') 4 | 5 | 6 | 7 | 8 | 9 | @endsection 10 | -------------------------------------------------------------------------------- /app/Models/UserSocial.php: -------------------------------------------------------------------------------- 1 | null, 3 | 'route' => null, 4 | 'show' => true, 5 | ]) 6 | 7 | @if ($show) 8 | 12 | {{ $slot }} 13 | 14 | @endif 15 | -------------------------------------------------------------------------------- /resources/views/components/nav-item.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'route', 3 | ]) 4 | 5 | 14 | {{ $slot }} 15 | 16 | -------------------------------------------------------------------------------- /resources/svg/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/button/icon.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'href' => null, 3 | 'icon' => null, 4 | ]) 5 | 6 | @php 7 | $element = isset($href) ? 'a' : 'button'; 8 | @endphp 9 | 10 | <{{ $element }} 11 | @isset($href) href="{{ $href }}" @endisset 12 | {{ $attributes->class('leading-none') }} 13 | > 14 | @svg($icon, 'fill-current inline w-5') 15 | 16 | -------------------------------------------------------------------------------- /resources/views/components/button/secondary.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'href' => null, 3 | 'icon' => null, 4 | 'size' => 'sm', 5 | ]) 6 | 7 | 15 | {{ $slot }} 16 | 17 | -------------------------------------------------------------------------------- /resources/views/conferences/showPublic.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | @if ($conference->isFlagged()) 6 | An issue has been reported for this conference. 7 | @endif 8 | 9 | 12 | 13 |
14 |
15 | 16 | @endsection 17 | -------------------------------------------------------------------------------- /app/helpers.php: -------------------------------------------------------------------------------- 1 | '.(new Parsedown)->text($text).''; 7 | } 8 | } 9 | 10 | if (! function_exists('menuRoute')) { 11 | function menuRoute($name, $parameters = [], $absolute = true) 12 | { 13 | return app('url')->menuRoute($name, $parameters, $absolute); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/views/bios/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.index', ['title' => 'All Bios']) 2 | 3 | @section('actions') 4 | 9 | Add Bio 10 | 11 | @endsection 12 | 13 | @section('list') 14 | @each('bios.listing', $bios, 'bio', 'bios.listing-empty') 15 | @endsection 16 | -------------------------------------------------------------------------------- /app/Collections/TalksCollection.php: -------------------------------------------------------------------------------- 1 | sortBy(function (Talk $talk) { 13 | return strtolower($talk->currentRevision->title); 14 | })->values(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/svg/dropdown.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/svg/map-pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/button/primary.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'href' => null, 3 | 'icon' => null, 4 | 'size' => 'sm', 5 | ]) 6 | 7 | 15 | {{ $slot }} 16 | 17 | -------------------------------------------------------------------------------- /resources/views/components/notice/index.blade.php: -------------------------------------------------------------------------------- 1 |
class([ 3 | 'cursor-pointer fixed inset-x-0 lg:inset-x-auto mx-auto p-4 lg:right-4 rounded text-center top-32 w-4/5 lg:w-1/5 z-50' 4 | ]) }} 5 | v-dismiss="2000" 6 | > 7 | 10 | {{ $slot }} 11 |
12 | -------------------------------------------------------------------------------- /resources/views/components/form.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'action', 3 | 'upload' => false, 4 | ]) 5 | 6 |
except('method') }} 10 | @if ($upload) enctype="multipart/form-data" @endif 11 | > 12 | @unless ($attributes->get('method', 'POST') == 'POST') 13 | @method($attributes->get('method')) 14 | @endunless 15 | @csrf 16 | {{ $slot }} 17 |
18 | -------------------------------------------------------------------------------- /resources/views/components/info.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'icon' => null, 3 | 'iconColor' => 'text-gray-500', 4 | 'textColor' => 'text-gray-500', 5 | ]) 6 | 7 | merge([ 8 | 'class' => "{$iconColor} text-sm flex items-center" 9 | ]) }}> 10 | @if ($icon) 11 | @svg($icon, 'inline fill-current w-4 mr-1') 12 | @endif 13 | 14 | {{ $slot }} 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/Exceptions/ValidationException.php: -------------------------------------------------------------------------------- 1 | errors = $errors; 16 | } 17 | 18 | public function errors() 19 | { 20 | return $this->errors; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Models/TightenSlack.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'mailgun' => [ 7 | 'transport' => 'mailgun', 8 | // 'client' => [ 9 | // 'timeout' => 5, 10 | // ], 11 | ], 12 | 13 | 'roundrobin' => [ 14 | 'transport' => 'roundrobin', 15 | 'mailers' => [ 16 | 'ses', 17 | 'postmark', 18 | ], 19 | ], 20 | ], 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /database/factories/RejectionFactory.php: -------------------------------------------------------------------------------- 1 | TalkRevision::factory(), 15 | 'conference_id' => Conference::factory(), 16 | ]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/svg/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/MeController.php: -------------------------------------------------------------------------------- 1 | guard('api')->user()); 14 | 15 | return response()->jsonApi([ 16 | 'data' => $me->toArray(), 17 | ]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | User::whereFeatured()->limit(6)->get(), 15 | 'conferences' => Conference::whereFeatured()->limit(3)->get(), 16 | ]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /database/factories/AcceptanceFactory.php: -------------------------------------------------------------------------------- 1 | TalkRevision::factory(), 15 | 'conference_id' => Conference::factory(), 16 | ]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /database/migrations/2024_07_02_000000_rename_password_resets_table.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/Policies/ConferencePolicy.php: -------------------------------------------------------------------------------- 1 | isAdmin()) { 16 | return true; 17 | } 18 | 19 | return $conference->author_id == $user->id; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 17 | 18 | exit($status); 19 | -------------------------------------------------------------------------------- /resources/svg/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/svg/rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/views/components/button/mobile-nav.blade.php: -------------------------------------------------------------------------------- 1 |
5 | 15 |
16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /vendor 3 | .env 4 | .sass-cache/ 5 | /storage/*.key 6 | /storage/pail 7 | /public/css/app.css 8 | /public/css/app.css.map 9 | /public/css/filament 10 | /public/js/app.js 11 | /public/js/app.js.map 12 | /public/js/filament 13 | /public/js/location.js 14 | /public/js/location.js.map 15 | /public/storage 16 | /storage/debugbar 17 | /storage/framework/testing 18 | /.phpunit.cache 19 | /.phpunit.result.cache 20 | .idea/* 21 | phpunit.xml 22 | /public/build 23 | /public/hot 24 | /.nova 25 | -------------------------------------------------------------------------------- /resources/views/emails/public-profile-contact.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Contact from your public profile page on Symposium

8 | 9 |
10 | Email: {{ $email }}
11 | Name: {{ $name }}
12 | Message:
13 |
14 | {!! str_replace("\n", "
", htmlentities($userMessage)) !!} 15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/Api/JsonApiTest.php: -------------------------------------------------------------------------------- 1 | call('GET', '/api/user/1/talks'); 13 | 14 | $this->assertEquals(200, $response->getStatusCode()); 15 | $this->assertEquals('application/vnd.api+json', $response->headers->get('Content-Type')); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Filament/Resources/ConferenceResource/Pages/ListConferences.php: -------------------------------------------------------------------------------- 1 | 3 | {{ Session::get('success-message', 'success') }} 4 | 5 | @endif 6 | 7 | @if (Session::has('error-message')) 8 | 9 | {{ Session::get('error-message') }} 10 | 11 | @endif 12 | 13 | @if (Session::has('message')) 14 | 15 | {{ Session::get('message') }} 16 | 17 | @endif 18 | -------------------------------------------------------------------------------- /resources/css/calendar.css: -------------------------------------------------------------------------------- 1 | #calendar button { 2 | @apply bg-indigo-500 text-white h-auto shadow-none; 3 | } 4 | 5 | #calendar .fc-button-group { 6 | @apply rounded overflow-hidden; 7 | } 8 | 9 | #calendar .fc-button-group button { 10 | @apply bg-indigo rounded-none; 11 | } 12 | 13 | #calendar .fc-button-group button.fc-button-active { 14 | @apply bg-indigo-900; 15 | } 16 | 17 | #calendar .fc-event-title { 18 | @apply p-1; 19 | } 20 | 21 | #calendar .fc-popover-close:after { 22 | @apply hidden; 23 | } 24 | -------------------------------------------------------------------------------- /resources/views/bios/show-public.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 | 11 | Return to profile for {{ $user->name }} 12 | 13 | 14 |

{{ $bio->nickname }}

15 | 16 | {!! str_replace("\n", "
", $bio->body) !!} 17 |
18 | 19 | @endsection 20 | -------------------------------------------------------------------------------- /app/Services/FakeCaptcha.php: -------------------------------------------------------------------------------- 1 | valid; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /resources/js/components/ModalToggle.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /resources/views/bios/form.blade.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /app/Casts/Coordinates.php: -------------------------------------------------------------------------------- 1 | latitude = $latitude; 14 | $this->longitude = $longitude; 15 | } 16 | 17 | public function getLatitude() 18 | { 19 | return $this->latitude; 20 | } 21 | 22 | public function getLongitude() 23 | { 24 | return $this->longitude; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /resources/views/bios/listing.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @svg('compose', 'w-5 fill-current inline') 5 | 6 | 7 | @svg('trash', 'w-5 fill-current inline') 8 | 9 | 10 | {{ $bio->nickname }} 11 | 12 | -------------------------------------------------------------------------------- /app/Filament/Resources/ConferenceIssueResource/Pages/ListConferenceIssues.php: -------------------------------------------------------------------------------- 1 | create([ 15 | 'name' => 'Matt Stauffer', 16 | 'email' => 'matt@savemyproposals.com', 17 | ]); 18 | 19 | User::factory()->count(10)->create([ 20 | 'is_featured' => rand(0, 1), 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /resources/views/conferences/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Create Conference']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | @include('conferences.form') 13 | 14 | 19 | Create 20 | 21 | 22 | 23 | @endsection 24 | -------------------------------------------------------------------------------- /app/Http/Controllers/RejectionController.php: -------------------------------------------------------------------------------- 1 | user()->id != $rejection->submission->talkRevision->talk->author_id) { 12 | return response('', 401); 13 | } 14 | 15 | $rejection->delete(); 16 | 17 | return response()->json(['status' => 'success', 'message' => 'Talk un-marked as rejected.']); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/svg/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/Http/Controllers/SubmissionReactionsController.php: -------------------------------------------------------------------------------- 1 | validate([ 13 | 'url' => 'required|url', 14 | ]); 15 | 16 | $submission->addReaction(request('url')); 17 | 18 | return redirect()->route('submission.edit', $submission); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/Social.php: -------------------------------------------------------------------------------- 1 | service), array_keys(config('social.services')))) { 17 | return redirect()->back(); 18 | } 19 | 20 | return $next($request); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Controllers/AcceptancesController.php: -------------------------------------------------------------------------------- 1 | user()->id != $acceptance->submission->talkRevision->talk->author_id) { 12 | return response('', 401); 13 | } 14 | 15 | $acceptance->delete(); 16 | 17 | return response()->json(['status' => 'success', 'message' => 'Talk un-marked as accepted.']); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/views/bios/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Add Bio']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | 13 | @include('bios.form') 14 | 15 | 16 | 21 | Create 22 | 23 | 24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /resources/views/talks/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Add Talk']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | 13 | @include('talks.form') 14 | 15 | 16 | 21 | Create 22 | 23 | 24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import "./calendar"; 2 | @import "./mobile-menu-button"; 3 | 4 | @tailwind base; 5 | @tailwind components; 6 | @tailwind utilities; 7 | 8 | [v-cloak] { 9 | display: none; 10 | } 11 | 12 | .caret { 13 | border-top: 4px solid; 14 | border-right: 4px solid transparent; 15 | border-left: 4px solid transparent; 16 | } 17 | 18 | @keyframes spin { 19 | from { transform: scale(1) rotate(0deg);} 20 | to { transform: scale(1) rotate(360deg);} 21 | } 22 | 23 | .toggle-checkbox:checked + .toggle-label { 24 | @apply text-indigo-900 bg-indigo-900; 25 | } 26 | -------------------------------------------------------------------------------- /resources/svg/user-group.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/Api/ApiTestCase.php: -------------------------------------------------------------------------------- 1 | user = User::first(); 24 | Passport::actingAs($this->user); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Providers/CaptchaServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->bind(Captcha::class, function () { 13 | $captcha = new Captcha; 14 | $captcha->setPublicKey(config('services.google.captcha.public')); 15 | $captcha->setPrivateKey(config('services.google.captcha.private')); 16 | 17 | return $captcha; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /resources/views/bios/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Edit Bio']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | 13 | @include('bios.form') 14 | 15 | 16 | 21 | Update 22 | 23 | 24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /app/Providers/BusServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapUsing(function ($command) { 13 | return Dispatcher::simpleMapping( 14 | $command, 15 | 'App\Commands', 16 | 'App\Handlers\Commands' 17 | ); 18 | }); 19 | } 20 | 21 | public function register(): void {} 22 | } 23 | -------------------------------------------------------------------------------- /resources/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 |

Log in

7 | 17 | @include('partials.log-in-form') 18 |
19 | 20 | @endsection 21 | -------------------------------------------------------------------------------- /resources/views/talks/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Edit Talk']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | 13 | @include('talks.form') 14 | 15 | 16 | 21 | Update 22 | 23 | 24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /app/Casts/Url.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 9 | })->purpose('Display an inspiring quote')->hourly(); 10 | 11 | Schedule::command('callingallpapers:sync') 12 | ->hourly(); 13 | 14 | Schedule::command('symposium:notifyCfps') 15 | ->dailyAt('08:00') 16 | ->environments('production'); 17 | 18 | Schedule::command('verify-conference-importer-heartbeat') 19 | ->dailyAt('10:00') 20 | ->environments('production'); 21 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 21 | -------------------------------------------------------------------------------- /app/CallingAllPapers/Client.php: -------------------------------------------------------------------------------- 1 | get())->cfps)->map(function ($cfpFromApi) { 12 | return Event::createFromApiObject($cfpFromApi); 13 | }); 14 | } 15 | 16 | private function get() 17 | { 18 | return Http::withHeaders(['User-Agent' => 'Symposium CLI']) 19 | ->get('https://api.callingallpapers.com/v1/cfp') 20 | ->getBody() 21 | ->getContents(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 12 | $table->string('token')->index(); 13 | $table->dateTime('created_at')->nullable(); 14 | }); 15 | } 16 | 17 | public function down(): void 18 | { 19 | Schema::drop('password_resets'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /app/Providers/ConfigServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'usd', 16 | 'food' => 1000, 17 | 'travel' => 0, 18 | ]); 19 | 20 | $this->assertEquals('10.00', $speakerPackage->toDecimal('food')); 21 | $this->assertEquals(null, $speakerPackage->toDecimal('travel')); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Services/Currency.php: -------------------------------------------------------------------------------- 1 | filter(function ($item) { 14 | return Money::isValidCurrency($item); 15 | }) 16 | ->map(function ($code) { 17 | return [ 18 | 'code' => $code, 19 | 'symbol' => Currencies::getSymbol($code), 20 | ]; 21 | }) 22 | ->toArray(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_oauth_personal_access_clients_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 12 | $table->integer('client_id')->index(); 13 | $table->timestamps(); 14 | }); 15 | } 16 | 17 | public function down(): void 18 | { 19 | Schema::drop('oauth_personal_access_clients'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /resources/views/partials/footer.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | $styles = $is_home ? 'bg-black text-gray-500' : 'text-gray-600 bg-white border-t border-gray-300'; 3 | @endphp 4 | 5 |
6 |
7 | 14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /app/Models/Bio.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 24 | } 25 | 26 | public function scopePublic($query) 27 | { 28 | return $query->where('public', true); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Models/UuidBase.php: -------------------------------------------------------------------------------- 1 | {$model->getKeyName()} = Uuid::uuid4()->toString(); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /resources/views/components/mobile-nav-item.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'url', 3 | 'route', 4 | 'show' => true, 5 | ]) 6 | 7 | @if ($show) 8 | @php 9 | $isActive = isset($url) 10 | ? request()->is(ltrim(parse_url($url, PHP_URL_PATH), '/')) 11 | : request()->isContainedBy($route); 12 | @endphp 13 | 22 | {{ $slot }} 23 | 24 | @endif 25 | -------------------------------------------------------------------------------- /database/migrations/2022_08_25_164159_add_soft_deletes_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | softDeletes()->after('updated_at'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->dropSoftDeletes(); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /resources/views/components/modal/add-talk-reaction.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'submission', 3 | ]) 4 | 5 | 6 | 7 | 12 | Add Talk Reaction 13 | 14 | 15 | 16 | 17 | 18 | Save 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/Unit/UserTest.php: -------------------------------------------------------------------------------- 1 | admin()->create(); 19 | $user = User::factory()->nonAdmin()->create(); 20 | 21 | $this->assertTrue($admin->canAccessPanel(new Panel)); 22 | $this->assertFalse($user->canAccessPanel(new Panel)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/migrations/2021_11_10_115054_add_reason_to_rejections_table.php: -------------------------------------------------------------------------------- 1 | text('reason')->after('conference_id')->nullable(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('rejections', function (Blueprint $table) { 19 | $table->dropColumn('reason'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_favorites_table.php: -------------------------------------------------------------------------------- 1 | integer('user_id')->unsigned()->index('favorites_user_id_foreign'); 12 | $table->string('conference_id', 36)->index('favorites_conference_id_foreign'); 13 | $table->timestamps(); 14 | }); 15 | } 16 | 17 | public function down(): void 18 | { 19 | Schema::drop('favorites'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_bios_table.php: -------------------------------------------------------------------------------- 1 | foreign('user_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('RESTRICT'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('bios', function (Blueprint $table) { 18 | $table->dropForeign('bios_user_id_foreign'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/migrations/2021_11_10_115106_add_reason_to_acceptances_table.php: -------------------------------------------------------------------------------- 1 | text('reason')->after('conference_id')->nullable(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('acceptances', function (Blueprint $table) { 19 | $table->dropColumn('reason'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_talks_table.php: -------------------------------------------------------------------------------- 1 | foreign('author_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('RESTRICT'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('talks', function (Blueprint $table) { 18 | $table->dropForeign('talks_author_id_foreign'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/migrations/2020_01_09_104729_add_featured_column_to_users_table.php: -------------------------------------------------------------------------------- 1 | boolean('featured')->after('allow_profile_contact')->default(0); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('users', function (Blueprint $table) { 19 | $table->dropColumn('featured'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2021_03_29_153621_add_provider_to_oauth_clients_table.php: -------------------------------------------------------------------------------- 1 | string('provider')->after('secret')->nullable(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('oauth_clients', function (Blueprint $table) { 19 | $table->dropColumn('provider'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /resources/js/directives/Dismiss.js: -------------------------------------------------------------------------------- 1 | function hideElement(el) { 2 | el.style.visibility = 'hidden'; 3 | } 4 | 5 | function fadeElement(el, duration) { 6 | el.animate([{ opacity: 1 }, { opacity: 0 }], duration) 7 | .onfinish = () => hideElement(el); 8 | } 9 | 10 | function hideElementOnClick(el) { 11 | const listener = function clickHandler() { 12 | hideElement(el); 13 | el.removeEventListener('click', clickHandler); 14 | }; 15 | 16 | el.addEventListener('click', listener); 17 | } 18 | 19 | export default { 20 | bind(el, binding) { 21 | setTimeout(() => fadeElement(el, 1000), binding.value); 22 | hideElementOnClick(el); 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import vue from '@vitejs/plugin-vue2'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: ['resources/css/app.css', 'resources/js/app.js'], 9 | refresh: true, 10 | }), 11 | vue({ 12 | template: { 13 | transformAssetUrls: { 14 | base: null, 15 | includeAbsolute: false, 16 | }, 17 | }, 18 | }), 19 | ], 20 | resolve: { 21 | alias: { 22 | vue: 'vue/dist/vue.js', 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /database/migrations/2020_01_09_122305_add_featured_column_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | boolean('featured')->after('author_id')->default(0); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->dropColumn('featured'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2022_07_21_193843_add_speaker_packages_column_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | json('speaker_package')->nullable(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->dropColumn('speaker_package'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2024_12_20_115249_add_location_name_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | string('location_name')->nullable()->after('location'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->dropColumn('location_name'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Reset Password']) 2 | 3 | @section('content') 4 | 5 | @include('partials.authentication-errors') 6 | 7 |
8 | {!! csrf_field() !!} 9 | 10 | 18 | 19 | 24 | Send Password Reset Link 25 | 26 |
27 | 28 | @endsection 29 | -------------------------------------------------------------------------------- /database/migrations/2022_11_11_132039_add_rejected_at_column_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | dateTime('rejected_at')->after('cfp_ends_at')->nullable(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->dropColumn('rejected_at'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /resources/views/components/input/date.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'label', 4 | 'value' => null, 5 | ]) 6 | 7 | only('v-model') }} 12 | > 13 | 24 | 25 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/BiosController.php: -------------------------------------------------------------------------------- 1 | guard('api')->user()->bios()->findOrFail($id); 17 | } catch (Exception $e) { 18 | App::abort(404); 19 | } 20 | 21 | $bio = new Bio($bio); 22 | 23 | return response()->jsonApi([ 24 | 'data' => $bio->toArray(), 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_oauth_refresh_tokens_table.php: -------------------------------------------------------------------------------- 1 | string('id', 100)->primary(); 12 | $table->string('access_token_id', 100)->index(); 13 | $table->boolean('revoked'); 14 | $table->dateTime('expires_at')->nullable(); 15 | }); 16 | } 17 | 18 | public function down(): void 19 | { 20 | Schema::drop('oauth_refresh_tokens'); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | foreign('author_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('RESTRICT'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('conferences', function (Blueprint $table) { 18 | $table->dropForeign('conferences_author_id_foreign'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_users_social_table.php: -------------------------------------------------------------------------------- 1 | foreign('user_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('CASCADE'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('users_social', function (Blueprint $table) { 18 | $table->dropForeign('users_social_user_id_foreign'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /resources/views/conferences/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('headerScripts') 4 | 7 | @endsection 8 | 9 | @section('content') 10 | 11 | @if ($conference->isFlagged()) 12 | An issue has been reported for this conference. 13 | @endif 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | @endsection 25 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_talk_revisions_table.php: -------------------------------------------------------------------------------- 1 | foreign('talk_id')->references('id')->on('talks')->onUpdate('RESTRICT')->onDelete('CASCADE'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('talk_revisions', function (Blueprint $table) { 18 | $table->dropForeign('talk_revisions_talk_id_foreign'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /resources/views/components/input/group.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name' => null, 3 | 'label' => null, 4 | 'labelClass' => null, 5 | ]) 6 | 7 |
except('v-text')->class('flex items-center') }} 9 | > 10 | @if (isset($name) && isset($label)) 11 | 17 | @endif 18 | 19 | only('v-text') }} 22 | > 23 |
24 | {{ $slot }} 25 |
26 |
27 | -------------------------------------------------------------------------------- /database/migrations/2021_10_13_143629_add_rejection_id_to_submissions_table.php: -------------------------------------------------------------------------------- 1 | string('rejection_id', 36)->nullable()->index('submissions_rejection_id_foreign'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('submissions', function (Blueprint $table) { 19 | $table->dropColumn('rejection_id'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/seeders/AcceptanceSeeder.php: -------------------------------------------------------------------------------- 1 | each(function ($submission) { 14 | if (mt_rand(0, 1)) { 15 | $acceptance = Acceptance::factory()->create([ 16 | 'talk_revision_id' => $submission->talk_revision_id, 17 | 'conference_id' => $submission->conference_id, 18 | ]); 19 | $submission->recordAcceptance($acceptance); 20 | } 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_users_social_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 12 | $table->integer('user_id')->unsigned()->index('users_social_user_id_foreign'); 13 | $table->string('social_id'); 14 | $table->string('service'); 15 | $table->timestamps(); 16 | }); 17 | } 18 | 19 | public function down(): void 20 | { 21 | Schema::drop('users_social'); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /resources/views/auth/no-signups.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 |

No new signups

7 |
8 |

9 | This project is being closed down. Please try Eventy instead! 10 |

11 | Return Home 12 |
13 |
14 | 15 | @endsection 16 | -------------------------------------------------------------------------------- /database/migrations/2021_11_22_161401_add_has_cfp_column_to_conferences_table.php: -------------------------------------------------------------------------------- 1 | boolean('has_cfp') 13 | ->default(true) 14 | ->after('ends_at'); 15 | }); 16 | } 17 | 18 | public function down(): void 19 | { 20 | Schema::table('conferences', function (Blueprint $table) { 21 | $table->dropColumn('has_cfp'); 22 | }); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /database/migrations/2022_05_25_101856_create_talk_reactions_table.php: -------------------------------------------------------------------------------- 1 | id(); 14 | $table->foreignIdFor(Submission::class)->constrained(); 15 | $table->string('url'); 16 | $table->timestamps(); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::dropIfExists('talk_reactions'); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /resources/js/components/CfpFields.vue: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_dismissed_conferences_table.php: -------------------------------------------------------------------------------- 1 | integer('user_id')->unsigned()->index('dismissed_conferences_user_id_foreign'); 12 | $table->string('conference_id', 36)->index('dismissed_conferences_conference_id_foreign'); 13 | $table->timestamps(); 14 | }); 15 | } 16 | 17 | public function down(): void 18 | { 19 | Schema::drop('dismissed_conferences'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /resources/css/mobile-menu-button.css: -------------------------------------------------------------------------------- 1 | .mobileMenuBtn { 2 | @apply relative flex justify-center items-center p-4; 3 | } 4 | 5 | .mobileMenuBtn:before, .mobileMenuBtn:after { 6 | @apply absolute block w-6 h-0.5 bg-white transition duration-75 ease-in-out; 7 | content: ''; 8 | } 9 | 10 | .mobileMenuBtn:before { 11 | top: calc(50% - 6px); 12 | transform: translateY(-50%) rotate(0deg); 13 | } 14 | 15 | .mobileMenuBtn:after { 16 | bottom: calc(50% - 8px); 17 | transform: translateY(-50%) rotate(0deg); 18 | } 19 | 20 | .mobileMenuBtn.isActive:before { 21 | transform: translateY(calc(-50% + 8px)) rotate(45deg); 22 | } 23 | 24 | .mobileMenuBtn.isActive:after { 25 | transform: translateY(calc(-50% - 4px)) rotate(-45deg); 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/TalksController.php: -------------------------------------------------------------------------------- 1 | guard('api')->user()->talks()->withCurrentRevision()->findOrFail($id); 17 | } catch (Exception $e) { 18 | App::abort(404); 19 | } 20 | 21 | $talk = new Talk($talk); 22 | 23 | return response()->jsonApi([ 24 | 'data' => $talk->toArray(), 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | 'America/Detroit', 8 | 9 | 'slack_endpoint' => env('SLACK_ENDPOINT'), 10 | 11 | 'aliases' => Facade::defaultAliases()->merge([ 12 | 'Gravatar' => Creativeorange\Gravatar\Facades\Gravatar::class, 13 | 'Image' => Intervention\Image\Facades\Image::class, 14 | 'Input' => Illuminate\Support\Facades\Input::class, 15 | 'Inspiring' => Illuminate\Foundation\Inspiring::class, 16 | 'Paginator' => Illuminate\Support\Facades\Paginator::class, 17 | 'Redis' => Illuminate\Support\Facades\Redis::class, 18 | 'Socialite' => Laravel\Socialite\Facades\Socialite::class, 19 | ])->toArray(), 20 | 21 | ]; 22 | -------------------------------------------------------------------------------- /database/migrations/2019_08_20_094313_extend_cap_id_field_by_four_characters_for_the_year.php: -------------------------------------------------------------------------------- 1 | string('calling_all_papers_id', 44)->nullable()->change(); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->string('calling_all_papers_id', 40)->nullable()->change(); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /database/factories/BioFactory.php: -------------------------------------------------------------------------------- 1 | User::factory(), 14 | 'nickname' => $this->faker->word(), 15 | 'body' => $this->faker->sentence(), 16 | ]; 17 | } 18 | 19 | public function public() 20 | { 21 | return $this->state([ 22 | 'public' => true, 23 | ]); 24 | } 25 | 26 | public function private() 27 | { 28 | return $this->state([ 29 | 'public' => false, 30 | ]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Requests/SpeakerSearchRequest.php: -------------------------------------------------------------------------------- 1 | query('query')), function ($value) { 25 | $this->merge([ 26 | 'query' => $value, 27 | 'original' => $this->query('query'), 28 | ]); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_talks_table.php: -------------------------------------------------------------------------------- 1 | string('id', 36)->default('0')->unique(); 12 | $table->integer('author_id')->unsigned()->index('talks_author_id_foreign'); 13 | $table->timestamps(); 14 | $table->boolean('public')->default(0); 15 | $table->boolean('is_archived')->default(0); 16 | }); 17 | } 18 | 19 | public function down(): void 20 | { 21 | Schema::drop('talks'); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /resources/svg/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oval 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/views/components/menu/index.blade.php: -------------------------------------------------------------------------------- 1 |
merge(['class' => 'relative']) }}> 2 | 3 |
4 | 10 |
17 | {{ $items }} 18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_bios_table.php: -------------------------------------------------------------------------------- 1 | string('id', 36)->unique(); 12 | $table->string('nickname'); 13 | $table->text('body', 65535); 14 | $table->integer('user_id')->unsigned()->index('bios_user_id_foreign'); 15 | $table->timestamps(); 16 | $table->boolean('public')->default(0); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::drop('bios'); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /resources/js/components/CurrencySelection.vue: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /resources/js/components/LoadingSpinner.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 28 | -------------------------------------------------------------------------------- /resources/views/layouts/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | @isset($title) 6 |

{{ $title }}

7 | @endisset 8 | 9 |
10 |
11 | @hasSection('sidebar') 12 |
13 | @yield('sidebar') 14 |
15 | @endif 16 | @yield('actions') 17 |
18 |
19 |
20 | @yield('list') 21 |
22 |
23 |
24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /app/macros.php: -------------------------------------------------------------------------------- 1 | map(fn ($default, $key) => array_merge( 11 | $defaults, 12 | $queryParams, 13 | $linkParams, 14 | )[$key])->toArray(); 15 | 16 | return $this->route($name, $outputParameters, $absolute); 17 | }); 18 | 19 | Response::macro('jsonApi', function ($value) { 20 | $response = Response::json($value); 21 | $response->headers->set('Content-Type', 'application/vnd.api+json'); 22 | 23 | return $response; 24 | }); 25 | -------------------------------------------------------------------------------- /tests/Unit/TalkRevisionTest.php: -------------------------------------------------------------------------------- 1 | create([ 18 | 'description' => '', 19 | 'organizer_notes' => '', 20 | ]); 21 | 22 | $this->assertEquals('(empty)', $revision->getDescription()); 23 | $this->assertEquals('(empty)', $revision->getHtmledOrganizerNotes()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Http/Controllers/DashboardController.php: -------------------------------------------------------------------------------- 1 | user()->load([ 12 | 'favoritedConferences' => fn ($query) => $query->future(), 13 | 'submissions' => function ($query) { 14 | $query->whereNotRejected()->whereFuture(); 15 | }, 16 | 'submissions.conference', 17 | 'submissions.acceptance', 18 | ]); 19 | 20 | return view('dashboard', [ 21 | 'conferences' => auth()->user()->favoritedConferences, 22 | 'submissions' => auth()->user()->submissions, 23 | ]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 12 | $table->text('connection', 65535); 13 | $table->text('queue', 65535); 14 | $table->text('payload'); 15 | $table->text('exception'); 16 | $table->timestamp('failed_at')->default(DB::raw('CURRENT_TIMESTAMP')); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::drop('failed_jobs'); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /resources/views/components/button/base.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'href', 3 | 'icon', 4 | 'size' => 'sm', 5 | ]) 6 | 7 | @php 8 | $element = isset($href) ? 'a' : 'button'; 9 | @endphp 10 | 11 | <{{ $element }} 12 | @isset($href) href="{{ $href }}" @endisset 13 | {{ $attributes->class([ 14 | "py-2 rounded inline-flex items-center justify-center space-x-2", 15 | 'px-2' => $size === 'sm' && $slot->isEmpty(), 16 | 'px-4' => $size === 'sm' && $slot->isNotEmpty(), 17 | 'font-semibold px-8 text-lg' => $size === 'md', 18 | ]) }} 19 | > 20 | @isset($icon) 21 | @svg($icon, 'w-3 h-3 fill-current inline') 22 | @endisset 23 | @if ($slot->isNotEmpty()) 24 | {{ $slot }} 25 | @endif 26 | 27 | -------------------------------------------------------------------------------- /tests/Unit/ContactRequestTest.php: -------------------------------------------------------------------------------- 1 | render(); 21 | 22 | $this->assertStringContainsString('vader@empire.com', $message); 23 | $this->assertStringContainsString('Darth Vader', $message); 24 | $this->assertStringContainsString('I am your father', $message); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | string('id', 100)->primary(); 12 | $table->integer('user_id'); 13 | $table->integer('client_id'); 14 | $table->text('scopes', 65535)->nullable(); 15 | $table->boolean('revoked'); 16 | $table->dateTime('expires_at')->nullable(); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::drop('oauth_auth_codes'); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /resources/views/talks/show-public.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 | 11 | Return to profile for {{ $user->name }} 12 | 13 | 14 |

{{ $talk->currentRevision->title }}

15 | 16 |

17 | {{ $talk->currentRevision->length }} minute {{ $talk->currentRevision->level }} {{ $talk->currentRevision->type }} 18 |

19 | 20 |

Description/Proposal

21 | 22 | {!! markdown($talk->currentRevision->getDescription()) !!} 23 |
24 | 25 | @endsection 26 | -------------------------------------------------------------------------------- /database/seeders/SubmissionsSeeder.php: -------------------------------------------------------------------------------- 1 | each(function ($conference) { 15 | $talkRevisions = TalkRevision::inRandomOrder()->limit(mt_rand(1, 5))->get(); 16 | $talkRevisions->each(function ($talkRevision) use ($conference) { 17 | Submission::factory()->create([ 18 | 'talk_revision_id' => $talkRevision->id, 19 | 'conference_id' => $conference->id, 20 | ]); 21 | }); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/UserBiosController.php: -------------------------------------------------------------------------------- 1 | guard('api')->user()->id) { 18 | App::abort(404); 19 | } 20 | 21 | $return = auth()->guard('api')->user()->bios->map(function ($bio) { 22 | return new Bio($bio); 23 | }); 24 | 25 | return response()->jsonApi([ 26 | 'data' => $return, 27 | ]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | 6 |

Are you sure?

7 | 8 |

Are you sure you want to delete your account?

9 | 10 | 11 |
12 | 16 | Yes 17 | 18 | 23 | No 24 | 25 |
26 |
27 | 28 | 29 | @endsection 30 | -------------------------------------------------------------------------------- /database/seeders/RejectionSeeder.php: -------------------------------------------------------------------------------- 1 | filter(function ($submission) { 14 | return ! $submission->isAccepted(); 15 | })->each(function ($submission) { 16 | if (mt_rand(0, 1)) { 17 | $rejection = Rejection::factory()->create([ 18 | 'talk_revision_id' => $submission->talk_revision_id, 19 | 'conference_id' => $submission->conference_id, 20 | ]); 21 | $submission->recordRejection($rejection); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/factories/TalkRevisionFactory.php: -------------------------------------------------------------------------------- 1 | 'My Awesome Title', 15 | 'type' => 'lightning', 16 | 'length' => '9', 17 | 'level' => 'beginner', 18 | 'slides' => 'http://speakerdeck.com/mattstauffer/the-best-talk-ever', 19 | 'description' => 'The best talk ever!', 20 | 'organizer_notes' => 'No really.', 21 | 'talk_id' => Talk::factory(), 22 | 'created_at' => Carbon::now()->subMinutes(1), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_acceptances_table.php: -------------------------------------------------------------------------------- 1 | char('id', 36)->primary(); 12 | $table->string('talk_revision_id', 36); 13 | $table->string('conference_id', 36)->index('acceptances_conference_id_foreign'); 14 | $table->timestamps(); 15 | $table->unique(['talk_revision_id', 'conference_id'], 'acceptances_talk_revision_conference_unique'); 16 | }); 17 | } 18 | 19 | public function down(): void 20 | { 21 | Schema::drop('acceptances'); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /tests/Unit/Notifications/ConferenceImporterErrorTest.php: -------------------------------------------------------------------------------- 1 | toSlack(new TightenSlack); 18 | 19 | tap(collect($notification->attachments)->first(), function ($attachment) { 20 | $this->assertNotNull($attachment); 21 | $this->assertStringContainsString('Test exception', $attachment->content); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 | # Handle X-XSRF-Token Header 13 | RewriteCond %{HTTP:x-xsrf-token} . 14 | RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] 15 | 16 | # Redirect Trailing Slashes If Not A Folder... 17 | RewriteCond %{REQUEST_FILENAME} !-d 18 | RewriteCond %{REQUEST_URI} (.+)/$ 19 | RewriteRule ^ %1 [L,R=301] 20 | 21 | # Send Requests To Front Controller... 22 | RewriteCond %{REQUEST_FILENAME} !-d 23 | RewriteCond %{REQUEST_FILENAME} !-f 24 | RewriteRule ^ index.php [L] 25 | 26 | -------------------------------------------------------------------------------- /tests/Api/MeApiTest.php: -------------------------------------------------------------------------------- 1 | create()); 16 | 17 | $response = $this->call('GET', 'api/me'); 18 | $data = json_decode($response->getContent()); 19 | 20 | $this->assertEquals(200, $response->getStatusCode()); 21 | $this->assertIsObject($data->data); 22 | } 23 | 24 | #[Test] 25 | public function only_authenticated_users_can_fetch_user_data(): void 26 | { 27 | $response = $this->getJson('api/me'); 28 | 29 | $response->assertUnauthorized(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Mail/ContactRequest.php: -------------------------------------------------------------------------------- 1 | email = $email; 23 | $this->name = $name; 24 | $this->userMessage = $userMessage; 25 | } 26 | 27 | /** 28 | * Build the message. 29 | */ 30 | public function build(): static 31 | { 32 | return $this->subject('Contact from your Symposium public profile page') 33 | ->view('emails.public-profile-contact'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2019_10_25_152352_add_conference_location.php: -------------------------------------------------------------------------------- 1 | string('location')->nullable(); 13 | $table->double('latitude')->nullable(); 14 | $table->double('longitude')->nullable(); 15 | }); 16 | } 17 | 18 | public function down(): void 19 | { 20 | Schema::table('conferences', function (Blueprint $table) { 21 | $table->dropColumn('location'); 22 | $table->dropColumn('latitude'); 23 | $table->dropColumn('longitude'); 24 | }); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /tests/Unit/Notifications/ConferenceImporterInactiveTest.php: -------------------------------------------------------------------------------- 1 | '2024-05-04 11:11:11']); 16 | 17 | $notification = (new ConferenceImporterInactive) 18 | ->toSlack(new TightenSlack); 19 | 20 | tap(collect($notification->attachments)->first(), function ($attachment) { 21 | $this->assertNotNull($attachment); 22 | $this->assertStringContainsString('2024-05-04 11:11:11', $attachment->content); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /resources/svg/dots.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/Unit/Notifications/CFPsAreOpenTest.php: -------------------------------------------------------------------------------- 1 | create(['title' => 'JediCon']); 20 | $user = User::factory()->create(); 21 | 22 | $notification = (new CFPsAreOpen([$conference])) 23 | ->toMail($user); 24 | 25 | $this->assertEquals('New Open CFPs', $notification->subject); 26 | $this->assertStringContainsString('JediCon', $notification->render()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /resources/views/conferences/issues/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 | 7 | 15 | 16 | 21 | 22 | 23 | 24 | 29 | Report Issue 30 | 31 | 32 | 33 | @endsection 34 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_favorites_table.php: -------------------------------------------------------------------------------- 1 | foreign('conference_id')->references('id')->on('conferences')->onUpdate('RESTRICT')->onDelete('CASCADE'); 12 | $table->foreign('user_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('CASCADE'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('favorites', function (Blueprint $table) { 19 | $table->dropForeign('favorites_conference_id_foreign'); 20 | $table->dropForeign('favorites_user_id_foreign'); 21 | }); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /app/Http/Requests/SaveTalkRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 18 | 'type' => 'required', 19 | 'level' => 'required', 20 | 'length' => 'required|integer|min:0', 21 | 'slides' => 'nullable|url', 22 | 'organizer_notes' => 'required', 23 | 'description' => 'required', 24 | 'public' => 'required', 25 | ]; 26 | } 27 | 28 | public function messages(): array 29 | { 30 | return [ 31 | 'slides.url' => 'Slides URL must contain a valid URL', 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_oauth_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | string('id', 100)->primary(); 12 | $table->integer('user_id')->nullable()->index(); 13 | $table->integer('client_id'); 14 | $table->string('name')->nullable(); 15 | $table->text('scopes', 65535)->nullable(); 16 | $table->boolean('revoked'); 17 | $table->timestamps(); 18 | $table->dateTime('expires_at')->nullable(); 19 | }); 20 | } 21 | 22 | public function down(): void 23 | { 24 | Schema::drop('oauth_access_tokens'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_oauth_clients_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 12 | $table->integer('user_id')->nullable()->index(); 13 | $table->string('name'); 14 | $table->string('secret', 100); 15 | $table->text('redirect', 65535); 16 | $table->boolean('personal_access_client'); 17 | $table->boolean('password_client'); 18 | $table->boolean('revoked'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | public function down(): void 24 | { 25 | Schema::drop('oauth_clients'); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 14 | */ 15 | protected $listen = []; 16 | 17 | protected $subscribe = [ 18 | SlackSubscriber::class, 19 | ]; 20 | 21 | /** 22 | * Register any events for your application. 23 | */ 24 | public function boot(): void 25 | { 26 | // 27 | } 28 | 29 | /** 30 | * Determine if events and listeners should be automatically discovered. 31 | */ 32 | public function shouldDiscoverEvents(): bool 33 | { 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_submissions_table.php: -------------------------------------------------------------------------------- 1 | char('id', 36)->primary(); 12 | $table->string('talk_revision_id', 36); 13 | $table->string('conference_id', 36)->index('submissions_conference_id_foreign'); 14 | $table->timestamps(); 15 | $table->string('acceptance_id', 36)->nullable()->index('submissions_acceptance_id_foreign'); 16 | $table->unique(['talk_revision_id', 'conference_id'], 'submissions_talk_revision_conference_unique'); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::drop('submissions'); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_acceptances_table.php: -------------------------------------------------------------------------------- 1 | foreign('conference_id')->references('id')->on('conferences')->onUpdate('RESTRICT')->onDelete('CASCADE'); 12 | $table->foreign('talk_revision_id')->references('id')->on('talk_revisions')->onUpdate('RESTRICT')->onDelete('CASCADE'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('acceptances', function (Blueprint $table) { 19 | $table->dropForeign('acceptances_conference_id_foreign'); 20 | $table->dropForeign('acceptances_talk_revision_id_foreign'); 21 | }); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | group(function () { 12 | Route::get('me', [MeController::class, 'index']); 13 | Route::get('bios/{bioId}', [BiosController::class, 'show']); 14 | Route::get('user/{userId}/bios', [UserBiosController::class, 'index']); 15 | Route::get('talks/{talkId}', [TalksController::class, 'show']); 16 | Route::get('user/{userId}/talks', [UserTalksController::class, 'index']); 17 | Route::get('conferences/{id}', [ConferencesController::class, 'show']); 18 | Route::get('conferences', [ConferencesController::class, 'index']); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/Unit/Notifications/NewConferenceTest.php: -------------------------------------------------------------------------------- 1 | create(['title' => 'JediCon']); 20 | 21 | $notification = (new NewConference($conference)) 22 | ->toSlack(new TightenSlack); 23 | 24 | tap(collect($notification->attachments)->first(), function ($attachment) { 25 | $this->assertNotNull($attachment); 26 | $this->assertStringContainsString('JediCon', $attachment->content); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Console/Commands/VerifyConferenceImporterHeartbeat.php: -------------------------------------------------------------------------------- 1 | importerAppearsInactive()) { 19 | (new TightenSlack)->notify(new ConferenceImporterInactive); 20 | } 21 | 22 | return Command::SUCCESS; 23 | } 24 | 25 | private function importerAppearsInactive() 26 | { 27 | return Carbon::parse(cache('conference_importer_last_ran_at')) 28 | ->isBefore(now()->subHours(24)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/svg/icon_find.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/views/components/input/textarea.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'label', 4 | 'help', 5 | 'value' => null, 6 | 'cols' => 50, 7 | 'rows' => 10, 8 | 'hideLabel' => false, 9 | ]) 10 | 11 |
only(['class']) }}> 12 | 18 | 19 | 29 | 30 | @isset($help) 31 | {{ $help }} 32 | @endisset 33 |
34 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_dismissed_conferences_table.php: -------------------------------------------------------------------------------- 1 | foreign('conference_id')->references('id')->on('conferences')->onUpdate('RESTRICT')->onDelete('CASCADE'); 12 | $table->foreign('user_id')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('CASCADE'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('dismissed_conferences', function (Blueprint $table) { 19 | $table->dropForeign('dismissed_conferences_conference_id_foreign'); 20 | $table->dropForeign('dismissed_conferences_user_id_foreign'); 21 | }); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/LoginFormRequest.php: -------------------------------------------------------------------------------- 1 | 'required|string', 26 | 'password' => 'required|string', 27 | ]; 28 | } 29 | 30 | public function messages(): array 31 | { 32 | return [ 33 | 'email.required' => 'The email field is required', 34 | 'password.required' => 'The password field is required', 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Notifications/ConferenceImporterInactive.php: -------------------------------------------------------------------------------- 1 | attachment(function ($attachment) { 23 | $attachment 24 | ->title('Conference importer may be inactive') 25 | ->content( 26 | 'The last successful run was at '.cache('conference_importer_last_ran_at') 27 | ) 28 | ->timestamp(Carbon::now()); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Handlers/Events/SlackSubscriber.php: -------------------------------------------------------------------------------- 1 | slack = $slack; 16 | } 17 | 18 | public function subscribe($events) 19 | { 20 | $events->listen('new-signup', [$this, 'onNewSignup']); // @codeCoverageIgnore 21 | $events->listen('new-conference', [$this, 'onNewConference']); 22 | } 23 | 24 | /** 25 | * @codeCoverageIgnore 26 | */ 27 | public function onNewSignup($user, $request) 28 | { 29 | $this->slack->notify(new NewUser($user, $request->getClientIp())); 30 | } 31 | 32 | public function onNewConference($conference) 33 | { 34 | $this->slack->notify(new NewConference($conference)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/seeders/ConferencesSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 30 | 'title' => $name, 31 | 'is_featured' => rand(0, 1), 32 | 'is_approved' => true, 33 | ]); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /resources/js/components/MenuToggle.vue: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /resources/views/components/input/radios.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'label', 4 | 'help', 5 | 'options' => [], 6 | 'value' => null, 7 | ]) 8 | 9 |
only(['class']) }}> 10 | 16 | 17 | @isset($help) 18 | {{ $help }} 19 | @endisset 20 | 21 | @foreach ($options as $label => $option) 22 | 32 | @endforeach 33 |
34 | -------------------------------------------------------------------------------- /resources/views/components/listing.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

6 | 7 | {{ $title }} 8 | 9 |

10 | @isset($header) 11 | {{ $header }} 12 | @endisset 13 |
14 |
15 | {{ $actions }} 16 |
17 |
18 | @isset($body) 19 |
20 | {{ $body }} 21 |
22 | @endisset 23 |
24 | @isset($footer) 25 | 26 | {{ $footer }} 27 | 28 | @endisset 29 |
30 | -------------------------------------------------------------------------------- /database/migrations/2022_08_23_145646_create_conference_issues_table.php: -------------------------------------------------------------------------------- 1 | id(); 14 | $table->foreignIdFor(Conference::class)->constrained(); 15 | $table->unsignedInteger('user_id')->constrained(); 16 | $table->string('reason'); 17 | $table->text('note'); 18 | $table->timestamp('closed_at')->nullable(); 19 | $table->timestamps(); 20 | 21 | $table->foreign('user_id')->references('id')->on('users'); 22 | }); 23 | } 24 | 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('conference_issues'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | environment('testing')) { 19 | DB::statement('SET FOREIGN_KEY_CHECKS = 0'); 20 | } 21 | 22 | $this->call([ 23 | UsersSeeder::class, 24 | TalksSeeder::class, 25 | BiosSeeder::class, 26 | ConferencesSeeder::class, 27 | SubmissionsSeeder::class, 28 | AcceptanceSeeder::class, 29 | RejectionSeeder::class, 30 | ]); 31 | 32 | if (! app()->environment('testing')) { 33 | DB::statement('SET FOREIGN_KEY_CHECKS = 1'); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /google-guide.md: -------------------------------------------------------------------------------- 1 | ## Google API Key Guide 2 | 3 | ### Create a Key 4 | 5 | 1. Create an API Key from the [Google Places API Documentation](https://developers.google.com/places/web-service/get-api-key) by clicking on the "GET A KEY" button. 6 | 7 | ![Generate API Key](https://www.dropbox.com/s/08jv8ngi8nm9r6x/google_key.gif?raw=1) 8 | 9 | 1. Once you have a key, save it to your .env file using the environment key `GOOGLE_MAPS_API_KEY`. 10 | 1. Next, click on the `API Console` link just below the API Key. This will take you to your API Console where you can enable/disable APIs and manage credentials. 11 | 12 | > It is highly recommended to restrict your API Key, especially in production. 13 | 14 | ### Enable Javascript API 15 | 16 | 1. Click on Library 17 | 1. Click on the Google Maps Javascript API 18 | 1. Click on Enable 19 | 20 | ![Enable Javascript API](https://www.dropbox.com/s/mitnsosb976pab2/enable_api_cropped.gif?raw=1) 21 | 22 | You're now ready to start using the API Key in your application! 23 | -------------------------------------------------------------------------------- /database/migrations/2020_10_30_131213_rename_featured_columns_to_is_featured.php: -------------------------------------------------------------------------------- 1 | renameColumn('featured', 'is_featured'); 13 | }); 14 | 15 | Schema::table('conferences', function (Blueprint $table) { 16 | $table->renameColumn('featured', 'is_featured'); 17 | }); 18 | } 19 | 20 | public function down(): void 21 | { 22 | Schema::table('users', function (Blueprint $table) { 23 | $table->renameColumn('is_featured', 'featured'); 24 | }); 25 | 26 | Schema::table('conferences', function (Blueprint $table) { 27 | $table->renameColumn('is_featured', 'featured'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /resources/views/components/input/toggle.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'label', 4 | 'value' => null, 5 | ]) 6 | 7 |
8 | 9 |
10 | 11 | except(['class']) }} 18 | /> 19 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /database/migrations/2021_10_28_000000_add_uuid_to_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | string('uuid')->after('id')->nullable()->unique(); 15 | }); 16 | 17 | DB::table('failed_jobs')->whereNull('uuid')->cursor()->each(function ($job) { 18 | DB::table('failed_jobs') 19 | ->where('id', $job->id) 20 | ->update(['uuid' => (string) Str::uuid()]); 21 | }); 22 | } 23 | 24 | public function down(): void 25 | { 26 | Schema::table('failed_jobs', function (Blueprint $table) { 27 | $table->dropColumn('uuid'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | withoutVite(); 22 | 23 | NotificationFake::macro('assertSentToTightenSlack', function ($notification) { 24 | $this->assertSentTo(new TightenSlack, $notification); 25 | }); 26 | 27 | NotificationFake::macro('assertNotSentToTightenSlack', function ($notification) { 28 | $this->assertNotSentTo(new TightenSlack, $notification); 29 | }); 30 | 31 | Notification::fake(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Notifications/NewConference.php: -------------------------------------------------------------------------------- 1 | conference = $conference; 17 | } 18 | 19 | public function via($notifiable): array 20 | { 21 | return ['slack']; 22 | } 23 | 24 | public function toSlack($notifiable) 25 | { 26 | return (new SlackMessage) 27 | ->attachment(function (SlackAttachment $attachment) { 28 | $attachment 29 | ->title('New conference created') 30 | ->content("{$this->conference->title}\nAPPROVE HERE: {$this->conference->link}") 31 | ->timestamp(Carbon::now()); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/seeders/BiosSeeder.php: -------------------------------------------------------------------------------- 1 | bios()->create(['nickname' => 'Short Bio', 'body' => 'I am short.']); 18 | $longBio = $author->bios()->create(['nickname' => 'Long Bio', 'body' => 'I am short and I love being short and this is very long.']); 19 | 20 | $author->bios()->saveMany([$shortBio, $longBio]); 21 | 22 | // Add a talk for user 2 for testing purposes 23 | $author = User::find(2); 24 | 25 | $trueBio = $author->bios()->create(['nickname' => 'True Bio', 'body' => 'I am a person.']); 26 | $falseBio = $author->bios()->create(['nickname' => 'False Bio', 'body' => 'I am an elephant.']); 27 | 28 | $author->bios()->saveMany([$trueBio, $falseBio]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Notifications/ConferenceImporterError.php: -------------------------------------------------------------------------------- 1 | message = $exception->getMessage(); 20 | } 21 | 22 | public function via($notifiable): array 23 | { 24 | return ['slack']; 25 | } 26 | 27 | public function toSlack($notifiable) 28 | { 29 | return (new SlackMessage) 30 | ->attachment(function ($attachment) { 31 | $attachment 32 | ->title('Conference importer encountered an error') 33 | ->content("Message: {$this->message}") 34 | ->timestamp(Carbon::now()); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/ApiResources/Me.php: -------------------------------------------------------------------------------- 1 | user = $user; 15 | } 16 | 17 | public function getId() 18 | { 19 | return $this->user->id; 20 | } 21 | 22 | public function getType() 23 | { 24 | return 'users'; 25 | } 26 | 27 | public function attributes() 28 | { 29 | return [ 30 | 'email' => $this->user->email, 31 | 'name' => $this->user->name, 32 | 'created_at' => (string) $this->user->created_at, 33 | 'updated_at' => (string) $this->user->updated_at, 34 | ]; 35 | } 36 | 37 | public function toArray() 38 | { 39 | return [ 40 | 'id' => $this->getId(), 41 | 'type' => $this->getType(), 42 | 'attributes' => $this->attributes(), 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_talk_revisions_table.php: -------------------------------------------------------------------------------- 1 | string('id', 36)->unique('talk_version_revisions_id_unique'); 12 | $table->string('title'); 13 | $table->string('type', 50); 14 | $table->integer('length'); 15 | $table->enum('level', ['beginner', 'intermediate', 'advanced']); 16 | $table->text('description', 65535); 17 | $table->string('slides')->nullable(); 18 | $table->text('organizer_notes', 65535); 19 | $table->timestamps(); 20 | $table->string('talk_id', 36)->default('')->index('talk_revisions_talk_id_foreign'); 21 | }); 22 | } 23 | 24 | public function down(): void 25 | { 26 | Schema::drop('talk_revisions'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /resources/js/components/UpdateQueryString.vue: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /app/Notifications/NewUser.php: -------------------------------------------------------------------------------- 1 | user = $user; 22 | $this->ip = $ip; 23 | } 24 | 25 | public function via($notifiable): array 26 | { 27 | return ['slack']; 28 | } 29 | 30 | public function toSlack($notifiable) 31 | { 32 | return (new SlackMessage) 33 | ->attachment(function (SlackAttachment $attachment) { 34 | $attachment 35 | ->title('New user signup') 36 | ->content("{$this->user->username}\n{$this->user->email}\n{$this->ip}") 37 | ->timestamp(Carbon::now()); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /resources/views/conferences/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Edit Conference']) 2 | 3 | @section('content') 4 | 5 |
    6 | @foreach ($errors->all() as $message) 7 |
  • {{ $message }}
  • 8 | @endforeach 9 |
10 | 11 | 12 | 13 | @include('conferences.form') 14 | 15 | @if (auth()->user()->isAdmin()) 16 | 26 | @endif 27 | 28 | 29 | 30 | 35 | Update 36 | 37 | 38 | 39 | @endsection 40 | -------------------------------------------------------------------------------- /database/factories/ConferenceIssueFactory.php: -------------------------------------------------------------------------------- 1 | User::factory(), 17 | 'conference_id' => Conference::factory(), 18 | 'reason' => Arr::random(ConferenceIssue::REASONS), 19 | 'note' => $this->faker->sentence(), 20 | ]; 21 | } 22 | 23 | public function open() 24 | { 25 | return $this->state(function () { 26 | return [ 27 | 'closed_at' => null, 28 | ]; 29 | }); 30 | } 31 | 32 | public function closed() 33 | { 34 | return $this->state(function () { 35 | return [ 36 | 'closed_at' => $this->faker->dateTime(), 37 | ]; 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /database/migrations/2023_03_31_000348_add_admin_note_column_to_conference_issues_table.php: -------------------------------------------------------------------------------- 1 | text('admin_note') 13 | ->after('note') 14 | ->nullable(); 15 | $table->unsignedInteger('closed_by') 16 | ->constrained() 17 | ->after('user_id') 18 | ->nullable(); 19 | 20 | $table->foreign('closed_by')->references('id')->on('users'); 21 | }); 22 | } 23 | 24 | public function down(): void 25 | { 26 | Schema::table('conference_issues', function (Blueprint $table) { 27 | $table->dropColumn('admin_note'); 28 | $table->dropForeign('conference_issues_closed_by_foreign'); 29 | $table->dropColumn('closed_by'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /app/ApiResources/Bio.php: -------------------------------------------------------------------------------- 1 | bio = $bio; 15 | } 16 | 17 | public function getId() 18 | { 19 | return $this->bio->id; 20 | } 21 | 22 | public function getType() 23 | { 24 | return 'bios'; 25 | } 26 | 27 | public function attributes() 28 | { 29 | return [ 30 | 'nickname' => $this->bio->nickname, 31 | 'public' => $this->bio->public, 32 | 'body' => $this->bio->body, 33 | 'created_at' => (string) $this->bio->created_at, 34 | 'updated_at' => (string) $this->bio->updated_at, 35 | ]; 36 | } 37 | 38 | public function toArray() 39 | { 40 | return [ 41 | 'id' => $this->getId(), 42 | 'type' => $this->getType(), 43 | 'attributes' => $this->attributes(), 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /database/migrations/2019_05_09_222406_drop_joindin_add_cap.php: -------------------------------------------------------------------------------- 1 | dropColumn('joindin_id'); 17 | }); 18 | Schema::table('conferences', function (Blueprint $table) { 19 | $table->string('calling_all_papers_id', 40)->nullable(); 20 | }); 21 | } 22 | 23 | public function down(): void 24 | { 25 | Schema::table('conferences', function (Blueprint $table) { 26 | $table->dropColumn('calling_all_papers_id'); 27 | }); 28 | Schema::table('conferences', function (Blueprint $table) { 29 | $table->integer('joindin_id')->nullable(); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Eventy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/Filament/Resources/ConferenceResource/Pages/EditConference.php: -------------------------------------------------------------------------------- 1 | record->isRejected()) { 16 | return parent::getHeading(); 17 | } 18 | 19 | return parent::getHeading().' (rejected)'; 20 | } 21 | 22 | protected function getHeaderActions(): array 23 | { 24 | return [ 25 | Action::make('Reject') 26 | ->color('danger') 27 | ->action(fn () => $this->record->reject()) 28 | ->visible(fn () => ! $this->record->isRejected()), 29 | Action::make('Restore') 30 | ->action(fn () => $this->record->restore()) 31 | ->visible(fn () => $this->record->isRejected()), 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Notifications/ConferenceIssueReported.php: -------------------------------------------------------------------------------- 1 | issue = $issue; 21 | } 22 | 23 | public function via($notifiable): array 24 | { 25 | return ['slack']; 26 | } 27 | 28 | public function toSlack($notifiable) 29 | { 30 | return (new SlackMessage) 31 | ->attachment(function ($attachment) { 32 | $attachment 33 | ->title("Issue reported: {$this->issue->conference->title}") 34 | ->content("View issue: {$this->issue->link}") 35 | ->timestamp(Carbon::now()); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142915_add_foreign_keys_to_submissions_table.php: -------------------------------------------------------------------------------- 1 | foreign('acceptance_id')->references('id')->on('acceptances')->onUpdate('RESTRICT')->onDelete('RESTRICT'); 12 | $table->foreign('conference_id')->references('id')->on('conferences')->onUpdate('RESTRICT')->onDelete('CASCADE'); 13 | $table->foreign('talk_revision_id')->references('id')->on('talk_revisions')->onUpdate('RESTRICT')->onDelete('CASCADE'); 14 | }); 15 | } 16 | 17 | public function down(): void 18 | { 19 | Schema::table('submissions', function (Blueprint $table) { 20 | $table->dropForeign('submissions_acceptance_id_foreign'); 21 | $table->dropForeign('submissions_conference_id_foreign'); 22 | $table->dropForeign('submissions_talk_revision_id_foreign'); 23 | }); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/UserTalksController.php: -------------------------------------------------------------------------------- 1 | guard('api')->user()->id) { 18 | App::abort(404); 19 | } 20 | 21 | $talks = auth()->guard('api') 22 | ->user() 23 | ->talks() 24 | ->withCurrentRevision() 25 | ->when((bool) request()->query('include-archived'), function ($query) { 26 | $query->withoutGlobalScope('active'); 27 | }) 28 | ->get() 29 | ->sortByTitle() 30 | ->map(function ($talk) { 31 | return new Talk($talk); 32 | })->values(); 33 | 34 | return response()->jsonApi([ 35 | 'data' => $talks, 36 | ]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /resources/views/components/input/text.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'label' => '', 4 | 'help', 5 | 'value' => null, 6 | 'type' => 'text', 7 | 'inline' => false, 8 | 'hideLabel' => false, 9 | 'roundedClass' => 'rounded', 10 | ]) 11 | 12 |
only(['class']) }}> 13 | 19 | 20 | @php 21 | $valueName = str($name)->replace('[', '.')->replace(']', ''); 22 | @endphp 23 | 24 | except('class') }} 29 | class=" 30 | bg-white border-form-200 form-input placeholder-form-400 {{ $roundedClass }} 31 | @unless ($inline) w-full @endunless 32 | @unless ($hideLabel) mt-1 @endunless 33 | " 34 | > 35 | 36 | @isset($help) 37 | {{ $help }} 38 | @endisset 39 |
40 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/RegisterFormRequest.php: -------------------------------------------------------------------------------- 1 | 'required|max:255', 29 | 'email' => 'required|email|max:255|unique:users|emailblacklist', 30 | 'password' => 'required|min:6', 31 | ]; 32 | } 33 | 34 | public function messages(): array 35 | { 36 | return [ 37 | 'name.required' => 'The name field is required', 38 | 'email.required' => 'The email field is required', 39 | 'password.required' => 'The password field is required', 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Transformers/TalkForConferenceTransformer.php: -------------------------------------------------------------------------------- 1 | currentRevision; 13 | $currentRevision->setRelation('talk', $talk); 14 | 15 | $submission = $talk->getMySubmissionForConference($conference); 16 | $acceptance = $submission ? $submission->acceptance : null; 17 | $rejection = $submission ? $submission->rejection : null; 18 | 19 | return [ 20 | 'id' => $talk->id, 21 | 'title' => $currentRevision->title, 22 | 'url' => $currentRevision->getUrl(), 23 | 'submitted' => (bool) $submission, 24 | 'submissionId' => $submission ? $submission->id : null, 25 | 'accepted' => (bool) $acceptance, 26 | 'acceptanceId' => $acceptance ? $acceptance->id : null, 27 | 'rejected' => (bool) $rejection, 28 | 'rejectionId' => $rejection ? $rejection->id : null, 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/views/bios/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 | 5 | 6 | 7 | 15 | 16 |
17 | 18 | {!! str_replace("\n", "
", $bio->body) !!} 19 |
20 | 21 |
22 |
Created
23 |
{{ $bio->created_at->toFormattedDateString() }}
24 |
25 |
26 |
Updated
27 |
{{ $bio->updated_at->toFormattedDateString() }}
28 |
29 |
30 |
31 | 32 | @endsection 33 | -------------------------------------------------------------------------------- /tests/Unit/Notifications/ConferenceIssueReportedTest.php: -------------------------------------------------------------------------------- 1 | for(Conference::factory()->create(['title' => 'JediCon'])) 22 | ->create(); 23 | 24 | $notification = (new ConferenceIssueReported($issue)) 25 | ->toSlack(new TightenSlack); 26 | 27 | tap(collect($notification->attachments)->first(), function ($attachment) use ($issue) { 28 | $this->assertNotNull($attachment); 29 | $this->assertStringContainsString('JediCon', $attachment->title); 30 | $this->assertStringContainsString("admin/issues/{$issue->id}", $attachment->content); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Feature/VerifyConferenceImporterHeartbeatTest.php: -------------------------------------------------------------------------------- 1 | now()->subHours(25)->toDateTimeString()]); 17 | 18 | $this->artisan('verify-conference-importer-heartbeat'); 19 | 20 | Notification::assertSentToTightenSlack(ConferenceImporterInactive::class); 21 | } 22 | 23 | #[Test] 24 | public function slack_is_not_notified_when_the_importer_has_run_within_24_hours(): void 25 | { 26 | Notification::fake(); 27 | cache(['conference_importer_last_ran_at' => now()->subHours(23)->toDateTimeString()]); 28 | 29 | $this->artisan('verify-conference-importer-heartbeat'); 30 | 31 | Notification::assertNotSentToTightenSlack(ConferenceImporterInactive::class); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | APP_DEBUG=true 3 | APP_KEY=SomeRandomString 4 | APP_URL=http://symposium.test/ 5 | APP_NAME=Symposium 6 | 7 | APP_LOCALE=en 8 | APP_FALLBACK_LOCALE=en 9 | APP_FAKER_LOCALE=en_US 10 | APP_MAINTENANCE_DRIVER=file 11 | APP_MAINTENANCE_STORE=database 12 | PHP_CLI_SERVER_WORKERS=4 13 | 14 | BCRYPT_ROUNDS=12 15 | 16 | DB_CONNECTION=mysql 17 | DB_HOST=localhost 18 | DB_DATABASE=symposium 19 | DB_USERNAME=root 20 | DB_PASSWORD= 21 | 22 | CACHE_STORE=file 23 | SESSION_DRIVER=file 24 | SCOUT_DRIVER=database 25 | QUEUE_CONNECTION=redis 26 | 27 | REDIS_CLIENT=predis 28 | REDIS_CLUSTER=predis 29 | REDIS_PREFIX="symposium_" 30 | 31 | FILESYSTEM_DISK=local 32 | 33 | MAIL_HOST= 34 | MAIL_USERNAME= 35 | MAIL_PASSWORD= 36 | ADMIN_EMAIL=email 37 | 38 | SLACK_ENDPOINT= 39 | 40 | CAPTCHA_PUBLIC= 41 | CAPTCHA_PRIVATE= 42 | 43 | FLARE_KEY= 44 | 45 | VITE_DISABLE_LOCATION_LOOKUP=false 46 | GOOGLE_MAPS_API_KEY= 47 | GOOGLE_MAPS_API_FRONTEND_KEY= 48 | 49 | ALGOLIA_APP_ID= 50 | ALGOLIA_SECRET= 51 | 52 | GITHUB_CLIENT_ID= 53 | GITHUB_CLIENT_SECRET= 54 | GITHUB_REDIRECT_URL="${APP_URL}login/github/callback" 55 | 56 | LOG_STACK="daily" 57 | 58 | SESSION_ENCRYPT=false 59 | SESSION_PATH=/ 60 | SESSION_DOMAIN=null 61 | -------------------------------------------------------------------------------- /database/migrations/2021_10_13_142431_create_rejections_table.php: -------------------------------------------------------------------------------- 1 | char('id', 36)->primary(); 13 | $table->string('talk_revision_id', 36); 14 | $table->string('conference_id', 36)->index('rejections_conference_id_foreign'); 15 | $table->timestamps(); 16 | $table->unique(['talk_revision_id', 'conference_id'], 'rejections_talk_revision_conference_unique'); 17 | }); 18 | 19 | Schema::table('rejections', function (Blueprint $table) { 20 | $table->foreign('conference_id')->references('id')->on('conferences')->onUpdate('RESTRICT')->onDelete('CASCADE'); 21 | $table->foreign('talk_revision_id')->references('id')->on('talk_revisions')->onUpdate('RESTRICT')->onDelete('CASCADE'); 22 | }); 23 | } 24 | 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('rejections'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/reset.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app', ['title' => 'Reset Password']) 2 | 3 | @section('content') 4 | 5 | @include('partials.authentication-errors') 6 | 7 |
8 | {!! csrf_field() !!} 9 | 10 | 11 | 19 | 20 | 28 | 29 | 37 | 38 | 43 | Reset Password 44 | 45 |
46 | 47 | @endsection 48 | -------------------------------------------------------------------------------- /app/Console/Commands/SendNotificationForOpenCFPs.php: -------------------------------------------------------------------------------- 1 | conferencesToShare()->get(); 29 | 30 | if ($conferences->isEmpty()) { 31 | return; 32 | } 33 | 34 | $this->conferencesToShare()->update(['is_shared' => true]); 35 | 36 | Notification::send(User::wantsNotifications()->get(), new CFPsAreOpen($conferences)); 37 | } 38 | 39 | protected function conferencesToShare() 40 | { 41 | return Conference::approved()->notShared()->whereCfpIsOpen(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /database/migrations/2019_04_19_142914_create_conferences_table.php: -------------------------------------------------------------------------------- 1 | string('id', 36)->unique(); 12 | $table->string('title'); 13 | $table->text('description', 65535); 14 | $table->string('url'); 15 | $table->integer('author_id')->unsigned()->index('conferences_author_id_foreign'); 16 | $table->dateTime('starts_at')->nullable(); 17 | $table->dateTime('ends_at')->nullable(); 18 | $table->dateTime('cfp_starts_at')->nullable(); 19 | $table->dateTime('cfp_ends_at')->nullable(); 20 | $table->integer('joindin_id')->nullable(); 21 | $table->timestamps(); 22 | $table->string('cfp_url')->nullable(); 23 | $table->boolean('is_approved')->default(0); 24 | $table->boolean('is_shared')->default(0); 25 | }); 26 | } 27 | 28 | public function down(): void 29 | { 30 | Schema::drop('conferences'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /resources/svg/icon_versioncontrol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/Notifications/CFPsAreOpen.php: -------------------------------------------------------------------------------- 1 | conferences = $conferences; 23 | } 24 | 25 | /** 26 | * Get the notification's delivery channels. 27 | * 28 | * @param mixed $notifiable 29 | */ 30 | public function via($notifiable): array 31 | { 32 | return ['mail']; 33 | } 34 | 35 | /** 36 | * Get the mail representation of the notification. 37 | * 38 | * @param mixed $notifiable 39 | */ 40 | public function toMail($notifiable): MailMessage 41 | { 42 | return (new MailMessage) 43 | ->subject('New Open CFPs') 44 | ->markdown('emails.open-cfps', [ 45 | 'conferences' => $this->conferences, 46 | ]); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'domain' => env('MAILGUN_DOMAIN'), 7 | 'secret' => env('MAILGUN_SECRET'), 8 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 9 | 'scheme' => 'https', 10 | ], 11 | 12 | 'sparkpost' => [ 13 | 'secret' => env('SPARKPOST_SECRET'), 14 | ], 15 | 16 | 'stripe' => [ 17 | 'model' => App\Models\User::class, 18 | 'key' => env('STRIPE_KEY'), 19 | 'secret' => env('STRIPE_SECRET'), 20 | 'webhook' => [ 21 | 'secret' => env('STRIPE_WEBHOOK_SECRET'), 22 | 'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300), 23 | ], 24 | ], 25 | 26 | 'google' => [ 27 | 'maps' => [ 28 | 'key' => env('GOOGLE_MAPS_API_KEY'), 29 | 'frontend_key' => env('GOOGLE_MAPS_API_FRONTEND_KEY'), 30 | ], 31 | 'captcha' => [ 32 | 'public' => env('CAPTCHA_PUBLIC'), 33 | 'private' => env('CAPTCHA_PRIVATE'), 34 | ], 35 | ], 36 | 37 | 'github' => [ 38 | 'client_id' => env('GITHUB_CLIENT_ID'), 39 | 'client_secret' => env('GITHUB_CLIENT_SECRET'), 40 | 'redirect' => env('GITHUB_REDIRECT_URL'), 41 | ], 42 | 43 | ]; 44 | -------------------------------------------------------------------------------- /tests/Feature/HomeTest.php: -------------------------------------------------------------------------------- 1 | featured()->create(['name' => 'Luke Skywalker']); 19 | User::factory()->notFeatured()->create(['name' => 'Han Solo']); 20 | 21 | $response = $this->get(route('home')); 22 | 23 | $response->assertSuccessful(); 24 | $response->assertSee('Luke Skywalker'); 25 | $response->assertDontSee('Han Solo'); 26 | } 27 | 28 | #[Test] 29 | public function including_featured_conferences() 30 | { 31 | Conference::factory()->featured()->create(['title' => 'Awesome Conference']); 32 | Conference::factory()->notFeatured()->create(['title' => 'Boring Conference']); 33 | 34 | $response = $this->get(route('home')); 35 | 36 | $response->assertSuccessful(); 37 | $response->assertSee('Awesome Conference'); 38 | $response->assertDontSee('Boring Conference'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'testing' => [ 7 | 'driver' => 'sqlite', 8 | 'database' => ':memory:', 9 | 'prefix' => '', 10 | ], 11 | 12 | 'mysql' => [ 13 | 'driver' => 'mysql', 14 | 'url' => env('DB_URL'), 15 | 'host' => env('DB_HOST', '127.0.0.1'), 16 | 'port' => env('DB_PORT', '3306'), 17 | 'database' => env('DB_DATABASE', 'forge'), 18 | 'username' => env('DB_USERNAME', 'forge'), 19 | 'password' => env('DB_PASSWORD', ''), 20 | 'unix_socket' => env('DB_SOCKET', ''), 21 | 'charset' => 'utf8mb4', 22 | 'collation' => 'utf8mb4_unicode_ci', 23 | 'prefix' => '', 24 | 'prefix_indexes' => true, 25 | 'strict' => false, 26 | 'engine' => null, 27 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 28 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 29 | ]) : [], 30 | ], 31 | ], 32 | 33 | 'migrations' => [ 34 | 'table' => 'migrations', 35 | 'update_date_on_publish' => false, // disable to preserve original behavior for existing applications 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /app/Services/Geocoder/Geocoder.php: -------------------------------------------------------------------------------- 1 | isInvalidAddress($address)) { 13 | throw new InvalidAddressGeocodingException; 14 | } 15 | 16 | return $this->requestGeocoding($address); 17 | } 18 | 19 | private function requestGeocoding($address) 20 | { 21 | $response = Http::acceptJson() 22 | ->withHeaders([ 23 | 'User-Agent' => 'Symposium CLI', 24 | ]) 25 | ->get('https://maps.googleapis.com/maps/api/geocode/json', [ 26 | 'address' => $address, 27 | 'key' => config('services.google.maps.key'), 28 | ]) 29 | ->json(); 30 | 31 | if (! count($response['results'])) { 32 | cache()->set('invalid-address::'.md5($address), true); 33 | throw new InvalidAddressGeocodingException; 34 | } 35 | 36 | return new GeocoderResponse($response); 37 | } 38 | 39 | private function isInvalidAddress($address) 40 | { 41 | return cache('invalid-address::'.md5($address)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /resources/svg/icon_trackconferences.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/Filament/Resources/ConferenceIssueResource/Pages/ViewConferenceIssue.php: -------------------------------------------------------------------------------- 1 | record->close(auth()->user(), $data['notes']); 18 | 19 | return $this->redirect($this->getResource()::getUrl('index')); 20 | } 21 | 22 | protected function getHeaderActions(): array 23 | { 24 | return [ 25 | Action::make('View conference') 26 | ->color('gray') 27 | ->icon('heroicon-o-arrow-right-circle') 28 | ->url(ConferenceResource::getUrl('edit', [$this->record->conference])), 29 | Action::make('Close issue') 30 | ->icon('heroicon-o-adjustments-vertical') 31 | ->action('close') 32 | ->form([ 33 | Textarea::make('notes')->required(), 34 | ]), 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/svg/icon_tracktalks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/views/talks/listing.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @svg('compose', 'w-5 fill-current inline') 5 | 6 | 7 | @svg('trash', 'w-5 fill-current inline') 8 | 9 | @if ($talk->isArchived()) 10 | 11 | @svg('folder-outline', 'w-5 fill-current inline') 12 | 13 | @else 14 | 15 | @svg('folder', 'w-5 fill-current inline') 16 | 17 | @endif 18 | 19 | 20 |
21 |
Created
22 |
{{ $talk->created_at->toFormattedDateString() }}
23 |
24 |
25 |
{{ $talk->currentRevision->length }}-minute {{ $talk->currentRevision->level }} {{ $talk->currentRevision->type }}
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | loginUser($request); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Services/CreateBioForm.php: -------------------------------------------------------------------------------- 1 | ['required'], 13 | 'body' => ['required'], 14 | 'public' => [], 15 | ]; 16 | 17 | private $input; 18 | 19 | private $user; 20 | 21 | private function __construct($input, $user) 22 | { 23 | $this->input = $this->removeEmptyFields($input); 24 | 25 | $this->input['public'] = array_key_exists('public', $this->input) && 26 | $this->input['public'] == '1'; 27 | 28 | $this->user = $user; 29 | } 30 | 31 | public static function fillOut($input, $user) 32 | { 33 | return new self($input, $user); 34 | } 35 | 36 | public function complete() 37 | { 38 | $validation = Validator::make($this->input, $this->rules); 39 | 40 | if ($validation->fails()) { 41 | throw new ValidationException('Invalid input provided, see errors', $validation->errors()); 42 | } 43 | 44 | return Bio::create(array_merge($this->input, [ 45 | 'user_id' => $this->user->id, 46 | ])); 47 | } 48 | 49 | private function removeEmptyFields($input) 50 | { 51 | return array_filter($input); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Feature/ConferenceIssuesTest.php: -------------------------------------------------------------------------------- 1 | create([ 19 | 'closed_at' => null, 20 | ]); 21 | $closedIssue = ConferenceIssue::factory()->create([ 22 | 'closed_at' => now(), 23 | ]); 24 | 25 | $this->assertTrue($openIssue->isOpen()); 26 | $this->assertFalse($closedIssue->isOpen()); 27 | } 28 | 29 | #[Test] 30 | public function closing_an_issue(): void 31 | { 32 | $user = User::factory()->create(); 33 | $issue = ConferenceIssue::factory()->create(); 34 | $this->assertTrue($issue->isOpen()); 35 | 36 | $issue->close($user, 'This conference is spam'); 37 | 38 | tap($issue->fresh(), function ($issue) use ($user) { 39 | $this->assertEquals($user->id, $issue->closed_by); 40 | $this->assertEquals('This conference is spam', $issue->admin_note); 41 | $this->assertNotNull($issue->closed_at); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /resources/views/components/panel/index.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'size' => 'lg', 3 | 'padding' => true, 4 | 'title', 5 | ]) 6 | 7 | @php 8 | $styles = [ 9 | 'sm' => 'p-0', 10 | 'md' => 'p-0', 11 | 'lg' => 'max-w-md mx-auto sm:max-w-3xl', 12 | 'xl' => 'w-full' 13 | ]; 14 | @endphp 15 | 16 |
19 |
20 | @isset($title) 21 |
22 |
23 |

24 | {{ $title }} 25 |

26 | 27 | @isset($subtitle) 28 | {{ $subtitle }} 29 | @endisset 30 |
31 | 32 | @isset($actions) 33 | {{ $actions }} 34 | @endisset 35 |
36 | @endisset 37 |
38 | 39 |
40 | {{ $slot }} 41 |
42 | 43 | @isset($body) 44 | {{ $body }} 45 | @endisset 46 | 47 | @isset($footer) 48 |
49 | {{ $footer }} 50 |
51 | @endisset 52 |
53 | 54 | -------------------------------------------------------------------------------- /resources/views/components/side-menu.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'title', 3 | 'links' => [], 4 | 'defaults' => [], 5 | ]) 6 | 7 | 8 |
{{ $title }}
9 |
10 | @isset($body) 11 | {{ $body }} 12 | @endisset 13 | 14 | @foreach ($links as $key => $link) 15 | @php 16 | if (isset($link['query'])) { 17 | $key = key($link['query']); 18 | $isActive = request($key) === $link['query'][$key] || 19 | ! request()->has($key) && $link['query'][$key] === $defaults[$key]; 20 | } else { 21 | $isActive = false; 22 | } 23 | @endphp 24 | {{ $link['label'] }} 35 | @endforeach 36 |
37 |
38 | -------------------------------------------------------------------------------- /tests/Feature/SyncCallingAllPapersEventsTest.php: -------------------------------------------------------------------------------- 1 | stubEvent(); 27 | $this->mockClient(); 28 | 29 | Artisan::call('callingallpapers:sync'); 30 | 31 | $this->assertEquals( 32 | '2022-05-04 11:11:11', 33 | cache('conference_importer_last_ran_at'), 34 | ); 35 | } 36 | 37 | #[Test] 38 | public function notifying_slack_when_command_errors(): void 39 | { 40 | Notification::fake(); 41 | $this->stubEvent(); 42 | $this->mockClientWithError(); 43 | 44 | Artisan::call('callingallpapers:sync'); 45 | 46 | Notification::assertSentToTightenSlack(ConferenceImporterError::class); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/ApiResources/Conference.php: -------------------------------------------------------------------------------- 1 | conference = $conference; 15 | } 16 | 17 | public function getId() 18 | { 19 | return $this->conference->id; 20 | } 21 | 22 | public function getType() 23 | { 24 | return 'conferences'; 25 | } 26 | 27 | public function attributes() 28 | { 29 | return [ 30 | 'title' => $this->conference->title, 31 | 'description' => $this->conference->description, 32 | 'url' => $this->conference->url, 33 | 'cfp_url' => $this->conference->cfp_url, 34 | 'starts_at' => (string) $this->conference->starts_at, 35 | 'ends_at' => (string) $this->conference->ends_at, 36 | 'cfp_starts_at' => (string) $this->conference->cfp_starts_at, 37 | 'cfp_ends_at' => (string) $this->conference->cfp_ends_at, 38 | ]; 39 | } 40 | 41 | public function toArray() 42 | { 43 | return [ 44 | 'id' => $this->getId(), 45 | 'type' => $this->getType(), 46 | 'attributes' => $this->attributes(), 47 | ]; 48 | } 49 | } 50 | --------------------------------------------------------------------------------