feat(server,web,mobile): Add optional password option for share links. (#4655)

* feat(server,web,mobile): Add optional password option for share links.

Signed-off-by: jarvis2f <137974272+jarvis2f@users.noreply.github.com>

* feat(server,web): Update shared-link.controller and page.svelte for improved cookie handling and metadata updates.

Signed-off-by: jarvis2f <137974272+jarvis2f@users.noreply.github.com>

---------

Signed-off-by: jarvis2f <137974272+jarvis2f@users.noreply.github.com>
This commit is contained in:
jarvis2f
2023-10-29 09:35:38 +08:00
committed by GitHub
parent b34cbd881a
commit 8a6889529c
33 changed files with 556 additions and 41 deletions

View File

@@ -311,6 +311,8 @@
"shared_link_edit_change_expiry": "Change expiration time",
"shared_link_edit_description": "Description",
"shared_link_edit_description_hint": "Enter the share description",
"shared_link_edit_password": "Password",
"shared_link_edit_password_hint": "Enter the share password",
"shared_link_edit_show_meta": "Show metadata",
"shared_link_edit_submit_button": "Update link",
"shared_link_empty": "You don't have any shared links",

View File

@@ -9,6 +9,7 @@ class SharedLink {
final bool allowUpload;
final String? thumbAssetId;
final String? description;
final String? password;
final DateTime? expiresAt;
final String key;
final bool showMetadata;
@@ -21,6 +22,7 @@ class SharedLink {
required this.allowUpload,
required this.thumbAssetId,
required this.description,
required this.password,
required this.expiresAt,
required this.key,
required this.showMetadata,
@@ -34,6 +36,7 @@ class SharedLink {
bool? allowDownload,
bool? allowUpload,
String? description,
String? password,
DateTime? expiresAt,
String? key,
bool? showMetadata,
@@ -46,6 +49,7 @@ class SharedLink {
allowDownload: allowDownload ?? this.allowDownload,
allowUpload: allowUpload ?? this.allowUpload,
description: description ?? this.description,
password: password ?? this.password,
expiresAt: expiresAt ?? this.expiresAt,
key: key ?? this.key,
showMetadata: showMetadata ?? this.showMetadata,
@@ -58,6 +62,7 @@ class SharedLink {
allowDownload = dto.allowDownload,
allowUpload = dto.allowUpload,
description = dto.description,
password = dto.password,
expiresAt = dto.expiresAt,
key = dto.key,
showMetadata = dto.showMetadata,
@@ -75,7 +80,7 @@ class SharedLink {
@override
String toString() =>
'SharedLink(id=$id, title=$title, thumbAssetId=$thumbAssetId, allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, expiresAt=$expiresAt, key=$key, showMetadata=$showMetadata, type=$type)';
'SharedLink(id=$id, title=$title, thumbAssetId=$thumbAssetId, allowDownload=$allowDownload, allowUpload=$allowUpload, description=$description, password=$password, expiresAt=$expiresAt, key=$key, showMetadata=$showMetadata, type=$type)';
@override
bool operator ==(Object other) =>
@@ -87,6 +92,7 @@ class SharedLink {
other.allowDownload == allowDownload &&
other.allowUpload == allowUpload &&
other.description == description &&
other.password == password &&
other.expiresAt == expiresAt &&
other.key == key &&
other.showMetadata == showMetadata &&
@@ -100,6 +106,7 @@ class SharedLink {
allowDownload.hashCode ^
allowUpload.hashCode ^
description.hashCode ^
password.hashCode ^
expiresAt.hashCode ^
key.hashCode ^
showMetadata.hashCode ^

View File

@@ -40,6 +40,7 @@ class SharedLinkService {
required bool allowDownload,
required bool allowUpload,
String? description,
String? password,
String? albumId,
List<String>? assetIds,
DateTime? expiresAt,
@@ -57,6 +58,7 @@ class SharedLinkService {
allowUpload: allowUpload,
expiresAt: expiresAt,
description: description,
password: password,
);
} else if (assetIds != null) {
dto = SharedLinkCreateDto(
@@ -66,6 +68,7 @@ class SharedLinkService {
allowUpload: allowUpload,
expiresAt: expiresAt,
description: description,
password: password,
assetIds: assetIds,
);
}
@@ -90,6 +93,7 @@ class SharedLinkService {
required bool? allowUpload,
bool? changeExpiry = false,
String? description,
String? password,
DateTime? expiresAt,
}) async {
try {
@@ -101,6 +105,7 @@ class SharedLinkService {
allowUpload: allowUpload,
expiresAt: expiresAt,
description: description,
password: password,
changeExpiryTime: changeExpiry,
),
);

View File

@@ -30,6 +30,8 @@ class SharedLinkEditPage extends HookConsumerWidget {
final descriptionController =
useTextEditingController(text: existingLink?.description ?? "");
final descriptionFocusNode = useFocusNode();
final passwordController =
useTextEditingController(text: existingLink?.password ?? "");
final showMetadata = useState(existingLink?.showMetadata ?? true);
final allowDownload = useState(existingLink?.allowDownload ?? true);
final allowUpload = useState(existingLink?.allowUpload ?? false);
@@ -113,6 +115,31 @@ class SharedLinkEditPage extends HookConsumerWidget {
);
}
Widget buildPasswordField() {
return TextField(
controller: passwordController,
enabled: newShareLink.value.isEmpty,
autofocus: false,
decoration: InputDecoration(
labelText: 'shared_link_edit_password'.tr(),
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: themeData.primaryColor,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
border: const OutlineInputBorder(),
hintText: 'shared_link_edit_password_hint'.tr(),
hintStyle: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 14,
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.withOpacity(0.5)),
),
),
);
}
Widget buildShowMetaButton() {
return SwitchListTile.adaptive(
value: showMetadata.value,
@@ -229,7 +256,9 @@ class SharedLinkEditPage extends HookConsumerWidget {
void copyLinkToClipboard() {
Clipboard.setData(
ClipboardData(
text: newShareLink.value,
text: passwordController.text.isEmpty
? newShareLink.value
: "Link: ${newShareLink.value}\nPassword: ${passwordController.text}",
),
).then((_) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -302,6 +331,9 @@ class SharedLinkEditPage extends HookConsumerWidget {
description: descriptionController.text.isEmpty
? null
: descriptionController.text,
password: passwordController.text.isEmpty
? null
: passwordController.text,
expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(),
);
ref.invalidate(sharedLinksStateProvider);
@@ -324,6 +356,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
bool? upload;
bool? meta;
String? desc;
String? password;
DateTime? expiry;
bool? changeExpiry;
@@ -343,6 +376,10 @@ class SharedLinkEditPage extends HookConsumerWidget {
desc = descriptionController.text;
}
if (passwordController.text != existingLink!.password) {
password = passwordController.text;
}
if (editExpiry.value) {
expiry = expiryAfter.value == 0 ? null : calculateExpiry();
changeExpiry = true;
@@ -354,6 +391,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
allowDownload: download,
allowUpload: upload,
description: desc,
password: password,
expiresAt: expiry,
changeExpiry: changeExpiry,
);
@@ -385,6 +423,10 @@ class SharedLinkEditPage extends HookConsumerWidget {
padding: const EdgeInsets.all(padding),
child: buildDescriptionField(),
),
Padding(
padding: const EdgeInsets.all(padding),
child: buildPasswordField(),
),
Padding(
padding: const EdgeInsets.only(
left: padding,

View File

@@ -185,7 +185,7 @@ This endpoint does not need any parameter.
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getMySharedLink**
> SharedLinkResponseDto getMySharedLink(key)
> SharedLinkResponseDto getMySharedLink(password, token, key)
@@ -208,10 +208,12 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SharedLinkApi();
final password = password; // String |
final token = token_example; // String |
final key = key_example; // String |
try {
final result = api_instance.getMySharedLink(key);
final result = api_instance.getMySharedLink(password, token, key);
print(result);
} catch (e) {
print('Exception when calling SharedLinkApi->getMySharedLink: $e\n');
@@ -222,6 +224,8 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**password** | **String**| | [optional]
**token** | **String**| | [optional]
**key** | **String**| | [optional]
### Return type

View File

@@ -14,6 +14,7 @@ Name | Type | Description | Notes
**assetIds** | **List<String>** | | [optional] [default to const []]
**description** | **String** | | [optional]
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
**password** | **String** | | [optional]
**showMetadata** | **bool** | | [optional] [default to true]
**type** | [**SharedLinkType**](SharedLinkType.md) | |

View File

@@ -13,6 +13,7 @@ Name | Type | Description | Notes
**changeExpiryTime** | **bool** | Few clients cannot send null to set the expiryTime to never. Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. | [optional]
**description** | **String** | | [optional]
**expiresAt** | [**DateTime**](DateTime.md) | | [optional]
**password** | **String** | | [optional]
**showMetadata** | **bool** | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -17,7 +17,9 @@ Name | Type | Description | Notes
**expiresAt** | [**DateTime**](DateTime.md) | |
**id** | **String** | |
**key** | **String** | |
**password** | **String** | |
**showMetadata** | **bool** | |
**token** | **String** | | [optional]
**type** | [**SharedLinkType**](SharedLinkType.md) | |
**userId** | **String** | |

View File

@@ -173,8 +173,12 @@ class SharedLinkApi {
/// Performs an HTTP 'GET /shared-link/me' operation and returns the [Response].
/// Parameters:
///
/// * [String] password:
///
/// * [String] token:
///
/// * [String] key:
Future<Response> getMySharedLinkWithHttpInfo({ String? key, }) async {
Future<Response> getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, }) async {
// ignore: prefer_const_declarations
final path = r'/shared-link/me';
@@ -185,6 +189,12 @@ class SharedLinkApi {
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (password != null) {
queryParams.addAll(_queryParams('', 'password', password));
}
if (token != null) {
queryParams.addAll(_queryParams('', 'token', token));
}
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
@@ -205,9 +215,13 @@ class SharedLinkApi {
/// Parameters:
///
/// * [String] password:
///
/// * [String] token:
///
/// * [String] key:
Future<SharedLinkResponseDto?> getMySharedLink({ String? key, }) async {
final response = await getMySharedLinkWithHttpInfo( key: key, );
Future<SharedLinkResponseDto?> getMySharedLink({ String? password, String? token, String? key, }) async {
final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}

View File

@@ -19,6 +19,7 @@ class SharedLinkCreateDto {
this.assetIds = const [],
this.description,
this.expiresAt,
this.password,
this.showMetadata = true,
required this.type,
});
@@ -47,6 +48,14 @@ class SharedLinkCreateDto {
DateTime? expiresAt;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? password;
bool showMetadata;
SharedLinkType type;
@@ -59,6 +68,7 @@ class SharedLinkCreateDto {
other.assetIds == assetIds &&
other.description == description &&
other.expiresAt == expiresAt &&
other.password == password &&
other.showMetadata == showMetadata &&
other.type == type;
@@ -71,11 +81,12 @@ class SharedLinkCreateDto {
(assetIds.hashCode) +
(description == null ? 0 : description!.hashCode) +
(expiresAt == null ? 0 : expiresAt!.hashCode) +
(password == null ? 0 : password!.hashCode) +
(showMetadata.hashCode) +
(type.hashCode);
@override
String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata, type=$type]';
String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, type=$type]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -96,6 +107,11 @@ class SharedLinkCreateDto {
json[r'expiresAt'] = this.expiresAt!.toUtc().toIso8601String();
} else {
// json[r'expiresAt'] = null;
}
if (this.password != null) {
json[r'password'] = this.password;
} else {
// json[r'password'] = null;
}
json[r'showMetadata'] = this.showMetadata;
json[r'type'] = this.type;
@@ -118,6 +134,7 @@ class SharedLinkCreateDto {
: const [],
description: mapValueOfType<String>(json, r'description'),
expiresAt: mapDateTime(json, r'expiresAt', ''),
password: mapValueOfType<String>(json, r'password'),
showMetadata: mapValueOfType<bool>(json, r'showMetadata') ?? true,
type: SharedLinkType.fromJson(json[r'type'])!,
);

View File

@@ -18,6 +18,7 @@ class SharedLinkEditDto {
this.changeExpiryTime,
this.description,
this.expiresAt,
this.password,
this.showMetadata,
});
@@ -56,6 +57,14 @@ class SharedLinkEditDto {
DateTime? expiresAt;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? password;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@@ -71,6 +80,7 @@ class SharedLinkEditDto {
other.changeExpiryTime == changeExpiryTime &&
other.description == description &&
other.expiresAt == expiresAt &&
other.password == password &&
other.showMetadata == showMetadata;
@override
@@ -81,10 +91,11 @@ class SharedLinkEditDto {
(changeExpiryTime == null ? 0 : changeExpiryTime!.hashCode) +
(description == null ? 0 : description!.hashCode) +
(expiresAt == null ? 0 : expiresAt!.hashCode) +
(password == null ? 0 : password!.hashCode) +
(showMetadata == null ? 0 : showMetadata!.hashCode);
@override
String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, showMetadata=$showMetadata]';
String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -113,6 +124,11 @@ class SharedLinkEditDto {
} else {
// json[r'expiresAt'] = null;
}
if (this.password != null) {
json[r'password'] = this.password;
} else {
// json[r'password'] = null;
}
if (this.showMetadata != null) {
json[r'showMetadata'] = this.showMetadata;
} else {
@@ -134,6 +150,7 @@ class SharedLinkEditDto {
changeExpiryTime: mapValueOfType<bool>(json, r'changeExpiryTime'),
description: mapValueOfType<String>(json, r'description'),
expiresAt: mapDateTime(json, r'expiresAt', ''),
password: mapValueOfType<String>(json, r'password'),
showMetadata: mapValueOfType<bool>(json, r'showMetadata'),
);
}

View File

@@ -22,7 +22,9 @@ class SharedLinkResponseDto {
required this.expiresAt,
required this.id,
required this.key,
required this.password,
required this.showMetadata,
this.token,
required this.type,
required this.userId,
});
@@ -51,8 +53,12 @@ class SharedLinkResponseDto {
String key;
String? password;
bool showMetadata;
String? token;
SharedLinkType type;
String userId;
@@ -68,7 +74,9 @@ class SharedLinkResponseDto {
other.expiresAt == expiresAt &&
other.id == id &&
other.key == key &&
other.password == password &&
other.showMetadata == showMetadata &&
other.token == token &&
other.type == type &&
other.userId == userId;
@@ -84,12 +92,14 @@ class SharedLinkResponseDto {
(expiresAt == null ? 0 : expiresAt!.hashCode) +
(id.hashCode) +
(key.hashCode) +
(password == null ? 0 : password!.hashCode) +
(showMetadata.hashCode) +
(token == null ? 0 : token!.hashCode) +
(type.hashCode) +
(userId.hashCode);
@override
String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, showMetadata=$showMetadata, type=$type, userId=$userId]';
String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, token=$token, type=$type, userId=$userId]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -114,7 +124,17 @@ class SharedLinkResponseDto {
}
json[r'id'] = this.id;
json[r'key'] = this.key;
if (this.password != null) {
json[r'password'] = this.password;
} else {
// json[r'password'] = null;
}
json[r'showMetadata'] = this.showMetadata;
if (this.token != null) {
json[r'token'] = this.token;
} else {
// json[r'token'] = null;
}
json[r'type'] = this.type;
json[r'userId'] = this.userId;
return json;
@@ -137,7 +157,9 @@ class SharedLinkResponseDto {
expiresAt: mapDateTime(json, r'expiresAt', ''),
id: mapValueOfType<String>(json, r'id')!,
key: mapValueOfType<String>(json, r'key')!,
password: mapValueOfType<String>(json, r'password'),
showMetadata: mapValueOfType<bool>(json, r'showMetadata')!,
token: mapValueOfType<String>(json, r'token'),
type: SharedLinkType.fromJson(json[r'type'])!,
userId: mapValueOfType<String>(json, r'userId')!,
);
@@ -195,6 +217,7 @@ class SharedLinkResponseDto {
'expiresAt',
'id',
'key',
'password',
'showMetadata',
'type',
'userId',

View File

@@ -32,7 +32,7 @@ void main() {
// TODO
});
//Future<SharedLinkResponseDto> getMySharedLink({ String key }) async
//Future<SharedLinkResponseDto> getMySharedLink({ String password, String token, String key }) async
test('test getMySharedLink', () async {
// TODO
});

View File

@@ -46,6 +46,11 @@ void main() {
// TODO
});
// String password
test('to test the property `password`', () async {
// TODO
});
// bool showMetadata (default value: true)
test('to test the property `showMetadata`', () async {
// TODO

View File

@@ -42,6 +42,11 @@ void main() {
// TODO
});
// String password
test('to test the property `password`', () async {
// TODO
});
// bool showMetadata
test('to test the property `showMetadata`', () async {
// TODO

View File

@@ -61,11 +61,21 @@ void main() {
// TODO
});
// String password
test('to test the property `password`', () async {
// TODO
});
// bool showMetadata
test('to test the property `showMetadata`', () async {
// TODO
});
// String token
test('to test the property `token`', () async {
// TODO
});
// SharedLinkType type
test('to test the property `type`', () async {
// TODO