├── tagadelic.install ├── templates └── tagadelic_taxonomy_cloud.tpl.php ├── tagadelic.info ├── lib └── Drupal │ └── tagadelic │ └── Tests │ ├── TagadelicTaxonomyTestBase.php │ ├── TagadelicTestBase.php │ ├── TagadelicTestCase.php │ ├── TagadelicTaxonomyAdminWebTestCase.php │ └── TagadelicTaxonomyTestCase.php ├── tagadelic_taxonomy.info ├── .travis.yml ├── tagadelic.css ├── tests ├── support │ └── FakeDrupal.php ├── TagadelicTagTest.php ├── TagadelicTagToStringTest.php ├── TagadelicDrupalWrapperTest.php ├── TagadelicTagMethodsTest.php └── TagadelicCloudTest.php ├── tagadelic.module ├── TagadelicDrupalWrapper.php ├── TagadelicCloud.php ├── tagadelic_taxonomy.module ├── TagadelicTag.php └── README.md /tagadelic.install: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/tagadelic_taxonomy_cloud.tpl.php: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /tagadelic.info: -------------------------------------------------------------------------------- 1 | name = Tagadelic 2 | description = "Tagadelic makes weighted tag clouds from any-ol' weighted list." 3 | core = 7.x 4 | dependencies[] = taxonomy 5 | package = "Taxonomy" 6 | 7 | ;files[] = tests/tagadelic_test_base.test 8 | ;files[] = tests/tagadelic.test 9 | files[] = tagadelic.module 10 | files[] = tagadelic.install 11 | 12 | files[] = TagadelicCloud.php 13 | files[] = TagadelicTag.php 14 | files[] = TagadelicDrupalWrapper.php 15 | -------------------------------------------------------------------------------- /lib/Drupal/tagadelic/Tests/TagadelicTaxonomyTestBase.php: -------------------------------------------------------------------------------- 1 | $arg) { 26 | if (is_array($arg) && !empty($arg)) { 27 | $args[$id] = serialize($arg); 28 | } 29 | elseif (is_array($arg) && empty($arg)) { 30 | $args[$id] = "Array"; 31 | } 32 | } 33 | $arglist = join(',', $args); 34 | return "{$func}({$arglist})"; 35 | } 36 | //@codeCoverageIgnoreEnd 37 | -------------------------------------------------------------------------------- /tests/TagadelicTagTest.php: -------------------------------------------------------------------------------- 1 | drupal = $this->getMock("TagadelicDrupalWrapper", array("check_plain", "l")); 21 | $this->drupal->expects($this->any()) 22 | ->method('check_plain') 23 | ->will($this->returnArgument(0)); 24 | $this->object = new TagadelicTag(42, "blackbeard", 2); 25 | $this->object->set_drupal($this->drupal); 26 | } 27 | 28 | /** 29 | * Tears down the fixture, for example, closes a network connection. 30 | * This method is called after a test is executed. 31 | */ 32 | protected function tearDown() { 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Drupal/tagadelic/Tests/TagadelicTestCase.php: -------------------------------------------------------------------------------- 1 | "Tagadelic Test", 14 | "description" => "Tests if Tagadelic correctly adds the libraries", 15 | "group" => "Tagadelic", 16 | ); 17 | } 18 | 19 | /** 20 | * testAutoloader Tests if classes are autoloaded. 21 | * @covers TagadelicCloud, TagadelicTag, TagaDelicDrupalWrapper. 22 | * @scope public 23 | */ 24 | public function testAutoloader() { 25 | $tag = new \TagadelicTag(12, "jane", 2); 26 | $cloud = new \TagadelicCloud(1337); 27 | $drupal = new \TagaDelicDrupalWrapper(); 28 | 29 | $this->assertEqual("TagadelicTag" , get_class($tag)); 30 | $this->assertEqual("TagadelicCloud" , get_class($cloud)); 31 | $this->assertEqual("TagadelicDrupalWrapper" , get_class($drupal)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tagadelic.module: -------------------------------------------------------------------------------- 1 | 6 | * @link http://berk.es 7 | */ 8 | 9 | /** 10 | ____________________________________ 11 | / This is an empty-ish file. Because \ 12 | \ Drupal needs a .module / 13 | ------------------------------------ 14 | \ / \ //\ 15 | \ |\___/| / \// \\ 16 | /0 0 \__ / // | \ \ 17 | / / \/_/ // | \ \ 18 | @_^_@'/ \/_ // | \ \ 19 | //_^_/ \/_ // | \ \ 20 | ( //) | \/// | \ \ 21 | ( / /) _|_ / ) // | \ _\ 22 | ( // /) '/,_ _ _/ ( ; -. | _ _\.-~ .-~~~^-. 23 | (( / / )) ,-{ _ `-.|.-~-. .~ `. 24 | (( // / )) '/\ / ~-. _ .-~ .-~^-. \ 25 | (( /// )) `. { } / \ \ 26 | (( / )) .----~-.\ \-' .~ \ `. \^-. 27 | ///.----..> \ _ -~ `. ^-` ^-_ 28 | ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~ 29 | /.-~ 30 | */ 31 | -------------------------------------------------------------------------------- /TagadelicDrupalWrapper.php: -------------------------------------------------------------------------------- 1 | drupal->expects($this->once()) 19 | ->method('l') 20 | ->will($this->returnValue("blackbeard")); 21 | } 22 | /** 23 | * @covers TagadelicTag::__ToString 24 | */ 25 | public function test__ToString() { 26 | $this->assertTag(array("tag" => "a", "content" => "blackbeard"), $this->object->__ToString()); 27 | } 28 | 29 | /** 30 | * @covers tagadelictag::__tostring 31 | */ 32 | public function test__ToStringHasLink() { 33 | $link = '/foo/bar'; 34 | $this->object->set_link($link); 35 | 36 | $this->drupal->expects($this->any()) 37 | ->method('l') 38 | ->with( 39 | $this->anything(), 40 | $this->equalto($link), 41 | $this->anything()); 42 | 43 | $this->object->__tostring(); 44 | } 45 | 46 | /** 47 | * @covers tagadelictag::__tostring 48 | */ 49 | public function test__ToStringHasTitle() { 50 | $this->object->set_description("Foo Bar"); 51 | $expected_attrs = array("title" => "Foo Bar"); 52 | 53 | $this->drupal->expects($this->any()) 54 | ->method('l') 55 | ->with( 56 | $this->anything(), 57 | $this->anything(), 58 | $this->equalto(array("attributes" => $expected_attrs))) 59 | ->will($this->returnvalue("")); 60 | 61 | $this->object->__tostring(); 62 | } 63 | 64 | /** 65 | * @covers tagadelictag::__tostring 66 | */ 67 | public function test__ToStringHasNoTitle() { 68 | $this->object->set_description(""); 69 | 70 | $this->drupal->expects($this->any()) 71 | ->method('l') 72 | ->with( 73 | $this->anything(), 74 | $this->anything(), 75 | $this->equalto(array())) 76 | ->will($this->returnvalue("")); 77 | 78 | $this->object->__tostring(); 79 | } 80 | 81 | /** 82 | * @covers tagadelictag::__tostring 83 | */ 84 | public function test__ToStringHasWeight() { 85 | $this->object->set_weight(3); 86 | $expected_attrs = array("class" => array("level3")); 87 | 88 | $this->drupal->expects($this->any()) 89 | ->method('l') 90 | ->with( 91 | $this->anything(), 92 | $this->anything(), 93 | $this->equalto(array("attributes" => $expected_attrs))) 94 | ->will($this->returnvalue("")); 95 | 96 | $this->object->__tostring(); 97 | } 98 | 99 | /** 100 | * @covers tagadelictag::__tostring 101 | */ 102 | public function test__ToStringHasNoWeight() { 103 | $this->object->set_weight(0); 104 | 105 | $this->drupal->expects($this->any()) 106 | ->method('l') 107 | ->with( 108 | $this->anything(), 109 | $this->anything(), 110 | $this->equalto(array())) 111 | ->will($this->returnvalue("")); 112 | 113 | $this->object->__tostring(); 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /tests/TagadelicDrupalWrapperTest.php: -------------------------------------------------------------------------------- 1 | object = new TagadelicDrupalWrapper(); 17 | } 18 | 19 | /** 20 | * Tears down the fixture, for example, closes a network connection. 21 | * This method is called after a test is executed. 22 | */ 23 | protected function tearDown() { 24 | } 25 | 26 | /** 27 | * @covers TagadelicDrupalWrapper::cache_get 28 | */ 29 | public function testCache_get() { 30 | $this->assertTrue(method_exists($this->object, "cache_get")); 31 | $this->assertSame($this->object->cache_get(1337), "cache_get(1337,cache)"); 32 | $this->assertSame($this->object->cache_get(1337,"custom"), "cache_get(1337,custom)"); 33 | } 34 | 35 | /** 36 | * @expectedException PHPUnit_Framework_Error 37 | */ 38 | public function testCache_get_requires_cid() { 39 | $this->object->cache_get(); 40 | } 41 | 42 | /** 43 | * @covers TagadelicDrupalWrapper::cache_set 44 | */ 45 | public function testCache_set() { 46 | $this->assertTrue(method_exists($this->object, "cache_set")); 47 | $this->assertSame($this->object->cache_set(1337, "hello"), "cache_set(1337,hello,cache)"); 48 | $this->assertSame($this->object->cache_set(1337, "hello","custom"), "cache_set(1337,hello,custom)"); 49 | $this->assertSame($this->object->cache_set(1337, "hello","custom", 280602000), "cache_set(1337,hello,custom,280602000)"); 50 | } 51 | 52 | /** 53 | * @expectedException PHPUnit_Framework_Error 54 | */ 55 | public function testCache_set_requires_cid() { 56 | $this->object->cache_set(); 57 | } 58 | /** 59 | * @expectedException PHPUnit_Framework_Error 60 | */ 61 | public function testCache_set_requires_data() { 62 | $this->object->cache_set(1337); 63 | } 64 | 65 | /** 66 | * @covers TagadelicDrupalWrapper::check_plain 67 | */ 68 | public function testCheck_plain() { 69 | $this->assertTrue(method_exists($this->object, "check_plain")); 70 | $this->assertSame($this->object->check_plain("hello"), "check_plain(hello)"); 71 | } 72 | 73 | /** 74 | * @expectedException PHPUnit_Framework_Error 75 | */ 76 | public function testCheck_plain_requires_text() { 77 | $this->object->check_plain(); 78 | } 79 | 80 | public function testL() { 81 | $this->assertTrue(method_exists($this->object, "l")); 82 | $this->assertSame($this->object->l("text", "path"), "l(text,path,Array)"); 83 | 84 | $options = array("attributes"=>array("title"=>"foo")); 85 | $serialized = serialize($options); 86 | $this->assertSame($this->object->l("text", "path", $options),"l(text,path,{$serialized})"); 87 | } 88 | 89 | /** 90 | * @expectedException PHPUnit_Framework_Error 91 | */ 92 | public function testL_requires_text() { 93 | $this->object->l(); 94 | } 95 | /** 96 | * @expectedException PHPUnit_Framework_Error 97 | */ 98 | public function testL_requires_path() { 99 | $this->object->l("text"); 100 | } 101 | 102 | /** 103 | * @covers TagadelicDrupalWrapper::shuffle 104 | */ 105 | public function testShuffle() { 106 | $array_to_shuffle = array("a", "b"); 107 | $this->assertTrue(method_exists($this->object, "shuffle")); 108 | //Cannot test the method signature, because we cannot redeclare global "shuffle()" 109 | } 110 | 111 | /** 112 | * @expectedException PHPUnit_Framework_Error 113 | */ 114 | public function testShuffle_requires_array() { 115 | $this->object->shuffle(); 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /tests/TagadelicTagMethodsTest.php: -------------------------------------------------------------------------------- 1 | 2 | assertSame(42, $this->object->get_id()); 19 | } 20 | 21 | /** 22 | * @covers TagadelicTag::get_name 23 | */ 24 | public function testGet_name() { 25 | $this->assertSame("blackbeard", $this->object->get_name()); 26 | } 27 | 28 | /** 29 | * @covers TagadelicTag::get_description 30 | */ 31 | public function testGet_description() { 32 | $this->object->set_description("Foo Bar"); 33 | $this->assertSame("Foo Bar", $this->object->get_description()); 34 | } 35 | 36 | /** 37 | * @covers TagadelicTag::get_weight 38 | */ 39 | public function testGet_weight() { 40 | $this->object->set_weight(123); 41 | $this->assertSame(123, $this->object->get_weight()); 42 | } 43 | 44 | /** 45 | * @covers TagadelicTag::get_weight 46 | */ 47 | public function testGet_count() { 48 | $this->assertSame(2, $this->object->get_count()); 49 | } 50 | 51 | /** 52 | * @covers TagadelicTag::set_weight 53 | */ 54 | public function testSet_weight() { 55 | $this->object->set_weight(123); 56 | $this->assertAttributeSame(123, "weight", $this->object); 57 | } 58 | 59 | /** 60 | * @covers TagadelicTag::set_drupal 61 | */ 62 | public function testSet_drupal() { 63 | $drupal = $this->getMock("TagaDelicDrupalWrapper"); 64 | $this->object->set_drupal($drupal); 65 | $this->assertAttributeSame($drupal, "drupal", $this->object); 66 | } 67 | 68 | /** 69 | * @covers TagadelicTag::drupal 70 | */ 71 | public function testDrupal() { 72 | $drupal = $this->getMock("TagaDelicDrupalWrapper"); 73 | $this->object->set_drupal($drupal); 74 | $this->assertSame($this->object->drupal(), $drupal); 75 | } 76 | 77 | /** 78 | * @covers TagadelicTag::drupal 79 | */ 80 | public function testDrupalInstatiatesNewWrapper() { 81 | $this->object->set_drupal(NULL); 82 | $this->assertInstanceOf("TagaDelicDrupalWrapper", $this->object->drupal()); 83 | } 84 | 85 | /** 86 | * @covers TagadelicTag::set_description 87 | */ 88 | public function testSet_description() { 89 | $this->object->set_description("Foo Bar"); 90 | $this->assertAttributeSame("Foo Bar", "description", $this->object); 91 | } 92 | 93 | /** 94 | * @covers TagadelicTag::set_link 95 | */ 96 | public function testSet_link() { 97 | $this->object->set_link("tag/blackbeard"); 98 | $this->assertAttributeSame("tag/blackbeard", "link", $this->object); 99 | } 100 | 101 | /** 102 | * @covers TagadelicTag::force_dirty 103 | */ 104 | public function testForce_dirty() { 105 | $this->object->force_dirty(); 106 | $this->assertAttributeSame(TRUE, "dirty", $this->object); 107 | } 108 | 109 | /** 110 | * @covers TagadelicTag::force_clean 111 | */ 112 | public function testForce_clean() { 113 | $this->object->force_clean(); 114 | $this->assertAttributeSame(FALSE, "dirty", $this->object); 115 | } 116 | 117 | /** 118 | * @covers TagadelicTag::clean() 119 | */ 120 | public function testCleansWhenDirty() { 121 | $drupal = $this->getMock("TagaDelicDrupalWrapper"); 122 | $drupal->expects($this->exactly(2))->method("check_plain"); 123 | 124 | $this->object->set_drupal($drupal); 125 | $this->object->force_dirty(); 126 | 127 | $this->object->get_name(); 128 | $this->object->get_description(); 129 | } 130 | 131 | /** 132 | * @covers TagadelicTag::clean() 133 | */ 134 | public function testSkipsCleanWhenClean() { 135 | $drupal = $this->getMock("TagaDelicDrupalWrapper"); 136 | $drupal->expects($this->never())->method("check_plain"); 137 | 138 | $this->object->set_drupal($drupal); 139 | $this->object->force_clean(); 140 | 141 | $this->object->get_name(); 142 | $this->object->get_description(); 143 | } 144 | /** 145 | * @covers TagadelicTag::distributed 146 | */ 147 | public function testDistributed() { 148 | $this->assertSame(log(2), $this->object->distributed()); 149 | } 150 | 151 | /** 152 | * @covers TagadelicTag::distributed 153 | */ 154 | public function testDistributed_NotInfinite() { 155 | 156 | $this->object = new TagadelicTag(24, "redhair", 0); 157 | 158 | $this->assertFalse((is_infinite($this->object->distributed()))); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /TagadelicCloud.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $this->tags = $tags; 28 | } 29 | 30 | /** 31 | * Getter for id 32 | * @ingroup getters 33 | * @returns Integer id of this cloud 34 | */ 35 | public function get_id() { 36 | return $this->id; 37 | } 38 | 39 | /** 40 | * Getter for tags 41 | * @ingroup getters 42 | * @returns Array list of tags 43 | */ 44 | public function get_tags() { 45 | $this->recalculate(); 46 | return $this->tags; 47 | } 48 | 49 | /** 50 | * Add a new tag to the cloud 51 | * @param $tag TagadelicTag 52 | * instance of TagadelicTag. 53 | * 54 | * return $this, for chaining. 55 | */ 56 | public function add_tag($tag) { 57 | $this->tags[] = $tag; 58 | return $this; 59 | } 60 | 61 | /** 62 | * setter for drupal(wrapper). Mostly for testability 63 | * Operates on $this 64 | * Returns $this 65 | */ 66 | public function set_drupal($drupal) { 67 | $this->drupal = $drupal; 68 | return $this; 69 | } 70 | 71 | /** 72 | * Getter for drupal 73 | * @return DrupalWrapper value in $this::$drupal. 74 | */ 75 | public function drupal() { 76 | if (empty($this->drupal)) { 77 | $this->drupal = new TagadelicDrupalWrapper(); 78 | } 79 | return $this->drupal; 80 | } 81 | 82 | /** 83 | * Instantiate $this from cache 84 | * Optionally pass $drupal, a Drupalwrapper along, mostly for testing. 85 | * Returns this 86 | */ 87 | public static function from_cache($id, $drupal) { 88 | $cache_id = "tagadelic_cloud_{$id}"; 89 | return $drupal->cache_get($cache_id); 90 | } 91 | 92 | /** 93 | * Writes the cloud to cache. Will recalculate if needed. 94 | * @return $this; for chaining. 95 | */ 96 | public function to_cache() { 97 | $cache_id = "tagadelic_cloud_{$this->id}"; 98 | $this->drupal()->cache_set($cache_id, $this); 99 | return $this; 100 | } 101 | 102 | /** 103 | * Sorts the tags by given property. 104 | * @return $this; for chaining. 105 | */ 106 | public function sort($by_property) { 107 | if ($by_property == "random") { 108 | $this->drupal()->shuffle($this->tags); 109 | } 110 | else { 111 | //Bug in PHP https://bugs.php.net/bug.php?id=50688, lets supress the error. 112 | @usort($this->tags, array($this, "cb_sort_by_{$by_property}")); 113 | } 114 | return $this; 115 | } 116 | 117 | /** 118 | * (Re)calculates the weights on the tags. 119 | * @param $recalculate. Optional flag to enfore recalculation of the weights for the tags in this cloud. 120 | * defaults to FALSE, meaning the value will be calculated once per cloud. 121 | * @return $this; for chaining 122 | */ 123 | private function recalculate() { 124 | $tags = array(); 125 | // Find minimum and maximum log-count. 126 | $min = 1e9; 127 | $max = -1e9; 128 | foreach ($this->tags as $id => $tag) { 129 | $min = min($min, $tag->distributed()); 130 | $max = max($max, $tag->distributed()); 131 | $tags[$id] = $tag; 132 | } 133 | // Note: we need to ensure the range is slightly too large to make sure even 134 | // the largest element is rounded down. 135 | $range = max(.01, $max - $min) * 1.0001; 136 | foreach ($tags as $id => $tag) { 137 | $this->tags[$id]->set_weight(1 + floor($this->steps * ($tag->distributed() - $min) / $range)); 138 | } 139 | return $this; 140 | } 141 | 142 | private function cb_sort_by_name($a, $b) { 143 | return strcoll($a->get_name(), $b->get_name()); 144 | } 145 | 146 | private function cb_sort_by_count($a, $b) { 147 | $ac = $a->get_count(); 148 | $bc = $b->get_count(); 149 | if ($ac == $bc) { 150 | return 0; 151 | } 152 | //Highest first, High to low 153 | return ($ac < $bc) ? +1 : -1; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/Drupal/tagadelic/Tests/TagadelicTaxonomyAdminWebTestCase.php: -------------------------------------------------------------------------------- 1 | "Tagadelic Taxonomy Admin Test", 18 | "description" => "Tests admin area in Tagadelic Taxonomy", 19 | "group" => "Tagadelic", 20 | ); 21 | } 22 | 23 | /** 24 | * @scope public 25 | * @returns Type Description of return value 26 | */ 27 | public function setUp(array $modules = array()) { 28 | parent::setUp($modules); 29 | $this->deleteVocabularies(); 30 | 31 | $this->web_user = $this->drupalCreateUser(array("administer site configuration", "administer users")); 32 | $this->drupalLogin($this->web_user); 33 | } 34 | 35 | public function testHasTagaDelicPage() { 36 | $this->drupalGet($this->admin_url); 37 | $this->assertResponse(200, "Can Access Page"); 38 | $this->assertText(t("Tag Cloud"), "Title of page is Tag Cloud"); 39 | } 40 | 41 | public function testIsOnlyAccessibleForAdmin() { 42 | $web_user = $this->drupalCreateUser(array('access content')); 43 | $this->drupalLogin($web_user); 44 | 45 | $this->drupalGet($this->admin_url); 46 | $this->assertResponse(403); 47 | } 48 | 49 | public function testHasCheckboxesForAllVocabularies() { 50 | $this->createVocabularies(5); 51 | $this->drupalGet($this->admin_url); 52 | foreach($this->vocabularies as $vocabulary) { 53 | $id = "edit-tagadelic-taxonomy-vocabularies-{$vocabulary->vid}"; 54 | $this->assertHasCheckbox($id); 55 | } 56 | } 57 | 58 | public function testCheckboxesGetDefaults() { 59 | $this->createVocabularies(5); 60 | 61 | foreach($this->vocabularies as $vocabulary) { 62 | $values[$vocabulary->vid] = $vocabulary->vid; 63 | } 64 | variable_set("tagadelic_taxonomy_vocabularies", $values); 65 | 66 | $this->drupalGet($this->admin_url); 67 | foreach($this->vocabularies as $vocabulary) { 68 | $id = "edit-tagadelic-taxonomy-vocabularies-{$vocabulary->vid}"; 69 | $this->assertFieldChecked($id); 70 | } 71 | } 72 | 73 | public function testSelectedVocabulariesAreSaved() { 74 | $values = $edit = array(); 75 | $this->createVocabularies(5); 76 | $this->drupalGet($this->admin_url); //Create the form item 77 | 78 | foreach($this->vocabularies as $vocabulary) { 79 | $values[$vocabulary->vid] = $vocabulary->vid; 80 | 81 | $key = "tagadelic_taxonomy_vocabularies[{$vocabulary->vid}]"; 82 | $edit[$key] = TRUE; 83 | } 84 | $this->drupalPost(NULL, $edit, "Save configuration"); 85 | 86 | $this->assertVariableIs("tagadelic_taxonomy_vocabularies", $values); 87 | } 88 | 89 | 90 | protected function assertHasCheckbox($id, $message = '', $group = 'Other') { 91 | if (empty($message)) { 92 | $message = "checkbox '{$id}' not found"; 93 | } 94 | 95 | $this->assertHasXpath(".//input[@id='{$id}'][@type='checkbox']"); 96 | } 97 | 98 | protected function assertHasXpath($xpath, $message = '', $group = 'Other') { 99 | if (empty($message)) { 100 | $message = "xpath '{$xpath}' not found."; 101 | } 102 | $xpath = $this->xpath($xpath); 103 | $truthiness = count($xpath) > 0; 104 | return $this->assertTrue($truthiness, $message, $group); 105 | } 106 | 107 | protected function assertVariableIs($name, $expected_value, $refresh = FALSE, $message = '', $group = 'Other') { 108 | if ($refresh) { 109 | $this->refreshVariables(); 110 | } 111 | $real_value = variable_get($name, NULL); 112 | 113 | // We want identical-ish arrays. 114 | if (is_array($expected_value)) { 115 | ksort($expected_value); 116 | } 117 | if (is_array($real_value)) { 118 | ksort($real_value); 119 | } 120 | 121 | if (empty($message)) { 122 | $expected = var_export($expected_value, TRUE); 123 | $real = var_export($real_value, TRUE); 124 | $message = "variable {$name} with {$expected} not found. Was {$real}."; 125 | } 126 | return $this->assertIdentical($expected_value, $real_value, $message); 127 | } 128 | 129 | /* 130 | * Builder functions 131 | */ 132 | private function createVocabularies($amount) { 133 | $tx_test = new \TaxonomyWebTestCase(); 134 | for ($i = 0; $i < $amount; $i++) { 135 | $this->vocabularies[] = $tx_test->createVocabulary(); 136 | } 137 | 138 | return $this; 139 | } 140 | 141 | private function deleteVocabularies() { 142 | foreach(taxonomy_vocabulary_load_multiple(FALSE) as $vocabulary) { 143 | taxonomy_vocabulary_delete($vocabulary->vid); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tagadelic_taxonomy.module: -------------------------------------------------------------------------------- 1 | 6 | * @link http://berk.es 7 | */ 8 | 9 | /** 10 | * Implements hook_menu(). 11 | * @see hook_menu() 12 | */ 13 | function tagadelic_taxonomy_menu() { 14 | $items['tagadelic_taxonomy'] = array( 15 | 'title' => 'Tag Cloud', 16 | 'page callback' => 'tagadelic_taxonomy_cloud', 17 | 'page arguments' => array('60'), 18 | 'access arguments' => array("access content"), 19 | 'expanded' => TRUE, 20 | ); 21 | # Admin pages 22 | $items['admin/structure/tagadelic_taxonomy'] = array( 23 | 'title' => 'Tag Cloud', 24 | 'page callback' => 'tagadelic_taxonomy_admin', 25 | 'access arguments' => array("administer site configuration"), 26 | ); 27 | return $items; 28 | } 29 | 30 | /** 31 | * Constructs a simple page. 32 | * Callback from the menu. 33 | */ 34 | function tagadelic_taxonomy_cloud() { 35 | return theme("tagadelic_taxonomy_cloud", array("tags" => tagadelic_taxonomy_get_cloud(60))); 36 | } 37 | 38 | function tagadelic_taxonomy_get_cloud($max_amount) { 39 | drupal_add_css(drupal_get_path('module', 'tagadelic') . '/tagadelic.css'); 40 | 41 | $cloud = new TagadelicCloud("tagadalic_taxonomy"); 42 | 43 | foreach (tagadelic_taxonomy_tags_from_db($max_amount) as $term) { 44 | $tag = new TagadelicTag($term->tid, $term->name, $term->count); 45 | $tag->set_link("taxonomy/term/{$term->tid}"); 46 | 47 | $cloud->add_tag($tag); 48 | } 49 | 50 | # Because now here wer're returning an array, not HTML. 51 | return $cloud->get_tags(); 52 | } 53 | 54 | function tagadelic_taxonomy_theme($existing, $type, $theme, $path) { 55 | return array( 56 | "tagadelic_taxonomy_cloud" => array( 57 | "variables" => array( 58 | "tags" => array(), 59 | "name" => "", 60 | ), 61 | "path" => "{$path}/templates", 62 | "template" => "tagadelic_taxonomy_cloud" 63 | ), // tagadelic_taxonomy_cloud 64 | 65 | ); 66 | } 67 | 68 | function tagadelic_taxonomy_tags_from_db($max_amount) { 69 | $tags = array(); 70 | 71 | $query = db_select('taxonomy_index', 'i'); 72 | 73 | $alias = $query->leftjoin('taxonomy_term_data', 't', '%alias.tid = i.tid'); 74 | 75 | $query->addExpression('COUNT(i.nid)', 'count'); 76 | $query->addField($alias, 'tid'); 77 | $query->addField($alias, 'name'); 78 | $query->addField($alias, 'description'); 79 | $query->orderBy('count', 'DESC'); 80 | 81 | foreach(variable_get("tagadelic_taxonomy_vocabularies", array()) as $vid => $state) { 82 | if ($state != $vid) { //Disabled 83 | $query->condition('t.vid', $vid, '<>'); 84 | } 85 | } 86 | 87 | $query->range(0, $max_amount) 88 | ->groupBy("i.tid"); 89 | 90 | return $query->execute(); 91 | } 92 | 93 | /******************************************************************** 94 | * Admin pages methods * 95 | *******************************************************************/ 96 | 97 | /** 98 | * tagadelic_taxonomy_admin Renders admin page 99 | * 100 | * @returns String $html The Contents of the page, as HTML 101 | */ 102 | function tagadelic_taxonomy_admin() { 103 | $html = ""; 104 | 105 | $form = drupal_get_form("tagadelic_taxonomy_admin_form"); 106 | $html .= drupal_render($form); 107 | 108 | return $html; 109 | } 110 | 111 | function tagadelic_taxonomy_admin_form($form, &$form_state) { 112 | $form = array(); 113 | $options = array(); 114 | 115 | foreach(taxonomy_get_vocabularies() as $vocabulary) { 116 | $options[$vocabulary->vid] = $vocabulary->name; 117 | } 118 | 119 | $form["tagadelic_taxonomy_vocabularies"] = array( 120 | "#type" => "checkboxes", 121 | "#title" => "Vocabularies used in Tag Cloud", 122 | "#options" => $options, 123 | "#default_value" => variable_get('tagadelic_taxonomy_vocabularies', array()), 124 | ); 125 | 126 | return system_settings_form($form); 127 | } 128 | 129 | /** 130 | * Implementation of hook_block_info 131 | * 132 | * @returns array $blocks 133 | */ 134 | function tagadelic_taxonomy_block_info() { 135 | return array( 136 | 'tagadelic_taxonomy' => array( 137 | 'info' => t('Tagadelic Tag cloud'), 138 | 'cache' => DRUPAL_NO_CACHE, 139 | ) 140 | ); 141 | } 142 | 143 | /** 144 | * Implementation of hook_block_view 145 | * 146 | * @param String $delta name key for the block 147 | * 148 | * @return array $block renderable array of terms cloud 149 | */ 150 | function tagadelic_taxonomy_block_view($delta = '') { 151 | $body = theme("tagadelic_taxonomy_cloud", array("tags" => tagadelic_taxonomy_get_cloud(12))); 152 | $body .= l(t("More tags"), "tagadelic_taxonomy", array("attributes" => array("class" => array("more")))); 153 | return array( 154 | 'subject' => t('Tag cloud'), 155 | 'content' => array( 156 | '#type' => 'markup', 157 | '#markup' => $body, 158 | ) 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /TagadelicTag.php: -------------------------------------------------------------------------------- 1 | id = $id; 26 | $this->name = $name; 27 | if($count != 0) { 28 | $this->count = $count; 29 | } 30 | } 31 | 32 | /** 33 | * Magic method to render the Tag. 34 | * turns the tag into an HTML link to its source. 35 | */ 36 | public function __ToString() { 37 | $this->clean(); 38 | 39 | $attributes = $options = array(); 40 | 41 | if (!empty($this->description)) $attributes["title"] = $this->description; 42 | if ($this->weight > 0) $attributes["class"][] = "level{$this->weight}"; 43 | 44 | if (!empty($attributes)) $options["attributes"] = $attributes; 45 | 46 | return $this->drupal()->l($this->name, $this->link, $options); 47 | } 48 | 49 | /** 50 | * Getter for the ID 51 | * @ingroup getters 52 | * return Integer Identifier 53 | **/ 54 | public function get_id() { 55 | return $this->id; 56 | } 57 | 58 | /** 59 | * Getter for the name 60 | * @ingroup getters 61 | * return String the human readable name 62 | **/ 63 | public function get_name() { 64 | $this->clean(); 65 | return $this->name; 66 | } 67 | 68 | /** 69 | * Getter for the description 70 | * @ingroup getters 71 | * return String the human readable description 72 | **/ 73 | public function get_description() { 74 | $this->clean(); 75 | return $this->description; 76 | } 77 | 78 | /** 79 | * Returns the weight, getter only. 80 | * Will call recalculate to calculate the weight. 81 | * @ingroup getters 82 | * return Float the weight of this tag. 83 | **/ 84 | public function get_weight() { 85 | return $this->weight; 86 | } 87 | 88 | /** 89 | * Returns the count, getter only. 90 | * @ingroup getters 91 | * return Int the count as provided when Initializing the Object. 92 | **/ 93 | public function get_count() { 94 | return $this->count; 95 | } 96 | 97 | /** 98 | * Sets the optional description. 99 | * A tag may have a description 100 | * @param $description String a description 101 | */ 102 | public function set_description($description) { 103 | $this->description = $description; 104 | } 105 | 106 | /** 107 | * Link to a resource. 108 | * @param link String Optional a link to a resource that represents 109 | * the tag. e.g. a listing with all things tagged with Tag, or 110 | * the article that represents the tag. 111 | */ 112 | public function set_link($link) { 113 | $this->link = $link; 114 | } 115 | 116 | /** 117 | * setter for weight 118 | * Operates on $this 119 | * Returns $this 120 | */ 121 | public function set_weight($weight) { 122 | $this->weight = $weight; 123 | return $this; 124 | } 125 | 126 | /** 127 | * setter for drupal(Wrapper) 128 | * Operates on $this 129 | * Returns $this 130 | */ 131 | public function set_drupal($drupal) { 132 | $this->drupal = $drupal; 133 | return $this; 134 | } 135 | /** 136 | * Getter for drupal, if not found, will instantiate a default TagaDelicDrupalWrapper 137 | * @return type value in $this::$drupal. 138 | */ 139 | public function drupal() { 140 | if (empty($this->drupal)) { 141 | $this->drupal = new TagaDelicDrupalWrapper(); 142 | } 143 | return $this->drupal; 144 | } 145 | 146 | /** 147 | * Flag $name and $description as dirty; none-cleaned. 148 | * BEWARE! This will probably lead to double escaping, unless you know what you are doing. 149 | */ 150 | public function force_dirty() { 151 | $this->dirty = true; 152 | } 153 | 154 | /** 155 | * Flag $name and $description as safe. 156 | * XSS-escaping and sanitizing is left to implementer. 157 | * BEWARE! Only enforce when you know what you are doing. Seriously! 158 | */ 159 | public function force_clean() { 160 | $this->dirty = false; 161 | } 162 | 163 | /** 164 | * Calculates a more evenly distributed value. 165 | */ 166 | public function distributed() { 167 | return log($this->count); 168 | } 169 | 170 | /** 171 | * Utility, to enforce XSS filtering on strings before they are 172 | * printed or returned. 173 | **/ 174 | private function clean() { 175 | if ($this->dirty) { 176 | $this->name = $this->drupal()->check_plain($this->name); 177 | $this->description = $this->drupal()->check_plain($this->description); 178 | $this->force_clean(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tagadelic # 2 | Tagadelic provides an API and a few simple turnkey modules, which allows you to easily create tagclouds, weighted lists, search-clouds and such. 3 | 4 | With the API you can build a module with a few lines of PHP, to turn anything that can be counted into a weighted cloud. Which can be presented to your users anyway and anywhere on your site. 5 | 6 | With the turnkey modules, you can add a page that shows taxonomy-terms in a weighted cloud: terms that are used more often are bigger. Another module provides a page that shows article-titles in a cloud: titles from articles that are read more often appear bigger. 7 | 8 | [![Build Status](https://travis-ci.org/berkes/tagadelic.png?branch=develop)](https://travis-ci.org/berkes/tagadelic) 9 | 10 | ## End-users ## 11 | Install and enable _"tagadelic taxonomy"_ for a tagcloud from your 12 | taxonomy-terms, and _"tagadelic titles"_ for a tagcloud from 13 | article-titles. The required libaries and such will be installed 14 | automatically. 15 | 16 | ### Documentation for end-users ### 17 | 18 | * [Tagadelic Taxonomy](https://github.com/berkes/tagadelic/wiki/Tagadelic-Taxonomy) 19 | * [Tagadelic Titles](https://github.com/berkes/tagadelic/wiki/Tagadelic-Titles) 20 | 21 | ## Developers ## 22 | 23 | Tagadelic is essentially a set of classes with APIs that allow you to 24 | build word-clouds from anything that can be counted. The end-user 25 | modules described above, should be considered example- and 26 | convenience-modules. Examples for you, when you want to develop your own 27 | clouds. And convenience for the poor souls who cannot develop such 28 | module :). 29 | 30 | An example: 31 | 32 | get_tags() as $tag) { 40 | print $tag; 41 | } 42 | ?> 43 | 44 | This will output: 45 | 46 | Anne Bonney 47 | Sadie the Goat 48 | Mary Read. 115 | -------------------------------------------------------------------------------- /tests/TagadelicCloudTest.php: -------------------------------------------------------------------------------- 1 | object = new TagadelicCloud(1337); 21 | 22 | $this->addTagStub("jane", "Jane", 200); 23 | $this->addTagStub("blackbeard", "Blackbeard", 100); 24 | } 25 | 26 | /** 27 | * Tears down the fixture, for example, closes a network connection. 28 | * This method is called after a test is executed. 29 | */ 30 | protected function tearDown() { 31 | } 32 | 33 | /** 34 | * constructor should set the ID to the variable passed. 35 | */ 36 | public function test__construct() { 37 | // First param should be assigned to the id. 38 | $this->assertAttributeSame(1337, "id", $this->object); 39 | 40 | // Optional second argument pre-sets the tags. 41 | $this->object = new TagadelicCloud(1337, $this->mock_tags); 42 | $this->assertAttributeSame($this->mock_tags, "tags", $this->object); 43 | } 44 | 45 | /** 46 | * @covers TagadelicCloud::get_id 47 | */ 48 | public function testGet_id() { 49 | $this->assertEquals(1337, $this->object->get_id()); 50 | } 51 | 52 | /** 53 | * @covers TagadelicCloud::get_tags 54 | * Tests if get_Tags returns an array only. 55 | */ 56 | public function testGet_tags() { 57 | $this->object = new TagadelicCloud(1337, $this->mock_tags); 58 | $this->assertSame($this->mock_tags, $this->object->get_tags()); 59 | } 60 | 61 | /** 62 | * @covers TagadelicCloud::add_tag 63 | */ 64 | public function testAdd_tag() { 65 | $this->object->add_tag($this->mock_tags["blackbeard"]); 66 | $this->assertAttributeContains($this->mock_tags["blackbeard"], "tags", $this->object); 67 | } 68 | 69 | /** 70 | * @covers TagadelicCloud::add_tag() 71 | */ 72 | public function testAdd_tagIsChainable() { 73 | $this->assertEquals($this->object->add_tag($this->mock_tags["blackbeard"]), $this->object); 74 | } 75 | 76 | /** 77 | * @covers TagadelicCloud::set_drupal() 78 | */ 79 | public function testSet_drupal() { 80 | $drupal = new StdClass(); 81 | $this->object->set_drupal($drupal); 82 | $this->assertAttributeSame($drupal, "drupal", $this->object); 83 | } 84 | 85 | /** 86 | * @covers TagadelicCloud::drupal() 87 | */ 88 | public function testDrupalReturnsSetValue() { 89 | $drupal = "ThisIsDrupal"; 90 | $this->object->set_drupal($drupal); 91 | $this->assertSame($this->object->drupal(), $drupal); 92 | } 93 | 94 | /** 95 | * @covers TagadelicCloud::drupal() 96 | */ 97 | public function testDrupalInstantiatesNewWrapper() { 98 | $this->object->set_drupal(NULL); 99 | $drupal = $this->getMock("TagadelicDrupalWrapper"); 100 | $this->assertInstanceOf("TagadelicDrupalWrapper", $this->object->drupal()); 101 | } 102 | 103 | /** 104 | * @covers tagadeliccloud::from_cache 105 | */ 106 | public function testfrom_cache() { 107 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("cache_get")); 108 | $drupal->expects($this->once()) 109 | ->method("cache_get") 110 | ->with("tagadelic_cloud_1337") 111 | ->will($this->returnvalue($this->object)); 112 | $cloud = TagadelicCloud::from_cache(1337, $drupal); 113 | $this->assertinstanceof("TagadelicCloud", $cloud); 114 | } 115 | 116 | /** 117 | * @covers tagadeliccloud::to_cache 118 | */ 119 | public function testTo_cache() { 120 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("cache_set")); 121 | $drupal->expects($this->once()) 122 | ->method("cache_set") 123 | ->with("tagadelic_cloud_1337", $this->object); 124 | $this->object->set_drupal($drupal); 125 | 126 | $this->object->to_cache(); 127 | } 128 | 129 | /** 130 | * Get Tags should calculate the weights 131 | */ 132 | public function testGetCalculatedTags() { 133 | foreach ($this->mock_tags as $mock_tag) { 134 | $mock_tag->expects($this->once()) 135 | ->method('set_weight') 136 | ->with($this->greaterThan(0)) 137 | ->will($this->returnSelf()); 138 | $mocks[] = $mock_tag; 139 | } 140 | $this->object = new TagadelicCloud(1337, $mocks); 141 | $this->object->get_tags(); 142 | } 143 | 144 | /** 145 | * Get Tags should calculate the weights 146 | */ 147 | public function testGetCalculatedWeights() { 148 | $mocks = array(); 149 | $assert_table = array( 150 | // name , count , weight 151 | array("Mary Read" , 1 , 1), 152 | array("Jean Fleury" , 1 , 1), 153 | array("François Le Clerc" , 1 , 1), 154 | array("Blackbeard" , 2 , 1), 155 | array("Henry Morgan" , 3 , 2), 156 | array("Bartolomew Roberts" , 10 , 3), 157 | array("Stede Bonnet" , 20 , 4), 158 | array("Edward Low" , 40 , 5), 159 | array("Anne Bonny" , 100 , 6), 160 | ); 161 | $i = 1; 162 | 163 | foreach($assert_table as $assertion) { 164 | $mock = $this->getMock("TagadelicTag", array("set_weight"), array($i++, $assertion[0], $assertion[1])); 165 | $mock->expects($this->once()) 166 | ->method("set_weight") 167 | ->with($assertion[2]) 168 | ->will($this->returnSelf()); 169 | 170 | $mocks[] = $mock; 171 | } 172 | $this->object = new TagadelicCloud(1337, $mocks); 173 | $this->object->get_tags(); 174 | } 175 | 176 | /** 177 | * Default is not sorted 178 | **/ 179 | public function testNotSorted() { 180 | $this->object->add_tag($this->addTagStub("bill", "William Kidd", 100)); 181 | $this->object->add_tag($this->addTagStub("cheung", "Cheung Po Tsai", 20)); 182 | 183 | $expected_order = array("William Kidd", "Cheung Po Tsai"); 184 | $given_order = array(); 185 | 186 | foreach($this->object->get_tags() as $tag) { 187 | $given_order[] = $tag->get_name(); 188 | } 189 | 190 | $this->assertSame($given_order, $expected_order); 191 | } 192 | 193 | /** 194 | * Sort By name 195 | **/ 196 | public function testSortByName() { 197 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("check_plain")); 198 | $drupal->expects($this->any())->method("check_plain")->will($this->returnArgument(0)); 199 | $this->object->set_drupal($drupal); 200 | 201 | $this->object->add_tag($this->addTagStub("bill", "William Kidd", 100)); 202 | $this->object->add_tag($this->addTagStub("cheung", "Cheung Po Tsai", 20)); 203 | 204 | $expected_order = array("Cheung Po Tsai", "William Kidd"); 205 | $given_order = array(); 206 | 207 | $this->object->sort("name"); 208 | 209 | foreach($this->object->get_tags() as $tag) { 210 | $given_order[] = $tag->get_name(); 211 | } 212 | 213 | $this->assertSame($given_order, $expected_order); 214 | } 215 | 216 | /** 217 | * Sort By name should sort International characters like ÅÄÖABO 218 | * 219 | * Uses Locale de_DE, which must be available on your system. 220 | * Debian/Ubuntu users: 221 | * $ locale -a 222 | * $ #lists all locales available. Find if de_DE is there, if not, install it. 223 | * $ sudo apt-get install language-pack-de-base 224 | **/ 225 | public function testSortByNameWithInternationalCharacters() { 226 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("check_plain")); 227 | $drupal->expects($this->any())->method("check_plain")->will($this->returnArgument(0)); 228 | $this->object->set_drupal($drupal); 229 | 230 | $this->object->add_tag($this->addTagStub("ae", "Ä", 10)); 231 | $this->object->add_tag($this->addTagStub("oe", "Ö", 20)); 232 | $this->object->add_tag($this->addTagStub("ue", "Ü", 30)); 233 | $this->object->add_tag($this->addTagStub("u", "U", 40)); 234 | $this->object->add_tag($this->addTagStub("o", "O", 50)); 235 | $this->object->add_tag($this->addTagStub("a", "A", 60)); 236 | 237 | $expected_order = array("A", "Ä", "O", "Ö", "U", "Ü"); 238 | $given_order = array(); 239 | 240 | setlocale(LC_COLLATE, 'fr_FR.utf8'); 241 | $this->object->sort("name"); 242 | 243 | foreach($this->object->get_tags() as $tag) { 244 | $given_order[] = $tag->get_name(); 245 | } 246 | 247 | $this->assertSame($given_order, $expected_order); 248 | } 249 | 250 | /** 251 | * Sort by count. Highest count first. 252 | */ 253 | public function testSortByCount() { 254 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("check_plain")); 255 | $drupal->expects($this->any())->method("check_plain")->will($this->returnArgument(0)); 256 | $this->object->set_drupal($drupal); 257 | 258 | $this->object->add_tag($this->addTagStub("bill", "William Kidd", 100)); 259 | $this->object->add_tag($this->addTagStub("cheung", "Cheung Po Tsai", 200)); 260 | 261 | $expected_order = array("Cheung Po Tsai", "William Kidd"); 262 | $given_order = array(); 263 | 264 | $this->object->sort("count"); 265 | 266 | foreach($this->object->get_tags() as $tag) { 267 | $given_order[] = $tag->get_name(); 268 | } 269 | 270 | $this->assertSame($given_order, $expected_order); 271 | } 272 | 273 | /** 274 | * Sort random. 275 | */ 276 | public function testSortRandom() { 277 | $drupal = $this->getMock("TagadelicDrupalWrapper", array("shuffle")); 278 | $drupal->expects($this->once()) 279 | ->method("shuffle"); 280 | $this->object->set_drupal($drupal); 281 | 282 | $this->object->sort("random"); 283 | } 284 | 285 | /** 286 | * Creates a stub for a tag 287 | */ 288 | private function addTagStub($id, $name, $count) { 289 | $stub = $this->getMock("TagadelicTag", array("get_name", "get_count", "set_weight"), array($id, $name, $count)); 290 | 291 | $stub->expects($this->any()) 292 | ->method("get_name") 293 | ->will($this->returnValue($name)); 294 | $stub->expects($this->any()) 295 | ->method("get_count") 296 | ->will($this->returnValue($count)); 297 | 298 | $this->mock_tags[$id] = $stub; 299 | return $stub; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /lib/Drupal/tagadelic/Tests/TagadelicTaxonomyTestCase.php: -------------------------------------------------------------------------------- 1 | "Tagadelic Taxonomy Test", 20 | "description" => "Tests tagclouds from Tagadelic Taxonomy", 21 | "group" => "Tagadelic", 22 | ); 23 | } 24 | 25 | /** 26 | * @scope public 27 | * @returns Type Description of return value 28 | */ 29 | public function setUp(array $modules = array()) { 30 | parent::setUp(array('block')); 31 | 32 | // Create an admin user allowed to edit block settings 33 | $this->admin_user = $this->drupalCreateUser(array( 34 | 'administer blocks', 35 | 'access administration pages', 36 | )); 37 | 38 | } 39 | 40 | public function testHasTagcloudPage() { 41 | $this->drupalGet("tagadelic_taxonomy"); 42 | $this->assertResponse(200, "Can Access Page"); 43 | $this->assertText(t("Tag Cloud"), "Title of page is Tag Cloud"); 44 | } 45 | 46 | public function testOnlyAccessContentAccessible() { 47 | # Remove access content permission from anonymous user. 48 | # As per http://drupal.stackexchange.com/a/70478/787 49 | user_role_revoke_permissions(1, array('access content')); 50 | $this->drupalGet("tagadelic_taxonomy"); 51 | $this->assertResponse(403); 52 | } 53 | 54 | public function testTagsWithoutNodesNotOnPage() { 55 | $this->createVocAndTags(3, FALSE); 56 | $this->drupalGet("tagadelic_taxonomy"); 57 | foreach($this->tags as $tag) { 58 | $this->assertNoText($tag->name); 59 | } 60 | } 61 | 62 | public function testHasTagsOnPage() { 63 | $this->createVocAndTags(1); 64 | $this->drupalGet("tagadelic_taxonomy"); 65 | $tag = $this->tags[0]; 66 | $this->assertText($tag->name); 67 | } 68 | 69 | public function testHasClickableLink() { 70 | $this->createVocAndTags(1); 71 | $this->drupalGet("tagadelic_taxonomy"); 72 | 73 | $link = "/taxonomy/term/{$this->tags[0]->tid}"; 74 | $this->assertHasXpath("//*/ul[@class='tag-cloud']/li/a[@href='{$link}']"); 75 | } 76 | 77 | public function testHasFiveTags() { 78 | $this->createVocAndTags(5); 79 | $this->drupalGet("tagadelic_taxonomy"); 80 | $amount = count($this->xpath("//*/ul[@class='tag-cloud']/li")); 81 | $this->assertEqual(5, $amount); 82 | } 83 | 84 | public function testHasMaxsixtyTags() { 85 | $this->createVocAndTags(61); 86 | $this->drupalGet("tagadelic_taxonomy"); 87 | $this->assertAmountTagsOnPage(60); 88 | } 89 | 90 | public function testOrdersTagsByUsage() { 91 | $this->createVocAndTags(3, FALSE); 92 | $this->createNodesWithTags(3); 93 | 94 | $this->drupalGet("tagadelic_taxonomy"); 95 | $tags = $this->xpath("//*/ul[@class='tag-cloud']/li/a"); 96 | 97 | $found = array(); 98 | foreach ($tags as $tag) { 99 | $attributes = $tag->attributes(); 100 | $found[] = (string) $attributes["href"]; 101 | } 102 | $this->assertEqual($found, array("/taxonomy/term/3", "/taxonomy/term/2", "/taxonomy/term/1")); 103 | } 104 | 105 | public function testOnlyHasTagsFromSelectedVocabularies() { 106 | $this->createVocsAndTags(3, 3); 107 | $this->createNodesWithTags(100); 108 | 109 | $disabled_voc = $this->vocabularies[0]; 110 | foreach($this->vocabularies as $vocabulary) { 111 | if ($disabled_voc->vid == $vocabulary->vid) { 112 | $setting[$vocabulary->vid] = 0; 113 | } 114 | else { 115 | $setting[$vocabulary->vid] = $vocabulary->vid; 116 | } 117 | } 118 | variable_set("tagadelic_taxonomy_vocabularies", $setting); 119 | $this->refreshVariables(); 120 | 121 | $this->drupalGet("tagadelic_taxonomy"); 122 | foreach($this->tags_by_voc[$disabled_voc->vid] as $term) { 123 | $this->assertNoText($term->name); 124 | } 125 | } 126 | 127 | public function testHasWeightedTags() { 128 | $this->createVocAndTags(10); 129 | $this->createNodesWithTags(100); 130 | $this->drupalGet("tagadelic_taxonomy"); 131 | 132 | $weights = array(1,2,3,4,4,5,5,6,6,6); 133 | $i = 0; 134 | foreach($this->tags as $tag) { 135 | $weight = $weights[$i++]; 136 | $this->assertTagHasWeight($tag->name, $weight); 137 | } 138 | } 139 | 140 | public function testAttachesCssToPage() { 141 | $this->createVocAndTags(1); 142 | $this->drupalGet("tagadelic_taxonomy"); 143 | $this->assertRaw('/tagadelic.css', "Tagadelic CSS added to styles"); 144 | } 145 | 146 | public function testAttachesCssOnlyToCloudPages() { 147 | $this->createVocAndTags(1); 148 | $this->drupalGet("node"); 149 | $this->assertNotRaw('/tagadelic.css', "Tagadelic CSS added to styles"); 150 | } 151 | 152 | public function testBlockRendering() { 153 | $this->enableBlock(); 154 | $this->createNodesWithTags(10); 155 | 156 | $this->drupalGet('node'); 157 | $this->assertText(t("Tag cloud"), "Title of block is Tag Cloud"); 158 | } 159 | 160 | public function testBlockHasMoreLinkToPage() { 161 | $this->enableBlock(); 162 | $this->createVocAndTags(11); 163 | 164 | $this->drupalGet('node'); 165 | 166 | $this->assertHasXpath('//*/a[@class="more"]'); 167 | $this->assertLink("More tags"); 168 | 169 | # This will cause a false-positive, as we have a link to tagadelic_taxonomy in the main-menu too! 170 | $this->assertLinkByHref("tagadelic_taxonomy"); 171 | } 172 | 173 | public function testBlockHasMaxTwelveTags() { 174 | $this->enableBlock(); 175 | $this->createVocAndTags(13); 176 | 177 | $this->drupalGet('node'); 178 | $this->assertAmountTagsOnPage(12); 179 | } 180 | 181 | /************************************************************************* 182 | * Test Helpers 183 | *************************************************************************/ 184 | private function createVocAndTags($amount_of_tags, $create_node = TRUE) { 185 | $tx_test = new \TaxonomyWebTestCase(); 186 | 187 | $this->tags = array(); 188 | $this->vocabulary = $tx_test->createVocabulary(); 189 | for ($i = 0; $i < $amount_of_tags; $i++) { 190 | $this->tags[] = $tx_test->createTerm($this->vocabulary); 191 | } 192 | 193 | if ($create_node) $this->createNodesWithTags(1); 194 | return $this; 195 | } 196 | 197 | private function createVocsAndTags($amount_of_vocs, $amount_of_tags_per_voc) { 198 | $tx_test = new \TaxonomyWebTestCase(); 199 | 200 | $this->tags = array(); 201 | $this->tags_by_voc = array(); 202 | 203 | for ($v = 0; $v < $amount_of_vocs; $v++) { 204 | $voc = $tx_test->createVocabulary(); 205 | $this->vocabularies[] = $voc; 206 | $this->tags_by_voc[$voc->vid] = array(); 207 | 208 | for ($t = 0; $t < $amount_of_tags_per_voc; $t++) { 209 | $tag = $tx_test->createTerm($voc); 210 | $this->tags[] = $tag; 211 | $this->tags_by_voc[$voc->vid][] = $tag; 212 | } 213 | 214 | } 215 | return $this; 216 | } 217 | 218 | /** 219 | * Creates $amount nodes with terms attached. 220 | * 221 | * 222 | * Fuck it, I am poking around in the database directly instead of testing 223 | * and preparing all this field, entity, admin-user and whatnot. I am not 224 | * interested in whether or not we can store nodes with tags, only that they 225 | * are there. By adding them to the database, we achieve that. 226 | */ 227 | private function createNodesWithTags($amount) { 228 | $this->nodes = array(); 229 | $attachable = $this->tags; 230 | 231 | for ($i = 0; $i < $amount; $i++) { 232 | // Post an article. 233 | $node = new \StdClass(); 234 | $node->title = $this->randomName(); 235 | $node->type = "story"; 236 | node_save($node); 237 | $this->nodes[] = $node; 238 | 239 | // Attach the terms 240 | $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); 241 | foreach($attachable as $tag) { 242 | $query->values(array( 243 | 'nid' => $node->nid, 244 | 'tid' => $tag->tid, 245 | 'sticky' => TRUE, 246 | 'created' => $node->created, 247 | )); 248 | } 249 | $query->execute(); 250 | 251 | //remove one tag, so the next node gets one less tag attached. 252 | array_shift($attachable); 253 | } 254 | return $this; 255 | } 256 | 257 | private function enableBlock() { 258 | theme_enable(array('seven')); 259 | $region = 'content'; 260 | $module = 'tagadelic_taxonomy'; 261 | // set the block to a theme region, taken from block.test 262 | $delta = db_query("SELECT delta FROM {block} WHERE module = :module AND theme = :theme", array(':module' => $module, ':theme' => 'seven'))->fetchField(); 263 | $edit = array(); 264 | $edit['blocks[' . $module . '_' . $delta . '][region]'] = $region; 265 | 266 | $this->drupalLogin($this->admin_user); 267 | $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); 268 | $this->drupalLogout(); 269 | } 270 | 271 | /** 272 | * assertHasXpath Asserts the existence of an xpath 273 | * 274 | * @scope private 275 | * @returns Boolean Boolean Assertion-result 276 | */ 277 | private function assertHasXpath($xpath, $message = '', $group = 'Other') { 278 | if (empty($message)) { 279 | $message = "xpath '{$xpath}' not found."; 280 | } 281 | $xpath = $this->xpath($xpath); 282 | $truthiness = count($xpath) > 0; 283 | return $this->assertTrue($truthiness, $message, $group); 284 | } 285 | 286 | private function assertHasTag($name, $message = '', $group = 'Other') { 287 | $xpath = "//ul[@class='tag-cloud']/li/a"; 288 | return $this->assertHasXpath($xpath, $message, $group); 289 | } 290 | 291 | private function assertAmountTagsOnPage($expected_amount, $message = '', $group = 'Other') { 292 | $amount = count($this->xpath("//*/ul[@class='tag-cloud']/li")); 293 | if (!$message) { 294 | $message = t("Expected $expected_amount Tags, found $amount"); 295 | } 296 | $this->assertEqual($expected_amount, $amount, $message); 297 | } 298 | 299 | private function assertTagHasWeight($name, $weight, $message = '', $group = 'Other') { 300 | $truthiness = FALSE; 301 | $class =''; 302 | $xpath = $this->xpath("//*/ul[@class='tag-cloud']/li/a[contains(text(),'{$name}')]"); 303 | 304 | if (!empty($xpath)) { 305 | $attributes = $xpath[0]->attributes(); 306 | $class = $attributes["class"]; 307 | if ($class == "level{$weight}") $truthiness = TRUE; 308 | } 309 | 310 | if (empty($message)) { 311 | $message = "No tag with name '{$name}' and class 'level{$weight}' found. Tag with '{$name}' has class '{$class}'"; 312 | } 313 | 314 | return $this->assertTrue($truthiness, $message, $group = 'Other'); 315 | } 316 | 317 | // Inverse of http://api.drupal.org/api/drupal/modules!simpletest!drupal_web_test_case.php/function/DrupalWebTestCase%3A%3AassertRaw/7 318 | private function assertNotRaw($raw, $message = '', $group = 'Other') { 319 | if (!$message) { 320 | $message = t('Raw "@raw" not found', array('@raw' => $raw)); 321 | } 322 | return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group); 323 | } 324 | } 325 | --------------------------------------------------------------------------------