1 # Bloxsom Plugin: Karma
2 # Author: Fletcher T. Penney < http://fletcher.freeshell.org/ >
3 # Original Concept: Beau Lebens
4 # Idea of using a form - Pasi Savolainen
10 # --- Configurable variables ----
12 # Where is the data kept?
13 $datafile = "$blosxom::plugin_state_dir/karma.dat";
15 # FYI, the data format is as follows
16 # Visits Positives Negatives Interest_Index Karma_Index Controversy_Index /Path/To/Article
17 # tabs are used as separators
19 # List of ip addresses to ignore for visit counts.
20 # Use '|' as a separator if you have more than one
21 # ie. $ip_list = '192.168.1.1|192.168.1.2'
25 # CGI variable names to use for specifying certain minimum scores
26 $minkarma_var = "minkarma";
27 $mincontroversy_var = "mincontroversy";
28 $mininterest_var = "mininterest";
30 # For instance, by adding "?minkarma=5", stories with a karma of less than 5 would be filtered.
31 # You can change the terminology above if you don't like the names.
33 # -------------------------------
37 # Initialize variables
38 $stats = ""; # This variable will contain the results
39 $votelinks = ""; # This variable will contain the links to submit a vote
49 my $interest_index = 0;
51 my $controversy_index = 0;
57 $message = ""; # Use this to report messages to the visitor
62 $path_noflavour = $blosxom::path_info;
63 if ($path_noflavour !~ s/\.[^\.]*$//) {
64 $path_noflavour =~ s/\/$//;
65 $path_noflavour .= "\/index";
66 $path_noflavour =~ s/^([^\/])/$1/;
68 $path_noflavour =~ s/^\/*//;
70 $remoteIP = $ENV{'REMOTE_ADDR'}; # Get IP address for visitor
71 $ip_list = 'disable' if ($ip_list eq ""); # Prevent an empty match expression
73 # Check to see if we are viewing a single story.
74 # If so, we want to increment the visits, unless this is a vote submission
75 # This does create the problem of reading a story on a category page, and voting for it
76 # No visit will be recorded - still looking for a solution
78 if (($path_noflavour !~ /index$/) && ($path_noflavour !~ /\/$/)) {
79 $do_increment = 1 if ($remoteIP !~ /($ip_list)/);
81 # Check to see whether a vote has been set
82 if (CGI::param("setkarma")) {
83 $vote = CGI::param("setkarma");
84 # If this is a vote, we don't want to update visit count
85 # They have already read the article and been counted
90 # Now we want to read in the data file, and update if appropriate
91 if (($do_increment eq 1) || ($vote ne "")) {
92 # The file should be locked during this process in case of two simultaneous accesses
93 open (DATA, "<$datafile");
94 flock (DATA, 2); # I think I am using this properly...
95 while ( $line = <DATA>) {
96 if ($line =~ /(\d+)\t(\d+)\t(\d+)\t[\.\d]+\t[\.\d]+\t[\.\d]+\t\/$path_noflavour$/) {
97 # This is the story currently being viewed, so read in info
98 $visits = $1 + $do_increment;
101 $pos ++ if ($vote =~ /^p/i);
102 $neg ++ if ($vote =~ /^n/i);
103 $total_votes = $pos + $neg;
105 # Try to correct for uncounted visits
106 $visits = $total_votes if ($total_votes > $visits);
108 $interest_index = ($pos + $neg)/$visits if ($visits ne 0);
109 $karma_index = $pos/($total_votes) if ( $total_votes ne 0);
110 $controversy_index = 2*$interest_index *(0.5 - abs($karma_index - 0.5));
112 $line = "$visits\t$pos\t$neg\t$interest_index\t$karma_index\t$controversy_index\t\/$path_noflavour\n";
119 open (DATA, ">$datafile");
120 flock (DATA, 2); # Lock again, if it was unlocked
121 print DATA $karmadata;
123 # If no match was found for the story, create a new record
124 if ($matchfound eq 0) {
127 $pos ++ if ($vote =~ /^p/i);
128 $neg ++ if ($vote =~ /^n/i);
129 $total_votes = $pos + $neg;
131 $interest_index = ($pos + $neg)/$visits if ($visits ne 0);
132 $karma_index = $pos/($total_votes) if ( $total_votes ne 0);
133 $controversy_index = 2*$interest_index *(0.5 - abs($karma_index - 0.5));
135 $line = "$visits\t$pos\t$neg\t$interest_index\t$karma_index\t$controversy_index\t\/$path_noflavour\n";
137 $karmadata .= "$line\n";
141 # Read in the existing data only - we are not viewing a single story
142 open(DATA, "<$datafile");
143 while ( $line = <DATA>) {$karmadata.=$line;}
146 flock(DATA, 8); # Unlock file
148 # Here is where we can create list of top controversies, etc
149 # And we have info loaded for the sort routine if necessary
150 # Currently the karma plugin can filter for certain scores, but not sort
157 my ($pkg, $files_ref) = @_;
158 my @files_list = keys %$files_ref;
160 if (CGI::param($minkarma_var)) {
161 $minkarma = CGI::param($minkarma_var);
165 if (CGI::param($mincontroversy_var)) {
166 $mincontroversy = CGI::param($mincontroversy_var);
170 if (CGI::param($mininterest_var)) {
171 $mininterest = CGI::param($mininterest_var);
176 if ($dofilter eq 1) {
177 foreach $file (@files_list) {
179 $file =~ s/^$blosxom::datadir//;
180 $file =~ s/\.$blosxom::file_extension//;
181 if ($karmadata =~ /(\d+)\t(\d+)\t(\d+)\t([\.\d]+)\t([\.\d]+)\t([\.\d]+)\t$file/) {
182 if ($minkarma && ($5 < $minkarma/10)) {
183 delete $files_ref->{$realfile};
185 if ($mincontroversy && ($6 < $mincontroversy/10)) {
186 delete $files_ref->{$realfile};
188 if ($mininterest && ($4 < $mininterest/10)) {
189 delete $files_ref->{$realfile};
192 delete $files_ref->{$realfile};
201 my $interest_index = 0;
202 my $controversy_index = 0;
204 $pos = $neg = $visits = 0;
206 my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
208 if ($karmadata =~ /(\d+)\t(\d+)\t(\d+)\t([\.\d]+)\t([\.\d]+)\t([\.\d]+)\t$path\/$filename/) {
212 $total_votes = $pos + $neg;
213 $visits = $total_votes if ($total_votes > $visits);
214 $interest_index = $4;
216 $controversy_index = $6;
218 # Clean up the values for display - we'll want to use the originals (or at least
219 # more decimal places for comparisons)
220 # All values are integers on scale from 1-10
221 $karma_index = int($karma_index * 10);
222 $controversy_index = int($controversy_index * 10);
223 $interest_index = int($interest_index * 10);
227 # Here you can define the HTML code that gets used to display your results
228 $stats = "Visits: $visits | Positives: $pos | Negatives: $neg<br>";
229 $stats.= "Karma: $karma_index | Interest: $interest_index | Controversy: $controversy_index";
231 # And this is the HTML code for the two vote submission links
232 #$votelinks = "<a href=\"$blosxom::url$path/$filename.$blosxom::flavour?setkarma=p\">I liked this article</a> | <a href=\"$blosxom::url$path/$filename.$blosxom::flavour?setkarma=n\">I didn't like it</a>.";
234 $votelinks = qq!<form method="POST" action="$blosxom::url$path/$filename.html">
235 <div><input type="submit" name="setkarma" value="p">I liked this article |
236 <input type="submit" name="setkarma" value="n">I didn't like it
250 Blosxom Plug-in: karma
254 Karma allows you to have your visitors score articles in a positive/negative fashion. It then calculates various scores that you can use to incorporate a "Top 10 Articles" section, or something similar, into your website.
258 1) place the karma plugin in your plugins directory, with proper file permissions
260 2) ensure the configuration variables are properly set:
261 $datafile should be left alone, unless you have customized where plugins store information
263 $ip_list should include the IP addresses of any visitors you do NOT wish to be counted in scoring. For instance, you should probably include your own IP address to prevent falsely biasing the numbers as you read and reread your site looking for errors
265 The $minkarma_var, $mincontroversy_var, and $mininterest_var variables can be used to customize the url's used to filter articles.
267 3) You can modify the HTML code snippets at the bottom of the story subroutine ($karma::stats and $karma::votelinks) to alter the display of the scores, and the vote submission links as you see fit.
270 Once installed, modify your story.flavour files to include the $karma::stats and $karma::votelinks variables.
272 Now, when you visit your site, you should see the scoring and links displayed with each story. Each time you view a story individually, its visit count is advanced one. Each time you click on the "I liked..." or "I didn't..." links, the positive and negative vote scores will be modified appropriately.
274 These numbers are used to calculate several variables:
276 $karma_index - this is a rating from 0 to 10 of how positively the story was viewed
278 $interest_index - this is a rating from 0 to 10 of what proportion of people who viewed the story bothered to vote on it. This will be artificially low, as many people might view the story as part of a category, and their visit will NOT be counted. I am open to ideas as to how to more accurately handle this, keeping in mind that many stories that are displayed are not actually read (ie, older stories at the bottom of a blog...)
280 $controversy_index - this is a rating that I came up with just for fun. It is a measure of how many people voted, and how divergent their opinions were. For instance, as the interest_index goes up, the controversy_index goes up. As the votes are more evenly split between positives and negatives, the controversy_index goes up. A story with 100% positive (or negative) votes, will have a 0 controversy index (everyone is in agreement, therefore there is no controversy...) It's not a very scientific number at all, but sort of fun nonetheless.
282 4) You can add links to your site that will display the articles with the highest interest, karma, or controversy by using the $minkarma_var variables discussed in #3 above.
284 For instance you could add a url in your header file like this:
285 <a href="/weblog/index.html?minkarma=5">Show The Most-Liked Articles</a>
287 This would allow the display of only those articles with a karma above 5. In the future, I hope to add a sort routine that would sort stories by the various indices, but currently the stories are unsorted.
291 This plugin filters out articles only. It does not locate any articles on its own.
293 Also, it uses the filter routine. Therefore, it will interact with any other plugins that make use of the filtered articles list. You may need to add numbers to the beginning of plugin names (ie, "55karma") to control the load order if you seem to be having unintended results. For instance, karma will prevent some topics from being added by my "menu" plugin, unless it loads after menu.
297 Fletcher T. Penney - http://fletcher.freeshell.org
298 Concept by Beau Lebens
300 This plugin is now maintained by the Blosxom Sourceforge Team,
301 <blosxom-devel@lists.sourceforge.net>.
305 This source is submitted to the public domain. Feel free to use and modify it. If you like, a comment in your modified source attributing credit to myself and Beau Lebens would be appreciated.
307 THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND. USE AT YOUR OWN RISK!