Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ abstract class CommonConfig(resources: GridConfigResources) extends AwsClientV1B
* {
* name = "field-a"
* label = "Field A"
* key = "Search field key for A" (optional)
* type = "string" # type can either be string, integer, select or datetime
* }
* {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.jdk.CollectionConverters._
case class DomainMetadataField(
name: String,
label: String,
key: Option[String] = None,
fieldType: String,
options: Option[List[String]] = None
)
Expand All @@ -32,10 +33,12 @@ object DomainMetadataSpec {
val description = if (config.hasPath("description")) Some(config.getString("description")) else None
val fields = config.getConfigList("fields").asScala.map(config => {
val fieldOptions = if (config.hasPath("options")) Some(config.getStringList("options").asScala.toList) else None
val key = if (config.hasPath("key")) Some(config.getString("key")) else None

DomainMetadataField(
config.getString("name"),
config.getString("label"),
key,
config.getString("type"),
fieldOptions
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,24 @@
e:form="domainMetadataEditForm">

<span ng-if="specField.value">
{{specField.value}}
<span ng-if="specField.key">
<span ng-if="specField.key == 'id'">
<a ui-sref="image({imageId: specField.value})"
aria-label="Goto image with id '{{specField.value}}'">
{{specField.value}}
</a>
</span>
<span ng-if="specField.key != 'id'">
<a ui-sref="search.results({query: (specField.value | queryFilter:'{{specField.key}}'), nonFree: ctrl.srefNonfree()})"
ng-click="ctrl.searchWithModifiers($event, specField.key, specField.value)"
aria-label="Search images with {{specField.key}} '{{specField.value}}'">
{{specField.value}}
</a>
</span>
</span>
<span ng-if="!specField.key">
{{specField.value}}
</span>
</span>
<span class="editable-empty" ng-if="! specField.value && ctrl.userCanEdit">
Unknown (click ✎ to add)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ gr-image-usage-list .gr-image-usage__record {
width: 90%;
}

.gr-image-usage__message {
margin-bottom: 10px;
width: 90%;
}

gr-image-usage-list .source-list__icon svg {
padding: 0 2px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
</div>
</div>

<div class="image-info__group" ng-if="ctrl.displayLinkedImagesMessage">
<div class="gr-image-usage__message" ng-repeat="message in ctrl.linkedImagesMessage">
<span>{{message}}</span>
</div>
</div>

<gr-delete-usages
ng-if="(ctrl.usagesCount > 0 && !(ctrl.showSendToPhotoSales && ctrl.hasSyndicationUsages))"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {deleteUsages} from '../gr-delete-usages/gr-delete-usages';

import '../gr-image-usage-photosales/gr-image-usage-photosales';
import {sendToCaptureUsagePanelTxt} from "../../util/constants/sendToCapture-config";
import {displayLinkedImagesMessage, linkedImagesMessage} from "../../util/linkedImages";

export const module = angular.module('gr.imageUsage', [
'gr.image-usages.service',
Expand All @@ -33,6 +34,9 @@ module.controller('grImageUsageCtrl', [

ctrl.$onInit = () => {
ctrl.showSendToPhotoSales = $window._clientConfig.showSendToPhotoSales;
const org = $window._clientConfig.staffPhotographerOrganisation;
ctrl.displayLinkedImagesMessage = displayLinkedImagesMessage(ctrl.image, org);
ctrl.linkedImagesMessage = linkedImagesMessage(ctrl.image, org);

const usages = imageUsagesService.getUsages(ctrl.image);
const usages$ = usages.groupedByState$.map((u) => u.toJS());
Expand Down
65 changes: 65 additions & 0 deletions kahuna/public/js/util/linkedImages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* ******************************************
Code to indicate if messaging relating to
linked images should be displayed
****************************************** */

const DEFAULT_LINKED_MESSAGE = "This image is linked to one or more equivalent images. To see usages for these linked images please use the links on the Metadata tab.";
const BBC_REPLACES_MESSAGE = "This image replaces a previous version. Super users can view the previous version’s usage by clicking the image ID under ‘Replaces’ in the metadata.";
const BBC_REPLACED_MESSAGE = "This image has been replaced by a newer version. Super users can view the latest version by clicking the image ID under ‘Replaced by’ in the metadata.";

const default_link_fields = [];
const bbc_link_fields = [
['data', 'metadata', 'domainMetadata', 'archives', 'supersedes'],
['data', 'metadata', 'domainMetadata', 'archives', 'superseded']
];

const checkValueAtPath = (obj, path) => {
for (let i = 0; i < path.length; i++) {
if (obj && obj.hasOwnProperty(path[i])) {
obj = obj[path[i]];
} else {
return false;
}
}
return obj !== undefined && obj !== null;
};

const checkFields = (image, fields) => {
let hasField = false;
for (let i = 0; i < fields.length; i++) {
if (checkValueAtPath(image, fields[i])) { hasField = true; }
}
return hasField;
};

const message = (image, org_name) => {
const messages = [];
switch (org_name.toLowerCase()) {
case 'bbc':
if (checkValueAtPath(image, bbc_link_fields[0])) {
messages.push(BBC_REPLACES_MESSAGE);
}
if (checkValueAtPath(image, bbc_link_fields[1])) {
messages.push(BBC_REPLACED_MESSAGE);
}
break;
default:
messages.push(DEFAULT_LINKED_MESSAGE);
break;
}
return messages;
};

export const displayLinkedImagesMessage = (image, org_name) => {
switch (org_name.toLowerCase()) {
case 'bbc':
return checkFields(image, bbc_link_fields);
default:
return checkFields(image, default_link_fields);
}
};

export const linkedImagesMessage = (image, org_name) => {
return message(image, org_name);
};

28 changes: 28 additions & 0 deletions media-api/app/controllers/MediaApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,34 @@ class MediaApi(
}
}

def forceDeleteImage(id: String) = auth.async { request =>
implicit val r: Request[AnyContent] = request
elasticSearch.getImageById(id) map {
case Some(image) if hasPermission(request.user, image) =>
val canDelete = authorisation.isUploaderOrHasPermission(request.user, image.uploadedBy, DeleteImagePermission)
if(canDelete){
val imageStatusRecord = ImageStatusRecord(id, request.user.accessor.identity, DateTime.now(DateTimeZone.UTC).toString, true)
softDeletedMetadataTable.setStatus(imageStatusRecord)
.map { _ =>
messageSender.publish(
UpdateMessage(
subject = SoftDeleteImage,
id = Some(id),
softDeletedMetadata = Some(SoftDeletedMetadata(
deleteTime = DateTime.now(DateTimeZone.UTC),
deletedBy = request.user.accessor.identity
))
)
)
}
Accepted
} else {
ImageDeleteForbidden
}
case _ => ImageNotFound(id)
}
}

def unSoftDeleteImage(id: String) = auth.async { request =>
implicit val r: Request[AnyContent] = request
elasticSearch.getImageById(id) map {
Expand Down
1 change: 1 addition & 0 deletions media-api/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ GET /images/:imageId/download controllers.
GET /images/:imageId/downloadOptimised controllers.MediaApi.downloadOptimisedImage(imageId: String, width: Int, height: Int, quality: Int)
POST /images/:id/:partnerName/:startPending/syndicateImage controllers.MediaApi.syndicateImage(id: String, partnerName: String, startPending:String)
DELETE /images/:id controllers.MediaApi.deleteImage(id: String)
DELETE /images/:id/force-delete controllers.MediaApi.forceDeleteImage(id: String)
DELETE /images/:id/hard-delete controllers.MediaApi.hardDeleteImage(id: String)
PUT /images/:id/undelete controllers.MediaApi.unSoftDeleteImage(id: String)
GET /images controllers.MediaApi.imageSearch()
Expand Down
Loading