1
0
Эх сурвалжийг харах

Update Settings, add default license and enforced media descriptions

Daniel Supernault 4 жил өмнө
parent
commit
67e3f6048f

+ 23 - 1
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
+			];
+		});
+	}
 }

+ 48 - 2
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!');
 	}
 
 }

+ 46 - 0
database/migrations/2021_07_23_062326_add_compose_settings_to_user_settings_table.php

@@ -0,0 +1,46 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddComposeSettingsToUserSettingsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('user_settings', function (Blueprint $table) {
+            $table->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');
+        });
+    }
+}

+ 5 - 10
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) {

+ 73 - 20
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}}
 							</div>
 						</div>
@@ -336,7 +336,13 @@
 							<p class="px-4 mb-0 py-2 cursor-pointer" @click="showTagCard()">Tag people</p>
 						</div>
 						<div class="border-bottom">
-							<p class="px-4 mb-0 py-2 cursor-pointer" @click="showLicenseCard()">Add license <span class="ml-2 badge badge-primary">NEW</span></p>
+							<p class="px-4 mb-0 py-2 cursor-pointer" @click="showLicenseCard()">
+								<span>Add license <span class="ml-2 badge badge-primary">NEW</span></span>
+								<span class="float-right">
+									<a v-if="licenseTitle" href="#" @click.prevent="showLicenseCard()" class="btn btn-outline-secondary btn-sm small mr-3 mt-n1 disabled" style="font-size:10px;padding:3px;text-transform: uppercase" disabled>{{licenseTitle}}</a>
+									<a href="#" @click.prevent="showLicenseCard()" class="text-decoration-none"><i class="fas fa-chevron-right fa-lg text-lighter"></i></a>
+								</span>
+							</p>
 						</div>
 						<div class="border-bottom">
 							<p class="px-4 mb-0 py-2 cursor-pointer" @click="showLocationCard()" v-if="!place">Add location</p>
@@ -591,11 +597,11 @@
 										<span></span>
 										<span>{{media[carouselCursor].license ? media[carouselCursor].license.length : 0}}/140</span>
 									</p> -->
-									<select class="form-control" v-model="licenseIndex">
+									<select class="form-control" v-model="licenseId">
 										<option
 											v-for="(item, index) in availableLicenses"
-											:value="index"
-											:selected="index === licenseIndex">
+											:value="item.id"
+											:selected="item.id == licenseId">
 											{{item.name}}
 										</option>
 									</select>
@@ -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':

+ 2 - 2
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) {

+ 49 - 0
resources/views/settings/media.blade.php

@@ -0,0 +1,49 @@
+@extends('settings.template')
+
+@section('section')
+
+	<div class="title">
+		<h3 class="font-weight-bold">Media</h3>
+	</div>
+	<hr>
+	<form method="post">
+		@csrf
+		<div class="form-group pb-3">
+			<label class="form-check-label font-weight-bold" for="">Default License</label>
+			<select class="form-control" name="default">
+				@foreach(App\Util\Media\License::get() as $license)
+				<option value="{{$license['id']}}" {{$compose['default_license'] == $license['id'] ? 'selected':''}}>
+					{{$license['name']}}
+					@if($license['id'] > 10)
+					({{$license['title']}})
+					@endif
+				</option>
+				@endforeach
+			</select>
+			<p class="text-muted small help-text">Set a default license for new posts.</p>
+		</div>
+
+		<div class="form-check pb-3">
+			<input class="form-check-input" type="checkbox" name="sync">
+			<label class="form-check-label font-weight-bold" for="">Sync Licenses</label>
+			<p class="text-muted small help-text">Update existing posts with your new default license. You can sync once every 24 hours.</p>
+		</div>
+
+		<div class="form-check pb-3">
+			<input class="form-check-input" type="checkbox" name="media_descriptions" {{$compose['media_descriptions'] == $license['id'] ? 'checked':''}}>
+			<label class="form-check-label font-weight-bold" for="">Require Media Descriptions</label>
+			<p class="text-muted small help-text">
+				Briefly describe your media to improve accessibility for vision impaired people. <br />
+				<span class="font-weight-bold">Not available for mobile or 3rd party apps at this time.</span>
+			</p>
+		</div>
+
+		<div class="form-group row mt-5 pt-5">
+			<div class="col-12 text-right">
+				<hr>
+				<button type="submit" class="btn btn-primary font-weight-bold py-0 px-5">Submit</button>
+			</div>
+		</div>
+	</form>
+
+@endsection

+ 3 - 0
resources/views/settings/partial/sidebar.blade.php

@@ -14,6 +14,9 @@
 				<a class="nav-link font-weight-light text-muted" href="{{route('settings.invites')}}">Invites</a>
 			</li>
 			@endif
+			<li class="nav-item pl-3 {{request()->is('settings/media*')?'active':''}}">
+				<a class="nav-link font-weight-light text-muted" href="{{route('settings.media')}}">Media</a>
+			</li>
 			<li class="nav-item pl-3 {{request()->is('settings/notifications')?'active':''}}">
 				<a class="nav-link font-weight-light text-muted" href="{{route('settings.notifications')}}">Notifications</a>
 			</li>

+ 3 - 0
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 () {