From 67e3f6048f52583f0db3a0d61dfd4fc0ef4c670c Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 23 Jul 2021 09:47:14 -0600 Subject: [PATCH] Update Settings, add default license and enforced media descriptions --- app/Http/Controllers/ComposeController.php | 24 ++++- app/Http/Controllers/SettingsController.php | 50 +++++++++- ...ompose_settings_to_user_settings_table.php | 46 +++++++++ resources/assets/js/components/Activity.vue | 15 +-- .../assets/js/components/ComposeModal.vue | 93 +++++++++++++++---- .../assets/js/components/NotificationCard.vue | 4 +- resources/views/settings/media.blade.php | 49 ++++++++++ .../views/settings/partial/sidebar.blade.php | 3 + routes/web.php | 3 + 9 files changed, 252 insertions(+), 35 deletions(-) create mode 100644 database/migrations/2021_07_23_062326_add_compose_settings_to_user_settings_table.php create mode 100644 resources/views/settings/media.blade.php diff --git a/app/Http/Controllers/ComposeController.php b/app/Http/Controllers/ComposeController.php index b20afab99..56e137441 100644 --- a/app/Http/Controllers/ComposeController.php +++ b/app/Http/Controllers/ComposeController.php @@ -15,7 +15,8 @@ use App\{ Profile, Place, Status, - UserFilter + UserFilter, + UserSetting }; use App\Transformer\Api\{ MediaTransformer, @@ -661,4 +662,25 @@ class ComposeController extends Controller 'finished' => $finished ]; } + + public function composeSettings(Request $request) + { + $uid = $request->user()->id; + + return Cache::remember('profile:compose:settings:' . $uid, now()->addHours(12), function() use($uid) { + $res = UserSetting::whereUserId($uid)->first(); + + if(!$res) { + return [ + 'default_license' => null, + 'media_descriptions' => false + ]; + } + + return json_decode($res->compose_settings, true) ?? [ + 'default_license' => null, + 'media_descriptions' => false + ]; + }); + } } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 65dfca5d0..3f8ef9879 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -7,6 +7,7 @@ use App\Following; use App\ProfileSponsor; use App\Report; use App\UserFilter; +use App\UserSetting; use Auth, Cookie, DB, Cache, Purify; use Illuminate\Support\Facades\Redis; use Carbon\Carbon; @@ -221,7 +222,7 @@ class SettingsController extends Controller $sponsors->sponsors = json_encode($res); $sponsors->save(); $sponsors = $res; - return redirect(route('settings'))->with('status', 'Sponsor settings successfully updated!');; + return redirect(route('settings'))->with('status', 'Sponsor settings successfully updated!'); } public function timelineSettings(Request $request) @@ -249,7 +250,52 @@ class SettingsController extends Controller } else { Redis::zrem('pf:tl:replies', $pid); } - return redirect(route('settings.timeline')); + return redirect(route('settings'))->with('status', 'Timeline settings successfully updated!');; + } + + public function mediaSettings(Request $request) + { + $setting = UserSetting::whereUserId($request->user()->id)->firstOrFail(); + $compose = $setting->compose_settings ? json_decode($setting->compose_settings, true) : [ + 'default_license' => null, + 'media_descriptions' => false + ]; + return view('settings.media', compact('compose')); + } + + public function updateMediaSettings(Request $request) + { + $this->validate($request, [ + 'default' => 'required|int|min:1|max:16', + 'sync' => 'nullable', + 'media_descriptions' => 'nullable' + ]); + + $license = $request->input('default'); + $sync = $request->input('sync') == 'on'; + $media_descriptions = $request->input('media_descriptions') == 'on'; + + $setting = UserSetting::whereUserId($request->user()->id)->firstOrFail(); + $compose = json_decode($setting->compose_settings, true); + $changed = false; + + if(!isset($compose['default_license']) || $compose['default_license'] !== $license) { + $compose['default_license'] = (int) $license; + $changed = true; + } + + if(!isset($compose['media_descriptions']) || $compose['media_descriptions'] !== $media_descriptions) { + $compose['media_descriptions'] = $media_descriptions; + $changed = true; + } + + if($changed) { + $setting->compose_settings = json_encode($compose); + $setting->save(); + Cache::forget('profile:compose:settings:' . $request->user()->id); + } + + return redirect(route('settings'))->with('status', 'Media settings successfully updated!'); } } diff --git a/database/migrations/2021_07_23_062326_add_compose_settings_to_user_settings_table.php b/database/migrations/2021_07_23_062326_add_compose_settings_to_user_settings_table.php new file mode 100644 index 000000000..58837cab3 --- /dev/null +++ b/database/migrations/2021_07_23_062326_add_compose_settings_to_user_settings_table.php @@ -0,0 +1,46 @@ +json('compose_settings')->nullable(); + }); + + Schema::table('media', function (Blueprint $table) { + $table->text('caption')->change(); + $table->index('profile_id'); + $table->index('mime'); + $table->index('license'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('user_settings', function (Blueprint $table) { + $table->dropColumn('compose_settings'); + }); + + Schema::table('media', function (Blueprint $table) { + $table->string('caption')->change(); + $table->dropIndex('profile_id'); + $table->dropIndex('mime'); + $table->dropIndex('license'); + }); + } +} diff --git a/resources/assets/js/components/Activity.vue b/resources/assets/js/components/Activity.vue index a5ef1525b..751cab0ae 100644 --- a/resources/assets/js/components/Activity.vue +++ b/resources/assets/js/components/Activity.vue @@ -134,20 +134,16 @@ export default { window._sharedData.curUser = res.data; window.App.util.navatar(); }); - axios.get('/api/pixelfed/v1/notifications', { - params: { - pg: true - } - }) + axios.get('/api/pixelfed/v1/notifications?pg=true') .then(res => { let data = res.data.filter(n => { - if(n.type == 'share' && !status) { + if(n.type == 'share' && !n.status) { return false; } - if(n.type == 'comment' && !status) { + if(n.type == 'comment' && !n.status) { return false; } - if(n.type == 'mention' && !status) { + if(n.type == 'mention' && !n.status) { return false; } return true; @@ -167,8 +163,7 @@ export default { } axios.get('/api/pixelfed/v1/notifications', { params: { - pg: true, - page: this.notificationCursor + max_id: this.notificationMaxId } }).then(res => { if(res.data.length) { diff --git a/resources/assets/js/components/ComposeModal.vue b/resources/assets/js/components/ComposeModal.vue index 0aa4f8b63..893bacf92 100644 --- a/resources/assets/js/components/ComposeModal.vue +++ b/resources/assets/js/components/ComposeModal.vue @@ -100,10 +100,10 @@ v-for="(item, index) in availableLicenses" class="list-group-item cursor-pointer" :class="{ - 'text-primary': licenseIndex === index, - 'font-weight-bold': licenseIndex === index + 'text-primary': licenseId === item.id, + 'font-weight-bold': licenseId === item.id }" - @click="toggleLicense(index)"> + @click="toggleLicense(item)"> {{item.name}} @@ -336,7 +336,13 @@

Tag people

-

Add license NEW

+

+ Add license NEW + + {{licenseTitle}} + + +

Add location

@@ -591,11 +597,11 @@ {{media[carouselCursor].license ? media[carouselCursor].license.length : 0}}/140

--> - @@ -845,52 +851,79 @@ export default { availableLicenses: [ { id: 1, - name: "All Rights Reserved" + name: "All Rights Reserved", + title: "" }, { id: 5, - name: "Public Domain Work" + name: "Public Domain Work", + title: "" }, { id: 6, - name: "Public Domain Dedication (CC0)" + name: "Public Domain Dedication (CC0)", + title: "CC0" }, { id: 11, - name: "Attribution" + name: "Attribution", + title: "CC BY" }, { id: 12, - name: "Attribution-ShareAlike" + name: "Attribution-ShareAlike", + title: "CC BY-SA" }, { id: 13, - name: "Attribution-NonCommercial" + name: "Attribution-NonCommercial", + title: "CC BY-NC" }, { id: 14, - name: "Attribution-NonCommercial-ShareAlike" + name: "Attribution-NonCommercial-ShareAlike", + title: "CC BY-NC-SA" }, { id: 15, - name: "Attribution-NoDerivs" + name: "Attribution-NoDerivs", + title: "CC BY-ND" }, { id: 16, - name: "Attribution-NonCommercial-NoDerivs" + name: "Attribution-NonCommercial-NoDerivs", + title: "CC BY-NC-ND" } ], licenseIndex: 0, video: { title: '', description: '' - } + }, + composeSettings: { + default_license: null, + media_descriptions: false + }, + licenseId: null, + licenseTitle: null } }, beforeMount() { this.fetchProfile(); this.filters = window.App.util.filters; + axios.get('/api/compose/v0/settings') + .then(res => { + this.composeSettings = res.data; + this.licenseId = this.composeSettings.default_license; + if(this.licenseId > 10) { + this.licenseTitle = this.availableLicenses.filter(l => { + return l.id == this.licenseId; + }).map(l => { + return l.title; + })[0]; + } + }); }, mounted() { @@ -1064,6 +1097,16 @@ export default { switch(state) { case 'publish' : + if(this.composeSettings.media_descriptions === true) { + let count = this.media.filter(m => { + return !m.hasOwnProperty('alt') || m.alt.length < 2; + }); + + if(count.length) { + swal('Missing media descriptions', 'You have enabled mandatory media descriptions. Please add media descriptions under Advanced settings to proceed. For more information, please see the media settings page.', 'warning'); + return; + } + } if(this.media.length == 0) { swal('Whoops!', 'You need to add media before you can save this!', 'warning'); return; @@ -1080,7 +1123,7 @@ export default { place: this.place, tagged: this.taggedUsernames, optimize_media: this.optimizeMedia, - license: this.availableLicenses[this.licenseIndex].id, + license: this.licenseId, video: this.video }; axios.post('/api/compose/v0/publish', data) @@ -1515,8 +1558,18 @@ export default { this.page = 'licensePicker'; }, - toggleLicense(index) { - this.licenseIndex = index; + toggleLicense(license) { + this.licenseId = license.id; + + if(this.licenseId > 10) { + this.licenseTitle = this.availableLicenses.filter(l => { + return l.id == this.licenseId; + }).map(l => { + return l.title; + })[0]; + } else { + this.licenseTitle = null; + } switch(this.mode) { case 'photo': diff --git a/resources/assets/js/components/NotificationCard.vue b/resources/assets/js/components/NotificationCard.vue index a52bde62f..db4139cc6 100644 --- a/resources/assets/js/components/NotificationCard.vue +++ b/resources/assets/js/components/NotificationCard.vue @@ -120,7 +120,7 @@ setTimeout(function() { self.profile = window._sharedData.curUser; self.fetchFollowRequests(); - }, 500); + }, 1500); }, updated() { @@ -157,7 +157,7 @@ } axios.get('/api/pixelfed/v1/notifications', { params: { - page: this.notificationCursor + max_id: this.notificationMaxId } }).then(res => { if(res.data.length) { diff --git a/resources/views/settings/media.blade.php b/resources/views/settings/media.blade.php new file mode 100644 index 000000000..4375ecd9c --- /dev/null +++ b/resources/views/settings/media.blade.php @@ -0,0 +1,49 @@ +@extends('settings.template') + +@section('section') + +
+

Media

+
+
+
+ @csrf +
+ + +

Set a default license for new posts.

+
+ +
+ + +

Update existing posts with your new default license. You can sync once every 24 hours.

+
+ +
+ + +

+ Briefly describe your media to improve accessibility for vision impaired people.
+ Not available for mobile or 3rd party apps at this time. +

+
+ +
+
+
+ +
+
+
+ +@endsection diff --git a/resources/views/settings/partial/sidebar.blade.php b/resources/views/settings/partial/sidebar.blade.php index 2be95ca2a..34f12971b 100644 --- a/resources/views/settings/partial/sidebar.blade.php +++ b/resources/views/settings/partial/sidebar.blade.php @@ -14,6 +14,9 @@ Invites @endif + diff --git a/routes/web.php b/routes/web.php index 2300a5ca8..77e834b2f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -119,6 +119,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::post('/publish', 'ComposeController@store'); Route::post('/publish/text', 'ComposeController@storeText'); Route::get('/media/processing', 'ComposeController@mediaProcessingCheck'); + Route::get('/settings', 'ComposeController@composeSettings'); }); }); @@ -429,6 +430,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::get('timeline', 'SettingsController@timelineSettings')->name('settings.timeline'); Route::post('timeline', 'SettingsController@updateTimelineSettings'); + Route::get('media', 'SettingsController@mediaSettings')->name('settings.media'); + Route::post('media', 'SettingsController@updateMediaSettings'); }); Route::group(['prefix' => 'site'], function () {