Social Widgets

Scope

In this example we are going to work through the process of
building a basic SocialOS application, a stream widget, that can
be embedded inside other web pages to provide social functionality.
We will be building the application in Python using the CherryPy
framework.

We will be using the Mustache template library to build the
application web pages. This is available as a library across Python,
Ruby, JavaScript, and most other major languages, so applications
written in different languages easily can share common UI.

We will be using the Pyhon client library discussed in the Client Library
example, along with MongoDB for local storage.

Features

For our initial application, we'll be building a simple social network.
We'll need the ability for users to sign up for our network; post and
respond to messages (reply, repost, comment and so on); and the ability
to create, join, and use networks. This only requires three out of about
twenty of the API classes provided by Social OS, but as we'll see,
that's enough to provide a very functional application.

There are a few things we get for free - authentication and
authorisation are handled by the User API; the Message API provides f
or replies, reposts, comments, private messages, and full-text search
without any work by our app; and the Network API will handle membership,
moderation and administration functions for us.

Let's take a look at the framework for our application. This is a bit
longer than any of our previous code samples, but is still relatively
compact and readable.

At this point, we are not yet rendering web pages - just returning the
responses from the API methods. Our page creation will be handled by
the page module using Mustache templates, and we'll examine that process
in our next article.

Code

To run this code you will need to install the following Python modules (via pip or easy_install):

  • CherryPy
  • MongoEngine (also installs PyMongo)
  • PyCrypto
  • Requests

Client Library

# coding=utf-8
version = '1.0.1'

from conf import domain
from page import page
from api import API, Session

from tools import log, getcookie, start


# noinspection PyDictCreation
class App(object):
    @page('default')
    def index(self):
        return 'Hello world!'

    @page('home')
    def login(self, user, password):
        Session.login(user, password)

        api = API()
        api.auth()
        data = {}
        data['user'] = api.get('user')
        data['messages'] = api.get('messages/user/%s' % data['user']['id'])
        return data

    @page('default')
    def logout(self):
        Session(token=getcookie('session').logout())

    @page('default')
    def signup(self, user, password):
        return API.easyadmin('user/add', action='POST', data={'user': user, 'password': password, 'domain': domain})


# noinspection PyDictCreation
class User(object):
    @page('home')
    def index(self):
        api = API()
        api.auth()
        data = {}
        data['user'] = api.get('user')
        data['messages'] = api.get('messages/user/%s' % data['user']['id'])
        return data

    @page('home')
    def update(self, **keywords):
        api = API()
        api.auth()

        api.patch('user', data=keywords)

        data = {}
        data['user'] = api.get('user')
        data['messages'] = api.get('message/user/%s' % data['user']['id'])
        return data

    @page('users')
    def search(self, query):
        api = API()
        api.auth()
        data = {}
        data['users'] = api.search('user/query/%s' % query)

        return data


# Message posting and viewing
class Message(object):
    @page('messages')
    def index(self):
        return {'messages': API.easy('message')}

    @page('messages')
    def inbox(self):
        return {'messages': API.easy('message/inbox')}

    @page('messages')
    def network(self, network):
        return {'messages': API.easy('message/network/%s' % network)}

    @page('messages')
    def add(self, text, networks=None):
        if not networks:
            networks = []
        return {'messages': API.easy('message', action='POST', data={'text': text, 'networks': networks})}

    @page('messages')
    def reply(self, id, text, networks=None):
        if not networks:
            networks = []
        return {'messages': API.easy('message', action='POST',
                                     data={'type': 'reply', 'id': id, 'text': text, 'networks': networks})}

    @page('messages')
    def repost(self, id, text=None, networks=None):
        if not networks:
            networks = []
        return {'messages': API.easy('message', action='POST',
                                     data={'type': 'repost', 'id': id, 'text': text, 'networks': networks})}

    @page('messages')
    def comment(self, id, text=None, networks=None):
        if not networks:
            networks = []
        return {'messages': API.easy('message', action='POST',
                                     data={'type': 'comment', 'id': id, 'text': text, 'networks': networks})}

    @page('messages')
    def update(self, id, text, networks=None):
        if not networks:
            networks = []
        return {'messages': API.easy('message/%s' % id, action='PATCH',
                                     data={'text': text, 'networks': networks})}

    @page('messages')
    def pm(self, to, text):
        return {'messages': API.easy('message' % id, action='POST',
                data={'to': to, 'text': text, 'private': True})}

    @page('messages')
    def delete(self, id):
        api = API()
        api.auth()
        api.delete('message/%s' % id, action='DELETE')
        return {'messages': API.easy('message')}

    @page('messages')
    def search(self, query):
        return {'messages': API.easy('message/search', data={'query': query})}


# Network management
class Network(object):
    @page('networks')
    def index(self):
        return {'networks': API.easy('networks')}

    @page('networks')
    def add(self, **keywords):
        api = API()
        api.auth()
        network = api.post('network', data=keywords)
        user = api.get('user')
        api.post('network/%s/join/%s' % (network['id'], user['id']))

        return {'networks': api.get('network/user/%s' % user['id'])}

    @page('networks')
    def update(self, **keywords):
        api = API()
        api.auth()
        api.patch('network', data=keywords)
        user = api.get('user')

        return {'networks': api.get('network/user/%s' % user['id'])}

    @page('home')
    def join(self, network):
        api = API()
        api.auth()
        api.post('network/%s/join' % network)
        return {'user': api.get('user')}

    @page('networks')
    def search(self, query):
        return {'networks': API.easy('network/search', data={'query': query})}


# Bind all app classes into a single instance
app = App()
app.user = User()
app.message = Message()
app.network = Network()

# And start the app
log('Starting Social OS Network sample app version %s.' % version)
start(app)