To register or not register

I’m at an impasse at the moment with regard to the forum. The classic way to run a forum was to create a user account with username, password and email that tied each and every post to a particular user. This made viewing the history of a user and establishing a reputation easy, but it also meant established users asserted their authority quite often. Sometimes objectionably.

Then there’s the ye olde imageboard system where a user may enter a name and password, but it’s only tagged per post via a pseudo-unique identifier. I’m not sure if this method is better than the registration, but it does cut down on the code requirements. It also makes viewing a user history more difficult as the system deliberately caters to anonymous posting first.

4Chan, the most well known imageboard in the West, uses this system as well. Something it inherited from 2Ch, the most famous textboard in the East. Despite 4Chan’s reputation as a wretched hive of scum and villainy a la Mos Eisley, there are sections that are remarkably well kept despite the anonymity. I’ve even seen intelligent and remarkably humane discussions take place, on a few salient boards.

Of course, registration doesn’t  automatically make for a well kept community either. Reddit, for example, can easily surpass the reputation of 4Chan. A cursory browse of some of the more unsavory subreddits can easily depress the most optimistic folks with an unshakable faith in humanity. Likewise, there are others that offer the same or better intelligent content as well. Of course, it also offers many other flavors that don’t quite fit anywhere on the spectrum of discussion.

The difference, then, is moderation.

I’m trying to create a voting system that, while remaining anonymous, still affords users a voice at a balanced volume in determining what should be promoted to the front page or remain in the “New/Firehose” section or which ones should be nuked from orbit. I also want to ensure voting power decreases over time. I.E. When a post is new, all votes for or against it count more than when it’s a few hours old. I think this prevents excessive judgment with the hindsight of over-analyzed social norms which, for better or worse, tend to be overcorrected. The user interface and online disinhibition make sound judgments more difficult, but we should all know what is obviously wrong upon first read and right away take appropriate action.

This level of self-moderation with rare moderator intervention early can work as long as consistency is maintained. I don’t believe in excessively long codes of conducts, which are seldom followed by those intent on not following them anyway. I mean the first law in all civil discourse is “Don’t be an ass”. How hard is that? Those obviously being asses are easy to spot and should have their candy taken away.

In that regard, I’m still following the old tried and true approach to community building and moderation. Least amount of friction, least amount of fluff, brutally simple and consistent.

Right then. Onward to building the damn thing.

Forum schema

I’ve been working slowly, but steadily, on the forum lately and thought I’d put up the schema I’ve been testing against for the past few weeks. This is for SQLite since it’s very simple to setup, change things and if necessary, completely scrap and start over without having to mess with a DB server.

I’ve been using the SQLite Manager plugin for Firefox to make quick changes to the schema on the go. I’ll post snippets of the rest of the script in increments when I have time to clean up and maybe I’ll post the whole thing on GitHub for the alpha release.

Maybe this will serve as a good starting point for someone else looking for ideas as well.

DROP TABLE IF EXISTS users;
CREATE TABLE users (
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	username VARCHAR NOT NULL UNIQUE, 
	password TEXT NOT NULL, 
	email VARCHAR NOT NULL UNIQUE, 
	token VARCHAR NOT NULL,
	bio TEXT, 
	pubkey TEXT, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	updated_at DATETIME NOT NULL , 
	status INTEGER NOT NULL DEFAULT 0
);

CREATE UNIQUE INDEX users_name_idx ON users (username, email);
CREATE INDEX users_activity_idx ON users (created_at, updated_at);
CREATE INDEX users_status_idx ON users (status);



DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	title VARCHAR,
	url VARCHAR,
	author VARCHAR NOT NULL, 
	user_id INTEGER NOT NULL DEFAULT 0, 
	summary TEXT NOT NULL, 
	body TEXT NULL, 
	reply_count INTEGER NOT NULL DEFAULT 0, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	updated_at DATETIME NOT NULL, 
	lastreply_at DATETIME NOT NULL, 
	status INTEGER NOT NULL  DEFAULT 0
);

CREATE INDEX posts_activity_idx ON posts (created_at, updated_at, lastreply_at);
CREATE INDEX posts_author_idx ON posts (author, user_id);
CREATE INDEX posts_url_idx ON posts (url) WHERE url IS NOT NULL;
CREATE INDEX posts_status_idx ON posts (status);



DROP TABLE IF EXISTS posts_family;
CREATE TABLE posts_family(
	parent_id INTEGER NOT NULL, 
	child_id INTEGER NOT NULL, 
	last_author VARCHAR NOT NULL, 
	last_id INTEGER NOT NULL, 
	lastreply_at DATETIME NOT NULL
);

CREATE INDEX posts_family_idx ON posts_family (parent_id, child_id);
CREATE INDEX posts_family_activity_idx ON posts_family 
	(last_author, last_id, lastreply_at);



DROP TABLE IF EXISTS posts_flags;
CREATE TABLE posts_flags(
	post_id INTEGER NOT NULL, 
	user_id INTEGER NOT NULL, 
	reason VARCHAR NOT NULL, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	status INTEGER NOT NULL DEFAULT 0
);

CREATE INDEX posts_flags_idx ON posts_flags (post_id, user_id);
CREATE INDEX posts_flags_created_at_idx ON posts_flags (created_at);
CREATE INDEX posts_flags_status_idx ON posts_flags (status);



DROP TABLE IF EXISTS taxonomy;
CREATE TABLE taxonomy (
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	label VARCHAR,
	term VARCHAR,
	post_count INTEGER NOT NULL DEFAULT 0, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	body TEXT NULL
);

CREATE INDEX taxonomy_created_at_idx ON taxonomy (created_at);
CREATE UNIQUE INDEX taxonomy_term_idx ON taxonomy (label, term);
CREATE INDEX taxonomy_count_idx ON taxonomy (post_count);



DROP TABLE IF EXISTS taxonomy_posts;
CREATE TABLE taxonomy_posts(
	taxonomy_id INTEGER NOT NULL, 
	post_id INTEGER NOT NULL
);

CREATE UNIQUE INDEX taxonomy_posts_idx ON taxonomy_posts (taxonomy_id, post_id);



DROP TABLE IF EXISTS pms;
CREATE TABLE pms (
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	title VARCHAR,
	sender_id INTEGER NOT NULL, 
	receiver_id INTEGER NOT NULL, 
	body TEXT NULL, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	status INTEGER NOT NULL  DEFAULT 0
);

CREATE INDEX pms_contact_idx ON pms (sender_id, receiver_id);
CREATE INDEX pms_created_idx ON pms (created_at);
CREATE INDEX pms_status_idx ON pms (status);



DROP TABLE IF EXISTS sessions;
CREATE TABLE sessions(
	id VARCHAR PRIMARY KEY, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	updated_at DATETIME NOT NULL,
	skey VARCHAR NOT NULL,
	data TEXT NOT NULL
);

CREATE INDEX sessions_activity_idx ON sessions (created_at, updated_at);



DROP TABLE IF EXISTS firewall;
CREATE TABLE firewall(
	id INTEGER PRIMARY KEY AUTOINCREMENT, 
	label VARCHAR,
	response VARCHAR,
	ip VARCHAR,
	ua VARCHAR,
	protocol VARCHAR,
	method VARCHAR,
	uri VARCHAR,
	headers TEXT NOT NULL, 
	request_at DATETIME NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX firewall_ip_idx ON firewall (ip, label);
CREATE INDEX firewall_response_idx ON firewall (response);
CREATE INDEX firewall_activity_idx ON firewall (request_at, created_at);



DROP TABLE IF EXISTS blocklist;
CREATE TABLE blocklist(
	ip VARCHAR PRIMARY KEY, 
	label VARCHAR,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	expires_at DATETIME NOT NULL
);

CREATE INDEX blocklist_label_idx ON blocklist (label);
CREATE INDEX blocklist_dates_idx ON blocklist (created_at, expires_at);



DROP TABLE IF EXISTS settings;
CREATE TABLE settings(
	id VARCHAR PRIMARY KEY, 
	data TEXT NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX settings_created_idx ON settings (created_at);



DROP TABLE IF EXISTS history;
CREATE TABLE history(
	post_id INTEGER NOT NULL,
	user_id INTEGER NOT NULL  DEFAULT 0, 
	anon_id VARCHAR, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX history_idx ON history (post_id, user_id, anon_id);
CREATE INDEX history_created_idx ON history (created_at);



DROP TABLE IF EXISTS actions;
CREATE TABLE actions(
	post_id INTEGER NOT NULL,
	user_id INTEGER NOT NULL  DEFAULT 0, 
	anon_id VARCHAR, 
	command VARCHAR, 
	auth_key VARCHAR, 
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 
	expires_at DATETIME NOT NULL
);

CREATE UNIQUE INDEX actions_idx ON actions (post_id, user_id, anon_id);
CREATE INDEX actions_auth_key_idx ON actions (auth_key);
CREATE INDEX actions_activity_idx ON actions (created_at, expires_at);

Harshest Forum rules Ever!

I’m working on a little forum side project and sent out some feelers to a few friends on who would like to help me moderate. I got a reply back from someone (and this was totally not a surprise) who not only volunteered and happened to be a co-admin with me before, but also forwarded a set of block messages for filtered posts that I had written 12 years ago! My how times change.

He said this was on a backup CD of the code found in some drawer.

We have filtered your post for the following reason(s) :

  • Your post is mostly in uppercase.
    Besides being harder to read on a screen, writing in uppercase is considered rude in online forums and is the text equivalent (since we can’t hear you) of yelling.
  • Your post has a high number of exclamation marks.
    This is totally unnecessary and gets on the readers’ nerves. One is good, two is OK, three is pushing it and four or more is just plain idiotic.
  • Your post is incomprehensible.
    Your post appears to be written in English, but it is unlike any form of the language we know. It would be nice if you are able to post something rest us can understand. Grammar, logic and coherence are vitally important.
  • Your post is in the wrong language forum.
    Obviously not every visitor is completely fluent in English, but since English is the primary language of this forum, we’ll have to move it to the appropriate language forum.
  • You posted something that has been repeated ad nauseam.
    It’s good to drive home a point if you truly believe it, but it’s not good to do so in an almost identical fashion to previous posts. Please be original.
  • Your post is short, but still contained “ALL YOUR BASE”.
    This usually means there’s a high likelihood your post is really quite stupid, unproductive or quite possibly both.
  • Your post might be spam.
    Our community has flagged your post for our review. This could be a misidentification, in which case we’ll let it through shortly, or if it is spam, it will be deleted.

Please familiarise yourself with the etiquette of posting here; feel free to browse and get used to the posts that did get published so you may add to them soon.

Whenever a post was held in moderation on the forum, the commenter would be shown some combination of these.

These were the preprogrammed responses to one of the very, very, very old forum software I wrote, which ran on my Windows 2000 server with ASP and VBScript (HA!)

Can’t wait to take it apart and see how far I’ve come. Totally looking forward to a nostalgia trip.

RE: WordPress.com Forum Options

This started off, yet again, as just a comment to a post, but as usual it started spreading to multiple paragraphs so I figured a post would do. Also, I wanted to properly answer those discussion questions.

For long-term use, wordpress.com isn’t really a viable option for hosting a forum. That’s not to say you technically can’t run a forum here, but it’s just not practical.

As mentioned, a blog isn’t a forum or a wiki and this is due to fundamental differences that are just not possible to reconcile without an actual forum “module” of sorts. And since hosted sites don’t have the option of custom plugins, that leaves bbPress — from the same folks who bring you WordPress and integrates with existing blogs — or Vanilla forums, which is a nice minimal forum also with good WP integration.

Fun fact: The WP support forums are using bbPress!

Of course, that also means running your own WP installation with no back-end bug-fix and feature boost goodness that we’ve been spoiled with here.

There are forum plugins for WordPress, but my experience with many of those haven’t been positive. That’s not to say the plugin authors haven’t done the job as well as they can, but there’s just no match for proper stand-alone software packages like Vanilla or bbPress which comes with their own support communities, plugin ecosystem and excellent customization options.

Both forums have very low barriers for participation, just like WordPress, which I think is the biggest factor in achieving critical mass. After all, no one goes to an empty restaurant.

And I do think it’s good to have a blog alongside a forum. A forum should be for discussion, not for “news of the forum” or even rules, which I think distracts from the discussion. Put those in the FAQ page of the blog and you clear up the forum for discussion. A community should have the least number of barriers or steps for communicating, the core function of a community, and sometimes extra features can get in the way of just plain talking.

I also have some philosophical objections to ProBoards in that every post you do make there is technically their property. And there’s no “export” feature for all your content, which is something that even WordPress.com offers; so a bit of a deal-breaker for me there. I think the forum you create should be yours and your users’ just like your blog.

Onward to the The Discussion : I’ll go from 3 – 1 in order of increasing verbosity. ;)

Have you considered creating an online forum to complement your blog?

For this blog? No because it’s basically a brain dump.

But I do have plans for a community that’s in the works and it will be something different from what I’ve done before and it will most definitely have a separate blog for announcements and other forum related info. If I’ve learned anything from social media, it’s that the fewer boundaries there are to participation, the quicker your community will grow so I’m going to put those lessons to good use.

It will not be a new social network! ;)

Do you have a forum in conjunction with your blog? Or are you a member of a forum operated in conjunction with a blog?

Every forum I’ve run had some sort of separate feed for notices.

I think every discussion forum can benefit with a blog or some other separate entity to discuss happenings of the forum. This allows the discussions to be clear of clutter while making clear provisions for routine announcements, news etc…

I’ve seen that forums that are just forums and are still online have either been on the net for a very long time with an already established user base or have a separate blog or site for sharing information about the forum. Newer forums will definitely benefit from having a separate blog for forum matters.

The forums that don’t have them tend to clutter up very quickly with nonsensical rubbish (unless that’s the goal of the community) or just talk about the forum so a separate entity is a must to keep discussions clear.

Have you ever set up and operated an online forum?

Lots! :D

I used to run my own private network from around the mid 90’s to mid 2000’s before I stepped down as admin and we had a pretty big community. There were no “forums” in the conventional sense. It’s what would be called today a distributed forum or a cloud forum, in that posts are their own entities and were grouped only by tags (minimum of 1 and maximum of 5) and each tag is a mini-forum of sorts. A new “forum” is made when a new tag is created for the first time, but we were able to merge tags, and we could also feature popular tags. So in a way, it was a bit like Twitter’s hashtags.

My old network and forum

I’ve also hosted and moderated on open forums as well.

I can tell you, the hardest part of running a community is the moderation. There’s really no automated substitute for proper moderation and we found that being rude to new users is the best way ruin a fledgling community. Any posts asking questions that have already been answered can be locked with a link to the answer while sparing a snarky RTFM!

The community fell apart with too much and too little moderation. But it may come back soon.

And I found that requiring registration or otherwise stopping posts from appearing without moderator approval was also stifling discussion. ProBoards, for example requires registration for posting, which I think kills a lot of passerby posts. It kills spam too, but having to deal with spam is a small price to pay for quality feedback.

This is not specific to your blog, but I’ve seen many blogs block new comments from appearing until they’ve been approved. Some blogs have it setup so they need to approve at least the first post, but many need approval for every post. The peril of this is that I’ve posted on lots of blogs that I completely forgot about a little while later.

Did they approve the comment later? Did I get a reply? I don’t know because not only do I not remember where I posted, sometimes WordPress doesn’t show those replies or my comments in the “Comments I’ve made” section. I know this because sometimes I remember a post I’ve made and go back to the post only to find there’s a reply, sometimes in the form of a question, and it’s already weeks old. The train of discussion has been lost.

This is obviously a glitch, but I don’t know how many of those glitches have occurred or how many times those notifications have failed to reach me. I could check that email box on the comment form in case someone replies, but considering the large volume I get on a daily basis, I’m not keen to add to the collection. Plus I have no idea if that works all the time either.

The common reason for pre-approval of comments seems to be moderating spam and I think this is a faulty premise. I’ve never had a spam problem on this blog or really any other blog I’ve run (that was up to date and had Akismet installed) and all pre-approval and pre-registration does is prevent legitimate posts from users with little patience for jumping through hoops. The only restriction I’ve placed on new comments is prevent posts with more than 2 links from automatically appearing without my approval since many spam posts have lots of links.

Very few spam comments get through Akismet into the spam queue on my blog and even several of those have been legitimate as well.

On this blog, all comments appear immediately after posting. There’s no registration restriction or even an email requirement and as a result I’ve received very helpful feedback from anonymous or unregistered posters.

Imagine how much feedback has been lost due to this additional hurdle. This is one more reason that good moderation just cannot be automated. It has to be an active part of every community. You need to spend some energy dealing with questions, comments and of course spam to make sure a community grows with sane boundaries. But it’s important to do this without raising the walls of your community too high or else you run the risk of being ignored.

Discussion Forum Update (tables and classes)

A bit of a long post today to show the updated table schema for the discussion forum and a few of the classes. There are quite a few improvements from the last version of the table schema in that there is no longer a PostRelations table. This was only adding more complexity to the retrival and storage methods so I opted to use a simple ParentId field to take care of the relationships. Now there is also a LastActivity field in the Posts table so that when a new post is added, only this field needs to be modified. The ContentHash field stores a rolling hash of the plain text content so measuring the quality of the post for the threshold will be more consistent.

Forum database schema

The object models also got an overhaul.

Onward to those…