Merge lp://staging/~davidstrauss/pressflow/cassandra-votingapi into lp://staging/pressflow

Proposed by David Strauss
Status: Work in progress
Proposed branch: lp://staging/~davidstrauss/pressflow/cassandra-votingapi
Merge into: lp://staging/pressflow
Diff against target: 5747 lines (+5599/-0)
28 files modified
cassandra.php (+63/-0)
dvote.php (+95/-0)
dvote.sh (+6/-0)
sites/all/modules/cassandra/cassandra.info (+3/-0)
sites/all/modules/cassandra/cassandra.module (+635/-0)
sites/all/modules/cassandra/cassandra.test (+85/-0)
sites/all/modules/cassandra_votingapi/cassandra_votingapi.info (+5/-0)
sites/all/modules/cassandra_votingapi/cassandra_votingapi.module (+333/-0)
sites/all/modules/cassandra_votingapi/storage-conf.xml (+261/-0)
sites/all/modules/votingapi/API.txt (+142/-0)
sites/all/modules/votingapi/CHANGELOG.txt (+71/-0)
sites/all/modules/votingapi/LICENSE.txt (+274/-0)
sites/all/modules/votingapi/README.txt (+21/-0)
sites/all/modules/votingapi/tests/votingapi.test (+185/-0)
sites/all/modules/votingapi/translations/fr.po (+275/-0)
sites/all/modules/votingapi/translations/ja.po (+156/-0)
sites/all/modules/votingapi/translations/nl.po (+274/-0)
sites/all/modules/votingapi/translations/uk-ua.po (+121/-0)
sites/all/modules/votingapi/translations/uk.po (+121/-0)
sites/all/modules/votingapi/views/votingapi.views.inc (+376/-0)
sites/all/modules/votingapi/views/votingapi.views_default.inc (+555/-0)
sites/all/modules/votingapi/views/votingapi_views_handler_field_value.inc (+49/-0)
sites/all/modules/votingapi/views/votingapi_views_handler_relationship.inc (+214/-0)
sites/all/modules/votingapi/votingapi.admin.inc (+155/-0)
sites/all/modules/votingapi/votingapi.api.php (+164/-0)
sites/all/modules/votingapi/votingapi.info (+11/-0)
sites/all/modules/votingapi/votingapi.install (+299/-0)
sites/all/modules/votingapi/votingapi.module (+650/-0)
To merge this branch: bzr merge lp://staging/~davidstrauss/pressflow/cassandra-votingapi
Reviewer Review Type Date Requested Status
Pressflow Administrators Pending
Review via email: mp+18701@code.staging.launchpad.net
To post a comment you must log in.
76. By David Strauss <straussd@barium>

Initial Cassandra API

77. By David Strauss <straussd@barium>

Fixes.

78. By David Strauss <straussd@barium>

Use JSON encoding.

79. By David Strauss <straussd@barium>

Working queries.

80. By David Strauss <straussd@barium>

Basic tests pass.

81. By David Strauss <straussd@barium>

Fix some tabs.

82. By David Strauss <straussd@barium>

Fix some tabs.

83. By David Strauss <straussd@barium>

Fix some tabs.

84. By David Strauss <straussd@barium>

Fix some tabs.

85. By David Strauss <straussd@barium>

Add remove() support and tests.

86. By David Strauss <straussd@barium>

Make save() much more intelligent when not merging columns.

87. By David Strauss <straussd@barium>

Reorder part of save() for clarity.

88. By David Strauss <straussd@barium>

Update tests. Add test for merge-mode save().

89. By David Strauss <straussd@barium>

Everything has changed. New tests pass.

90. By David Strauss <straussd@barium>

Update some API calls in cassandra_voting_api. Add tests around Countable implementation and fix bugs.

91. By David Strauss <straussd@barium>

Continue adapting cassandra_votingapi to the new Cassandra API.

92. By David Strauss <straussd@barium>

Continue adapting the Voting API to Cassandra's API

93. By David Strauss <straussd@barium>

This is starting to work really well.

Unmerged revisions

93. By David Strauss <straussd@barium>

This is starting to work really well.

92. By David Strauss <straussd@barium>

Continue adapting the Voting API to Cassandra's API

91. By David Strauss <straussd@barium>

Continue adapting cassandra_votingapi to the new Cassandra API.

90. By David Strauss <straussd@barium>

Update some API calls in cassandra_voting_api. Add tests around Countable implementation and fix bugs.

89. By David Strauss <straussd@barium>

Everything has changed. New tests pass.

88. By David Strauss <straussd@barium>

Update tests. Add test for merge-mode save().

87. By David Strauss <straussd@barium>

Reorder part of save() for clarity.

86. By David Strauss <straussd@barium>

Make save() much more intelligent when not merging columns.

85. By David Strauss <straussd@barium>

Add remove() support and tests.

84. By David Strauss <straussd@barium>

Fix some tabs.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'cassandra.php'
2--- cassandra.php 1970-01-01 00:00:00 +0000
3+++ cassandra.php 2010-03-02 11:43:13 +0000
4@@ -0,0 +1,63 @@
5+<?php
6+include_once './includes/bootstrap.inc';
7+drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
8+
9+$connection = new Cassandra();
10+
11+$document = array(
12+ 'first_name' => 'David',
13+ 'last_name' => 'Strauss',
14+);
15+
16+$connection->Keyspace1->Standard2['1234'] = $document;
17+
18+$document = array(
19+ 'first_name' => 'Aaron',
20+ 'last_name' => 'Stanush',
21+);
22+
23+$connection->Keyspace1->Standard2['1255'] = $document;
24+
25+echo '== Read "1234" ==' . "\n";
26+
27+echo 'First name: ', $connection->Keyspace1->Standard2['1234']['first_name'], "\n";
28+echo 'Last name: ', $connection->Keyspace1->Standard2['1234']['last_name'], "\n";
29+
30+echo '== Save only one field to "1234" ==' . "\n";
31+
32+$document = array(
33+ 'last_name' => 'Nienkerk',
34+);
35+
36+$connection->Keyspace1->Standard2['1234'] = $document;
37+
38+echo '== Read "1234" ==' . "\n";
39+
40+echo 'First name: ', $connection->Keyspace1->Standard2['1234']['first_name'], "\n";
41+echo 'Last name: ', $connection->Keyspace1->Standard2['1234']['last_name'], "\n";
42+
43+echo '== Remove "last_name" from "1234" ==' . "\n";
44+
45+unset($connection->Keyspace1->Standard2['1234']['last_name']);
46+
47+echo '== Read "1234" ==' . "\n";
48+
49+echo 'First name: ', $connection->Keyspace1->Standard2['1234']['first_name'], "\n";
50+echo 'Last name: ', $connection->Keyspace1->Standard2['1234']['last_name'], "\n";
51+
52+echo '== Individually set values for "1234" ==' . "\n";
53+
54+$connection->Keyspace1->Standard2['1234']['first_name'] = 'Todd';
55+$connection->Keyspace1->Standard2['1234']['last_name'] = 'Nienkerk';
56+
57+echo '== Read "1234" ==' . "\n";
58+
59+echo 'First name: ', $connection->Keyspace1->Standard2['1234']['first_name'], "\n";
60+echo 'Last name: ', $connection->Keyspace1->Standard2['1234']['last_name'], "\n";
61+
62+echo '== Remove "1234" ==' . "\n";
63+
64+unset($connection->Keyspace1->Standard2['1234']);
65+
66+$result = isset($connection->Keyspace1->Standard2['1234']);
67+var_dump($result);
68
69=== added file 'dvote.php'
70--- dvote.php 1970-01-01 00:00:00 +0000
71+++ dvote.php 2010-03-02 11:43:13 +0000
72@@ -0,0 +1,95 @@
73+<?php
74+include_once './includes/bootstrap.inc';
75+drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
76+
77+function create_nodes($count) {
78+ $nids = array();
79+
80+ for ($i = 0; $i < $count; $i++) {
81+ $node = new stdClass();
82+ $node->title = 'Test node';
83+ $node->type = 'page';
84+ node_save($node);
85+ $nids[] = $node->nid;
86+ }
87+
88+ return $nids;
89+}
90+
91+function vote_now($nids) {
92+ $uid = 0;
93+
94+ foreach ($nids as $nid) {
95+ for ($i = 0; $i < 100; $i++) {
96+ $votes = array();
97+ $votes[] = array(
98+ 'content_type' => 'recommend-node',
99+ 'content_id' => $nid,
100+ 'uid' => ++$uid,
101+ 'value_type' => 'points',
102+ 'value' => 1,
103+ );
104+ //cassandra_votingapi_add_votes($votes);
105+ cassandra_votingapi_set_votes($votes);
106+ }
107+ }
108+}
109+
110+function vote_slowly($nids) {
111+ $uid = 0;
112+
113+ foreach ($nids as $nid) {
114+ for ($i = 0; $i < 100; $i++) {
115+ $votes = array();
116+ $votes[] = array(
117+ 'content_type' => 'recommend-node',
118+ 'content_id' => $nid,
119+ 'uid' => ++$uid,
120+ 'value_type' => 'points',
121+ 'value' => 1,
122+ );
123+ //votingapi_add_votes($votes);
124+ votingapi_set_votes($votes);
125+ }
126+ }
127+}
128+
129+if (function_exists('thrift_protocol_write_binary')) {
130+ echo 'Thrift accelerator installed.' . "\n";
131+}
132+
133+//$connection = _distributed_votingapi_connection();
134+//echo 'Size: ' . $connection->votingapi->votes->count() . "\n";
135+
136+$start = microtime(TRUE);
137+$nids = create_nodes(20);
138+echo 'Node creation: ' . (microtime(TRUE) - $start) . "\n";
139+
140+print_r($nids);
141+
142+variable_set('votingapi_calculation_schedule', 'cron');
143+variable_get('votingapi_last_cron', $_SERVER['REQUEST_TIME']);
144+
145+$start = microtime(TRUE);
146+vote_now($nids);
147+$cassandra = (microtime(TRUE) - $start);
148+echo 'Vote casting: ' . $cassandra . "\n";
149+
150+
151+$start = microtime(TRUE);
152+cassandra_votingapi_cron();
153+$recalc = (microtime(TRUE) - $start);
154+echo 'Vote recalculation: ' . $recalc . "\n";
155+
156+$start = microtime(TRUE);
157+vote_slowly($nids);
158+$original = (microtime(TRUE) - $start);
159+echo 'Original vote casting: ' . $original . "\n";
160+
161+$start = microtime(TRUE);
162+votingapi_cron();
163+$orig_recalc = (microtime(TRUE) - $start);
164+echo 'Original vote recalculation: ' . $orig_recalc . "\n";
165+
166+echo "\n";
167+echo 'Cassandra was ' . round($original / $cassandra, 2) . 'x faster in real-time votes.' . "\n";
168
169=== added file 'dvote.sh'
170--- dvote.sh 1970-01-01 00:00:00 +0000
171+++ dvote.sh 2010-03-02 11:43:13 +0000
172@@ -0,0 +1,6 @@
173+#!/bin/sh
174+php dvote.php &
175+php dvote.php &
176+php dvote.php &
177+php dvote.php &
178+php dvote.php &
179
180=== added directory 'sites/all/modules'
181=== added directory 'sites/all/modules/cassandra'
182=== added file 'sites/all/modules/cassandra/cassandra.info'
183--- sites/all/modules/cassandra/cassandra.info 1970-01-01 00:00:00 +0000
184+++ sites/all/modules/cassandra/cassandra.info 2010-03-02 11:43:13 +0000
185@@ -0,0 +1,3 @@
186+name = Cassandra
187+core = 6.x
188+
189
190=== added file 'sites/all/modules/cassandra/cassandra.module'
191--- sites/all/modules/cassandra/cassandra.module 1970-01-01 00:00:00 +0000
192+++ sites/all/modules/cassandra/cassandra.module 2010-03-02 11:43:13 +0000
193@@ -0,0 +1,635 @@
194+<?php
195+
196+class CassandraNotSupportedException extends Exception {}
197+class CassandraNotImplementedException extends CassandraNotSupportedException {}
198+
199+class Cassandra {
200+ protected static $initialized = FALSE;
201+ protected $client;
202+ protected $transport;
203+ protected $writes = array();
204+ protected $consistencyLevel;
205+
206+ public function __construct($address = '127.0.0.1', $port = 9160) {
207+ if (!self::$initialized) {
208+ $GLOBALS['THRIFT_ROOT'] = '/usr/share/php/Thrift';
209+ require $GLOBALS['THRIFT_ROOT'].'/packages/cassandra/Cassandra.php';
210+ //require $GLOBALS['THRIFT_ROOT'].'/packages/cassandra/cassandra_types.php';
211+ require $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
212+ require $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
213+ require $GLOBALS['THRIFT_ROOT'].'/transport/TFramedTransport.php';
214+ //require $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
215+ self::$initialized = TRUE;
216+ }
217+
218+ $socket = new TSocket($address, $port);
219+ $this->transport = new TBufferedTransport($socket, 1024, 1024);
220+ $protocol = new TBinaryProtocolAccelerated($this->transport);
221+ //$protocol = new TBinaryProtocol($this->transport);
222+ $this->client = new CassandraClient($protocol);
223+ $this->transport->open();
224+
225+ $this->consistencyLevel = cassandra_ConsistencyLevel::QUORUM;
226+ }
227+
228+ public function __destruct() {
229+ $this->transport->close();
230+ }
231+
232+ public function __get($keyspaceName) {
233+ return new CassandraKeyspace($this, $keyspaceName);
234+ }
235+
236+ public function getClient() {
237+ return $this->client;
238+ }
239+
240+ public function setConsistencyLevel($level = cassandra_ConsistencyLevel::QUORUM) {
241+ $old = $this->consistencyLevel;
242+ $this->consistencyLevel = $level;
243+ return $old;
244+ }
245+
246+ public function getConsistencyLevel() {
247+ return $this->consistencyLevel;
248+ }
249+
250+ public static function getTimestamp() {
251+ return floor(microtime(TRUE) * 1000);
252+ }
253+}
254+
255+class CassandraKeyspace {
256+ protected $connection;
257+ protected $keyspaceName;
258+
259+ public function __construct(Cassandra $connection, $keyspaceName) {
260+ $this->connection = $connection;
261+ $this->keyspaceName = $keyspaceName;
262+ }
263+
264+ public function __get($columnFamilyName) {
265+ return new CassandraColumnFamily($this, $columnFamilyName);
266+ }
267+
268+ public function getName() {
269+ return $this->keyspaceName;
270+ }
271+
272+ public function getConnection() {
273+ return $this->connection;
274+ }
275+}
276+
277+class CassandraColumnFamily implements ArrayAccess, Iterator {
278+ protected $keyspace;
279+ protected $columnFamilyName;
280+
281+ public function __construct(CassandraKeyspace $keyspace, $columnFamilyName) {
282+ $this->keyspace = $keyspace;
283+ $this->columnFamilyName = $columnFamilyName;
284+ }
285+
286+ public function getKeyspace() {
287+ return $this->keyspace;
288+ }
289+
290+ public function getName() {
291+ return $this->columnFamilyName;
292+ }
293+
294+ public function getRows(array $keys, array $restrictToColumnNames = NULL) {
295+ // TODO: Use multiget or multiget_slice to fetch the rows in parallel.
296+ $rows = array();
297+ foreach ($keys as $key) {
298+ $rows[$key] = $this->offsetGet($key);
299+ }
300+ return $rows;
301+ }
302+
303+ public function getRowRange($beginKey, $endKey, array $restrictToColumnNames = NULL) {
304+ throw new CassandraNotImplementedException();
305+
306+ $rows = array();
307+
308+ // TODO: Use get_range_slice to fetch the matching rows.
309+
310+ return $rows;
311+ }
312+
313+ /*
314+ * Functions to implement ArrayAccess.
315+ */
316+
317+ /**
318+ * Try to pull at least one column for $key.
319+ */
320+ public function offsetExists($key) {
321+ // Use the current column family.
322+ $columnParent = new cassandra_ColumnParent();
323+ $columnParent->column_family = $this->columnFamilyName;
324+ $columnParent->super_column = NULL;
325+
326+ // Restrict to one column.
327+ $slice_range = new cassandra_SliceRange();
328+ $slice_range->start = '';
329+ $slice_range->finish = '';
330+ $slice_range->count = 1;
331+
332+ // Build a predicate that integrates the one-column restriction.
333+ $predicate = new cassandra_SlicePredicate();
334+ $predicate->slice_range = $slice_range;
335+
336+ $set = $this->getKeyspace()->getConnection()->getClient()->get_slice(
337+ $this->keyspace->getName(),
338+ $key,
339+ $columnParent,
340+ $predicate,
341+ $this->getKeyspace()->getConnection()->getConsistencyLevel()
342+ );
343+
344+ // TODO: Verify functionality.
345+ return !empty($set);
346+ }
347+
348+ public function offsetGet($key) {
349+ return new CassandraRow($this, $key);
350+ }
351+
352+ public function offsetSet($key, $columns) {
353+ // Cassandra does not support appending rows with auto-generated keys.
354+ if (empty($key)) {
355+ throw new CassandraNotSupportedException('A key must be specified to create a row.');
356+ }
357+
358+ // Value set must be an array.
359+ if (!is_array($columns)) {
360+ throw new CassandraNotSupportedException('Only an array can be stored to a row.');
361+ }
362+
363+ // Cache the timestamp so it can be consistent for all columns we're
364+ // working with.
365+ $milliseconds_since_epoch = $this->getKeyspace()->getConnection()->getTimestamp();
366+
367+ // Construct the set of columns from the document.
368+ $c_or_scs = array();
369+ foreach ($columns as $column_name => $column_value) {
370+ if (isset($column_value)) {
371+ $column = new cassandra_Column();
372+ $column->name = $column_name;
373+ $column->value = json_encode($column_value);
374+ $column->timestamp = $milliseconds_since_epoch;
375+ $c_or_sc = new cassandra_ColumnOrSuperColumn();
376+ $c_or_sc->column = $column;
377+ $c_or_scs[] = $c_or_sc;
378+ }
379+ }
380+
381+ $mutation = array($this->getName() => $c_or_scs);
382+
383+ // Insert the new values.
384+
385+ $this->getKeyspace()->getConnection()->getClient()->batch_insert(
386+ $this->getKeyspace()->getName(),
387+ $key,
388+ $mutation,
389+ $this->getKeyspace()->getConnection()->getConsistencyLevel()
390+ );
391+
392+ // Delete the old values.
393+
394+ $column_path = new cassandra_ColumnPath();
395+ $column_path->column_family = $this->getName();
396+ $column_path->super_column = NULL;
397+ $column_path->column = NULL;
398+
399+ // This row deletion is based on the assumption that anything
400+ // just a touch older is a stale column.
401+ try {
402+ $this->getKeyspace()->getConnection()->getClient()->remove(
403+ $this->getKeyspace()->getName(),
404+ $key,
405+ $column_path,
406+ $milliseconds_since_epoch - 1,
407+ $this->getKeyspace()->getConnection()->getConsistencyLevel()
408+ );
409+ }
410+ catch (Exception $e) {
411+ echo 'Caught exception: ', $e->getMessage(), "\n";
412+ }
413+ }
414+
415+ public function offsetUnset($key) {
416+ $column_path = new cassandra_ColumnPath();
417+ $column_path->column_family = $this->columnFamilyName;
418+ $column_path->super_column = NULL;
419+ $column_path->column = NULL;
420+
421+ $this->getKeyspace()->getConnection()->getClient()->remove(
422+ $this->getKeyspace()->getName(),
423+ $key,
424+ $column_path,
425+ $this->getKeyspace()->getConnection()->getTimestamp(),
426+ $this->getKeyspace()->getConnection()->getConsistencyLevel()
427+ );
428+ }
429+
430+ /*
431+ * Functions to implement Iterator.
432+ */
433+
434+ public function rewind() {
435+ throw new CassandraNotImplementedException();
436+ }
437+
438+ public function current() {
439+ throw new CassandraNotImplementedException();
440+ }
441+
442+ public function key() {
443+ throw new CassandraNotImplementedException();
444+ }
445+
446+ public function next() {
447+ throw new CassandraNotImplementedException();
448+ }
449+
450+ public function valid() {
451+ throw new CassandraNotImplementedException();
452+ }
453+}
454+
455+class CassandraRow implements ArrayAccess, Iterator, Countable {
456+ protected $columnFamily;
457+ protected $key;
458+ protected $columnLimit = 1000;
459+
460+ // Used for iteration:
461+ protected $columns;
462+ protected $allColumnsLoaded = FALSE;
463+
464+ public function __construct($columnFamily, $key) {
465+ $this->columnFamily = $columnFamily;
466+ $this->key = $key;
467+ }
468+
469+ public function getColumnFamily() {
470+ return $this->columnFamily;
471+ }
472+
473+ public function getKey() {
474+ return $this->key;
475+ }
476+
477+ public function loadAll() {
478+ $column_parent = new cassandra_ColumnParent();
479+ $column_parent->column_family = $this->getColumnFamily()->getName();
480+ $column_parent->super_column = NULL;
481+ $column_parent->column = NULL;
482+
483+ // Fetch all columns.
484+ $slice_range = new cassandra_SliceRange();
485+ $slice_range->start = '';
486+ $slice_range->finish = '';
487+ $slice_range->count = $this->columnLimit;
488+
489+ // Build a predicate that integrates the all-column range.
490+ $predicate = new cassandra_SlicePredicate();
491+ $predicate->slice_range = $slice_range;
492+
493+ $c_or_scs = $this->getColumnFamily()->getKeyspace()->getConnection()->getClient()->get_slice(
494+ $this->getColumnFamily()->getKeyspace()->getName(),
495+ $this->getKey(),
496+ $column_parent,
497+ $predicate,
498+ $this->getColumnFamily()->getKeyspace()->getConnection()->getConsistencyLevel()
499+ );
500+
501+ $this->columns = array();
502+ foreach ($c_or_scs as $c_or_sc) {
503+ $this->columns[$c_or_sc->column->name] = json_decode($c_or_sc->column->value);
504+ }
505+
506+ $this->allColumnsLoaded = TRUE;
507+ }
508+
509+ // TODO: Use this value when implementing Iterator.
510+ public function setColumnLimit($columnLimit) {
511+ $this->columnLimit = $columnLimit;
512+ }
513+
514+ /*
515+ * Functions to implement ArrayAccess.
516+ */
517+
518+ public function offsetSet($columnName, $columnValue) {
519+ // Cassandra does not allow appending anonymous columns.
520+ if (empty($columnName)) {
521+ throw new CassandraNotSupportedException('A column name must be specified to store a column.');
522+ }
523+
524+ $column_path = new cassandra_ColumnPath();
525+ $column_path->column_family = $this->getColumnFamily()->getName();
526+ $column_path->super_column = NULL;
527+ $column_path->column = $columnName;
528+
529+ $this->getColumnFamily()->getKeyspace()->getConnection()->getClient()->insert(
530+ $this->getColumnFamily()->getKeyspace()->getName(),
531+ $this->getKey(),
532+ $column_path,
533+ json_encode($columnValue),
534+ $this->getColumnFamily()->getKeyspace()->getConnection()->getTimestamp(),
535+ $this->getColumnFamily()->getKeyspace()->getConnection()->getConsistencyLevel()
536+ );
537+
538+ $this->columns[$columnName] = $columnValue;
539+ }
540+
541+ public function offsetExists($columnName) {
542+ return !empty($this->offsetGet[$columnName]);
543+ }
544+
545+ public function offsetUnset($columnName) {
546+ unset($this->columns[$columnName]);
547+
548+ $column_path = new cassandra_ColumnPath();
549+ $column_path->column_family = $this->getColumnFamily()->getName();
550+ $column_path->super_column = NULL;
551+ $column_path->column = NULL;
552+
553+ $this->getColumnFamily()->getKeyspace()->getConnection()->getClient()->remove(
554+ $this->getColumnFamily()->getKeyspace()->getName(),
555+ $this->getKey(),
556+ $column_path,
557+ $this->getColumnFamily()->getKeyspace()->getConnection()->getTimestamp(),
558+ $this->getColumnFamily()->getKeyspace()->getConnection()->getConsistencyLevel()
559+ );
560+ }
561+
562+ public function offsetGet($columnName) {
563+ // TODO: Allow forced reading of updated values?
564+
565+ if (!isset($this->columns[$columnName])) {
566+ $column_path = new cassandra_ColumnPath();
567+ $column_path->column_family = $this->getColumnFamily()->getName();
568+ $column_path->super_column = NULL;
569+ $column_path->column = $columnName;
570+
571+ try {
572+ $c_or_sc = $this->getColumnFamily()->getKeyspace()->getConnection()->getClient()->get(
573+ $this->getColumnFamily()->getKeyspace()->getName(),
574+ $this->getKey(),
575+ $column_path,
576+ $this->getColumnFamily()->getKeyspace()->getConnection()->getConsistencyLevel()
577+ );
578+ }
579+ catch (cassandra_NotFoundException $e) {
580+ return NULL;
581+ }
582+
583+ $this->columns[$columnName] = json_decode($c_or_sc->column->value);
584+ }
585+
586+ return $this->columns[$columnName];
587+ }
588+
589+ /*
590+ * Functions to implement Iterator.
591+ */
592+
593+ public function rewind() {
594+ // Initialize the complete column set, if necessary.
595+ if (!$this->allColumnsLoaded) {
596+ $this->loadAll();
597+ }
598+
599+ reset($this->columns);
600+ }
601+
602+ public function current() {
603+ return current($this->columns);
604+ }
605+
606+ public function key() {
607+ return key($this->columns);
608+ }
609+
610+ public function next() {
611+ return next($this->columns);
612+ }
613+
614+ public function valid() {
615+ return $this->current() !== FALSE;
616+ }
617+
618+ /*
619+ * Functions to implement Countable.
620+ */
621+
622+ public function count() {
623+ if ($this->allColumnsLoaded) {
624+ return count($this->columns);
625+ }
626+
627+ $column_parent = new cassandra_ColumnParent();
628+ $column_parent->column_family = $this->getColumnFamily()->getName();
629+ $column_parent->super_column = NULL;
630+ $column_parent->column = NULL;
631+
632+ $count = $this->getColumnFamily()->getKeyspace()->getConnection()->getClient()->get_count(
633+ $this->getColumnFamily()->getKeyspace()->getName(),
634+ $this->getKey(),
635+ $column_parent,
636+ $this->getColumnFamily()->getKeyspace()->getConnection()->getConsistencyLevel()
637+ );
638+
639+ return $count;
640+ }
641+}
642+
643+/*
644+abstract class CassandraRows implements ArrayAccess, Iterator {
645+ protected $currentKey = NULL;
646+ protected $rows = NULL;
647+
648+ protected function initializeIterator() {
649+
650+ }
651+
652+ function rewind() {
653+ $keys = array_keys($rows);
654+ $this->currentKey = $keys[0];
655+ }
656+
657+ function currentKey() {
658+ if (!$this->valid()) {
659+ return NULL;
660+ }
661+
662+ return $this->set[$this->position]->key;
663+ }
664+
665+ function current() {
666+ // TODO: Cache processed document.
667+
668+ if (!$this->valid()) {
669+ return NULL;
670+ }
671+
672+ $document = array(
673+ '_id' => $this->set[$this->position]->key,
674+ );
675+
676+ foreach ($this->set[$this->position]->columns as $c_or_cs) {
677+ $document[$c_or_cs->column->name] = json_decode($c_or_cs->column->value);
678+ }
679+
680+ return $document;
681+ }
682+
683+ function key() {
684+ return $this->position;
685+ }
686+
687+ function next() {
688+ ++$this->position;
689+ }
690+
691+ function valid() {
692+ return isset($this->set[$this->position]);
693+ }
694+}
695+
696+class CassandraRowRange extends CassandraRows {
697+ protected $startKey, $endKey;
698+
699+ public function __construct($startKey, $endKey) {
700+ $this->startKey = $startKey;
701+ $this->endKey = $endKey;
702+ }
703+}
704+
705+class CassandraRowSet extends CassandraRows implements Countable {
706+ protected $keys;
707+
708+ public function __construct(array $keys) {
709+ $this->keys = $keys;
710+ }
711+
712+ public function count() {
713+ return count($this->keys);
714+ }
715+}
716+
717+class CassandraColumns implements ArrayAccess, Iterator, Countable {
718+ protected $allColumns = FALSE;
719+ protected $columns;
720+ protected $currentName;
721+
722+ public function offsetSet($offset, $value) {
723+ if ($offset == '') {
724+ // TODO: Insert column
725+ }
726+ else {
727+ $this->container[$offset] = $value;
728+ }
729+ }
730+
731+ public function offsetExists($offset) {
732+ return isset($this->container[$offset]);
733+ }
734+
735+ public function offsetUnset($offset) {
736+ unset($this->container[$offset]);
737+ }
738+
739+ public function offsetGet($offset) {
740+ return isset($this->container[$offset]) ? $this->container[$offset] : null;
741+ }
742+
743+ public function rewind() {
744+ reset($this->container);
745+ }
746+
747+ public function current() {
748+ return current($this->container);
749+ }
750+
751+ public function key() {
752+ return key($this->container);
753+ }
754+
755+ public function next() {
756+ return next($this->container);
757+ }
758+
759+ public function valid() {
760+ return isset($this->column);
761+ }
762+
763+ public function count() {
764+ return count($this->columns);
765+ }
766+}
767+
768+class CassandraCursor implements Iterator {
769+ protected $position;
770+ protected $set;
771+
772+ public function __construct($set) {
773+ $this->position = 0;
774+
775+ // TODO: Lazily prune deleted records.
776+ $clean = array();
777+ foreach ($set as $item) {
778+ if (!empty($item->columns)) {
779+ $clean[] = $item;
780+ }
781+ }
782+
783+ $this->set = $clean;
784+ }
785+
786+ function rewind() {
787+ $this->position = 0;
788+ }
789+
790+ function currentKey() {
791+ if (!$this->valid()) {
792+ return NULL;
793+ }
794+
795+ return $this->set[$this->position]->key;
796+ }
797+
798+ function current() {
799+ // TODO: Cache processed document.
800+
801+ if (!$this->valid()) {
802+ return NULL;
803+ }
804+
805+ $document = array(
806+ '_id' => $this->set[$this->position]->key,
807+ );
808+
809+ foreach ($this->set[$this->position]->columns as $c_or_cs) {
810+ $document[$c_or_cs->column->name] = json_decode($c_or_cs->column->value);
811+ }
812+
813+ return $document;
814+ }
815+
816+ function key() {
817+ return $this->position;
818+ }
819+
820+ function next() {
821+ ++$this->position;
822+ }
823+
824+ function valid() {
825+ return isset($this->set[$this->position]);
826+ }
827+}
828+*/
829
830=== added file 'sites/all/modules/cassandra/cassandra.test'
831--- sites/all/modules/cassandra/cassandra.test 1970-01-01 00:00:00 +0000
832+++ sites/all/modules/cassandra/cassandra.test 2010-03-02 11:43:13 +0000
833@@ -0,0 +1,85 @@
834+<?php
835+// $Id: votingapi.test,v 1.1.2.3 2009/06/24 19:16:05 eaton Exp $
836+
837+/**
838+ * @file
839+ * Test file for Cassandra module.
840+ */
841+
842+class CassandraTestCase extends DrupalWebTestCase {
843+ protected $connection;
844+
845+ public static function getInfo() {
846+ return array(
847+ 'name' => t('Cassandra'),
848+ 'description' => t('Cassandra'),
849+ 'group' => t('Cassandra'),
850+ );
851+ }
852+
853+ function setUp() {
854+ parent::setUp('cassandra');
855+ $this->connection = new Cassandra();
856+ }
857+
858+ function tearDown() {
859+ parent::tearDown();
860+ }
861+
862+ function testOperations() {
863+ $now = time();
864+ $connection = $this->connection;
865+
866+ $document = array(
867+ 'first_name' => 'David',
868+ 'last_name' => 'Strauss',
869+ 'now' => $now,
870+ );
871+ $connection->Keyspace1->Standard2['1234'] = $document;
872+ $result = $connection->Keyspace1->Standard2['1234'];
873+ $this->assertEqual($result['now'], $now, t('Field "now" on loaded record matches saved record.'));
874+ $this->assertEqual($result['first_name'], 'David', t('Field "first_name" on loaded record matches saved record.'));
875+ $this->assertEqual($result['last_name'], 'Strauss', t('Field "last_name" on loaded record matches saved record.'));
876+ $this->assertEqual(count($result), 3, t('Result has three columns.'));
877+
878+ $document = array(
879+ 'first_name' => 'Aaron',
880+ 'last_name' => 'Stanush',
881+ );
882+ $connection->Keyspace1->Standard2['1255'] = $document;
883+ $result = $connection->Keyspace1->Standard2['1255'];
884+ $this->assertNull($result['now'], t('Field "now" does not exist.'));
885+ $this->assertEqual($result['first_name'], 'Aaron', t('Field "first_name" on loaded record matches saved record.'));
886+ $this->assertEqual($result['last_name'], 'Stanush', t('Field "last_name" on loaded record matches saved record.'));
887+ $this->assertEqual(count($result), 2, t('Result has two columns.'));
888+
889+ $document = array(
890+ 'last_name' => 'Nienkerk',
891+ );
892+ $connection->Keyspace1->Standard2['1234'] = $document;
893+ $result = $connection->Keyspace1->Standard2['1234'];
894+ $this->assertNull($result['now'], t('Field "now" no longer exists.'));
895+ $this->assertNull($result['first_name'], t('Field "first_name" no longer exists.'));
896+ $this->assertEqual($result['last_name'], 'Nienkerk', t('Field "last_name" on loaded record matches saved record.'));
897+
898+ unset($connection->Keyspace1->Standard2['1234']['last_name']);
899+ $this->assertNull($result['last_name'], t('Field "last_name" no longer exists.'));
900+
901+ $connection->Keyspace1->Standard2['1234']['first_name'] = 'Todd';
902+ $connection->Keyspace1->Standard2['1234']['last_name'] = 'Nienkerk';
903+ $result = $connection->Keyspace1->Standard2['1234'];
904+ $this->assertEqual($result['first_name'], 'Todd', t('Field "first_name" on loaded record matches individually saved column.'));
905+ $this->assertEqual($result['last_name'], 'Nienkerk', t('Field "last_name" on loaded record matches individually saved column.'));
906+
907+ $connection->Keyspace1->Standard2['1234']['last_name'] = 'Strauss';
908+ $result = $connection->Keyspace1->Standard2['1234'];
909+ $this->assertEqual($result['first_name'], 'Todd', t('Field "first_name" on loaded record was not overwritten.'));
910+ $this->assertEqual($result['last_name'], 'Strauss', t('Field "last_name" on loaded record matches individually saved column.'));
911+
912+ unset($connection->Keyspace1->Standard2['1234']);
913+ $this->assertFalse(isset($connection->Keyspace1->Standard2['1234']), t('Record "1234" has been successfully removed.'));
914+
915+ unset($connection->Keyspace1->Standard2['1255']);
916+ $this->assertFalse(isset($connection->Keyspace1->Standard2['1255']), t('Record "1255" has been successfully removed.'));
917+ }
918+}
919
920=== added directory 'sites/all/modules/cassandra_votingapi'
921=== added file 'sites/all/modules/cassandra_votingapi/cassandra_votingapi.info'
922--- sites/all/modules/cassandra_votingapi/cassandra_votingapi.info 1970-01-01 00:00:00 +0000
923+++ sites/all/modules/cassandra_votingapi/cassandra_votingapi.info 2010-03-02 11:43:13 +0000
924@@ -0,0 +1,5 @@
925+name = Cassandra Voting API
926+core = 6.x
927+package = Voting
928+dependencies[] = votingapi
929+
930
931=== added file 'sites/all/modules/cassandra_votingapi/cassandra_votingapi.module'
932--- sites/all/modules/cassandra_votingapi/cassandra_votingapi.module 1970-01-01 00:00:00 +0000
933+++ sites/all/modules/cassandra_votingapi/cassandra_votingapi.module 2010-03-02 11:43:13 +0000
934@@ -0,0 +1,333 @@
935+<?php
936+
937+class cassandraVotingApiNotSupported extends Exception {}
938+class cassandraVotingApiNotImplemented extends cassandraVotingApiNotSupported {}
939+
940+function _cassandra_votingapi_connection() {
941+ static $connection;
942+ if (!isset($connection)) {
943+ $connection = new Cassandra();
944+
945+ // Drop consistency level to ONE.
946+ $connection->setConsistencyLevel(cassandra_ConsistencyLevel::ONE);
947+ }
948+ return $connection;
949+}
950+
951+function cassandra_votingapi_cron() {
952+ $connection = _cassandra_votingapi_connection();
953+ $erasure = $connection->VotingAPI->VotesByEntity['recalculate'];
954+ $recalculate = $connection->VotingAPI->VotesByEntity['recalculate'];
955+ $count = 0;
956+ $cached = array();
957+ foreach ($recalculate as $entity_key => $junk) {
958+ list($type, $id) = explode(':', $entity_key);
959+ $cached = array_merge($cached, cassandra_votingapi_recalculate_results($type, $id, TRUE, FALSE));
960+ unset($erasure[$entity_key]);
961+
962+ // Insert in batches.
963+ if (count($cached) >= 20) {
964+ $sql = 'REPLACE INTO {votingapi_cache} (content_type, content_id, value, value_type, tag, function, timestamp) VALUES ';
965+ $values = array();
966+ $sets = array();
967+ foreach ($cached as $row) {
968+ $sets[] = "('%s', %d, %f, '%s', '%s', '%s', %d)";
969+ $values = array_merge($values, array_values($row));
970+ }
971+ $sql .= implode(', ', $sets);
972+ db_query($sql, $values);
973+ $cached = array();
974+ }
975+
976+ $count++;
977+ }
978+
979+ if (!empty($cached)) {
980+ $sql = 'INSERT INTO {votingapi_cache} (content_type, content_id, value, value_type, tag, function, timestamp) VALUES ';
981+ $values = array();
982+ $sets = array();
983+ foreach ($cached as $row) {
984+ $sets[] = "('%s', %d, %f, '%s', '%s', '%s', %d)";
985+ $values = array_merge($values, array_values($row));
986+ }
987+ $sql .= implode(', ', $sets);
988+ db_query($sql, $values);
989+ }
990+}
991+
992+function cassandra_votingapi_select_votes($criteria = array(), $limit = NULL) {
993+ $connection = _cassandra_votingapi_connection();
994+
995+ // Attempt to narrow results to one row (and ideally one column).
996+ if (isset($criteria['content_type']) && isset($criteria['content_id'])) {
997+ $entity_key = $criteria['content_type'] . ':' . $criteria['content_id'];
998+ unset($criteria['content_type']);
999+ unset($criteria['content_id']);
1000+ if (isset($criteria['uid'])) {
1001+ unset($criteria['uid']);
1002+
1003+ // Exact match.
1004+ $source_key = $criteria['uid'];
1005+ $votes = array($connection->VotingAPI->VotesByEntity[$entity_key][$source_key]);
1006+ }
1007+ else if (isset($criteria['source'])) {
1008+ unset($criteria['source']);
1009+
1010+ // Exact match.
1011+ $source_key = $criteria['source'];
1012+ $votes = array($connection->VotingAPI->VotesByEntity[$entity_key][$source_key]);
1013+ }
1014+ else {
1015+ $votes = $connection->VotingAPI->VotesByEntity[$entity_key];
1016+ }
1017+ }
1018+ else if (isset($criteria['uid'])) {
1019+ unset($criteria['uid']);
1020+ $source_key = $criteria['uid'];
1021+ $votes = $connection->VotingAPI->VotesBySource[$source_key];
1022+ }
1023+ else if (isset($criteria['source'])) {
1024+ unset($criteria['source']);
1025+ $source_key = $criteria['source'];
1026+ $votes = $connection->VotingAPI->VotesBySource[$source_key];
1027+ }
1028+
1029+ // TODO: Support PHP-side filtering for other criteria, but fail for now.
1030+ if (!empty($criteria)) {
1031+ throw new cassandraVotingApiNotImplemented();
1032+ }
1033+
1034+ if (!empty($limit)) {
1035+ $votes->setColumnLimit($limit);
1036+ }
1037+
1038+ /*
1039+ $anon_window = variable_get('votingapi_anonymous_window', 3600);
1040+ if (!empty($criteria['vote_source']) && $anon_window > 0) {
1041+ $criteria['timestamp'] = $_SERVER['REQUEST_TIME'] - $anon_window;
1042+ }
1043+
1044+ // Find items greater than the specified timestamp.
1045+ if (isset($criteria['timestamp'])) {
1046+ $criteria['timestamp'] = array('$gt' => $criteria['timestamp']);
1047+ }
1048+ */
1049+
1050+ return $votes;
1051+}
1052+
1053+function cassandra_votingapi_delete_votes($votes = array()) {
1054+ $connection = _cassandra_votingapi_connection();
1055+ foreach ($votes as $vote) {
1056+ $entity_key = $vote['content_type'] . ':' . $vote['content_id'];
1057+ $source_key = $vote['vote_source'];
1058+ if ($vote['uid'] > 0) {
1059+ $source_key = $vote['uid'];
1060+ }
1061+ unset($connection->VotingAPI->VotesByEntity[$entity_key][$source_key]);
1062+ unset($connection->VotingAPI->VotesBySource[$source_key][$entity_key]);
1063+ $connection->VotingAPI->VotesByEntity['recalculate'][$entity_key] = 1;
1064+ }
1065+}
1066+
1067+function cassandra_votingapi_add_votes(&$votes) {
1068+ if ($votes == FALSE) {
1069+ return $time;
1070+ }
1071+
1072+ // TODO: Change to use "entity" for D7
1073+ if (!empty($votes['content_id'])) {
1074+ $votes = array($votes);
1075+ }
1076+
1077+ $connection = _cassandra_votingapi_connection();
1078+
1079+ // Write each vote to Cassandra.
1080+ foreach ($votes as $key => $vote) {
1081+ _votingapi_prep_vote($vote);
1082+
1083+ // TODO: Change to use "entity" for D7
1084+ $entity_key = $vote['content_type'] . ':' . $vote['content_id'];
1085+
1086+ // Set the source to UID if authenticated or IP address if anonymous.
1087+ $source_key = $vote['vote_source'];
1088+ if ($vote['uid'] > 0) {
1089+ $source_key = $vote['uid'];
1090+ }
1091+
1092+ //$start = microtime(TRUE);
1093+
1094+ $connection->VotingAPI->VotesByEntity[$entity_key][$source_key] = $vote;
1095+ $connection->VotingAPI->VotesBySource[$source_key][$entity_key] = $vote;
1096+
1097+ // Mark for reindexing.
1098+ $connection->VotingAPI->VotesByEntity['recalculate'][$entity_key] = $source_key;
1099+ //echo 'Storage: ' . (microtime(TRUE) - $start) . "\n";
1100+ }
1101+
1102+ module_invoke_all('votingapi_insert', $votes);
1103+ return $votes;
1104+}
1105+
1106+function _cassandra_votingapi_get_standard_results($content_type, $content_id) {
1107+ $connection = _cassandra_votingapi_connection();
1108+ $criteria = array(
1109+ 'content_id' => $content_id,
1110+ 'content_type' => $content_type,
1111+ );
1112+
1113+ $votes = cassandra_votingapi_select_votes($criteria);
1114+
1115+ $cache = array();
1116+
1117+ foreach ($votes as $vote) {
1118+ // Type-cast, at least for now. :-/
1119+ $vote = (array) $vote;
1120+
1121+ if ($vote['value_type'] == 'points' || $vote['value_type'] == 'percent') {
1122+ $cache[$vote['tag']][$vote['value_type']]['count']++;
1123+ $cache[$vote['tag']][$vote['value_type']]['sum'] += $vote['value'];
1124+
1125+ // We could probably not calculate this after every vote.
1126+ $cache[$vote['tag']][$vote['value_type']]['average'] = $cache[$vote['tag']][$vote['value_type']]['sum'] / $cache[$vote['tag']][$vote['value_type']]['count'];
1127+ }
1128+ else if ($vote['value_type'] == 'option') {
1129+ if (!isset($cache[$vote['tag']][$vote['value_type']]['option-' . $vote['value']])) {
1130+ $cache[$vote['tag']][$vote['value_type']]['option-' . $vote['value']] = 0;
1131+ }
1132+
1133+ $cache[$vote['tag']][$vote['value_type']]['option-' . $vote['value']] += $vote['score'];
1134+ }
1135+ }
1136+
1137+ return $cache;
1138+}
1139+
1140+function cassandra_votingapi_recalculate_results($content_type, $content_id, $force_calculation = FALSE, $save = TRUE) {
1141+ // if we're operating in cron mode, and the 'force recalculation' flag is NOT set,
1142+ // bail out. The cron run will pick up the results.
1143+
1144+ if (variable_get('votingapi_calculation_schedule', 'immediate') != 'cron' || $force_calculation == TRUE) {
1145+
1146+ $connection = _cassandra_votingapi_connection();
1147+
1148+ $criteria = array('content_type' => $content_type, 'content_id' => $content_id);
1149+
1150+ if ($save) {
1151+ _votingapi_delete('cache', $criteria);
1152+ }
1153+
1154+ // Bulk query to pull the majority of the results we care about.
1155+ $cache = _cassandra_votingapi_get_standard_results($content_type, $content_id);
1156+
1157+ // Give other modules a chance to alter the collection of votes.
1158+ drupal_alter('votingapi_results', $cache, $content_type, $content_id);
1159+
1160+ // Now, do the caching. Woo.
1161+ $cached = array();
1162+ foreach ($cache as $tag => $types) {
1163+ foreach ($types as $type => $functions) {
1164+ foreach ($functions as $function => $value) {
1165+ $cached[] = array(
1166+ 'content_type' => $content_type,
1167+ 'content_id' => $content_id,
1168+ 'value_type' => $type,
1169+ 'value' => $value,
1170+ 'tag' => $tag,
1171+ 'function' => $function,
1172+ );
1173+ }
1174+ }
1175+ }
1176+
1177+ if ($save) {
1178+ votingapi_add_results($cached);
1179+ }
1180+
1181+ // Give other modules a chance to act on the results of the vote totaling.
1182+ module_invoke_all('votingapi_results', $cached, $content_type, $content_id);
1183+
1184+ return $cached;
1185+ }
1186+}
1187+
1188+function cassandra_votingapi_select_single_result_value($criteria = array()) {
1189+ if ($results = cassandra_votingapi_select_votes($criteria)) {
1190+ return $results[0]['value'];
1191+ }
1192+}
1193+
1194+/**
1195+ * Cast a vote on a particular piece of content.
1196+ *
1197+ * This function does most of the heavy lifting needed by third-party modules
1198+ * based on VotingAPI. Handles clearing out old votes for a given piece of
1199+ * content, saving the incoming votes, and re-tallying the results given the
1200+ * new data.
1201+ *
1202+ * Modules that need more explicit control can call votingapi_add_votes() and
1203+ * manage the deletion/recalculation tasks manually.
1204+ *
1205+ * @param $votes
1206+ * An array of votes, each with the following structure:
1207+ * $vote['content_type'] (Optional, defaults to 'node')
1208+ * $vote['content_id'] (Required)
1209+ * $vote['value_type'] (Optional, defaults to 'percent')
1210+ * $vote['value'] (Required)
1211+ * $vote['tag'] (Optional, defaults to 'vote')
1212+ * $vote['uid'] (Optional, defaults to current user)
1213+ * $vote['vote_source'] (Optional, defaults to current IP)
1214+ * $vote['timestamp'] (Optional, defaults to time())
1215+ * @param $criteria
1216+ * A keyed array used to determine what votes will be deleted when the current
1217+ * vote is cast. If no value is specified, all votes for the current content
1218+ * by the current user will be reset. If an empty array is passed in, no votes
1219+ * will be reset and all incoming votes will be saved IN ADDITION to existing
1220+ * ones.
1221+ * $criteria['vote_id'] (If this is set, all other keys are skipped)
1222+ * $criteria['content_type']
1223+ * $criteria['content_type']
1224+ * $criteria['value_type']
1225+ * $criteria['tag']
1226+ * $criteria['uid']
1227+ * $criteria['vote_source']
1228+ * $criteria['timestamp'] (If this is set, records with timestamps
1229+ * GREATER THAN the set value will be selected.)
1230+ * @return
1231+ * An array of vote result records affected by the vote. The values are
1232+ * contained in a nested array keyed thusly:
1233+ * $value = $results[$content_type][$content_id][$tag][$value_type][$function]
1234+ *
1235+ * @see cassandra_votingapi_add_votes()
1236+ * @see cassandra_votingapi_recalculate_results()
1237+ */
1238+function cassandra_votingapi_set_votes(&$votes) {
1239+ $touched = array();
1240+ if (!empty($votes['content_id'])) {
1241+ $votes = array($votes);
1242+ }
1243+
1244+ // Don't handle clearing out old votes.
1245+ // Cassandra will just overwrite them.
1246+
1247+ foreach ($votes as $key => $vote) {
1248+ _votingapi_prep_vote($vote);
1249+ $votes[$key] = $vote; // Is this needed? Check to see how PHP4 handles refs.
1250+ }
1251+
1252+ // Cast the actual votes, inserting them into the database.
1253+ cassandra_votingapi_add_votes($votes);
1254+
1255+ foreach ($votes as $vote) {
1256+ $touched[$vote['content_type']][$vote['content_id']] = TRUE;
1257+ }
1258+
1259+ if (variable_get('votingapi_calculation_schedule', 'immediate') != 'cron') {
1260+ foreach ($touched as $type => $ids) {
1261+ foreach ($ids as $id => $bool) {
1262+ $touched[$type][$id] = cassandra_votingapi_recalculate_results($type, $id);
1263+ }
1264+ }
1265+ }
1266+ return $touched;
1267+}
1268
1269=== added file 'sites/all/modules/cassandra_votingapi/storage-conf.xml'
1270--- sites/all/modules/cassandra_votingapi/storage-conf.xml 1970-01-01 00:00:00 +0000
1271+++ sites/all/modules/cassandra_votingapi/storage-conf.xml 2010-03-02 11:43:13 +0000
1272@@ -0,0 +1,261 @@
1273+<Storage>
1274+ <ClusterName>Test Cluster</ClusterName>
1275+ <AutoBootstrap>false</AutoBootstrap>
1276+ <Keyspaces>
1277+ <Keyspace Name="Keyspace1">
1278+ <KeysCachedFraction>0.2</KeysCachedFraction>
1279+ <ColumnFamily CompareWith="BytesType" Name="Standard1"/>
1280+ <ColumnFamily CompareWith="UTF8Type" Name="Standard2"/>
1281+ <ColumnFamily CompareWith="TimeUUIDType" Name="StandardByUUID1"/>
1282+ <ColumnFamily ColumnType="Super"
1283+ CompareWith="UTF8Type"
1284+ CompareSubcolumnsWith="UTF8Type"
1285+ Name="Super1"
1286+ Comment="A column family with supercolumns, whose column and subcolumn names are UTF8 strings"/>
1287+ </Keyspace>
1288+ <Keyspace Name="VotingAPI">
1289+ <KeysCachedFraction>0.2</KeysCachedFraction>
1290+ <ColumnFamily CompareWith="UTF8Type"
1291+ Name="VotesByEntity"
1292+ Comment="Votes grouped by entity." />
1293+ <ColumnFamily CompareWith="UTF8Type"
1294+ Name="VotesBySource"
1295+ Comment="Votes grouped by source." />
1296+ <ColumnFamily CompareWith="UTF8Type"
1297+ Name="AggregatedVotesByEntity"
1298+ Comment="Votes aggregated by entity." />
1299+ </Keyspace>
1300+ </Keyspaces>
1301+
1302+ <!--
1303+ ~ Partitioner: any IPartitioner may be used, including your own as long
1304+ ~ as it is on the classpath. Out of the box, Cassandra provides
1305+ ~ org.apache.cassandra.dht.RandomPartitioner,
1306+ ~ org.apache.cassandra.dht.OrderPreservingPartitioner, and
1307+ ~ org.apache.cassandra.dht.CollatingOrderPreservingPartitioner.
1308+ ~ (CollatingOPP colates according to EN,US rules, not naive byte
1309+ ~ ordering. Use this as an example if you need locale-aware collation.)
1310+ ~ Range queries require using an order-preserving partitioner.
1311+ ~
1312+ ~ Achtung! Changing this parameter requires wiping your data
1313+ ~ directories, since the partitioner can modify the sstable on-disk
1314+ ~ format.
1315+ -->
1316+ <Partitioner>org.apache.cassandra.dht.OrderPreservingPartitioner</Partitioner>
1317+
1318+ <!--
1319+ ~ If you are using an order-preserving partitioner and you know your key
1320+ ~ distribution, you can specify the token for this node to use. (Keys
1321+ ~ are sent to the node with the "closest" token, so distributing your
1322+ ~ tokens equally along the key distribution space will spread keys
1323+ ~ evenly across your cluster.) This setting is only checked the first
1324+ ~ time a node is started.
1325+
1326+ ~ This can also be useful with RandomPartitioner to force equal spacing
1327+ ~ of tokens around the hash space, especially for clusters with a small
1328+ ~ number of nodes.
1329+ -->
1330+ <InitialToken></InitialToken>
1331+
1332+ <!--
1333+ ~ EndPointSnitch: Setting this to the class that implements
1334+ ~ IEndPointSnitch which will see if two endpoints are in the same data
1335+ ~ center or on the same rack. Out of the box, Cassandra provides
1336+ ~ org.apache.cassandra.locator.EndPointSnitch
1337+ -->
1338+ <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
1339+
1340+ <!--
1341+ ~ Strategy: Setting this to the class that implements
1342+ ~ IReplicaPlacementStrategy will change the way the node picker works.
1343+ ~ Out of the box, Cassandra provides
1344+ ~ org.apache.cassandra.locator.RackUnawareStrategy and
1345+ ~ org.apache.cassandra.locator.RackAwareStrategy (place one replica in
1346+ ~ a different datacenter, and the others on different racks in the same
1347+ ~ one.)
1348+ -->
1349+ <ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
1350+
1351+ <!-- Number of replicas of the data -->
1352+ <ReplicationFactor>1</ReplicationFactor>
1353+
1354+ <!--
1355+ ~ Directories: Specify where Cassandra should store different data on
1356+ ~ disk. Keep the data disks and the CommitLog disks separate for best
1357+ ~ performance
1358+ -->
1359+ <CommitLogDirectory>/var/lib/cassandra/commitlog</CommitLogDirectory>
1360+ <DataFileDirectories>
1361+ <DataFileDirectory>/var/lib/cassandra/data</DataFileDirectory>
1362+ </DataFileDirectories>
1363+ <CalloutLocation>/var/lib/cassandra/callouts</CalloutLocation>
1364+ <StagingFileDirectory>/var/lib/cassandra/staging</StagingFileDirectory>
1365+
1366+
1367+ <!--
1368+ ~ Addresses of hosts that are deemed contact points. Cassandra nodes
1369+ ~ use this list of hosts to find each other and learn the topology of
1370+ ~ the ring. You must change this if you are running multiple nodes!
1371+ -->
1372+ <Seeds>
1373+ <Seed>127.0.0.1</Seed>
1374+ </Seeds>
1375+
1376+
1377+ <!-- Miscellaneous -->
1378+
1379+ <!-- Time to wait for a reply from other nodes before failing the command -->
1380+ <RpcTimeoutInMillis>5000</RpcTimeoutInMillis>
1381+ <!-- Size to allow commitlog to grow to before creating a new segment -->
1382+ <CommitLogRotationThresholdInMB>128</CommitLogRotationThresholdInMB>
1383+
1384+
1385+ <!-- Local hosts and ports -->
1386+
1387+ <!--
1388+ ~ Address to bind to and tell other nodes to connect to. You _must_
1389+ ~ change this if you want multiple nodes to be able to communicate!
1390+ ~
1391+ ~ Leaving it blank leaves it up to InetAddress.getLocalHost(). This
1392+ ~ will always do the Right Thing *if* the node is properly configured
1393+ ~ (hostname, name resolution, etc), and the Right Thing is to use the
1394+ ~ address associated with the hostname (it might not be).
1395+ -->
1396+ <ListenAddress>localhost</ListenAddress>
1397+ <!-- TCP port, for commands and data -->
1398+ <StoragePort>7000</StoragePort>
1399+ <!-- UDP port, for membership communications (gossip) -->
1400+ <ControlPort>7001</ControlPort>
1401+
1402+ <!--
1403+ ~ The address to bind the Thrift RPC service to. Unlike ListenAddress
1404+ ~ above, you *can* specify 0.0.0.0 here if you want Thrift to listen on
1405+ ~ all interfaces.
1406+ ~
1407+ ~ Leaving this blank has the same effect it does for ListenAddress,
1408+ ~ (i.e. it will be based on the configured hostname of the node).
1409+ -->
1410+ <ThriftAddress>localhost</ThriftAddress>
1411+ <!-- Thrift RPC port (the port clients connect to). -->
1412+ <ThriftPort>9160</ThriftPort>
1413+ <!--
1414+ ~ Whether or not to use a framed transport for Thrift. If this option
1415+ ~ is set to true then you must also use a framed transport on the
1416+ ~ client-side, (framed and non-framed transports are not compatible).
1417+ -->
1418+ <ThriftFramedTransport>false</ThriftFramedTransport>
1419+
1420+
1421+ <!--======================================================================-->
1422+ <!-- Memory, Disk, and Performance -->
1423+ <!--======================================================================-->
1424+
1425+ <!--
1426+ ~ Buffer size to use when performing contiguous column slices. Increase
1427+ ~ this to the size of the column slices you typically perform.
1428+ ~ (Name-based queries are performed with a buffer size of
1429+ ~ ColumnIndexSizeInKB.)
1430+ -->
1431+ <SlicedBufferSizeInKB>64</SlicedBufferSizeInKB>
1432+
1433+ <!--
1434+ ~ Buffer size to use when flushing memtables to disk. (Only one
1435+ ~ memtable is ever flushed at a time.) Increase (decrease) the index
1436+ ~ buffer size relative to the data buffer if you have few (many)
1437+ ~ columns per key. Bigger is only better _if_ your memtables get large
1438+ ~ enough to use the space. (Check in your data directory after your
1439+ ~ app has been running long enough.) -->
1440+ <FlushDataBufferSizeInMB>32</FlushDataBufferSizeInMB>
1441+ <FlushIndexBufferSizeInMB>8</FlushIndexBufferSizeInMB>
1442+
1443+ <!--
1444+ ~ Add column indexes to a row after its contents reach this size.
1445+ ~ Increase if your column values are large, or if you have a very large
1446+ ~ number of columns. The competing causes are, Cassandra has to
1447+ ~ deserialize this much of the row to read a single column, so you want
1448+ ~ it to be small - at least if you do many partial-row reads - but all
1449+ ~ the index data is read for each access, so you don't want to generate
1450+ ~ that wastefully either.
1451+ -->
1452+ <ColumnIndexSizeInKB>640</ColumnIndexSizeInKB>
1453+
1454+ <!--
1455+ ~ The maximum amount of data to store in memory per ColumnFamily before
1456+ ~ flushing to disk. Note: There is one memtable per column family, and
1457+ ~ this threshold is based solely on the amount of data stored, not
1458+ ~ actual heap memory usage (there is some overhead in indexing the
1459+ ~ columns).
1460+ -->
1461+ <MemtableSizeInMB>64</MemtableSizeInMB>
1462+ <!--
1463+ ~ The maximum number of columns in millions to store in memory per
1464+ ~ ColumnFamily before flushing to disk. This is also a per-memtable
1465+ ~ setting. Use with MemtableSizeInMB to tune memory usage.
1466+ -->
1467+ <MemtableObjectCountInMillions>0.1</MemtableObjectCountInMillions>
1468+ <!--
1469+ ~ The maximum time to leave a dirty memtable unflushed.
1470+ ~ (While any affected columnfamilies have unflushed data from a
1471+ ~ commit log segment, that segment cannot be deleted.)
1472+ ~ This needs to be large enough that it won't cause a flush storm
1473+ ~ of all your memtables flushing at once because none has hit
1474+ ~ the size or count thresholds yet. For production, a larger
1475+ ~ value such as 1440 is recommended.
1476+ -->
1477+ <MemtableFlushAfterMinutes>60</MemtableFlushAfterMinutes>
1478+
1479+ <!--
1480+ ~ Unlike most systems, in Cassandra writes are faster than reads, so
1481+ ~ you can afford more of those in parallel. A good rule of thumb is 2
1482+ ~ concurrent reads per processor core. Increase ConcurrentWrites to
1483+ ~ the number of clients writing at once if you enable CommitLogSync +
1484+ ~ CommitLogSyncDelay. -->
1485+ <ConcurrentReads>8</ConcurrentReads>
1486+ <ConcurrentWrites>32</ConcurrentWrites>
1487+
1488+ <!--
1489+ ~ CommitLogSync may be either "periodic" or "batch." When in batch
1490+ ~ mode, Cassandra won't ack writes until the commit log has been
1491+ ~ fsynced to disk. It will wait up to CommitLogSyncBatchWindowInMS
1492+ ~ milliseconds for other writes, before performing the sync.
1493+
1494+ ~ This is less necessary in Cassandra than in traditional databases
1495+ ~ since replication reduces the odds of losing data from a failure
1496+ ~ after writing the log entry but before it actually reaches the disk.
1497+ ~ So the other option is "timed," where writes may be acked immediately
1498+ ~ and the CommitLog is simply synced every CommitLogSyncPeriodInMS
1499+ ~ milliseconds.
1500+ -->
1501+ <CommitLogSync>periodic</CommitLogSync>
1502+ <!--
1503+ ~ Interval at which to perform syncs of the CommitLog in periodic mode.
1504+ ~ Usually the default of 10000ms is fine; increase it if your i/o
1505+ ~ load is such that syncs are taking excessively long times.
1506+ -->
1507+ <CommitLogSyncPeriodInMS>10000</CommitLogSyncPeriodInMS>
1508+ <!--
1509+ ~ Delay (in milliseconds) during which additional commit log entries
1510+ ~ may be written before fsync in batch mode. This will increase
1511+ ~ latency slightly, but can vastly improve throughput where there are
1512+ ~ many writers. Set to zero to disable (each entry will be synced
1513+ ~ individually). Reasonable values range from a minimal 0.1 to 10 or
1514+ ~ even more if throughput matters more than latency.
1515+ -->
1516+ <!-- <CommitLogSyncBatchWindowInMS>1</CommitLogSyncBatchWindowInMS> -->
1517+
1518+ <!--
1519+ ~ Time to wait before garbage-collection deletion markers. Set this to
1520+ ~ a large enough value that you are confident that the deletion marker
1521+ ~ will be propagated to all replicas by the time this many seconds has
1522+ ~ elapsed, even in the face of hardware failures. The default value is
1523+ ~ ten days.
1524+ -->
1525+ <GCGraceSeconds>864000</GCGraceSeconds>
1526+
1527+ <!--
1528+ ~ The threshold size in megabytes the binary memtable must grow to,
1529+ ~ before it's submitted for flushing to disk.
1530+ -->
1531+ <BinaryMemtableSizeInMB>256</BinaryMemtableSizeInMB>
1532+
1533+</Storage>
1534
1535=== added directory 'sites/all/modules/votingapi'
1536=== added file 'sites/all/modules/votingapi/API.txt'
1537--- sites/all/modules/votingapi/API.txt 1970-01-01 00:00:00 +0000
1538+++ sites/all/modules/votingapi/API.txt 2010-03-02 11:43:13 +0000
1539@@ -0,0 +1,142 @@
1540+$Id: API.txt,v 1.6.4.7 2009/06/21 01:29:33 eaton Exp $
1541+
1542+What's this, now?
1543+=================
1544+VotingAPI provides a flexible, easy-to-use framework for rating, voting, moderation, and consensus-gathering modules in Drupal. It allows module developers to focus on their ideas (say, providing a 'rate this thread' widget at the bottom of each forum post) without worrying about the grunt work of storing votes, preventing ballot-stuffing, calculating the results, and so on.
1545+
1546+VotingAPI handles three key things for module developers:
1547+
1548+1) CRUD
1549+Create/Retrieve/Update/Delete operations for voting data. The simplest modules only need to call two functions -- votingapi_set_vote() and votingapi_select_results() -- to use the API. Others can use finer-grain functions for complete control.
1550+
1551+2) Calculation
1552+Every time a user casts a vote, VotingAPI calculates the results and caches them for you. You can use the default calculations (like average, total, etc) or implement your own custom tallying functions.
1553+
1554+3) Display
1555+VotingAPI integrates with Views.module, allowing you to slice and dice your site's content based on user consensus. While some custom modules may need to implement thier own Views integration to provide customized displays, the vast majority can use the built-in system without any additional work.
1556+
1557+How Data Is Stored
1558+==================
1559+VotingAPI manages a raw 'pool' of vote data -- it doesn't keep track of any content directly. Instead, it lets modules store each vote with a 'content type' and 'content id', so that the same APIs can be used to rate nodes, comments, users, aggregator items, or even other votes (in a Slashdot-esque meta-moderation system). It can also be used by modules that need to store and calculate custom data like polling results -- using a custom content_type ensures that other modules won't trample on your module's voting data.
1560+
1561+For each discrete vote, the API stores the following information:
1562+
1563+content_type -- This *usually* corresponds to a type of Drupal content, like 'node' or 'comment' or 'user'.
1564+content_id -- The key ID of the content being rated.
1565+value -- This is the actual value of the vote that was cast by the user.
1566+value_type -- This determines how vote results are totaled. VotingAPI supports three value types by default: 'percent' votes are averaged, 'points' votes are summed up, and 'option' votes get a count of votes cast for each specific option. Modules can use other value_types, but must implement their own calculation functions to generate vote results -- more on that later.
1567+tag -- Modules can use different tags to store votes on specific aspects of a piece of content, like 'accuracy' and 'timeliness' of a news story. If you don't need to vote on multiple criteria, you should use the default value of 'vote'. If you use multiple tags, it is STRONGLY recommended that you provide an average or 'overall' value filed under the default 'vote' tag. This gives other modules that display voting data a single value to key on for sorting, etc.
1568+uid -- The user ID of the person who voted.
1569+timestamp -- The time the vote was cast.
1570+vote_source -- A unique identifier used to distinguish votes cast by anonymous users. By default, this is the IP address of the remote machine.
1571+
1572+Whenever a vote is cast, VotingAPI gathers up all the votes for the content_type/content_id combination, and creates a collection of cached 'result' records. Each voting result recorded stores the following information:
1573+
1574+content_type -- Just what you'd expect from the individual vote objects.
1575+content_id -- Ditto.
1576+value_type -- Ditto.
1577+tag -- Ditto.
1578+function -- The aggregate function that's been calculated -- for example, 'average', 'sum', and so on.
1579+value -- The value of the aggregate function.
1580+timestamp -- The time the results were calculated.
1581+
1582+
1583+Upgrading from VotingAPI 1.x
1584+============================
1585+Version 2.0 of VotingAPI offers several notable changes. Modules MUST be updated to work with VotingAPI 2.0, but changes for most modules should be minimal. Among other things, version 2.0 offers automatic support for anonymous votes -- something that required custom vote handling in version 1.x.
1586+
1587+1) Functions accept objects and arrays of objects instead of long parameter lists
1588+VotingAPI 1.x used relatively complex parameter lists in its most commonly used functions. In version 2.x, VotingAPI vote-casting functions accept a single vote object or array of vote objects, while vote and result retrieval functions accept a keyed array describing the filter criteria to be used.
1589+
1590+2) hook_votingapi_update() Removed
1591+This function allowed modules to intervene whenever a user changed their vote. The processing overhead that it imposed on most operations, however, was severe. No modules in contrib implemented the hook, and it has been eliminated. hook_votingapi_insert() and hook_votingapi_delete() are still available.
1592+
1593+3) Retrieval functions consolidated
1594+In VotingAPI 1.x, votes for a content object were retrieved using a dizzying array of functions, including the ugly buy often-used internal function, _votingapi_get_raw_votes(). In version 2.x, the following functions are provided:
1595+
1596+* votingapi_select_votes();
1597+* votingapi_select_results();
1598+* votingapi_select_single_vote_value()
1599+* votingapi_select_single_result_value();
1600+
1601+4) Custom result calculations must do their own SQL
1602+In version 1.x, VotingAPI loaded all votes for a given content object in order to calculate the average vote using PHP. Modules calculating their own results (median, etc.) were handed the stack of vote objects and given an opportunity to do more using hook_votingapi_calculate(). This was fine for simple cases, but consumed monstrous amounts of RAM whenever a single piece of content accumulated large numbers of votes.
1603+
1604+In version 2.x, VotingAPI modules may implement hook_votingapi_results_alter() instead. They receive the same information about the content object, and the stack of results to modify, but are responsible for using their own SQL to generate their results. Fortunately, most modules implementing custom results required complex calculations more efficiently done in SQL anyways.
1605+
1606+5) Views integration hooks have changed
1607+VotingAPI now supports any valid base table when exposing its data to Views. Modules that cast votes on non-node content can implement hook_votingapi_views_content_types() to let VotingAPI know what base tables should get the relationships.
1608+
1609+Because Views' internal data structures have changed, and the VotingAPI integration now supports additional base tables, the data handed off to hook_votingapi_views_formatters() has changed. See the reference at the bottom of this document for an example implementation.
1610+
1611+In the future, modules that need to offer highly customized ways of presenting VotingAPI data in a view are advised to use hook_views_data_alter() to simply add a new custom field to the VotingAPI table definition. That will give full control over display and filtering, but will take advantage of the flexible, base-table-agnostic join handlers VotingAPI provides.
1612+
1613+
1614+An example custom calculation
1615+=============================
1616+The following function adds a standard_deviation result to the calculated result data.
1617+Note that in previous versions of VotingAPI, this function received in-memory copies of
1618+each and every cast vote to avoid the need for custom SQL. This turned out to be very,
1619+very, very inefficient -- the slowdown of possibly running multiple aggregate queries is
1620+far outweighed by the memory savings of each module handling its own queries. After all,
1621+MySQL calculates standard deviation far faster than you can in PHP.
1622+
1623+function mymodule_votingapi_results_alter(&$results, $content_type, $content_id) {
1624+ // We're using a MySQLism (STDDEV isn't ANSI SQL), but it's OK because this is
1625+ // an example. And no one would ever base real code on sample code. Ever. Never.
1626+ $sql = "SELECT v.tag, STDDEV(v.value) as standard_deviation ";
1627+ $sql .= "FROM {votingapi_vote} v ";
1628+ $sql .= "WHERE v.content_type = '%s' AND v.content_id = %d AND v.value_type = 'percent' ";
1629+ $sql .= "GROUP BY v.tag";
1630+
1631+ $results = db_query($sql, $content_type, $content_id);
1632+
1633+ // VotingAPI wants the data in the following format:
1634+ // $cache[$tag][$value_type][$math_function] = $value;
1635+ while ($result = db_fetch_array($results)) {
1636+ $cache[$result['tag']]['percent']['standard_deviation'] = $result['standard_deviation'];
1637+ }
1638+}
1639+
1640+An example of advanced Views integration
1641+========================================
1642+VotingAPI provides Views Relationship connections for votes cast on nodes and comments. If your module casts other types of votes that should be made available via Views, it needs to implement hook_votingapi_relationships().
1643+
1644+
1645+function mymodule_votingapi_relationships() {
1646+ $relationships[] = array(
1647+ // 'description' is used to construct the field description in the Views UI.
1648+ 'description' => t('nodes'),
1649+ // 'content_type' contain the value that your module stores in the voting
1650+ // api 'content_type' column. 'node', 'comment', etc.
1651+ 'content_type' => 'node',
1652+ // 'base_table' contain the name of the Views base table that stores the
1653+ // data your votes apply to.
1654+ 'base_table' => 'node',
1655+ // 'content_id_column' contains the name of the views field that represents
1656+ // your base_table's primary key. This column will be joined against the
1657+ // voting api 'content_id' column.
1658+ 'content_id_column' => 'nid',
1659+ // VotingAPI constructs pseudo-tables so that multiple relationships can
1660+ // point to the same base table (normal and translation-based votes nodes
1661+ // for example. These two columns allow you to override the names of the
1662+ // pseudo-tables. You probably don't need to change this part unless you're
1663+ // nedjo.
1664+ 'pseudo_vote' => 'votingapi_vote',
1665+ 'pseudo_cache' => 'votingapi_cache',
1666+ );
1667+ return $relationships;
1668+}
1669+
1670+An example of a custom Views value formatter
1671+============================================
1672+VotingAPI's Views integration can present vote results as simple numbers, but most users will want to see something a bit snazzier. By implementing hook_votingapi_views_formatters(), you can expose a custom formatter for any given VotingAPI view field. For the View field that's passed in, your hook should return an array of key => value pairs, where the key is a the name of a callback function that will format the values from the database.
1673+
1674+function mymodule_votingapi_views_formatters($field) {
1675+ if ($field->field == 'value') {
1676+ return array('mymodule_funky_formatter' => t('MyModule value formatter'));
1677+ }
1678+ if ($field->field == 'tag') {
1679+ return array('mymodule_funky_tags' => t('MyModule tag formatter'));
1680+ }
1681+}
1682
1683=== added file 'sites/all/modules/votingapi/CHANGELOG.txt'
1684--- sites/all/modules/votingapi/CHANGELOG.txt 1970-01-01 00:00:00 +0000
1685+++ sites/all/modules/votingapi/CHANGELOG.txt 2010-03-02 11:43:13 +0000
1686@@ -0,0 +1,71 @@
1687+// $Id: CHANGELOG.txt,v 1.4.4.9 2009/08/15 17:59:54 eaton Exp $
1688+
1689+02/11/06
1690+--------
1691+First stab at Views integration with VotingAPI. Configuring it is a little ugly -- mainly useful for custom modules that want to expose a vote-based default_view without coding all the table/field/filter stuff for Views.
1692+
1693+04/21/06
1694+--------
1695+Expose public votingapi_add_vote and votingapi_delete_vote functions to allow modules to override normal behavior. This makes it possible to implement anonymous voting, though future explicit support is coming.
1696+
1697+04/22/06
1698+--------
1699+There is now a working infrastructure for modules to expose their own 'voting actions' without requiring user customization.
1700+
1701+04/23/06
1702+--------
1703+Major enhancements to the actions subsystem, the addition of an experimental widgets library, and the (beginnings) of major enhancements to views integration.
1704+Using the latest CVS version of Views.module, it's now possible to display a particular vote result (say, the number of people who've voted) without using esoteric combinations of filters.
1705+
1706+04/29/06
1707+--------
1708+A new setting has been introduced -- vote result calculation can be deferred until cron-time. This can be useful on heavy-load sites where MANY votes are being cast and complex, db-intensive calculations are used to weight the results.
1709+
1710+05/01/06
1711+--------
1712+The action loading and caching infrastructure has been made much more robust, and CRUD functions are in place for reading and writing actions. voting_actions.module has now been added to facilitate direct user manipulation of action criteria, etc. It's the part that's still heavily under development. In many installations, it'll be unecessary.
1713+The votingapi_delete_vote() function no longer automatically recalculates. Modules using it should call recalculate manually after explicitly removing a vote.
1714+Those needing a consistent, stable VotingAPI should probably still stick with the 4.7 branch. This version will be branched as soon as the issues are ironed out.
1715+
1716+06/07/06
1717+--------
1718+Foundation laid for a move to discrete hooks rather than a single _votingapi function. Because each of the $ops for the function needed a wildly different set of parameters, implementing a single function was an exercise in frustration for module developers. Right now, each location that called *_votingapi() ALSO calls hook_votingapi_*(). the raw *_votingapi() function will be depricated, and likely removed from a future version.
1719+
1720+07/05/07
1721+--------
1722+First run at a Drupal 6 compatible version (after an ill-fated accidental checkin on the D5 branch). Once this works, work will start on v2.0. The plan is to add anonymous voting support and cleaner function signatures by passing in full vote objects rather than long param lists. It's uncertain whether 1.x will be released for Drupal 6 directly, or whether I'll just get the 2.0 version out for it, since modules will already need to be updated.
1723+
1724+04/09/08
1725+--------
1726+Several important bug fixes for the D6 version of VotingAPI. #223517, #232233, #232236, and #235174 fixed thanks to patches posted by quicksketch and sammys. Also checked in an initial stub file for views2 integration, removing the old hook_init() code since Views2 handles pulling the .inc files in itself.
1727+
1728+06/05/08
1729+--------
1730+Views integration rewritten from scratch - many old limitations and snafus are now gone, thankfully. Several default views will be included in the final release.
1731+
1732+Calculation of vote results no longer loads all votes into memory: modules that implement custom result algorithms must do their own SQL. This saves tremendous amounts of RAM on sites with large numbers of votes on a single content object.
1733+
1734+06/24/08
1735+--------
1736+Views integration code seriously reworked (again) to use Views 2 'relationships' rather than custom field handlers. Downside: for the VotingAPI fields, sorts, and filters to appear, a relationship must first be added to it. Upside: using hook_votingapi_views_content_types(), modules that cast votes on non-node content can tell VotingAPI to add its relationship to other tables too (like users, comments, etc). This should make it much, much easier to build displays of vote-aware content that lives in non-node base tables.
1737+
1738+09/19/08
1739+--------
1740+Updated for compatibility with Views RC2, added the ability to filter Views relationships to votes cast by the current user, and added three default views to demonstrate the Views integration. Also added basic Simpletests, fixed a bug that manifested when modules cast votes with minimal information (nothing but a content id and a value, for example). Finally, added the ability to auto-generate dummy votes for testing purposes. This feature is only visible if Devel Generate module is installed and activated.
1741+
1742+06/20/09
1743+--------
1744+Changed hook_votingapi_content_types() to hook_votingapi_relationships(), since it only deals with Views relationships. Added extra documentation to API.txt and fixed assorted ugly bugs. Moved a bunch of API docs to votingapi.api.php so API.module can generate pretty docs for the hooks. Nodes and comments now have pre-configured relationships available, any other entity types need a hook_votingapi_relationships() implementation.
1745+
1746+Added votingapi_metadata() to gather metadata about defined value_types, tags, and aggregate functions. Modified the views relationship handler to use that function rather than querying the tables directly.
1747+
1748+07/20/09
1749+--------
1750+#480036 by j_ten_man - allow votingapi relationships to be chained off of other relationships (like node references)
1751+#496454 - PHPDoc incorrectly listed 'uid' and 'vote_source' as valid filtering criteria on cached aggregate results.
1752+Added an 'Other' option for Relationships filters, to allow nonstandard tags, filters, and so on to be used in VotingAPI relationships with minimal hassle.
1753+
1754+08/15/09
1755+--------
1756+#527970 - Fixing issues with the 'Other' option when creating relationship filters. Thanks to te-brian for spotting it and whipping up a patch.
1757+#541236 - Add support for user ratings to the default set of relationships. Patch by Likeless.
1758\ No newline at end of file
1759
1760=== added file 'sites/all/modules/votingapi/LICENSE.txt'
1761--- sites/all/modules/votingapi/LICENSE.txt 1970-01-01 00:00:00 +0000
1762+++ sites/all/modules/votingapi/LICENSE.txt 2010-03-02 11:43:13 +0000
1763@@ -0,0 +1,274 @@
1764+GNU GENERAL PUBLIC LICENSE
1765+
1766+ Version 2, June 1991
1767+
1768+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
1769+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
1770+verbatim copies of this license document, but changing it is not allowed.
1771+
1772+ Preamble
1773+
1774+The licenses for most software are designed to take away your freedom to
1775+share and change it. By contrast, the GNU General Public License is
1776+intended to guarantee your freedom to share and change free software--to
1777+make sure the software is free for all its users. This General Public License
1778+applies to most of the Free Software Foundation's software and to any other
1779+program whose authors commit to using it. (Some other Free Software
1780+Foundation software is covered by the GNU Library General Public License
1781+instead.) You can apply it to your programs, too.
1782+
1783+When we speak of free software, we are referring to freedom, not price. Our
1784+General Public Licenses are designed to make sure that you have the
1785+freedom to distribute copies of free software (and charge for this service if
1786+you wish), that you receive source code or can get it if you want it, that you
1787+can change the software or use pieces of it in new free programs; and that
1788+you know you can do these things.
1789+
1790+To protect your rights, we need to make restrictions that forbid anyone to
1791+deny you these rights or to ask you to surrender the rights. These restrictions
1792+translate to certain responsibilities for you if you distribute copies of the
1793+software, or if you modify it.
1794+
1795+For example, if you distribute copies of such a program, whether gratis or for
1796+a fee, you must give the recipients all the rights that you have. You must make
1797+sure that they, too, receive or can get the source code. And you must show
1798+them these terms so they know their rights.
1799+
1800+We protect your rights with two steps: (1) copyright the software, and (2)
1801+offer you this license which gives you legal permission to copy, distribute
1802+and/or modify the software.
1803+
1804+Also, for each author's protection and ours, we want to make certain that
1805+everyone understands that there is no warranty for this free software. If the
1806+software is modified by someone else and passed on, we want its recipients
1807+to know that what they have is not the original, so that any problems
1808+introduced by others will not reflect on the original authors' reputations.
1809+
1810+Finally, any free program is threatened constantly by software patents. We
1811+wish to avoid the danger that redistributors of a free program will individually
1812+obtain patent licenses, in effect making the program proprietary. To prevent
1813+this, we have made it clear that any patent must be licensed for everyone's
1814+free use or not licensed at all.
1815+
1816+The precise terms and conditions for copying, distribution and modification
1817+follow.
1818+
1819+ GNU GENERAL PUBLIC LICENSE
1820+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
1821+ MODIFICATION
1822+
1823+0. This License applies to any program or other work which contains a notice
1824+placed by the copyright holder saying it may be distributed under the terms
1825+of this General Public License. The "Program", below, refers to any such
1826+program or work, and a "work based on the Program" means either the
1827+Program or any derivative work under copyright law: that is to say, a work
1828+containing the Program or a portion of it, either verbatim or with
1829+modifications and/or translated into another language. (Hereinafter, translation
1830+is included without limitation in the term "modification".) Each licensee is
1831+addressed as "you".
1832+
1833+Activities other than copying, distribution and modification are not covered
1834+by this License; they are outside its scope. The act of running the Program is
1835+not restricted, and the output from the Program is covered only if its contents
1836+constitute a work based on the Program (independent of having been made
1837+by running the Program). Whether that is true depends on what the Program
1838+does.
1839+
1840+1. You may copy and distribute verbatim copies of the Program's source
1841+code as you receive it, in any medium, provided that you conspicuously and
1842+appropriately publish on each copy an appropriate copyright notice and
1843+disclaimer of warranty; keep intact all the notices that refer to this License
1844+and to the absence of any warranty; and give any other recipients of the
1845+Program a copy of this License along with the Program.
1846+
1847+You may charge a fee for the physical act of transferring a copy, and you
1848+may at your option offer warranty protection in exchange for a fee.
1849+
1850+2. You may modify your copy or copies of the Program or any portion of it,
1851+thus forming a work based on the Program, and copy and distribute such
1852+modifications or work under the terms of Section 1 above, provided that you
1853+also meet all of these conditions:
1854+
1855+a) You must cause the modified files to carry prominent notices stating that
1856+you changed the files and the date of any change.
1857+
1858+b) You must cause any work that you distribute or publish, that in whole or in
1859+part contains or is derived from the Program or any part thereof, to be
1860+licensed as a whole at no charge to all third parties under the terms of this
1861+License.
1862+
1863+c) If the modified program normally reads commands interactively when run,
1864+you must cause it, when started running for such interactive use in the most
1865+ordinary way, to print or display an announcement including an appropriate
1866+copyright notice and a notice that there is no warranty (or else, saying that
1867+you provide a warranty) and that users may redistribute the program under
1868+these conditions, and telling the user how to view a copy of this License.
1869+(Exception: if the Program itself is interactive but does not normally print such
1870+an announcement, your work based on the Program is not required to print
1871+an announcement.)
1872+
1873+These requirements apply to the modified work as a whole. If identifiable
1874+sections of that work are not derived from the Program, and can be
1875+reasonably considered independent and separate works in themselves, then
1876+this License, and its terms, do not apply to those sections when you distribute
1877+them as separate works. But when you distribute the same sections as part
1878+of a whole which is a work based on the Program, the distribution of the
1879+whole must be on the terms of this License, whose permissions for other
1880+licensees extend to the entire whole, and thus to each and every part
1881+regardless of who wrote it.
1882+
1883+Thus, it is not the intent of this section to claim rights or contest your rights to
1884+work written entirely by you; rather, the intent is to exercise the right to
1885+control the distribution of derivative or collective works based on the
1886+Program.
1887+
1888+In addition, mere aggregation of another work not based on the Program
1889+with the Program (or with a work based on the Program) on a volume of a
1890+storage or distribution medium does not bring the other work under the scope
1891+of this License.
1892+
1893+3. You may copy and distribute the Program (or a work based on it, under
1894+Section 2) in object code or executable form under the terms of Sections 1
1895+and 2 above provided that you also do one of the following:
1896+
1897+a) Accompany it with the complete corresponding machine-readable source
1898+code, which must be distributed under the terms of Sections 1 and 2 above
1899+on a medium customarily used for software interchange; or,
1900+
1901+b) Accompany it with a written offer, valid for at least three years, to give
1902+any third party, for a charge no more than your cost of physically performing
1903+source distribution, a complete machine-readable copy of the corresponding
1904+source code, to be distributed under the terms of Sections 1 and 2 above on
1905+a medium customarily used for software interchange; or,
1906+
1907+c) Accompany it with the information you received as to the offer to distribute
1908+corresponding source code. (This alternative is allowed only for
1909+noncommercial distribution and only if you received the program in object
1910+code or executable form with such an offer, in accord with Subsection b
1911+above.)
1912+
1913+The source code for a work means the preferred form of the work for
1914+making modifications to it. For an executable work, complete source code
1915+means all the source code for all modules it contains, plus any associated
1916+interface definition files, plus the scripts used to control compilation and
1917+installation of the executable. However, as a special exception, the source
1918+code distributed need not include anything that is normally distributed (in
1919+either source or binary form) with the major components (compiler, kernel,
1920+and so on) of the operating system on which the executable runs, unless that
1921+component itself accompanies the executable.
1922+
1923+If distribution of executable or object code is made by offering access to
1924+copy from a designated place, then offering equivalent access to copy the
1925+source code from the same place counts as distribution of the source code,
1926+even though third parties are not compelled to copy the source along with the
1927+object code.
1928+
1929+4. You may not copy, modify, sublicense, or distribute the Program except as
1930+expressly provided under this License. Any attempt otherwise to copy,
1931+modify, sublicense or distribute the Program is void, and will automatically
1932+terminate your rights under this License. However, parties who have received
1933+copies, or rights, from you under this License will not have their licenses
1934+terminated so long as such parties remain in full compliance.
1935+
1936+5. You are not required to accept this License, since you have not signed it.
1937+However, nothing else grants you permission to modify or distribute the
1938+Program or its derivative works. These actions are prohibited by law if you
1939+do not accept this License. Therefore, by modifying or distributing the
1940+Program (or any work based on the Program), you indicate your acceptance
1941+of this License to do so, and all its terms and conditions for copying,
1942+distributing or modifying the Program or works based on it.
1943+
1944+6. Each time you redistribute the Program (or any work based on the
1945+Program), the recipient automatically receives a license from the original
1946+licensor to copy, distribute or modify the Program subject to these terms and
1947+conditions. You may not impose any further restrictions on the recipients'
1948+exercise of the rights granted herein. You are not responsible for enforcing
1949+compliance by third parties to this License.
1950+
1951+7. If, as a consequence of a court judgment or allegation of patent
1952+infringement or for any other reason (not limited to patent issues), conditions
1953+are imposed on you (whether by court order, agreement or otherwise) that
1954+contradict the conditions of this License, they do not excuse you from the
1955+conditions of this License. If you cannot distribute so as to satisfy
1956+simultaneously your obligations under this License and any other pertinent
1957+obligations, then as a consequence you may not distribute the Program at all.
1958+For example, if a patent license would not permit royalty-free redistribution
1959+of the Program by all those who receive copies directly or indirectly through
1960+you, then the only way you could satisfy both it and this License would be to
1961+refrain entirely from distribution of the Program.
1962+
1963+If any portion of this section is held invalid or unenforceable under any
1964+particular circumstance, the balance of the section is intended to apply and
1965+the section as a whole is intended to apply in other circumstances.
1966+
1967+It is not the purpose of this section to induce you to infringe any patents or
1968+other property right claims or to contest validity of any such claims; this
1969+section has the sole purpose of protecting the integrity of the free software
1970+distribution system, which is implemented by public license practices. Many
1971+people have made generous contributions to the wide range of software
1972+distributed through that system in reliance on consistent application of that
1973+system; it is up to the author/donor to decide if he or she is willing to
1974+distribute software through any other system and a licensee cannot impose
1975+that choice.
1976+
1977+This section is intended to make thoroughly clear what is believed to be a
1978+consequence of the rest of this License.
1979+
1980+8. If the distribution and/or use of the Program is restricted in certain
1981+countries either by patents or by copyrighted interfaces, the original copyright
1982+holder who places the Program under this License may add an explicit
1983+geographical distribution limitation excluding those countries, so that
1984+distribution is permitted only in or among countries not thus excluded. In such
1985+case, this License incorporates the limitation as if written in the body of this
1986+License.
1987+
1988+9. The Free Software Foundation may publish revised and/or new versions
1989+of the General Public License from time to time. Such new versions will be
1990+similar in spirit to the present version, but may differ in detail to address new
1991+problems or concerns.
1992+
1993+Each version is given a distinguishing version number. If the Program specifies
1994+a version number of this License which applies to it and "any later version",
1995+you have the option of following the terms and conditions either of that
1996+version or of any later version published by the Free Software Foundation. If
1997+the Program does not specify a version number of this License, you may
1998+choose any version ever published by the Free Software Foundation.
1999+
2000+10. If you wish to incorporate parts of the Program into other free programs
2001+whose distribution conditions are different, write to the author to ask for
2002+permission. For software which is copyrighted by the Free Software
2003+Foundation, write to the Free Software Foundation; we sometimes make
2004+exceptions for this. Our decision will be guided by the two goals of
2005+preserving the free status of all derivatives of our free software and of
2006+promoting the sharing and reuse of software generally.
2007+
2008+ NO WARRANTY
2009+
2010+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
2011+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
2012+PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
2013+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
2014+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
2015+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
2016+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2017+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2018+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
2019+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
2020+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
2021+NECESSARY SERVICING, REPAIR OR CORRECTION.
2022+
2023+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
2024+AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
2025+ANY OTHER PARTY WHO MAY MODIFY AND/OR
2026+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
2027+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
2028+SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
2029+ARISING OUT OF THE USE OR INABILITY TO USE THE
2030+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
2031+OR DATA BEING RENDERED INACCURATE OR LOSSES
2032+SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
2033+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
2034+IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
2035+THE POSSIBILITY OF SUCH DAMAGES.
2036+
2037+ END OF TERMS AND CONDITIONS
2038
2039=== added file 'sites/all/modules/votingapi/README.txt'
2040--- sites/all/modules/votingapi/README.txt 1970-01-01 00:00:00 +0000
2041+++ sites/all/modules/votingapi/README.txt 2010-03-02 11:43:13 +0000
2042@@ -0,0 +1,21 @@
2043+The Basics
2044+==========
2045+VotingAPI is a framework for content voting and rating systems in Drupal. It does not directly provide any voting 'features' to users -- instead, it offers a consistent API for other module developers to build their voting and rating systems on top of. If you're an end user who just wants to rate nodes, check out some of the modules that use VotingAPI's framework:
2046+
2047+Fivestar
2048+UserReview
2049+Vote-Up-Down
2050+LoveHate
2051+Node Moderation
2052+
2053+... and more.
2054+
2055+If you're a developer who wants to build a voting or rating related module, check out API.txt in the VotingAPI directory.
2056+
2057+Installation
2058+============
2059+Installing VotingAPI is pretty straightforward. Just copy the VotingAPI directory into your web site's /modules directory. Then, activate the module by visiting http://www.example.com/admin/modules, where example.com is the URL of your web site.
2060+
2061+Requirements
2062+============
2063+VotingAPI requires Drupal 4.7 or later.
2064
2065=== added directory 'sites/all/modules/votingapi/tests'
2066=== added file 'sites/all/modules/votingapi/tests/votingapi.test'
2067--- sites/all/modules/votingapi/tests/votingapi.test 1970-01-01 00:00:00 +0000
2068+++ sites/all/modules/votingapi/tests/votingapi.test 2010-03-02 11:43:13 +0000
2069@@ -0,0 +1,185 @@
2070+<?php
2071+// $Id: votingapi.test,v 1.1.2.3 2009/06/24 19:16:05 eaton Exp $
2072+
2073+/**
2074+ * @file
2075+ * Test file for VotingAPI module.
2076+ */
2077+
2078+class VotingAPITestCase extends DrupalWebTestCase {
2079+ public static function getInfo() {
2080+ return array(
2081+ 'name' => t('Voting API'),
2082+ 'description' => t('Voting API'),
2083+ 'group' => t('Voting API Tests'),
2084+ );
2085+ }
2086+
2087+ function setUp() {
2088+ parent::setUp('votingapi');
2089+ // Create a new page
2090+ $node = new stdClass();
2091+ $node->title = '';
2092+ $node->teaser = t('Teaser text');
2093+ $node->body = t('Here is the body of the page');
2094+ $node->uid = 1;
2095+ $node->type = 'page';
2096+ $node->status = 1;
2097+ $node->promote = 0;
2098+ node_save($node);
2099+ variable_set('votingapi_nid1', $node->nid);
2100+ $node->title = t('Node @id', array('@id' => $node->nid));
2101+ node_save($node);
2102+ }
2103+
2104+ function tearDown() {
2105+ $nid = variable_get('votingapi_nid1', NULL);
2106+ if ($nid) {
2107+ node_delete($nid);
2108+ variable_del('votingapi_nid1');
2109+ }
2110+ parent::tearDown();
2111+ }
2112+
2113+ /**
2114+ * Ensure that the optional fields are truly optional.
2115+ */
2116+ function testMinimalAdd() {
2117+ $nid = variable_get('votingapi_nid1', NULL);
2118+ $value = '85';
2119+ // The minimum required fields according to the documentation are
2120+ // content_id and value.
2121+ $vote = array(
2122+ 'content_id' => $nid,
2123+ 'value' => $value
2124+ );
2125+ try {
2126+ $result = votingapi_add_votes($vote);
2127+ // Test that the result has its fields set appropriately.
2128+ $this->validateVote('testMinimalAdd()', $result, $nid, array($value));
2129+ $this->assertTrue(time() - $result[0]['timestamp'] < 2, t('The timestamp should be less than 2 seconds ago.'));
2130+ }
2131+ catch (Exception $e) {
2132+ $this->fail(t('Could not add a vote with only content_id and value.'));
2133+ return;
2134+ }
2135+ }
2136+
2137+ /**
2138+ * Add a vote and ensure that the vote was stored properly.
2139+ */
2140+ function testAddVote() {
2141+ global $user;
2142+ $value = '7';
2143+ $nid = variable_get('votingapi_nid1', NULL);
2144+ $vote = array('content_id' => $nid,
2145+ 'value' => $value,
2146+ 'content_type' => 'node');
2147+ try {
2148+ $result = votingapi_add_votes($vote);
2149+ // Test that the result has its fields set appropriately.
2150+ $this->validateVote("testAddVote()", $result, $nid, $value, 'node', $user->uid);
2151+ $this->assertTrue(time() - $result[0]['timestamp'] < 2, t('The timestamp should be less than 2 seconds ago.'));
2152+ }
2153+ catch (Exception $e) {
2154+ $this->fail('The votingapi_add_votes threw an error during the "votingapi_add_votes" call.');
2155+ return;
2156+ }
2157+ // Load the vote back in and verify it matches.
2158+ $vote_results = votingapi_recalculate_results('node', $nid);
2159+ $this->validateVoteCounts('testAddVote()', $vote_results, $nid, array($value));
2160+ }
2161+
2162+ /**
2163+ * Add multiple votes and ensure that the vote calculations are working.
2164+ */
2165+ function testAddMultipleVotes() {
2166+ $users = array();
2167+ $users[] = $this->drupalCreateUser();
2168+ $users[] = $this->drupalCreateUser();
2169+ $users[] = $this->drupalCreateUser();
2170+ $nid = variable_get('votingapi_nid1', NULL);
2171+ $values = array(72, 13, 27);
2172+ $votes = array();
2173+ try {
2174+ for ($index = 0; $index < count($values); $index++) {
2175+ $votes[$index] = array();
2176+ $votes[$index]['content_type'] = 'node';
2177+ $votes[$index]['content_id'] = $nid;
2178+ $votes[$index]['uid'] = $users[$index]->uid;
2179+ $votes[$index]['value'] = $values[$index];
2180+ }
2181+ $result = votingapi_add_votes($votes);
2182+ // Test that the result has its fields set appropriately.
2183+ $this->validateVote("testAddMultipleVotes()", $result, $nid, $values);
2184+ }
2185+ catch (Exception $e) {
2186+ $this->fail('The votingapi_add_votes threw an error during the "votingapi_add_votes" call.');
2187+ return;
2188+ }
2189+ // Load the vote back in and verify it matches.
2190+ $vote_results = votingapi_recalculate_results('node', $nid);
2191+ $this->validateVoteCounts('testAddVote()', $vote_results, $nid, $values);
2192+ }
2193+
2194+ function validateVote($prefix, $vote, $content_id, $value, $content_type = 'node',
2195+ $uid = NULL, $value_type = 'percent', $tag = 'vote', $vote_source = NULL) {
2196+ global $user;
2197+ if ($vote_source == NULL) {
2198+ $vote_source = ip_address();
2199+ }
2200+ $prefix_array = array('@prefix' => $prefix);
2201+ for ($index = 0; $index < count($vote); $index++) {
2202+ $this->assertTrue($vote[$index]['content_id'] == $content_id, t('@prefix: content_id should match.', $prefix_array));
2203+ $this->assertTrue($vote[$index]['value'] == $value[$index], t('@prefix: value should match.', $prefix_array));
2204+ $this->assertTrue($vote[$index]['content_type'] == $content_type, t('@prefix: content_type should match, default = "node".', $prefix_array));
2205+ $this->assertTrue($vote[$index]['value_type'] == $value_type, t('@prefix: value_type should match, default= "percent".', $prefix_array));
2206+ $this->assertTrue($vote[$index]['tag'] == $tag, t('@prefix: tag should match, default = "vote".', $prefix_array));
2207+ $this->assertTrue($vote[$index]['vote_source'] == $vote_source, t('@prefix: vote_source should match, default = ip address.', $prefix_array));
2208+ if ($uid != NULL) {
2209+ $this->assertTrue($vote[0]['uid'] == $uid, t('@prefix: uid should match.', $prefix_array));
2210+ }
2211+ }
2212+ }
2213+
2214+ function validateVoteCounts($prefix, $votes, $content_id, $values, $content_type = 'node',
2215+ $value_type = 'percent', $tag = 'vote') {
2216+ $count_summary = 0;
2217+ $average_summary = 1;
2218+ $count = 0.0;
2219+ $sum = 0.0;
2220+ foreach ($values as $value) {
2221+ $sum += $value;
2222+ $count++;
2223+ }
2224+ $average = $sum / $count;
2225+ $prefix_array = array('@prefix' => $prefix);
2226+ foreach ($votes as $summary) {
2227+ if ($summary['function'] == 'count') {
2228+ $summary_desc = 'count summary';
2229+ $prefix_array['@summary_desc'] = $summary_desc;
2230+ $this->assertTrue($summary['value'] == $count,
2231+ t('@prefix: (@summary_desc) value should match the number of values.', $prefix_array));
2232+ }
2233+ elseif ($summary['function'] == 'average') {
2234+ $summary_desc = 'average summary';
2235+ $prefix_array['@summary_desc'] = $summary_desc;
2236+ $this->assertTrue($summary['value'] == $average,
2237+ t('@prefix: (@summary_desc) value should match the average of values', $prefix_array));
2238+ }
2239+ else {
2240+ $prefix_array['@summary'] = $summary['function'];
2241+ $prefix_array['@summary_desc'] = $summary['function'] . ' summary';
2242+ $this->assertFalse(TRUE, t('@prefix: Unknown summary type @summary.', $prefix_array));
2243+ }
2244+ $this->assertTrue($summary['content_type'] == $content_type,
2245+ t('@prefix: (@summary_desc) content_type should match, default = "node"', $prefix_array));
2246+ $this->assertTrue($summary['content_id'] == $content_id,
2247+ t('@prefix: (@summary_desc) content_id should match', $prefix_array));
2248+ $this->assertTrue($summary['value_type'] == $value_type,
2249+ t('@prefix: (@summary_desc) value_type should match.', $prefix_array));
2250+ $this->assertTrue($summary['tag'] == $tag,
2251+ t('@prefix: (@summary_desc) tag should match', $prefix_array));
2252+ }
2253+ }
2254+}
2255
2256=== added directory 'sites/all/modules/votingapi/translations'
2257=== added file 'sites/all/modules/votingapi/translations/fr.po'
2258--- sites/all/modules/votingapi/translations/fr.po 1970-01-01 00:00:00 +0000
2259+++ sites/all/modules/votingapi/translations/fr.po 2010-03-02 11:43:13 +0000
2260@@ -0,0 +1,275 @@
2261+# $Id: fr.po,v 1.1.2.1 2009/07/07 17:19:10 slybud Exp $
2262+#
2263+# LANGUAGE translation of Drupal (general)
2264+# Copyright YEAR NAME <EMAIL@ADDRESS>
2265+# Generated from files:
2266+# votingapi.admin.inc,v 1.1.2.2 2008/10/01 15:47:46 eaton
2267+# votingapi.module,v 1.46.2.15 2008/10/09 21:12:35 eaton
2268+# votingapi.info,v 1.4 2007/07/06 03:02:34 eaton
2269+# votingapi.views.inc,v 1.1.2.11 2008/12/04 18:34:39 eaton
2270+# votingapi_views_handler_relationship.inc,v 1.1.2.3 2008/10/01 15:47:47 eaton
2271+# votingapi_views_handler_field_value.inc,v 1.1.2.2 2008/10/01 15:47:47 eaton
2272+#
2273+msgid ""
2274+msgstr ""
2275+"Project-Id-Version: french translation for drupal6 votingapi module\n"
2276+"POT-Creation-Date: 2009-07-01 14:52+0200\n"
2277+"PO-Revision-Date: 2009-07-07 19:13+0100\n"
2278+"Last-Translator: Sylvain Moreau <sylvain.moreau@ows.fr>\n"
2279+"Language-Team: Pierre Ternon, OWS/Reviewed by Sylvain Moreau, OWS <pierre@ows.fr>\n"
2280+"MIME-Version: 1.0\n"
2281+"Content-Type: text/plain; charset=utf-8\n"
2282+"Content-Transfer-Encoding: 8bit\n"
2283+"Plural-Forms: nplurals=2; plural=(n>1);\n"
2284+"X-Poedit-Language: French\n"
2285+
2286+#: votingapi.admin.inc:13
2287+msgid "Immediately"
2288+msgstr "Immédiatement"
2289+
2290+#: votingapi.admin.inc:13
2291+msgid "Never"
2292+msgstr "Jamais"
2293+
2294+#: votingapi.admin.inc:17
2295+msgid "Anonymous vote rollover"
2296+msgstr "Vote anonyme multiple"
2297+
2298+#: votingapi.admin.inc:18
2299+msgid "The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to 'never' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes."
2300+msgstr "Durée à partir de laquelle deux votes anonymes effectués depuis un même ordinateur sont pris en compte séparément. Sélectionnez \"Jamais\" afin d'éliminer la plupart des cas de votes en doublon d'une même personne. Cependant, plusieurs anonymes ne pourront alors pas voter depuis un même ordinateur (par exemple les clients d'un cyber café)."
2301+
2302+#: votingapi.admin.inc:25
2303+msgid "Vote tallying"
2304+msgstr "Décompte des votes"
2305+
2306+#: votingapi.admin.inc:26
2307+msgid "On high-traffic sites, administrators can use this setting to postpone the calculation of vote results."
2308+msgstr "Pour des sites à fort trafic, les administrateurs peuvent utiliser ce paramétrage pour différer le calcul des résultats."
2309+
2310+#: votingapi.admin.inc:29
2311+msgid "Tally results whenever a vote is cast"
2312+msgstr "Calcul des résultats à chaque vote"
2313+
2314+#: votingapi.admin.inc:30
2315+msgid "Tally results at cron-time"
2316+msgstr "Calcul des résultats déclenché par tâche planifiée (cron)"
2317+
2318+#: votingapi.admin.inc:31
2319+msgid "Do not tally results automatically: I am using a module that manages its own vote results."
2320+msgstr "Ne pas lancer de calcul automatiquement : j'utilise un module calculant lui-même ses résultats des votes."
2321+
2322+#: votingapi.admin.inc:49
2323+msgid "Which node types should receive votes?"
2324+msgstr "Sur quel type de noeuds voter ?"
2325+
2326+#: votingapi.admin.inc:56
2327+msgid "What type of votes should be generated?"
2328+msgstr "Quel type de vote doit être généré ?"
2329+
2330+#: votingapi.admin.inc:58
2331+msgid "Fivestar style"
2332+msgstr "Type \"Fivestar\""
2333+
2334+#: votingapi.admin.inc:59
2335+msgid "Digg style"
2336+msgstr "Type \"Digg\""
2337+
2338+#: votingapi.admin.inc:60
2339+msgid "Reddit style"
2340+msgstr "Type \"Reddit\""
2341+
2342+#: votingapi.admin.inc:67
2343+msgid "Delete existing votes before generating new ones."
2344+msgstr "Supprimer les votes existants avant d'en générer de nouveaux."
2345+
2346+#: votingapi.admin.inc:72
2347+msgid "Do it!"
2348+msgstr "Générer les votes !"
2349+
2350+#: votingapi.module:48
2351+msgid "administer voting api"
2352+msgstr "administrer voting api"
2353+
2354+#: votingapi.module:20
2355+#: votingapi.info:0
2356+msgid "Voting API"
2357+msgstr "Voting API"
2358+
2359+#: votingapi.module:21
2360+msgid "Global settings for the Voting API."
2361+msgstr "Paramètres globaux de Voting API"
2362+
2363+#: votingapi.module:32
2364+msgid "Generate votes"
2365+msgstr "Générer des votes"
2366+
2367+#: votingapi.module:33
2368+msgid "Generate a given number of votes on site content. Optionally delete existing votes."
2369+msgstr "Génère un nombre défini de votes sur le contenu du site. Supprime en option les votes existants."
2370+
2371+#: votingapi.module:0
2372+msgid "votingapi"
2373+msgstr "votingapi"
2374+
2375+#: votingapi.info:0
2376+msgid "Provides a shared voting API for other modules."
2377+msgstr "Fournit une API de votes partagée pour les autres modules."
2378+
2379+#: votingapi.info:0
2380+msgid "Voting"
2381+msgstr "Voting"
2382+
2383+#: views/votingapi.views.inc:43
2384+msgid "Voting API votes"
2385+msgstr "Votes (Voting API)"
2386+
2387+#: views/votingapi.views.inc:44
2388+msgid "Voting API results"
2389+msgstr "Résultats (Voting API)"
2390+
2391+#: views/votingapi.views.inc:51;171
2392+msgid "Value"
2393+msgstr "Valeur"
2394+
2395+#: views/votingapi.views.inc:52;172
2396+msgid "The value of an individual cast vote."
2397+msgstr "La valeur d'un vote individuel."
2398+
2399+#: views/votingapi.views.inc:69;188
2400+#: views/votingapi_views_handler_relationship.inc:62
2401+msgid "Value type"
2402+msgstr "Type de valeur"
2403+
2404+#: views/votingapi.views.inc:70
2405+msgid "The nature of the vote being cast (points, percentage, etc)."
2406+msgstr "La nature du vote en cours (points, pourcentages, etc.)"
2407+
2408+#: views/votingapi.views.inc:86;205
2409+msgid "Tag"
2410+msgstr "Etiquette"
2411+
2412+#: views/votingapi.views.inc:87
2413+msgid "An optional tag to group multi-criteria votes."
2414+msgstr "Une étiquette optionnelle destinée à grouper les votes multi-critères."
2415+
2416+#: views/votingapi.views.inc:103
2417+msgid "User"
2418+msgstr "Utilisateur"
2419+
2420+#: views/votingapi.views.inc:104
2421+msgid "The user who cast the vote."
2422+msgstr "L'utilisateur qui effectue le vote."
2423+
2424+#: views/votingapi.views.inc:123
2425+msgid "Current user"
2426+msgstr "Utilisateur courant"
2427+
2428+#: views/votingapi.views.inc:124
2429+msgid "Restrict votes to those cast by the current user."
2430+msgstr "Restreindre aux votes de l'utilisateur courant."
2431+
2432+#: views/votingapi.views.inc:133
2433+msgid "IP Address"
2434+msgstr "Adresse IP"
2435+
2436+#: views/votingapi.views.inc:134
2437+msgid "The IP address of the user who cast the vote."
2438+msgstr "L'adresse IP de l'utilisateur ayant voté."
2439+
2440+#: views/votingapi.views.inc:151;241
2441+msgid "Timestamp"
2442+msgstr "Horodatage"
2443+
2444+#: views/votingapi.views.inc:152
2445+msgid "The time the vote was cast."
2446+msgstr "La date et l'heure du vote."
2447+
2448+#: views/votingapi.views.inc:189
2449+msgid "The nature of the results in question (points, percentage, etc)."
2450+msgstr "La nature des résultats de la question (points, pourcentage, etc)."
2451+
2452+#: views/votingapi.views.inc:206
2453+msgid "An optional tag to group multi-criteria results."
2454+msgstr "Une étiquette optionnelle destinée à grouper les résultats multi-critères."
2455+
2456+#: views/votingapi.views.inc:223
2457+msgid "Function"
2458+msgstr "Fonction"
2459+
2460+#: views/votingapi.views.inc:224
2461+msgid "The aggregate function used to calculate the result."
2462+msgstr "La fonction d'agrégation utilisée pour calculer les résultats."
2463+
2464+#: views/votingapi.views.inc:242
2465+msgid "The time the results were calculated."
2466+msgstr "La date et l'heure de calcul des résultats."
2467+
2468+#: views/votingapi.views.inc:266
2469+msgid "Percent"
2470+msgstr "Pourcent"
2471+
2472+#: views/votingapi.views.inc:267
2473+msgid "Points"
2474+msgstr "Points"
2475+
2476+#: views/votingapi.views.inc:273;288;305
2477+msgid "No filtering"
2478+msgstr "Aucun filtre"
2479+
2480+#: views/votingapi.views.inc:282
2481+msgid "Default vote"
2482+msgstr "Vote par défaut"
2483+
2484+#: views/votingapi.views.inc:297
2485+msgid "Sum"
2486+msgstr "Somme"
2487+
2488+#: views/votingapi.views.inc:298
2489+msgid "Count"
2490+msgstr "Décompte"
2491+
2492+#: views/votingapi.views.inc:299
2493+msgid "Average"
2494+msgstr "Moyenne"
2495+
2496+#: views/votingapi.views.inc:322
2497+msgid "nodes"
2498+msgstr "noeuds"
2499+
2500+#: views/votingapi.views.inc:403
2501+msgid "Display %display defines a VotingAPI vote relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view."
2502+msgstr "L'affichage %display définit une relation avec les votes Voting API, mais ne restreint pas les données liées. Cela peut entraîner des doublons dans la vue résultante."
2503+
2504+#: views/votingapi.views.inc:410
2505+msgid "Display %display defines a VotingAPI results relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view."
2506+msgstr "L'affichage %display définit une relation avec les résultats Voting API, mais ne restreint pas les données liées. Cela peut entraîner des doublons dans la vue résultante."
2507+
2508+#: views/votingapi_views_handler_field_value.inc:20
2509+msgid "Default appearance"
2510+msgstr "Apparence par défaut"
2511+
2512+#: views/votingapi_views_handler_field_value.inc:26
2513+msgid "Appearance"
2514+msgstr "Apparence"
2515+
2516+#: views/votingapi_views_handler_relationship.inc:57
2517+msgid "Data filters"
2518+msgstr "Filtres de données"
2519+
2520+#: views/votingapi_views_handler_relationship.inc:58
2521+msgid "For each piece of content, many pieces of voting data may be saved. Use these options to specify exactly which types should be available via this relationship. <strong>Warning!</strong> Leaving any of these filters empty may result in multiple copies of each piece of content being displayed in listings."
2522+msgstr "Pour chaque type de contenu, plusieurs types de données \"votes\" peuvent être enregistrés. Cette option permet de spécifier précisément quels types doivent être disponibles par cette relation. <strong>Attention !</strong> Ne pas sélectionner un des filtres ci-dessous peut occasionner des doublons pour chaque contenu dans les résultats de la vue."
2523+
2524+#: views/votingapi_views_handler_relationship.inc:68
2525+msgid "Vote tag"
2526+msgstr "Etiquette de vote"
2527+
2528+#: views/votingapi_views_handler_relationship.inc:76
2529+msgid "Aggregation function"
2530+msgstr "Fonction d'agrégation"
2531+
2532+#: views/votingapi_views_handler_relationship.inc:84
2533+msgid "Restrict to current user"
2534+msgstr "Restreindre à l'utilisateur courant"
2535+
2536
2537=== added file 'sites/all/modules/votingapi/translations/ja.po'
2538--- sites/all/modules/votingapi/translations/ja.po 1970-01-01 00:00:00 +0000
2539+++ sites/all/modules/votingapi/translations/ja.po 2010-03-02 11:43:13 +0000
2540@@ -0,0 +1,156 @@
2541+# $Id: ja.po,v 1.1.2.1 2007/11/17 20:58:37 hass Exp $
2542+# -----------------------------------------------------------------------------
2543+# Japanese translation of Drupal (votingapi.module)
2544+#
2545+# Copyright (c) 2006-2007 Drupal Japan ( http://drupal.jp/ ) /
2546+# Drupal Nippon ( http://drupon.org/ ) /
2547+# Takafumi ( jp.drupal@imagine **reverse order**)
2548+#
2549+# Generated from file:
2550+# votingapi.module,v 1.40.2.5 2007/03/19 10:37:09 eaton
2551+# votingapi.info,v 1.2 2006/10/14 02:01:43 eaton
2552+# votingapi_views.inc,v 1.11.2.5 2007/03/22 12:50:30 eaton
2553+#
2554+# -----------------------------------------------------------------------------
2555+msgid ""
2556+msgstr ""
2557+"POT-Creation-Date: 2007-04-06 20:55+0900\n"
2558+"Last-Translator: Takafumi <jp.drupal@imagine **reverse order**>\n"
2559+"Language-Team: Drupal Japan / Drupal Nippon\n"
2560+"MIME-Version: 1.0\n"
2561+"Content-Type: text/plain; charset=UTF-8\n"
2562+"Content-Transfer-Encoding: 8bit\n"
2563+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
2564+
2565+#: votingapi.module:16
2566+msgid "Global settings for the Voting API."
2567+msgstr "Voting APIの全般的な設定を行います。"
2568+
2569+#: votingapi.module:39
2570+msgid "Vote tallying"
2571+msgstr "投票の集計"
2572+
2573+#: votingapi.module:40
2574+msgid "On high-traffic sites, administrators can use this setting to postpone the calculation of vote results."
2575+msgstr "投票結果の集計をするタイミングを指定してください。 アクセス数の多いサイトでは、この設定を使用して投票結果の計算を遅延させることができます。"
2576+
2577+#: votingapi.module:43
2578+msgid "Tally results whenever a vote is cast"
2579+msgstr "票が投じられた場合は常に結果を集計"
2580+
2581+#: votingapi.module:44
2582+msgid "Tally results at cron-time"
2583+msgstr "cronタスク実行時に結果を集計"
2584+
2585+#: votingapi.module:45
2586+msgid "Never tally votes: I am using a custom module to control vote results"
2587+msgstr "投票集計をしない: 投票結果のコントロールは他のモジュールを使用する"
2588+
2589+#: votingapi.module:33
2590+msgid "administer voting api"
2591+msgstr "VotingAPIの管理"
2592+
2593+#: votingapi.info:0
2594+msgid "Provides a shared voting API for other modules."
2595+msgstr "他のモジュールに共有される投票用APIを提供します。"
2596+
2597+#: votingapi.info:0
2598+msgid "Voting"
2599+msgstr "投票"
2600+
2601+#: votingapi.info:0
2602+msgid "votingapi"
2603+msgstr "votingapi"
2604+
2605+#: votingapi_views.inc:32;74
2606+msgid "VotingAPI !type !tag value"
2607+msgstr "VotingAPI: !type !tag value"
2608+
2609+#: votingapi_views.inc:39
2610+msgid "VotingAPI !type !tag value (current user only)"
2611+msgstr "VotingAPI: !type !tag value (現ユーザのみ)"
2612+
2613+#: votingapi_views.inc:49;70;81
2614+msgid "VotingAPI !type !tag timestamp"
2615+msgstr "VotingAPI: !type !tag timestamp"
2616+
2617+#: votingapi_views.inc:53
2618+msgid "As Short Date"
2619+msgstr "日付 (S)"
2620+
2621+#: votingapi_views.inc:54
2622+msgid "As Medium Date"
2623+msgstr "日付 (M)"
2624+
2625+#: votingapi_views.inc:55
2626+msgid "As Long Date"
2627+msgstr "日付 (L)"
2628+
2629+#: votingapi_views.inc:56
2630+msgid "As Time Ago"
2631+msgstr "経過時間"
2632+
2633+#: votingapi_views.inc:62;90
2634+msgid "VotingAPI !type !tag user"
2635+msgstr "VotingAPI: !type !tag user"
2636+
2637+#: votingapi_views.inc:68
2638+msgid "VotingAPI !type !tag vote"
2639+msgstr "VotingAPI: !type !tag vote"
2640+
2641+#: votingapi_views.inc:69
2642+msgid "VotingAPI !type !tag voter"
2643+msgstr "VotingAPI: !type !tag voter"
2644+
2645+#: votingapi_views.inc:78
2646+msgid "Filter nodes by values of the individual votes users cast for them."
2647+msgstr "ユーザが投じた個々の票の値によってノードを絞り込みます。"
2648+
2649+#: votingapi_views.inc:86
2650+msgid "Filter nodes by the date they were voted on."
2651+msgstr "投票された日付によってノードを絞り込みます。"
2652+
2653+#: votingapi_views.inc:91
2654+msgid "Has been voted on by"
2655+msgstr "投票済みの"
2656+
2657+#: votingapi_views.inc:91
2658+msgid "Has not been voted on by"
2659+msgstr "未投票の"
2660+
2661+#: votingapi_views.inc:92
2662+msgid "Currently Logged In User"
2663+msgstr "現在ログインしているユーザ"
2664+
2665+#: votingapi_views.inc:92
2666+msgid "Any user"
2667+msgstr "すべてのユーザ"
2668+
2669+#: votingapi_views.inc:95
2670+msgid "Filter nodes by whether the currently logged in user has voted."
2671+msgstr "ユーザが投票済みかどうかでノードを絞り込みます。"
2672+
2673+#: votingapi_views.inc:137;146;150
2674+msgid "VotingAPI !type !tag result (!function)"
2675+msgstr "VotingAPI: !type !tag result (!function)"
2676+
2677+#: votingapi_views.inc:154
2678+msgid "Filter nodes by the aggregate results of votes cast."
2679+msgstr "投票総数の集計結果によってノードを絞り込みます。"
2680+
2681+#: votingapi_views.inc:216
2682+msgid "Raw value"
2683+msgstr "そのままの値"
2684+
2685+#: votingapi_views.inc:217
2686+msgid "Cleaned version"
2687+msgstr "書式化された値"
2688+
2689+#: votingapi_views.inc:237
2690+msgid "points"
2691+msgstr "ポイント"
2692+
2693+#: votingapi.module:15 votingapi.info:0
2694+msgid "Voting API"
2695+msgstr "Voting API"
2696+
2697
2698=== added file 'sites/all/modules/votingapi/translations/nl.po'
2699--- sites/all/modules/votingapi/translations/nl.po 1970-01-01 00:00:00 +0000
2700+++ sites/all/modules/votingapi/translations/nl.po 2010-03-02 11:43:13 +0000
2701@@ -0,0 +1,274 @@
2702+# $Id: nl.po,v 1.1.2.1 2009/06/06 15:42:46 libeco Exp $
2703+#
2704+# Dutch translation of Drupal (general)
2705+# Copyright YEAR NAME <EMAIL@ADDRESS>
2706+# Generated from files:
2707+# votingapi.admin.inc,v 1.1.2.2 2008/10/01 15:47:46 eaton
2708+# votingapi.module,v 1.46.2.15 2008/10/09 21:12:35 eaton
2709+# votingapi.info,v 1.4 2007/07/06 03:02:34 eaton
2710+# votingapi.views.inc,v 1.1.2.11 2008/12/04 18:34:39 eaton
2711+# votingapi_views_handler_relationship.inc,v 1.1.2.3 2008/10/01 15:47:47 eaton
2712+# votingapi_views_handler_field_value.inc,v 1.1.2.2 2008/10/01 15:47:47 eaton
2713+#
2714+msgid ""
2715+msgstr ""
2716+"Project-Id-Version: PROJECT VERSION\n"
2717+"POT-Creation-Date: 2009-06-04 16:11+0200\n"
2718+"PO-Revision-Date: 2009-06-04 16:28+0100\n"
2719+"Last-Translator: L.B. Cohn <lichai@999games.nl>\n"
2720+"Language-Team: Dutch <EMAIL@ADDRESS>\n"
2721+"MIME-Version: 1.0\n"
2722+"Content-Type: text/plain; charset=utf-8\n"
2723+"Content-Transfer-Encoding: 8bit\n"
2724+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
2725+
2726+#: votingapi.admin.inc:13
2727+msgid "Immediately"
2728+msgstr "Meteen"
2729+
2730+#: votingapi.admin.inc:13
2731+msgid "Never"
2732+msgstr "Nooit"
2733+
2734+#: votingapi.admin.inc:17
2735+msgid "Anonymous vote rollover"
2736+msgstr "Anonieme stem-rollover"
2737+
2738+#: votingapi.admin.inc:18
2739+msgid "The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to 'never' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes."
2740+msgstr "De tijd die moet verstrijken voordat twee anonieme stemmen van dezelfde computer als uniek worden beschouwd. Dit op 'nooit' instellen zal de meeste dubbele stemmen elimineren, maar zal meerdere anonieme stemmen op dezelfde computer (bijvoorbeeld in een internetcafé) niet toestaan."
2741+
2742+#: votingapi.admin.inc:25
2743+msgid "Vote tallying"
2744+msgstr "Stembalans opmaken"
2745+
2746+#: votingapi.admin.inc:26
2747+msgid "On high-traffic sites, administrators can use this setting to postpone the calculation of vote results."
2748+msgstr "Op drukbezochte sites kunnen beheerders deze instelling gebruiken om het berekenen van de resultaten uit te stellen."
2749+
2750+#: votingapi.admin.inc:29
2751+msgid "Tally results whenever a vote is cast"
2752+msgstr "Maak de balans op nadat een stem is uitgebracht"
2753+
2754+#: votingapi.admin.inc:30
2755+msgid "Tally results at cron-time"
2756+msgstr "Maak de balans op bij een cron run"
2757+
2758+#: votingapi.admin.inc:31
2759+msgid "Do not tally results automatically: I am using a module that manages its own vote results."
2760+msgstr "Maak geen automatische balans op, ik gebruik een module die zijn eigen resultaten bijhoudt."
2761+
2762+#: votingapi.admin.inc:49
2763+msgid "Which node types should receive votes?"
2764+msgstr "Welke nodetypes moeten stemmen ontvangen?"
2765+
2766+#: votingapi.admin.inc:56
2767+msgid "What type of votes should be generated?"
2768+msgstr "Welk type stemmen moet worden gegenereerd?"
2769+
2770+#: votingapi.admin.inc:58
2771+msgid "Fivestar style"
2772+msgstr "Vijfsterstijl"
2773+
2774+#: votingapi.admin.inc:59
2775+msgid "Digg style"
2776+msgstr "Diggstijl"
2777+
2778+#: votingapi.admin.inc:60
2779+msgid "Reddit style"
2780+msgstr "Redditstijl"
2781+
2782+#: votingapi.admin.inc:67
2783+msgid "Delete existing votes before generating new ones."
2784+msgstr "Verwijder bestaande stemmen voor je nieuwe maakt."
2785+
2786+#: votingapi.admin.inc:72
2787+msgid "Do it!"
2788+msgstr "Doe het!"
2789+
2790+#: votingapi.module:48
2791+msgid "administer voting api"
2792+msgstr "beheer stemmen api"
2793+
2794+#: votingapi.module:20
2795+#: votingapi.info:0
2796+msgid "Voting API"
2797+msgstr "Stemmen API"
2798+
2799+#: votingapi.module:21
2800+msgid "Global settings for the Voting API."
2801+msgstr "Globale instellingen voor de Stemmen API."
2802+
2803+#: votingapi.module:32
2804+msgid "Generate votes"
2805+msgstr "Genereer stemmen"
2806+
2807+#: votingapi.module:33
2808+msgid "Generate a given number of votes on site content. Optionally delete existing votes."
2809+msgstr "Genereer een aantal stemmen op site-inhoud. Optioneel kun je bestaande stemmen verwijderen."
2810+
2811+#: votingapi.module:0
2812+msgid "votingapi"
2813+msgstr "stemmenapi"
2814+
2815+#: votingapi.info:0
2816+msgid "Provides a shared voting API for other modules."
2817+msgstr "Levert een gedeelde stemmen API voor andere modules"
2818+
2819+#: votingapi.info:0
2820+msgid "Voting"
2821+msgstr "Stemmen"
2822+
2823+#: views/votingapi.views.inc:43
2824+msgid "Voting API votes"
2825+msgstr "Stemmen API stemmen"
2826+
2827+#: views/votingapi.views.inc:44
2828+msgid "Voting API results"
2829+msgstr "Stemmen API resultaten"
2830+
2831+#: views/votingapi.views.inc:51;171
2832+msgid "Value"
2833+msgstr "Waarde"
2834+
2835+#: views/votingapi.views.inc:52;172
2836+msgid "The value of an individual cast vote."
2837+msgstr "De waarde van een individuele uitgebrachte stem."
2838+
2839+#: views/votingapi.views.inc:69;188
2840+#: views/votingapi_views_handler_relationship.inc:62
2841+msgid "Value type"
2842+msgstr "Waardetype"
2843+
2844+#: views/votingapi.views.inc:70
2845+msgid "The nature of the vote being cast (points, percentage, etc)."
2846+msgstr "Het type stem dat werd uitgebracht (punten, percentage, etc.)."
2847+
2848+#: views/votingapi.views.inc:86;205
2849+msgid "Tag"
2850+msgstr "Tag"
2851+
2852+#: views/votingapi.views.inc:87
2853+msgid "An optional tag to group multi-criteria votes."
2854+msgstr "Een optionele tag om multi-criteria stemmen te groeperen."
2855+
2856+#: views/votingapi.views.inc:103
2857+msgid "User"
2858+msgstr "Gebruiker"
2859+
2860+#: views/votingapi.views.inc:104
2861+msgid "The user who cast the vote."
2862+msgstr "De gebruiker die de stem uitbracht."
2863+
2864+#: views/votingapi.views.inc:123
2865+msgid "Current user"
2866+msgstr "Huidige gebruiker"
2867+
2868+#: views/votingapi.views.inc:124
2869+msgid "Restrict votes to those cast by the current user."
2870+msgstr "Beperk stemmen tot diegene uitgebracht door de huidige gebruiker."
2871+
2872+#: views/votingapi.views.inc:133
2873+msgid "IP Address"
2874+msgstr "IP-adres"
2875+
2876+#: views/votingapi.views.inc:134
2877+msgid "The IP address of the user who cast the vote."
2878+msgstr "Het IP-adres van de gebruiker die de stem uitbracht."
2879+
2880+#: views/votingapi.views.inc:151;241
2881+msgid "Timestamp"
2882+msgstr "Tijdstip"
2883+
2884+#: views/votingapi.views.inc:152
2885+msgid "The time the vote was cast."
2886+msgstr "De tijd dat een stem werd uitgebracht."
2887+
2888+#: views/votingapi.views.inc:189
2889+msgid "The nature of the results in question (points, percentage, etc)."
2890+msgstr "Het type resultaat (punten, percentage, etc.)."
2891+
2892+#: views/votingapi.views.inc:206
2893+msgid "An optional tag to group multi-criteria results."
2894+msgstr "Een optionele tag om multi-criteria resultaten te groeperen."
2895+
2896+#: views/votingapi.views.inc:223
2897+msgid "Function"
2898+msgstr "Functie"
2899+
2900+#: views/votingapi.views.inc:224
2901+msgid "The aggregate function used to calculate the result."
2902+msgstr "De aggregatiefunctie die wordt gebruikt om de resultaten te berekenen."
2903+
2904+#: views/votingapi.views.inc:242
2905+msgid "The time the results were calculated."
2906+msgstr "De tijd dat resultaten werden berekend."
2907+
2908+#: views/votingapi.views.inc:266
2909+msgid "Percent"
2910+msgstr "Procent"
2911+
2912+#: views/votingapi.views.inc:267
2913+msgid "Points"
2914+msgstr "Punten"
2915+
2916+#: views/votingapi.views.inc:273;288;305
2917+msgid "No filtering"
2918+msgstr "Geen filtering"
2919+
2920+#: views/votingapi.views.inc:282
2921+msgid "Default vote"
2922+msgstr "Standaard stem"
2923+
2924+#: views/votingapi.views.inc:297
2925+msgid "Sum"
2926+msgstr "Som"
2927+
2928+#: views/votingapi.views.inc:298
2929+msgid "Count"
2930+msgstr "Aantal"
2931+
2932+#: views/votingapi.views.inc:299
2933+msgid "Average"
2934+msgstr "Gemiddelde"
2935+
2936+#: views/votingapi.views.inc:322
2937+msgid "nodes"
2938+msgstr "nodes"
2939+
2940+#: views/votingapi.views.inc:403
2941+msgid "Display %display defines a VotingAPI vote relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view."
2942+msgstr "Weergave %display levert een Stemmen API stemmenrelatie, maar beperkt de relatiedata niet. Dit kan resulteren in gedupliceerde resultaten."
2943+
2944+#: views/votingapi.views.inc:410
2945+msgid "Display %display defines a VotingAPI results relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view."
2946+msgstr "Weergave %display levert een Stemmen API resultatenrelatie, maar beperkt de relatiedata niet. Dit kan resulteren in gedupliceerde resultaten."
2947+
2948+#: views/votingapi_views_handler_field_value.inc:20
2949+msgid "Default appearance"
2950+msgstr "Standaard verschijning"
2951+
2952+#: views/votingapi_views_handler_field_value.inc:26
2953+msgid "Appearance"
2954+msgstr "Verschijning"
2955+
2956+#: views/votingapi_views_handler_relationship.inc:57
2957+msgid "Data filters"
2958+msgstr "Datafilters"
2959+
2960+#: views/votingapi_views_handler_relationship.inc:58
2961+msgid "For each piece of content, many pieces of voting data may be saved. Use these options to specify exactly which types should be available via this relationship. <strong>Warning!</strong> Leaving any of these filters empty may result in multiple copies of each piece of content being displayed in listings."
2962+msgstr "Voor elk stuk inhoud kunnen veel stukken stemdata worden opgeslagen. Gebruik deze opties om aan te geven welke types beschikbaar moeten zijn via deze relatie. <strong>Waarschuwing!</strong> één van deze filters leeglaten kan resulteren in het weergeven van meerdere kopieën van elk stuk inhoud in lijsten."
2963+
2964+#: views/votingapi_views_handler_relationship.inc:68
2965+msgid "Vote tag"
2966+msgstr "Stemtag"
2967+
2968+#: views/votingapi_views_handler_relationship.inc:76
2969+msgid "Aggregation function"
2970+msgstr "Aggregatiefunctie"
2971+
2972+#: views/votingapi_views_handler_relationship.inc:84
2973+msgid "Restrict to current user"
2974+msgstr "Beperk tot huidige gebruiker"
2975+
2976
2977=== added file 'sites/all/modules/votingapi/translations/uk-ua.po'
2978--- sites/all/modules/votingapi/translations/uk-ua.po 1970-01-01 00:00:00 +0000
2979+++ sites/all/modules/votingapi/translations/uk-ua.po 2010-03-02 11:43:13 +0000
2980@@ -0,0 +1,121 @@
2981+# $Id: uk-ua.po,v 1.1.4.2 2009/05/21 12:05:33 podarok Exp $
2982+#
2983+# Ukrainian translation of Drupal (general)
2984+# Copyright YEAR NAME <EMAIL@ADDRESS>
2985+# Generated from files:
2986+# votingapi.admin.inc,v 1.1.2.2 2008/10/01 15:47:46 eaton
2987+# votingapi.module,v 1.46.2.15 2008/10/09 21:12:35 eaton
2988+# votingapi.info,v 1.4 2007/07/06 03:02:34 eaton
2989+#
2990+msgid ""
2991+msgstr ""
2992+"Project-Id-Version: votingapi uk Ukrainian Translations\n"
2993+"POT-Creation-Date: 2009-05-21 14:44+0300\n"
2994+"PO-Revision-Date: 2009-05-21 15:05+0300\n"
2995+"Last-Translator: Andriy Podanenko <podarok@ua.fm>\n"
2996+"Language-Team: uk <podarok@ua.fm>\n"
2997+"MIME-Version: 1.0\n"
2998+"Content-Type: text/plain; charset=utf-8\n"
2999+"Content-Transfer-Encoding: 8bit\n"
3000+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
3001+"X-Poedit-Language: Ukrainian\n"
3002+"X-Poedit-Country: UKRAINE\n"
3003+"X-Poedit-SourceCharset: utf-8\n"
3004+
3005+#: votingapi.admin.inc:13
3006+msgid "Immediately"
3007+msgstr "Миттєво"
3008+
3009+#: votingapi.admin.inc:13
3010+msgid "Never"
3011+msgstr "Ніколи"
3012+
3013+#: votingapi.admin.inc:17
3014+msgid "Anonymous vote rollover"
3015+msgstr "Повторювання анонімних голосувань"
3016+
3017+#: votingapi.admin.inc:18
3018+msgid "The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to 'never' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes."
3019+msgstr "Кількість часу, що мине перед тим як два анонімних голоси з одного комп'ютера рахуватимуться різними. Вказування \"ніколи\" дозвроятиме накрутки, але надасть можливість багатьом анонімним користувачам на одному комп'ютері (приклад, інтернет каже) участвувати в голосуваннях"
3020+
3021+#: votingapi.admin.inc:25
3022+msgid "Vote tallying"
3023+msgstr "Підрахунок голосів"
3024+
3025+#: votingapi.admin.inc:26
3026+msgid "On high-traffic sites, administrators can use this setting to postpone the calculation of vote results."
3027+msgstr "На високонавантажених сайтах, адміністратори можуть використати ці параметри для відкладеного обрахунку результатів голосувань"
3028+
3029+#: votingapi.admin.inc:29
3030+msgid "Tally results whenever a vote is cast"
3031+msgstr "Підрахунок результатів під час додавання голосу"
3032+
3033+#: votingapi.admin.inc:30
3034+msgid "Tally results at cron-time"
3035+msgstr "Підрахунок голосів cron-ом"
3036+
3037+#: votingapi.admin.inc:31
3038+msgid "Do not tally results automatically: I am using a module that manages its own vote results."
3039+msgstr "Не рахувати голоси автоматично: Я використовую модуль, що керує сам результатами голосувань"
3040+
3041+#: votingapi.admin.inc:49
3042+msgid "Which node types should receive votes?"
3043+msgstr "Які типи матеріалів можуть отримувати голоси?"
3044+
3045+#: votingapi.admin.inc:56
3046+msgid "What type of votes should be generated?"
3047+msgstr "Які типи голосів повинні бути генеровано?"
3048+
3049+#: votingapi.admin.inc:58
3050+msgid "Fivestar style"
3051+msgstr "Стиль Fivestar"
3052+
3053+#: votingapi.admin.inc:59
3054+msgid "Digg style"
3055+msgstr "Стиль Digg"
3056+
3057+#: votingapi.admin.inc:60
3058+msgid "Reddit style"
3059+msgstr "Стиль Reddit"
3060+
3061+#: votingapi.admin.inc:67
3062+msgid "Delete existing votes before generating new ones."
3063+msgstr "Видалення існуючих голосів перед генерацією нових"
3064+
3065+#: votingapi.admin.inc:72
3066+msgid "Do it!"
3067+msgstr "Зробити!"
3068+
3069+#: votingapi.module:48
3070+msgid "administer voting api"
3071+msgstr "керування API голосувань"
3072+
3073+#: votingapi.module:20
3074+#: votingapi.info:0
3075+msgid "Voting API"
3076+msgstr "API голосувань"
3077+
3078+#: votingapi.module:21
3079+msgid "Global settings for the Voting API."
3080+msgstr "Глобальні параметри API голосувань"
3081+
3082+#: votingapi.module:32
3083+msgid "Generate votes"
3084+msgstr "Генерація голосів"
3085+
3086+#: votingapi.module:33
3087+msgid "Generate a given number of votes on site content. Optionally delete existing votes."
3088+msgstr "Генерувати вказану кількість голосів на матеріалі сайту. Опційно видаляє існуючі голоси"
3089+
3090+#: votingapi.module:0
3091+msgid "votingapi"
3092+msgstr "votingapi"
3093+
3094+#: votingapi.info:0
3095+msgid "Provides a shared voting API for other modules."
3096+msgstr "Надає доступ до API голосувань для інших модулів"
3097+
3098+#: votingapi.info:0
3099+msgid "Voting"
3100+msgstr "Голосування"
3101+
3102
3103=== added file 'sites/all/modules/votingapi/translations/uk.po'
3104--- sites/all/modules/votingapi/translations/uk.po 1970-01-01 00:00:00 +0000
3105+++ sites/all/modules/votingapi/translations/uk.po 2010-03-02 11:43:13 +0000
3106@@ -0,0 +1,121 @@
3107+# $Id: uk.po,v 1.1.4.2 2009/05/21 12:05:33 podarok Exp $
3108+#
3109+# Ukrainian translation of Drupal (general)
3110+# Copyright YEAR NAME <EMAIL@ADDRESS>
3111+# Generated from files:
3112+# votingapi.admin.inc,v 1.1.2.2 2008/10/01 15:47:46 eaton
3113+# votingapi.module,v 1.46.2.15 2008/10/09 21:12:35 eaton
3114+# votingapi.info,v 1.4 2007/07/06 03:02:34 eaton
3115+#
3116+msgid ""
3117+msgstr ""
3118+"Project-Id-Version: votingapi uk Ukrainian Translations\n"
3119+"POT-Creation-Date: 2009-05-21 14:44+0300\n"
3120+"PO-Revision-Date: 2009-05-21 15:05+0300\n"
3121+"Last-Translator: Andriy Podanenko <podarok@ua.fm>\n"
3122+"Language-Team: uk <podarok@ua.fm>\n"
3123+"MIME-Version: 1.0\n"
3124+"Content-Type: text/plain; charset=utf-8\n"
3125+"Content-Transfer-Encoding: 8bit\n"
3126+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
3127+"X-Poedit-Language: Ukrainian\n"
3128+"X-Poedit-Country: UKRAINE\n"
3129+"X-Poedit-SourceCharset: utf-8\n"
3130+
3131+#: votingapi.admin.inc:13
3132+msgid "Immediately"
3133+msgstr "Миттєво"
3134+
3135+#: votingapi.admin.inc:13
3136+msgid "Never"
3137+msgstr "Ніколи"
3138+
3139+#: votingapi.admin.inc:17
3140+msgid "Anonymous vote rollover"
3141+msgstr "Повторювання анонімних голосувань"
3142+
3143+#: votingapi.admin.inc:18
3144+msgid "The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to 'never' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes."
3145+msgstr "Кількість часу, що мине перед тим як два анонімних голоси з одного комп'ютера рахуватимуться різними. Вказування \"ніколи\" дозвроятиме накрутки, але надасть можливість багатьом анонімним користувачам на одному комп'ютері (приклад, інтернет каже) участвувати в голосуваннях"
3146+
3147+#: votingapi.admin.inc:25
3148+msgid "Vote tallying"
3149+msgstr "Підрахунок голосів"
3150+
3151+#: votingapi.admin.inc:26
3152+msgid "On high-traffic sites, administrators can use this setting to postpone the calculation of vote results."
3153+msgstr "На високонавантажених сайтах, адміністратори можуть використати ці параметри для відкладеного обрахунку результатів голосувань"
3154+
3155+#: votingapi.admin.inc:29
3156+msgid "Tally results whenever a vote is cast"
3157+msgstr "Підрахунок результатів під час додавання голосу"
3158+
3159+#: votingapi.admin.inc:30
3160+msgid "Tally results at cron-time"
3161+msgstr "Підрахунок голосів cron-ом"
3162+
3163+#: votingapi.admin.inc:31
3164+msgid "Do not tally results automatically: I am using a module that manages its own vote results."
3165+msgstr "Не рахувати голоси автоматично: Я використовую модуль, що керує сам результатами голосувань"
3166+
3167+#: votingapi.admin.inc:49
3168+msgid "Which node types should receive votes?"
3169+msgstr "Які типи матеріалів можуть отримувати голоси?"
3170+
3171+#: votingapi.admin.inc:56
3172+msgid "What type of votes should be generated?"
3173+msgstr "Які типи голосів повинні бути генеровано?"
3174+
3175+#: votingapi.admin.inc:58
3176+msgid "Fivestar style"
3177+msgstr "Стиль Fivestar"
3178+
3179+#: votingapi.admin.inc:59
3180+msgid "Digg style"
3181+msgstr "Стиль Digg"
3182+
3183+#: votingapi.admin.inc:60
3184+msgid "Reddit style"
3185+msgstr "Стиль Reddit"
3186+
3187+#: votingapi.admin.inc:67
3188+msgid "Delete existing votes before generating new ones."
3189+msgstr "Видалення існуючих голосів перед генерацією нових"
3190+
3191+#: votingapi.admin.inc:72
3192+msgid "Do it!"
3193+msgstr "Зробити!"
3194+
3195+#: votingapi.module:48
3196+msgid "administer voting api"
3197+msgstr "керування API голосувань"
3198+
3199+#: votingapi.module:20
3200+#: votingapi.info:0
3201+msgid "Voting API"
3202+msgstr "API голосувань"
3203+
3204+#: votingapi.module:21
3205+msgid "Global settings for the Voting API."
3206+msgstr "Глобальні параметри API голосувань"
3207+
3208+#: votingapi.module:32
3209+msgid "Generate votes"
3210+msgstr "Генерація голосів"
3211+
3212+#: votingapi.module:33
3213+msgid "Generate a given number of votes on site content. Optionally delete existing votes."
3214+msgstr "Генерувати вказану кількість голосів на матеріалі сайту. Опційно видаляє існуючі голоси"
3215+
3216+#: votingapi.module:0
3217+msgid "votingapi"
3218+msgstr "votingapi"
3219+
3220+#: votingapi.info:0
3221+msgid "Provides a shared voting API for other modules."
3222+msgstr "Надає доступ до API голосувань для інших модулів"
3223+
3224+#: votingapi.info:0
3225+msgid "Voting"
3226+msgstr "Голосування"
3227+
3228
3229=== added directory 'sites/all/modules/votingapi/views'
3230=== added file 'sites/all/modules/votingapi/views/votingapi.views.inc'
3231--- sites/all/modules/votingapi/views/votingapi.views.inc 1970-01-01 00:00:00 +0000
3232+++ sites/all/modules/votingapi/views/votingapi.views.inc 2010-03-02 11:43:13 +0000
3233@@ -0,0 +1,376 @@
3234+<?php
3235+// $Id: votingapi.views.inc,v 1.1.2.18 2009/08/15 17:51:38 eaton Exp $
3236+
3237+/**
3238+ * @file
3239+ * Provide views data for votingapi.module.
3240+ */
3241+
3242+/**
3243+ * @defgroup views_votingapi_module votingapi.module handlers.
3244+ *
3245+ * @{
3246+ */
3247+
3248+
3249+/**
3250+ * Implementation of hook_views_handlers().
3251+ */
3252+function votingapi_views_handlers() {
3253+ return array(
3254+ 'handlers' => array(
3255+ 'votingapi_views_handler_field_value' => array(
3256+ 'parent' => 'views_handler_field_numeric',
3257+ 'path' => drupal_get_path('module', 'votingapi') . '/views',
3258+ ),
3259+ 'votingapi_views_handler_relationship' => array(
3260+ 'parent' => 'views_handler_relationship',
3261+ 'path' => drupal_get_path('module', 'votingapi') . '/views',
3262+ ),
3263+ ),
3264+ );
3265+}
3266+
3267+
3268+/**
3269+ * Implementation of hook_views_data().
3270+ */
3271+function votingapi_views_data() {
3272+ // Basic table information.
3273+
3274+ // Define the base group of this table. Fields that don't
3275+ // have a group defined will go into this field by default.
3276+ $data['votingapi_vote']['table']['group'] = t('Votes');
3277+ $data['votingapi_cache']['table']['group'] = t('Vote results');
3278+
3279+ // ----------------------------------------------------------------
3280+ // Fields
3281+
3282+ // value
3283+ $data['votingapi_vote']['value'] = array(
3284+ 'title' => t('Value'), // The item it appears as on the UI,
3285+ 'help' => t('The value of an individual cast vote.'), // The help that appears on the UI,
3286+ // Information for displaying a title as a field
3287+ 'field' => array(
3288+ 'handler' => 'votingapi_views_handler_field_value',
3289+ 'click sortable' => TRUE,
3290+ ),
3291+ 'filter' => array(
3292+ 'handler' => 'views_handler_filter_numeric',
3293+ 'allow empty' => TRUE,
3294+ ),
3295+ 'sort' => array(
3296+ 'handler' => 'views_handler_sort',
3297+ ),
3298+ );
3299+
3300+ // value type
3301+ $data['votingapi_vote']['value_type'] = array(
3302+ 'title' => t('Value type'), // The item it appears as on the UI,
3303+ 'help' => t('The nature of the vote being cast (points, percentage, etc).'), // The help that appears on the UI,
3304+ // Information for displaying a title as a field
3305+ 'field' => array(
3306+ 'handler' => 'views_handler_field',
3307+ 'click sortable' => TRUE,
3308+ ),
3309+ 'filter' => array(
3310+ 'handler' => 'views_handler_filter_string',
3311+ ),
3312+ 'sort' => array(
3313+ 'handler' => 'views_handler_sort',
3314+ ),
3315+ );
3316+
3317+ // tag
3318+ $data['votingapi_vote']['tag'] = array(
3319+ 'title' => t('Tag'), // The item it appears as on the UI,
3320+ 'help' => t('An optional tag to group multi-criteria votes.'), // The help that appears on the UI,
3321+ // Information for displaying a title as a field
3322+ 'field' => array(
3323+ 'handler' => 'views_handler_field',
3324+ 'click sortable' => TRUE,
3325+ ),
3326+ 'filter' => array(
3327+ 'handler' => 'views_handler_filter_string',
3328+ ),
3329+ 'sort' => array(
3330+ 'handler' => 'views_handler_sort',
3331+ ),
3332+ );
3333+
3334+ // uid
3335+ $data['votingapi_vote']['uid'] = array(
3336+ 'title' => t('User'), // The item it appears as on the UI,
3337+ 'help' => t('The user who cast the vote.'), // The help that appears on the UI,
3338+ // Information for displaying a title as a field
3339+ 'field' => array(
3340+ 'handler' => 'views_handler_field_user',
3341+ 'click sortable' => TRUE,
3342+ ),
3343+ 'filter' => array(
3344+ 'handler' => 'views_handler_filter_user_name',
3345+ ),
3346+ 'sort' => array(
3347+ 'handler' => 'views_handler_sort',
3348+ ),
3349+ 'argument' => array(
3350+ 'handler' => 'views_handler_argument_user_uid',
3351+ ),
3352+ 'relationship' => array(
3353+ 'handler' => 'views_handler_relationship',
3354+ 'base' => 'users',
3355+ 'field' => 'uid',
3356+ 'label' => t('Individual voter'),
3357+ ),
3358+ );
3359+
3360+ // uid
3361+ $data['votingapi_vote']['current_uid'] = array(
3362+ 'title' => t('Current user'), // The item it appears as on the UI,
3363+ 'help' => t('Restrict votes to those cast by the current user.'), // The help that appears on the UI,
3364+ 'real field' => 'uid',
3365+ 'filter' => array(
3366+ 'handler' => 'views_handler_filter_user_current',
3367+ ),
3368+ );
3369+
3370+ // vote_source
3371+ $data['votingapi_vote']['vote_source'] = array(
3372+ 'title' => t('IP Address'), // The item it appears as on the UI,
3373+ 'help' => t('The IP address of the user who cast the vote.'), // The help that appears on the UI,
3374+ // Information for displaying a title as a field
3375+ 'field' => array(
3376+ 'handler' => 'views_handler_field',
3377+ 'click sortable' => TRUE,
3378+ ),
3379+ 'filter' => array(
3380+ 'handler' => 'views_handler_filter_string',
3381+ ),
3382+ 'sort' => array(
3383+ 'handler' => 'views_handler_sort',
3384+ ),
3385+ );
3386+
3387+
3388+ // timestamp
3389+ $data['votingapi_vote']['timestamp'] = array(
3390+ 'title' => t('Timestamp'), // The item it appears as on the UI,
3391+ 'help' => t('The time the vote was cast.'), // The help that appears on the UI,
3392+ // Information for displaying a title as a field
3393+ 'field' => array(
3394+ 'handler' => 'views_handler_field_date',
3395+ 'click sortable' => TRUE,
3396+ ),
3397+ 'filter' => array(
3398+ 'handler' => 'views_handler_filter_date',
3399+ ),
3400+ 'sort' => array(
3401+ 'handler' => 'views_handler_sort',
3402+ ),
3403+ );
3404+
3405+
3406+ // Cache table fields
3407+
3408+ // value
3409+ $data['votingapi_cache']['value'] = array(
3410+ 'title' => t('Value'), // The item it appears as on the UI,
3411+ 'help' => t('The value of an individual cast vote.'), // The help that appears on the UI,
3412+ // Information for displaying a title as a field
3413+ 'field' => array(
3414+ 'handler' => 'votingapi_views_handler_field_value',
3415+ 'click sortable' => TRUE,
3416+ ),
3417+ 'filter' => array(
3418+ 'handler' => 'views_handler_filter_numeric',
3419+ 'allow empty' => TRUE,
3420+ ),
3421+ 'sort' => array(
3422+ 'handler' => 'views_handler_sort',
3423+ ),
3424+ );
3425+
3426+ // value type
3427+ $data['votingapi_cache']['value_type'] = array(
3428+ 'title' => t('Value type'), // The item it appears as on the UI,
3429+ 'help' => t('The nature of the results in question (points, percentage, etc).'), // The help that appears on the UI,
3430+ // Information for displaying a title as a field
3431+ 'field' => array(
3432+ 'handler' => 'views_handler_field',
3433+ 'click sortable' => TRUE,
3434+ ),
3435+ 'filter' => array(
3436+ 'handler' => 'views_handler_filter_string',
3437+ ),
3438+ 'sort' => array(
3439+ 'handler' => 'views_handler_sort',
3440+ ),
3441+ );
3442+
3443+ // tag
3444+ $data['votingapi_cache']['tag'] = array(
3445+ 'title' => t('Tag'), // The item it appears as on the UI,
3446+ 'help' => t('An optional tag to group multi-criteria results.'), // The help that appears on the UI,
3447+ // Information for displaying a title as a field
3448+ 'field' => array(
3449+ 'handler' => 'views_handler_field',
3450+ 'click sortable' => TRUE,
3451+ ),
3452+ 'filter' => array(
3453+ 'handler' => 'views_handler_filter_string',
3454+ ),
3455+ 'sort' => array(
3456+ 'handler' => 'views_handler_sort',
3457+ ),
3458+ );
3459+
3460+
3461+ // function
3462+ $data['votingapi_cache']['function'] = array(
3463+ 'title' => t('Function'), // The item it appears as on the UI,
3464+ 'help' => t('The aggregate function used to calculate the result.'), // The help that appears on the UI,
3465+ // Information for displaying a title as a field
3466+ 'field' => array(
3467+ 'handler' => 'views_handler_field',
3468+ 'click sortable' => TRUE,
3469+ ),
3470+ 'filter' => array(
3471+ 'handler' => 'views_handler_filter_string',
3472+ ),
3473+ 'sort' => array(
3474+ 'handler' => 'views_handler_sort',
3475+ ),
3476+ );
3477+
3478+
3479+ // timestamp
3480+ $data['votingapi_cache']['timestamp'] = array(
3481+ 'title' => t('Timestamp'), // The item it appears as on the UI,
3482+ 'help' => t('The time the results were calculated.'), // The help that appears on the UI,
3483+ // Information for displaying a title as a field
3484+ 'field' => array(
3485+ 'handler' => 'views_handler_field_date',
3486+ 'click sortable' => TRUE,
3487+ ),
3488+ 'filter' => array(
3489+ 'handler' => 'views_handler_filter_date',
3490+ ),
3491+ 'sort' => array(
3492+ 'handler' => 'views_handler_sort',
3493+ ),
3494+ );
3495+ return $data;
3496+}
3497+
3498+/**
3499+ * @}
3500+ */
3501+
3502+function votingapi_views_data_alter(&$views_data) {
3503+ // Add relationship handlers for both tables, for any base tables currently
3504+ // available to Views.
3505+
3506+ $default_relationships = module_invoke_all('votingapi_relationships');
3507+
3508+ $default_relationships[] = array(
3509+ 'description' => t('nodes'),
3510+ 'content_type' => 'node',
3511+ 'base_table' => 'node',
3512+ 'content_id_column' => 'nid',
3513+ 'pseudo_vote' => 'votingapi_vote', // for legacy compatability w/RC1.
3514+ 'pseudo_cache' => 'votingapi_cache', // for legacy compatability w/RC1.
3515+ );
3516+ $default_relationships[] = array(
3517+ 'description' => t('comments'),
3518+ 'content_type' => 'comment',
3519+ 'base_table' => 'comments',
3520+ 'content_id_column' => 'cid',
3521+ 'pseudo_vote' => 'votingapi_vote', // for legacy compatability w/RC1.
3522+ 'pseudo_cache' => 'votingapi_cache', // for legacy compatability w/RC1.
3523+ );
3524+ $default_relationships[] = array(
3525+ 'description' => t('users'),
3526+ 'content_type' => 'user',
3527+ 'base_table' => 'users',
3528+ 'content_id_column' => 'uid',
3529+ 'pseudo_vote' => 'votingapi_vote', // for legacy compatability w/RC1.
3530+ 'pseudo_cache' => 'votingapi_cache', // for legacy compatability w/RC1.
3531+ );
3532+
3533+ foreach ($default_relationships as $data) {
3534+ $pseudo = str_replace(array(' ','-','.'), '_', $data['content_type'] . '_' . $data['content_id_column']);
3535+ $pseudo_vote = empty($data['pseudo_vote']) ? 'vapi_'. $pseudo : $data['pseudo_vote'];
3536+ $pseudo_cache = empty($data['pseudo_cache']) ? 'vapic_'. $pseudo : $data['pseudo_cache'];
3537+
3538+ $views_data[$data['base_table']][$pseudo_vote]['relationship'] = array(
3539+ 'title' => 'Votes',
3540+ 'help' => 'Votes cast by users on ' . $data['description'] . '.',
3541+ 'base' => 'votingapi_vote',
3542+ 'field' => 'content_id',
3543+ 'relationship field' => $data['content_id_column'],
3544+ 'handler' => 'votingapi_views_handler_relationship',
3545+ 'extra' => array(
3546+ array(
3547+ 'field' => 'content_type',
3548+ 'value' => $data['content_type'],
3549+ 'numeric' => FALSE
3550+ ),
3551+ ),
3552+ );
3553+
3554+ $views_data[$data['base_table']][$pseudo_cache]['relationship'] = array(
3555+ 'title' => 'Vote results',
3556+ 'help' => 'Aggregate results of votes cast on ' . $data['description'] . '.',
3557+ 'base' => 'votingapi_cache',
3558+ 'field' => 'content_id',
3559+ 'relationship field' => $data['content_id_column'],
3560+ 'handler' => 'votingapi_views_handler_relationship',
3561+ 'extra' => array(
3562+ array(
3563+ 'field' => 'content_type',
3564+ 'value' => $data['content_type'],
3565+ 'numeric' => FALSE
3566+ ),
3567+ ),
3568+ );
3569+ }
3570+}
3571+
3572+
3573+function _votingapi_views_sql_safe_content_type($string) {
3574+ return str_replace(array(' ','-','.'), '_', $string);
3575+}
3576+
3577+
3578+/**
3579+ * Implementation of hook_views_analyze().
3580+ *
3581+ * Warns admins if a VotingAPI relationship has been defined, but filters on the
3582+ * relationship haven't been set.
3583+ */
3584+function votingapi_views_analyze($view) {
3585+ $ret = array();
3586+ // Check for something other than the default display:
3587+ foreach ($view->display as $id => $display) {
3588+ if (!$display->handler->is_defaulted('access') || !$display->handler->is_defaulted('filters')) {
3589+ $relationships = $display->handler->get_option('relationships');
3590+ foreach ($relationships as $relationship) {
3591+ if ($relationship['field'] == 'votingapi_vote') {
3592+ if (empty($relationship['votingapi']['value_type']) ||
3593+ empty($relationship['votingapi']['tag'])) {
3594+ $ret[] = views_ui_analysis(t('Display %display defines a VotingAPI vote relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view.', array('%display' => $display->display_title)), 'warning');
3595+ }
3596+ }
3597+ elseif ($relationship['field'] == 'votingapi_cache') {
3598+ if (empty($relationship['votingapi']['value_type']) ||
3599+ empty($relationship['votingapi']['tag']) ||
3600+ empty($relationship['votingapi']['function'])) {
3601+ $ret[] = views_ui_analysis(t('Display %display defines a VotingAPI results relationship, but does not restrict the relationship data. This may result in duplicate entries in the resulting view.', array('%display' => $display->display_title)), 'warning');
3602+ }
3603+ }
3604+ }
3605+ }
3606+ }
3607+
3608+ return $ret;
3609+}
3610
3611=== added file 'sites/all/modules/votingapi/views/votingapi.views_default.inc'
3612--- sites/all/modules/votingapi/views/votingapi.views_default.inc 1970-01-01 00:00:00 +0000
3613+++ sites/all/modules/votingapi/views/votingapi.views_default.inc 2010-03-02 11:43:13 +0000
3614@@ -0,0 +1,555 @@
3615+<?php
3616+// $Id: votingapi.views_default.inc,v 1.1.2.6 2008/12/04 18:34:39 eaton Exp $
3617+
3618+/**
3619+ * @file
3620+ * Provide default views data for votingapi.module.
3621+ */
3622+
3623+function votingapi_views_default_views() {
3624+ $views = array();
3625+
3626+ $view = new view;
3627+ $view->name = 'top_content';
3628+ $view->description = 'Top rated content';
3629+ $view->tag = 'votingapi';
3630+ $view->view_php = '';
3631+ $view->base_table = 'node';
3632+ $view->is_cacheable = FALSE;
3633+ $view->api_version = 2;
3634+ $view->disabled = TRUE; /* Edit this to true to make a default view disabled initially */
3635+ $handler = $view->new_display('default', 'Defaults', 'default');
3636+ $handler->override_option('fields', array(
3637+ 'name' => array(
3638+ 'label' => 'Author',
3639+ 'link_to_user' => 1,
3640+ 'exclude' => 0,
3641+ 'id' => 'name',
3642+ 'table' => 'users',
3643+ 'field' => 'name',
3644+ 'relationship' => 'none',
3645+ ),
3646+ 'title' => array(
3647+ 'label' => 'Title',
3648+ 'link_to_node' => 1,
3649+ 'exclude' => 0,
3650+ 'id' => 'title',
3651+ 'table' => 'node',
3652+ 'field' => 'title',
3653+ 'relationship' => 'none',
3654+ ),
3655+ 'created' => array(
3656+ 'label' => 'Posted',
3657+ 'date_format' => 'time ago',
3658+ 'custom_date_format' => '1',
3659+ 'exclude' => 0,
3660+ 'id' => 'created',
3661+ 'table' => 'node',
3662+ 'field' => 'created',
3663+ 'relationship' => 'none',
3664+ ),
3665+ ));
3666+ $handler->override_option('filters', array(
3667+ 'status' => array(
3668+ 'operator' => '=',
3669+ 'value' => 1,
3670+ 'group' => '0',
3671+ 'exposed' => FALSE,
3672+ 'expose' => array(
3673+ 'operator' => FALSE,
3674+ 'label' => '',
3675+ ),
3676+ 'id' => 'status',
3677+ 'table' => 'node',
3678+ 'field' => 'status',
3679+ 'relationship' => 'none',
3680+ ),
3681+ ));
3682+ $handler->override_option('access', array(
3683+ 'type' => 'none',
3684+ ));
3685+ $handler->override_option('title', 'Top rated content');
3686+ $handler->override_option('use_pager', '1');
3687+ $handler->override_option('style_plugin', 'table');
3688+ $handler->override_option('style_options', array(
3689+ 'grouping' => '',
3690+ 'override' => 1,
3691+ 'sticky' => 1,
3692+ 'order' => 'desc',
3693+ 'columns' => array(
3694+ 'name' => 'name',
3695+ 'title' => 'title',
3696+ 'created' => 'created',
3697+ ),
3698+ 'info' => array(
3699+ 'name' => array(
3700+ 'sortable' => 1,
3701+ 'separator' => '',
3702+ ),
3703+ 'title' => array(
3704+ 'sortable' => 1,
3705+ 'separator' => '',
3706+ ),
3707+ 'created' => array(
3708+ 'sortable' => 1,
3709+ 'separator' => '',
3710+ ),
3711+ ),
3712+ 'default' => 'title',
3713+ ));
3714+ $handler = $view->new_display('page', 'Fivestar', 'page_1');
3715+ $handler->override_option('relationships', array(
3716+ 'votingapi_cache' => array(
3717+ 'label' => 'Voting results',
3718+ 'required' => 0,
3719+ 'votingapi' => array(
3720+ 'value_type' => 'percent',
3721+ 'tag' => 'vote',
3722+ 'function' => 'average',
3723+ ),
3724+ 'id' => 'votingapi_cache',
3725+ 'table' => 'node',
3726+ 'field' => 'votingapi_cache',
3727+ 'override' => array(
3728+ 'button' => 'Use default',
3729+ ),
3730+ 'relationship' => 'none',
3731+ ),
3732+ ));
3733+ $handler->override_option('fields', array(
3734+ 'name' => array(
3735+ 'label' => 'Author',
3736+ 'link_to_user' => 1,
3737+ 'exclude' => 0,
3738+ 'id' => 'name',
3739+ 'table' => 'users',
3740+ 'field' => 'name',
3741+ 'relationship' => 'none',
3742+ ),
3743+ 'title' => array(
3744+ 'label' => 'Title',
3745+ 'link_to_node' => 1,
3746+ 'exclude' => 0,
3747+ 'id' => 'title',
3748+ 'table' => 'node',
3749+ 'field' => 'title',
3750+ 'relationship' => 'none',
3751+ ),
3752+ 'created' => array(
3753+ 'label' => 'Posted',
3754+ 'date_format' => 'time ago',
3755+ 'custom_date_format' => '1',
3756+ 'exclude' => 0,
3757+ 'id' => 'created',
3758+ 'table' => 'node',
3759+ 'field' => 'created',
3760+ 'relationship' => 'none',
3761+ ),
3762+ 'value' => array(
3763+ 'label' => 'Rating',
3764+ 'set_precision' => FALSE,
3765+ 'precision' => 0,
3766+ 'decimal' => '.',
3767+ 'separator' => ',',
3768+ 'prefix' => '',
3769+ 'suffix' => '',
3770+ 'appearance' => 'fivestar_views_value_display_handler',
3771+ 'exclude' => 0,
3772+ 'id' => 'value',
3773+ 'table' => 'votingapi_cache',
3774+ 'field' => 'value',
3775+ 'override' => array(
3776+ 'button' => 'Use default',
3777+ ),
3778+ 'relationship' => 'votingapi_cache',
3779+ ),
3780+ ));
3781+ $handler->override_option('sorts', array(
3782+ 'value' => array(
3783+ 'order' => 'DESC',
3784+ 'id' => 'value',
3785+ 'table' => 'votingapi_cache',
3786+ 'field' => 'value',
3787+ 'relationship' => 'votingapi_cache',
3788+ 'override' => array(
3789+ 'button' => 'Use default',
3790+ ),
3791+ ),
3792+ ));
3793+ $handler->override_option('header', 'This page is a demonstration of VotingAPI\'s integration with the Views module. It\'s set up to display nodes sorted by percentage ratings, the style used by Fivestar module.');
3794+ $handler->override_option('header_format', '1');
3795+ $handler->override_option('header_empty', 0);
3796+ $handler->override_option('style_options', array(
3797+ 'grouping' => '',
3798+ 'override' => 1,
3799+ 'sticky' => 1,
3800+ 'order' => 'desc',
3801+ 'columns' => array(
3802+ 'name' => 'name',
3803+ 'title' => 'title',
3804+ 'created' => 'created',
3805+ 'value' => 'value',
3806+ ),
3807+ 'info' => array(
3808+ 'name' => array(
3809+ 'sortable' => 1,
3810+ 'separator' => '',
3811+ ),
3812+ 'title' => array(
3813+ 'sortable' => 1,
3814+ 'separator' => '',
3815+ ),
3816+ 'created' => array(
3817+ 'sortable' => 1,
3818+ 'separator' => '',
3819+ ),
3820+ 'value' => array(
3821+ 'sortable' => 1,
3822+ 'separator' => '',
3823+ ),
3824+ ),
3825+ 'default' => 'value',
3826+ ));
3827+ $handler->override_option('path', 'top-rated-percent');
3828+ $handler->override_option('menu', array(
3829+ 'type' => 'normal',
3830+ 'title' => 'Top content (Fivestar)',
3831+ 'weight' => '0',
3832+ 'name' => 'navigation',
3833+ ));
3834+ $handler->override_option('tab_options', array(
3835+ 'type' => 'none',
3836+ 'title' => '',
3837+ 'weight' => 0,
3838+ ));
3839+ $handler = $view->new_display('page', 'Plus1', 'page_2');
3840+ $handler->override_option('relationships', array(
3841+ 'votingapi_cache' => array(
3842+ 'label' => 'Voting results',
3843+ 'required' => 0,
3844+ 'votingapi' => array(
3845+ 'value_type' => 'points',
3846+ 'tag' => 'vote',
3847+ 'function' => 'sum',
3848+ ),
3849+ 'id' => 'votingapi_cache',
3850+ 'table' => 'node',
3851+ 'field' => 'votingapi_cache',
3852+ 'override' => array(
3853+ 'button' => 'Use default',
3854+ ),
3855+ 'relationship' => 'none',
3856+ ),
3857+ ));
3858+ $handler->override_option('fields', array(
3859+ 'name' => array(
3860+ 'label' => 'Author',
3861+ 'link_to_user' => 1,
3862+ 'exclude' => 0,
3863+ 'id' => 'name',
3864+ 'table' => 'users',
3865+ 'field' => 'name',
3866+ 'relationship' => 'none',
3867+ ),
3868+ 'title' => array(
3869+ 'label' => 'Title',
3870+ 'link_to_node' => 1,
3871+ 'exclude' => 0,
3872+ 'id' => 'title',
3873+ 'table' => 'node',
3874+ 'field' => 'title',
3875+ 'relationship' => 'none',
3876+ ),
3877+ 'created' => array(
3878+ 'label' => 'Posted',
3879+ 'date_format' => 'time ago',
3880+ 'custom_date_format' => '1',
3881+ 'exclude' => 0,
3882+ 'id' => 'created',
3883+ 'table' => 'node',
3884+ 'field' => 'created',
3885+ 'relationship' => 'none',
3886+ ),
3887+ 'value' => array(
3888+ 'label' => 'Rating',
3889+ 'set_precision' => FALSE,
3890+ 'precision' => 0,
3891+ 'decimal' => '.',
3892+ 'separator' => ',',
3893+ 'prefix' => '',
3894+ 'suffix' => ' points',
3895+ 'appearance' => '',
3896+ 'exclude' => 0,
3897+ 'id' => 'value',
3898+ 'table' => 'votingapi_cache',
3899+ 'field' => 'value',
3900+ 'override' => array(
3901+ 'button' => 'Use default',
3902+ ),
3903+ 'relationship' => 'votingapi_cache',
3904+ ),
3905+ ));
3906+ $handler->override_option('sorts', array(
3907+ 'value' => array(
3908+ 'order' => 'DESC',
3909+ 'id' => 'value',
3910+ 'table' => 'votingapi_cache',
3911+ 'field' => 'value',
3912+ 'override' => array(
3913+ 'button' => 'Use default',
3914+ ),
3915+ 'relationship' => 'votingapi_cache',
3916+ ),
3917+ ));
3918+ $handler->override_option('header', 'This page is a demonstration of VotingAPI\'s integration with the Views module. It\'s set up to display nodes sorted by point ratings, the style used by the Plus1 and VoteUpDown modules.');
3919+ $handler->override_option('header_format', '1');
3920+ $handler->override_option('header_empty', 0);
3921+ $handler->override_option('style_options', array(
3922+ 'grouping' => '',
3923+ 'override' => 1,
3924+ 'sticky' => 1,
3925+ 'order' => 'desc',
3926+ 'columns' => array(
3927+ 'name' => 'name',
3928+ 'title' => 'title',
3929+ 'created' => 'created',
3930+ 'value' => 'value',
3931+ ),
3932+ 'info' => array(
3933+ 'name' => array(
3934+ 'sortable' => 1,
3935+ 'separator' => '',
3936+ ),
3937+ 'title' => array(
3938+ 'sortable' => 1,
3939+ 'separator' => '',
3940+ ),
3941+ 'created' => array(
3942+ 'sortable' => 1,
3943+ 'separator' => '',
3944+ ),
3945+ 'value' => array(
3946+ 'sortable' => 1,
3947+ 'separator' => '',
3948+ ),
3949+ ),
3950+ 'default' => 'value',
3951+ ));
3952+ $handler->override_option('path', 'top-rated-points');
3953+ $handler->override_option('menu', array(
3954+ 'type' => 'normal',
3955+ 'title' => 'Top content (Plus1)',
3956+ 'weight' => '0',
3957+ 'name' => 'navigation',
3958+ ));
3959+ $handler->override_option('tab_options', array(
3960+ 'type' => 'none',
3961+ 'title' => '',
3962+ 'weight' => 0,
3963+ ));
3964+ $handler = $view->new_display('page', 'Current user', 'page_3');
3965+ $handler->override_option('relationships', array(
3966+ 'votingapi_vote' => array(
3967+ 'label' => 'Individual votes',
3968+ 'required' => 1,
3969+ 'votingapi' => array(
3970+ 'value_type' => '',
3971+ 'tag' => '',
3972+ ),
3973+ 'current_user' => 0,
3974+ 'id' => 'votingapi_vote',
3975+ 'table' => 'node',
3976+ 'field' => 'votingapi_vote',
3977+ 'override' => array(
3978+ 'button' => 'Use default',
3979+ ),
3980+ 'relationship' => 'none',
3981+ ),
3982+ ));
3983+ $handler->override_option('fields', array(
3984+ 'title' => array(
3985+ 'label' => 'Title',
3986+ 'link_to_node' => 1,
3987+ 'exclude' => 0,
3988+ 'id' => 'title',
3989+ 'table' => 'node',
3990+ 'field' => 'title',
3991+ 'relationship' => 'none',
3992+ ),
3993+ 'value_type' => array(
3994+ 'label' => 'Vote type',
3995+ 'exclude' => 0,
3996+ 'id' => 'value_type',
3997+ 'table' => 'votingapi_vote',
3998+ 'field' => 'value_type',
3999+ 'override' => array(
4000+ 'button' => 'Override',
4001+ ),
4002+ 'relationship' => 'votingapi_vote',
4003+ ),
4004+ 'value' => array(
4005+ 'label' => 'Value',
4006+ 'set_precision' => FALSE,
4007+ 'precision' => 0,
4008+ 'decimal' => '.',
4009+ 'separator' => ',',
4010+ 'prefix' => '',
4011+ 'suffix' => '',
4012+ 'appearance' => '',
4013+ 'exclude' => 0,
4014+ 'id' => 'value',
4015+ 'table' => 'votingapi_vote',
4016+ 'field' => 'value',
4017+ 'override' => array(
4018+ 'button' => 'Override',
4019+ ),
4020+ 'relationship' => 'votingapi_vote',
4021+ ),
4022+ 'timestamp' => array(
4023+ 'label' => 'Timestamp',
4024+ 'date_format' => 'time ago',
4025+ 'custom_date_format' => '1',
4026+ 'exclude' => 0,
4027+ 'id' => 'timestamp',
4028+ 'table' => 'votingapi_vote',
4029+ 'field' => 'timestamp',
4030+ 'override' => array(
4031+ 'button' => 'Use default',
4032+ ),
4033+ 'relationship' => 'votingapi_vote',
4034+ ),
4035+ ));
4036+ $handler->override_option('sorts', array(
4037+ 'timestamp' => array(
4038+ 'order' => 'DESC',
4039+ 'id' => 'timestamp',
4040+ 'table' => 'votingapi_vote',
4041+ 'field' => 'timestamp',
4042+ 'override' => array(
4043+ 'button' => 'Use default',
4044+ ),
4045+ 'relationship' => 'votingapi_vote',
4046+ ),
4047+ ));
4048+ $handler->override_option('arguments', array(
4049+ 'uid' => array(
4050+ 'default_action' => 'default',
4051+ 'style_plugin' => 'default_summary',
4052+ 'style_options' => array(),
4053+ 'wildcard' => 'all',
4054+ 'wildcard_substitution' => 'Everyone',
4055+ 'title' => '%1\'s votes',
4056+ 'default_argument_type' => 'current_user',
4057+ 'default_argument' => '',
4058+ 'validate_type' => 'none',
4059+ 'validate_fail' => 'not found',
4060+ 'break_phrase' => 0,
4061+ 'not' => 0,
4062+ 'id' => 'uid',
4063+ 'table' => 'votingapi_vote',
4064+ 'field' => 'uid',
4065+ 'relationship' => 'votingapi_vote',
4066+ 'override' => array(
4067+ 'button' => 'Use default',
4068+ ),
4069+ 'default_options_div_prefix' => '',
4070+ 'default_argument_user' => 0,
4071+ 'default_argument_fixed' => '',
4072+ 'default_argument_php' => '',
4073+ 'validate_argument_node_type' => array(
4074+ 'page' => 0,
4075+ 'story' => 0,
4076+ ),
4077+ 'validate_argument_node_access' => 0,
4078+ 'validate_argument_nid_type' => 'nid',
4079+ 'validate_argument_vocabulary' => array(),
4080+ 'validate_argument_type' => 'tid',
4081+ 'validate_argument_php' => '',
4082+ ),
4083+ ));
4084+ $handler->override_option('filters', array(
4085+ 'status' => array(
4086+ 'operator' => '=',
4087+ 'value' => 1,
4088+ 'group' => '0',
4089+ 'exposed' => FALSE,
4090+ 'expose' => array(
4091+ 'operator' => FALSE,
4092+ 'label' => '',
4093+ ),
4094+ 'id' => 'status',
4095+ 'table' => 'node',
4096+ 'field' => 'status',
4097+ 'relationship' => 'none',
4098+ ),
4099+ 'value' => array(
4100+ 'operator' => 'not empty',
4101+ 'value' => array(
4102+ 'value' => '',
4103+ 'min' => '',
4104+ 'max' => '',
4105+ ),
4106+ 'group' => '0',
4107+ 'exposed' => FALSE,
4108+ 'expose' => array(
4109+ 'operator' => FALSE,
4110+ 'label' => '',
4111+ ),
4112+ 'id' => 'value',
4113+ 'table' => 'votingapi_vote',
4114+ 'field' => 'value',
4115+ 'override' => array(
4116+ 'button' => 'Use default',
4117+ ),
4118+ 'relationship' => 'votingapi_vote',
4119+ ),
4120+ ));
4121+ $handler->override_option('title', 'Your votes');
4122+ $handler->override_option('style_options', array(
4123+ 'grouping' => '',
4124+ 'override' => 1,
4125+ 'sticky' => 1,
4126+ 'order' => 'desc',
4127+ 'columns' => array(
4128+ 'title' => 'title',
4129+ 'value_type' => 'value_type',
4130+ 'value' => 'value',
4131+ 'timestamp' => 'timestamp',
4132+ ),
4133+ 'info' => array(
4134+ 'title' => array(
4135+ 'sortable' => 1,
4136+ 'separator' => '',
4137+ ),
4138+ 'value_type' => array(
4139+ 'sortable' => 1,
4140+ 'separator' => '',
4141+ ),
4142+ 'value' => array(
4143+ 'sortable' => 1,
4144+ 'separator' => '',
4145+ ),
4146+ 'timestamp' => array(
4147+ 'sortable' => 1,
4148+ 'separator' => '',
4149+ ),
4150+ ),
4151+ 'default' => 'timestamp',
4152+ ));
4153+ $handler->override_option('path', 'your-votes');
4154+ $handler->override_option('menu', array(
4155+ 'type' => 'normal',
4156+ 'title' => 'Your votes',
4157+ 'weight' => '0',
4158+ 'name' => 'navigation',
4159+ ));
4160+ $handler->override_option('tab_options', array(
4161+ 'type' => 'none',
4162+ 'title' => '',
4163+ 'weight' => 0,
4164+ ));
4165+
4166+
4167+ $views[$view->name] = $view;
4168+ return $views;
4169+}
4170
4171=== added file 'sites/all/modules/votingapi/views/votingapi_views_handler_field_value.inc'
4172--- sites/all/modules/votingapi/views/votingapi_views_handler_field_value.inc 1970-01-01 00:00:00 +0000
4173+++ sites/all/modules/votingapi/views/votingapi_views_handler_field_value.inc 2010-03-02 11:43:13 +0000
4174@@ -0,0 +1,49 @@
4175+<?php
4176+// $Id: votingapi_views_handler_field_value.inc,v 1.1.2.3 2009/06/21 01:10:39 eaton Exp $
4177+
4178+/**
4179+ * @file
4180+ * Provide a views handlers for votingapi data fields.
4181+ */
4182+
4183+class votingapi_views_handler_field_value extends views_handler_field_numeric {
4184+ function construct() {
4185+ parent::construct();
4186+ $this->definition['float'] = TRUE;
4187+ }
4188+
4189+ function option_definition() {
4190+ $options = parent::option_definition();
4191+ $options['appearance'] = '';
4192+ return $options;
4193+ }
4194+
4195+ function options_form(&$form, &$form_state) {
4196+ parent::options_form($form, $form_state);
4197+
4198+ $appearances = module_invoke_all('votingapi_views_formatters', $this);
4199+ $options = array('' => t('Default appearance'));
4200+ $options += $appearances;
4201+
4202+ if (count($options) > 1) {
4203+ $form['appearance'] = array(
4204+ '#type' => 'select',
4205+ '#title' => t('Appearance'),
4206+ '#options' => $options,
4207+ '#default_value' => $this->options['appearance'],
4208+ '#weight' => -5,
4209+ );
4210+ }
4211+ }
4212+
4213+ function render($values) {
4214+ $value = $values->{$this->field_alias};
4215+ $function = $this->options['appearance'];
4216+ if (!empty($function) && function_exists($function)) {
4217+ return $function($value, $this, $values);
4218+ }
4219+ else {
4220+ return parent::render($values);
4221+ }
4222+ }
4223+}
4224
4225=== added file 'sites/all/modules/votingapi/views/votingapi_views_handler_relationship.inc'
4226--- sites/all/modules/votingapi/views/votingapi_views_handler_relationship.inc 1970-01-01 00:00:00 +0000
4227+++ sites/all/modules/votingapi/views/votingapi_views_handler_relationship.inc 2010-03-02 11:43:13 +0000
4228@@ -0,0 +1,214 @@
4229+<?php
4230+// $Id: votingapi_views_handler_relationship.inc,v 1.1.2.8 2009/08/15 17:58:06 eaton Exp $
4231+
4232+/**
4233+ * @file
4234+ * Provide views handler for votingapi joins.
4235+ */
4236+
4237+/**
4238+ * A custom join handler that connects arbitrary base tables to VotingAPI's data.
4239+ *
4240+ * The base relationship handler can only handle a single join. Some relationships
4241+ * are more complex and might require chains of joins; for those, you must
4242+ * utilize a custom relationship handler.
4243+ *
4244+ * Definition items:
4245+ * - base: The new base table this relationship will be adding. This does not
4246+ * have to be a declared base table, but if there are no tables that
4247+ * utilize this base table, it won't be very effective.
4248+ * - relationship table: The actual table this relationship operates against.
4249+ * This is analogous to using a 'table' override.
4250+ * - relationship field: The actual field this relationsihp operates against.
4251+ * This is analogous to using a 'real field' override.
4252+ * - label: The default label to provide for this relationship, which is
4253+ * shown in parentheses next to any field/sort/filter/argument that uses
4254+ * the relationship.
4255+ */
4256+class votingapi_views_handler_relationship extends views_handler_relationship {
4257+ function option_definition() {
4258+ $options = parent::option_definition();
4259+
4260+ $label = !empty($this->definition['label']) ? $this->definition['label'] : $this->definition['title'];
4261+ $options['label'] = array('default' => $label, 'translatable' => TRUE);
4262+
4263+ $options['votingapi']['value_type'] = array('default' => NULL);
4264+ $options['votingapi']['tag'] = array('default' => NULL);
4265+ if ($this->definition['base'] == 'votingapi_cache') {
4266+ $options['votingapi']['function'] = array('default' => NULL);
4267+ }
4268+ elseif ($this->definition['base'] == 'votingapi_vote') {
4269+ $options['current_user'] = array('default' => FALSE);
4270+ }
4271+
4272+ return $options;
4273+ }
4274+
4275+ /**
4276+ * Default options form that provides the label widget that all fields
4277+ * should have.
4278+ */
4279+ function options_form(&$form, &$form_state) {
4280+ parent::options_form($form, $form_state);
4281+
4282+ $options['value_types'][''] = t('No filtering');
4283+ $options['tags'][''] = t('No filtering');
4284+ $options['functions'][''] = t('No filtering');
4285+ foreach(votingapi_metadata() as $bin => $bin_data) {
4286+ foreach ($bin_data as $key => $data) {
4287+ $options[$bin][$key] = $data['name'];
4288+ }
4289+ }
4290+
4291+ $form['votingapi'] = array(
4292+ '#type' => 'fieldset',
4293+ '#collapsible' => FALSE,
4294+ '#title' => t('Data filters'),
4295+ '#description' => t('For each piece of content, many pieces of voting data may be saved. Use these options to specify exactly which types should be available via this relationship. <strong>Warning!</strong> Leaving any of these filters empty may result in multiple copies of each piece of content being displayed in listings.'),
4296+ '#tree' => TRUE,
4297+ );
4298+
4299+ $default = $this->options['votingapi']['value_type'];
4300+ if (!isset($options['value_types'][$default])) {
4301+ $default = '**OTHER**';
4302+ }
4303+ $form['votingapi']['value_type'] = array(
4304+ '#title' => t('Value type'),
4305+ '#type' => 'select',
4306+ '#options' => $options['value_types'],
4307+ '#default_value' => $default,
4308+ );
4309+ $form['votingapi']['value_type']['#options']['**OTHER**'] = t(' -Other- ');
4310+ $form['votingapi']['value_type_other'] = array(
4311+ '#type' => 'textfield',
4312+ '#default_value' => $this->options['votingapi']['value_type'],
4313+ '#process' => array('views_process_dependency'),
4314+ '#dependency' => array('edit-options-votingapi-value-type' => array('**OTHER**')),
4315+ );
4316+
4317+ $default = $this->options['votingapi']['tag'];
4318+ if (!isset($options['tags'][$default])) {
4319+ $default = '**OTHER**';
4320+ }
4321+ $form['votingapi']['tag'] = array(
4322+ '#title' => t('Vote tag'),
4323+ '#type' => 'select',
4324+ '#options' => $options['tags'],
4325+ '#default_value' => $default,
4326+ );
4327+ $form['votingapi']['tag']['#options']['**OTHER**'] = t(' -Other- ');
4328+ $form['votingapi']['tag_other'] = array(
4329+ '#type' => 'textfield',
4330+ '#default_value' => $this->options['votingapi']['tag'],
4331+ '#process' => array('views_process_dependency'),
4332+ '#dependency' => array('edit-options-votingapi-tag' => array('**OTHER**')),
4333+ );
4334+
4335+ if ($this->definition['base'] == 'votingapi_cache') {
4336+ $default = $this->options['votingapi']['function'];
4337+ if (!isset($options['functions'][$default])) {
4338+ $default = '**OTHER**';
4339+ }
4340+ $form['votingapi']['function'] = array(
4341+ '#title' => t('Aggregation function'),
4342+ '#type' => 'select',
4343+ '#options' => $options['functions'],
4344+ '#default_value' => $default,
4345+ );
4346+ $form['votingapi']['function']['#options']['**OTHER**'] = t(' -Other- ');
4347+ $form['votingapi']['function_other'] = array(
4348+ '#type' => 'textfield',
4349+ '#default_value' => $this->options['votingapi']['function'],
4350+ '#process' => array('views_process_dependency'),
4351+ '#dependency' => array('edit-options-votingapi-function' => array('**OTHER**')),
4352+ );
4353+ }
4354+ else {
4355+ $form['current_user'] = array(
4356+ '#title' => t('Restrict to current user'),
4357+ '#type' => 'checkbox',
4358+ '#return_value' => TRUE,
4359+ '#default_value' => $this->options['current_user'],
4360+ );
4361+ }
4362+ }
4363+
4364+ /**
4365+ * Perform any necessary changes to the form values prior to storage.
4366+ * There is no need for this function to actually store the data.
4367+ */
4368+ function options_submit($form, &$form_state) {
4369+ $v = $form_state['values']['options']['votingapi'];
4370+ if ($v['value_type'] == '**OTHER**') {
4371+ $form_state['values']['options']['votingapi']['value_type'] = $v['value_type_other'];
4372+ }
4373+
4374+ if ($v['tag'] == '**OTHER**') {
4375+ $form_state['values']['options']['votingapi']['tag'] = $v['tag_other'];
4376+ }
4377+
4378+ if ($v['function'] == '**OTHER**') {
4379+ $form_state['values']['options']['votingapi']['function'] = $v['function_other'];
4380+ }
4381+
4382+ foreach (array('function_other', 'value_type_other', 'tag_other') as $key) {
4383+ unset($form_state['values']['options']['votingapi'][$key]);
4384+ }
4385+ }
4386+
4387+ /**
4388+ * Called to implement a relationship in a query.
4389+ */
4390+ function query() {
4391+ // Figure out what base table this relationship brings to the party.
4392+ $table_data = views_fetch_data($this->definition['base']);
4393+
4394+ $def = $this->definition;
4395+ $def['table'] = $this->definition['base'];
4396+ $def['field'] = 'content_id';
4397+ $def['left_table'] = $this->relationship ? $this->relationship : $this->table;
4398+ $def['left_field'] = $this->field;
4399+ if (!empty($this->options['required'])) {
4400+ $def['type'] = 'INNER';
4401+ }
4402+
4403+ if (!empty($def['join_handler']) && class_exists($def['join_handler'])) {
4404+ $join = new $def['join_handler'];
4405+ }
4406+ else {
4407+ $join = new views_join();
4408+ }
4409+
4410+ // use a short alias for this:
4411+ $alias = $def['table'] . '_' . $def['left_table'];
4412+
4413+ if (!empty($this->options['votingapi'])) {
4414+ foreach ($this->options['votingapi'] as $field => $value) {
4415+ if (!empty($value)) {
4416+ $def['extra'][] = array(
4417+ 'field' => $field,
4418+ 'value' => $value,
4419+ 'numeric' => FALSE
4420+ );
4421+ $alias .= '_'. $value;
4422+ }
4423+ }
4424+ }
4425+
4426+ if (!empty($this->options['current_user'])) {
4427+ $def['extra'][] = array(
4428+ 'field' => 'uid',
4429+ 'value' => '***CURRENT_USER***',
4430+ 'numeric' => FALSE
4431+ );
4432+ $alias .= '_curuser';
4433+ }
4434+
4435+ $join->definition = $def;
4436+ $join->construct();
4437+
4438+ $this->ensure_my_table();
4439+
4440+ $this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship);
4441+ }
4442+}
4443
4444=== added file 'sites/all/modules/votingapi/votingapi.admin.inc'
4445--- sites/all/modules/votingapi/votingapi.admin.inc 1970-01-01 00:00:00 +0000
4446+++ sites/all/modules/votingapi/votingapi.admin.inc 2010-03-02 11:43:13 +0000
4447@@ -0,0 +1,155 @@
4448+<?php
4449+// $Id: votingapi.admin.inc,v 1.1.2.3 2009/05/26 03:50:00 eaton Exp $
4450+
4451+/**
4452+ * @file
4453+ * Configuration forms and helper functions for VotingAPI module.
4454+ */
4455+
4456+/**
4457+ * Administrative settings for VotingAPI.
4458+ */
4459+function votingapi_settings_form($form_state) {
4460+ $period = array(0 => t('Immediately')) + drupal_map_assoc(array(300, 900, 1800, 3600, 10800, 21600, 32400, 43200, 86400, 172800, 345600, 604800), 'format_interval') + array(-1 => t('Never'));
4461+
4462+ $form['votingapi_anonymous_window'] = array(
4463+ '#type' => 'select',
4464+ '#title' => t('Anonymous vote rollover'),
4465+ '#description' => t('The amount of time that must pass before two anonymous votes from the same computer are considered unique. Setting this to \'never\' will eliminate most double-voting, but will make it impossible for multiple anonymous on the same computer (like internet cafe customers) from casting votes.'),
4466+ '#default_value' => variable_get('votingapi_anonymous_window', 86400),
4467+ '#options' => $period
4468+ );
4469+
4470+ $form['votingapi_calculation_schedule'] = array(
4471+ '#type' => 'radios',
4472+ '#title' => t('Vote tallying'),
4473+ '#description' => t('On high-traffic sites, administrators can use this setting to postpone the calculation of vote results.'),
4474+ '#default_value' => variable_get('votingapi_calculation_schedule', 'immediate'),
4475+ '#options' => array(
4476+ 'immediate' => t('Tally results whenever a vote is cast'),
4477+ 'cron' => t('Tally results at cron-time'),
4478+ 'manual' => t('Do not tally results automatically: I am using a module that manages its own vote results.')
4479+ ),
4480+ );
4481+
4482+ return system_settings_form($form);
4483+}
4484+
4485+/**
4486+ * Developer tool to generate dummy votes.
4487+ */
4488+function votingapi_generate_votes_form() {
4489+ $types = node_get_types();
4490+ foreach ($types as $type) {
4491+ $options[$type->type] = t($type->name);
4492+ }
4493+
4494+ $form['node_types'] = array(
4495+ '#type' => 'checkboxes',
4496+ '#title' => t('Which node types should receive votes?'),
4497+ '#options' => $options,
4498+ '#default_value' => array_keys($options),
4499+ );
4500+
4501+ $form['vote_type'] = array(
4502+ '#type' => 'select',
4503+ '#title' => t('What type of votes should be generated?'),
4504+ '#options' => array(
4505+ 'five' => t('Fivestar style'),
4506+ 'flag' => t('Digg style'),
4507+ 'updown' => t('Reddit style'),
4508+ ),
4509+ '#default_value' => 'percent',
4510+ );
4511+
4512+ $form['kill_votes'] = array(
4513+ '#type' => 'checkbox',
4514+ '#title' => t('Delete existing votes before generating new ones.'),
4515+ '#default_value' => FALSE,
4516+ );
4517+ $form['submit'] = array(
4518+ '#type' => 'submit',
4519+ '#value' => t('Do it!'),
4520+ );
4521+ return $form;
4522+}
4523+
4524+/**
4525+ * Submit handler for votingapi_generate_votes_form.
4526+ */
4527+function votingapi_generate_votes_form_submit($form, &$form_state) {
4528+ votingapi_generate_votes($form_state['values']['node_types'], $form_state['values']['vote_type'], $form_state['values']['kill_votes']);
4529+}
4530+
4531+/**
4532+ * Utility function to generate votes.
4533+ */
4534+function votingapi_generate_votes($node_types, $vote_type, $kill_votes = FALSE) {
4535+ module_load_include('inc', 'devel_generate');
4536+
4537+ if ($kill_votes) {
4538+ db_query("TRUNCATE TABLE {votingapi_vote}");
4539+ db_query("TRUNCATE TABLE {votingapi_cache}");
4540+ }
4541+
4542+ $uids = devel_get_users();
4543+ $nids = array();
4544+
4545+ $sql = "SELECT n.nid, n.created FROM {node} n ";
4546+ $sql .= "WHERE n.type IN (" . db_placeholders($node_types, 'varchar') . ") ";
4547+ $sql .= "ORDER BY n.created DESC";
4548+
4549+ $results = db_query($sql, $node_types);
4550+ while ($node = db_fetch_array($results)) {
4551+ $nids[$node['nid']] = $node['created'];
4552+ }
4553+
4554+ foreach ($nids as $nid => $timestamp) {
4555+ _votingapi_cast_votes($nid, $timestamp, $uids, $vote_type);
4556+ }
4557+}
4558+
4559+/**
4560+ * Utility function to generate votes on a node by a set of users.
4561+ */
4562+function _votingapi_cast_votes($nid, $timestamp, $uids, $style) {
4563+ $votes = array();
4564+ foreach ($uids as $uid) {
4565+ switch ($style) {
4566+ case 'five':
4567+ if (rand(0, 2)) {
4568+ $votes[] = array(
4569+ 'uid' => $uid,
4570+ 'content_id' => $nid,
4571+ 'value_type' => 'percent',
4572+ 'timestamp' => time() - rand(0, time() - $timestamp),
4573+ 'value' => rand(0, 5) * 20,
4574+ );
4575+ }
4576+ break;
4577+ case 'flag':
4578+ if (rand(0, 1)) {
4579+ $votes[] = array(
4580+ 'uid' => $uid,
4581+ 'content_id' => $nid,
4582+ 'value_type' => 'points',
4583+ 'timestamp' => time() - rand(0, time() - $timestamp),
4584+ 'value' => 1,
4585+ );
4586+ }
4587+ break;
4588+ case 'updown':
4589+ if (rand(0, 3)) {
4590+ $votes[] = array(
4591+ 'uid' => $uid,
4592+ 'content_id' => $nid,
4593+ 'value_type' => 'points',
4594+ 'timestamp' => time() - rand(0, time() - $timestamp),
4595+ 'value' => rand(0, 1) ? 1 : -1,
4596+ );
4597+ }
4598+ break;
4599+ }
4600+ }
4601+ votingapi_set_votes($votes, array());
4602+}
4603
4604=== added file 'sites/all/modules/votingapi/votingapi.api.php'
4605--- sites/all/modules/votingapi/votingapi.api.php 1970-01-01 00:00:00 +0000
4606+++ sites/all/modules/votingapi/votingapi.api.php 2010-03-02 11:43:13 +0000
4607@@ -0,0 +1,164 @@
4608+<?php
4609+// $Id: votingapi.api.php,v 1.1.2.1 2009/06/24 18:57:26 eaton Exp $
4610+
4611+/**
4612+ * @file
4613+ * Provides hook documentation for the VotingAPI module.
4614+ */
4615+
4616+
4617+/**
4618+ * Adds to or changes the calculated vote results for a piece of content.
4619+ *
4620+ * VotingAPI calculates a number of common aggregate functions automatically,
4621+ * including the average vote and total number of votes cast. Results are grouped
4622+ * by 'tag', 'value_type', and then 'function' in the following format:
4623+ *
4624+ * $results[$tag][$value_type][$aggregate_function] = $value;
4625+ *
4626+ * If no custom tag is being used for votes, the catch-all "vote" tag should be
4627+ * used. In cases where custom tags are used to vote on different aspects of a
4628+ * piece of content, a catch-all "vote" value should still be calculated for use
4629+ * on summary screens, etc.
4630+ *
4631+ * @param $results
4632+ * An alterable array of aggregatre vote results.
4633+ * @param $content_type
4634+ * A string identifying the type of content being rated. Node, comment,
4635+ * aggregator item, etc.
4636+ * @param $content_id
4637+ * The key ID of the content being rated.
4638+ *
4639+ * @see votingapi_recalculate_results()
4640+ */
4641+function hook_votingapi_results_alter(&$results, $content_type, $content_id) {
4642+ // We're using a MySQLism (STDDEV isn't ANSI SQL), but it's OK because this is
4643+ // an example. And no one would ever base real code on sample code. Ever. Never.
4644+
4645+ $sql = "SELECT v.tag, STDDEV(v.value) as standard_deviation ";
4646+ $sql .= "FROM {votingapi_vote} v ";
4647+ $sql .= "WHERE v.content_type = '%s' AND v.content_id = %d AND v.value_type = 'percent' ";
4648+ $sql .= "GROUP BY v.tag";
4649+
4650+ $results = db_query($sql, $content_type, $content_id);
4651+
4652+ // VotingAPI wants the data in the following format:
4653+ // $cache[$tag][$value_type][$aggregate_function] = $value;
4654+
4655+ while ($result = db_fetch_array($results)) {
4656+ $cache[$result['tag']]['percent']['standard_deviation'] = $result['standard_deviation'];
4657+ }
4658+}
4659+
4660+
4661+/**
4662+ * Adds to or alters metadata describing Voting tags, value_types, and functions.
4663+ *
4664+ * If your module uses custom tags or value_types, or calculates custom aggregate
4665+ * functions, please implement this hook so other modules can properly interperet
4666+ * and display your data.
4667+ *
4668+ * Three major bins of data are stored: tags, value_types, and aggregate result
4669+ * functions. Each entry in these bins is keyed by the value stored in the actual
4670+ * VotingAPI tables, and contains an array with (minimally) 'name' and
4671+ * 'description' keys. Modules can add extra keys to their entries if desired.
4672+ *
4673+ * @param $data
4674+ * An alterable array of aggregate vote results.
4675+ *
4676+ * @see votingapi_metadata()
4677+ */
4678+function hook_votingapi_metadata_alter(&$data) {
4679+ // Document several custom tags for rating restaurants and meals.
4680+ $data['tags']['bread'] = array(
4681+ 'name' => t('Bread'),
4682+ 'description' => t('The quality of the food at a restaurant.'),
4683+ 'module' => 'mymodule', // This is optional; we can add it for our own purposes.
4684+ );
4685+ $data['tags']['circuses'] = array(
4686+ 'name' => t('Circuses'),
4687+ 'description' => t('The quality of the presentation and atmosphere at a restaurant.'),
4688+ 'module' => 'mymodule',
4689+ );
4690+
4691+ // Document two custom aggregate function.
4692+ $data['functions']['standard_deviation'] = array(
4693+ 'name' => t('Standard deviation'),
4694+ 'description' => t('The standard deviation of all votes cast on a given piece of content. Use this to find controversial content.'),
4695+ 'module' => 'mymodule',
4696+ );
4697+ $data['functions']['median'] = array(
4698+ 'name' => t('Median vote'),
4699+ 'description' => t('The median vote value cast on a given piece of content. More accurate than a pure average when there are a few outlying votes.'),
4700+ 'module' => 'mymodule',
4701+ );
4702+}
4703+
4704+/**
4705+ * Return metadata used to build Views relationships on voting data.
4706+ *
4707+ * VotingAPI can store votes on any entity in the Drupal database: its content_type
4708+ * and content_id columns can be used to store "node"/1, "comment"/2, and so
4709+ * on. This hook is used to tell VotingAPI what Views base table the content_type
4710+ * field corresponds to, and what field in that base table contains the value in
4711+ * votingapi's content_id table.
4712+ *
4713+ * @return
4714+ * An array of records containing 'description', 'content_type', 'base_table',
4715+ * and 'content_id_column' entries.
4716+ */
4717+function hook_votingapi_relationships() {
4718+ $relationships[] = array(
4719+ // 'description' is used to construct the field description in the Views UI.
4720+ 'description' => t('users'),
4721+
4722+ // 'content_type' contain the value that your module stores in the voting
4723+ // api 'content_type' column. 'node', 'comment', etc.
4724+ 'content_type' => 'user',
4725+
4726+ // 'base_table' contain the name of the Views base table that stores the
4727+ // data your votes apply to.
4728+ 'base_table' => 'user',
4729+
4730+ // 'content_id_column' contains the name of the views field that represents
4731+ // your base_table's primary key. This column will be joined against the
4732+ // voting api 'content_id' column.
4733+ 'content_id_column' => 'uid',
4734+
4735+ // VotingAPI constructs pseudo-tables so that multiple relationships can
4736+ // point to the same base table (normal and translation-based votes nodes
4737+ // for example. These two columns allow you to override the names of the
4738+ // pseudo-tables. You probably don't need to change this part unless you're
4739+ // nedjo.
4740+ 'pseudo_vote' => 'votingapi_vote_special',
4741+ 'pseudo_cache' => 'votingapi_cache_special',
4742+ );
4743+}
4744+
4745+
4746+
4747+/**
4748+ * Returns callback functions and descriptions to format a VotingAPI Views field.
4749+ *
4750+ * Loads all votes for a given piece of content, then calculates and caches the
4751+ * aggregate vote results. This is only intended for modules that have assumed
4752+ * responsibility for the full voting cycle: the votingapi_set_vote() function
4753+ * recalculates automatically.
4754+ *
4755+ * @param $field
4756+ * A Views field object. This can be used to expose formatters only for tags,
4757+ * vote values, aggregate functions, etc.
4758+ * @return
4759+ * An array of key-value pairs, in which each key is a callback function and
4760+ * each value is a human-readable description of the formatter.
4761+ *
4762+ * @see votingapi_set_votes()
4763+ */
4764+function hook_votingapi_views_formatters($field) {
4765+ if ($field->field == 'value') {
4766+ return array('mymodule_funky_formatter' => t('MyModule value formatter'));
4767+ }
4768+ if ($field->field == 'tag') {
4769+ return array('mymodule_funky_tags' => t('MyModule tag formatter'));
4770+ }
4771+}
4772
4773=== added file 'sites/all/modules/votingapi/votingapi.info'
4774--- sites/all/modules/votingapi/votingapi.info 1970-01-01 00:00:00 +0000
4775+++ sites/all/modules/votingapi/votingapi.info 2010-03-02 11:43:13 +0000
4776@@ -0,0 +1,11 @@
4777+; $Id: votingapi.info,v 1.4 2007/07/06 03:02:34 eaton Exp $
4778+name = Voting API
4779+description = Provides a shared voting API for other modules.
4780+package = Voting
4781+core = 6.x
4782+; Information added by drupal.org packaging script on 2009-08-15
4783+version = "6.x-2.3"
4784+core = "6.x"
4785+project = "votingapi"
4786+datestamp = "1250359559"
4787+
4788
4789=== added file 'sites/all/modules/votingapi/votingapi.install'
4790--- sites/all/modules/votingapi/votingapi.install 1970-01-01 00:00:00 +0000
4791+++ sites/all/modules/votingapi/votingapi.install 2010-03-02 11:43:13 +0000
4792@@ -0,0 +1,299 @@
4793+<?php
4794+// $Id: votingapi.install,v 1.21.4.11 2009/06/29 14:05:01 eaton Exp $
4795+
4796+/**
4797+ * @file
4798+ * Installation file for VotingAPI module.
4799+ */
4800+
4801+function votingapi_schema() {
4802+ $schema['votingapi_vote'] = array(
4803+ 'fields' => array(
4804+ 'vote_id' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
4805+ 'content_type' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'node'),
4806+ 'content_id' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
4807+ 'value' => array('type' => 'float', 'not null' => TRUE, 'default' => 0),
4808+ 'value_type' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'percent'),
4809+ 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'vote'),
4810+ 'uid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
4811+ 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
4812+ 'vote_source' => array('type' => 'varchar', 'length' => 255),
4813+ ),
4814+ 'primary key' => array('vote_id'),
4815+ 'indexes' => array(
4816+ 'content_uid' => array('content_type', 'content_id', 'uid'),
4817+ 'content_uid_2' => array('content_type', 'uid'),
4818+ 'content_source' => array('content_type', 'content_id', 'vote_source'),
4819+ 'content_value_tag' => array('content_type', 'content_id', 'value_type', 'tag'),
4820+ ),
4821+ );
4822+ $schema['votingapi_cache'] = array(
4823+ 'fields' => array(
4824+ 'vote_cache_id' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
4825+ 'content_type' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'node'),
4826+ 'content_id' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
4827+ 'value' => array('type' => 'float', 'not null' => TRUE, 'default' => 0),
4828+ 'value_type' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'percent'),
4829+ 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => 'vote'),
4830+ 'function' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''),
4831+ 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
4832+ ),
4833+ 'primary key' => array('vote_cache_id'),
4834+ 'indexes' => array(
4835+ 'content' => array('content_type', 'content_id'),
4836+ 'content_function' => array('content_type', 'content_id', 'function'),
4837+ 'content_tag_func' => array('content_type', 'content_id', 'tag', 'function'),
4838+ 'content_vtype_tag' => array('content_type', 'content_id', 'value_type', 'tag'),
4839+ 'content_vtype_tag_func' => array('content_type', 'content_id', 'value_type', 'tag', 'function'),
4840+ ),
4841+ );
4842+ return $schema;
4843+}
4844+
4845+function votingapi_install() {
4846+ drupal_install_schema('votingapi');
4847+}
4848+
4849+function votingapi_uninstall() {
4850+ drupal_uninstall_schema('votingapi');
4851+}
4852+
4853+/**
4854+ * UTF8 update
4855+ */
4856+function votingapi_update_1() {
4857+ return _system_update_utf8(array('votingapi_vote', 'votingapi_cache'));
4858+}
4859+
4860+/**
4861+ * Value fields changed to signed floats.
4862+ */
4863+function votingapi_update_2() {
4864+ $ret = array();
4865+ db_drop_primary_key($ret, 'votingapi_cache');
4866+ db_drop_primary_key($ret, 'votingapi_vote');
4867+ db_change_field($ret, votingapi_cache, value, value, array('type' => 'float', 'default' => NULL), array('primary_key' => array('vote_cache_id')));
4868+ db_change_field($ret, votingapi_vote, value, value, array('type' => 'float', 'default' => NULL), array('primary_key' => array('vote_id')));
4869+ return $ret;
4870+}
4871+
4872+/**
4873+ * Value fields changed to signed floats.
4874+ */
4875+function votingapi_update_3() {
4876+ $ret = array();
4877+ switch ($GLOBALS['db_type']) {
4878+ case 'mysql':
4879+ case 'mysqli':
4880+ $ret[] = update_sql("UPDATE {votingapi_cache} SET value_type = 'percent' WHERE value_type = '1'");
4881+ $ret[] = update_sql("UPDATE {votingapi_cache} SET value_type = 'points' WHERE value_type = '2'");
4882+ $ret[] = update_sql("UPDATE {votingapi_cache} SET value_type = 'option' WHERE value_type = '3'");
4883+
4884+ $ret[] = update_sql("UPDATE {votingapi_vote} SET value_type = 'percent' WHERE value_type = '1'");
4885+ $ret[] = update_sql("UPDATE {votingapi_vote} SET value_type = 'points' WHERE value_type = '2'");
4886+ $ret[] = update_sql("UPDATE {votingapi_vote} SET value_type = 'option' WHERE value_type = '3'");
4887+ break;
4888+ }
4889+
4890+ return $ret;
4891+}
4892+
4893+/**
4894+ * Initial work to roll Voting Actions functionality into Voting API.
4895+ */
4896+
4897+function votingapi_update_4() {
4898+ $ret = array();
4899+ $name = 'votingapi_action_set';
4900+ $table_action_step = array(
4901+ 'fields' => array(
4902+ 'vasid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
4903+ 'parent' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
4904+ 'required' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
4905+ 'mask' => array('type' => 'varchar', 'default' => 'AND'),
4906+ 'content_type' => array('type' => 'varchar', 'length' => 20, 'default' => NULL),
4907+ 'name' => array('type' => 'varchar', 'length' => 128, 'default' => NULL),
4908+ 'enabled' => array('type' => 'int', 'default' => 1),
4909+ 'source' => array('type' => 'varchar', 'length' => 65, 'default' => NULL),
4910+ 'weight' => array('type' => 'int', 'length' => 10, 'not null' => TRUE, 'default' => 0),
4911+ ),
4912+ 'primary key' => array('vasid'),
4913+ );
4914+ db_create_table($ret, $name, $table_action_set);
4915+
4916+ $name = 'votingapi_action_condition';
4917+ $table_action_condition = array(
4918+ 'fields' => array(
4919+ 'vacid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
4920+ 'vasid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE),
4921+ 'weight' => array('type' => 'int', 'length' => 10, 'not null' => TRUE, 'default' => 0),
4922+ 'value' => array('type' => 'varchar', 'length' => 255),
4923+ 'handler' => array('type' => 'varchar', 'length' => 255),
4924+ ),
4925+ 'primary key' => array('vacid'),
4926+ );
4927+ db_create_table($ret, $name, $table_action_condition);
4928+
4929+ $name = 'votingapi_action';
4930+ $table_votingapi_action = array(
4931+ 'fields' => array(
4932+ 'vasid' => array('type' => 'int', 'length' => 10, 'unsigned' => TRUE, 'not null' => TRUE),
4933+ 'aid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 0),
4934+ ),
4935+ );
4936+ db_create_table($ret, $name, $table_votingapi_action);
4937+
4938+ return $ret;
4939+}
4940+
4941+/**
4942+ * Fixed index definition, corrected table prefixes.
4943+ */
4944+
4945+function votingapi_update_5() {
4946+ $ret = array();
4947+ db_add_index($ret, '{votingapi_vote}', 'content', array('content_type', 'content_id'));
4948+ db_add_index($ret, '{votingapi_cache}', 'content', array('content_type', 'content_id'));
4949+
4950+ db_drop_index($ret, '{votingapi_vote}', 'content_type');
4951+ db_drop_index($ret, '{votingapi_vote}', 'content_id');
4952+ db_drop_index($ret, '{votingapi_cache}', 'content_type');
4953+ db_drop_index($ret, '{votingapi_cache}', 'content_id');
4954+
4955+ db_rename_table($ret, 'votingapi_action_condition', '{votingapi_action_condition}');
4956+ db_rename_table($ret, 'votingapi_action', '{votingapi_action}');
4957+
4958+ switch ($GLOBALS['db_type']) {
4959+ case 'mysql':
4960+ case 'mysqli':
4961+ $ret[] = update_sql("UPDATE {sequences} SET name = '{votingapi_cache}' WHERE name = 'votingapi_cache';");
4962+ $ret[] = update_sql("UPDATE {sequences} SET name = '{votingapi_vote}' WHERE name = 'votingapi_vote';");
4963+ break;
4964+ }
4965+
4966+ return $ret;
4967+}
4968+
4969+function votingapi_update_6() {
4970+ $ret = array();
4971+
4972+ db_add_column($ret, 'votingapi_action_condition', 'name', 'varchar', array('length' => 128));
4973+ db_add_column($ret, 'votingapi_action_set', 'name', 'description', array('length' => 255, 'not null' => TRUE));
4974+ db_change_column($ret, 'votingapi_action_condition', 'value', 'data', 'varchar', array('length' => 255));
4975+
4976+ return $ret;
4977+}
4978+
4979+
4980+function votingapi_update_7() {
4981+ // There are quite a few changes. Let's just take the easy way and nuke this puppy.
4982+ // Nothing has been using the tables up to this point, anyhow.
4983+
4984+ db_drop_table($ret, 'votingapi_action_set');
4985+ db_drop_table($ret, 'votingapi_action_condition');
4986+ db_drop_table($ret, 'votingapi_action');
4987+
4988+ $name = 'votingapi_action_set';
4989+ $table_votingapi_action_set = array(
4990+ 'fields' => array(
4991+ 'name' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE),
4992+ 'parent_name' => array('type' => 'varchar', 'default' => NULL),
4993+ 'content_type' => array('type' => 'varchar', 'length' => 20, 'default' => NULL),
4994+ 'source' => array('type' => 'varchar', 'length' => 64, 'default' => NULL),
4995+ 'description' => array('type' => 'varchar', 'length' => 255, 'default' => NULL),
4996+ 'required' => array('type' => 'int', 'length' => 8, 'not null' => TRUE, 'default' => 0),
4997+ 'criteria_mask' => array('type' => 'varchar', 'length' => 8, 'default' => 'AND'),
4998+ 'weight' => array('type' => 'int', 'length' => 10, 'not null' => TRUE, 'default' => 0),
4999+ ),
5000+ 'primary_key' => array('name'),
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: