Jump to content

Release: Discussions


apeisa

Recommended Posts

Very simple discussion board using pw pages as topics and replies. It means that you can use the great API with your discussions also.

This is VERY tiny in features, but I am open to suggestions. I want to keep this small, since there are plenty of great php based forum software around, and this tries to nail much simpler needs. What this does currently is:

  • Multiple forums on site
  • Multiple topics on forum
  • Only logged in users are allowed to post
  • Admin stuff is handled true pw-admin (everything are just pages)
  • Replies and topics are tied to user accounts

Current shortcomings:

  • You cannot change markup easily
  • You cannot change text / date formats etc (waiting for PW 2.2 for this)

How to install

  • Make sure you run the very latest version of ProcessWire 2.1, at least this commit or newer.
  • Download the file from github and copy it to /site/modules/Discussions.module
  • Go to admin -> modules and "check for new modules". After that click Install on "Discussions"
  • It creates 3 fields and 3 templates for you. But it doesn't create 2 required template files - you need to get your hands dirty.
  • Create two new template files: discussions-forum.php and discussions-topic.php
  • Edit your new templates and add this line to both of those: echo $modules->get("Discussions")->render();
  • Create a new page and give it template: discussions-forum
  • You probably want to add Markdown Extra textformatter to your discussions_message field. (Ryan, how to do this from install script?)
  • That is pretty much it. It will look ugly as hell, so you really want to add & edit some css (very basic starting point provided below)

/* DISCUSSIONS -basic styling */
#discussions {
position: relative;
width:100%;
overflow: hidden;
}

.discussions-reply, .discussions-form {
padding: 20px 0;
border-bottom: 1px solid #ddd;
clear: both;
overflow: hidden;
}

.discussions-form {
border: none;
}

.discussions-information {
float: left;
width: 20%;
overflow: hidden;
}

.discussions-author {
font-weight: bold;
display: block;
}

discussions-datetime {
font-size: 0.9em;
color: #666;
margin-bottom: 1.5em;
}

.discussions-message {
border-left: 1px solid #eee;
padding-left: 3%;
margin-left: 2%;
width: 74%;
float: left;
}

.discussions-form label {
display: block;
}

.discussions-form textarea {
width: 90%;
}

#discussions .discussions-message p, #discussions .discussions-message table, #discussions .discussions-message ul, #discussions .discussions-message ol {
margin-top: 0 !important;
}

/* Pagination */
.MarkupPagerNav {
        margin: 1em 0; 
        font-family: Arial, sans-serif;
	overflow: hidden;
}
.MarkupPagerNav li {
display: inline;
list-style: none !important;
margin: 0 !important;
}

.MarkupPagerNav li a,
.MarkupPagerNav li.MarkupPagerNavSeparator {
display: block;
float: left !important;
padding: 2px 9px;                
color: #fff !important;
background: #2f4248;
margin-right: 3px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
}
                
.MarkupPagerNav li.MarkupPagerNavOn a,
.MarkupPagerNav li a:hover {
color: #fff;
background: #db1174;
text-decoration: none;
}

.MarkupPagerNav li.MarkupPagerNavSeparator {
display: inline;
color: #777;
background: #d2e4ea;
padding-left: 3px;
padding-right: 3px;
}

This was very fast to code (I estimate 6 hours, certainly under 8 hours). I consider this version as a alpha, not tested that much (well, it works on my computer ;)) and I do wanna add some features and get feedback (how it works, any security issues etc). All feedback is welcome.

  • Like 2
Link to comment
Share on other sites

Wow! Great stuff, Apeisa. Some very cool modules got released here recently. I will sure check it as soon as possible. Also, I hope it will help me understand how to write custom modules properly. Only 6 hours of development is pretty quick, I will definitely spend much more time developing my first module  ;D

Link to comment
Share on other sites

Wow! Great stuff, Apeisa. Some very cool modules got released here recently. I will sure check it as soon as possible. Also, I hope it will help me understand how to write custom modules properly. Only 6 hours of development is pretty quick, I will definitely spend much more time developing my first module  ;D

Thanks slkwrm! I would have recorded a screencast, but I am hitting bandwith limit on screencast.com.. should probably start using YouTube.

This will definitely take few more hours to polish (even if I don't add any new features), but I was very surprised how little time this took. I was prepared for few days of coding. But the fact that I use pages for all the admin stuff (edit, remove) makes huge savings though. But I am planning to add inline edit functionality for admins, so that you don't have to go admin every time you need to edit or remove posts.

Link to comment
Share on other sites

Very cool! Nice work Antti!

I'm just playing around with it here and wondering about the discussion-reply template. Is this one that we should create the file for too, and is there anything specific that needs to go in that? Currently when I click on a reply, I get a 404.

The other question I had is that after submitting a topic, I get another blank form and wasn't sure if that was intended or not. If I type something in it and submit, then it shows up as a reply to the original one posted.

Thanks,

Ryan

post-1-132614279476_thumb.png

post-1-132614279489_thumb.png

Link to comment
Share on other sites

Very cool! Nice work Antti!

Thanks!

I'm just playing around with it here and wondering about the discussion-reply template. Is this one that we should create the file for too, and is there anything specific that needs to go in that? Currently when I click on a reply, I get a 404.

Nope, you don't need discussions-reply template. Based on your screenshot I think you have echo $modules->get("Discussions")->renderForum() in both of your templates. In discussions-topic template you need to have: echo $modules->get("Discussions")->renderTopic().

Now that I wrote that it doesn't make any sense to have different methods for those, I updated it to support just render() on both of those templates (old way works too).

PS: I wanted to keep discussions-reply as their own template mainly for nice template-based selectors. Also to make sure that replies cannot have any children.

Link to comment
Share on other sites

PS: I wanted to keep discussions-reply as their own template mainly for nice template-based selectors.

What I mean here, is that you can get pretty cool stuff through API easily.

All topics started by current user:

$pages->find("template=discussions-topic, discussions_author=$user");

10 latest posts from all forums:

$pages->find("template=discussions-reply|discussions-topic, sort=-created, limit=10");

etc...

Link to comment
Share on other sites

Nope, you don't need discussions-reply template. Based on your screenshot I think you have echo $modules->get("Discussions")->renderForum() in both of your templates. In discussions-topic template you need to have: echo $modules->get("Discussions")->renderTopic().

You were right. :) I'd pasted in the wrong thing. It works perfectly now. Sorry about that.

What I mean here, is that you can get pretty cool stuff through API easily.

Great examples, I'm very impressed with this–great module you've put together!

Thanks,

Ryan

Link to comment
Share on other sites

I've been looking through the code and it looks very nicely put together. A few comments on things I wasn't sure about:

<?php
if (!$post->discussions_message) wire('session')->redirect("./?discussions_title={$post->discussions_title}");
if (!$post->discussions_title) wire('session')->redirect("./?discussions_message={$post->discussions_message}");

There might be issues sending all that copy through a redirect header, especially straight out of the $post input. I'd recommend instead saving it to a $session variable and then just redirecting to something like "./?missing_something=1", and then pull your discussions_title or discussions_message from the $session var instead.

With any API vars, these are all equivalent:

<?php
$page = Wire::getFuel('page');    // older style for static use
$page = $this->fuel('page');      // older style for internal API
$page = wire('page');             // newer style for external API, internal or static use
$page = $this->page;              // internal API

internal API = classes descended from Wire

external API = other classes or other applications or command line API

It doesn't matter which you use, but just wanted to mention they are all the same because I saw you 3 different versions. The $this->page is the 'prettiest', but relies on the class or one of it's parent class(es) checking the fuel and matching it. This is something that WireData (the class you are using) does, so you can use that format if you prefer it. That's the version I usually use, though technically it has a little more overhead than the rest.

If you want one that you can use anywhere regardless of context, I would just use the wire('page') function.

For your render() methods, I recommend adding an output formatter to your discussion_message. It's likely that title will already have an output formatter applied to it (htmlentities by default), though maybe good to double check. Here's how you would add a textformatter during your install (second to last line):

<?php
$field_message = new Field();
$field_message->type = $this->modules->get("FieldtypeTextarea");
$field_message->name = 'discussions_message';
$field_message->label = 'Message';
$field_message->textformatters = array('TextformatterEntities'); // or TextformatterMarkdownExtra
$field_message->save();

Double check that it works (written in browser). :)

Another option might be to dispense with textformatters completely and send any output through htmlentities() or the filter_var() that you are using in the inputs.

  • Like 1
Link to comment
Share on other sites

Ryan, thanks for your feedback! I really appreciate it.

You are right about the issues with that solution using get params in redirect. Gotta say that it was last "feature" I build and don't know how I ended up using that solution. Will use $session there.

I have copied code snippets from various sources, so that explains the differences in $page. Will probably settle with wire('page') - don't know why but I like it :)

Thanks for the textformatter tip also - I actually use MarkdownExtra on my test installation and I am planning to implement markItUp editor into discussions also (maybe with preview).

Link to comment
Share on other sites

I just used wire('page') as an example, but of course it applies to any API variable.

Also, speaking of wire('page'), I just committed an update that makes the ready() function part of the module interface. Now you can add a ready() function to any autoload module, and that function will be called when the API is fully ready (with the $page variable). I figured this would be more convenient than hooking ProcessPageView::ready, but either will work.

I've always liked that markItUp editor. I even had an Inputfield for it in PW1. Will have to bring it back in PW2 at some point.

Link to comment
Share on other sites

Also, speaking of wire('page'), I just committed an update that makes the ready() function part of the module interface. Now you can add a ready() function to any autoload module, and that function will be called when the API is fully ready (with the $page variable). I figured this would be more convenient than hooking ProcessPageView::ready, but either will work.

Yep, that looks even better. Will switch again.

Kids went just bed and wife is doing some knitting so it means some evening hacking here :)

Link to comment
Share on other sites

I wish my wife would pick up knitting– If she is home I'm in trouble if I get near the computer. :)  In fairness, she's been quite supportive in me cutting my client work 50% to focus on PW. But I do miss the evening hacking. :)

  • Like 1
Link to comment
Share on other sites

Doing some refactoring here with this module, and while building uninstall method I started to thinking about fields. Currently we have system and permanent fields. Should we have module fields also? They could be fields that cannot be added (at least too easily) to other templates and/or are hidden from fields list by default? Not given this much thinking, since it's getting late.

I wanted to build easy and quick way to uninstall this (just check if there isn't any pages using discussions-* templates, then remove all templates, fieldgroups & fields that module have created). I achieved this already, but realized that (at least in my current method) fails if site builder have used discussions_* fields in some other templates also.

Link to comment
Share on other sites

That might be a good thing to add. In the Languages module, I was running into the same question yesterday. I ended up making them system fields and templates. So for fields I did this:

$field->flags = Field::flagSystem | Field::flagPermanent;

flagPermanent means it can't be removed from the template it's assigned to (which you may or may not want). flagSystem means it can't be deleted from the system and doesn't show up in the regular fields list.

And for templates I did this:

$template->flags = Template::flagSystem;

Using this method, your fields and templates shouldn't show up in the user-defined places, and the user can't delete them either. Then the question of uninstall… I'm working on an override option so that the API can delete them (for the uninstall method).

  • Like 1
Link to comment
Share on other sites

I've just committed and update that will let you remove any system flag/status from Fields, Templates or Pages using this method below. This is how you could delete system fields, templates or pages. The code below is what you would do right before you delete them. Please don't do this on any of PW's system fields, templates or pages. :) The system flags exist precisely so people can't delete these. This override is provided only for uses like yours and mine where we've created system fields/templates (or pages) with modules but want to be able to uninstall them with the API. So here's how you'd do it:

Field:

<?php
$field = $this->fields->get("discussions_message"); 
$field->flags = Field::flagSystemOverride; 
$field->flags = 0; // all flags now removed, can be deleted

Template:

<?php
$template = $this->templates->get("discussions-forum"); 
$template->flags = Template::flagSystemOverride; 
$template->flags = 0; // all flags now removed, can be deleted

Page:

<?php
$page = $this->pages->get('some system page'); 
$page->status = Page::statusSystemOverride; 
$page->status = 0; // all status removed from page, can be deleted

With regard to users adding your fields to other templates, I would just throw an exception on your uninstall if that is the case. That way you can tell them to remove the field from any templates where they may have assigned it. Though if you've made them system fields, chances are people won't be adding them to other templates since one has to be in the $config->advanced system dev mode in order to do that.

<?php
if($field->getTemplates() as $t) if($t->name != 'discussion-topic')
    throw new WireException("Please remove {$field->name} from {$t->name} template before uninstalling."); 

If you want to do a recursive delete of a discussion tree, you could do it like this:

<?php
foreach($this->pages->find("template=discussion-forum") as $p) {
    $this->pages->delete($p, true); // true = make it a recursive delete
}
  • Like 1
Link to comment
Share on other sites

Didn't go to flags yet, but pushed this to GitHub with bunch of fixes (stuff we talked about earlier). Also updated the stylesheets in first post.

It is starting to take a shape nicely. I need to think about flags & uninstalling a little bit more. I also want this to be easily extendable if someone has bigger needs, so then all the extra magic or hiding will be just annoying.

Link to comment
Share on other sites

  • 1 year later...
  • 3 months later...
  • 7 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...