Overview

Nodes

Primary database where all content is stored.

Keys

/ character in key has some special meaning and separates different document types.

<slug>

A node.

Example:

my-cool-entry

<year>/<month>/<day>/<slug>

A time based node. To avoid name conflicts, nodes like news, blog posts, etc., are prefixed with date.

Example:

2012/01/02/my-blog-post

h/<node-id>/<timestamp>

History entry for a node. This key consists of seconds sins 1970 and nano seconds.

Example:

h/2012/01/02/my-blog-post/1325887114-195708362

<uuid>

Nodes without title, for example, comments.

Example:

74bb1021-e1a1-4b77-91a0-7a076cb0e39e

Fields

type

Node type. Possible types:

article

comment

debate

discussion

image

hardware

This node describes a hardware.

Full example:

'lenovo-thinkpad-x61-tablet': {
    'type': 'hardware',
    'created': '2012-01-07',
    'updated': '2012-01-07',
    'author': 'sirex',
    'tags': [
        'lenovo',
        'laptop',
        'machine',
    ],
    'hardware' {
        'type': 'machine',
        'machine-type': 'laptop',
        'make': 'Lenovo',
        'model': 'ThinkPad X61 Tablet'
    }
},
'intel-gm965-gl960': {
    'type': 'hardware',
    'created': '2012-01-07',
    'updated': '2012-01-07',
    'author': 'sirex',
    'tags': [
        'intel',
        'video',
    ],
    'hardware': {
        'type': 'video',
        'make': 'Intel',
        'model': 'GM965/GL960'
    },
}
    'audio': {
        'make': 'Intel',
        'model': '82801H (ICH8 Family) HD'
    },
    'wifi': {
        'make': 'Intel',
        'model': 'PRO/Wireless 4965 AG or AGN [Kedron]'
    }

hardware-compatibility

This node is used to describe hardware that is used be users.

Full example:

'3aad7045-4c6a-4abc-8fce-44e90bb3e751': {
    'type': 'hardware',
    'created': '2012-01-07',
    'updated': '2012-01-07',
    'author': 'sirex',
    'tags': [
        'laptop',
        'lenovo',
        'ubuntu-11-10',
        'lenovo-thinkpad-x61-tablet'
    ],
    'mashine': '114ec740-64f0-4026-89df-9ae71c6f4f86',
    'distribution': 'ubuntu-11-10',
    'hardware': {
        'type': 'machine',
        'make': 'lenovo',
        'model': 'thinkpad-x61-tablet'
    }
    'compatibility': 5,
    'body': 'Works out of the box!'
}

Here compatibility is a number from 1 to 5:

1

Does not work at all.

2

Does not work out of the box bug it is possible to make it work.

3

Does not work out of the box but it is easy to make it work.

4

Does not work out of the box due proprietary drivers that must be installed manually.

5

Works out of the box without any problems.

link

poll

question

redirect

This node type is used to track node slug changes.

Example scenario:

  1. Node slug changed from my-cool-node to my-very-cool-node.
  2. New node copy with slug my-very-cool-node created.
  3. History node with name h/my-very-cool-node/1325887114-418302237 created.
  4. Old node my-cool-node type changed to redirect.

When some one visits my-cool-node, then he will be automatically redirected to my-very-cool-node.

tag

screenshot

history

created

updated

author

Original node author, who created this node.

tags

Node tags.

parents

Parent nodes of this node.

title

body

Node body, where main node text in reStructuredText format is stored.

history

This field is used only for history nodes. All other fields should not be touched. All history related information must be stored here, to be possible easily restore node to it's original state.

type

Original node type.

created

When this history record was created.

author

Who created this history node.

action

What was changed. Possible actions are:

  • change
  • type-change
  • like
  • dislike

likes

How many people like this node.

dislikes

How many people dislike this node.

solves

Flag used for question type nodes and specifies, that this node solves a problem described in parent node.

migration

Migration specific field.

source

Source, from where this node was migrated. Can be phpbb, drupal, ...

origurl

Original url, how this node was published before migration.

Example for phpbb:

forum/viewtopic.php?f=1&t=2955

This field is used to redirect users from old urls to new ones.

Defining nodes

All nodes are defined in settings.py file:

SBOARD_NODES = [
    'sboard.articles.nodes.ArticleNode',
    'sboard.discussions.nodes.DiscussionNode',
    'sboard.hardware.nodes.HardwareNode',
    'sboard.hardware.nodes.HardwareCompatibilityNode',
    'sboard.links.nodes.LinkNode',
    'sboard.polls.nodes.PollNode',
    'sboard.questions.nodes.QuestionNode',
    'sboard.screenshots.nodes.ScreenshotNode',
]

Some core nodes are always defined, here is list of core nodes:

sboard.nodes.BaseNode
sboard.nodes.CommentNode
sboard.nodes.HistoryNode
sboard.nodes.ImageNode
sboard.nodes.RedirectNode
sboard.nodes.TagNode

Each specified node is class, responsible for all node related operations. All node classes should extend BaseNode class:

from sboard.nodes import BaseNode


class MyNode(BaseNode):
    slug = 'my-node'
    name = _('My node')

Node API

Each node class can override these method and attributes:

slug (required)

Defines node slug.

name (required)

Defines user friendly node name.

model

Node model class.

form

Node form class.

create(request)

View for node create action.

read(request)

View for node read action.

update(request)

View for node update action.

delete(request)

View for node delete action.

list_entry(node)

Node list entry renderer, than can change context variables and override template.

Profiles

Keys

Profile keys are simple user names.

Fields

karma

Karma is increased each time, when someone likes a node created by this user. Also user losses his karma, when someone dislikes his node. Karma value can be negative and if karma becomes big enough below zero, then user automatically will be banned.

User automatically gets more privileges, when his karma becomes bigger.

Possible karma value interpretation, provided numbers means, that user has specified karma or more to be able to do what is described:

-9
  • can create new nodes and write comments on existing nodes

2

  • can like nodes

5

  • can edit editable nodes created by other users

10

  • can dislike nodes

30

  • can edit node tags
  • can specify a comment, that solves described problem

50

  • can edit tag nodes

hardware

This field contains list of hardware that this user uses.

Example:

[
    {
        'machine-id': '114ec740-64f0-4026-89df-9ae71c6f4f86',
        'distribution': 'ubuntu-11-10',
        'updated': '2012-01-07',
        'machine': {
            'type': 'laptop',
            'make': 'Lenovo',
            'model': 'ThinkPad X61 Tablet'
        },
        'video': {
            'make': 'Intel',
            'model': 'GM965/GL960'
        },
        'audio': {
            'make': 'Intel',
            'model': '82801H (ICH8 Family) HD'
        },
        'wifi': {
            'make': 'Intel',
            'model': 'PRO/Wireless 4965 AG or AGN [Kedron]'
        }
    }
]

Profile extensions

User profiles can be extended using profile extensions. Profile extensions are defined in settings.py file:

SBOARD_PROFILE_EXTENSIONS = [
    'migrations.Migrate',
    'hardware.Hardware',
    'support.Support',
]

Urls

/create/

Handled by <node>.create(). View for creating new top level node without parent.

/<slug>/

Handled by <node>.read().

/<slug>/create/

Handled by <node>.create(). View for creating node with given <slug> as parent.

/<slug>/update/

Handled by <node>.update(). This view displays node form and handles form post.

/<slug>/delete/

Handled by <node>.delete(). This view displays node deletion confirmation form and handles deletion form post.

/profiles/<username>/

User profile, handled by sboard.views.profile_details.

/<url>

Handled by <migration>.not_found. This view is used to handle everything, that was not handled and used to do redirects from old urls to new ones.

If even old url not found, then this view raises 404 error.

Migration scripts

Migration scripts are defined in settings.py file:

SBOARD_MIGRATION_SCRIPTS = {
    'phpbb': {
        'app': 'phpbb.PhpBB',
        'dbi': 'mysql://root@localhost/phpbb?charset=utf8&use_unicode=1',
        'version': '3.0.6',
    }
    'drupal': {
        'app': 'drupal.Drupal',
        'dbi': 'mysql://root@localhost/drupal?charset=utf8&use_unicode=1',
        'version': '6.14',
    }
    'mediawiki': {
        'app': 'mediawiki.MediaWiki',
        'dbi': 'mysql://root@localhost/mediawiki?charset=utf8&use_unicode=1',
        'version': '1.15.1',
    }
}

Permissions

All permissions are stored in dictionary of 4-tuple keys and karma values:

{
    (<action>, 'owner', None,         <node>|None): <karma>|None,
    (<action>, 'user',  <user>|None,  <node>|None): <karma>|None,
    (<action>, 'group', <group>,      <node>|None): <karma>|None,
    (<action>, 'all',   None,         <node>|None): <karma>|None,
}

When we need to check for permission, method that is responsible for permission checking, generates list of keys to check, then gets all defined permissions and tries to find if any of key in defined order exists in permissions dictionary.

For example if we want to check if user user1 that is member of group1 and group2 groups, has 'create' permission, to create 'comment' node in node1 node. Then permissions will be checked in this way:

  1. Generate list of permission keys, to check, in this example we will get this list of keys:

    [
        ('create', 'owner', None,     'comment'),
        ('create', 'owner', None,     None),
        ('create', 'user',  'user1',  'comment'),
        ('create', 'user',  'user1',  None),
        ('create', 'group', 'group1', 'comment'),
        ('create', 'group', 'group1', None),
        ('create', 'group', 'group2', 'comment'),
        ('create', 'group', 'group2', None),
        ('create', 'all',   None,    'comment'),
        ('create', 'all',   None,    None),
    ]
    

    This list of permission keys is ordered by how permission is specific. Most specific permissions goes to top of list and least specific to bottom.

    Here, first key:

    ('create', 'owner', None,     'comment'),
    

    checks if user is owner of parent node and if he can create comment node in that parent node.

    Second key:

    ('create', 'owner', None,     None),
    

    checks if user is owner of parent node and if he can create node of any type.

  2. Then, permissions will be collected from these places and in this order:

    1. Get permissions from node view class.
    2. Get permissions from all ancestors of node instance starting from least immediate ancestors.
    3. Get permissions from node instance.
    4. Each node view class has possibility to override final set of permissions.

    Each next gathered permissions dictionary will override previous.

  3. Finally all generated keys are checked with collected dictionary.

    If we have this collected permissions dictionary:

    {
        ('create', 'all',   None,    'comment'): None,
    }
    

    Since dictionary returns None value, it means, that creating comments are not allowed.

    If we would have this collected permissions dictionary:

    {
        ('create', 'all',   None,    'comment'): 10,
    }
    

    This would mean, that only users, that have more or equal karma points that 10, can create comments.

    If we would have this collected permissions dictionary:

    {
    }
    

    This would mean, that no one has any permissions.

Anonymous users

To specify permissions to anonymous users, you must provide None for all permission (third permission key tuple item):

{
    ('create', 'all',   None,    'comment'): None,
}

This permissions dictionary tells, that anonymous users can not create comments.

Authenticated users

To specify permissions to authenticated users, you must provide authenticated value for all:

{
    ('create', 'all',   'authenticated',    'comment'): 10,
}

This permissions says, that only authenticated users, that has 10 or greater karma, can create comments.

Permission actions

create
If user can create a child node to this node.
update
If user can update this node.
delete

If user can delete this node.

If user can delete this node and node has children, first permissions of all children must be checked and if user does not have permission to delete at least one of children, then he also can not delete this node.

convert
If user can convert this node to other node.
move
If user can change parent of this node.
tag
If user can tag this node.
untag
If user can remove tags from this node.
settings
If user can change settings of this node.
permissions
If user can change permissions of this node.