207 lines
5.8 KiB
JavaScript
207 lines
5.8 KiB
JavaScript
import store from '../store';
|
|
import utils from './utils';
|
|
|
|
const forbiddenFolderNameMatcher = /^\.stackedit-data$|^\.stackedit-trash$|\.md$|\.sync$|\.publish$/;
|
|
|
|
export default {
|
|
/**
|
|
* Create a file in the store with the specified fields.
|
|
*/
|
|
async createFile({
|
|
name,
|
|
parentId,
|
|
text,
|
|
properties,
|
|
discussions,
|
|
comments,
|
|
} = {}, background = false) {
|
|
const id = utils.uid();
|
|
const item = {
|
|
id,
|
|
name: utils.sanitizeName(name),
|
|
parentId: parentId || null,
|
|
};
|
|
const content = {
|
|
id: `${id}/content`,
|
|
text: utils.sanitizeText(text || store.getters['data/computedSettings'].newFileContent),
|
|
properties: utils
|
|
.sanitizeText(properties || store.getters['data/computedSettings'].newFileProperties),
|
|
discussions: discussions || {},
|
|
comments: comments || {},
|
|
};
|
|
const workspaceUniquePaths = store.getters['workspace/hasUniquePaths'];
|
|
|
|
// Show warning dialogs
|
|
if (!background) {
|
|
// If name is being stripped
|
|
if (item.name !== utils.defaultName && item.name !== name) {
|
|
await store.dispatch('modal/open', {
|
|
type: 'stripName',
|
|
item,
|
|
});
|
|
}
|
|
|
|
// Check if there is already a file with that path
|
|
if (workspaceUniquePaths) {
|
|
const parentPath = store.getters.itemPaths[item.parentId] || '';
|
|
const path = parentPath + item.name;
|
|
if (store.getters.pathItems[path]) {
|
|
await store.dispatch('modal/open', {
|
|
type: 'pathConflict',
|
|
item,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save file and content in the store
|
|
store.commit('content/setItem', content);
|
|
store.commit('file/setItem', item);
|
|
if (workspaceUniquePaths) {
|
|
this.makePathUnique(id);
|
|
}
|
|
|
|
// Return the new file item
|
|
return store.state.file.itemMap[id];
|
|
},
|
|
|
|
/**
|
|
* Make sanity checks and then create/update the folder/file in the store.
|
|
*/
|
|
async storeItem(item) {
|
|
const id = item.id || utils.uid();
|
|
const sanitizedName = utils.sanitizeName(item.name);
|
|
|
|
if (item.type === 'folder' && forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
|
await store.dispatch('modal/open', {
|
|
type: 'unauthorizedName',
|
|
item,
|
|
});
|
|
throw new Error('Unauthorized name.');
|
|
}
|
|
|
|
// Show warning dialogs
|
|
// If name has been stripped
|
|
if (sanitizedName !== utils.defaultName && sanitizedName !== item.name) {
|
|
await store.dispatch('modal/open', {
|
|
type: 'stripName',
|
|
item,
|
|
});
|
|
}
|
|
// Check if there is a path conflict
|
|
if (store.getters['workspace/hasUniquePaths']) {
|
|
const parentPath = store.getters.itemPaths[item.parentId] || '';
|
|
const path = parentPath + sanitizedName;
|
|
const pathItems = store.getters.pathItems[path] || [];
|
|
if (pathItems.some(itemWithSamePath => itemWithSamePath.id !== id)) {
|
|
await store.dispatch('modal/open', {
|
|
type: 'pathConflict',
|
|
item,
|
|
});
|
|
}
|
|
}
|
|
|
|
return this.setOrPatchItem({
|
|
...item,
|
|
id,
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Create/update the folder/file in the store and make sure its path is unique.
|
|
*/
|
|
setOrPatchItem(patch) {
|
|
const item = {
|
|
...store.getters.allItemMap[patch.id] || patch,
|
|
};
|
|
if (!item.id) {
|
|
return null;
|
|
}
|
|
|
|
if (patch.parentId !== undefined) {
|
|
item.parentId = patch.parentId || null;
|
|
}
|
|
if (patch.name) {
|
|
const sanitizedName = utils.sanitizeName(patch.name);
|
|
if (item.type !== 'folder' || !forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
|
item.name = sanitizedName;
|
|
}
|
|
}
|
|
|
|
// Save item in the store
|
|
store.commit(`${item.type}/setItem`, item);
|
|
|
|
// Ensure path uniqueness
|
|
if (store.getters['workspace/hasUniquePaths']) {
|
|
this.makePathUnique(item.id);
|
|
}
|
|
|
|
return store.getters.allItemMap[item.id];
|
|
},
|
|
|
|
/**
|
|
* Delete a file in the store and all its related items.
|
|
*/
|
|
deleteFile(fileId) {
|
|
// Delete the file
|
|
store.commit('file/deleteItem', fileId);
|
|
// Delete the content
|
|
store.commit('content/deleteItem', `${fileId}/content`);
|
|
// Delete the syncedContent
|
|
store.commit('syncedContent/deleteItem', `${fileId}/syncedContent`);
|
|
// Delete the contentState
|
|
store.commit('contentState/deleteItem', `${fileId}/contentState`);
|
|
// Delete sync locations
|
|
(store.getters['syncLocation/groupedByFileId'][fileId] || [])
|
|
.forEach(item => store.commit('syncLocation/deleteItem', item.id));
|
|
// Delete publish locations
|
|
(store.getters['publishLocation/groupedByFileId'][fileId] || [])
|
|
.forEach(item => store.commit('publishLocation/deleteItem', item.id));
|
|
},
|
|
|
|
/**
|
|
* Ensure two files/folders don't have the same path if the workspace doesn't support it.
|
|
*/
|
|
ensureUniquePaths() {
|
|
if (store.getters['workspace/hasUniquePaths']) {
|
|
if (Object.keys(store.getters.itemPaths).some(id => this.makePathUnique(id))) {
|
|
this.ensureUniquePaths();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Return false if the file/folder path is unique.
|
|
* Add a prefix to its name and return true otherwise.
|
|
*/
|
|
makePathUnique(id) {
|
|
const { pathItems, allItemMap, itemPaths } = store.getters;
|
|
const item = allItemMap[id];
|
|
if (!item) {
|
|
return false;
|
|
}
|
|
let path = itemPaths[id];
|
|
if (pathItems[path].length === 1) {
|
|
return false;
|
|
}
|
|
const isFolder = item.type === 'folder';
|
|
if (isFolder) {
|
|
// Remove trailing slash
|
|
path = path.slice(0, -1);
|
|
}
|
|
for (let suffix = 1; ; suffix += 1) {
|
|
let pathWithPrefix = `${path}.${suffix}`;
|
|
if (isFolder) {
|
|
pathWithPrefix += '/';
|
|
}
|
|
if (!pathItems[pathWithPrefix]) {
|
|
store.commit(`${item.type}/patchItem`, {
|
|
id: item.id,
|
|
name: `${item.name}.${suffix}`,
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
};
|