mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Upgrade sandstorm integration
Both sandstorm and LibreBoard have significantly evolved since the last release of LibreBoard on sandstorm. This commit: * adds some more attributes on the sandstorm manifest * introduces support with the sandstorm sharing box * uses a server redirection to point to the board document * hides the top shortcut bar on sandstorm Fixes #163.
This commit is contained in:
parent
fc2435b042
commit
1b4fcc67f4
7 changed files with 142 additions and 82 deletions
|
|
@ -70,7 +70,7 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (! Meteor.user().isBoardMember())
|
if (! Meteor.userId() || ! Meteor.user().isBoardMember())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self.$(lists).sortable({
|
self.$(lists).sortable({
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,27 @@ template(name="header")
|
||||||
list all starred boards with a link to go there. This is inspired by the
|
list all starred boards with a link to go there. This is inspired by the
|
||||||
Reddit "subreddit" bar.
|
Reddit "subreddit" bar.
|
||||||
The first link goes to the boards page.
|
The first link goes to the boards page.
|
||||||
if currentUser
|
unless isSandstorm
|
||||||
#header-quick-access
|
if currentUser
|
||||||
ul
|
#header-quick-access
|
||||||
li
|
ul
|
||||||
+linkTo(route="Boards")
|
li
|
||||||
span.fa.fa-home
|
+linkTo(route="Boards")
|
||||||
| All boards
|
span.fa.fa-home
|
||||||
each currentUser.starredBoards
|
| All boards
|
||||||
li.separator -
|
each currentUser.starredBoards
|
||||||
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
li.separator -
|
||||||
+linkTo(route="Board" data=this)
|
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
||||||
= title
|
+linkTo(route="Board" data=this)
|
||||||
else
|
= title
|
||||||
li.current Star a board to add a shortcut in this bar.
|
else
|
||||||
|
li.current Star a board to add a shortcut in this bar.
|
||||||
|
|
||||||
li
|
li
|
||||||
a.js-create-board
|
a.js-create-board
|
||||||
i.fa.fa-plus(title="Create a new board")
|
i.fa.fa-plus(title="Create a new board")
|
||||||
|
|
||||||
+headerUserBar
|
+headerUserBar
|
||||||
|
|
||||||
//-
|
//-
|
||||||
The main bar is a colorful bar that provide all the meta-data for the
|
The main bar is a colorful bar that provide all the meta-data for the
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ BlazeComponent.extendComponent({
|
||||||
|
|
||||||
onRendered: function() {
|
onRendered: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (! Meteor.user().isBoardMember())
|
if (! Meteor.userId() || ! Meteor.user().isBoardMember())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$(document).on('mouseover', function() {
|
$(document).on('mouseover', function() {
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ Boards.before.insert(function(userId, doc) {
|
||||||
// In some cases (Chinese and Japanese for instance) the `getSlug` function
|
// In some cases (Chinese and Japanese for instance) the `getSlug` function
|
||||||
// return an empty string. This is causes bugs in our application so we set
|
// return an empty string. This is causes bugs in our application so we set
|
||||||
// a default slug in this case.
|
// a default slug in this case.
|
||||||
doc.slug = getSlug(doc.title) || 'board';
|
doc.slug = doc.slug || getSlug(doc.title) || 'board';
|
||||||
doc.createdAt = new Date();
|
doc.createdAt = new Date();
|
||||||
doc.archived = false;
|
doc.archived = false;
|
||||||
doc.members = [{
|
doc.members = [{
|
||||||
|
|
@ -153,22 +153,13 @@ Boards.before.insert(function(userId, doc) {
|
||||||
// Handle labels
|
// Handle labels
|
||||||
var colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
|
var colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
|
||||||
var defaultLabelsColors = _.clone(colors).splice(0, 6);
|
var defaultLabelsColors = _.clone(colors).splice(0, 6);
|
||||||
doc.labels = [];
|
doc.labels = _.map(defaultLabelsColors, function(val) {
|
||||||
_.each(defaultLabelsColors, function(val) {
|
return {
|
||||||
doc.labels.push({
|
|
||||||
_id: Random.id(6),
|
_id: Random.id(6),
|
||||||
name: '',
|
name: '',
|
||||||
color: val
|
color: val
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// We randomly chose one of the default background colors for the board
|
|
||||||
if (Meteor.isClient) {
|
|
||||||
doc.background = {
|
|
||||||
type: 'color',
|
|
||||||
color: Random.choice(Boards.simpleSchema()._schema.color.allowedValues)
|
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Boards.before.update(function(userId, doc, fieldNames, modifier) {
|
Boards.before.update(function(userId, doc, fieldNames, modifier) {
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,10 @@ Users.helpers({
|
||||||
});
|
});
|
||||||
|
|
||||||
Users.before.insert(function(userId, doc) {
|
Users.before.insert(function(userId, doc) {
|
||||||
doc.profile = {};
|
doc.profile = doc.profile || {};
|
||||||
|
|
||||||
// connect profile.status default
|
// connect profile.status default
|
||||||
doc.profile.status = 'offline';
|
doc.profile.status = 'offline';
|
||||||
|
|
||||||
// slugify to username
|
|
||||||
//doc.username = getSlug(doc.profile.name, '');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Use use the meteor-spk tool to generate a sandstorm package (spk) from this
|
||||||
|
# meteor application source code. https://github.com/sandstorm-io/meteor-spk
|
||||||
@0xa5275bd3ad124e12;
|
@0xa5275bd3ad124e12;
|
||||||
|
|
||||||
using Spk = import "/sandstorm/package.capnp";
|
using Spk = import "/sandstorm/package.capnp";
|
||||||
|
|
@ -10,18 +12,32 @@ const pkgdef :Spk.PackageDefinition = (
|
||||||
# "pkgdef" constant.
|
# "pkgdef" constant.
|
||||||
|
|
||||||
id = "m86q05rdvj14yvn78ghaxynqz7u2svw6rnttptxx49g1785cdv1h",
|
id = "m86q05rdvj14yvn78ghaxynqz7u2svw6rnttptxx49g1785cdv1h",
|
||||||
# Your app ID is actually its public key. The private key was placed in your
|
# The app ID is actually its public key. The private key was placed in your
|
||||||
# keyring. All updates must be signed with the same key.
|
# keyring. All updates must be signed with the same key.
|
||||||
|
|
||||||
manifest = (
|
manifest = (
|
||||||
# This manifest is included in your app package to tell Sandstorm
|
# This manifest is included in our app package to tell Sandstorm about our
|
||||||
# about your app.
|
# app.
|
||||||
|
|
||||||
appVersion = 1, # Increment this for every release.
|
appTitle = (defaultText = "LibreBoard"),
|
||||||
|
# The name of the app as it is displayed to the user.
|
||||||
|
|
||||||
|
appVersion = 2,
|
||||||
|
# Increment this for every release.
|
||||||
|
|
||||||
|
appMarketingVersion = (defaultText = "0.9.0"),
|
||||||
|
# Human-readable presentation of the app version.
|
||||||
|
|
||||||
|
minUpgradableAppVersion = 0,
|
||||||
|
# The minimum version of the app which can be safely replaced by this app
|
||||||
|
# package without data loss. This might be non-zero if the app's data store
|
||||||
|
# format changed drastically in the past and the app is no longer able to
|
||||||
|
# read the old format.
|
||||||
|
|
||||||
actions = [
|
actions = [
|
||||||
# Define your "new document" handlers here.
|
# Define your "new document" handlers here.
|
||||||
( title = (defaultText = "New board"),
|
(
|
||||||
|
title = (defaultText = "New board"),
|
||||||
command = .myCommand
|
command = .myCommand
|
||||||
# The command to run when starting for the first time. (".myCommand" is
|
# The command to run when starting for the first time. (".myCommand" is
|
||||||
# just a constant defined at the bottom of the file.)
|
# just a constant defined at the bottom of the file.)
|
||||||
|
|
@ -43,11 +59,43 @@ const pkgdef :Spk.PackageDefinition = (
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
alwaysInclude = [ "." ]
|
alwaysInclude = [ "." ],
|
||||||
# This says that we always want to include all files from the source map. (An
|
# This says that we always want to include all files from the source map. (An
|
||||||
# alternative is to automatically detect dependencies by watching what the app
|
# alternative is to automatically detect dependencies by watching what the app
|
||||||
# opens while running in dev mode. To see what that looks like, run `spk init`
|
# opens while running in dev mode. To see what that looks like, run `spk init`
|
||||||
# without the -A option.)
|
# without the -A option.)
|
||||||
|
|
||||||
|
bridgeConfig = (
|
||||||
|
viewInfo = (
|
||||||
|
permissions = [(
|
||||||
|
name = "participate",
|
||||||
|
title = (defaultText = "participate"),
|
||||||
|
description = (defaultText = "allows participating in the board")
|
||||||
|
), (
|
||||||
|
name = "configure",
|
||||||
|
title = (defaultText = "configure"),
|
||||||
|
description = (defaultText = "allows configuring the board")
|
||||||
|
)],
|
||||||
|
|
||||||
|
roles = [(
|
||||||
|
title = (defaultText = "observer"),
|
||||||
|
permissions = [false, false],
|
||||||
|
verbPhrase = (defaultText = "can read")
|
||||||
|
), (
|
||||||
|
title = (defaultText = "member"),
|
||||||
|
permissions = [true, false],
|
||||||
|
verbPhrase = (defaultText = "can edit"),
|
||||||
|
default = true
|
||||||
|
# ), (
|
||||||
|
# title = (defaultText = "administrator"),
|
||||||
|
# permissions = [true, true],
|
||||||
|
# verbPhrase = (defaultText = "can configure")
|
||||||
|
#
|
||||||
|
# XXX Administrators configuration options aren’t implemented yet, so this
|
||||||
|
# role is currently useless.
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const myCommand :Spk.Manifest.Command = (
|
const myCommand :Spk.Manifest.Command = (
|
||||||
|
|
|
||||||
101
sandstorm.js
101
sandstorm.js
|
|
@ -8,71 +8,94 @@ var isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||||
// redirect the user to this particular board.
|
// redirect the user to this particular board.
|
||||||
var sandstormBoard = {
|
var sandstormBoard = {
|
||||||
_id: 'sandstorm',
|
_id: 'sandstorm',
|
||||||
slug: 'board',
|
|
||||||
|
|
||||||
// XXX Should be shared with the grain instance name.
|
// XXX Should be shared with the grain instance name.
|
||||||
title: 'LibreBoard',
|
title: 'LibreBoard',
|
||||||
permission: 'public',
|
slug: 'libreboard',
|
||||||
background: {
|
|
||||||
type: 'color',
|
|
||||||
color: '#16A085'
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX Not certain this is a bug, but we except these fields to get inserted
|
// Board access security is handled by sandstorm, so in our point of view we
|
||||||
// by the `Lists.before.insert` collection-hook. Since this hook is not called
|
// can alway assume that the board is public (unauthorized users won’t be able
|
||||||
// in this case, we have to duplicate the logic and set them here.
|
// to access it anyway).
|
||||||
archived: false,
|
permission: 'public'
|
||||||
createdAt: new Date()
|
};
|
||||||
|
|
||||||
|
// The list of permissions a user have is provided by sandstorm accounts
|
||||||
|
// package.
|
||||||
|
var userHasPermission = function(user, permission) {
|
||||||
|
var userPermissions = user.services.sandstorm.permissions;
|
||||||
|
return userPermissions.indexOf(permission) > -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// On the first launch of the instance a user is automatically created thanks to
|
|
||||||
// the `accounts-sandstorm` package. After its creation we insert the unique
|
|
||||||
// board document. Note that when the `Users.after.insert` hook is called, the
|
|
||||||
// user is inserted into the database but not connected. So despite the
|
|
||||||
// appearances `userId` is null in this block.
|
|
||||||
//
|
|
||||||
// If the hard-coded board already exists and we are inserting a new user, we
|
|
||||||
// assume that the owner of the board want to share write privileges with the
|
|
||||||
// new user.
|
|
||||||
// XXX Improve that when the Sandstorm sharing model (“Powerbox”) arrives.
|
|
||||||
if (isSandstorm && Meteor.isServer) {
|
if (isSandstorm && Meteor.isServer) {
|
||||||
|
// Redirect the user to the hard-coded board. On the first launch the user
|
||||||
|
// will be redirected to the board before its creation. But that’s not a
|
||||||
|
// problem thanks to the reactive board publication. We used to do this
|
||||||
|
// redirection on the client side but that was sometime visible on loading,
|
||||||
|
// and the home page was accessible by pressing the back button of the
|
||||||
|
// browser, a server-side redirection solves both of these issues.
|
||||||
|
//
|
||||||
|
// XXX Maybe sandstorm manifest could provide some kind of "home url"?
|
||||||
|
Router.route('/', function() {
|
||||||
|
var base = this.request.headers['x-sandstorm-base-path'];
|
||||||
|
// XXX If this routing scheme changes, this will break. We should generation
|
||||||
|
// the location url using the router, but at the time of writting, the
|
||||||
|
// router is only accessible on the client.
|
||||||
|
var path = '/boards/' + sandstormBoard._id + '/' + sandstormBoard.slug;
|
||||||
|
|
||||||
|
this.response.writeHead(301, {
|
||||||
|
Location: base + path
|
||||||
|
});
|
||||||
|
this.response.end();
|
||||||
|
}, { where: 'server' });
|
||||||
|
|
||||||
|
// On the first launch of the instance a user is automatically created thanks
|
||||||
|
// to the `accounts-sandstorm` package. After its creation we insert the
|
||||||
|
// unique board document. Note that when the `Users.after.insert` hook is
|
||||||
|
// called, the user is inserted into the database but not connected. So
|
||||||
|
// despite the appearances `userId` is null in this block.
|
||||||
Users.after.insert(function(userId, doc) {
|
Users.after.insert(function(userId, doc) {
|
||||||
if (! Boards.findOne(sandstormBoard._id)) {
|
if (! Boards.findOne(sandstormBoard._id)) {
|
||||||
Boards.insert(_.extend(sandstormBoard, { userId: doc._id }));
|
Boards.insert(sandstormBoard, {validate: false});
|
||||||
Boards.update(sandstormBoard._id, {
|
Boards.update(sandstormBoard._id, {
|
||||||
$set: {
|
$set: {
|
||||||
'members.0.userId': doc._id
|
// The first member (the grain creator) has all rights
|
||||||
|
'members.0': {
|
||||||
|
userId: doc._id,
|
||||||
|
isActive: true,
|
||||||
|
isAdmin: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Activities.update({
|
Activities.update(
|
||||||
activityTypeId: sandstormBoard._id
|
{ activityTypeId: sandstormBoard._id }, {
|
||||||
}, {
|
$set: { userId: doc._id }
|
||||||
$set: {
|
|
||||||
userId: doc._id
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// If the hard-coded board already exists and we are inserting a new user,
|
||||||
|
// we need to update our user collection.
|
||||||
|
else if (userHasPermission(doc, 'participate')) {
|
||||||
Boards.update({
|
Boards.update({
|
||||||
_id: sandstormBoard._id,
|
_id: sandstormBoard._id,
|
||||||
permission: 'public'
|
permission: 'public'
|
||||||
}, {
|
}, {
|
||||||
$push: {
|
$push: {
|
||||||
members: doc._id
|
members: {
|
||||||
|
userId: doc._id,
|
||||||
|
isActive: true,
|
||||||
|
isAdmin: userHasPermission(doc, 'configure')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The sandstom user package put the username in `profile.name`. We need to
|
||||||
|
// move this field value to follow our schema.
|
||||||
|
Users.update(doc._id, { $rename: { 'profile.name': 'username' }});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// On the client, redirect the user to the hard-coded board. On the first launch
|
|
||||||
// the user will be redirected to the board before its creation. But that’s not
|
|
||||||
// a problem thanks to the reactive board publication.
|
|
||||||
if (isSandstorm && Meteor.isClient) {
|
if (isSandstorm && Meteor.isClient) {
|
||||||
Router.go('Board', {
|
|
||||||
boardId: sandstormBoard._id,
|
|
||||||
slug: getSlug(sandstormBoard.title)
|
|
||||||
});
|
|
||||||
|
|
||||||
// XXX Hack. `Meteor.absoluteUrl` doesn't work in Sandstorm, since every
|
// XXX Hack. `Meteor.absoluteUrl` doesn't work in Sandstorm, since every
|
||||||
// session has a different URL whereas Meteor computes absoluteUrl based on
|
// session has a different URL whereas Meteor computes absoluteUrl based on
|
||||||
// the ROOT_URL environment variable. So we overwrite this function on a
|
// the ROOT_URL environment variable. So we overwrite this function on a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue