SmartEdit allows us to easily manage media in our website. We can upload different type of images from our device to components like Simple Banner Component, Simple Responsive Banner component in website.
OOTB these components allowed few media types – jpeg, jpg, gif, bmp, tiff, tif, png. It means we can’t upload images outside of this types in SmartEdit and if we try to upload e.g. SVG, SmartEdit will give Selected file type is invalid error.
Today we are going to learn how can we customize SmartEdit to upload other media types as well such as SVG. This blog post will be based on SAP Commerce 1811 version.
Before customizing this feature, let’s first understand how it is working currently in SmartEdit.
Existing Logic
There are two types of validation applied in SmartEdit when uploading any media : Front-end Validation (AngularJS based) and Back-end Validation (JAVA based).
Front-end Validation (AngularJS) –
SmartEdit build on top of AngularJS. This front-end validation available in cmssmartedit extension. There are two files seFileMimeTypeService.js and seFileValidationService.js which are mainly responsible for Front-end Media Validation.
cmssmartedit/web/features/cmssmarteditContainer/components/genericEditor/media/services/seFileMimeTypeService.js
cmssmartedit/web/features/cmssmarteditContainer/components/genericEditor/media/services/seFileValidationService.js
These services validate Media based on its MAGIC NUMBER. Magic numbers are the first bits of a file which uniquely identify the type of file.
Lets have look some piece of code of these files –
-
-
seFileMimeTypeService.js
-
angular.module('seFileMimeTypeServiceModule', [])
.constant('seFileMimeTypeServiceConstants', {
VALID_IMAGE_MIME_TYPE_CODES: ['FFD8FFDB', 'FFD8FFE0', 'FFD8FFE1', '474946383761', '424D', '49492A00', '4D4D002A', '89504E470D0A1A0A']
})
.....
Here VALID_IMAGE_MIME_TYPE_CODES variable contains list of supported Magic Numbers which help to validate uploaded media.
-
-
seFileValidationService.js
-
angular.module('seFileValidationServiceModule', ['seObjectValidatorFactoryModule', 'seFileMimeTypeServiceModule'])
.constant('seFileValidationServiceConstants', {
ACCEPTED_FILE_TYPES: ['jpeg', 'jpg', 'gif', 'bmp', 'tiff', 'tif', 'png'],
MAX_FILE_SIZE_IN_BYTES: 20 * 1024 * 1024,
I18N_KEYS: {
FILE_TYPE_INVALID: 'se.upload.file.type.invalid',
FILE_SIZE_INVALID: 'se.upload.file.size.invalid'
}
})
......
Here ACCEPTED_FILE_TYPES variable contains valid file type extensions.
Back-end Validation (JAVA) –
There is one configurable property available in Platform extension which contains list of validate file extensions. Once Front-end validation passed, a Back-end validation would be applied against this property.
media.allowed.extensions.for.ClassLoader=jpeg,jpg,gif,bmp,tiff,vcard,templ,tif,csv,eps,pdf,png
Customization
To add SVG Support in SmartEdit, we need to override both Front-end and Back-end Validation.
Front-end Customization –
-
- For front-end customization, we need to first create one custom SmartEdit extension. Please follow these links to setup & create one custom extension e.g. trainingsmartedit.
-
- Once new extension is created, Go to trainingsmartedit/web/features/trainingsmarteditContainer folder and create a new package structure /components/genericEditor/media/services (Here we will override OOTB media validation service).
-
- SVG DOES NOT SUPPORT MAGIC NUMBER.It means we can’t simply add magic number like others for validation. SVG images are XML files. We can read file content as a text and parse it with DOMParser. If an error occurs, this will tell us that file is invalid.
-
- Go Inside trainingsmartedit/web/features/trainingsmarteditContainer/components/genericEditor/media/services/ folder and create two new empty fles -> seTrainingFileMimeTypeService.js and seTrainingFileValidationService.js.
This is the time to write custom logic in these two files.
seTrainingFileMimeTypeService.js
angular.module('seTrainingFileMimeTypeServiceModule', ['seFileMimeTypeServiceModule'])
.constant('seFileMimeTypeServiceConstants', {
VALID_IMAGE_MIME_TYPE_CODES: ['FFD8FFDB', 'FFD8FFE0', 'FFD8FFE1', '474946383761', '424D', '49492A00', '4D4D002A', '89504E470D0A1A0A']
})
.factory('seFileMimeTypeService', function(seFileMimeTypeServiceConstants, seFileReader, $q) {
var _validateMimeTypeFromFile = function(loadedFile) {
var fileAsBytes = (new Uint8Array(loadedFile)).subarray(0, 8);
var header = fileAsBytes.reduce(function(header, byte) {
var byteAsStr = byte.toString(16);
if (byteAsStr.length === 1) {
byteAsStr = '0' + byteAsStr;
}
header += byteAsStr;
return header;
}, '');
return seFileMimeTypeServiceConstants.VALID_IMAGE_MIME_TYPE_CODES.some(function(mimeTypeCode) {
return header.toLowerCase().indexOf(mimeTypeCode.toLowerCase()) === 0;
});
};
var isFileMimeTypeValid = function(file) {
var deferred = $q.defer();
if (file.type === 'image/svg+xml') {
// Special Handling for SVG
seFileReader.read(file, {
onLoadEnd: function(e) {
var u8arr = new Uint8Array(e.target.result);
try {
var fileContentAsString = new TextDecoder('utf-8').decode(u8arr);
var parser = new DOMParser();
parser.parseFromString(fileContentAsString, 'image/svg+xml');
deferred.resolve();
} catch (e) {
deferred.reject();
}
},
onError: function() {
deferred.reject();
}
});
return deferred.promise;
}
seFileReader.read(file, {
onLoadEnd: function(e) {
if (_validateMimeTypeFromFile(e.target.result)) {
deferred.resolve();
} else {
deferred.reject();
}
},
onError: function() {
deferred.reject();
}
});
return deferred.promise;
};
return {
isFileMimeTypeValid: isFileMimeTypeValid
};
});
Here we have written a special handling for SVG validation. We are using TextDecoder API to convert the array buffer to string, then parse the string with DOMParser for validation.
seTrainingFileValidationService.js
angular.module('seTrainingFileValidationServiceModule', ['seFileValidationServiceModule', 'seObjectValidatorFactoryModule', 'seTrainingFileMimeTypeServiceModule'])
.constant('seFileValidationServiceConstants', {
ACCEPTED_FILE_TYPES: ['jpeg', 'jpg', 'gif', 'bmp', 'tiff', 'tif', 'png','svg'],
MAX_FILE_SIZE_IN_BYTES: 20 * 1024 * 1024,
I18N_KEYS: {
FILE_TYPE_INVALID: 'se.upload.file.type.invalid',
FILE_SIZE_INVALID: 'se.upload.file.size.invalid'
}
});
Here we have append svg as well as a valid file type.
Once our two custom services are ready, we need to inject them into trainingsmarteditcontainerModule.ts file.
Open /trainingsmartedit/web/features/trainingsmarteditContainer/trainingsmarteditcontainerModule.ts file and add seTrainingFileMimeTypeServiceModule and seTrainingFileValidationServiceModule entry like this
........
@SeModule({
imports: [
'smarteditServicesModule',
'abAnalyticsToolbarItemModule',
'seTrainingFileMimeTypeServiceModule',
'seTrainingFileValidationServiceModule'
],
........
Thats it! Now we have completed our front-end validation changes.
Backend-end Customization –
Override media.allowed.extensions.for.ClassLoader property to add SVG file support as well
media.allowed.extensions.for.ClassLoader=jpeg,jpg,gif,bmp,tiff,vcard,templ,tif,csv,eps,pdf,png,svg
Build Step –
Make sure at the time of SmartEdit setup, assuming you already done ant npminstall.
-
- Now run ant clean all and this will compile our newly added services and generate new trainingsmarteditContainer.js file.
-
- Once build is successful, start the Hybris Server and open SmartEdit.
-
- Make sure that the following impex run at the time of setup.
INSERT_UPDATE SmartEditConfiguration;key[unique=true];value
;applications.trainingsmartedit;{"smartEditLocation":"/trainingsmartedit/trainingsmartedit/js/trainingsmartedit.js","extends": "cmssmartedit"}
;applications.trainingsmarteditContainer;{"smartEditContainerLocation":"/trainingsmartedit/trainingsmartedit/js/trainingsmarteditContainer.js","extends": "cmssmarteditContainer"}
-
- Finally test the changes, SmartEdit should support SVG media as well.
Conclusion
This way we can extend and customize SmartEdit media feature and support any media type which we want for our website. This blog post gives an example of how to support SVG media. Hope this will help.