GitLab provider (part 2)
This commit is contained in:
parent
6b91c2bafb
commit
da9b87620f
@ -219,7 +219,7 @@ export default {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: darken($error-color, 10%);
|
color: darken($error-color, 10%);
|
||||||
background-color: transparentize(lighten($error-color, 33%), 0.1);
|
background-color: transparentize(lighten($error-color, 33%), 0.075);
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1.33;
|
line-height: 1.33;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -64,7 +64,7 @@ let cachedHistoryContextHash;
|
|||||||
let revisionsPromise;
|
let revisionsPromise;
|
||||||
let revisionContentPromises;
|
let revisionContentPromises;
|
||||||
const pageSize = 30;
|
const pageSize = 30;
|
||||||
const spacerThreshold = 60 * 60 * 1000; // 1h
|
const spacerThreshold = 6 * 60 * 60 * 1000; // 6h
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -129,8 +129,8 @@ export default {
|
|||||||
if (fileSyncData && contentSyncData) {
|
if (fileSyncData && contentSyncData) {
|
||||||
return {
|
return {
|
||||||
...historyContext,
|
...historyContext,
|
||||||
fileSyncData,
|
fileSyncDataId: fileSyncData.id,
|
||||||
contentSyncData,
|
contentSyncDataId: contentSyncData.id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,23 +9,19 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<menu-entry @click.native="addCouchdbWorkspace">
|
<menu-entry @click.native="addCouchdbWorkspace">
|
||||||
<icon-provider slot="icon" provider-id="couchdbWorkspace"></icon-provider>
|
<icon-provider slot="icon" provider-id="couchdbWorkspace"></icon-provider>
|
||||||
<div>CouchDB workspace</div>
|
<span>Add a <b>CouchDB</b> backed workspace</span>
|
||||||
<span>Add a workspace synced with a CouchDB database.</span>
|
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<menu-entry @click.native="addGithubWorkspace">
|
<menu-entry @click.native="addGithubWorkspace">
|
||||||
<icon-provider slot="icon" provider-id="githubWorkspace"></icon-provider>
|
<icon-provider slot="icon" provider-id="githubWorkspace"></icon-provider>
|
||||||
<div>GitHub workspace</div>
|
<span>Add a <b>GitHub</b> backed workspace</span>
|
||||||
<span>Add a workspace synced with a GitHub repository.</span>
|
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<menu-entry @click.native="addGitlabWorkspace">
|
<menu-entry @click.native="addGitlabWorkspace">
|
||||||
<icon-provider slot="icon" provider-id="gitlabWorkspace"></icon-provider>
|
<icon-provider slot="icon" provider-id="gitlabWorkspace"></icon-provider>
|
||||||
<div>GitLab workspace</div>
|
<span>Add a <b>GitLab</b> backed workspace</span>
|
||||||
<span>Add a workspace synced with a GitLab project.</span>
|
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<menu-entry @click.native="addGoogleDriveWorkspace">
|
<menu-entry @click.native="addGoogleDriveWorkspace">
|
||||||
<icon-provider slot="icon" provider-id="googleDriveWorkspace"></icon-provider>
|
<icon-provider slot="icon" provider-id="googleDriveWorkspace"></icon-provider>
|
||||||
<div>Google Drive workspace</div>
|
<span>Add a <b>Google Drive</b> backed workspace</span>
|
||||||
<span>Add a workspace synced with a Google Drive folder.</span>
|
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<menu-entry @click.native="manageWorkspaces">
|
<menu-entry @click.native="manageWorkspaces">
|
||||||
<icon-database slot="icon"></icon-database>
|
<icon-database slot="icon"></icon-database>
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<p>Link your <b>GitLab</b> account to <b>StackEdit</b>.</p>
|
<p>Link your <b>GitLab</b> account to <b>StackEdit</b>.</p>
|
||||||
<form-entry label="GitLab URL" error="serverUrl">
|
<form-entry label="GitLab URL" error="serverUrl">
|
||||||
<input slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
|
<input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl">
|
||||||
|
<input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
|
||||||
<div class="form-entry__info">
|
<div class="form-entry__info">
|
||||||
<b>Example:</b> https://gitlab.example.com/
|
<b>Example:</b> https://gitlab.example.com/
|
||||||
</div>
|
</div>
|
||||||
@ -42,14 +43,15 @@ export default modalTemplate({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resolve() {
|
resolve() {
|
||||||
if (!this.serverUrl) {
|
const serverUrl = this.config.forceServerUrl || this.serverUrl;
|
||||||
|
if (!serverUrl) {
|
||||||
this.setError('serverUrl');
|
this.setError('serverUrl');
|
||||||
}
|
}
|
||||||
if (!this.applicationId) {
|
if (!this.applicationId) {
|
||||||
this.setError('applicationId');
|
this.setError('applicationId');
|
||||||
}
|
}
|
||||||
if (this.serverUrl && this.applicationId) {
|
if (serverUrl && this.applicationId) {
|
||||||
const parsedUrl = this.serverUrl.match(/^(https:\/\/[^/]+)/);
|
const parsedUrl = serverUrl.match(/^(https:\/\/[^/]+)/);
|
||||||
if (!parsedUrl) {
|
if (!parsedUrl) {
|
||||||
this.setError('serverUrl');
|
this.setError('serverUrl');
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<form-entry label="Project URL" error="projectUrl">
|
<form-entry label="Project URL" error="projectUrl">
|
||||||
<input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()">
|
<input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()">
|
||||||
<div class="form-entry__info">
|
<div class="form-entry__info">
|
||||||
<b>Example:</b> {{config.token.serverUrl}}path/to/project
|
<b>Example:</b> {{config.token.serverUrl}}/path/to/project
|
||||||
</div>
|
</div>
|
||||||
</form-entry>
|
</form-entry>
|
||||||
<form-entry label="File path" error="path">
|
<form-entry label="File path" error="path">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const origin = `${window.location.protocol}//${window.location.host}`;
|
const origin = `${window.location.protocol}//${window.location.host}`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
cleanTrashAfter: 0 * 24 * 60 * 60 * 1000, // 7 days
|
cleanTrashAfter: 7 * 24 * 60 * 60 * 1000, // 7 days
|
||||||
origin,
|
origin,
|
||||||
oauth2RedirectUri: `${origin}/oauth2/callback`,
|
oauth2RedirectUri: `${origin}/oauth2/callback`,
|
||||||
types: [
|
types: [
|
||||||
|
@ -44,15 +44,18 @@ export default class Provider {
|
|||||||
* Parse content serialized with serializeContent()
|
* Parse content serialized with serializeContent()
|
||||||
*/
|
*/
|
||||||
static parseContent(serializedContent, id) {
|
static parseContent(serializedContent, id) {
|
||||||
const result = utils.deepCopy(store.state.content.itemsById[id]) || emptyContent(id);
|
let text = serializedContent;
|
||||||
result.text = utils.sanitizeText(serializedContent);
|
|
||||||
result.history = [];
|
|
||||||
const extractedData = dataExtractor.exec(serializedContent);
|
const extractedData = dataExtractor.exec(serializedContent);
|
||||||
if (extractedData) {
|
let result;
|
||||||
|
if (!extractedData) {
|
||||||
|
// In case stackedit's data has been manually removed, try to restore them
|
||||||
|
result = utils.deepCopy(store.state.content.itemsById[id]) || emptyContent(id);
|
||||||
|
} else {
|
||||||
|
result = emptyContent(id);
|
||||||
try {
|
try {
|
||||||
const serializedData = extractedData[1].replace(/\s/g, '');
|
const serializedData = extractedData[1].replace(/\s/g, '');
|
||||||
const parsedData = JSON.parse(utils.decodeBase64(serializedData));
|
const parsedData = JSON.parse(utils.decodeBase64(serializedData));
|
||||||
result.text = utils.sanitizeText(serializedContent.slice(0, extractedData.index));
|
text = text.slice(0, extractedData.index);
|
||||||
if (parsedData.properties) {
|
if (parsedData.properties) {
|
||||||
result.properties = utils.sanitizeText(parsedData.properties);
|
result.properties = utils.sanitizeText(parsedData.properties);
|
||||||
}
|
}
|
||||||
@ -62,11 +65,15 @@ export default class Provider {
|
|||||||
if (parsedData.comments) {
|
if (parsedData.comments) {
|
||||||
result.comments = parsedData.comments;
|
result.comments = parsedData.comments;
|
||||||
}
|
}
|
||||||
result.history = parsedData.history || [];
|
result.history = parsedData.history;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result.text = utils.sanitizeText(text);
|
||||||
|
if (!result.history) {
|
||||||
|
result.history = [];
|
||||||
|
}
|
||||||
return utils.addItemHash(result);
|
return utils.addItemHash(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,8 +194,8 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async listFileRevisions({ token, contentSyncData }) {
|
async listFileRevisions({ token, contentSyncDataId }) {
|
||||||
const body = await couchdbHelper.retrieveDocumentWithRevisions(token, contentSyncData.id);
|
const body = await couchdbHelper.retrieveDocumentWithRevisions(token, contentSyncDataId);
|
||||||
const revisions = [];
|
const revisions = [];
|
||||||
body._revs_info.forEach((revInfo, idx) => { // eslint-disable-line no-underscore-dangle
|
body._revs_info.forEach((revInfo, idx) => { // eslint-disable-line no-underscore-dangle
|
||||||
if (revInfo.status === 'available') {
|
if (revInfo.status === 'available') {
|
||||||
@ -209,19 +209,19 @@ export default new Provider({
|
|||||||
});
|
});
|
||||||
return revisions;
|
return revisions;
|
||||||
},
|
},
|
||||||
async loadFileRevision({ token, contentSyncData, revision }) {
|
async loadFileRevision({ token, contentSyncDataId, revision }) {
|
||||||
if (revision.loaded) {
|
if (revision.loaded) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const body = await couchdbHelper.retrieveDocument(token, contentSyncData.id, revision.id);
|
const body = await couchdbHelper.retrieveDocument(token, contentSyncDataId, revision.id);
|
||||||
revision.sub = body.sub;
|
revision.sub = body.sub;
|
||||||
revision.created = body.time;
|
revision.created = body.time;
|
||||||
revision.loaded = true;
|
revision.loaded = true;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async getFileRevisionContent({ token, contentSyncData, revisionId }) {
|
async getFileRevisionContent({ token, contentSyncDataId, revisionId }) {
|
||||||
const body = await couchdbHelper
|
const body = await couchdbHelper
|
||||||
.retrieveDocumentWithAttachments(token, contentSyncData.id, revisionId);
|
.retrieveDocumentWithAttachments(token, contentSyncDataId, revisionId);
|
||||||
return Provider.parseContent(body.attachments.data, body.item.id);
|
return Provider.parseContent(body.attachments.data, body.item.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -46,12 +46,19 @@ export default new Provider({
|
|||||||
async initWorkspace() {
|
async initWorkspace() {
|
||||||
const { owner, repo, branch } = utils.queryParams;
|
const { owner, repo, branch } = utils.queryParams;
|
||||||
const workspaceParams = this.getWorkspaceParams({ owner, repo, branch });
|
const workspaceParams = this.getWorkspaceParams({ owner, repo, branch });
|
||||||
|
if (!branch) {
|
||||||
|
workspaceParams.branch = 'master';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract path param
|
||||||
const path = (utils.queryParams.path || '')
|
const path = (utils.queryParams.path || '')
|
||||||
|
.trim()
|
||||||
.replace(/^\/*/, '') // Remove leading `/`
|
.replace(/^\/*/, '') // Remove leading `/`
|
||||||
.replace(/\/*$/, '/'); // Add trailing `/`
|
.replace(/\/*$/, '/'); // Add trailing `/`
|
||||||
if (path !== '/') {
|
if (path !== '/') {
|
||||||
workspaceParams.path = path;
|
workspaceParams.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceId = utils.makeWorkspaceId(workspaceParams);
|
const workspaceId = utils.makeWorkspaceId(workspaceParams);
|
||||||
const workspace = store.getters['workspace/workspacesById'][workspaceId];
|
const workspace = store.getters['workspace/workspacesById'][workspaceId];
|
||||||
|
|
||||||
@ -217,14 +224,14 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async listFileRevisions({ token, fileSyncData }) {
|
async listFileRevisions({ token, fileSyncDataId }) {
|
||||||
const { owner, repo, branch } = store.getters['workspace/currentWorkspace'];
|
const { owner, repo, branch } = store.getters['workspace/currentWorkspace'];
|
||||||
const entries = await githubHelper.getCommits({
|
const entries = await githubHelper.getCommits({
|
||||||
token,
|
token,
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
sha: branch,
|
sha: branch,
|
||||||
path: getAbsolutePath(fileSyncData),
|
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return entries.map(({
|
return entries.map(({
|
||||||
@ -242,11 +249,12 @@ export default new Provider({
|
|||||||
const sub = `${githubHelper.subPrefix}:${user.id}`;
|
const sub = `${githubHelper.subPrefix}:${user.id}`;
|
||||||
userSvc.addInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
userSvc.addInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
||||||
const date = (commit.author && commit.author.date)
|
const date = (commit.author && commit.author.date)
|
||||||
|| (commit.committer && commit.committer.date);
|
|| (commit.committer && commit.committer.date)
|
||||||
|
|| 1;
|
||||||
return {
|
return {
|
||||||
id: sha,
|
id: sha,
|
||||||
sub,
|
sub,
|
||||||
created: date ? new Date(date).getTime() : 1,
|
created: new Date(date).getTime(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -257,14 +265,14 @@ export default new Provider({
|
|||||||
async getFileRevisionContent({
|
async getFileRevisionContent({
|
||||||
token,
|
token,
|
||||||
contentId,
|
contentId,
|
||||||
fileSyncData,
|
fileSyncDataId,
|
||||||
revisionId,
|
revisionId,
|
||||||
}) {
|
}) {
|
||||||
const { data } = await githubHelper.downloadFile({
|
const { data } = await githubHelper.downloadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
branch: revisionId,
|
branch: revisionId,
|
||||||
path: getAbsolutePath(fileSyncData),
|
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||||
});
|
});
|
||||||
return Provider.parseContent(data, contentId);
|
return Provider.parseContent(data, contentId);
|
||||||
},
|
},
|
||||||
|
@ -135,24 +135,17 @@ export default new Provider({
|
|||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
|
||||||
return entries.map(({
|
return entries.map((entry) => {
|
||||||
author,
|
const email = entry.author_email || entry.committer_email;
|
||||||
committer,
|
const sub = `${gitlabHelper.subPrefix}:${token.serverUrl}/${email}`;
|
||||||
commit,
|
userSvc.addInfo({
|
||||||
sha,
|
id: sub,
|
||||||
}) => {
|
name: entry.author_name || entry.committer_name,
|
||||||
let user;
|
imageUrl: '',
|
||||||
if (author && author.login) {
|
});
|
||||||
user = author;
|
const date = entry.authored_date || entry.committed_date || 1;
|
||||||
} else if (committer && committer.login) {
|
|
||||||
user = committer;
|
|
||||||
}
|
|
||||||
const sub = `${gitlabHelper.subPrefix}:${user.id}`;
|
|
||||||
userSvc.addInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
|
||||||
const date = (commit.author && commit.author.date)
|
|
||||||
|| (commit.committer && commit.committer.date);
|
|
||||||
return {
|
return {
|
||||||
id: sha,
|
id: entry.id,
|
||||||
sub,
|
sub,
|
||||||
created: date ? new Date(date).getTime() : 1,
|
created: date ? new Date(date).getTime() : 1,
|
||||||
};
|
};
|
||||||
|
@ -45,29 +45,44 @@ export default new Provider({
|
|||||||
return getAbsolutePath({ id });
|
return getAbsolutePath({ id });
|
||||||
},
|
},
|
||||||
async initWorkspace() {
|
async initWorkspace() {
|
||||||
const { projectPath, branch } = utils.queryParams;
|
const { serverUrl, branch } = utils.queryParams;
|
||||||
const workspaceParams = this.getWorkspaceParams({ projectPath, branch });
|
const workspaceParams = this.getWorkspaceParams({ serverUrl, branch });
|
||||||
|
if (!branch) {
|
||||||
|
workspaceParams.branch = 'master';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract project path param
|
||||||
|
const projectPath = (utils.queryParams.projectPath || '')
|
||||||
|
.trim()
|
||||||
|
.replace(/^\/*/, '') // Remove leading `/`
|
||||||
|
.replace(/\/*$/, ''); // Remove trailing `/`
|
||||||
|
workspaceParams.projectPath = projectPath;
|
||||||
|
|
||||||
|
// Extract path param
|
||||||
const path = (utils.queryParams.path || '')
|
const path = (utils.queryParams.path || '')
|
||||||
|
.trim()
|
||||||
.replace(/^\/*/, '') // Remove leading `/`
|
.replace(/^\/*/, '') // Remove leading `/`
|
||||||
.replace(/\/*$/, '/'); // Add trailing `/`
|
.replace(/\/*$/, '/'); // Add trailing `/`
|
||||||
if (path !== '/') {
|
if (path !== '/') {
|
||||||
workspaceParams.path = path;
|
workspaceParams.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceId = utils.makeWorkspaceId(workspaceParams);
|
const workspaceId = utils.makeWorkspaceId(workspaceParams);
|
||||||
const workspace = store.getters['workspace/workspacesById'][workspaceId];
|
const workspace = store.getters['workspace/workspacesById'][workspaceId];
|
||||||
|
|
||||||
// See if we already have a token
|
// See if we already have a token
|
||||||
let token;
|
const sub = workspace ? workspace.sub : utils.queryParams.sub;
|
||||||
if (workspace) {
|
let token = store.getters['data/gitlabTokensBySub'][sub];
|
||||||
// Token sub is in the workspace
|
|
||||||
token = store.getters['data/gitlabTokensBySub'][workspace.sub];
|
|
||||||
}
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
|
const { applicationId } = await store.dispatch('modal/open', {
|
||||||
token = await gitlabHelper.addAccount(serverUrl, applicationId);
|
type: 'gitlabAccount',
|
||||||
|
forceServerUrl: serverUrl,
|
||||||
|
});
|
||||||
|
token = await gitlabHelper.addAccount(serverUrl, applicationId, sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
|
const projectId = await gitlabHelper.getProjectId(token, workspaceParams);
|
||||||
const pathEntries = (path || '').split('/');
|
const pathEntries = (path || '').split('/');
|
||||||
const projectPathEntries = (projectPath || '').split('/');
|
const projectPathEntries = (projectPath || '').split('/');
|
||||||
const name = pathEntries[pathEntries.length - 2] // path ends with `/`
|
const name = pathEntries[pathEntries.length - 2] // path ends with `/`
|
||||||
@ -75,6 +90,7 @@ export default new Provider({
|
|||||||
store.dispatch('workspace/patchWorkspacesById', {
|
store.dispatch('workspace/patchWorkspacesById', {
|
||||||
[workspaceId]: {
|
[workspaceId]: {
|
||||||
...workspaceParams,
|
...workspaceParams,
|
||||||
|
projectId,
|
||||||
id: workspaceId,
|
id: workspaceId,
|
||||||
sub: token.sub,
|
sub: token.sub,
|
||||||
name,
|
name,
|
||||||
@ -178,12 +194,13 @@ export default new Provider({
|
|||||||
async uploadWorkspaceContent({ token, content, file }) {
|
async uploadWorkspaceContent({ token, content, file }) {
|
||||||
const path = store.getters.gitPathsByItemId[file.id];
|
const path = store.getters.gitPathsByItemId[file.id];
|
||||||
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
||||||
const res = await gitlabHelper.uploadFile({
|
const sha = gitWorkspaceSvc.shaByPath[path];
|
||||||
|
await gitlabHelper.uploadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
path: absolutePath,
|
path: absolutePath,
|
||||||
content: Provider.serializeContent(content),
|
content: Provider.serializeContent(content),
|
||||||
sha: gitWorkspaceSvc.shaByPath[path],
|
sha,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return new sync data
|
// Return new sync data
|
||||||
@ -192,7 +209,7 @@ export default new Provider({
|
|||||||
id: store.getters.gitPathsByItemId[content.id],
|
id: store.getters.gitPathsByItemId[content.id],
|
||||||
type: content.type,
|
type: content.type,
|
||||||
hash: content.hash,
|
hash: content.hash,
|
||||||
sha: res.content.sha,
|
sha,
|
||||||
},
|
},
|
||||||
fileSyncData: {
|
fileSyncData: {
|
||||||
id: path,
|
id: path,
|
||||||
@ -223,33 +240,26 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async listFileRevisions({ token, fileSyncData }) {
|
async listFileRevisions({ token, fileSyncDataId }) {
|
||||||
const { projectId, branch } = store.getters['workspace/currentWorkspace'];
|
const { projectId, branch } = store.getters['workspace/currentWorkspace'];
|
||||||
const entries = await gitlabHelper.getCommits({
|
const entries = await gitlabHelper.getCommits({
|
||||||
token,
|
token,
|
||||||
projectId,
|
projectId,
|
||||||
sha: branch,
|
sha: branch,
|
||||||
path: getAbsolutePath(fileSyncData),
|
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return entries.map(({
|
return entries.map((entry) => {
|
||||||
author,
|
const email = entry.author_email || entry.committer_email;
|
||||||
committer,
|
const sub = `${gitlabHelper.subPrefix}:${token.serverUrl}/${email}`;
|
||||||
commit,
|
userSvc.addInfo({
|
||||||
sha,
|
id: sub,
|
||||||
}) => {
|
name: entry.author_name || entry.committer_name,
|
||||||
let user;
|
imageUrl: '', // No way to get user's avatar url...
|
||||||
if (author && author.login) {
|
});
|
||||||
user = author;
|
const date = entry.authored_date || entry.committed_date || 1;
|
||||||
} else if (committer && committer.login) {
|
|
||||||
user = committer;
|
|
||||||
}
|
|
||||||
const sub = `${gitlabHelper.subPrefix}:${user.id}`;
|
|
||||||
userSvc.addInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
|
||||||
const date = (commit.author && commit.author.date)
|
|
||||||
|| (commit.committer && commit.committer.date);
|
|
||||||
return {
|
return {
|
||||||
id: sha,
|
id: entry.id,
|
||||||
sub,
|
sub,
|
||||||
created: date ? new Date(date).getTime() : 1,
|
created: date ? new Date(date).getTime() : 1,
|
||||||
};
|
};
|
||||||
@ -262,14 +272,14 @@ export default new Provider({
|
|||||||
async getFileRevisionContent({
|
async getFileRevisionContent({
|
||||||
token,
|
token,
|
||||||
contentId,
|
contentId,
|
||||||
fileSyncData,
|
fileSyncDataId,
|
||||||
revisionId,
|
revisionId,
|
||||||
}) {
|
}) {
|
||||||
const { data } = await gitlabHelper.downloadFile({
|
const { data } = await gitlabHelper.downloadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
branch: revisionId,
|
branch: revisionId,
|
||||||
path: getAbsolutePath(fileSyncData),
|
path: getAbsolutePath({ id: fileSyncDataId }),
|
||||||
});
|
});
|
||||||
return Provider.parseContent(data, contentId);
|
return Provider.parseContent(data, contentId);
|
||||||
},
|
},
|
||||||
|
@ -167,8 +167,8 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async listFileRevisions({ token, contentSyncData }) {
|
async listFileRevisions({ token, contentSyncDataId }) {
|
||||||
const revisions = await googleHelper.getAppDataFileRevisions(token, contentSyncData.id);
|
const revisions = await googleHelper.getAppDataFileRevisions(token, contentSyncDataId);
|
||||||
return revisions.map(revision => ({
|
return revisions.map(revision => ({
|
||||||
id: revision.id,
|
id: revision.id,
|
||||||
sub: `${googleHelper.subPrefix}:${revision.lastModifyingUser.permissionId}`,
|
sub: `${googleHelper.subPrefix}:${revision.lastModifyingUser.permissionId}`,
|
||||||
@ -179,9 +179,9 @@ export default new Provider({
|
|||||||
// Revisions are already loaded
|
// Revisions are already loaded
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
async getFileRevisionContent({ token, contentSyncData, revisionId }) {
|
async getFileRevisionContent({ token, contentSyncDataId, revisionId }) {
|
||||||
const content = await googleHelper
|
const content = await googleHelper
|
||||||
.downloadAppDataFileRevision(token, contentSyncData.id, revisionId);
|
.downloadAppDataFileRevision(token, contentSyncDataId, revisionId);
|
||||||
return JSON.parse(content);
|
return JSON.parse(content);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -508,8 +508,8 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async listFileRevisions({ token, fileSyncData }) {
|
async listFileRevisions({ token, fileSyncDataId }) {
|
||||||
const revisions = await googleHelper.getFileRevisions(token, fileSyncData.id);
|
const revisions = await googleHelper.getFileRevisions(token, fileSyncDataId);
|
||||||
return revisions.map(revision => ({
|
return revisions.map(revision => ({
|
||||||
id: revision.id,
|
id: revision.id,
|
||||||
sub: `${googleHelper.subPrefix}:${revision.lastModifyingUser.permissionId}`,
|
sub: `${googleHelper.subPrefix}:${revision.lastModifyingUser.permissionId}`,
|
||||||
@ -523,10 +523,10 @@ export default new Provider({
|
|||||||
async getFileRevisionContent({
|
async getFileRevisionContent({
|
||||||
token,
|
token,
|
||||||
contentId,
|
contentId,
|
||||||
fileSyncData,
|
fileSyncDataId,
|
||||||
revisionId,
|
revisionId,
|
||||||
}) {
|
}) {
|
||||||
const content = await googleHelper.downloadFileRevision(token, fileSyncData.id, revisionId);
|
const content = await googleHelper.downloadFileRevision(token, fileSyncDataId, revisionId);
|
||||||
return Provider.parseContent(content, contentId);
|
return Provider.parseContent(content, contentId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -88,8 +88,8 @@ export default {
|
|||||||
store.dispatch('data/addGitlabToken', token);
|
store.dispatch('data/addGitlabToken', token);
|
||||||
return token;
|
return token;
|
||||||
},
|
},
|
||||||
addAccount(serverUrl, applicationId) {
|
addAccount(serverUrl, applicationId, sub = null) {
|
||||||
return this.startOauth2(serverUrl, applicationId);
|
return this.startOauth2(serverUrl, applicationId, sub);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,7 +133,7 @@ export default {
|
|||||||
path,
|
path,
|
||||||
}) {
|
}) {
|
||||||
return request(token, {
|
return request(token, {
|
||||||
url: `projects/${encodeURIComponent(projectId)}/repository/tree`,
|
url: `projects/${encodeURIComponent(projectId)}/repository/commits`,
|
||||||
params: {
|
params: {
|
||||||
ref_name: branch,
|
ref_name: branch,
|
||||||
path,
|
path,
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
|||||||
if (!loginToken) {
|
if (!loginToken) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const loginType = store.getters['workspace/loginToken'];
|
const loginType = store.getters['workspace/loginType'];
|
||||||
const prefix = subPrefixesByType[loginType];
|
const prefix = subPrefixesByType[loginType];
|
||||||
return prefix ? `${prefix}:${loginToken.sub}` : loginToken.sub;
|
return prefix ? `${prefix}:${loginToken.sub}` : loginToken.sub;
|
||||||
},
|
},
|
||||||
@ -44,7 +44,7 @@ export default {
|
|||||||
const [type, sub] = parseUserId(userId);
|
const [type, sub] = parseUserId(userId);
|
||||||
|
|
||||||
// Try to find a token with this sub to resolve name as soon as possible
|
// Try to find a token with this sub to resolve name as soon as possible
|
||||||
const token = store.getters[`data/${type}TokensBySub`][sub];
|
const token = store.getters['data/tokensByType'][type][sub];
|
||||||
if (token) {
|
if (token) {
|
||||||
store.commit('userInfo/addItem', {
|
store.commit('userInfo/addItem', {
|
||||||
id: userId,
|
id: userId,
|
||||||
|
@ -94,11 +94,15 @@ export default {
|
|||||||
},
|
},
|
||||||
sanitizeName(name) {
|
sanitizeName(name) {
|
||||||
return `${name || ''}`
|
return `${name || ''}`
|
||||||
// Replace `/`, control characters and other kind of spaces with a space
|
|
||||||
.replace(/[/\x00-\x1F\x7f-\xa0\s]+/g, ' ').trim() // eslint-disable-line no-control-regex
|
|
||||||
// Keep only 250 characters
|
// Keep only 250 characters
|
||||||
.slice(0, 250) || constants.defaultName;
|
.slice(0, 250) || constants.defaultName;
|
||||||
},
|
},
|
||||||
|
sanitizeFilename(name) {
|
||||||
|
return this.sanitizeName(`${name || ''}`
|
||||||
|
// Replace `/`, control characters and other kind of spaces with a space
|
||||||
|
.replace(/[/\x00-\x1F\x7f-\xa0\s]+/g, ' ') // eslint-disable-line no-control-regex
|
||||||
|
.trim()) || constants.defaultName;
|
||||||
|
},
|
||||||
deepCopy,
|
deepCopy,
|
||||||
serializeObject(obj) {
|
serializeObject(obj) {
|
||||||
return obj === undefined ? obj : JSON.stringify(obj, (key, value) => {
|
return obj === undefined ? obj : JSON.stringify(obj, (key, value) => {
|
||||||
@ -128,6 +132,7 @@ export default {
|
|||||||
return array.cl_map(value => alphabet[value % radix]).join('');
|
return array.cl_map(value => alphabet[value % radix]).join('');
|
||||||
},
|
},
|
||||||
hash(str) {
|
hash(str) {
|
||||||
|
// https://stackoverflow.com/a/7616484/1333165
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
if (!str) return hash;
|
if (!str) return hash;
|
||||||
for (let i = 0; i < str.length; i += 1) {
|
for (let i = 0; i < str.length; i += 1) {
|
||||||
|
@ -20,7 +20,7 @@ export default {
|
|||||||
const id = utils.uid();
|
const id = utils.uid();
|
||||||
const item = {
|
const item = {
|
||||||
id,
|
id,
|
||||||
name: utils.sanitizeName(name),
|
name: utils.sanitizeFilename(name),
|
||||||
parentId: parentId || null,
|
parentId: parentId || null,
|
||||||
};
|
};
|
||||||
const content = {
|
const content = {
|
||||||
@ -72,7 +72,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
async storeItem(item) {
|
async storeItem(item) {
|
||||||
const id = item.id || utils.uid();
|
const id = item.id || utils.uid();
|
||||||
const sanitizedName = utils.sanitizeName(item.name);
|
const sanitizedName = utils.sanitizeFilename(item.name);
|
||||||
|
|
||||||
if (item.type === 'folder' && forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
if (item.type === 'folder' && forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
||||||
await store.dispatch('modal/open', {
|
await store.dispatch('modal/open', {
|
||||||
@ -125,7 +125,7 @@ export default {
|
|||||||
item.parentId = patch.parentId || null;
|
item.parentId = patch.parentId || null;
|
||||||
}
|
}
|
||||||
if (patch.name) {
|
if (patch.name) {
|
||||||
const sanitizedName = utils.sanitizeName(patch.name);
|
const sanitizedName = utils.sanitizeFilename(patch.name);
|
||||||
if (item.type !== 'folder' || !forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
if (item.type !== 'folder' || !forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
||||||
item.name = sanitizedName;
|
item.name = sanitizedName;
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ textarea {
|
|||||||
|
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: #f2f2f2;
|
background-color: #f0f0f0;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,12 +85,12 @@ samp {
|
|||||||
blockquote {
|
blockquote {
|
||||||
color: rgba(0, 0, 0, 0.5);
|
color: rgba(0, 0, 0, 0.5);
|
||||||
padding-left: 1.5em;
|
padding-left: 1.5em;
|
||||||
border-left: 5px solid rgba(0, 0, 0, 0.075);
|
border-left: 5px solid rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.app--dark .layout__panel--editor &,
|
.app--dark .layout__panel--editor &,
|
||||||
.app--dark .layout__panel--preview & {
|
.app--dark .layout__panel--preview & {
|
||||||
color: rgba(255, 255, 255, 0.4);
|
color: rgba(255, 255, 255, 0.4);
|
||||||
border-left-color: rgba(255, 255, 255, 0.075);
|
border-left-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ $code-border-radius: 3px;
|
|||||||
$link-color: #0c93e4;
|
$link-color: #0c93e4;
|
||||||
$error-color: #f31;
|
$error-color: #f31;
|
||||||
$border-radius-base: 3px;
|
$border-radius-base: 3px;
|
||||||
$hr-color: rgba(128, 128, 128, 0.2);
|
$hr-color: rgba(128, 128, 128, 0.33);
|
||||||
$navbar-bg: #2c2c2c;
|
$navbar-bg: #2c2c2c;
|
||||||
$navbar-color: mix($navbar-bg, #fff, 33%);
|
$navbar-color: mix($navbar-bg, #fff, 33%);
|
||||||
$navbar-hover-color: #fff;
|
$navbar-hover-color: #fff;
|
||||||
|
Loading…
Reference in New Issue
Block a user