Skip to content

Commit

Permalink
PT-14783: Refactor attach image url (#714)
Browse files Browse the repository at this point in the history
feat: Refactored the attachment of image URLs. Extracted "add external link" functionality to a separate interface. Introduced the catalog:add-external-image permission, providing access control for this feature. Only HTTPS links are now allowed for image attachments, prioritizing security.
  • Loading branch information
OlegoO committed Dec 21, 2023
1 parent 1f22a30 commit 24add45
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 99 deletions.
2 changes: 2 additions & 0 deletions src/VirtoCommerce.CatalogModule.Core/ModuleConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static class Permissions
public const string Delete = "catalog:delete";
public const string Export = "catalog:export";
public const string Import = "catalog:import";
public const string AddExternalImage = "catalog:add-external-image";
public const string CatalogBrowseFiltersRead = "catalog:BrowseFilters:Read";
public const string CatalogBrowseFiltersUpdate = "catalog:BrowseFilters:Update";
public const string CategoryChange = "bulk-action:category:change";
Expand All @@ -39,6 +40,7 @@ public static class Permissions
Delete,
Export,
Import,
AddExternalImage,
CatalogBrowseFiltersRead,
CatalogBrowseFiltersUpdate,
CategoryChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,9 @@
"add-property": "Eigenschaft hinzufügen",
"gallery": "Galerie",
"map": "Karte",
"default": "Als Standard einstellen"
"default": "Als Standard einstellen",
"link": "Verlinken",
"external-link": "Zum externen link"
},
"permissions": {
"catalog-scope": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@
},
"descr": {
"external-link": "Use an external image URL instead of uploading it to assets",
"external-link-warning": "\"Example: scheme://your_resource.png\". For security reasons, you must only use images that support the URL scheme of the Storefront when uploading external images."
"external-link-warning": "Note: Only https link is allowed for security reasons."
}
},
"video-list": {
Expand Down Expand Up @@ -573,6 +573,9 @@
"image-upload": {
"title": "Upload images"
},
"image-add-external-link": {
"title": "Add external link"
},
"aggregation-properties-details": {
"name": {
"label": "Property name"
Expand Down Expand Up @@ -996,7 +999,9 @@
"gallery": "Gallery",
"map": "Map",
"open-item": "Open item",
"default": "Set as default"
"default": "Set as default",
"link": "Link",
"external-link": "External Link"
},
"permissions": {
"catalog-scope": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@
},
"descr": {
"external-link": "Использовать внешний URL-адрес образа вместо его загрузки",
"external-link-warning": "\"Пример: http(s)://ваш_ресурс.png\". По соображениям безопасности необходимо использовать изображения, поддерживающие URL-схему Фронта."
"external-link-warning": "Примечание. Из соображений безопасности разрешена только ссылка https."
}
},
"video-list": {
Expand Down Expand Up @@ -566,6 +566,9 @@
"image-upload": {
"title": "Загрузить изображения"
},
"image-add-external-link": {
"title": "Добавить внешнюю ссылку"
},
"aggregation-properties-details": {
"name": {
"label": "Название свойства"
Expand Down Expand Up @@ -918,7 +921,9 @@
"gallery": "Галерея",
"map": "Карта",
"open-item": "Открыть элемент",
"default": "Сделать по-умолчанию"
"default": "Сделать по-умолчанию",
"link": "Связать",
"external-link": "Внешняя Ссылка"
},
"permissions": {
"catalog-scope": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
angular.module('virtoCommerce.catalogModule')
.controller('virtoCommerce.catalogModule.imagesAddExternalLinkController',
['$scope', '$translate', 'platformWebApp.bladeNavigationService', 'platformWebApp.settings',
function ($scope, $translate, bladeNavigationService, settings) {
var blade = $scope.blade;

blade.headIcon = 'fa fa-external-link';

$scope.isValid = false;
blade.isLoading = false;

blade.refresh = function (item) {
initialize(item);
}

var promise = settings.getValues({ id: 'VirtoCommerce.Core.General.Languages' }).$promise;

$scope.languages = [];
function initialize(item) {
promise.then(function (promiseData) {
$scope.languages = promiseData;
});

blade.item = item;
blade.title = 'catalog.blades.image-add-external-link.title';
$scope.imageTypes = settings.getValues({ id: 'Catalog.ImageCategories' });

blade.currentEntities = [];
}

$scope.addImageFromUrlHandler = function () {
$scope.isValid = true;

var image = {
isImage: true,
group: blade.imageType,
url: blade.newExternalImageUrl,
name: blade.newExternalImageUrl.split('/').pop(),
relativeUrl: blade.newExternalImageUrl
};
blade.currentEntities.push(image);
};

$scope.saveChanges = function () {
if (blade.onSelect) {
_.each(blade.currentEntities, function (entity) {
entity.languageCode = blade.selectedLanguageCode;
});
blade.onSelect(blade.currentEntities);
}

$scope.bladeClose();
};

$scope.toggleImageSelect = function (e, image) {
if (e.ctrlKey == 1) {
image.$selected = !image.$selected;
} else {
if (image.$selected) {
image.$selected = false;
} else {
image.$selected = true;
}
}
}

$scope.copyUrl = function (data) {
$translate('catalog.blades.images.labels.copy-url-prompt').then(function (promptMessage) {
window.prompt(promptMessage, data.url);
});
}

$scope.openDictionarySettingManagement = function () {
var newBlade = {
id: 'settingDetailChild',
isApiSave: true,
currentEntityId: 'Catalog.ImageCategories',
parentRefresh: function (data) { $scope.imageTypes = data; },
controller: 'platformWebApp.settingDictionaryController',
template: '$(Platform)/Scripts/app/settings/blades/setting-dictionary.tpl.html'
};
bladeNavigationService.showBlade(newBlade, blade);
};

initialize(blade.item);

}]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<div class="blade-static __bottom" ng-include="'Modules/$(VirtoCommerce.Catalog)/Scripts/blades/common/templates/ok-cancel.tpl.html'"></div>
<div class="blade-content">
<div class="blade-inner">
<div class="inner-block">
<form class="form" name="uploadForm">
<div class="sub-t">{{ 'catalog.blades.images.labels.select-image-category' | translate }}</div>
<div class="columns clearfix">
<div class="column">
<div class="form-group">
<label class="form-label">{{ 'catalog.blades.images.labels.image-category' | translate }} <a href="" ng-click="openDictionarySettingManagement()" class="form-edit" va-permission="catalog:imagetypes"><i class="form-ico fa fa-pencil"></i></a></label>
<div class="form-input">
<ui-select ng-model="blade.imageType" on-select="changeImageCategory($item, $model)">
<ui-select-match allow-clear="true" placeholder="{{ 'catalog.blades.images.placeholders.image-category' | translate }}">{{$select.selected}}</ui-select-match>
<ui-select-choices repeat="x in imageTypes | filter: $select.search">
<span ng-bind-html="x | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>
</div>
</div>
</div>
<div class="column">
<div class="form-group">
<label class="form-label">{{ 'catalog.blades.images.labels.language' | translate }}</label>
<div class="form-input">
<ui-select ng-model="blade.selectedLanguageCode" ng-disabled="disabled">
<ui-select-match allow-clear placeholder="{{'catalog.blades.images.placeholders.language' | translate}}">{{$select.selected}}</ui-select-match>
<ui-select-choices repeat="x in languages | filter: $select.search">
<span ng-bind-html="x | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>
</div>
</div>
</div>
</div>

<div class="sub-t">{{ 'catalog.blades.images.labels.external-link' | translate }}</div>

<div class="columns">
<div class="column">
<div class="form-group">
<div class="__info list">
<div class="list-descr">{{ 'catalog.blades.images.descr.external-link' | translate }}</div>
</div>
</div>
</div>
</div>

<div class="form-input __file form-group">
<input ng-model="blade.newExternalImageUrl" required pattern="^https:\/\/.*$" placeholder="{{ 'catalog.blades.images.placeholders.image-url' | translate }}" tabindex="-1">
<button class="btn __file" type="button" ng-click="addImageFromUrlHandler()" title="Add image by URL">
<i class="btn-ico fas fa-plus"></i>
</button>
<div class="__info list">
<div class="list-descr">{{ 'catalog.blades.images.descr.external-link-warning' | translate }}</div>
</div>
</div>

<div ng-show="uploader.isUploading">
<div class="sub-t">{{ 'catalog.blades.images.labels.progress' | translate }}</div>
<div class="progress-bar __aqua" ng-style="{'width': uploader.progress + '%'}"></div>
</div>

<!-- preview -->
<div class="sub-t" ng-show="blade.currentEntities.length">{{ 'catalog.blades.images.labels.preview' | translate }}</div>
<div class="tile-group triple __images-list">
<div class="tile" ng-repeat="image in blade.currentEntities" ng-include="'imageTemplate.html'"></div>
</div>

</form>


</div>
</div>
</div>

<script type="text/ng-template" id="imageTemplate.html">
<img class="tile-img" ng-src="{{image.url ? image.url : ''}}">
</script>
50 changes: 6 additions & 44 deletions src/VirtoCommerce.CatalogModule.Web/Scripts/blades/images-add.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
angular.module('virtoCommerce.catalogModule')
.controller('virtoCommerce.catalogModule.imagesAddController',
['$scope', '$filter', '$translate', 'FileUploader', 'platformWebApp.dialogService',
'platformWebApp.bladeNavigationService', 'platformWebApp.authService',
'platformWebApp.assets.api', 'platformWebApp.settings',
function ($scope, $filter, $translate, FileUploader, dialogService, bladeNavigationService, authService, assets, settings) {
['$scope', '$translate', 'FileUploader', 'platformWebApp.bladeNavigationService', 'platformWebApp.settings',
function ($scope, $translate, FileUploader, bladeNavigationService, settings) {
var blade = $scope.blade;

blade.hasAssetCreatePermission = bladeNavigationService.checkPermission('platform:asset:create');

blade.headIcon = 'fa fa-image';

$scope.isValid = true;
$scope.isValid = false;

blade.isLoading = false;

blade.useExternalUrl = false;

blade.refresh = function (item) {
initialize(item);
}
Expand Down Expand Up @@ -51,54 +47,22 @@ angular.module('virtoCommerce.catalogModule')
image.group = blade.imageType;
blade.currentEntities.push(image);
});

$scope.isValid = true;
};

uploader.onAfterAddingAll = function (addedItems) {
bladeNavigationService.setError(null, blade);
};

uploader.onErrorItem = function (element, response, status, headers) {
$scope.isValid = false;
bladeNavigationService.setError(element._file.name + ' failed: ' + (response.message ? response.message : status), blade);
};
}
blade.currentEntities = [];
}

$scope.addImageFromUrlHandler = function () {
if (blade.useExternalUrl) {
$scope.addImageDirectlyFromUrl();
} else {
$scope.addImageFromUrl();
}
};

$scope.addImageFromUrl = function () {
if (blade.newExternalImageUrl) {
assets.uploadFromUrl({ folderUrl: getImageUrl(blade.folderPath, blade.imageType).folderUrl, url: blade.newExternalImageUrl }, function (data) {
_.each(data, function (x) {
x.isImage = true;
x.group = blade.imageType;
blade.currentEntities.push(x);
});
blade.newExternalImageUrl = undefined;
});
}
};

$scope.addImageDirectlyFromUrl = function () {
if (blade.newExternalImageUrl) {
var image = {
isImage: true,
group: blade.imageType,
url: blade.newExternalImageUrl,
name: blade.newExternalImageUrl.split('/').pop(),
relativeUrl: blade.newExternalImageUrl
};
blade.currentEntities.push(image);
blade.newExternalImageUrl = undefined;
}
};

$scope.saveChanges = function () {
if (blade.onSelect) {
_.each(blade.currentEntities, function (entity) {
Expand Down Expand Up @@ -145,9 +109,7 @@ angular.module('virtoCommerce.catalogModule')
};

function getImageUrl(path, imageType) {

var folderUrl = 'catalog/' + (path + (imageType ? '/' + imageType : ''));

return { folderUrl: folderUrl, relative: 'api/assets?folderUrl=' + folderUrl };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,36 +51,6 @@
</button>
</div>

<div class="columns">
<div class="column">
<div class="form-group">
<label class="form-label">{{ 'catalog.blades.images.labels.external-link' | translate }}</label>
<div class="form-input">
<label class="form-label __switch">
<input type="checkbox" ng-model="blade.useExternalUrl">
<span class="switch"></span>
</label>
</div>
<div class="__info list">
<div class="list-descr">{{ 'catalog.blades.images.descr.external-link' | translate }}</div>
</div>
</div>
</div>
</div>

<div class="form-input __file form-group">
<input ng-model="blade.newExternalImageUrl" type="url" placeholder="{{ 'catalog.blades.images.placeholders.image-url' | translate }}" tabindex="-1">
<button class="btn __file" type="button" ng-click="addImageFromUrlHandler()" title="Add image by URL">
<i class="btn-ico fas fa-plus"></i>
</button>
<div class="__info list">
<div class="list-descr">{{ 'catalog.blades.images.descr.external-link-warning' | translate }}</div>
</div>
</div>




<div ng-show="uploader.isUploading">
<div class="sub-t">{{ 'catalog.blades.images.labels.progress' | translate }}</div>
<div class="progress-bar __aqua" ng-style="{'width': uploader.progress + '%'}"></div>
Expand Down
Loading

0 comments on commit 24add45

Please sign in to comment.