CrowdedText.com is a site where businesses and other customers can create writing contests for their creative and content needs. Writers then compete in these contests, uploading their entries, and getting paid if selected as a winner.

In order to create a contest, the customer fills out a multi-step form, selecting contest type, filling out requirements and prize to award, selecting any upgrades, and finally checking out and paying the corresponding fees. The customer can manage their contests by voting on entries, sorting entries, and sending private messages to writers. They can award an entry at any time.

CrowdedText.com was built in Drupal 6. The entire process was managed and implemented by PropDrop.

Contributed Modules Used:

One of the reasons I chose Drupal to implement this site, as opposed to doing it from scratch, was because of the immense community that develops and maintains quality contributions. Through some of my previous experience, I knew that existing modules could provide more than 90% of the functionality I needed. My main job was to provide the glue.

CCK – I had two key content types: contests and entries. The contest type has 16 extra fields, which is to be expected as this content type is the main driver of the site. Some fields were for more traditional uses, like the prize amount and end date of the contest. But many fields are used as simply boolean type variables. For example, there is a field that tracks whether an email has been sent regarding this contest, one that is set when the contest has been upgraded, and one that is set when there are new entries since the last time the Contest Holder had logged in.

I debated creating my own tables to keep track of the information I needed, but I realized I would be reinventing the wheel, losing out on CCK's built-in validation and Views integration. And the Views integration, especially, saved weeks of my time when I was building the necessary lists of content. Without CCK, writing these queries would have been a pain, especially on the administration side of things, which is covered under the Views section.

An entry has a node refernce type to reference the contest, a field that is set if the entry is a winning entry, and a field that contains the user id of the Contest Holder. I provided the last one because, while a contest is still running, only a Conest Holder should be able to view entries. Putting the Contest Holder id in a cck field when an entry is created prevents extra database calls every time someone tries to view an entry.

Views – This was my first extensive experience with Views 2 and I absolutely loved the improvements from Views 1. The easy templating alone was manna from heaven.

Since the contest listing would need to look different based on the upgrades it did or didn't have, I could just include the CCK upgrade fields, and then exclude them from display. That way the Views template had access to the values and I could change the CSS class of the row or the title text based on them. Very simple to do.

I also wanted this site to be easy and effortless to manage. This is another spot where many of the boolean CCK fields come into play. I created administration only views, and I can filter based on those values and get a glance of what is going on with the site very easily. I can also see all users that have won contests, yet have not received a payment for winnings. Without views, these queries would have been tedious torture.

The other key place Views and it's templating abilities came into play were listing the entries of a contest. But it would look different depending on if the contest holder was looking at the page or if it was someone else. To complicate things, there also needed to be a third version for when the contest was over.

This was accomplished by creating three different types of views. For the contest holder, one had a clickable fivestar widget and a clickable entry name. The other once had a static fivestar widget and just the entry name without a link. And the third, for after a contest ended, had a linked entry name, but still had a static fivestar widget.

In the template file for contests, I did a simple check on the current user and the contest time, and then embedded the appropriate view. The flexibility really is endless with embedded views.

Ubercart – Using a full cart solution just for the payment gateway functionality may seem like overkill at first. However, this goes back to why I didn't want to do the whole site from scratch. The Ubercart developers have writtin a robust payment solution that is maintained and supported, it just works and I'm familiar with the API from previous projects. It's also modular, so I didn't need to enable a lot of what is included in the module.

Using Ubercart also allows for easy expansion if I decide to add functionality in the future. This has already paid off even before I launched. I added the ability for Contest Holders to extend their contest for three additional days. If I had done the payment gateway from scratch, I would have had to hack my original code again and go through rounds and rounds of retesting everything. With Ubercart, I just had to write one line of code using the Ubercart API.

Ubercart also made it simple for an anonymous user to begin creating a contest, and after they logged in or registered for the very last step of the form, their cart would be transferred automatically to the logged in session. All I had to do was redirect to the checkout page.

Userpoints and VotingAPI – A holistic way to list the Top Writers on the site was critical to encourage quality writers to contribute. But I didn't want the list to be determined solely on the number of contests won, as I felt it would be too rigid. There would be many quality entries that wouldn't necessarily win a contest. So I decided that every entry that got a vote of 3 stars or more would also improve the ranking of a writer.

To accomplish this, all I had to do was call the hook_votingapi_insert, make sure the value of the vote was high enough, and then use the Userpoints API to add the point within the hook. Similarly, if the vote was cancled, I needed to subtract the point, so an implementation of hook_votingapi_delete was also necessary.

Other contributed modules I used and am thankful for:

  • Fivestar
  • Content Profile
  • Privatemsg
  • Imagecache
  • Imagecache Profile Pictures
  • Automatic Nodetitles
  • Token
  • Views Display Tabs
  • Secure Pages

Custom Modules:

I made use of three custom modules. In retrospect, these should have been broken down even further based on functionality, but three is a manageable number, and none of them exceed 800 lines of code. Plus, they should be easy to port to Drupal 7 if and when I decide to make the switch.

One module takes care of the contest creation multi-step form, and calling the necessary Ubercart API calls to add the correct nodes to the cart and then redirect immediately to the checkout page.

One module handled all functionality dealing with entries and awarding them as winners.

The last module simply implemented hook_cron and handles all of the logic for email reminders. There are custom email reminders written for when a contest ends, when an entry is selected as a winner, when there are new entries that have been added to the contest since the Contest Holder last logged in, and when the Contest Holder has not left enough feedback.

All in all, I ended up having just one custom table that kept track of all awarded entries.

Customized Profiles:

Advanced functionality on the user profiles was a must so people could manage their contests. But I also wanted it to aid in conversion. When a user creates a contest at first, it is set to "unpublished". Only after checkout will the contest then be "published" and listed on the site.

So on the profile, a list of Pending Contests is displayed, with a button to "Launch this Contest". That way if they went away, but came back, they didn't have to refill all the contest information in again. This is done with a simple embedded view in the profile template.

They also get exactly one email reminder per pending contest that exists. But only one to keep down the annoyance factor. I hope this will increase conversions in the long run.

Challenges:

1.Hidden, Read Only Fields – Fields that I didn't want users to be able to edit in Contests and Entries were easily hidden by surrounding them with a div that had a style of "display: none". However, anyone with a tool like firebug could easily manipulate the data. So for each form that had fields I wanted to be uneditable, I added an extra validate function. Every validate function has access to the $form variable, which has a copy of the node being edited. So the validate function simply set the form variables back to their original values before the form is processed, with no extra calls to the database.

2.Keeping Track of Payments – The main part of managing the site will be distributing payments to winning writers. But how to know how much and also track when they had been paid previously? I certainly didn't want to do a complicated query. So I took the simple route. Whenever a writer has a winning entry, the prize amount is added to their "funds" record. A view was created that displays all users with a positive balance. Using Views Bulk Operations, I can set these "fund" records to zero after they are paid.

For the Future:

The main hurdle for the future will be scalability. As the site grows, we will begin utlizing a CDN and implement more aggressive server-side caching. For the moment, however, Page Caching and CSS aggregation handles the load admirably.

Comments

ryan_courtnage’s picture

This is a fantastic idea for a crowdsourcing website. Like CrowdSpring but for writing. Excellent!

Wishing you good luck!

jazzdrive3’s picture

Thanks! Now I just need to come up with clever ways to market it. I was hoping that since no one had really ever done anything like this in Drupal before, I'd get a boost from the community. We'll see.

coltrane’s picture

It's not really crowdsourcing, it's just a competition website. The ideas of real crowdsourcing have been muddled because competition, spec-work sites like crowdspring and this just call themselves "crowdsourcing".

JohnForsythe’s picture

I have to agree, this is a very clever twist on the old "spec work" idea. There's a ton of interest in the crowd-sourced article generation business right now, but it remains to be seen how search engines will adapt to the flood of new content.

monkeytrousers’s picture

That's a really nice looking site (not in your portfolio though?). Did you make the theme from scratch or start off from an existing one?

jazzdrive3’s picture

Haven't put it in my portfolio yet.

Yeah it was pretty much from scratch. I have a custom base theme I start with for everything.

dboulet’s picture

Regarding the hidden fields on the node edit form, could you not have done this with the Content Permissions module included with CCK?

jazzdrive3’s picture

For some reason, I ran into a little quirk where I didn't have access to the data at the time I needed it. I can't remember exactly what it was at this point.

Swisx’s picture

I have seen this on http://www.template-web.org

jazzdrive3’s picture

Umm...where? Have a link? Because this was a completely custom design.

docwilmot’s picture

Another brilliant idea I wish I'd thought of myself.
Comments on the site:
I find the red background on the "home-text" div a bit distracting next to the big black "How it works" square. I feel that when I remove the background image of "home-text" in firebug and make the font dark, the whole page looks like it flows better
And to be honest the front page doesn't sell itself well. Perhaps instead of "harnest the power..." state what you actually can do with the site, like "start your own writing contest!" and the subsequent text should explain that clearer (start the sentence with "whether you need...." instead?). It took me a few reads to figure i could use the site to design a slogan; I thought it was for short stories and novels and such.
But simple and good idea.

-------------------------------------------------
Always be nice to people on the way up; because you'll meet the same people on the way down.
Wilson Mizner (1876 - 1933)

jazzdrive3’s picture

Thanks for the feedback. Haven't really tested the copy much yet, so I appreciate your thoughts.

dgastudio’s picture

Add to body {margin: 0}.

BetoAveiga’s picture

Hi! Thanks for all the info, it's very useful and practical. Listing the modules that you are using is an excellent reference. I'm planning to build a website with many of these characteristics.
Thanks for sharing real know how! :D

IIG Luis Aveiga
Ecuador

Drupal Backend / Frontend Developer

macrodesign’s picture

simply brilliant

asd123asd5’s picture

the theme seems weird with the browser padding...

also some pages the footer doesn't reach the bottom on 1680x1050

sexy site tho, not sure about it's application, never been into competition sites

samkat12’s picture

I didn't like Drupal the first time i saw it but i fell in love with it the more i got to know it.

CrowdedText.com is an example of what drupal is capable of doing, I strongly beleive that one day Drupal is going to produce a site that will be the next talked about web application. You have heard about Facebook,Twitter e.t.c. You can bulid the same sites using drupal. jazzdrive3, CrowdedText.com is a great Idea and i am sure millions of people will use it, because it gives power to the people. "Build it and they will come...".
Thanks for the write up and wish you all the best with your new venture.
I would appriciate it if you could shed some light on the following:

  • How did you create your step by step forms(did yo use any module or just ckk and ???) also how did you create your next buttons on the edit forms?
  • Did you use any modules for the popups in the text forms(i love the way they jump out at you) they are better than the help text that comes with ckk.
  • Did you use any php snippet to publish Contest after payment? also what modules did you use Ubercart,?,? in order to be able to publish after payment. a brief over view and php snippet would be great if you do not mind.
  • Your response and guide regarding the above questions will be appriciated. "Give a man fish and he will eat the all day,Team him how to fish and he will eat every day."

    Thanks again.

    Lets go Harness the Power of Crowds
    jazzdrive3’s picture

    1. For multi-step forms, I used a variation of what is explained at http://www.andrewyager.com/content/view/51/27/ . Basically, you have a counter in $form_state where you keep track of which page you are on, and only display the corresponding form elements.

    You add another submit button that doesn't submit the form, but just increments/decrements the counter and saves the current step's form elements in another $form_state variable.

    2. The popup help texts are a jquery plugin. Very easy to use. http://craigsworks.com/projects/qtip/ .

    3. There was a lot involved in getting the payment process to work just how I wanted. But essentially I used sessions and the built-in cart functionality of Ubercart. One of my custom modules has a function that I redirect to after payment where I publish the contest that was just paid for and clear out the relevant session data. It does a bit more, but that's the essence of it.

    samkat12’s picture

    Thanks for the response.
    for those who do not know PHP ther is a module for Multistep Form

    johnhanley’s picture

    I don't normally read (let alone respond) to showcase postings, but I found yours to be informative and insightful. I particular enjoyed the technical implementations descriptions. I can tell you approached each design decision with careful thought and consideration. I love the overall "less is more" restrained look and feel.

    Well done.

    Thomasr976’s picture

    Hmm...........that feature is not working. Site says find contests. When you do and want to enter one, it just says log in to submit content and when one does that you get your profile and no link or form to enable you to submit content.

    Otherwise pretty cool idea and site.

    jazzdrive3’s picture

    Looks like I need to put a ?destination at the end of those links to make things less confusing.

    Thanks!

    kraymer’s picture

    Site looks GREAT!

    Congratulations on a job well done.

    ~kraymer

    PWG’s picture

    Wonderful idea! The best of luck!

    StevenSokulski’s picture

    As a writer, I signed up for your site and have entered two contests already.

    A couple things that seem odd to me:

    • You aren't using PathAuto, leaving your URLs muddy and unfriendly.
    • Your user profile pictures are restrictive. I had to open up Photoshop and futz around for a few minutes to get an image small enough (dimensions and minimum file size of 30k) which I'm not sure others will do.
    • The fact that your profile fields and actual user information are split is not intuitive. There is no information stored in your fields that I don't think could be easily stored in the actual user profile, giving the user only one page on which to make changes to their account.

    I'll be interested to see how the site progresses. Good luck!

    jazzdrive3’s picture

    Filesize restriction for the profile pics have been raised. Thanks for pointing that out.

    And it's my plan to eventually consolidate the fields onto one page. But I wanted content profile for easy node manipulation that I do on the backend for admin purposes

    targoo’s picture

    awesome !
    I was wondering how you resolve the png problem with IE6 ? I us pngfix but you didn't ?
    Again, great job !

    jeff784ty43182’s picture

    impressive website

    dabblela’s picture

    Great idea and great execution. A few questions about the Ubercart integration if you wouldn't mind: why didn't you use UC Node Checkout to handle the contest creation? When you add upgrades, are those product attributes or entirely separate nodes?

    jazzdrive3’s picture

    I needed a lot more fine-tuning and more control of exactly WHEN the node was published than the node checkout module provided. I wanted all my code related to this in one place as well for easier maintenance, as opposed to some in a contributed module, and some in a custom module. One example of this was the product upgrades, where I needed to reference the unpublished node to get the upgrades, and then add those upgrades to the cart.

    That, coupled with the fact that it is still in beta, just pushed me to do it on my own. It was very simple to do when you don't need to take into account a large audience of users, like UC Node Checkout does.

    Upgrades are entirely separate nodes. Easier to work with than attributes, especially since I have so few of them. And it will be much easier to add additional upgrades in the future if I want.

    DotaMaster’s picture

    nice work!

    aac’s picture

    Thanks for the nice writeup.
    Congratulations for a great site!!!

    ---~~~***~~~---
    aac

    fallenleaf’s picture

    The boolean type field use is fresh to me.
    What the method you use to validiate the form? both javascript and PHP? Or some modules.
    library of brands

    vkr11’s picture

    Great site !!