Source for file story-defs.php

Documentation is available at story-defs.php

  1. <?php
  2. /* ******************************************************************** */
  3. /* CATALYST PHP Source Code */
  4. /* -------------------------------------------------------------------- */
  5. /* This program is free software; you can redistribute it and/or modify */
  6. /* it under the terms of the GNU General Public License as published by */
  7. /* the Free Software Foundation; either version 2 of the License, or */
  8. /* (at your option) any later version. */
  9. /* */
  10. /* This program is distributed in the hope that it will be useful, */
  11. /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
  12. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
  13. /* GNU General Public License for more details. */
  14. /* */
  15. /* You should have received a copy of the GNU General Public License */
  16. /* along with this program; if not, write to: */
  17. /* The Free Software Foundation, Inc., 59 Temple Place, Suite 330, */
  18. /* Boston, MA 02111-1307 USA */
  19. /* -------------------------------------------------------------------- */
  20. /* */
  21. /* Filename: story-defs.php */
  22. /* Author: Paul Waite */
  23. /* Description: Definitions for managing and using stories/news */
  24. /* */
  25. /* ******************************************************************** */
  26. /** @package cm */
  27. include_once("catalog-defs.php");
  28. /** HTMLArea wysiwyg */
  29. ("htmlarea-defs.php");
  30.  
  31. // HTMLArea settings..
  32. htmlarea_plugins("CSS,ContextMenu,ListType,CharacterMap");
  33.  
  34. // Some global widths for form elements etc..
  35. $width = 590;
  36. $width_img_preview = 125;
  37. $height_img_preview = 125;
  38. $width_icon_preview = 90;
  39. $height_icon_preview = 50;
  40. $widthpx = $width . "px";
  41. $smlwidthpx = ceil($width/3) . "px";
  42. $stdwidthpx = ceil($width/2) . "px";
  43. $bigwidthpx = ceil((2 * $width)/3) . "px";
  44.  
  45. /** New story ID indicator */
  46. ("NEW_STORY", -1);
  47.  
  48. /**
  49. * A class which encapsulates a story or article item. Provides methods
  50. * to get/save to database, edit the story in a popup window, and view it.
  51. * Also provides methods to index/unindex to Lucene.
  52. * @package cm
  53. */
  54. class story extends RenderableObject {
  55. var $story_id = NEW_STORY; // Our unique DB key for the story
  56. var $story_category = false; // The group of stories this belongs to
  57. var $story_category_desc = ""; // Wordy descriptive version of above
  58. var $has_media = true; // By category: associated media
  59. var $has_multimedia = false; // By category: more then one assoc. media
  60. var $has_precis = true; // By category: has a precis
  61. var $has_expiry = true; // By category: has an expiry option
  62. var $has_multilang = true; // By category: can be translated
  63. var $language = 0; // Language this story is in
  64. var $story_headline = ""; // Headline of this story (0 = default)
  65. var $story_precis = ""; // The lead-in section of this story
  66. var $story_content = ""; // The main story body content
  67. var $story_author = ""; // The story author - FK from ax_user.user_id
  68. var $story_author_name = ""; // The story author full name
  69. var $story_type = ""; // The story type - 'a' - Article, 'f' - Feature
  70. var $story_date = ""; // Datetime written, DISPLAY_DATE_FORMAT format
  71. var $story_date_ts = 0; // Unix timestamp of datetime story was written
  72. var $expiry_date = ""; // Datetime to expire, DISPLAY_DATE_FORMAT format
  73. var $expiry_date_ts = 0; // Unix timestamp of datetime story should expire
  74. var $lastmodified = ""; // Datetime last modified, NICE_FULLDATETIME format
  75. var $lastmodified_ts = 0; // Unix timestamp of datetime story last modified
  76. var $story_media = array(); // An array of media associated with this story
  77. var $story_icon; // The catalogitem object of the icon image for this story
  78. var $story_icon_url; // The URL for the icon image for this story
  79. var $visible = false; // True if story is visible on the website
  80. var $story_locs = array(); // An array of locations this story is published to
  81. var $story_translations = array(); // An array of media associated with this story
  82. var $root_translation_id = -1; // Story ID of root (original) of translated stories
  83. var $root_translation_lang; // Language of root (original) of translated stories
  84.  
  85. // Internal Flags and Vars..
  86. var $deleted = false; // True if story has been flagged as deleted
  87. var $info_msg = ""; // Contains info/error message as appropriate
  88. var $newstory = false; // True if we just created this story
  89. var $valid = false; // True if story was retreived successfully
  90. var $storymode = ""; // Mode of action on this story
  91. var $formname = ""; // Name of the form we use
  92. var $bytesize = 0; // Size of article + media in bytes
  93. var $wordcount = 0; // Number of words written
  94. // .....................................................................
  95. /** Constructor
  96. * @param mixed $id Story ID, or false if not known
  97. * @param mixed $category String category identifier, or false if unknown
  98. * @param mixed $language Integer language code, or false if default
  99. */
  100. function story($id=false, $category=false, $language=false) {
  101. global $storymode;
  102. global $story_id, $cat, $lang;
  103.  
  104. // Set up our vars..
  105. $this->initialise();
  106.  
  107. // Form..
  108. $this->formname = "storyfm";
  109.  
  110. // Default the mode..
  111. if (!isset($storymode)) $this->storymode = "view";
  112. else $this->storymode = $storymode;
  113.  
  114. // Set the story ID..
  115. if ($id === false) {
  116. if (isset($story_id)) {
  117. $this->story_id = $story_id;
  118. }
  119. }
  120. else {
  121. $this->story_id = $id;
  122. }
  123.  
  124. // Set the category..
  125. if ($category === false) {
  126. if (isset($cat)) {
  127. $this->story_category = $cat;
  128. }
  129. }
  130. else {
  131. $this->story_category = $category;
  132. }
  133.  
  134. // Set the language..
  135. if ($language === false) {
  136. if (isset($lang)) {
  137. $this->language = $lang;
  138. }
  139. }
  140. else {
  141. $this->language = $language;
  142. }
  143.  
  144. // Detect new story creation..
  145. if ($this->story_id === false || $this->story_id == NEW_STORY) {
  146. // Create a brand new one
  147. $this->story_id = get_next_sequencevalue("seq_story_id", "ax_story", "story_id");
  148. $this->story_date_ts = time();
  149. $this->story_date = timestamp_to_displaydate(DISPLAY_DATE_FORMAT, $this->story_date_ts);
  150. $this->newstory = true;
  151. $this->get_author_info();
  152. $this->get_category_info();
  153. $this->get_default_locations();
  154. $this->valid = true;
  155. $this->storymode = "adding";
  156. }
  157. // Further processing for existing stories..
  158. if (!$this->newstory) {
  159. if ($this->storymode == "adding") {
  160. $this->newstory = true;
  161. $this->valid = true;
  162. }
  163. else {
  164. // Attempt to get the story
  165. $this->get_story();
  166. }
  167. // Process POST from form..
  168. $this->POSTprocess();
  169.  
  170. // Final get for display..
  171. $this->get_story();
  172. }
  173. } // story
  174. // .....................................................................
  175. /** Initialise local vars.. */
  176.  
  177. function initialise() {
  178. global $RESPONSE;
  179. $this->language = 0;
  180. $this->story_headline = "";
  181. $this->story_content = "";
  182. $this->story_precis = "";
  183. if (isset($RESPONSE)) {
  184. $this->story_author = $RESPONSE->userid;
  185. $this->story_author_name = $RESPONSE->name;
  186. }
  187. else {
  188. $this->story_author = "";
  189. $this->story_author_name = "";
  190. }
  191. $this->story_type = "a";
  192. $this->story_date = "";
  193. $this->story_date_ts = 0;
  194. $this->expiry_date = "";
  195. $this->expiry_date_ts = 0;
  196. $this->lastmodified = "";
  197. $this->lastmodified_ts = 0;
  198. $this->deleted = false;
  199. $this->visible = true;
  200. $this->newstory = false;
  201. $this->info_msg = "";
  202. $this->valid = false;
  203. } // story initialise
  204. // .....................................................................
  205. /**
  206. * Get a story in total. We always access stories by their ID.
  207. * @param mixed $id Story ID, or false if not known
  208. */
  209. function get_story($id=false) {
  210. $res = false;
  211. if ($id !== false) $this->story_id = $id;
  212. if ($this->story_id !== false) {
  213. $q = "SELECT * FROM ax_story s";
  214. $q .= " WHERE story_id=$this->story_id";
  215. $storyQ = dbrecordset($q);
  216. if ($storyQ->hasdata) {
  217. // Main story content..
  218. $this->language = $storyQ->field("lang_id");
  219. $this->story_category = $storyQ->field("category_id");
  220. $this->story_headline = $storyQ->field("story_headline");
  221. $this->story_precis = $storyQ->field("story_precis");
  222. $this->story_content = $storyQ->field("story_content");
  223. $this->story_author = $storyQ->field("story_author");
  224. $this->story_type = $storyQ->field("story_type");
  225. $this->story_icon_url = $storyQ->field("story_icon_url");
  226. if ($storyQ->field("story_icon") != "") {
  227. $iconitem = new catalogitem($storyQ->field("story_icon"));
  228. if ($iconitem->valid) {
  229. $this->story_icon = new story_media($this->story_id, $iconitem);
  230. }
  231. }
  232. // Dates and flags..
  233. $story_date = $storyQ->field("story_date");
  234. if ($story_date != "") {
  235. $this->story_date = datetime_to_displaydate(NICE_FULLDATETIME, $story_date);
  236. $this->story_date_ts = datetime_to_timestamp($story_date);
  237. }
  238. $expiry_date = $storyQ->field("expiry_date");
  239. if ($expiry_date != "") {
  240. $this->expiry_date = datetime_to_displaydate(NICE_FULLDATETIME, $expiry_date);
  241. $this->expiry_date_ts = datetime_to_timestamp($expiry_date);
  242. }
  243. $this->lastmodified = datetime_to_displaydate(NICE_FULLDATETIME, $storyQ->field("last_modified"));
  244. $this->lastmodified_ts = datetime_to_timestamp($storyQ->field("last_modified"));
  245. $this->deleted = $storyQ->istrue("deleted");
  246. $this->visible = $storyQ->istrue("visible");
  247. $res = true;
  248.  
  249. // Now go grab sundry other associated story info..
  250. $this->get_author_info();
  251. $this->get_category_info();
  252. if ($this->has_media) {
  253. $this->get_story_media();
  254. }
  255. $this->get_story_locations();
  256. $this->get_story_metrics();
  257. }
  258. else {
  259. $this->info_msg = "No record of story ID: $this->story_id";
  260. }
  261. }
  262. else {
  263. $this->info_msg = "No story ID given";
  264. }
  265. // Did we succeed..?
  266. $this->valid = $res;
  267. return $res;
  268. } // story get_story
  269. // .....................................................................
  270. /**
  271. * Determine wwhether user can edit this story.
  272. * @return boolean True if user can edit the story, else false.
  273. */
  274. function user_can_edit() {
  275. global $RESPONSE;
  276. $can = false;
  277. if ($RESPONSE->ismemberof_group("Editor")
  278. || ($RESPONSE->ismemberof_group("Author") && $this->story_author == $RESPONSE->userid)
  279. ) {
  280. $can = true;
  281. }
  282. return $can;
  283. } // user_can_edit
  284. // .....................................................................
  285. /**
  286. * Get story author info. Allow override of user id via argument
  287. * passed in, otherwise use the resident story author ID.
  288. * @param string $userid Override user_id to use to get info
  289. * @access private
  290. */
  291. function get_author_info($userid=false) {
  292. if ($userid !== false) {
  293. $story_author = $userid;
  294. }
  295. else {
  296. $story_author = $this->story_author;
  297. }
  298. if ($story_author != "" && $story_author !== false) {
  299. $su = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . addslashes($story_author) . "'");
  300. if ($su->hasdata) {
  301. $this->story_author_name = $su->field("full_name");
  302. $this->story_author = $story_author;
  303. }
  304. }
  305. } // get_author_info
  306. // .....................................................................
  307. /**
  308. * Get story category info. Allow override of category id via argument
  309. * passed in, otherwise use the resident story category ID.
  310. * @param integer $catid Override category_id to use to get info
  311. * @access private
  312. */
  313. function get_category_info($catid=false) {
  314. if ($catid !== false) {
  315. $story_category = $catid;
  316. }
  317. else {
  318. $story_category = $this->story_category;
  319. }
  320. if ($story_category != "" && $story_category !== false) {
  321. $cat = dbrecordset("SELECT * FROM ax_story_category WHERE category_id=$story_category");
  322. if ($cat->hasdata) {
  323. $this->story_category_desc = $cat->field("category_desc");
  324. $this->has_media = $cat->istrue("has_media");
  325. $this->has_multimedia = $cat->istrue("has_multimedia");
  326. $this->has_precis = $cat->istrue("has_precis");
  327. $this->has_expiry = $cat->istrue("has_expiry");
  328. $this->has_multilang = $cat->istrue("has_multilang");
  329. $this->story_category = $story_category;
  330. }
  331. }
  332. } // get_category_info
  333. // .....................................................................
  334. /**
  335. * Get media associated with this story. This should be called after the
  336. * story category info has been ascertained. This method populates the
  337. * class variable 'story_media', an array which contains media catalog
  338. * ID and the filename separated by "|".
  339. * @access private
  340. */
  341. function get_story_media() {
  342. $this->story_media = array();
  343. $q = "SELECT * FROM ax_story_media";
  344. $q .= " WHERE story_id=$this->story_id";
  345. $q .= " ORDER BY display_order";
  346. $sm = dbrecordset($q);
  347. if ($sm->hasdata) {
  348. do {
  349. $cat_id = $sm->field("cat_id");
  350. $media = new story_media($this->story_id);
  351. $media->get_catalogitem($cat_id);
  352. $media->justify = $sm->field("justify");
  353. $media->caption = $sm->field("caption");
  354. $media->width = $sm->field("width");
  355. $media->height = $sm->field("height");
  356. $this->story_media[$cat_id] = $media;
  357. } while ($sm->get_next());
  358. }
  359. } // get_story_media
  360. // .....................................................................
  361. /**
  362. * Get the story locations defined for this story. This method
  363. * is an internal one designed to be used to populate the current
  364. * locations to publish the story in.
  365. * @access private
  366. */
  367. function get_story_locations() {
  368. $this->story_locs = array();
  369. $q = "SELECT * FROM ax_story_location";
  370. $q .= " WHERE story_id=$this->story_id";
  371. $loc = dbrecordset($q);
  372. if ($loc->hasdata) {
  373. do {
  374. $locid = $loc->field("location_id");
  375. $this->story_locs[] = $locid;
  376. } while ($loc->get_next());
  377. }
  378. } // get_story_locations
  379. // .....................................................................
  380. /**
  381. * Get the default locations for this story category. This method
  382. * is an internal one designed to be used to populate the initial
  383. * locations to publish a new story to.
  384. * @access private
  385. */
  386. function get_default_locations() {
  387. $this->story_locs = array();
  388. $q = "SELECT * FROM ax_story_category_locs";
  389. $q .= " WHERE category_id=$this->story_category";
  390. $loc = dbrecordset($q);
  391. if ($loc->hasdata) {
  392. do {
  393. $locid = $loc->field("location_id");
  394. $this->story_locs[] = $locid;
  395. } while ($loc->get_next());
  396. }
  397. } // get_default_locations
  398. // .....................................................................
  399. /**
  400. * Get the story locations defined for this story. This method
  401. * is an internal one designed to be used to populate the current
  402. * locations to publish the story in.
  403. * @access private
  404. */
  405. function get_story_metrics() {
  406. global $RESPONSE;
  407. $words = $this->story_headline . $this->story_precis . $this->story_content;
  408. $bytesize = sizeof($words);
  409. if (count($this->story_media) > 0) {
  410. foreach ($this->story_media as $media) {
  411. if (isset($media->catalogitem) && $media->catalogitem->filepath != "") {
  412. if (file_exists($RESPONSE->site_docroot . $media->catalogitem->filepath)) {
  413. $bytesize += filesize($RESPONSE->site_docroot . $media->catalogitem->filepath);
  414. }
  415. }
  416. } // foreach
  417. }
  418. $this->bytesize = $bytesize;
  419. $this->wordcount = $this->word_count();
  420. } // get_story_metrics
  421. // .....................................................................
  422. /**
  423. * Get the stories which are translated versions of this one.
  424. * @access private
  425. */
  426. function get_story_translations() {
  427. $this->story_translations = array();
  428. // Find root story info for this set of translations..
  429. debugbr("translation family: this story ID is $this->story_id");
  430. $this->root_translation_id = -1;
  431. $q = "SELECT st.story_id as sid, s.lang_id as lang";
  432. $q .= " FROM ax_story_translation st, ax_story s";
  433. $q .= " WHERE st.translated_story_id=$this->story_id";
  434. $q .= " AND s.story_id=st.story_id";
  435. $q .= " AND s.deleted=FALSE";
  436. $q .= " UNION ";
  437. $q .= "SELECT st.story_id as sid, s.lang_id as lang";
  438. $q .= " FROM ax_story_translation st, ax_story s";
  439. $q .= " WHERE st.story_id=$this->story_id";
  440. $q .= " AND s.story_id=st.story_id";
  441. $q .= " AND s.deleted=FALSE";
  442. $trans = dbrecordset($q);
  443. if ($trans->hasdata) {
  444. $this->root_translation_id = $trans->field("sid");
  445. $this->root_translation_lang = $trans->field("lang");
  446. debugbr("translation story id root = $this->root_translation_id");
  447. // Add root story if it's not us..
  448. if ($this->root_translation_id != $this->story_id) {
  449. debugbr("ADDING translation story id (root)=$this->root_translation_id lang=$this->root_translation_lang");
  450. $this->story_translations[$this->root_translation_id] = $this->root_translation_lang;
  451. }
  452. }
  453. // Now get all translations of the root..
  454. $q = "SELECT st.translated_story_id as sid, s.lang_id as lang";
  455. $q .= " FROM ax_story_translation st, ax_story s";
  456. $q .= " WHERE st.story_id=$this->root_translation_id";
  457. $q .= " AND s.story_id=st.translated_story_id";
  458. $q .= " AND s.deleted=FALSE";
  459. $trans = dbrecordset($q);
  460. if ($trans->hasdata) {
  461. do {
  462. $storyid = $trans->field("sid");
  463. $langid = $trans->field("lang");
  464. // Add story if it's not us..
  465. if ($storyid != $this->story_id) {
  466. debugbr("ADDING translation story id=$storyid lang=$langid");
  467. $this->story_translations[$storyid] = $langid;
  468. }
  469. } while ($trans->get_next());
  470. }
  471. } // get_story_translations
  472. // .....................................................................
  473. /** Index this story to Lucene, if enabled for this website. */
  474.  
  475. function index() {
  476. global $CONTEXT, $LIBDIR;
  477. // Deal with Lucene indexing if enabled. In this case we then
  478. // use the unique story_id as the index ID, and index the story
  479. // heading and body text. We also categorise it..
  480. if ( isset($CONTEXT) && $CONTEXT->configvalue("Lucene Site Indexing") ) {
  481. include_once("lucene-defs.php");
  482. $allcontent[] = $this->story_headline;
  483. $allcontent[] = $this->story_precis;
  484. $allcontent[] = $this->story_content;
  485. $I = new lucene_indexmsg();
  486. $I->index_field("category:Text", "news");
  487. $I->index_field("title:Text", $this->story_headline);
  488. $I->index_field("story_date:Date", $this->story_date_ts);
  489. $I->index_field("story_author:Text", $this->story_author);
  490. $I->index_field("story_lang:Text", $this->language);
  491. $I->index_field("story_category:Text", $this->story_category);
  492. $I->index_field("story_type:Text", $this->story_type);
  493. if ($this->story_url != "") {
  494. $I->index_field("story_url:Text", $this->story_url);
  495. }
  496. $I->index_content($this->story_id, strip_tags(implode(" ", $allcontent)));
  497. $I->send();
  498. }
  499. } // story index
  500. // .....................................................................
  501. /** Un-Index this story from Lucene, if enabled for this website. */
  502.  
  503. function unindex() {
  504. global $CONTEXT, $LIBDIR;
  505. if ( isset($CONTEXT) && $CONTEXT->configvalue("Lucene Site Indexing") ) {
  506. include_once("lucene-defs.php");
  507. $UI = new lucene_unindexmsg();
  508. $UI->unindex($this->story_id);
  509. $UI->send();
  510. }
  511. } // unindex
  512. // .....................................................................
  513. /** Routine to save the story to the database. */
  514.  
  515. function save_story() {
  516. if ($this->story_id) {
  517. if ($this->newstory) {
  518. $sup = new dbinsert("ax_story");
  519. $sup->set("story_id", $this->story_id);
  520. }
  521. else {
  522. $sup = new dbupdate("ax_story");
  523. }
  524. $sup->set("lang_id", $this->language);
  525. $sup->set("story_headline", $this->story_headline);
  526. $sup->set("story_precis", $this->story_precis);
  527. $sup->set("story_content", $this->story_content);
  528. $sup->set("story_author", ($this->story_author != "") ? $this->story_author : NULLVALUE);
  529. $sup->set("story_type", $this->story_type);
  530. $sup->set("story_icon_url", $this->story_icon_url);
  531. $sup->set("category_id", ($this->story_category !== false) ? $this->story_category : NULLVALUE);
  532. if (isset($this->story_icon) && $this->story_icon !== "") {
  533. $sup->set("story_icon", $this->story_icon->catalogitem->cat_id);
  534. }
  535. else {
  536. $sup->set("story_icon", NULLVALUE);
  537. }
  538. if ($this->story_date_ts == 0) {
  539. $sup->set("story_date", NULLVALUE);
  540. }
  541. else {
  542. $sup->set("story_date", timestamp_to_datetime($this->story_date_ts));
  543. }
  544. if ($this->expiry_date_ts == 0) {
  545. $sup->set("expiry_date", NULLVALUE);
  546. }
  547. else {
  548. $sup->set("expiry_date", timestamp_to_datetime($this->expiry_date_ts));
  549. }
  550. $sup->set("last_modified", 'now()');
  551. $sup->set("visible", $this->visible);
  552. $sup->set("deleted", $this->deleted);
  553. $sup->where("story_id=$this->story_id");
  554. $sup->execute();
  555. }
  556. } // save_story
  557. // .....................................................................
  558. /** Remove the story from the system. We actually just flag it as
  559. * deleted on the database, and keep the record.
  560. */
  561. function delete_story() {
  562. global $CONTEXT;
  563. if ($this->valid && !$this->deleted) {
  564. $del = new dbupdate("ax_story");
  565. $del->set("deleted", true);
  566. $del->where("story_id=$this->story_id");
  567. $this->deleted = $del->execute();
  568. if ($this->deleted) {
  569. $this->unindex();
  570. }
  571. }
  572. } // delete_story
  573. // .....................................................................
  574. /** Process the POST from form. This method deals with POSTed content
  575. * from the edit form.
  576. */
  577. function POSTprocess() {
  578. global $storysave_x; // Clicked save
  579. global $storyedit_x; // Clicked edit
  580. global $storycancel_x; // Clicked cancel
  581. global $translate_x; // Clicked translate
  582. global $story_headline; // Story headline
  583. global $story_precis; // Story precis/lead-in
  584. global $story_content; // Story content
  585. global $story_media; // Reference to picture, movie etc.
  586. global $story_icon; // Reference to icon catalog item
  587. global $story_icon_url; // URL for story icon
  588. global $uploadmedia; // Uploaded media file present
  589. global $story_locs; // List of locations to publish to
  590. global $caption; // New image caption
  591. global $media_justify; // Image justify setting 'left' or 'right'
  592. global $media_width; // Image width px
  593. global $media_height; // Image height px
  594. global $story_author; // Story author
  595. global $story_type; // Story type
  596. global $story_date; // Story date setting
  597. global $story_language; // Story language setting
  598. global $new_language; // New story langauage - translated
  599. global $expiry_date; // Expiry date setting
  600. global $visible; // Visible flag
  601.  
  602. // Save story
  603. if (isset($storysave_x)) {
  604. if (isset($story_headline)) $this->story_headline = $story_headline;
  605. if (isset($story_precis)) $this->story_precis = $story_precis;
  606. if (isset($story_content)) $this->story_content = $story_content;
  607. if (isset($story_author)) $this->story_author = $story_author;
  608. if (isset($story_type)) $this->story_type = $story_type;
  609. if (isset($story_icon_url)) $this->story_icon_url = $story_icon_url;
  610. if ($this->has_multilang && isset($story_language)) {
  611. $this->language = $story_language;
  612. }
  613.  
  614. // Story dates..
  615. if (isset($story_date)) {
  616. $this->story_date = $story_date;
  617. if ($story_date != "") {
  618. $this->story_date_ts = strtotime($story_date);
  619. debugbr("story_date=$story_date ts=$this->story_date_ts");
  620. $this->story_date = timestamp_to_displaydate(NICE_FULLDATETIME, $this->story_date_ts);
  621. debugbr("translated story date=$this->story_date");
  622. }
  623. else {
  624. $this->story_date_ts = 0;
  625. }
  626. }
  627. if ($this->has_expiry && isset($expiry_date)) {
  628. $this->expiry_date = $expiry_date;
  629. if ($expiry_date != "") {
  630. $this->expiry_date_ts = strtotime($expiry_date);
  631. $this->expiry_date = timestamp_to_displaydate(NICE_FULLDATETIME, $this->expiry_date_ts);
  632. }
  633. else {
  634. $this->expiry_date_ts = 0;
  635. }
  636. }
  637.  
  638. // Visible flag..
  639. $this->visible = isset($visible);
  640.  
  641. // Story icon
  642. if (isset($story_icon) && $story_icon != "") {
  643. $icon_bits = explode("|", $story_icon);
  644. $cat_id = $icon_bits[0];
  645. $newci = new catalogitem($cat_id);
  646. if ($newci->valid) {
  647. $this->story_icon = new story_media($this->story_id, $newci);
  648. }
  649. }
  650. else {
  651. unset($this->story_icon);
  652. }
  653.  
  654. // Save it if changed..
  655. $this->save_story();
  656.  
  657. // Media data POSTings..
  658. if ($this->has_media) {
  659. // Some defaults..
  660. if ($media_width == "") $media_width = 0;
  661. if ($media_height == "") $media_height = 0;
  662.  
  663. // Deal with a new media file upload. In this case we
  664. // assume we are just adding, if we have multimedia set
  665. // otherwise we will clear out pre-existing media first..
  666. $smdel = new dbdelete("ax_story_media");
  667. $smdel->where("story_id=$this->story_id");
  668. $smdel->execute();
  669. $this->story_media = array();
  670. if (isset($uploadmedia) && $uploadmedia != "none" && $uploadmedia != "") {
  671. $newci = new catalogitem();
  672. $errmsgs = $newci->upload($caption, $this->category);
  673. if ($newci->valid) {
  674. $smin = new dbinsert("ax_story_media");
  675. $smin->set("story_id", $this->story_id);
  676. $smin->set("cat_id", $newci->cat_id);
  677. $smin->set("caption", $caption);
  678. $smin->set("justify", $media_justify);
  679. $smin->set("width", $media_width);
  680. $smin->set("height", $media_height);
  681. $smin->execute();
  682. $media = new story_media($this->story_id, $newci);
  683. $media->caption = $caption;
  684. $media->justify = $media_justify;
  685. $media->width = $media_width;
  686. $media->height = $media_height;
  687. $this->story_media[$newci->cat_id] = $media;
  688. }
  689. else {
  690. if (count($errmsgs) > 0) {
  691. $this->info_msg = implode("<br>", $errmsgs);
  692. }
  693. }
  694. }
  695. // Otherwise, just re-create the media refs..
  696. else {
  697. $smdel = new dbdelete("ax_story_media");
  698. $smdel->where("story_id=$this->story_id");
  699. $smdel->execute();
  700. // Turn incoming value(s) into an array..
  701. $new_story_media = array();
  702. if (is_array($story_media)) {
  703. $new_story_media = $story_media;
  704. }
  705. elseif ($story_media != "") {
  706. $new_story_media = array($story_media);
  707. }
  708. // Start from scratch and rebuild media array..
  709. $this->story_media = array();
  710. foreach ($new_story_media as $cat_info) {
  711. if ($cat_info != "") {
  712. $cat_bits = explode("|", $cat_info);
  713. $cat_id = $cat_bits[0];
  714. $newci = new catalogitem($cat_id);
  715. if ($newci->valid) {
  716. // Save it to database..
  717. $ssin = new dbinsert("ax_story_media");
  718. $ssin->set("story_id", $this->story_id);
  719. $ssin->set("cat_id", $cat_id);
  720. $ssin->set("caption", $caption);
  721. $ssin->set("justify", $media_justify);
  722. $ssin->set("width", $media_width);
  723. $ssin->set("height", $media_height);
  724. $ssin->execute();
  725. // Save it to local media array too..
  726. $media = new story_media($this->story_id, $newci);
  727. $media->caption = $caption;
  728. $media->justify = $media_justify;
  729. $media->width = $media_width;
  730. $media->height = $media_height;
  731. $this->story_media[$cat_id] = $media;
  732. }
  733. }
  734. } // foreach
  735. }
  736. } // has_media
  737.  
  738. // Story publish to locations..
  739. $sldel = new dbdelete("ax_story_location");
  740. $sldel->where("story_id=$this->story_id");
  741. $sldel->execute();
  742. $this->story_locs = array();
  743. if (isset($story_locs)) {
  744. foreach ($story_locs as $loc_id) {
  745. if ($loc_id != "") {
  746. $slin = new dbinsert("ax_story_location");
  747. $slin->set("story_id", $this->story_id);
  748. $slin->set("location_id", $loc_id);
  749. $slin->execute();
  750. }
  751. }
  752. $this->story_locs = $story_locs;
  753. }
  754. $this->info_msg = "Story was saved";
  755. $this->storymode = "viewagain";
  756. } // storysave_x
  757.  
  758. // Edit story
  759. elseif (isset($storyedit_x)) {
  760. $this->storymode = "edit";
  761. } // storyedit_x
  762.  
  763. // Cancel current mode
  764. elseif (isset($storycancel_x)) {
  765. $this->storymode = "viewagain";
  766. } // storycancel_x
  767.  
  768. // Translate current story into new language.
  769. elseif (isset($translate_x)) {
  770. $translation = $this->get_translation($new_language);
  771. if ($translation !== false) {
  772. $this->get_story($translation);
  773. }
  774. } // translate_x
  775.  
  776. // Remove story
  777. elseif ($this->storymode == "remove") {
  778. $this->delete_story();
  779. $this->storymode = "viewagain";
  780. } // remove
  781. } // story POSTprocess
  782. // .....................................................................
  783. /**
  784. * Returns the story_id of a translation of the current story in the
  785. * given language. If it already exists, then it just returns the story
  786. * ID. If it doesn't exist, then it simply makes a copy of this story,
  787. * assigns it the language it _will_ be translated into, and records a
  788. * relationship to the other associated translations in the database table
  789. * 'story_tranlsation'. This latter table allows us to put a list of
  790. * languages (or little country flags) on any stories which have alternatives
  791. * in another language.
  792. * @param integer $language Language to get translated story in
  793. */
  794. function get_translation($language) {
  795. // Check if this story already has a translation available, and
  796. // we just return the translated story id if so..
  797. $this->get_story_translations();
  798. foreach ($this->story_translations as $translated_storyid => $chklang) {
  799. if ($chklang == $language) {
  800. return $translated_storyid;
  801. break;
  802. }
  803. }
  804.  
  805. // Preserve some info for use later on..
  806. $original_story_id = $this->story_id;
  807. $original_translations = $this->story_translations;
  808.  
  809. // Create new story..
  810. start_transaction();
  811. $this->story_id = get_next_sequencevalue("seq_story_id", "ax_story", "story_id");
  812. // Set the new language..
  813. $this->language = $language;
  814. $this->story_date_ts = time();
  815. $this->story_date = timestamp_to_displaydate(DISPLAY_DATE_FORMAT, $this->story_date_ts);
  816. $this->newstory = true;
  817. $this->valid = true;
  818. $this->save_story();
  819.  
  820. // Duplicate media..
  821. $ord = 1;
  822. foreach ($this->story_media as $cat_id => $media) {
  823. $in = new dbinsert("ax_story_media");
  824. $in->set("story_id", $this->story_id);
  825. $in->set("cat_id", $cat_id);
  826. $in->set("caption", $media->caption);
  827. $in->set("width", $media->width);
  828. $in->set("height", $media->height);
  829. $in->set("justify", $media->justify);
  830. $in->set("display_order", $ord++);
  831. $in->execute();
  832. }
  833. // Duplicate sports..
  834. foreach ($this->story_sports as $sport_id) {
  835. $in = new dbinsert("ax_story_sport");
  836. $in->set("story_id", $this->story_id);
  837. $in->set("sport_id", $sport_id);
  838. $in->execute();
  839. }
  840. // Duplicate locations..
  841. $ord = 1;
  842. foreach ($this->story_locs as $loc_id) {
  843. $in = new dbinsert("ax_story_location");
  844. $in->set("story_id", $this->story_id);
  845. $in->set("location_id", $loc_id);
  846. $in->set("display_order", $ord++);
  847. $in->execute();
  848. }
  849. if (commit()) {
  850. // Create translated story relationship..
  851. if ($this->root_translation_id == -1) {
  852. $root_trans_id = $original_story_id;
  853. }
  854. else {
  855. $root_trans_id = $this->root_translation_id;
  856. }
  857. $in = new dbinsert("ax_story_translation");
  858. $in->set("story_id", $root_trans_id);
  859. $in->set("translated_story_id", $this->story_id);
  860. $in->execute();
  861. }
  862. return $this->story_id;
  863. } // translate_into
  864. // .....................................................................
  865. /** Do a re-count of the story words. Set our local variable
  866. * and also return the value as a by-product..
  867. * @return integer Count of words in the story
  868. */
  869. function word_count() {
  870. $words = explode(" ",
  871. $this->story_headline . " "
  872. . $this->story_precis . " "
  873. . $this->story_content
  874. );
  875. return count($words);
  876. } // word_count
  877. // .....................................................................
  878. /** Generate a precis from the story content.
  879. * @param integer $maxwords Maximum number of words for the precis
  880. * @param integer $minwords Minimum number of words for the precis
  881. * @return string The precis
  882. */
  883. function make_precis($maxwords=50, $minwords=5) {
  884. $precis = "";
  885. $patt = "(([\S]+[\s]+){" . $minwords . "," . $maxwords . "})";
  886. preg_match("/$patt/", strip_tags($this->story_content), $matches);
  887. if (isset($matches[1])) {
  888. $precis = $matches[1];
  889. }
  890. $precis = str_replace("\x0d\x0a", " ", $precis);
  891. return $precis;
  892. } // make_precis
  893. // .....................................................................
  894. /**
  895. * Return the rendering of the story icon (if one exists) either as a
  896. * standard HTML anchor tag if an icon URL exists, or as an image.
  897. * @return string HTML for anchor or image of the story icon
  898. */
  899. function render_story_icon() {
  900. $s = "";
  901. if (isset($this->story_icon) && is_object($this->story_icon)) {
  902. $s = $this->story_icon->catalogitem->Insitu();
  903. if ($this->story_icon_url != "") {
  904. $a = new anchor($this->story_icon_url, $s);
  905. if (stristr($this->story_icon_url, "http")) {
  906. $a->settarget("_new");
  907. }
  908. $s = $a->render();
  909. }
  910. }
  911. return $s;
  912. } // render_story_icon
  913. // .....................................................................
  914. /** Render the story details as an edit form.
  915. * @return string The editform complete.
  916. */
  917. function editform() {
  918. global $RESPONSE;
  919. global $LIBDIR, $CMDIR, $IMAGESDIR;
  920. global $width, $width_img_preview;
  921. global $widthpx, $smlwidthpx, $stdwidthpx, $bigwidthpx;
  922.  
  923. // CONTROL BUTTONS
  924. $s = "";
  925. if ($this->user_can_edit()) {
  926. $savb = new form_imagebutton("storysave", "Save", "", "$LIBDIR/img/_save.gif", "Save changes", 57, 15);
  927. $canb = new form_imagebutton("storycancel", "Cancel", "", "$LIBDIR/img/_cancel.gif", "Cancel", 57, 15);
  928. if ($this->newstory) {
  929. $canb->set_onclick("window.close()");
  930. }
  931. $s .= $savb->render() . "&nbsp;&nbsp;" . $canb->render();
  932. }
  933. $CONTROL_BUTTONS = $s;
  934.  
  935. $Tst = new table("story");
  936. $Tst->setpadding(4);
  937.  
  938. $rowbg = "axbglite";
  939.  
  940. // EDITOR HEADER
  941. $Thd = new table("editor heading");
  942. $Thd->tr($rowbg);
  943. $title = "$this->story_category_desc ";
  944. if ($this->newstory) {
  945. $title .= "New Article";
  946. }
  947. else {
  948. $title .= "Editor";
  949. }
  950. $Thd->td("<h3>$title</h3>", "axfg");
  951. $Thd->td($CONTROL_BUTTONS);
  952. $Thd->td_alignment("right");
  953. $Tst->tr($rowbg);
  954. $Tst->td($Thd->render(), "border-bottom:1px solid black");
  955. $Tst->td_colspan(2);
  956.  
  957. // ERRMSG
  958. if ($this->info_msg != "") {
  959. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  960. $Tst->tr($rowbg);
  961. $Tst->td($this->info_msg, "axerror");
  962. $Tst->td_colspan(2);
  963. $Tst->td_alignment("center");
  964. }
  965.  
  966. // HEADLINE
  967. $Fld = new form_textfield("story_headline", "Headline", $this->story_headline);
  968. $Fld->setclass("axtxtbox");
  969. $Fld->setstyle("width:$widthpx;");
  970. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  971. $Tst->tr($rowbg);
  972. $Tst->td("Headline:", "axfg");
  973. $Tst->td_css("padding-top:10px;");
  974. $Tst->td($Fld->render());
  975. $Tst->td_css("padding-top:10px;");
  976.  
  977. // PRECIS/LEAD IN
  978. if ($this->has_precis) {
  979. $Fld = new form_wysiwygfield("story_precis", "Lead In", $this->story_precis);
  980. $Fld->setclass("axmemo");
  981. $Fld->setstyle("width:$widthpx;height:120px;");
  982. $Fld->set_statusbar(false);
  983. $Fld->set_toolbar("basic");
  984. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  985. $Tst->tr($rowbg);
  986. $Tst->td("Lead In:", "axfg");
  987. $Tst->td_css("vertical-align:top;padding-top:5px;");
  988. $Tst->td($Fld->render());
  989. }
  990.  
  991. // STORY AUTHOR
  992. if ($RESPONSE->ismemberof_group("Editor")) {
  993. // Editors can change the author..
  994. $Fld = new form_combofield("story_author", "Author", $this->story_author);
  995. $Fld->setclass("axcombo");
  996. $Fld->setstyle("width:$stdwidthpx;");
  997. $Fld->additem("");
  998. $q = "SELECT * FROM ax_user u, ax_user_group ug, ax_group g";
  999. $q .= " WHERE ug.user_id=u.user_id";
  1000. $q .= " AND g.group_id=ug.group_id";
  1001. $q .= " AND g.group_desc IN ('Editor','Author')";
  1002. $q .= " AND u.enabled";
  1003. $q .= " ORDER BY u.full_name";
  1004. $authors = dbrecordset($q);
  1005. if ($authors->hasdata) {
  1006. $Fld->add_querydata($authors, "user_id", "full_name");
  1007. }
  1008. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1009. $Tst->tr($rowbg);
  1010. $Tst->td("Author:", "axfg");
  1011. $Tst->td($Fld->render());
  1012. }
  1013. else {
  1014. // Standard authors can't change by-line..
  1015. $Fld = new form_labelfield("story_author", "<b>$this->story_author_name ($this->story_author)</b>");
  1016. $hid = new form_hiddenfield("story_author", $this->story_author);
  1017. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1018. $Tst->tr($rowbg);
  1019. $Tst->td("Author:", "axfg");
  1020. $Tst->td($Fld->render() . $hid->render());
  1021. }
  1022.  
  1023. // STORY CONTENT
  1024. $Fld = new form_wysiwygfield("story_content", "Article", $this->story_content);
  1025. $Fld->setclass("axmemo");
  1026. $Fld->setstyle("width:$widthpx;height:350px;");
  1027. $Fld->register_plugins("all");
  1028. $Fld->set_toolbar("full");
  1029. $Fld->set_statusbar(false);
  1030. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1031. $Tst->tr($rowbg);
  1032. $Tst->td("Content:", "axfg");
  1033. $Tst->td_css("vertical-align:top;padding-top:5px;");
  1034. $Tst->td($Fld->render());
  1035.  
  1036. // STORY MEDIA
  1037. if ($this->has_media) {
  1038. // CATALOG
  1039. $catalog = new catalog();
  1040. $catalog->search("", array("image"));
  1041.  
  1042. // Media details table..
  1043. $Tmed = new table("storymedia");
  1044. $Tmed->tr();
  1045. $Tmed->setwidth("100%");
  1046.  
  1047. // Find the first (selected) media object $selmedia_first. This object
  1048. // is used extensively below to populate the various form fields. We
  1049. // currently only properly support one object (the first one).
  1050. if (count($this->story_media) > 0) {
  1051. reset($this->story_media);
  1052. list($catid, $selmedia_first) = each($this->story_media);
  1053. }
  1054. else {
  1055. $selmedia_first = new story_media();
  1056. }
  1057. // Media selector..
  1058. $Fld = new form_combofield("story_media", "Media", $selmedia_first->keyinfo());
  1059. $Fld->setclass("axcombo");
  1060. $Fld->setstyle("width:$bigwidthpx;");
  1061. $Fld->additem("", "None");
  1062. foreach ($catalog->catalogitems as $catid => $catitem) {
  1063. if (isset($this->story_media[$catid])) {
  1064. $media = $this->story_media[$catid];
  1065. }
  1066. else {
  1067. $media = new story_media($this->story_id, $catitem);
  1068. $media->caption = $catitem->cat_name;
  1069. }
  1070. $key = $media->keyinfo();
  1071. $label = $media->catalogitem->cat_name;
  1072. if ($label == "") {
  1073. $label = $media->catalogitem->filepath;
  1074. }
  1075. $Fld->additem($key, $label);
  1076. }
  1077. $Fld->set_onchange("imgPreview(this.options[this.selectedIndex].value)");
  1078. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1079. $Tmed->tr($rowbg);
  1080. $Tmed->td($Fld->render());
  1081. $Tmed->td_alignment("", "top");
  1082.  
  1083. // Create a preview image..
  1084. $imgFld = new img(
  1085. $selmedia_first->catalogitem->filepath,
  1086. "preview",
  1087. "Preview",
  1088. $width_img_preview,
  1089. $height_img_preview
  1090. );
  1091. $imgFld->setalign("right");
  1092. $imgFld->sethspace(4);
  1093. $Tmed->td($imgFld->render());
  1094. $Tmed->td_alignment("right", "top");
  1095. $Tmed->td_rowspan(4);
  1096.  
  1097. // Width and Height override fields
  1098. $sizes_defaulted = ($selmedia_first->width == 0 || $selmedia_first->height == 0);
  1099. if ($sizes_defaulted) {
  1100. $width = $selmedia_first->catalogitem->width;
  1101. $height = $selmedia_first->catalogitem->height;
  1102. }
  1103. else {
  1104. $width = $selmedia_first->width;
  1105. $height = $selmedia_first->height;
  1106. }
  1107. $Tin = new table();
  1108. $Tin->tr();
  1109. $wFld = new form_textfield("media_width", "Width", $width);
  1110. $wFld->setclass("axnumbox");
  1111. $wFld->setstyle("width:50px");
  1112. $hFld = new form_textfield("media_height", "Height", $height);
  1113. $hFld->setclass("axnumbox");
  1114. $hFld->setstyle("width:50px");
  1115. $dFld = new form_checkbox("media_size_default");
  1116. $dFld->setclass("axchkbox");
  1117. $dFld->checked = ($sizes_defaulted === true);
  1118. if ($sizes_defaulted) {
  1119. $wFld->disabled = true;
  1120. $hFld->disabled = true;
  1121. }
  1122. $dFld->set_onclick("defSizingToggle(this)");
  1123. $Tin->td("Sizing:", "axfg");
  1124. $Tin->td($wFld->render() . "&nbsp;x&nbsp;" . $hFld->render() . "&nbsp;&nbsp;Default&nbsp;" . $dFld->render(), "axfg");
  1125. $Tin->set_width_profile("20%,80%");
  1126. $Tmed->tr($rowbg);
  1127. $Tmed->td($Tin->render());
  1128.  
  1129. // Image justify setting
  1130. $Fld = new form_combofield("media_justify", "Justify", $selmedia_first->justify);
  1131. $Fld->setclass("axcombo");
  1132. $Fld->setstyle("width:100px");
  1133. $Fld->additem("", "default");
  1134. $Fld->additem("left", "Left");
  1135. $Fld->additem("right", "Right");
  1136. $Tin = new table();
  1137. $Tin->td("Justify:", "axfg");
  1138. $Tin->td($Fld->render());
  1139. $Tin->set_width_profile("20%,80%");
  1140. $Tmed->tr($rowbg);
  1141. $Tmed->td($Tin->render());
  1142.  
  1143. // File upload field - allows them to add media on the
  1144. // fly to go with a story..
  1145. $Fld = new form_fileuploadfield("uploadmedia", "Upload");
  1146. $Fld->setclass("axtxtbox");
  1147. $Fld->setstyle("width:$smlwidthpx;");
  1148. $Tmed->tr($rowbg);
  1149. $Tmed->td($Fld->render());
  1150.  
  1151. // Now we render the above sub-table $Tmed inside the main table..
  1152. $Tst->tr($rowbg);
  1153. $Tst->td("Image:", "axfg");
  1154. $Tst->td_css("vertical-align:top;padding-top:5px;");
  1155. $Tst->td($Tmed->render());
  1156.  
  1157. // CAPTION
  1158. // NB: We kinda support multiple media, but the issue of them
  1159. // each having a separate caption is not resolved in this current
  1160. // implementation - recommend using a recmaintainer for that
  1161. // when the time comes.
  1162. $Fld = new form_textfield("caption", "Caption", $selmedia_first->caption);
  1163. $Fld->setclass("axtxtbox");
  1164. $Fld->setstyle("width:$stdwidthpx;");
  1165. $Tst->tr($rowbg);
  1166. $Tst->td("Image Caption:", "axfg");
  1167. $Tst->td($Fld->render());
  1168.  
  1169. $Tmed = new table("storyicon");
  1170. $Tmed->tr();
  1171. $Tmed->setwidth("100%");
  1172. if (isset($this->story_icon)) {
  1173. $selicon_first = $this->story_icon;
  1174. }
  1175. else {
  1176. $selicon_first = new story_media();
  1177. }
  1178. // Icon selector..
  1179. $Fld = new form_combofield("story_icon", "Icon", $selicon_first->keyinfo());
  1180. $Fld->setclass("axcombo");
  1181. $Fld->setstyle("width:$bigwidthpx;");
  1182. $Fld->additem("", "None");
  1183. foreach ($catalog->catalogitems as $catid => $catitem) {
  1184. $icon = new story_media($this->story_id, $catitem);
  1185. $icon->caption = $catitem->cat_name;
  1186. $key = $icon->keyinfo();
  1187. $label = $icon->catalogitem->cat_name;
  1188. if ($label == "") {
  1189. $label = $icon->catalogitem->filepath;
  1190. }
  1191. $Fld->additem($key, $label);
  1192. }
  1193. $Fld->set_onchange("iconPreview(this.options[this.selectedIndex].value)");
  1194. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1195. $Tmed->tr($rowbg);
  1196. $Tmed->td($Fld->render());
  1197. $Tmed->td_alignment("", "top");
  1198. // Create a preview icon..
  1199. $imgFld = new img(
  1200. $selicon_first->catalogitem->filepath,
  1201. "iconpreview",
  1202. "Icon Preview",
  1203. $width_icon_preview,
  1204. $height_icon_preview
  1205. );
  1206. $imgFld->setalign("right");
  1207. $imgFld->sethspace(4);
  1208. $Tmed->td($imgFld->render());
  1209. $Tmed->td_alignment("right", "top");
  1210. $Tmed->td_rowspan(2);
  1211. // Now we render the above sub-table $Tmed inside the main table..
  1212. $Tst->tr($rowbg);
  1213. $Tst->td("Icon:", "axfg");
  1214. $Tst->td($Tmed->render());
  1215. // Icon URL entry field
  1216. $Fld = new form_textfield("story_icon_url", "Url", $this->story_icon_url);
  1217. $Fld->setclass("axtxtbox");
  1218. $Fld->setstyle("width:$stdwidthpx");
  1219. $Tst->tr($rowbg);
  1220. $Tst->td("Link to:", "axfg");
  1221. $Tst->td($Fld->render());
  1222.  
  1223. // This allows us to preview images without refresh..
  1224. $RESPONSE->head->add_script(
  1225. "function imgPreview(key) {\n"
  1226. . " var keyparts=key.split('|');\n"
  1227. . " var imgfile=keyparts[1];\n"
  1228. . " if (imgfile!='') {\n"
  1229. . " document.$this->formname.preview.src=imgfile;\n"
  1230. . " }\n"
  1231. . "}\n"
  1232. . "function iconPreview(key) {\n"
  1233. . " var keyparts=key.split('|');\n"
  1234. . " var iconfile=keyparts[1];\n"
  1235. . " if (iconfile!='') {\n"
  1236. . " document.$this->formname.iconpreview.src=iconfile;\n"
  1237. . " }\n"
  1238. . "}\n"
  1239. . "function defSizingToggle(chkbox) {\n"
  1240. . " if (chkbox.checked) {\n"
  1241. . " document.$this->formname.media_width.value='';\n"
  1242. . " document.$this->formname.media_width.disabled=true;\n"
  1243. . " document.$this->formname.media_height.value='';\n"
  1244. . " document.$this->formname.media_height.disabled=true;\n"
  1245. . " }\n"
  1246. . " else {\n"
  1247. . " document.$this->formname.media_width.disabled=false;\n"
  1248. . " document.$this->formname.media_height.disabled=false;\n"
  1249. . " }\n"
  1250. . "}\n"
  1251. );
  1252. }
  1253.  
  1254. // STORY DATE
  1255. $Fld = new form_textfield("story_date", "Article date", $this->story_date);
  1256. $Fld->setclass("axdatetime");
  1257. $Fld->setstyle("width:$smlwidthpx;");
  1258. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1259. $Tst->tr($rowbg);
  1260. $Tst->td("Article Date:", "axfg");
  1261. $Tst->td($Fld->render() . "&nbsp;<small><i>eg: Sep 3rd 2004 [hh:mm]</i></small>");
  1262.  
  1263. // EXPIRY DATE
  1264. if ($this->has_expiry) {
  1265. $Fld = new form_textfield("expiry_date", "Expiry date", $this->expiry_date);
  1266. $Fld->setclass("axdatetime");
  1267. $Fld->setstyle("width:$smlwidthpx;");
  1268. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1269. $Tst->tr($rowbg);
  1270. $Tst->td("Expiry Date:", "axfg");
  1271. $Tst->td($Fld->render());
  1272. }
  1273.  
  1274. // STORY TYPE
  1275. $Fld = new form_combofield("story_type", "Article type", $this->story_type);
  1276. $Fld->setclass("axcombo");
  1277. $Fld->setstyle("width:$smlwidthpx;");
  1278. $Fld->additem("n", "News item");
  1279. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1280. $Tst->tr($rowbg);
  1281. $Tst->td("Article Type:", "axfg");
  1282. $Tst->td($Fld->render());
  1283.  
  1284. // LANGUAGE
  1285. if ($this->has_multilang) {
  1286. $this->get_story_translations();
  1287. $Tlng = new table("language");
  1288.  
  1289. $Fld = new form_combofield("story_language", "", $this->language);
  1290. $Fld->setclass("axcombo");
  1291. $Fld->setstyle("width:$smlwidthpx;");
  1292.  
  1293. // Fill the dropdown selector with all possibilities..
  1294. $q = "SELECT * FROM ax_language";
  1295. $q .= " WHERE enabled=TRUE";
  1296. $q .= " ORDER BY display_order";
  1297. $langs = dbrecordset($q);
  1298. if ($langs->hasdata) {
  1299. $Fld->add_querydata($langs, "lang_id", "lang_desc");
  1300. }
  1301. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1302. $Tlng->tr($rowbg);
  1303. $Tlng->td($Fld->render());
  1304.  
  1305. $Fld = new form_combofield("new_language");
  1306. $Fld->setclass("axcombo");
  1307. $Fld->setstyle("width:$smlwidthpx;");
  1308. $Fld->additem("", "&mdash; Translate into &mdash;");
  1309. // Determine languages already translated..
  1310. $already_translated = array($this->language);
  1311. foreach ($this->story_translations as $sid => $langid) {
  1312. $already_translated[] = $langid;
  1313. }
  1314. $q = "SELECT * FROM ax_language WHERE enabled=TRUE";
  1315. if (count($already_translated) > 0) {
  1316. $langlist = implode(",", $already_translated);
  1317. if ($langlist != "") {
  1318. $q .= " AND NOT lang_id IN (" . implode(",", $already_translated) . ")";
  1319. }
  1320. }
  1321. $q .= " ORDER BY display_order";
  1322. $tlangs = dbrecordset($q);
  1323. if ($tlangs->hasdata) {
  1324. $Fld->add_querydata($tlangs, "lang_id", "lang_desc");
  1325. }
  1326. $transbtn = new form_imagebutton("translate", "", "", "$LIBDIR/img/_translate.gif", "Translate", 77, 15);
  1327. $Tlng->td($Fld->render());
  1328. $Tlng->td_alignment("right");
  1329. $Tlng->td($transbtn->render());
  1330. $Tlng->td_alignment("left");
  1331.  
  1332. if (count($this->story_translations) > 0) {
  1333. $translist = array();
  1334. foreach ($this->story_translations as $trans_story_id => $trans_lang_id) {
  1335. $lq = dbrecordset("SELECT * FROM ax_language WHERE lang_id=$trans_lang_id");
  1336. if ($lq->hasdata) {
  1337. $translist[] = ucfirst($lq->field("lang_desc"));
  1338. }
  1339. }
  1340. if (count($translist) > 0) {
  1341. $Tlng->tr($rowbg);
  1342. $Tlng->td("Existing Translations: " . implode("&nbsp;|&nbsp;", $translist));
  1343. $Tlng->td_colspan(2);
  1344. }
  1345. }
  1346. $Tst->tr($rowbg);
  1347. $Tst->td("Language:", "axfg");
  1348. $Tst->td_alignment("", "top");
  1349. $Tst->td($Tlng->render());
  1350. }
  1351.  
  1352. // VISIBLE
  1353. $Fld = new form_checkbox("visible", "Visible");
  1354. $Fld->checked = $this->visible;
  1355. $Fld->setclass("axchkbox");
  1356. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1357. $Tst->tr($rowbg);
  1358. $Tst->td("Visible:", "axfg");
  1359. $Tst->td($Fld->render());
  1360.  
  1361. // STORY LOCATIONS
  1362. $Fld = new form_combofield("story_locs", "", $this->story_locs);
  1363. $Fld->multiselect = true;
  1364. $Fld->setclass("axlistbox");
  1365. $Fld->size = 6;
  1366. $Fld->setstyle("width:$stdwidthpx;");
  1367. $q = "SELECT * FROM ax_content_location";
  1368. $q .= " WHERE enabled=TRUE";
  1369. $q .= " ORDER BY location_name";
  1370. $locs = dbrecordset($q);
  1371. if ($locs->hasdata) {
  1372. $Fld->add_querydata($locs, "location_id", "location_name");
  1373. }
  1374. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1375. $Tst->tr($rowbg);
  1376. $Tst->td("Publish to:", "axfg");
  1377. $Tst->td_alignment("", "top");
  1378. $Tst->td($Fld->render());
  1379.  
  1380. // LAST MODIFIED
  1381. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1382. $Tst->tr($rowbg);
  1383. $Tst->td("Last modified:", "axfg");
  1384. $Tst->td($this->lastmodified);
  1385.  
  1386. // Rule-off row..
  1387. $Tst->tr("axfoot");
  1388. $Tst->td("", "axfoot");
  1389. $Tst->td_colspan(2);
  1390.  
  1391. $Tst->set_width_profile("20%,80%");
  1392.  
  1393. return $Tst->render();
  1394. } // editform
  1395. // .....................................................................
  1396. /** Render the story as a maintainer reader would view it. Note that this
  1397. * is not a fully dressed-up story viewer. It is designed as a view that
  1398. * a story administrator would see, showing all the technical bits and
  1399. * pieces such as story byte-size etc. You should create your own viewer
  1400. * for rendering stories 'prettily' on your website.
  1401. * @return string The HTML for the view story content.
  1402. */
  1403. function view() {
  1404. global $RESPONSE;
  1405. global $LIBDIR;
  1406.  
  1407. // CONTROL BUTTONS
  1408. $s = "";
  1409. // Buttons for administrators and editors only..
  1410. $doneb = new form_imagebutton("closewin", "Close", "", "$LIBDIR/img/_done.gif", "Close viewer", 57, 15);
  1411. $doneb->set_onclick("window.close()");
  1412. if ($this->user_can_edit()) {
  1413. $editb = new form_imagebutton("storyedit", "Edit", "", "$LIBDIR/img/_edit.gif", "Edit this article", 42, 15);
  1414. $remvb = new form_imagebutton("storyremove", "Delete", "", "$LIBDIR/img/_delete.gif", "Delete this article", 57, 15);
  1415. $remvb->set_onclick("remove_confirm()");
  1416. $s .= $editb->render() . "&nbsp;&nbsp;" . $remvb->render();
  1417. // Removal protection..
  1418. $RESPONSE->head->add_script(
  1419. "function remove_confirm() {\n"
  1420. . " var msg = '\\n\\nWARNING: Do you really want to delete\\n';\n"
  1421. . " msg += 'the article. This is irrevocable.\\n';"
  1422. . " rc = confirm(msg);\n"
  1423. . " if (rc) {\n"
  1424. . " document.$this->formname.storymode.value='remove';\n"
  1425. . " document.$this->formname.submit();\n"
  1426. . " }\n"
  1427. . " else alert('Delete is cancelled.');\n"
  1428. . "}\n"
  1429. );
  1430. }
  1431. if ($s != "") $s .= "&nbsp;&nbsp;";
  1432. $s .= $doneb->render();
  1433. $CONTROL_BUTTONS = $s;
  1434.  
  1435. $Tvw = new table("storyviewer");
  1436. $Tvw->setpadding(3);
  1437.  
  1438. $rowbg = "axbgdark";
  1439.  
  1440. // EDITOR HEADER
  1441. $Thd = new table("viewerhead");
  1442. $Thd->tr($rowbg);
  1443. $Thd->td("<h3>$this->story_category_desc</h3>", "axfg");
  1444. $Thd->td($CONTROL_BUTTONS);
  1445. $Thd->td_alignment("right");
  1446.  
  1447. if ($this->language != 0) {
  1448. $lq = dbrecordset("SELECT * FROM ax_language WHERE lang_id=$this->language");
  1449. if ($lq->hasdata) {
  1450. $Thd->tr($rowbg);
  1451. $Thd->td("in " . $lq->field("lang_desc"));
  1452. $Thd->td_colspan(2);
  1453. }
  1454. }
  1455.  
  1456. $Tvw->tr($rowbg);
  1457. $Tvw->td($Thd->render(), "border-bottom:1px solid black");
  1458. $Tvw->td_colspan(2);
  1459.  
  1460. if ($this->info_msg != "") {
  1461. $Tvw->tr($rowbg);
  1462. $Tvw->td($this->info_msg, "axerror");
  1463. $Tvw->td_colspan(2);
  1464. $Tvw->td_alignment("center");
  1465. }
  1466.  
  1467. // HEADLINE, BY-LINE, STORY TYPE & WORDCOUNT
  1468. // STORY TYPE
  1469. switch ($this->story_type) {
  1470. case "n":
  1471. $type = "News article";
  1472. break;
  1473. default:
  1474. $type = "";
  1475. }
  1476.  
  1477. $Thd = new table("masthead");
  1478. $Thd->tr($rowbg);
  1479. $Thd->td("<h2>" . $this->story_headline . "</h2>",
  1480. "vertical-align:bottom;padding-bottom:0px;margin-bottom:0px;"
  1481. );
  1482. $Thd->td($type);
  1483. $Thd->td_alignment("right");
  1484. $Thd->tr($rowbg);
  1485. $byline = "by ";
  1486. $byline .= ($this->story_author_name != "") ? $this->story_author_name : "(anonymous)";
  1487. $Thd->td("<h6>$byline</h6>",
  1488. "vertical-align:top;padding-bottom:5px;"
  1489. );
  1490. $Thd->td($this->wordcount . " words (" . nicebytesize($this->bytesize,1) . ")");
  1491. $Thd->td_alignment("right");
  1492. if (isset($this->story_icon)) {
  1493. $Thd->tr();
  1494. $Thd->td($this->render_story_icon());
  1495. $Thd->td_colspan(2);
  1496. }
  1497. $Tvw->tr($rowbg);
  1498. $Tvw->td($Thd->render());
  1499. $Tvw->td_colspan(2);
  1500.  
  1501. // STORY DATE & EXPIRY DATE
  1502. $Tvw->tr($rowbg);
  1503. $Tvw->td( timestamp_to_displaydate(NICE_FULLDATETIME, $this->story_date_ts) );
  1504. if ($this->has_expiry && $this->expiry_date != "") {
  1505. if ($this->expiry_date_ts - $this->story_date_ts > 0) {
  1506. $Tvw->td("expires on " . timestamp_to_displaydate(NICE_FULLDATETIME, $this->expiry_date_ts));
  1507. }
  1508. else {
  1509. $Tvw->td("expired as of " . timestamp_to_displaydate(NICE_FULLDATETIME, $this->expiry_date_ts), "axhl");
  1510. }
  1511. $Tvw->td_alignment("right");
  1512. }
  1513. else {
  1514. $Tvw->td("&nbsp;");
  1515. }
  1516.  
  1517. // PUBLISHING STATUS
  1518. $status = "<b>Published to:</b>&nbsp;";
  1519. if (!$this->visible) {
  1520. $status .= "Currently hidden";
  1521. }
  1522. else {
  1523. if (count($this->story_locs) == 0) {
  1524. $status .= "No location is selected";
  1525. }
  1526. else {
  1527. $q .= "SELECT * FROM ax_content_location";
  1528. $q .= " WHERE location_id in (" . implode(",", $this->story_locs) . ")";
  1529. $locs = dbrecordset($q);
  1530. if ($locs->hasdata) {
  1531. $locnames = array();
  1532. do {
  1533. $locnames[] = $locs->field("location_name");
  1534. } while ($locs->get_next());
  1535. $status .= implode(", ", $locnames);
  1536. }
  1537. }
  1538. }
  1539. $Tvw->tr($rowbg);
  1540. $Tvw->td($status, "padding-top:5px;padding-bottom:5px;border-top:1px solid black");
  1541. $Tvw->td_colspan(2);
  1542.  
  1543. // LEAD-IN & STORY CONTENT
  1544. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1545. $Tvw->tr($rowbg);
  1546. //$content = $this->story_precis . " " . $this->story_content;
  1547. $media_content = "";
  1548. if ($this->has_media) {
  1549. if (count($this->story_media) > 0) {
  1550. foreach ($this->story_media as $cat_id => $media) {
  1551. $width = ($media->width > 0) ? $media->width : $media->catalogitem->width;
  1552. $height = ($media->height > 0) ? $media->height : $media->catalogitem->height;
  1553. $caption = ($media->caption != "") ? $media->caption : $media->catalogitem->cat_name;
  1554. $pic = new img(
  1555. $media->catalogitem->filepath,
  1556. $caption,
  1557. $caption,
  1558. $width,
  1559. $height
  1560. );
  1561. $pic->setalign(($media->justify != "") ? $media->justify : "right");
  1562. $pic->setstyle("padding:2px");
  1563. $media_content .= $pic->render();
  1564. } // foreach
  1565. }
  1566. }
  1567. // Content..
  1568. $story_lead_in = str_replace("\r\n\r\n", "<p>", $this->story_precis);
  1569. $story_content = str_replace("\r\n\r\n", "<p>", $this->story_content);
  1570. $content = "<p>" . $story_lead_in . "</p>"
  1571. . $media_content
  1572. . "<p>" . $story_content . "</p>";
  1573. $Tvw->td($content, "padding-top:20px;padding-bottom:50px;border-top:1px solid black");
  1574. $Tvw->td_colspan(2);
  1575.  
  1576. // TRANSLATIONS
  1577. $this->get_story_translations();
  1578. if (count($this->story_translations) > 0) {
  1579. $RESPONSE->head->add_script(
  1580. "function reloadViewer(url) {\n"
  1581. . " document.location=url;\n"
  1582. . "}\n"
  1583. );
  1584. $translinks = array();
  1585. foreach ($this->story_translations as $trans_story_id => $trans_lang_id) {
  1586. $lq = dbrecordset("SELECT * FROM ax_language WHERE lang_id=$trans_lang_id");
  1587. if ($lq->hasdata) {
  1588. $auth_code = $RESPONSE->get_auth_code();
  1589. $shref = "/story-viewer.php";
  1590. $shref = href_addparm($shref, "story_id", $trans_story_id);
  1591. $shref = href_addparm($shref, "auth_code", $auth_code);
  1592. $href = "javascript:reloadViewer('$shref')";
  1593. $translink = new anchor($href, ucfirst($lq->field("lang_desc")));
  1594. $translinks[] = $translink->render();
  1595. }
  1596. }
  1597. if (count($translinks) > 0) {
  1598. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1599. $Tvw->tr($rowbg);
  1600. $Tvw->td("Translations: " . implode("&nbsp;|&nbsp;", $translinks), "border-top:1px solid black");
  1601. $Tvw->td_colspan(2);
  1602. }
  1603. }
  1604.  
  1605. // LAST MODIFIED
  1606. $rowbg = ($rowbg == "axbglite") ? "axbgdark" : "axbglite";
  1607. $Tvw->tr($rowbg);
  1608. $Tvw->td("Last modified:&nbsp;$this->lastmodified",
  1609. "border-top:1px solid black"
  1610. );
  1611. $Tvw->td("#$this->story_id", "border-top:1px solid black");
  1612. $Tvw->td_alignment("right");
  1613.  
  1614. // Rule-off row..
  1615. $Tvw->tr("axfoot");
  1616. $Tvw->td("", "axfoot");
  1617. $Tvw->td_colspan(2);
  1618.  
  1619. return $Tvw->render();
  1620. } // view
  1621. // .....................................................................
  1622. /**
  1623. * Return the content of this story formatted for plaintext display
  1624. * @param integer $wrapchars Number of characters to wrap the lines at
  1625. */
  1626. function plaintext_content($wrapchars=0) {
  1627. // Join all hard-breaks into single lines..
  1628. $content = str_replace("\n", " ", $this->story_content);
  1629. // Split into paragraphs..
  1630. $paras = explode("<p>", $content);
  1631. // Wrap each paragrph if required..
  1632. if ($wrapchars > 0) {
  1633. $newparas = array();
  1634. foreach ($paras as $para) {
  1635. $para = wordwrap($para, $wrapchars, "\r\n");
  1636. $newparas[] = $para;
  1637. }
  1638. $paras = $newparas;
  1639. }
  1640. // Join up into multiple paragraphs split by CRLF..
  1641. $content = strip_tags( implode("\r\n\r\n", $paras) );
  1642. return $content;
  1643. } // plaintext_content
  1644. // .....................................................................
  1645. /** Render the story. We render the story as a table within a form containing all
  1646. * the form elements required to manipulate the story content, email it to
  1647. * someone, save it, and delete it etc...
  1648. * @return string The HTML for edit or view.
  1649. */
  1650. function html() {
  1651. global $RESPONSE;
  1652. // HIDDEN FIELDS
  1653. $cathid = new form_hiddenfield("cat", $this->story_category);
  1654. $authhid = new form_hiddenfield("auth_code", $RESPONSE->auth_code);
  1655. $modehid = new form_hiddenfield("storymode", $this->storymode);
  1656. $sidhid = new form_hiddenfield("story_id", $this->story_id);
  1657.  
  1658. // STORY FORM, VIEW or EDIT..
  1659.  
  1660. switch ($this->storymode) {
  1661. case "edit":
  1662. case "adding":
  1663. $story_form = new multipart_form($this->formname);
  1664. $story_form->add_text($this->editform());
  1665. break;
  1666. default:
  1667. $story_form = new form($this->formname);
  1668. $story_form->add_text($this->view());
  1669. } // switch
  1670.  
  1671. // Render hidden fields too..
  1672. $story_form->add($cathid);
  1673. $story_form->add($authhid);
  1674. $story_form->add($modehid);
  1675. $story_form->add($sidhid);
  1676.  
  1677. return $story_form->render();
  1678. } // story html
  1679.  
  1680.  
  1681.  
  1682. } // story class
  1683. // -----------------------------------------------------------------------
  1684.  
  1685. /**
  1686. * A container class for media item associated with a story. Contains
  1687. * a single piece of media which is associated with this story.
  1688. * @package cm
  1689. */
  1690. class story_media {
  1691. /** ID of story this media belongs to */
  1692.  
  1693. var $story_id = false;
  1694. /** The catalogitem object */
  1695.  
  1696. var $catalogitem;
  1697. /** The caption for this item */
  1698.  
  1699. var $caption = "";
  1700. /** The way to justify this item */
  1701.  
  1702. var $justify = "";
  1703. /** Local override width */
  1704.  
  1705. var $width = 0;
  1706. /** Local override height */
  1707.  
  1708. var $height = 0;
  1709. // .....................................................................
  1710. /**
  1711. * Create a new piece of story media. This comprises a catalogitem
  1712. * object, and a set of methods to access it.
  1713. * @param mixed $id Story ID, or false if not known
  1714. * @param mixed $item Object catalogitem, or false if initially undefined
  1715. */
  1716. */
  1717. function story_media($story_id=false, $item=false) {
  1718. if ($story_id !== false) {
  1719. $this->story_id = $story_id;
  1720. }
  1721. if ($item !== false && is_object($item)) {
  1722. $this->catalogitem = $item;
  1723. }
  1724. else {
  1725. $this->catalogitem = new catalogitem();
  1726. }
  1727. } // story_media
  1728. // .....................................................................
  1729. /**
  1730. * Define this story media object from the given catalog item key. This
  1731. * will obtain the given piece of catalog media from the database and
  1732. * assign the object variables accordingly.
  1733. * @param integer $catid Catalog item ID to obtain
  1734. */
  1735. function get_catalogitem($catid) {
  1736. $this->catalogitem = new catalogitem($catid);
  1737. } // get_catalogitem
  1738. // .....................................................................
  1739. /**
  1740. * Return the keyinfo string for this media item. This is encoded
  1741. * as follows, and is used in select combos:
  1742. * 'cat_id|filepath|width|height|justify'
  1743. */
  1744. function keyinfo() {
  1745. $info = array();
  1746. if (isset($this->catalogitem)) {
  1747. $info[] = $this->catalogitem->cat_id;
  1748. $info[] = $this->catalogitem->filepath;
  1749. $info[] = ($this->width != 0) ? $this->width : $this->catalogitem->width;
  1750. $info[] = ($this->height != 0) ? $this->height : $this->catalogitem->height;
  1751. $info[] = $this->justify;
  1752. }
  1753. return implode("|", $info);
  1754. } // keyinfo
  1755.  
  1756. } // story_media class
  1757. // -----------------------------------------------------------------------
  1758.  
  1759. ?>

Documentation generated by phpDocumentor 1.3.0RC3