Skip to content

Commit

Permalink
add modified property to dynamic panel schema, fix svg rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
IshavSohal committed Dec 6, 2024
1 parent d8f60c9 commit 51b0a3e
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 66 deletions.
4 changes: 4 additions & 0 deletions StorylinesSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
"type": {
"type": "string",
"enum": ["dynamic"]
},
"modified": {
"type": "boolean",
"description": "An optional tag that specifies whether the panel has been modified from its default configuration"
}
},
"required": ["content", "type", "children", "title"]
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"nouislider": "^15.5.0",
"ramp-config-editor_editeur-config-pcar": "^3.6.0",
"ramp-pcar": "^4.8.0",
"ramp-storylines_demo-scenarios-pcar": "^3.2.8",
"ramp-storylines_demo-scenarios-pcar":"^3.2.8",
"throttle-debounce": "^5.0.0",
"url": "^0.11.3",
"uuid": "^9.0.0",
Expand Down
4 changes: 4 additions & 0 deletions public/StorylinesSlideSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
"customStyles": {
"type": "string",
"description": "Additional CSS styles to apply to the panel."
},
"modified": {
"type": "boolean",
"description": "An optional tag that specifies whether the panel has been modified from its default configuration"
}
},
"additionalProperties": false,
Expand Down
82 changes: 42 additions & 40 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ app.use(cors());
// POST requests made to /upload will be handled here.
app.route(ROUTE_PREFIX + '/upload').post(function (req, res, next) {
// TODO: Putting this on the header isn't great. The body has the zipped folder. And usernames in the URL doesn't look great either. Maybe improve this somehow.
const user = req.headers.user
const user = req.headers.user;
if (!user) {
// Send back error if the user uploading the storyline was not provided.
responseMessages.push({
Expand Down Expand Up @@ -123,10 +123,9 @@ app.route(ROUTE_PREFIX + '/upload').post(function (req, res, next) {
// Initialize a new git repo if this is a new storyline.
// Otherwise, simply create a new commit with the zipped folder.
if (!newStorylines) {
await commitToRepo(fileName, user, false)
}
else {
await initGitRepo(fileName, user)
await commitToRepo(fileName, user, false);
} else {
await initGitRepo(fileName, user);
}
// Finally, delete the uploaded zip file.
safeRM(secureFilename, UPLOAD_PATH);
Expand All @@ -143,10 +142,10 @@ app.route(ROUTE_PREFIX + '/upload').post(function (req, res, next) {
// GET requests made to /retrieve/ID/commitHash will be handled here.
// Calling this with commitHash as "latest" simply fetches the product as normal.
app.route(ROUTE_PREFIX + '/retrieve/:id/:hash').get(function (req, res, next) {
// This user is only needed for backwards compatibility.
// This user is only needed for backwards compatibility.
// If we have an existing storylines product that is not a git repo, we need to initialize a git repo
// and make an initial commit for it, but we need the user for the commit.
const user = req.headers.user
const user = req.headers.user;
if (!user) {
// Send back error if the user uploading the storyline was not provided.
responseMessages.push({
Expand All @@ -161,35 +160,38 @@ app.route(ROUTE_PREFIX + '/retrieve/:id/:hash').get(function (req, res, next) {
var archive = archiver('zip');
const PRODUCT_PATH = `${TARGET_PATH}/${req.params.id}`;
const uploadLocation = `${UPLOAD_PATH}/${req.params.id}-outgoing.zip`;
const commitHash = req.params.hash
const commitHash = req.params.hash;

// Check if the product exists.
if (
fs.access(PRODUCT_PATH, async (error) => {
if (!error) {
// Backwards compatibility. If the existing product is not a git repo i.e. it existed before git version control,
// we make a git repo for it before returning the version history. Otherwise, the code below will explode.
await initGitRepo(PRODUCT_PATH, user)
await initGitRepo(PRODUCT_PATH, user);
const git = simpleGit(PRODUCT_PATH);
// Get the current branch. We do it this way instead of assuming its "main" in case someone has it set to master.
const branches = await git.branchLocal()
const currBranch = branches.current
const branches = await git.branchLocal();
const currBranch = branches.current;
if (commitHash !== 'latest') {
// If the user does not ask for the latest commit, we checkout a new branch at the point of the requested commit,
// If the user does not ask for the latest commit, we checkout a new branch at the point of the requested commit,
// and then proceed with getting the zipped folder below.
try {
// First, we check if the requested commit exists.
// NOTE: When calling from frontend, the catch block should never run.
const commitExists = await git.catFile(['-t', commitHash]);
if (commitExists !== 'commit\n') {
throw new Error()
throw new Error();
}
} catch (error) {
responseMessages.push({
type: 'INFO',
message: `Access attempt to version ${commitHash} of product ${req.params.id} failed, does not exist.`
});
logger('INFO', `Access attempt to version ${commitHash} of product ${req.params.id} failed, does not exist.`);
logger(
'INFO',
`Access attempt to version ${commitHash} of product ${req.params.id} failed, does not exist.`
);
res.status(404).send({ status: 'Not Found' });
return;
}
Expand Down Expand Up @@ -219,10 +221,9 @@ app.route(ROUTE_PREFIX + '/retrieve/:id/:hash').get(function (req, res, next) {
// Since the user has not asked for the latest commit, we need to clean up.
// Go back to the main branch and delete the newly created branch.
await git.checkout(currBranch);
await git.deleteLocalBranch(`version-${commitHash}`)
await git.deleteLocalBranch(`version-${commitHash}`);
}
});

});

// Write the product data to the ZIP file.
Expand All @@ -235,7 +236,6 @@ app.route(ROUTE_PREFIX + '/retrieve/:id/:hash').get(function (req, res, next) {

responseMessages.push({ type: 'INFO', message: `Successfully loaded product ${req.params.id}` });
logger('INFO', `Successfully loaded product ${req.params.id}`);

} else {
responseMessages.push({
type: 'INFO',
Expand Down Expand Up @@ -291,10 +291,10 @@ app.route(ROUTE_PREFIX + '/retrieve/:id/:lang').get(function (req, res) {
});

app.route(ROUTE_PREFIX + '/history/:id').get(function (req, res, next) {
// This user is only needed for backwards compatibility.
// This user is only needed for backwards compatibility.
// If we have an existing storylines product that is not a git repo, we need to initialize a git repo
// and make an initial commit for it, but we need the user for the commit.
const user = req.headers.user
const user = req.headers.user;
if (!user) {
// Send back error if the user uploading the storyline was not provided.
responseMessages.push({
Expand All @@ -316,21 +316,21 @@ app.route(ROUTE_PREFIX + '/history/:id').get(function (req, res, next) {
});
logger('INFO', `Access attempt to versions of ${req.params.id} failed, does not exist.`);
res.status(404).send({ status: 'Not Found' });
}
else {
} else {
// Backwards compatibility. If the existing product is not a git repo i.e. it existed before git version control,
// we make a git repo for it before returning the version history. Otherwise, the code below will explode.
await initGitRepo(PRODUCT_PATH, user)
await initGitRepo(PRODUCT_PATH, user);
// Get version history for this product via git log command
const git = simpleGit(PRODUCT_PATH);
const log = await git.log()
const log = await git.log();
// TODO: Remove the 10 version limit once pagination is implemented
const history = log.all.slice(0, 10).map((commit) => ({hash: commit.hash, created: commit.date, storylineUUID: req.params.id}))
res.json(history)
const history = log.all
.slice(0, 10)
.map((commit) => ({ hash: commit.hash, created: commit.date, storylineUUID: req.params.id }));
res.json(history);
}
})

})
});
});

// GET reuests made to /retrieveMessages will recieve all the responseMessages currently queued.
app.route(ROUTE_PREFIX + '/retrieveMessages').get(function (req, res) {
Expand All @@ -341,7 +341,7 @@ app.route(ROUTE_PREFIX + '/retrieveMessages').get(function (req, res) {
/*
* Initializes a git repo at the requested path, if one does not already exist.
* Creates an initial commit with any currently existing files in the directory.
*
*
* @param {string} path the path of the git repo
* @param {string} username the name of the user initializing the repo
*/
Expand All @@ -357,16 +357,16 @@ async function initGitRepo(path, username) {
// Product directory is in a git repo but not top-level, we are working locally.
repoExists = false;
}
} catch(error) {
} catch (error) {
// Product directory is not a git repo nor is it within a git repo.
repoExists = false;
}

if (!repoExists) {
// Repo does not exist for the storyline product.
// Repo does not exist for the storyline product.
// Initialize a git repo and add an initial commit with all existing files.
await git.init()
await commitToRepo(path, username, true)
await git.init();
await commitToRepo(path, username, true);
}
}

Expand All @@ -378,20 +378,22 @@ async function initGitRepo(path, username) {
* @param {boolean} initial specifies whether this is the initial commit
*/
async function commitToRepo(path, username, initial) {
const date = moment().format('YYYY-MM-DD')
const time = moment().format('hh:mm:ss a')
const date = moment().format('YYYY-MM-DD');
const time = moment().format('hh:mm:ss a');
// Initialize git
const git = simpleGit(path);
let versionNumber = 1
let versionNumber = 1;
if (!initial) {
// Compute version number for storyline if this is not the initial commit.
const log = await git.log()
const lastMessage = log.latest.message
versionNumber = lastMessage.split(' ')[3]
const log = await git.log();
const lastMessage = log.latest.message;
versionNumber = lastMessage.split(' ')[3];
versionNumber = Number(versionNumber) + 1;
}
// Commit the files for this storyline to its repo.
await git.add('./*').commit(`Add product version ${versionNumber} on ${date} at ${time}`, {'--author': `"${username} <>"`})
await git
.add('./*')
.commit(`Add product version ${versionNumber} on ${date} at ${time}`, { '--author': `"${username} <>"` });
}

/*
Expand Down
2 changes: 1 addition & 1 deletion src/components/editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ import {
ConfigFileStructure,
HelpSection,
MetadataContent,
Slide,
MultiLanguageSlide,
Slide,
SourceCounts,
StoryRampConfig,
TextPanel
Expand Down
35 changes: 26 additions & 9 deletions src/components/image-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,33 @@ export default class ImageEditorV extends Vue {
const assetSrc = `${image.src.substring(image.src.indexOf('/') + 1)}`;
const filename = image.src.replace(/^.*[\\/]/, '');
const assetFile = this.configFileStructure.zip.file(assetSrc);
const assetType = assetSrc.split('.').at(-1);
if (assetFile) {
this.imagePreviewPromises.push(
assetFile.async('blob').then((res: Blob) => {
return {
...image,
id: filename ? filename : image.src,
src: URL.createObjectURL(res)
} as ImageFile;
})
);
if (assetType != 'svg') {
this.imagePreviewPromises.push(
assetFile.async('blob').then((res: Blob) => {
return {
...image,
id: filename ? filename : image.src,
src: URL.createObjectURL(res)
} as ImageFile;
})
);
} else {
this.imagePreviewPromises.push(
assetFile.async('text').then((res) => {
const imageFile = new File([res], filename, {
type: 'image/svg+xml'
});
return {
...image,
id: filename ? filename : image.src,
src: URL.createObjectURL(imageFile)
} as ImageFile;
})
);
}
}
});
Expand Down
49 changes: 36 additions & 13 deletions src/components/metadata-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -563,9 +563,9 @@ import {
ImagePanel,
MapPanel,
MetadataContent,
MultiLanguageSlide,
PanelType,
Slide,
MultiLanguageSlide,
SlideshowPanel,
SourceCounts,
StoryRampConfig,
Expand Down Expand Up @@ -764,12 +764,24 @@ export default class MetadataEditorV extends Vue {
if (logo) {
const logoFile = this.configFileStructure?.zip.file(logoSrc);
const logoType = logoSrc.split('.').at(-1);
if (logoFile) {
logoFile.async('blob').then((img: Blob) => {
this.logoImage = new File([img], this.metadata.logoName);
this.metadata.logoPreview = URL.createObjectURL(img);
this.loadStatus = 'loaded';
});
if (logoType !== 'svg') {
logoFile.async('blob').then((img: Blob) => {
this.logoImage = new File([img], this.metadata.logoName);
this.metadata.logoPreview = URL.createObjectURL(img);
this.loadStatus = 'loaded';
});
} else {
logoFile.async('text').then((img) => {
const logoImageFile = new File([img], this.metadata.logoName, {
type: 'image/svg+xml'
});
this.logoImage = logoImageFile;
this.metadata.logoPreview = URL.createObjectURL(logoImageFile);
this.loadStatus = 'loaded';
});
}
} else {
// Fill in the field with this value whether it exists or not.
this.metadata.logoName = logo;
Expand Down Expand Up @@ -1269,19 +1281,30 @@ export default class MetadataEditorV extends Vue {
if (logo) {
// Set the alt text for the logo.
this.metadata.logoAltText = config.introSlide.logo?.altText ? config.introSlide.logo.altText : '';
this.metadata.logoName = logo.split('/').at(-1);
// Fetch the logo from the folder (if it exists).
const logoSrc = `${logo.substring(logo.indexOf('/') + 1)}`;
const logoName = `${logo.split('/')[logo.split('/').length - 1]}`;
const logoFile = this.configFileStructure?.zip.file(logoSrc);
const logoType = logoSrc.split('.').at(-1);
if (logoFile) {
logoFile.async('blob').then((img: Blob) => {
this.logoImage = new File([img], logoName);
this.metadata.logoPreview = URL.createObjectURL(img);
this.metadata.logoName = logoName;
this.loadStatus = 'loaded';
});
if (logoType !== 'svg') {
logoFile.async('blob').then((img: Blob) => {
this.logoImage = new File([img], this.metadata.logoName);
this.metadata.logoPreview = URL.createObjectURL(img);
this.loadStatus = 'loaded';
});
} else {
logoFile.async('text').then((img) => {
const logoImageFile = new File([img], this.metadata.logoName, {
type: 'image/svg+xml'
});
this.logoImage = logoImageFile;
this.metadata.logoPreview = URL.createObjectURL(logoImageFile);
this.loadStatus = 'loaded';
});
}
} else {
// Fill in the field with this value whether it exists or not.
this.metadata.logoName = logo;
Expand Down

0 comments on commit 51b0a3e

Please sign in to comment.