Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Submission: Added SAP community badges to enhanced picture #5

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions _i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Toolbar1=Choose Selfie Template
Toolbar2=Choose Your Picture and Upload
Toolbar3=Edit and Download Final Image
Upload=Enhance Your Picture
SaveAs=Save As
placeholder=Choose File for Upload...
enterSapCommunityName=Enter SAP Community Name
yourSapCommunityNameEG=Your community name, e. g.
close=Close
enhance=Enhance
gui.loading=Processing Picture
gui.loadingLong=Please wait ...Processing Picture
44 changes: 41 additions & 3 deletions app/profilePic/profilePic/controller/App.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ sap.ui.define([
"sap/ui/core/Core",
"sap/ui/model/json/JSONModel",
"sap/ui/Device",
"sap/suite/ui/commons/library"
"sap/suite/ui/commons/library",
"sap/m/Dialog",
"sap/m/Button",
"sap/m/Input",
],
function (BaseController, MessageToast, oCore, JSONModel, Device, SuiteLibrary) {
function (BaseController, MessageToast, oCore, JSONModel, Device, SuiteLibrary, Dialog, Button, Input) {

return BaseController.extend("profilePic.controller.App", {
onInit: function () {
Expand All @@ -25,7 +28,41 @@ sap.ui.define([
}
},

uploadPressed: async function (oEvent) {
enhancePressed: async function (oEvent) {
if (!this.oEnhanceDialog) {
var oResourceBundle = this.getView()
.getModel("i18n")
.getResourceBundle()
this.oEnhanceDialog = new Dialog({
title: oResourceBundle.getText("enterSapCommunityName"),
draggable: true,
content: new Input({
placeholder: oResourceBundle.getText("yourSapCommunityNameEG") + "dj.adams.sap",
value: "{/sapCommunityName}"
}),
buttons: [
new Button({
text: oResourceBundle.getText("enhance"),
press: function () {
this.upload()
this.oEnhanceDialog.close()
}.bind(this)
}),
new Button({
text: oResourceBundle.getText("close"),
press: function () {
this.oEnhanceDialog.close()
}.bind(this)
})
]
})
this.getView().addDependent(this.oEnhanceDialog)
}

this.oEnhanceDialog.open()
},

upload: async function (oEvent) {
let view = this.getView()
let controller = view.getController()
let oFileUploader = view.byId("fileToUpload")
Expand All @@ -35,6 +72,7 @@ sap.ui.define([
}
let param = view.byId("uploadParam")
//param.setValue(oInput.getActivePage())
param.setValue(this.getView().getModel().getData().sapCommunityName)
oFileUploader.getParameters()
var oImageEditor = this.getView().byId("image")
oImageEditor.applyVisibleCrop()
Expand Down
4 changes: 2 additions & 2 deletions app/profilePic/profilePic/view/App.view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
<e:customToolbarControls>
<unified:FileUploader id="fileToUpload" sendXHR="true" width="400px" placeholder="{i18n>placeholder}" uploadUrl="/upload_profile_pic" uploadComplete="uploadComplete" change="onFileChange">
<unified:parameters>
<unified:FileUploaderParameter id="uploadParam" name="selectedPic"/>
<unified:FileUploaderParameter id="uploadParam" name="sapCommunityName"/>
</unified:parameters>
</unified:FileUploader>
<Button id="button" text="{i18n>Upload}" press="uploadPressed"></Button>
<Button id="button" text="{i18n>Upload}" press="enhancePressed"></Button>
<Button type="Emphasized" text="Save As" press="onSaveAsPress" />
</e:customToolbarControls>
<e:customEllipseCropItems>
Expand Down
93 changes: 88 additions & 5 deletions routes/profilePic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import sharp from 'sharp'
import multer from 'multer'
import lodash from 'lodash'
import * as svg from '../utils/svgRender.js'
import { promises as fs } from 'fs'
import { promises as fsPromises } from 'fs'
import * as path from 'path'

import https from 'https'
import fs from 'fs'

/**
* Route for handing upload, manipulation, and download of the profile picture
* @param {Object} app - Express application instance
Expand All @@ -17,10 +20,71 @@ export default function (app) {
* @returns {Promise<string>}
*/
async function loadImageB64(image) {
return await fsPromises.readFile(path.resolve(app.baseDir, image), { encoding: 'base64' })
}

/**
* Download an image URL to the file system
* @param {string} url
* @returns {Promise<T>}
*/
async function download(url) {
const path = getPathFromImgUrl(url)
if (fs.existsSync(path)) return
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(path)
https.get(url, (res) => {
res.pipe(writeStream)
writeStream.on('finish', () => resolve())
}).on('error', (error) => reject(error))
})
}

/**
* Get the file system path for an image URL
* @param {string} url
* @returns {string}
*/
function getPathFromImgUrl (url) {
return './images/' + path.basename(new URL(url).pathname)
}

return await fs.readFile(path.resolve(app.baseDir, image), { encoding: 'base64' })
/**
* Get URLs of badges for a SAP community name
* @param {string} sapCommunityName
* @returns {Promise<Array<string>>}
*/
async function getBadgeUrls (sapCommunityName) {
const url = 'https://people-api.services.sap.com/rs/showcaseBadge/' + sapCommunityName;
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let body = ''
res.on('data', (chunk) => body += chunk.toString())
res.on('end', () => {
if (res.statusCode === 200) {
let badgeUrls = []
JSON.parse(body).forEach((ele) => {
badgeUrls.push(ele.imageUrl)
})
resolve(badgeUrls)
}
})
res.on('error', (error) => reject(error))
})
})
}

/**
* Read an svg image from file system and return it as a base64 png string
* @param {string} path
* @returns {Promise<string>}
*/
async function loadSvgAsPngB64 (path) {
const svgBuffer = await fsPromises.readFile(path)
const pngBuffer = await sharp(svgBuffer).png().toBuffer()
return await pngBuffer.toString('base64')
}

const limits = {
files: 1, // allow only 1 file per request
fileSize: 1024 * 1024 * 20, // 20 MB (max file size)
Expand Down Expand Up @@ -69,16 +133,35 @@ export default function (app) {
//Convert the uploaded content to PNG, rotate based upon metadata and transfer it to a buffer
const uploadContent = await sharp(file.buffer).rotate().png().toBuffer()
const uploadContentMeta = await sharp(uploadContent).metadata()

let body =
svg.svgHeader(uploadContentMeta.width, uploadContentMeta.height) +
svg.svgItem(0, 0, 0, uploadContent.toString('base64'), uploadContentMeta.height, uploadContentMeta.width, true) +
svg.svgItem(0, 0, 0, await loadImageB64('./images/boarder.png'), uploadContentMeta.height, uploadContentMeta.width, true) +
svg.svgEnd()
svg.svgItem(0, 0, 0, await loadImageB64('./images/boarder.png'), uploadContentMeta.height, uploadContentMeta.width, true)

//Load profile badges from SAP community and add them to the image
const badgeUrls = await getBadgeUrls(req.body.sapCommunityName)
await Promise.all(badgeUrls.map((badgeUrl) => download(badgeUrl)))
const halfImgHeight = uploadContentMeta.height / 2
const halfImgWidth = uploadContentMeta.width / 2
const badgeRadius = uploadContentMeta.width / 12
for (let i = 0; i < badgeUrls.length; i++) {
//Parametric equation of a circle: x=radius*cos(angle[rad]) y=radius*sin(angle[rad])
//However, y and x axis are switched in svg library. So x is the vertical axis in the following code
const x = halfImgHeight - 0.80 * halfImgHeight * Math.sin(Math.PI * (1.22 + i * 0.14)) - badgeRadius
const y = halfImgWidth + 0.80 * halfImgWidth * Math.cos(Math.PI * (1.22 + i * 0.14)) - badgeRadius
const badgePath = getPathFromImgUrl(badgeUrls[i])
const imgB64 = badgeUrls[i].slice(-3) === 'svg'
? await loadSvgAsPngB64(badgePath)
: await loadImageB64(badgePath)
body += svg.svgItem(x, y, 0, imgB64, badgeRadius * 2, badgeRadius * 2, true)
}

body += svg.svgEnd()

const png = await sharp(Buffer.from(body)).png().toBuffer()
const pngOut = await png.toString('base64')
res.type("image/png").status(200).send(pngOut)

})
})
}