PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/packages/items/item.class.php

https://bitbucket.org/navigatecms/navigatecms
PHP | 785 lines | 624 code | 125 blank | 36 comment | 101 complexity | 733d0cb8bcb9f2a3332933672a23d1d5 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-2.1, BSD-3-Clause, AGPL-3.0, Apache-2.0
  1. <?php
  2. require_once(NAVIGATE_PATH.'/lib/packages/templates/template.class.php');
  3. require_once(NAVIGATE_PATH.'/lib/packages/webdictionary/webdictionary.class.php');
  4. require_once(NAVIGATE_PATH.'/lib/packages/webdictionary/webdictionary_history.class.php');
  5. require_once(NAVIGATE_PATH.'/lib/packages/paths/path.class.php');
  6. require_once(NAVIGATE_PATH.'/lib/packages/webuser_votes/webuser_vote.class.php');
  7. class item
  8. {
  9. public $id;
  10. public $website;
  11. public $association;
  12. public $category;
  13. public $embedding; // 0 => not embedded (own path), 1 => embedded (no path on item)
  14. public $template;
  15. public $date_to_display;
  16. public $date_published;
  17. public $date_unpublish;
  18. public $galleries;
  19. public $date_created;
  20. public $date_modified;
  21. public $comments_enabled_to; // 0 => nobody, 1=>registered, 2=>everyone
  22. public $comments_moderator; // user_id
  23. public $access; // 0 => everyone, 1 => logged in, 2 => not logged in, 3 => selected webuser groups
  24. public $groups;
  25. public $permission; // 0 => public, 1 => private (only navigate cms users), 2 => hidden
  26. public $views;
  27. public $author;
  28. public $votes;
  29. public $score;
  30. public $position;
  31. public $dictionary;
  32. public $paths;
  33. public $properties;
  34. private $_comments_count;
  35. public function load($id)
  36. {
  37. global $DB;
  38. global $website;
  39. if($DB->query('SELECT * FROM nv_items
  40. WHERE id = '.intval($id).'
  41. AND website = '.$website->id))
  42. {
  43. $data = $DB->result();
  44. $this->load_from_resultset($data); // there will be as many entries as languages enabled
  45. }
  46. }
  47. public function load_random($categories=array())
  48. {
  49. global $DB;
  50. global $website;
  51. if($DB->query('SELECT * FROM nv_items
  52. WHERE category IN('.implode(',', $categories).')
  53. AND website = '.$website->id.'
  54. ORDER BY RAND()
  55. LIMIT 1'))
  56. {
  57. $data = $DB->result();
  58. $this->load_from_resultset($data); // there will be as many entries as languages enabled
  59. }
  60. }
  61. public function load_from_resultset($rs)
  62. {
  63. $main = $rs[0];
  64. $this->id = $main->id;
  65. $this->website = $main->website;
  66. $this->association = $main->association;
  67. $this->category = $main->category;
  68. $this->embedding = $main->embedding;
  69. $this->template = $main->template;
  70. $this->date_to_display = (empty($main->date_to_display)? '' : $main->date_to_display);
  71. $this->date_published = (empty($main->date_published)? '' : $main->date_published);
  72. $this->date_unpublish = (empty($main->date_unpublish)? '' : $main->date_unpublish);
  73. $this->date_created = $main->date_created;
  74. $this->date_modified = $main->date_modified;
  75. $this->galleries = mb_unserialize($main->galleries);
  76. $this->comments_enabled_to = $main->comments_enabled_to;
  77. $this->comments_moderator = $main->comments_moderator;
  78. $this->access = $main->access;
  79. $this->permission = $main->permission;
  80. $this->author = $main->author;
  81. $this->views = $main->views;
  82. $this->votes = $main->votes;
  83. $this->score = $main->score;
  84. $this->position = $main->position;
  85. $this->dictionary = webdictionary::load_element_strings('item', $this->id);
  86. $this->paths = path::loadElementPaths('item', $this->id);
  87. // to get the array of groups first we remove the "g" character
  88. $this->groups = $main->groups;
  89. if(!is_array($this->groups))
  90. {
  91. // to get the array of groups first we remove the "g" character
  92. $groups = str_replace('g', '', $this->groups);
  93. $this->groups = explode(',', $groups);
  94. }
  95. if(!is_array($this->groups))
  96. $this->groups = array($this->groups);
  97. if($this->association == 'free')
  98. $this->category = '';
  99. }
  100. public function load_from_post()
  101. {
  102. global $website;
  103. global $user;
  104. $this->association = $_REQUEST['association'][0];
  105. $this->category = intval($_REQUEST['category']);
  106. $this->embedding = ($_REQUEST['embedding'][0]!='1' || $this->association=='free')? '0' : '1';
  107. $this->template = ($this->embedding=='0' || $this->association=='free')? $_REQUEST['template'] : '';
  108. $this->author = intval($_REQUEST['item-author']);
  109. $this->date_to_display = (empty($_REQUEST['date_to_display'])? '' : core_date2ts($_REQUEST['date_to_display']));
  110. $this->date_published = (empty($_REQUEST['date_published'])? '' : core_date2ts($_REQUEST['date_published']));
  111. $this->date_unpublish = (empty($_REQUEST['date_unpublish'])? '' : core_date2ts($_REQUEST['date_unpublish']));
  112. $this->access = intval($_REQUEST['access']);
  113. $this->groups = $_REQUEST['groups'];
  114. if($this->access < 3)
  115. $this->groups = array();
  116. if($user->permission("items.publish") != 'false')
  117. $this->permission = intval($_REQUEST['permission']);
  118. // if comment settings were not visible, keep the original values
  119. if(isset($_REQUEST['item-comments_enabled_to']))
  120. {
  121. $this->comments_enabled_to = intval($_REQUEST['item-comments_enabled_to']);
  122. $this->comments_moderator = intval($_REQUEST['item-comments_moderator']);
  123. }
  124. // language strings and options
  125. $this->dictionary = array();
  126. $this->paths = array();
  127. $template = $this->load_template();
  128. //$fields = array('title', 'body');
  129. $fields = array('title', 'tags');
  130. if(!is_array($template->sections))
  131. $template->sections = array('id' => 'main');
  132. foreach($template->sections as $section)
  133. {
  134. if(is_object($section))
  135. $section = (array) $section;
  136. // compatibility fix: auto-correct template sections with missing ID (only "code" provided)
  137. if(!isset($section['id']))
  138. $section['id'] = $section['code'];
  139. if(is_array($section))
  140. $section = $section['id'];
  141. $fields[] = 'section-'.$section;
  142. }
  143. foreach($_REQUEST as $key => $value)
  144. {
  145. if(empty($value)) continue;
  146. foreach($fields as $field)
  147. {
  148. if(substr($key, 0, strlen($field.'-'))==$field.'-')
  149. $this->dictionary[substr($key, strlen($field.'-'))][$field] = $value;
  150. }
  151. if(substr($key, 0, strlen('path-'))=='path-')
  152. {
  153. // set a path only if "Free" or "Category/Own Path" associations
  154. if( $this->association=='free' ||
  155. ($this->association=='category' && $this->embedding==0) )
  156. {
  157. $this->paths[substr($key, strlen('path-'))] = $value;
  158. }
  159. }
  160. }
  161. // image galleries
  162. $this->galleries = array();
  163. $items = explode("#", $_REQUEST['items-gallery-elements-order']);
  164. if(!is_array($items)) $items = array();
  165. $gallery_items = array();
  166. $gallery_items[0] = array();
  167. if(!is_array($website->languages)) $website->languages = array();
  168. foreach($items as $item)
  169. {
  170. if(empty($item)) continue;
  171. // capture image captions
  172. $gallery_items[0][$item] = array();
  173. foreach($website->languages_list as $lang)
  174. {
  175. $gallery_items[0][$item][$lang] = $_REQUEST['items-gallery-item-'.$item.'-dictionary-'.$lang];
  176. }
  177. }
  178. $this->galleries = $gallery_items;
  179. // galleries[0] = array( [id-file] => array(es => '', ca => '',.. ), [id-file-2] => array... )
  180. }
  181. public function save()
  182. {
  183. if(!empty($this->id))
  184. return $this->update();
  185. else
  186. return $this->insert();
  187. }
  188. public function delete()
  189. {
  190. global $DB;
  191. global $user;
  192. if($user->permission("items.delete") == 'false')
  193. throw new Exception(t(610, "Sorry, you are not allowed to execute this function."));
  194. if(!empty($this->id) && !empty($this->website))
  195. {
  196. // remove dictionary elements
  197. webdictionary::save_element_strings('item', $this->id, array(), $this->website);
  198. // remove path elements
  199. path::saveElementPaths('item', $this->id, array(), $this->website);
  200. // remove all votes assigned to element
  201. webuser_vote::remove_object_votes('item', $this->id);
  202. // remove all element properties
  203. property::remove_properties('item', $this->id, $this->website);
  204. // remove grid notes
  205. grid_notes::remove_all('item', $this->id);
  206. // finally remove the item
  207. $DB->execute('
  208. DELETE FROM nv_items
  209. WHERE id = '.intval($this->id).'
  210. AND website = '.$this->website
  211. );
  212. }
  213. return $DB->get_affected_rows();
  214. }
  215. public function insert()
  216. {
  217. global $DB;
  218. global $website;
  219. global $events;
  220. global $user;
  221. if(!empty($user->id))
  222. {
  223. if( $user->permission("items.create") == 'false' ||
  224. !structure::category_allowed($this->category)
  225. )
  226. throw new Exception(t(610, "Sorry, you are not allowed to execute this function."));
  227. }
  228. $this->date_created = core_time();
  229. $this->date_modified = core_time();
  230. if(empty($this->comments_enabled_to))
  231. {
  232. // apply default comment settings from website properties
  233. $this->comments_enabled_to = $website->comments_enabled_for;
  234. $this->comments_moderator = $website->comments_default_moderator;
  235. if($this->comments_moderator == 'c_author')
  236. $this->comments_moderator = $this->author;
  237. }
  238. $groups = '';
  239. if(is_array($this->groups))
  240. {
  241. $this->groups = array_unique($this->groups); // remove duplicates
  242. $this->groups = array_filter($this->groups); // remove empty
  243. if(!empty($this->groups))
  244. $groups = 'g'.implode(',g', $this->groups);
  245. }
  246. if($groups == 'g')
  247. $groups = '';
  248. if(empty($this->website))
  249. $this->website = $website->id;
  250. if(!empty($user->id) && $user->permission("items.publish") == 'false')
  251. {
  252. if($this->permission == 0)
  253. $this->permission = 1;
  254. }
  255. if(empty($this->position))
  256. {
  257. $last_position_in_category = $DB->query_single('MAX(position)', 'nv_items', ' category = '.value_or_default($this->category, 0));
  258. $default_position = $last_position_in_category + 1;
  259. }
  260. $ok = $DB->execute('
  261. INSERT INTO nv_items
  262. (id, website, association, category, embedding, template,
  263. date_to_display, date_published, date_unpublish, date_created, date_modified, author,
  264. galleries, comments_enabled_to, comments_moderator,
  265. access, groups, permission,
  266. views, votes, score, position)
  267. VALUES
  268. (:id, :website, :association, :category, :embedding, :template,
  269. :date_to_display, :date_published, :date_unpublish, :date_created, :date_modified, :author,
  270. :galleries, :comments_enabled_to, :comments_moderator,
  271. :access, :groups, :permission,
  272. :views, :votes, :score, :position)
  273. ',
  274. array(
  275. ":id" => 0,
  276. ":website" => $this->website,
  277. ":association" => value_or_default($this->association, 'free'),
  278. ":category" => value_or_default($this->category, 0),
  279. ":embedding" => value_or_default($this->embedding, 0),
  280. ":template" => value_or_default($this->template, ''),
  281. ":date_to_display" => intval($this->date_to_display),
  282. ":date_published" => intval($this->date_published),
  283. ":date_unpublish" => intval($this->date_unpublish),
  284. ":date_created" => $this->date_created,
  285. ":date_modified" => $this->date_modified,
  286. ":author" => value_or_default($this->author, ''),
  287. ":galleries" => serialize($this->galleries),
  288. ":comments_enabled_to" => value_or_default($this->comments_enabled_to, 0),
  289. ":comments_moderator" => value_or_default($this->comments_moderator, 0),
  290. ":access" => value_or_default($this->access, 0),
  291. ":groups" => $groups,
  292. ":permission" => value_or_default($this->permission, 0),
  293. ":views" => 0,
  294. ":votes" => 0,
  295. ":score" => 0,
  296. ":position" => value_or_default($this->position, $default_position)
  297. )
  298. );
  299. if(!$ok)
  300. throw new Exception($DB->get_last_error());
  301. $this->id = $DB->get_last_id();
  302. // when item is embedded to a category, set the default title (if not set)
  303. if($this->embedding==1 && !empty($this->category))
  304. {
  305. $cat = new structure();
  306. $cat->load($this->category);
  307. if(!empty($cat->dictionary))
  308. {
  309. foreach($cat->dictionary as $cat_lang => $cat_text)
  310. {
  311. if(empty($this->dictionary[$cat_lang]['title']))
  312. $this->dictionary[$cat_lang]['title'] = $cat_text['title'];
  313. }
  314. }
  315. }
  316. webdictionary::save_element_strings('item', $this->id, $this->dictionary, $this->website);
  317. webdictionary_history::save_element_strings('item', $this->id, $this->dictionary, false, $this->website);
  318. path::saveElementPaths('item', $this->id, $this->paths, $this->website);
  319. if(method_exists($events, 'trigger'))
  320. {
  321. $events->trigger(
  322. 'item',
  323. 'save',
  324. array(
  325. 'item' => $this
  326. )
  327. );
  328. }
  329. return true;
  330. }
  331. public function update()
  332. {
  333. global $DB;
  334. global $events;
  335. global $user;
  336. if(!is_null($user))
  337. {
  338. if($user->permission("items.edit") == 'false' && $this->author != $user->id)
  339. throw new Exception(t(610, "Sorry, you are not allowed to execute this function."));
  340. if( !structure::category_allowed($this->category) )
  341. throw new Exception(t(610, "Sorry, you are not allowed to execute this function."));
  342. }
  343. $this->date_modified = core_time();
  344. $groups = '';
  345. if(is_array($this->groups))
  346. {
  347. $this->groups = array_unique($this->groups); // remove duplicates
  348. $this->groups = array_filter($this->groups); // remove empty
  349. if(!empty($this->groups))
  350. $groups = 'g'.implode(',g', $this->groups);
  351. }
  352. if($groups == 'g')
  353. $groups = '';
  354. $ok = $DB->execute('
  355. UPDATE nv_items
  356. SET
  357. association = :association,
  358. category = :category,
  359. embedding = :embedding,
  360. template = :template,
  361. date_to_display = :date_to_display,
  362. date_published = :date_published,
  363. date_unpublish = :date_unpublish,
  364. date_modified = :date_modified,
  365. author = :author,
  366. galleries = :galleries,
  367. comments_enabled_to = :comments_enabled_to,
  368. comments_moderator = :comments_moderator,
  369. access = :access,
  370. groups = :groups,
  371. permission = :permission,
  372. views = :views,
  373. votes = :votes,
  374. score = :score,
  375. position = :position
  376. WHERE id = :id
  377. AND website = :website',
  378. array(
  379. ":association" => $this->association,
  380. ":category" => $this->category,
  381. ":embedding" => $this->embedding,
  382. ":template" => $this->template,
  383. ":date_to_display" => value_or_default($this->date_to_display, 0),
  384. ":date_published" => value_or_default($this->date_published, 0),
  385. ":date_unpublish" => value_or_default($this->date_unpublish, 0),
  386. ":date_modified" => $this->date_modified,
  387. ":author" => value_or_default($this->author, 0),
  388. ":galleries" => serialize($this->galleries),
  389. ":comments_enabled_to" => value_or_default($this->comments_enabled_to, 0),
  390. ":comments_moderator" => value_or_default($this->comments_moderator, 0),
  391. ":access" => $this->access,
  392. ":groups" => $groups,
  393. ":permission" => $this->permission,
  394. ":views" => $this->views,
  395. ":votes" => $this->votes,
  396. ":score" => $this->score,
  397. ":position" => value_or_default($this->position, 0),
  398. ":id" => $this->id,
  399. ":website" => $this->website
  400. )
  401. );
  402. if(!$ok)
  403. throw new Exception($DB->get_last_error());
  404. webdictionary::save_element_strings('item', $this->id, $this->dictionary, $this->website);
  405. webdictionary_history::save_element_strings('item', $this->id, $this->dictionary, false, $this->website);
  406. path::saveElementPaths('item', $this->id, $this->paths, $this->website);
  407. $events->trigger(
  408. 'item',
  409. 'save',
  410. array(
  411. 'item' => $this
  412. )
  413. );
  414. return true;
  415. }
  416. public function load_template()
  417. {
  418. global $DB;
  419. global $website;
  420. $template = new template();
  421. if( $this->association == 'free' ||
  422. ($this->association == 'category' && $this->embedding == '0'))
  423. {
  424. $template->load($this->template);
  425. }
  426. else
  427. {
  428. $category_template = $DB->query_single(
  429. 'template',
  430. 'nv_structure',
  431. ' id = '.protect($this->category).' AND website = '.$website->id
  432. );
  433. $template->load($category_template);
  434. }
  435. return $template;
  436. }
  437. public function property($property_name, $raw=false)
  438. {
  439. global $DB;
  440. // load properties if not already done
  441. if(empty($this->properties))
  442. {
  443. // check if this is an embedded item or it is a free element
  444. if($this->embedding == 1 && $this->association == 'category')
  445. {
  446. // properties are given in structure definition
  447. $structure_template = @$DB->query_single('template', 'nv_structure', 'id = '.intval($this->category));
  448. $this->properties = property::load_properties('structure', $structure_template, 'item', $this->id);
  449. }
  450. else
  451. {
  452. $this->properties = property::load_properties('item', $this->template, 'item', $this->id);
  453. }
  454. }
  455. for($p=0; $p < count($this->properties); $p++)
  456. {
  457. if($this->properties[$p]->name==$property_name || $this->properties[$p]->id==$property_name)
  458. {
  459. if($raw)
  460. $out = $this->properties[$p]->value;
  461. else
  462. $out = $this->properties[$p]->value;
  463. break;
  464. }
  465. }
  466. return $out;
  467. }
  468. public function property_exists($property_name)
  469. {
  470. global $DB;
  471. // load properties if not already done
  472. if(empty($this->properties))
  473. {
  474. // check if this is an embedded item or it is a free element
  475. if($this->embedding == 1 && $this->association == 'category')
  476. {
  477. // properties are given in structure definition
  478. $structure_template = @$DB->query_single('template', 'nv_structure', 'id = '.intval($this->category));
  479. $this->properties = property::load_properties('structure', $structure_template, 'item', $this->id);
  480. }
  481. else
  482. {
  483. $this->properties = property::load_properties('item', $this->template, 'item', $this->id);
  484. }
  485. }
  486. for($p=0; $p < count($this->properties); $p++)
  487. {
  488. if($this->properties[$p]->name==$property_name || $this->properties[$p]->id==$property_name)
  489. return true;
  490. }
  491. return false;
  492. }
  493. public function property_definition($property_name)
  494. {
  495. global $DB;
  496. // load properties if not already done
  497. if(empty($this->properties))
  498. {
  499. // check if this is an embedded item or it is a free element
  500. if($this->embedding == 1 && $this->association == 'category')
  501. {
  502. // properties are given in structure definition
  503. $structure_template = @$DB->query_single('template', 'nv_structure', 'id = '.intval($this->category));
  504. $this->properties = property::load_properties('structure', $structure_template, 'item', $this->id);
  505. }
  506. else
  507. {
  508. $this->properties = property::load_properties('item', $this->template, 'item', $this->id);
  509. }
  510. }
  511. for($p=0; $p < count($this->properties); $p++)
  512. {
  513. if($this->properties[$p]->name==$property_name || $this->properties[$p]->id==$property_name)
  514. {
  515. $out = $this->properties[$p];
  516. break;
  517. }
  518. }
  519. return $out;
  520. }
  521. public function link($lang)
  522. {
  523. $url = $this->paths[$lang];
  524. if(empty($url))
  525. $url = '/node/'.$this->id;
  526. $url = nvweb_prepare_link($url);
  527. return $url;
  528. }
  529. public function comments_count()
  530. {
  531. global $DB;
  532. if(empty($this->_comments_count))
  533. {
  534. $DB->query('
  535. SELECT COUNT(*) as total
  536. FROM nv_comments
  537. WHERE website = ' . protect($this->website) . '
  538. AND object_type = "item"
  539. AND object_id = ' . protect($this->id) . '
  540. AND status = 0'
  541. );
  542. $out = $DB->result('total');
  543. $this->_comments_count = $out[0];
  544. }
  545. return $this->_comments_count;
  546. }
  547. public static function convert_from_rss($articles = array())
  548. {
  549. global $website;
  550. $items = array();
  551. if(!is_array($articles))
  552. $articles = array();
  553. foreach($articles as $key => $article)
  554. {
  555. $item = new item();
  556. $item->id = 'rss-'.$key;
  557. $item->association = 'free';
  558. $item->category = 0;
  559. $item->embedding = 0;
  560. $item->template = 0;
  561. $item->date_to_display = $article['timestamp'];
  562. $item->date_published = 0;
  563. $item->date_unpublish = 0;
  564. $item->galleries = 0;
  565. $item->date_created = $article['timestamp'];
  566. $item->data_modified = $article['timestamp'];
  567. $item->comments_enabled_to = 2; // 0 => nobody, 1=>registered, 2=>everyone
  568. $item->comments_moderator = 0; // user_id
  569. $item->access = 0; // 0 => everyone, 1 => registered and logged in, 2 => not registered or not logged in
  570. $item->groups = array();
  571. $item->permission = 0; // 0 => public, 1 => private (only navigate cms users), 2 => hidden
  572. $item->views = 0;
  573. $item->author = value_or_default($article['creator'], 0);
  574. $item->votes = 0;
  575. $item->score = 0;
  576. $item->properties = array();
  577. $item->dictionary = array();
  578. $item->paths = array();
  579. foreach($website->languages_list as $wlang)
  580. {
  581. $item->dictionary[$wlang] = array(
  582. 'title' => $article['title'],
  583. 'section-main' => $article['description']
  584. );
  585. $item->paths[$wlang] = $article['link'];
  586. }
  587. $items[] = $item;
  588. }
  589. return $items;
  590. }
  591. public static function reorder($order)
  592. {
  593. global $DB;
  594. global $website;
  595. $items = explode("#", $order);
  596. for($i=0; $i < count($items); $i++)
  597. {
  598. if(empty($items[$i])) continue;
  599. $ok = $DB->execute('UPDATE nv_items
  600. SET position = '.($i+1).'
  601. WHERE id = '.$items[$i].'
  602. AND website = '.$website->id);
  603. if(!$ok) return array("error" => $DB->get_last_error());
  604. }
  605. return true;
  606. }
  607. public function quicksearch($text)
  608. {
  609. global $DB;
  610. global $website;
  611. $where = '';
  612. $search = explode(" ", $text);
  613. $search = array_filter($search);
  614. sort($search);
  615. foreach($search as $text)
  616. {
  617. $like = ' LIKE '.protect('%'.$text.'%');
  618. // we search for the IDs at the dictionary NOW (to avoid inefficient requests)
  619. $DB->query('SELECT DISTINCT (nvw.node_id)
  620. FROM nv_webdictionary nvw
  621. WHERE nvw.node_type = "item"
  622. AND nvw.website = '.$website->id.'
  623. AND nvw.text '.$like, 'array');
  624. $dict_ids = $DB->result("node_id");
  625. // all columns to look for
  626. $cols[] = 'i.id' . $like;
  627. /* INEFFICIENT WAY
  628. $cols[] = 'i.id IN ( SELECT nvw.node_id
  629. FROM nv_webdictionary nvw
  630. WHERE nvw.node_type = "item" AND
  631. nvw.text '.$like.'
  632. )' ;
  633. */
  634. if(!empty($dict_ids))
  635. $cols[] = 'i.id IN ('.implode(',', $dict_ids).')';
  636. $where .= ' AND ( ';
  637. $where .= implode( ' OR ', $cols);
  638. $where .= ')';
  639. }
  640. return $where;
  641. }
  642. public function backup($type='json')
  643. {
  644. global $DB;
  645. global $website;
  646. $out = array();
  647. $DB->query('SELECT * FROM nv_items WHERE website = '.protect($website->id), 'object');
  648. if($type='json')
  649. $out = json_encode($DB->result());
  650. return $out;
  651. }
  652. public static function __set_state(array $obj)
  653. {
  654. $tmp = new item();
  655. foreach($obj as $key => $val)
  656. $tmp->$key = $val;
  657. return $tmp;
  658. }
  659. }
  660. ?>