Skip to content
al-Amjad Tawfiq Isstaif edited this page Jul 20, 2013 · 22 revisions

microcommunity

About MicroCommunity

MicroCommunity is a project that aims to build an open source social networking platform, built as a modular system where developers can mix plugins supporting features such as diverse content types production, content organization and curation and collaboration styles. The goal is to make it easy for developers easily create dedicated platforms that encourage content production and knowledge exchange in small-scale communities, or MicroCommunities! We think that those communities for a majority in the online Arab community.

Overview

The current model behind MicroCommunity is about allowing you to create collections of content and users called containers. You can customize those collections by defining new content types, and assigning new membership roles to those users. The model also has the flexibility to allow you to to customize the organization, creation and appearance of those containers.

You can as well consider your website as a one global container. You can have a hybrid approach where you allow a global context next to containers which could be of several types as well.

The final goal is to allow you as a developer to easily develop social experience that exactly match the needs of your community/organization with ease and joy.

Creating a MicroCommunity app

var microcommunity = require('microcommunity')
microcommunity.registerApp(__dirname)
var app = microcommunity.createApplication(__dirname)
//instance configuration
app.listen(3000)

Creating and using a plugin

	microcommunity.registerPlugin(__dirname)
	//global configuration
	module.exports = function(options){
		var app = microcommunity.createPlugin(__dirname)
		//instance configuration
		return app
	}

Using a plugin by an application

	var microcommunity = require('microcommunity')
	var plugin = require('microcmmunity-plugin')
	microcommunity.registerApp(__dirname)
	//global configuration
	module.exports = function(){
		var app = microcommunity.createApplication(__dirname)
		app.use(plugin({ option1 : 'value', option2 : 'value' }))
		//instance configuration
		return app
	}

How to extend MicroCommunity Apps and Plugins

You basically extend an app or a plugin by creating pages with dynamic features or Apps and extending the JSON API of your website.

Creating an App

On the server side, you should describe the mounting point of your app.

//main app
app.get('/', function(req, res){
	res.loadPage('myapp', { message : 'Hello, World!'})
})

In the views directory you should provide the server side template (here it is in jade). The template represents the server template of your app. Here you declare the main regions of your app. Your template is going to be integrated with the main layout of the website. You

You can access the passed data using the global server.data object

h1 = server.data.message

Finally you should provide the javascript app object. Here you place your logic. You should place the app module in the client/apps directory, so the system can identify it and run it in the web page once it is loaded.

define(['app'], function(App){	
	App.addInitializer(function(){
		console.log(server.data.message)
	})		
	return App
})

Extending the JSON API

Extending the JSON API of your website is so much easy thanks to the flexibility of Node and Express.

app.post('/api/request/path', function(req, res){
	res.send(200, { message : 'JSON API'})
}

Defining a new model

var postSchema = new mongoose.Schema({})
microcommunity.models.define('Post', 'post', 'posts', postSchema)

Calling a model and creating an instance

var Post = microcommunity.model('Post')
var new_post = new Post({ content : 'Hello, MicroCommunity!' })

Defining an Item

On the server side, you should create a new model and use the isItem plugin

var microcommunity = require('microcommunity')
	, isItem = microcommunity.models.plugins.isItem

var postSchema = new mongoose.Schema({})
postSchema.plugin(isItem)
microcommunity.models.define('Post', 'post', 'posts', postSchema)

Then you should register the client model Registering an item

microcommunity.items.addItem('Post', 'path/to/client/model')

You should define a client model for an item. Which will have four attributes: contentView, messageTemplate, pluginView, and actions

var Post = Item.extend({	
	contentView : PostView,
	messageTemplate : messageTemplate,
	pluginView : Comments,
	actions : [
		{ label : 'Comment', name : 'comment' }
	]
})

Adding a Wall and a Stream to an object

On the server side you should use the hasWall and hasStream plugin

var hasWall  = microcommunity.models.plugins.hasWall
  , hasStream = microcommunity.models.plugins.hasStream
// ... schema definition
userSchema.plugin(hasWall, { displayNameAttribute : 'displayName', wallType : 'user' })
userSchema.plugin(hasStream)

To load the wall or a stream of an item, use the loadItems method on the stream or wall model.

User.findById(id, function(err, user){
	Wall.loadItems(user.wall, function(err, items){
		res.loadPage("yourApp", {items : items})
	}) 
})

On the client side you should use the Stream App module. This can be both used with a stream or with a wall.

var options = { items : server.data.items, type : "stream" }	
var Stream = streamModule(App, App.stream, options)

Using a Publisher

You usually use a Publisher with a wall in order to create.

var options = {
	wall : App.currentUser.get('wall'),
	publishers : [PostPublisher, PhotoPublisher]
}		
var Publisher = publiserhModule(App, App.publisher, options)	

In order to define a publisher, you should use the following interface. You should implement an exportData method, that should return an object that includes the set of item attributes after you retrieve them from the form controls.

var PostPublisher = Backbone.Marionette.ItemView.extend({

	// ... other view options
	
	exportData : function(){
		return {
			content : this.ui.content.val()
		}						
	}										
})	

return { 
	objectType : 'post', 
	icon : 'icon-pencil', 
	label : 'Post', 
	view :  PostPublisher 
}

Authentication

Authentication is provided by default. You can check if user is logged in on server side using the ensureAuthenticated middleware. The user object is attached to the request object as req.user

app.get('/some/path', auth.ensureAuthenticated, function(req, res){ 
})

On the client side you can use the isLoggedIn method.

if (App.isLoggedIn()) //your code

Global roles

Global roles for your users is supported as well. You need to configure basic information in the site.json file in your app directory.

{
	"rootUser" : "[email protected]",
	"roles" : ["student", "teacher"],
	"defaultRole" : "student"
}

You can access the user role user.role. You are also provided with helper methods. On server you can use these middlewares to ensure the user has the root role, or has a specific role.

//ensure root role
app.get('/root/area', auth.ensureRoot, function(req, res){ 
})

//ensure teacher role
app.get('/teacher/area', auth.ensureRole('teacher'), function(req, res){ 
})

On the client side, you have the corresponding methods.

if (App.isRootUser()) //code for root user
if (App.hasRole('teacher')) //code for teacher

Authorization

MicroCommunity uses an action-based authorization system. A user can be 'authorized' to perform a certain action on a certain type of object. Special information will be attached to that object that will help you implement functional security in your application, both on the server side and client side.

An example of authorizing the current user to delete an item.

var can = require('microcommunity').can
can.authorizeCollection(item, 'item', 'delete', req.user, function(err, item){
    console.log(item.can.comment) //result will be true
})			

You can use a middleware as well. It will return an error code if the current user does not have the sufficient privileges for the specified action. However, you should fetch that object from the database by another middleware and providing it as an attribute of the request object, with the objectType name. In this example the item should be at req.item.

app.delete('/api/items/:item/', fetchItem, can.authorizeMiddlewareAPI('item', 'delete'),function(req, res){	
//request handler code
})

If you send this object to the client. There is a tiny Backbone helper method that you can call:

Item.can('delete') //true

Defining the code to handle the authorization is easy. Registration is as follows:

microcommunity.can.define('item', 'delete', authorizationHandler)

The handler will be passed the user how is going to be authorized, the object on which the authorization will occur and the action to be authorized, and callback to be called (which allows you to perform asynchronous authorization).

function authorizationHandler(item, user, callback){
//performing authorization
}

In order to perform the actual authorization, you need to use the authorization helper attachAction. You pass the action you need to authorize or de-authorize.

var helpers = microcommuity.can.helper
function authorizationHandler(item, user, callback){
    helpers.attachAction(item, 'delete', false)
    callback(null, item)
}

Container

You should first define your model as a container.

var isContainer = microcommunity.models.plugins.isContainer
var groupSchema = new mongoose.Schema({ /* schema definition */ })

var containerOptions = { 
	containerType : 'group',
	displayNameAttribute : 'displayName'
}
groupSchema.plugin(isContainer, containerOptions)
microcommunity.models.define('Group', 'group', 'containers', groupSchema)

Container membership

//ensure some role in the container
app.get('/some/comtainer/:container/path', auth.ensureContainerRole('role'), function(req, res){ 
})

//ensure member
app.get('/some/comtainer/:container/path', auth.ensureContainerMember('role'), function(req, res){ 
})

//ensure admin
app.get('/some/comtainer/:container/path', auth.ensureContainerAdmin('role'), function(req, res){ 
})

On the client side:

if (App.hasContainerRole('role')) //some role in the container
if (App.isContainerMember()) //container member 
if (App.isContainerAdmin()) //container admin

In order to make a user a member of a container you should create a mbmership object first, then assign the roles you want to that user.

container.newMembership(req.user)
container.addRole(req.user, 'mc:admin')	//granting adminstration role
container.addRole(req.user, 'mc:member') //granting basic membership role
container.save(function(err){

})

In order to make a content part of a container you only need to use the isContent plugin, and provide the container attribute to the object.