ElggCoreRegressionBugsTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <?php
  2. /**
  3. * Elgg Regression Tests -- GitHub Bugfixes
  4. * Any bugfixes from GitHub that require testing belong here.
  5. *
  6. * @package Elgg
  7. * @subpackage Test
  8. */
  9. class ElggCoreRegressionBugsTest extends \ElggCoreUnitTest {
  10. /**
  11. * Called before each test object.
  12. */
  13. public function __construct() {
  14. $this->ia = elgg_set_ignore_access(true);
  15. parent::__construct();
  16. // all __construct() code should come after here
  17. }
  18. /**
  19. * Called after each test object.
  20. */
  21. public function __destruct() {
  22. elgg_set_ignore_access($this->ia);
  23. // all __destruct() code should go above here
  24. parent::__destruct();
  25. }
  26. /**
  27. * #1558
  28. */
  29. public function testElggObjectDeleteAnnotations() {
  30. $this->entity = new \ElggObject();
  31. $guid = $this->entity->save();
  32. $this->entity->annotate('test', 'hello', ACCESS_PUBLIC);
  33. $this->entity->deleteAnnotations('does not exist');
  34. $num = $this->entity->countAnnotations('test');
  35. //$this->assertIdentical($num, 1);
  36. $this->assertEqual($num, 1);
  37. // clean up
  38. $this->entity->delete();
  39. }
  40. /**
  41. * #2063 - get_resized_image_from_existing_file() fails asked for image larger than selection and not scaling an image up
  42. * Test get_image_resize_parameters().
  43. */
  44. public function testElggResizeImage() {
  45. $orig_width = 100;
  46. $orig_height = 150;
  47. // test against selection > max
  48. $options = array(
  49. 'maxwidth' => 50,
  50. 'maxheight' => 50,
  51. 'square' => true,
  52. 'upscale' => false,
  53. 'x1' => 25,
  54. 'y1' => 75,
  55. 'x2' => 100,
  56. 'y2' => 150
  57. );
  58. // should get back the same x/y offset == x1, y1 and an image of 50x50
  59. $params = get_image_resize_parameters($orig_width, $orig_height, $options);
  60. $this->assertEqual($params['newwidth'], $options['maxwidth']);
  61. $this->assertEqual($params['newheight'], $options['maxheight']);
  62. $this->assertEqual($params['xoffset'], $options['x1']);
  63. $this->assertEqual($params['yoffset'], $options['y1']);
  64. // test against selection < max
  65. $options = array(
  66. 'maxwidth' => 50,
  67. 'maxheight' => 50,
  68. 'square' => true,
  69. 'upscale' => false,
  70. 'x1' => 75,
  71. 'y1' => 125,
  72. 'x2' => 100,
  73. 'y2' => 150
  74. );
  75. // should get back the same x/y offset == x1, y1 and an image of 25x25 because no upscale
  76. $params = get_image_resize_parameters($orig_width, $orig_height, $options);
  77. $this->assertEqual($params['newwidth'], 25);
  78. $this->assertEqual($params['newheight'], 25);
  79. $this->assertEqual($params['xoffset'], $options['x1']);
  80. $this->assertEqual($params['yoffset'], $options['y1']);
  81. }
  82. // #3722 Check canEdit() works for contains regardless of groups
  83. function test_can_write_to_container() {
  84. $user = new \ElggUser();
  85. $user->username = 'test_user_' . rand();
  86. $user->name = 'test_user_name_' . rand();
  87. $user->email = 'test@user.net';
  88. $user->container_guid = 0;
  89. $user->owner_guid = 0;
  90. $user->save();
  91. $object = new \ElggObject();
  92. $object->save();
  93. $group = new \ElggGroup();
  94. $group->save();
  95. // disable access overrides because we're admin.
  96. $ia = elgg_set_ignore_access(false);
  97. $this->assertFalse(can_write_to_container($user->guid, $object->guid));
  98. global $elgg_test_user;
  99. $elgg_test_user = $user;
  100. // register hook to allow access
  101. function can_write_to_container_test_hook($hook, $type, $value, $params) {
  102. global $elgg_test_user;
  103. if ($params['user']->getGUID() == $elgg_test_user->getGUID()) {
  104. return true;
  105. }
  106. }
  107. elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'can_write_to_container_test_hook');
  108. $this->assertTrue(can_write_to_container($user->guid, $object->guid));
  109. elgg_unregister_plugin_hook_handler('container_permissions_check', 'all', 'can_write_to_container_test_hook');
  110. $this->assertFalse(can_write_to_container($user->guid, $group->guid));
  111. $group->join($user);
  112. $this->assertTrue(can_write_to_container($user->guid, $group->guid));
  113. elgg_set_ignore_access($ia);
  114. $user->delete();
  115. $object->delete();
  116. $group->delete();
  117. }
  118. /**
  119. * https://github.com/elgg/elgg/issues/3210 - Don't remove -s in friendly titles
  120. * https://github.com/elgg/elgg/issues/2276 - improve char encoding
  121. */
  122. public function test_friendly_title() {
  123. $cases = array(
  124. // acid test
  125. "B&N > Amazon, OK? <bold> 'hey!' $34"
  126. => "bn-amazon-ok-bold-hey-34",
  127. // hyphen, underscore and ASCII whitespace replaced by separator,
  128. // other non-alphanumeric ASCII removed
  129. "a-a_a a\na\ra\ta\va!a\"a#a\$a%aa'a(a)a*a+a,a.a/a:a;a=a?a@a[a\\a]a^a`a{a|a}a~a"
  130. => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  131. // separators trimmed
  132. "-_ hello _-"
  133. => "hello",
  134. // accents removed, lower case, other multibyte chars are URL encoded
  135. "I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n, AND \xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"
  136. // Iñtërnâtiônàlizætiøn, AND 日本語
  137. => 'internationalizaetion-and-%E6%97%A5%E6%9C%AC%E8%AA%9E',
  138. );
  139. // where available, string is converted to NFC before transliteration
  140. if (\Elgg\Translit::hasNormalizerSupport()) {
  141. $form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)
  142. $cases[$form_d] = "a";
  143. }
  144. foreach ($cases as $case => $expected) {
  145. $friendly_title = elgg_get_friendly_title($case);
  146. $this->assertIdentical($expected, $friendly_title);
  147. }
  148. }
  149. /**
  150. * Test #5369 -- parse_urls()
  151. * https://github.com/Elgg/Elgg/issues/5369
  152. */
  153. public function test_parse_urls() {
  154. $cases = array(
  155. 'no.link.here' =>
  156. 'no.link.here',
  157. 'simple link http://example.org test' =>
  158. 'simple link <a href="http://example.org" rel="nofollow">http:/<wbr />/<wbr />example.org</a> test',
  159. 'non-ascii http://ñew.org/ test' =>
  160. 'non-ascii <a href="http://ñew.org/" rel="nofollow">http:/<wbr />/<wbr />ñew.org/<wbr /></a> test',
  161. // section 2.1
  162. 'percent encoded http://example.org/a%20b test' =>
  163. 'percent encoded <a href="http://example.org/a%20b" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a%20b</a> test',
  164. // section 2.2: skipping single quote and parenthese
  165. 'reserved characters http://example.org/:/?#[]@!$&*+,;= test' =>
  166. 'reserved characters <a href="http://example.org/:/?#[]@!$&*+,;=" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />:/<wbr />?#[]@!$&*+,;=</a> test',
  167. // section 2.3
  168. 'unreserved characters http://example.org/a1-._~ test' =>
  169. 'unreserved characters <a href="http://example.org/a1-._~" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a1-._~</a> test',
  170. 'parameters http://example.org/?val[]=1&val[]=2 test' =>
  171. 'parameters <a href="http://example.org/?val[]=1&val[]=2" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />?val[]=1&val[]=2</a> test',
  172. 'port http://example.org:80/ test' =>
  173. 'port <a href="http://example.org:80/" rel="nofollow">http:/<wbr />/<wbr />example.org:80/<wbr /></a> test',
  174. 'parentheses (http://www.google.com) test' =>
  175. 'parentheses (<a href="http://www.google.com" rel="nofollow">http:/<wbr />/<wbr />www.google.com</a>) test',
  176. 'comma http://elgg.org, test' =>
  177. 'comma <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>, test',
  178. 'period http://elgg.org. test' =>
  179. 'period <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>. test',
  180. 'exclamation http://elgg.org! test' =>
  181. 'exclamation <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>! test',
  182. 'already anchor <a href="http://twitter.com/">twitter</a> test' =>
  183. 'already anchor <a href="http://twitter.com/">twitter</a> test',
  184. 'ssl https://example.org/ test' =>
  185. 'ssl <a href="https://example.org/" rel="nofollow">https:/<wbr />/<wbr />example.org/<wbr /></a> test',
  186. 'ftp ftp://example.org/ test' =>
  187. 'ftp <a href="ftp://example.org/" rel="nofollow">ftp:/<wbr />/<wbr />example.org/<wbr /></a> test',
  188. 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>' =>
  189. 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>',
  190. 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>' =>
  191. 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>',
  192. 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>' =>
  193. 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>',
  194. 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' =>
  195. 'parens in uri <a href="http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx" rel="nofollow">http:/<wbr />/<wbr />thedailywtf.com/<wbr />Articles/<wbr />A-(Long-Overdue)-BuildMaster-Introduction.aspx</a>'
  196. );
  197. foreach ($cases as $input => $output) {
  198. $this->assertEqual($output, parse_urls($input));
  199. }
  200. }
  201. /**
  202. * Ensure additional select columns do not end up in entity attributes.
  203. *
  204. * https://github.com/Elgg/Elgg/issues/5538
  205. */
  206. public function test_extra_columns_dont_appear_in_attributes() {
  207. global $ENTITY_CACHE;
  208. // may not have groups in DB - let's create one
  209. $group = new \ElggGroup();
  210. $group->name = 'test_group';
  211. $group->access_id = ACCESS_PUBLIC;
  212. $this->assertTrue($group->save() !== false);
  213. // entity cache interferes with our test
  214. $ENTITY_CACHE = array();
  215. foreach (array('site', 'user', 'group', 'object') as $type) {
  216. $entities = elgg_get_entities(array(
  217. 'type' => $type,
  218. 'selects' => array('1 as _nonexistent_test_column'),
  219. 'limit' => 1,
  220. ));
  221. if (!$this->assertTrue($entities, "Query for '$type' did not return an entity.")) {
  222. continue;
  223. }
  224. $entity = $entities[0];
  225. $this->assertNull($entity->_nonexistent_test_column, "Additional select columns are leaking to attributes for '$type'");
  226. }
  227. $group->delete();
  228. }
  229. /**
  230. * Ensure that \ElggBatch doesn't go into infinite loop when disabling annotations recursively when show hidden is enabled.
  231. *
  232. * https://github.com/Elgg/Elgg/issues/5952
  233. */
  234. public function test_disabling_annotations_infinite_loop() {
  235. //let's have some entity
  236. $group = new \ElggGroup();
  237. $group->name = 'test_group';
  238. $group->access_id = ACCESS_PUBLIC;
  239. $this->assertTrue($group->save() !== false);
  240. $total = 51;
  241. //add some annotations
  242. for ($cnt = 0; $cnt < $total; $cnt++) {
  243. $group->annotate('test_annotation', 'value_' . $total);
  244. }
  245. //disable them
  246. $show_hidden = access_get_show_hidden_status();
  247. access_show_hidden_entities(true);
  248. $options = array(
  249. 'guid' => $group->guid,
  250. 'limit' => $total, //using strict limit to avoid real infinite loop and just see \ElggBatch limiting on it before finishing the work
  251. );
  252. elgg_disable_annotations($options);
  253. access_show_hidden_entities($show_hidden);
  254. //confirm all being disabled
  255. $annotations = $group->getAnnotations(array(
  256. 'limit' => $total,
  257. ));
  258. foreach ($annotations as $annotation) {
  259. $this->assertTrue($annotation->enabled == 'no');
  260. }
  261. //delete group and annotations
  262. $group->delete();
  263. }
  264. public function test_ElggXMLElement_does_not_load_external_entities() {
  265. $elLast = libxml_disable_entity_loader(false);
  266. // build payload that should trigger loading of external entity
  267. $payload = file_get_contents(dirname(__FILE__) . '/test_files/xxe/request.xml');
  268. $path = realpath(dirname(__FILE__) . '/test_files/xxe/external_entity.txt');
  269. $path = str_replace('\\', '/', $path);
  270. if ($path[0] != '/') {
  271. $path = '/' . $path;
  272. }
  273. $path = 'file://' . $path;
  274. $payload = sprintf($payload, $path);
  275. // make sure we can actually this in this environment
  276. $element = new SimpleXMLElement($payload);
  277. $can_load_entity = preg_match('/secret/', (string)$element->methodName);
  278. $this->skipUnless($can_load_entity, "XXE vulnerability cannot be tested on this system");
  279. if ($can_load_entity) {
  280. $this->expectError("SimpleXMLElement::__construct(): I/O warning : failed to load external entity &quot;" . $path . "&quot;");
  281. $el = new \ElggXMLElement($payload);
  282. $chidren = $el->getChildren();
  283. $content = $chidren[0]->getContent();
  284. $this->assertNoPattern('/secret/', $content);
  285. }
  286. libxml_disable_entity_loader($elLast);
  287. }
  288. public function test_update_handlers_can_change_attributes() {
  289. $object = new \ElggObject();
  290. $object->subtype = 'issue6225';
  291. $object->access_id = ACCESS_PUBLIC;
  292. $object->save();
  293. $guid = $object->guid;
  294. elgg_register_event_handler('update', 'object', array('\ElggCoreRegressionBugsTest', 'handleUpdateForIssue6225test'));
  295. $object->save();
  296. elgg_unregister_event_handler('update', 'object', array('\ElggCoreRegressionBugsTest', 'handleUpdateForIssue6225test'));
  297. _elgg_invalidate_cache_for_entity($guid);
  298. $object = get_entity($guid);
  299. $this->assertEqual($object->access_id, ACCESS_PRIVATE);
  300. $object->delete();
  301. }
  302. public static function handleUpdateForIssue6225test($event, $type, \ElggObject $object) {
  303. $object->access_id = ACCESS_PRIVATE;
  304. }
  305. /**
  306. * elgg_admin_sort_page_menu() should not expect that the supplied menu has a certain hierarchy
  307. *
  308. * https://github.com/Elgg/Elgg/issues/6379
  309. */
  310. function test_admin_sort_page_menu() {
  311. elgg_push_context('admin');
  312. elgg_register_plugin_hook_handler('prepare', 'menu:page', 'elgg_admin_sort_page_menu');
  313. $result = elgg_trigger_plugin_hook('prepare', 'menu:page', array(), array());
  314. $this->assertTrue(is_array($result), "Admin page menu fails to prepare for viewing");
  315. elgg_pop_context();
  316. }
  317. /**
  318. * Tests get_entity_statistics() without owner
  319. * @covers get_entity_statistics()
  320. */
  321. function test_global_get_entity_statistics() {
  322. $subtype = 'issue7845' . rand(0,100);
  323. $object = new \ElggObject();
  324. $object->subtype = $subtype;
  325. $object->save();
  326. $stats = get_entity_statistics();
  327. $this->assertEqual($stats['object'][$subtype], 1);
  328. $object->delete();
  329. }
  330. /**
  331. * Tests get_entity_statistics() with an owner
  332. * @covers get_entity_statistics()
  333. */
  334. function test_owned_get_entity_statistics() {
  335. $user = new \ElggUser();
  336. $user->save();
  337. $subtype = 'issue7845' . rand(0,100);
  338. $object = new \ElggObject();
  339. $object->subtype = $subtype;
  340. $object->owner_guid = $user->guid;
  341. $object->save();
  342. $stats = get_entity_statistics($user->guid);
  343. $this->assertEqual($stats['object'][$subtype], 1);
  344. $user->delete();
  345. }
  346. }