system/classes/story.class.php
author Dirk Haun <dirk@haun-online.de>
Sun, 11 Oct 2009 11:00:55 +0200
branchHEAD
changeset 7370 e5bbed84d3b1
parent 7365 33a378b62bb0
child 7378 5c0a38462d53
permissions -rw-r--r--
Added new option $_CONF['article_comment_close_enabled'] to enable/disable automatically closing stories for comments after a certain amount of days (bug #0000959). Changed handling of comment_expire field in gl_stories such that 0 means the story is always open for comments.
     1 <?php
     2 
     3 /* Reminder: always indent with 4 spaces (no tabs). */
     4 // +---------------------------------------------------------------------------+
     5 // | Geeklog 1.6                                                               |
     6 // +---------------------------------------------------------------------------+
     7 // | story.class.php                                                           |
     8 // |                                                                           |
     9 // | Geeklog Story Abstraction.                                                |
    10 // +---------------------------------------------------------------------------+
    11 // | Copyright (C) 2006-2009 by the following authors:                         |
    12 // |                                                                           |
    13 // | Authors: Michael Jervis, mike AT fuckingbrit DOT com                      |
    14 // +---------------------------------------------------------------------------+
    15 // |                                                                           |
    16 // | This program is free software; you can redistribute it and/or             |
    17 // | modify it under the terms of the GNU General Public License               |
    18 // | as published by the Free Software Foundation; either version 2            |
    19 // | of the License, or (at your option) any later version.                    |
    20 // |                                                                           |
    21 // | This program is distributed in the hope that it will be useful,           |
    22 // | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
    23 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
    24 // | GNU General Public License for more details.                              |
    25 // |                                                                           |
    26 // | You should have received a copy of the GNU General Public License         |
    27 // | along with this program; if not, write to the Free Software Foundation,   |
    28 // | Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           |
    29 // |                                                                           |
    30 // +---------------------------------------------------------------------------+
    31 
    32 /**
    33  * This file provides a class to represent a story, or article. It provides a
    34  * finite state machine for articles. Switching them between the various states:
    35  *  1) Post Data
    36  *  2) Display Mode
    37  *  3) Edit Mode
    38  *  4) Database Mode
    39  *
    40  * @package Geeklog
    41  * @filesource
    42  * @version 0.1
    43  * @since GL 1.4.2
    44  * @copyright Copyright &copy; 2006-2009
    45  * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
    46  * @author Michael Jervis, mike AT fuckingbrit DOT com
    47  *
    48  */
    49 
    50 /**
    51  * Constants for stories:
    52  * Loading from database:
    53  */
    54 define('STORY_LOADED_OK', 1);
    55 
    56 define('STORY_INVALID_SID', -1);
    57 define('STORY_PERMISSION_DENIED', -2);
    58 define('STORY_EDIT_DENIED', -3);
    59 
    60 /**
    61  * Constants for Stories:
    62  * Saving to database
    63  */
    64 define('STORY_SAVED', 2);
    65 define('STORY_SAVED_SUBMISSION', 3);
    66 
    67 /**
    68  * Constants for Stories:
    69  * Loading from request.
    70  */
    71 define('STORY_DUPLICATE_SID', -4);
    72 define('STORY_EXISTING_NO_EDIT_PERMISSION', -5);
    73 define('STORY_NO_ACCESS_PARAMS', -6);
    74 define('STORY_EMPTY_REQUIRED_FIELDS', -7);
    75 define('STORY_NO_ACCESS_TOPIC', -8);
    76 
    77 /**
    78   * Constants for our magic loader
    79   */
    80 define('STORY_AL_ALPHANUM', 0);
    81 define('STORY_AL_NUMERIC', 1);
    82 define('STORY_AL_CHECKBOX', 2);
    83 define('STORY_AL_ANYTHING', 3);
    84 
    85 class Story
    86 {
    87     //*************************************************************************/
    88     // Variables:
    89 
    90     // Public
    91     /**
    92      * Mode, either 'admin' for in the admin screens, or submission, for
    93      * when the user is using submit.php. Controls tons of stuff.
    94      */
    95     var $mode = 'admin';
    96 
    97     /**
    98      * Type of story. User submission or normal editor entered stuff.
    99      * Will be 'submission' if it's from the submission queue.
   100      */
   101     var $type = 'article';
   102 
   103     //Private
   104 
   105     /**
   106      * PRIVATE MEMBER VARIABLES: Things that make up a story.
   107      */
   108     var $_sid;
   109     var $_title;
   110     var $_meta_description;
   111     var $_meta_keywords;    
   112     var $_introtext;
   113     var $_bodytext;
   114     var $_postmode;
   115     var $_uid;
   116     var $_draft_flag;
   117     var $_tid;
   118     var $_date;
   119     var $_hits;
   120     var $_numemails;
   121     var $_comment_expire;
   122     var $_comments;
   123     var $_trackbacks;
   124     var $_related;
   125     var $_featured;
   126     var $_show_topic_icon;
   127     var $_commentcode;
   128     var $_trackbackcode;
   129     var $_statuscode;
   130     var $_expire;
   131     var $_advanced_editor_mode;
   132     var $_frontpage;
   133     var $_owner_id;
   134     var $_group_id;
   135     var $_perm_owner;
   136     var $_perm_group;
   137     var $_perm_members;
   138     var $_perm_anon;
   139 
   140     /* Misc display fields we also load from the database for a story: */
   141     var $_username;
   142     var $_fullname;
   143     var $_photo;
   144     var $_email;
   145     var $_topic;
   146     var $_imageurl;
   147 
   148     /**
   149      * The original SID of the article, cached incase it's changed:
   150      */
   151     var $_originalSid;
   152 
   153     /**
   154      * The access level.
   155      */
   156     var $_access;
   157 
   158     /**
   159      * Array of images uploaded for the story.
   160      */
   161     var $_storyImages;
   162 
   163     /**
   164      * Magic array used for cheating when loading/saving stories from/to db.
   165      *
   166      * List of database field names (which are translated into member variables
   167      * by prepending _ to the value) as pointers to whether or not they are used
   168      * to save data. Everything with a save value of 1 will be saved, those with
   169      * a save value of 0 will just be loaded.
   170      *
   171      * This allows us to automate the loading of story, user and topic from a
   172      * database result array, and generate saving of a story, from the same
   173      * magic array.
   174      */
   175     var $_dbFields = array
   176          (
   177            'sid' => 1,
   178            'uid' => 1,
   179            'draft_flag' => 1,
   180            'tid' => 1,
   181            'date' => 1,
   182            'title' => 1,
   183            'meta_description' => 1,
   184            'meta_keywords' => 1,           
   185            'introtext' => 1,
   186            'bodytext' => 1,
   187            'hits' => 1,
   188            'numemails' => 1,
   189            'comments' => 1,
   190            'trackbacks' => 1,
   191            'related' => 1,
   192            'featured' => 1,
   193            'show_topic_icon' => 1,
   194            'commentcode' => 1,
   195            'comment_expire' => 1,
   196            'trackbackcode' => 1,
   197            'statuscode' => 1,
   198            'expire' => 1,
   199            'postmode' => 1,
   200            'advanced_editor_mode' => 1,
   201            'frontpage' => 1,
   202            'owner_id' => 1,
   203            'group_id' => 1,
   204            'perm_owner' => 1,
   205            'perm_group' => 1,
   206            'perm_members' => 1,
   207            'perm_anon' => 1,
   208            'imageurl' => 0,
   209            'topic' => 0,
   210            'access' => 0,
   211            'photo' => 0,
   212            'email' => 0
   213          );
   214     /**
   215      * Magic array used for loading basic data from posted form. Of form:
   216      * postfield -> numeric, target, used with COM_applyFilter. Some fields
   217      * have exceptions applied
   218      */
   219     var $_postFields = array
   220          (
   221            'uid' => array
   222               (
   223                 STORY_AL_NUMERIC,
   224                 '_uid'
   225               ),
   226            'tid' => array
   227               (
   228                 STORY_AL_ALPHANUM,
   229                 '_tid'
   230               ),
   231            'meta_description' => array
   232               (
   233                 STORY_AL_ANYTHING,
   234                 '_meta_description'
   235               ),
   236            'meta_keywords' => array
   237               (
   238                 STORY_AL_ANYTHING,
   239                 '_meta_keywords'
   240               ),
   241            'show_topic_icon' => array
   242               (
   243                 STORY_AL_CHECKBOX,
   244                 '_show_topic_icon'
   245               ),
   246            'draft_flag' => array
   247               (
   248                 STORY_AL_CHECKBOX,
   249                 '_draft_flag'
   250               ),
   251            'statuscode' => array
   252               (
   253                 STORY_AL_NUMERIC,
   254                 '_statuscode'
   255               ),
   256            'featured' => array
   257               (
   258                 STORY_AL_NUMERIC,
   259                 '_featured'
   260               ),
   261            'frontpage' => array
   262               (
   263                 STORY_AL_NUMERIC,
   264                 '_frontpage'
   265               ),
   266            'commentcode' => array
   267               (
   268                 STORY_AL_NUMERIC,
   269                 '_commentcode'
   270               ),
   271            'trackbackcode' => array
   272               (
   273                 STORY_AL_NUMERIC,
   274                 '_trackbackcode'
   275               ),
   276            'postmode' => array
   277               (
   278                 STORY_AL_ALPHANUM,
   279                 '_postmode'
   280               ),
   281            'story_hits' => array
   282               (
   283                 STORY_AL_NUMERIC,
   284                 '_hits'
   285               ),
   286            'story_comments' => array
   287               (
   288                 STORY_AL_NUMERIC,
   289                 '_comments'
   290               ),
   291            'story_emails' => array
   292               (
   293                 STORY_AL_NUMERIC,
   294                 '_numemails'
   295               ),
   296            'story_trackbacks' => array
   297               (
   298                 STORY_AL_NUMERIC,
   299                 '_trackbacks'
   300               ),
   301            'owner_id' => array
   302               (
   303                 STORY_AL_NUMERIC,
   304                 '_owner_id'
   305               ),
   306            'group_id' => array
   307               (
   308                 STORY_AL_NUMERIC,
   309                 '_group_id'
   310               ),
   311            'type' => array
   312               (
   313                 STORY_AL_ALPHANUM,
   314                 'type'
   315               ),
   316            'hits' => array
   317               (
   318                 STORY_AL_NUMERIC,
   319                 '_hits'
   320               ),
   321            'comments' => array
   322               (
   323                 STORY_AL_NUMERIC,
   324                 '_comments'
   325               ),
   326            'trackbacks' => array
   327               (
   328                 STORY_AL_NUMERIC,
   329                 '_trackbacks'
   330               )
   331          );
   332 
   333     //End Private
   334 
   335     // End Variables.
   336     /**************************************************************************/
   337 
   338     /**************************************************************************/
   339     // Public Methods:
   340     /**
   341      * Constructor, creates a story, taking a (geeklog) database object.
   342      * @param $mode   string    Story class mode, either 'admin' or 'submission'
   343      */
   344     function Story($mode = 'admin')
   345     {
   346         $this->mode = $mode;
   347     }
   348 
   349     /**
   350      * Check to see if there is any content in the story, for
   351      * bothering to preview testing really.
   352      *
   353      * @return boolean trim(title+intro+body) != ''
   354      */
   355     function hasContent()
   356     {
   357         if (trim($this->_title) != '') {
   358             return true;
   359         }
   360         if (trim($this->_introtext) != '') {
   361             return true;
   362         }
   363         if (trim($this->_bodytext) != '') {
   364             return true;
   365         }
   366 
   367         return false;
   368     }
   369 
   370     /**
   371       * Loads a story object from an array (that's come back from the db..)
   372       *
   373       * Used from loadFromDatabase, and used on it's own from story list
   374       * pages.
   375       * @param  $story  array   Story array from db
   376       * @return nowt?
   377       */
   378     function loadFromArray($story)
   379     {
   380         /* Use the magic cheat array to quickly reload the whole story
   381          * from the database result array, doing the quick stripslashes.
   382          */
   383         reset($this->_dbFields);
   384 
   385         while (list($fieldname,$save) = each($this->_dbFields)) {
   386             $varname = '_' . $fieldname;
   387 
   388             if (array_key_exists($fieldname, $story)) {
   389                 $this->{$varname}= stripslashes($story[$fieldname]);
   390             }
   391         }
   392 
   393         if (array_key_exists('username', $story)) {
   394             $this->_username = $story['username'];
   395         }
   396         if (array_key_exists('fullname', $story)) {
   397             $this->_fullname = $story['fullname'];
   398         }
   399 
   400         // Overwrite the date with the timestamp.
   401         $this->_date = $story['unixdate'];
   402 
   403         if (!empty($story['expireunix'])) {
   404             $this->_expire = $story['expireunix'];
   405         } else {
   406             $this->_expire = 0;
   407         }
   408 
   409         if (!empty($story['cmt_expire_unix'])) {
   410             $this->_comment_expire = $story['cmt_expire_unix'];
   411         } else {
   412             $this->_comment_expire = 0;
   413         }
   414 
   415         // Store the original SID
   416         $this->_originalSid = $this->_sid;
   417     }
   418 
   419     /**
   420      * Load a Story object from the sid specified, returning a status result.
   421      * The result will either be a permission denied message, invalid SID
   422      * message, or a loaded ok message. If it's loaded ok, then we've got all
   423      * the exciting gubbins here.
   424      *
   425      * Only used from story admin and submit.php!
   426      *
   427      * @param $sid  string  Story Identifier, valid geeklog story id from the db.
   428      * @return Integer from a constant.
   429      */
   430     function loadFromDatabase($sid, $mode = 'edit')
   431     {
   432         global $_TABLES, $_CONF, $_USER;
   433 
   434         $sid = addslashes(COM_applyFilter($sid));
   435 
   436         if (!empty($sid) && (($mode == 'edit') || ($mode == 'view'))) {
   437             $sql = array();
   438 
   439             $sql['mysql']
   440             = "SELECT STRAIGHT_JOIN s.*, UNIX_TIMESTAMP(s.date) AS unixdate, UNIX_TIMESTAMP(s.expire) AS expireunix, UNIX_TIMESTAMP(s.comment_expire) AS cmt_expire_unix, "
   441                 . "u.username, u.fullname, u.photo, u.email, t.topic, t.imageurl " . "FROM {$_TABLES['stories']} AS s, {$_TABLES['users']} AS u, {$_TABLES['topics']} AS t " . "WHERE (s.uid = u.uid) AND (s.tid = t.tid) AND (sid = '$sid')";
   442 
   443             $sql['mssql'] =
   444                 "SELECT STRAIGHT_JOIN s.sid, s.uid, s.draft_flag, s.tid, s.date, s.title, CAST(s.introtext AS text) AS introtext, CAST(s.bodytext AS text) AS bodytext, s.hits, s.numemails, s.comments, s.trackbacks, s.related, s.featured, s.show_topic_icon, s.commentcode, s.trackbackcode, s.statuscode, s.expire, s.postmode, s.frontpage, s.owner_id, s.group_id, s.perm_owner, s.perm_group, s.perm_members, s.perm_anon, s.advanced_editor_mode, " . " UNIX_TIMESTAMP(s.date) AS unixdate, UNIX_TIMESTAMP(s.expire) AS expireunix, UNIX_TIMESTAMP(s.comment_expire) AS cmt_expire_unix, " . "u.username, u.fullname, u.photo, u.email, t.topic, t.imageurl " . "FROM {$_TABLES['stories']} AS s, {$_TABLES['users']} AS u, {$_TABLES['topics']} AS t " . "WHERE (s.uid = u.uid) AND (s.tid = t.tid) AND (sid = '$sid')";
   445         } elseif (!empty($sid) && ($mode == 'editsubmission')) {
   446             $sql = 'SELECT STRAIGHT_JOIN s.*, UNIX_TIMESTAMP(s.date) AS unixdate, '
   447                 . 'u.username, u.fullname, u.photo, u.email, t.topic, t.imageurl, t.group_id, ' . 't.perm_owner, t.perm_group, t.perm_members, t.perm_anon ' . 'FROM ' . $_TABLES['storysubmission'] . ' AS s, ' . $_TABLES['users'] . ' AS u, ' . $_TABLES['topics'] . ' AS t WHERE (s.uid = u.uid) AND' . ' (s.tid = t.tid) AND (sid = \'' . $sid . '\')';
   448         } elseif ($mode == 'edit') {
   449             $this->_sid = COM_makesid();
   450             $this->_old_sid = $this->_sid;
   451 
   452             if (isset($_CONF['draft_flag'])) {
   453                 $this->_draft_flag = $_CONF['draft_flag'];
   454             } else {
   455                 $this->_draft_flag = 0;
   456             }
   457 
   458             if (isset($_CONF['show_topic_icon'])) {
   459                 $this->_show_topic_icon = $_CONF['show_topic_icon'];
   460             } else {
   461                 $this->_show_topic_icon = 1;
   462             }
   463 
   464             if (COM_isAnonUser()) {
   465                 $this->_uid = 1;
   466             } else {
   467                 $this->_uid = $_USER['uid'];
   468             }
   469             $this->_date = time();
   470             $this->_expire = time();
   471             if ($_CONF['article_comment_close_enabled']) {
   472                 $this->_comment_expire = time() +
   473                     ($_CONF['article_comment_close_days'] * 86400);
   474             } else {
   475                 $this->_comment_expire = 0;
   476             }
   477             $this->_commentcode = $_CONF['comment_code'];
   478             $this->_trackbackcode = $_CONF['trackback_code'];
   479             $this->_title = '';
   480             $this->_meta_description = '';
   481             $this->_meta_keywords = '';            
   482             $this->_introtext = '';
   483             $this->_bodytext = '';
   484 
   485             if (isset($_CONF['frontpage'])) {
   486                 $this->_frontpage = $_CONF['frontpage'];
   487             } else {
   488                 $this->_frontpage = 1;
   489             }
   490 
   491             $this->_hits = 0;
   492             $this->_comments = 0;
   493             $this->_trackbacks = 0;
   494             $this->_numemails = 0;
   495 
   496             if (isset($_CONF['advanced_editor']) && $_CONF['advanced_editor'] && ($_CONF['postmode'] != 'plaintext')) {
   497                 $this->_advanced_editor_mode = 1;
   498                 $this->_postmode = 'adveditor';
   499             } else {
   500                 $this->_postmode = $_CONF['postmode'];
   501                 $this->_advanced_editor_mode = 0;
   502             }
   503 
   504             $this->_statuscode = 0;
   505             $this->_featured = 0;
   506             if (COM_isAnonUser()) {
   507                 $this->_owner_id = 1;
   508             } else {
   509                 $this->_owner_id = $_USER['uid'];
   510             }
   511 
   512             if (isset($_GROUPS['Story Admin'])) {
   513                 $this->_group_id = $_GROUPS['Story Admin'];
   514             } else {
   515                 $this->_group_id = SEC_getFeatureGroup('story.edit');
   516             }
   517 
   518             $array = array();
   519             SEC_setDefaultPermissions($array, $_CONF['default_permissions_story']);
   520             $this->_perm_owner = $array['perm_owner'];
   521             $this->_perm_group = $array['perm_group'];
   522             $this->_perm_anon = $array['perm_anon'];
   523             $this->_perm_members = $array['perm_members'];
   524         } else {
   525             $this->loadFromArgsArray($_POST);
   526         }
   527 
   528         /* if we have SQL, load from it */
   529         if (!empty($sql)) {
   530             $result = DB_query($sql);
   531 
   532             if ($result) {
   533                 $story = DB_fetchArray($result, false);
   534                 if ($story == null) {
   535                     return STORY_INVALID_SID;
   536                 }
   537                 $this->loadFromArray($story);
   538                 if (!isset($story['owner_id'])) {
   539                     $story['owner_id'] = 1;
   540                 }
   541                 $access = SEC_hasAccess($story['owner_id'], $story['group_id'],
   542                             $story['perm_owner'], $story['perm_group'],
   543                             $story['perm_members'], $story['perm_anon']);
   544 
   545                 $this->_access = min($access, SEC_hasTopicAccess($this->_tid));
   546 
   547                 if ($this->_access == 0) {
   548                     return STORY_PERMISSION_DENIED;
   549                 } elseif ($this->_access == 2 && $mode != 'view') {
   550                     return STORY_EDIT_DENIED;
   551                 } elseif ((($this->_access == 2) && ($mode == 'view')) && (($this->_draft_flag == 1) || ($this->_date > time()))) {
   552                         return STORY_INVALID_SID;
   553                 }
   554             } else {
   555                 return STORY_INVALID_SID;
   556             }
   557         }
   558 
   559         if ($mode == 'editsubmission') {
   560             if (isset($_CONF['draft_flag'])) {
   561                 $this->_draft_flag = $_CONF['draft_flag'];
   562             } else {
   563                 $this->_draft_flag = 1;
   564             }
   565 
   566             if (isset($_CONF['show_topic_icon'])) {
   567                 $this->_show_topic_icon = $_CONF['show_topic_icon'];
   568             } else {
   569                 $this->_show_topic_icon = 1;
   570             }
   571 
   572             $this->_commentcode = $_CONF['comment_code'];
   573             $this->_trackbackcode = $_CONF['trackback_code'];
   574             $this->_featured = 0;
   575             $this->_expire = time();
   576             if ($_CONF['article_comment_close_enabled']) {
   577                 $this->_comment_expire = time() +
   578                     ($_CONF['article_comment_close_days'] * 86400);
   579             } else {
   580                 $this->_comment_expire = 0;
   581             }
   582 
   583             if (DB_getItem($_TABLES['topics'], 'archive_flag', "tid = '{$this->_tid}'") == 1) {
   584                 $this->_frontpage = 0;
   585             } elseif (isset($_CONF['frontpage'])) {
   586                 $this->_frontpage = $_CONF['frontpage'];
   587             } else {
   588                 $this->_frontpage = 1;
   589             }
   590 
   591             $this->_comments = 0;
   592             $this->_trackbacks = 0;
   593             $this->_numemails = 0;
   594             $this->_statuscode = 0;
   595             $this->_owner_id = $this->_uid;
   596         }
   597 
   598         $this->_sanitizeData();
   599         return STORY_LOADED_OK;
   600     }
   601 
   602     /**
   603      * Saves the story in it's final state to the database.
   604      *
   605      * Handles all the SID magic etc.
   606      * @return Integer status result from a constant list.
   607      */
   608     function saveToDatabase()
   609     {
   610         global $_TABLES;
   611 
   612         if (DB_getItem($_TABLES['topics'], 'tid', 'archive_flag=1') == $this->_tid) {
   613             $this->_featured = 0;
   614             $this->_frontpage = 0;
   615             $this->_statuscode = STORY_ARCHIVE_ON_EXPIRE;
   616         }
   617 
   618         /* if a featured, non-draft, that goes live straight away, unfeature
   619          * other stories in same topic:
   620          */
   621         if ($this->_featured == '1') {
   622             // there can only be one non-draft featured story
   623             if ($this->_draft_flag == 0 AND $this->_date <= time()) {
   624                 if ($this->_frontpage == 1) {
   625                     // un-feature any featured frontpage story
   626                     DB_query("UPDATE {$_TABLES['stories']} SET featured = 0 WHERE featured = 1 AND draft_flag = 0 AND frontpage = 1 AND date <= NOW()");
   627                 }
   628 
   629                 // un-feature any featured story in the same topic
   630                 DB_query("UPDATE {$_TABLES['stories']} SET featured = 0 WHERE featured = 1 AND draft_flag = 0 AND tid = '{$this->_tid}' AND date <= NOW()");
   631             }
   632         }
   633 
   634         $oldArticleExists = false;
   635         $currentSidExists = false;
   636 
   637         /* Fix up old sid => new sid stuff */
   638         $checksid = addslashes($this->_originalSid); // needed below
   639 
   640         if ($this->_sid != $this->_originalSid) {
   641             /* The sid has changed. Load from request will have
   642              * ensured that if the new sid exists an error has
   643              * been thrown, but we need to know if the old sid
   644              * actually existed (as opposed to being a generated
   645              * sid that was then thrown away) to reduce the sheer
   646              * number of SQL queries we do.
   647              */
   648             $newsid = addslashes($this->_sid);
   649 
   650             $sql = "SELECT 1 FROM {$_TABLES['stories']} WHERE sid='{$checksid}'";
   651             $result = DB_query($sql);
   652 
   653             if ($result && (DB_numRows($result) > 0)) {
   654                 $oldArticleExists = true;
   655             }
   656 
   657             if ($oldArticleExists) {
   658                 /* Move Comments */
   659                 $sql = "UPDATE {$_TABLES['comments']} SET sid='$newsid' WHERE type='article' AND sid='$checksid'";
   660                 DB_query($sql);
   661 
   662                 /* Move Images */
   663                 $sql = "UPDATE {$_TABLES['article_images']} SET ai_sid = '{$newsid}' WHERE ai_sid = '{$checksid}'";
   664                 DB_query($sql);
   665 
   666                 /* Move trackbacks */
   667                 $sql = "UPDATE {$_TABLES['trackback']} SET sid='{$newsid}' WHERE sid='{$checksid}' AND type='article'";
   668                 DB_query($sql);
   669             }
   670         }
   671 
   672         /* Acquire Comment Count */
   673         $sql = "SELECT COUNT(1) FROM {$_TABLES['comments']} WHERE type='article' AND sid='{$this->_sid}'";
   674         $result = DB_query($sql);
   675 
   676         if ($result && (DB_numRows($result) == 1)) {
   677             $array = DB_fetchArray($result);
   678             $this->_comments = $array[0];
   679         } else {
   680             $this->_comments = 0;
   681         }
   682 
   683         /* Format dates for storage: */
   684         /*
   685          * Doing this here would use the webserver's timezone, but we need
   686          * to use the DB server's timezone so that ye olde timezone hack
   687          * still works. See use of FROM_UNIXTIME in the SQL below.
   688          *
   689          * $this->_date = date('Y-m-d H:i:s', $this->_date);
   690          * $this->_expire = date('Y-m-d H:i:s', $this->_expire);
   691          *
   692          */
   693 
   694         // Get the related URLs
   695         $this->_related = implode("\n", STORY_extractLinks("{$this->_introtext} {$this->_bodytext}"));
   696         $sql = 'REPLACE INTO ' . $_TABLES['stories'] . ' (';
   697         $values = ' VALUES (';
   698         reset($this->_dbFields);
   699 
   700         /* This uses the database field array to generate a SQL Statement. This
   701          * means that when adding new fields to save and load, all we need to do
   702          * is add the field name to the array, and the code will magically cope.
   703          */
   704         while (list($fieldname, $save) = each($this->_dbFields)) {
   705             if ($save === 1) {
   706                 $varname = '_' . $fieldname;
   707                 $sql .= $fieldname . ', ';
   708                 if (($fieldname == 'date') || ($fieldname == 'expire') ||
   709                         ($fieldname == 'comment_expire')) {
   710                     // let the DB server do this conversion (cf. timezone hack)
   711                     $values .= 'FROM_UNIXTIME(' . $this->{$varname} . '), ';
   712                 } else {
   713                     $values .= '\'' . addslashes($this->{$varname}) . '\', ';
   714                 }
   715             }
   716         }
   717 
   718         $sql = substr($sql, 0, strlen($sql) - 2);
   719         $values = substr($values, 0, strlen($values) - 2);
   720         $sql .= ') ' . $values . ')';
   721 
   722         DB_query($sql);
   723 
   724         /* Clean up the old story */
   725         if ($oldArticleExists) {
   726             $sql = "DELETE FROM {$_TABLES['stories']} WHERE sid='$checksid'";
   727             DB_query($sql);
   728         }
   729 
   730         if ($this->type == 'submission') {
   731             /* there might be a submission, clean it up */
   732             DB_delete($_TABLES['storysubmission'], 'sid', $checksid);
   733         }
   734 
   735         return STORY_SAVED;
   736     }
   737 
   738     /**
   739      * Loads a story from the post data. This is the most exciting function in
   740      * the whole entire world. First it'll clean up that horrible Magic Quotes
   741      * crap. Then it'll do all Geeklog's funky security stuff, anti XSS, anti
   742      * SQL Injection. Yay.
   743      */
   744     function loadFromArgsArray(&$array)
   745     {
   746         global $_TABLES;
   747 
   748         /* magic_quotes_gpc cleanup routine now in submitstory() in
   749          * /public_html/admin/story.php
   750          */
   751 
   752         $retval = STORY_LOADED_OK; // default to success
   753 
   754         /* Load the trivial stuff: */
   755         $this->_loadBasics($array);
   756 
   757         /* Check to see if we have permission to edit this sid, and that this
   758          * sid is not a duplicate or anything horrible like that. ewww.
   759          */
   760         $sql
   761         = 'SELECT owner_id, group_id, perm_owner, perm_group, perm_members, perm_anon ' . ' FROM ' . $_TABLES['stories']
   762             . ' WHERE sid=\'' . $this->_sid . '\'';
   763         $result = DB_query($sql);
   764 
   765         if ($result && (DB_numRows($result) > 0)) {
   766             /* Sid exists! Is it our article? */
   767             if ($this->_sid != $this->_originalSid) {
   768                 // for story preview: don't abort
   769                 $retval = STORY_DUPLICATE_SID;
   770             }
   771 
   772             $article = DB_fetchArray($result);
   773             /* Check Security */
   774             if (SEC_hasAccess($article['owner_id'], $article['group_id'],
   775                     $article['perm_owner'], $article['perm_group'],
   776                     $article['perm_members'], $article['perm_anon']) < 3) {
   777                 return STORY_EXISTING_NO_EDIT_PERMISSION;
   778             }
   779         }
   780 
   781         $access = SEC_hasAccess($this->_owner_id, $this->_group_id, $this->_perm_owner, $this->_perm_group,
   782                                     $this->_perm_members, $this->_perm_anon);
   783 
   784         if (($access < 3) || !SEC_hasTopicAccess($this->_tid) || !SEC_inGroup($this->_group_id)) {
   785             return STORY_NO_ACCESS_PARAMS;
   786         }
   787 
   788         /* Load up the topic name and icon */
   789         $topic = DB_query("SELECT topic, imageurl FROM {$_TABLES['topics']} WHERE tid='{$this->_tid}'");
   790         $topic = DB_fetchArray($topic);
   791         $this->_topic = $topic['topic'];
   792         $this->_imageurl = $topic['imageurl'];
   793 
   794         //$title = COM_stripSlashes( $array['title'] );
   795         //$intro = COM_stripSlashes( $array['introtext'] );
   796         //$body = COM_stripSlashes( $array['bodytext'] );
   797 
   798         /* Then load the title, intro and body */
   799         if (($array['postmode'] == 'html') || ($array['postmode'] == 'adveditor') || ($array['postmode'] == 'wikitext')) {
   800             $this->_htmlLoadStory($array['title'], $array['introtext'], $array['bodytext']);
   801 
   802             if ($this->_postmode == 'adveditor') {
   803                 $this->_advanced_editor_mode = 1;
   804                 $this->_postmode = 'html';
   805             } else {
   806                 $this->_advanced_editor_mode = 0;
   807             }
   808         } else {
   809             $this->_advanced_editor_mode = 0;
   810             $this->_plainTextLoadStory($array['title'], $array['introtext'], $array['bodytext']);
   811         }
   812 
   813         if (empty($this->_title) || empty($this->_introtext)) {
   814             return STORY_EMPTY_REQUIRED_FIELDS;
   815         }
   816 
   817         $this->_sanitizeData();
   818 
   819         return $retval;
   820     }
   821 
   822     /**
   823      * Sets up basic data for a new user submission story
   824      *
   825      * @param   string   Topic the user picked before heading to submission
   826      */
   827     function initSubmission($topic)
   828     {
   829         global $_USER, $_CONF, $_TABLES;
   830 
   831         if (COM_isAnonUser()) {
   832             $this->_uid = 1;
   833         } else {
   834             $this->_uid = $_USER['uid'];
   835         }
   836 
   837         $this->_postmode = $_CONF['postmode'];
   838 
   839         // If a topic has been specified, use it, if permitted
   840         // otherwise, fall back to the default permitted topic.
   841         // if we still don't have one...
   842 
   843         // Have we specified a permitted topic?
   844         if (!empty($topic)) {
   845             $allowed
   846             = DB_getItem($_TABLES['topics'], 'tid', "tid = '" . addslashes($topic) . "'" . COM_getTopicSql('AND'));
   847 
   848             if ($allowed != $topic) {
   849                 $topic = '';
   850             }
   851         }
   852 
   853         // Do we now not have a topic?
   854         if (empty($topic)) {
   855             // Get default permitted:
   856             $topic = DB_getItem($_TABLES['topics'], 'tid', 'is_default = 1' . COM_getPermSQL('AND'));
   857         }
   858 
   859         // Use what we have:
   860         $this->_tid = $topic;
   861         $this->_date = time();
   862     }
   863 
   864     /**
   865      * Loads a submitted story from postdata
   866      */
   867     function loadSubmission()
   868     {
   869         $array = $_POST;
   870 
   871         $this->_expire = time();
   872         $this->_date = time();
   873         if ($_CONF['article_comment_close_enabled']) {
   874             $this->_comment_expire = time() +
   875                 ($_CONF['article_comment_close_days'] * 86400);
   876         } else {
   877             $this->_comment_expire = 0;
   878         }
   879 
   880         // Handle Magic GPC Garbage:
   881         while (list($key, $value) = each($array))
   882         {
   883             $array[$key] = COM_stripslashes($value);
   884         }
   885 
   886         $this->_postmode = COM_applyFilter($array['postmode']);
   887         $this->_sid = COM_applyFilter($array['sid']);
   888         $this->_uid = COM_applyFilter($array['uid'], true);
   889         if ($this->_uid < 1) {
   890             $this->_uid = 1;
   891         }
   892         $this->_unixdate = COM_applyFilter($array['date'], true);
   893 
   894         if (!isset($array['bodytext'])) {
   895             $array['bodytext'] = '';
   896         }
   897 
   898         /* Then load the title, intro and body */
   899         if (($array['postmode'] == 'html') || ($array['postmode'] == 'adveditor')) {
   900             $this->_htmlLoadStory($array['title'], $array['introtext'], $array['bodytext']);
   901 
   902             if ($this->_postmode == 'adveditor') {
   903                 $this->_advanced_editor_mode = 1;
   904                 $this->_postmode = 'html';
   905             } else {
   906                 $this->_advanced_editor_mode = 0;
   907             }
   908         } else {
   909             $this->_advanced_editor_mode = 0;
   910             $this->_plainTextLoadStory($array['title'], $array['introtext'], $array['bodytext']);
   911         }
   912 
   913         $this->_tid = COM_applyFilter($array['tid']);
   914 
   915         if (empty($this->_title) || empty($this->_introtext)) {
   916             return STORY_EMPTY_REQUIRED_FIELDS;
   917         }
   918 
   919         return STORY_LOADED_OK;
   920     }
   921 
   922     /**
   923      * Returns a story formatted for spam check:
   924      *
   925      * @return  string Story formatted for spam check.
   926      */
   927     function GetSpamCheckFormat()
   928     {
   929         return "<h1>{$this->_title}</h1><p>{$this->_introtext}</p><p>{$this->_bodytext}</p>";
   930     }
   931 
   932     /**
   933      * Saves a story submission.
   934      *
   935      * @return  integer result code explaining behaviour.
   936      */
   937     function saveSubmission()
   938     {
   939         global $_USER, $_CONF, $_TABLES;
   940         $this->_sid = COM_makeSid();
   941 
   942         if (COM_isAnonUser()) {
   943             $this->_uid = 1;
   944         } else {
   945             $this->_uid = $_USER['uid'];
   946         }
   947 
   948         $tmptid = addslashes(COM_sanitizeID($this->_tid));
   949 
   950         $result = DB_query('SELECT group_id,perm_owner,perm_group,perm_members,perm_anon FROM ' .
   951                             "{$_TABLES['topics']} WHERE tid = '{$tmptid}'" .
   952                             COM_getTopicSQL('AND'));
   953 
   954         if (DB_numRows($result) == 0) {
   955             // user doesn't have access to this topic - bail
   956             return STORY_NO_ACCESS_TOPIC;
   957         }
   958 
   959         $T = DB_fetchArray($result);
   960 
   961         if (($_CONF['storysubmission'] == 1) && !SEC_hasRights('story.submit')) {
   962             $this->_sid = addslashes($this->_sid);
   963             $this->_tid = $tmptid;
   964             $this->_title = addslashes($this->_title);
   965             $this->_introtext = addslashes($this->_introtext);
   966             $this->_bodytext = addslashes($this->_bodytext);
   967             $this->_postmode = addslashes($this->_postmode);
   968             DB_save($_TABLES['storysubmission'], 'sid,tid,uid,title,introtext,bodytext,date,postmode',
   969                         "{$this->_sid},'{$this->_tid}',{$this->_uid},'{$this->_title}'," .
   970                         "'{$this->_introtext}','{$this->_bodytext}',NOW(),'{$this->_postmode}'");
   971 
   972             return STORY_SAVED_SUBMISSION;
   973         } else {
   974             // post this story directly. First establish the necessary missing data.
   975             $this->_sanitizeData();
   976 
   977             if (!isset($_CONF['show_topic_icon'])) {
   978                 $_CONF['show_topic_icon'] = 1;
   979             }
   980 
   981             if (DB_getItem($_TABLES['topics'], 'archive_flag', "tid = '{$tmptid}'") == 1) {
   982                 $this->_frontpage = 0;
   983             } elseif (isset($_CONF['frontpage'])) {
   984                 $this->_frontpage = $_CONF['frontpage'];
   985             } else {
   986                 $this->_frontpage = 1;
   987             }
   988 
   989             $this->_oldsid = $this->_sid;
   990             $this->_date = mktime();
   991             $this->_featured = 0;
   992             $this->_commentcode = $_CONF['comment_code'];
   993             $this->_trackbackcode = $_CONF['trackback_code'];
   994             $this->_statuscode = 0;
   995             $this->_show_topic_icon = $_CONF['show_topic_icon'];
   996             if (COM_isAnonUser()) {
   997                 $this->_owner_id = 1;
   998             } else {
   999                 $this->_owner_id = $_USER['uid'];
  1000             }
  1001             $this->_group_id = $T['group_id'];
  1002             $this->_perm_owner = $T['perm_owner'];
  1003             $this->_perm_group = $T['perm_group'];
  1004             $this->_perm_members = $T['perm_members'];
  1005             $this->_perm_anon = $T['perm_anon'];
  1006 
  1007             $this->saveToDatabase();
  1008 
  1009             PLG_itemSaved($this->_sid, 'article');
  1010             COM_rdfUpToDateCheck();
  1011             COM_olderStuff();
  1012 
  1013             return STORY_SAVED;
  1014         }
  1015     }
  1016 
  1017     /**
  1018      * Inserts image HTML into the place of Image Placeholders for stories
  1019      * with images.
  1020      *
  1021      * @return array    containing errors, or empty.
  1022      */
  1023     function insertImages()
  1024     {
  1025         global $_CONF, $_TABLES, $LANG24;
  1026 
  1027         // Grab member vars into locals:
  1028         $intro = $this->_introtext;
  1029         $body = $this->_bodytext;
  1030         $fulltext = "$intro $body";
  1031 
  1032         $result = DB_query("SELECT ai_filename FROM {$_TABLES['article_images']} WHERE "
  1033                             ."ai_sid = '{$this->_sid}' ORDER BY ai_img_num"
  1034                           );
  1035         $nrows = DB_numRows($result);
  1036         $errors = array();
  1037         $stdImageLoc = true;
  1038 
  1039         if (!strstr($_CONF['path_images'], $_CONF['path_html'])) {
  1040             $stdImageLoc = false;
  1041         }
  1042 
  1043         for ($i = 1; $i <= $nrows; $i++) {
  1044             $A = DB_fetchArray($result);
  1045 
  1046             $sizeattributes = COM_getImgSizeAttributes($_CONF['path_images'] . 'articles/' . $A['ai_filename']);
  1047 
  1048             $norm = '[image' . $i . ']';
  1049             $left = '[image' . $i . '_left]';
  1050             $right = '[image' . $i . '_right]';
  1051 
  1052             $unscalednorm = '[unscaled' . $i . ']';
  1053             $unscaledleft = '[unscaled' . $i . '_left]';
  1054             $unscaledright = '[unscaled' . $i . '_right]';
  1055 
  1056             // See how many times image $i is used in the fulltext of the article:
  1057             $icount = substr_count($fulltext, $norm) + substr_count($fulltext, $left) +
  1058                       substr_count($fulltext, $right);
  1059             // including unscaled.
  1060             $icount = $icount + substr_count($fulltext, $unscalednorm) +
  1061                       substr_count($fulltext, $unscaledleft)
  1062                       + substr_count($fulltext, $unscaledright);
  1063 
  1064             // If the image we are currently looking at wasn't used, we need
  1065             // to log an error
  1066             if ($icount == 0) {
  1067                 // There is an image that wasn't used, create an error
  1068                 $errors[] = $LANG24[48] . " #$i, {$A['ai_filename']}, " . $LANG24[53];
  1069             } else {
  1070                 // We had no errors, so this image and all previous images
  1071                 // are used, so we will then go and replace them
  1072                 if (count($errors) == 0) {
  1073 
  1074                     $imgpath = '';
  1075 
  1076                     // If we are storing images on a "standard path" i.e. is
  1077                     // available to the host web server, then the url to this
  1078                     // image is based on the path to images, site url, articles
  1079                     // folder and it's filename.
  1080                     //
  1081                     // Otherwise, we have to use the image handler to load the
  1082                     // image from whereever else on the file system we're
  1083                     // keeping them:
  1084                     if ($stdImageLoc) {
  1085                         $imgpath = substr($_CONF['path_images'], strlen($_CONF['path_html']));
  1086                         $imgSrc = $_CONF['site_url'] . '/' . $imgpath . 'articles/' . $A['ai_filename'];
  1087                     } else {
  1088                         $imgSrc = $_CONF['site_url'] . '/getimage.php?mode=articles&amp;image=' . $A['ai_filename'];
  1089                     }
  1090 
  1091                     // Build image tags for each flavour of the image:
  1092                     $img_noalign = '<img ' . $sizeattributes . 'src="' . $imgSrc . '" alt=""' . XHTML . '>';
  1093                     $img_leftalgn = '<img ' . $sizeattributes . 'class="floatleft" src="' . $imgSrc . '" alt=""' . XHTML . '>';
  1094                     $img_rightalgn = '<img ' . $sizeattributes . 'class="floatright" src="' . $imgSrc . '" alt=""' . XHTML . '>';
  1095 
  1096 
  1097                     // Are we keeping unscaled images?
  1098                     if ($_CONF['keep_unscaled_image'] == 1) {
  1099                         // Yes we are, so, we need to find out what the filename
  1100                         // of the original, unscaled image is:
  1101                         $lFilename_large = substr_replace($A['ai_filename'], '_original.',
  1102                                                 strrpos($A['ai_filename'], '.'), 1);
  1103                         $lFilename_large_complete = $_CONF['path_images'] . 'articles/' .
  1104                                                         $lFilename_large;
  1105 
  1106                         // We need to map that filename to the right location
  1107                         // or the fetch script:
  1108                         if ($stdImageLoc) {
  1109                             $lFilename_large_URL = $_CONF['site_url'] . '/' . $imgpath .
  1110                                                     'articles/' . $lFilename_large;
  1111                         } else {
  1112                             $lFilename_large_URL = $_CONF['site_url'] .
  1113                                                     '/getimage.php?mode=show&amp;image=' .
  1114                                                     $lFilename_large;
  1115                         }
  1116 
  1117                         // And finally, replace the [imageX_mode] tags with the
  1118                         // image and its hyperlink (only when the large image
  1119                         // actually exists)
  1120                         $lLink_url  = '';
  1121                         $lLink_attr = '';
  1122                         if (file_exists($lFilename_large_complete)) {
  1123                             $lLink_url = $lFilename_large_URL;
  1124                             $lLink_attr = array('title' => $LANG24[57]);
  1125                         }
  1126                     }
  1127 
  1128                     if (!empty($lLink_url)) {
  1129                         $intro = str_replace($norm,  COM_createLink($img_noalign,   $lLink_url, $lLink_attr), $intro);
  1130                         $body  = str_replace($norm,  COM_createLink($img_noalign,   $lLink_url, $lLink_attr), $body);
  1131                         $intro = str_replace($left,  COM_createLink($img_leftalgn,  $lLink_url, $lLink_attr), $intro);
  1132                         $body  = str_replace($left,  COM_createLink($img_leftalgn,  $lLink_url, $lLink_attr), $body);
  1133                         $intro = str_replace($right, COM_createLink($img_rightalgn, $lLink_url, $lLink_attr), $intro);
  1134                         $body  = str_replace($right, COM_createLink($img_rightalgn, $lLink_url, $lLink_attr), $body);
  1135                     } else {
  1136                         // We aren't wrapping our image tags in hyperlinks, so
  1137                         // just replace the [imagex_mode] tags with the image:
  1138                         $intro = str_replace($norm,  $img_noalign,   $intro);
  1139                         $body  = str_replace($norm,  $img_noalign,   $body);
  1140                         $intro = str_replace($left,  $img_leftalgn,  $intro);
  1141                         $body  = str_replace($left,  $img_leftalgn,  $body);
  1142                         $intro = str_replace($right, $img_rightalgn, $intro);
  1143                         $body  = str_replace($right, $img_rightalgn, $body);
  1144                     }
  1145 
  1146                     // And insert the unscaled mode images:
  1147                     if (($_CONF['allow_user_scaling'] == 1) and ($_CONF['keep_unscaled_image'] == 1)) {
  1148                         if (file_exists($lFilename_large_complete)) {
  1149                             $imgSrc = $lFilename_large_URL;
  1150                             $sizeattributes = COM_getImgSizeAttributes($lFilename_large_complete);
  1151                         }
  1152 
  1153                         $intro = str_replace($unscalednorm, '<img ' . $sizeattributes . 'src="' .
  1154                                              $imgSrc . '" alt=""' . XHTML . '>', $intro);
  1155                         $body  = str_replace($unscalednorm, '<img ' . $sizeattributes . 'src="' .
  1156                                              $imgSrc . '" alt=""' . XHTML . '>', $body);
  1157                         $intro = str_replace($unscaledleft, '<img ' . $sizeattributes .
  1158                                              'align="left" src="' . $imgSrc . '" alt=""' . XHTML . '>', $intro);
  1159                         $body  = str_replace($unscaledleft, '<img ' . $sizeattributes .
  1160                                              'align="left" src="' . $imgSrc . '" alt=""' . XHTML . '>', $body);
  1161                         $intro = str_replace($unscaledright, '<img ' . $sizeattributes .
  1162                                              'align="right" src="' . $imgSrc. '" alt=""' . XHTML . '>', $intro);
  1163                         $body  = str_replace($unscaledright, '<img ' . $sizeattributes .
  1164                                              'align="right" src="' . $imgSrc . '" alt=""' . XHTML . '>', $body);
  1165                     }
  1166                 }
  1167             }
  1168         }
  1169 
  1170         $this->_introtext = $intro;
  1171         $this->_bodytext  = $body;
  1172 
  1173         return $errors;
  1174     }
  1175 
  1176     /**
  1177      * This replaces all article image HTML in intro and body with
  1178      * GL special syntax
  1179      *
  1180      * @param    string      $sid    ID for story to parse
  1181      * @param    string      $intro  Intro text
  1182      * @param    string      $body   Body text
  1183      * @return   string      processed text
  1184      *
  1185      */
  1186     function replaceImages($text)
  1187     {
  1188         global $_CONF, $_TABLES, $LANG24;
  1189 
  1190         $stdImageLoc = true;
  1191 
  1192         if (!strstr($_CONF['path_images'], $_CONF['path_html'])) {
  1193             $stdImageLoc = false;
  1194         }
  1195 
  1196         $count = 0;
  1197         /* If we haven't already cached the images for this story, do so */
  1198         if (!is_array($this->_storyImages)) {
  1199             $result= DB_query("SELECT ai_filename FROM {$_TABLES['article_images']} WHERE " .
  1200                               "ai_sid = '{$this->_sid}' ORDER BY ai_img_num");
  1201             $nrows = DB_numRows($result);
  1202             $this->_storyImages = array();
  1203 
  1204             for ($i = 1; $i <= $nrows; $i++)
  1205             {
  1206                 $this->_storyImages[] = DB_fetchArray($result);
  1207             }
  1208 
  1209             $count = $nrows;
  1210         } else {
  1211             $count = count($this->_storyImages);
  1212         }
  1213 
  1214         // If the article has any images, remove them back to [image] tags.
  1215         for ($i = 0; $i < $count; $i++) {
  1216             $A = $this->_storyImages[$i];
  1217 
  1218             $imageX = '[image' . ($i + 1) . ']';
  1219             $imageX_left = '[image' . ($i + 1) . '_left]';
  1220             $imageX_right = '[image' . ($i + 1) . '_right]';
  1221 
  1222             $sizeattributes = COM_getImgSizeAttributes($_CONF['path_images'] . 'articles/' . $A['ai_filename']);
  1223 
  1224             $lLinkPrefix = '';
  1225             $lLinkSuffix = '';
  1226 
  1227             if ($_CONF['keep_unscaled_image'] == 1) {
  1228                 $lFilename_large = substr_replace($A['ai_filename'],
  1229                 '_original.', strrpos($A['ai_filename'], '.'), 1);
  1230                 $lFilename_large_complete = $_CONF['path_images'] . 'articles/' . $lFilename_large;
  1231 
  1232                 if ($stdImageLoc) {
  1233                     $imgpath = substr($_CONF['path_images'], strlen($_CONF['path_html']));
  1234                     $lFilename_large_URL = $_CONF['site_url'] . '/' . $imgpath . 'articles/' . $lFilename_large;
  1235                 } else {
  1236                     $lFilename_large_URL = $_CONF['site_url'] . '/getimage.php?mode=show&amp;image='
  1237                                            . $lFilename_large;
  1238                 }
  1239 
  1240                 if (file_exists($lFilename_large_complete)) {
  1241                     $lLinkPrefix = '<a href="' . $lFilename_large_URL . '" title="' . $LANG24[57] . '">';
  1242                     $lLinkSuffix = '</a>';
  1243                 }
  1244             }
  1245 
  1246             if ($stdImageLoc) {
  1247                 $imgpath = substr($_CONF['path_images'], strlen($_CONF['path_html']));
  1248                 $imgSrc = $_CONF['site_url'] . '/' . $imgpath . 'articles/' . $A['ai_filename'];
  1249             } else {
  1250                 $imgSrc = $_CONF['site_url'] . '/getimage.php?mode=articles&amp;image=' . $A['ai_filename'];
  1251             }
  1252 
  1253             $norm = $lLinkPrefix . '<img ' . $sizeattributes . 'src="' . $imgSrc . '" alt=""' . XHTML . '>' . $lLinkSuffix;
  1254             $left = $lLinkPrefix . '<img ' . $sizeattributes . 'class="floatleft" src="' . $imgSrc . '" alt=""' . XHTML . '>'
  1255                     . $lLinkSuffix;
  1256             $right = $lLinkPrefix . '<img ' . $sizeattributes . 'class="floatright" src="' . $imgSrc . '" alt=""' . XHTML . '>'
  1257                     . $lLinkSuffix;
  1258 
  1259             $text = str_replace($norm, $imageX, $text);
  1260             $text = str_replace($left, $imageX_left, $text);
  1261             $text = str_replace($right, $imageX_right, $text);
  1262 
  1263             if (($_CONF['allow_user_scaling'] == 1) and ($_CONF['keep_unscaled_image'] == 1)) {
  1264                 $unscaledX = '[unscaled' . ($i + 1) . ']';
  1265                 $unscaledX_left = '[unscaled' . ($i + 1) . '_left]';
  1266                 $unscaledX_right = '[unscaled' . ($i + 1) . '_right]';
  1267 
  1268                 if (file_exists($lFilename_large_complete)) {
  1269                     $sizeattributes = COM_getImgSizeAttributes($lFilename_large_complete);
  1270                     $norm = '<img ' . $sizeattributes . 'src="' . $lFilename_large_URL . '" alt=""' . XHTML . '>';
  1271                     $left = '<img ' . $sizeattributes . 'align="left" src="' . $lFilename_large_URL . '" alt=""' . XHTML . '>';
  1272                     $right = '<img ' . $sizeattributes . 'align="right" src="' . $lFilename_large_URL . '" alt=""' . XHTML . '>';
  1273                 }
  1274 
  1275                 $text = str_replace($norm, $unscaledX, $text);
  1276                 $text = str_replace($left, $unscaledX_left, $text);
  1277                 $text = str_replace($right, $unscaledX_right, $text);
  1278             }
  1279         }
  1280 
  1281         return $text;
  1282     }
  1283 
  1284     /**
  1285      * Return the SID in a clean way
  1286      *
  1287      * @param $fordb    boolean True if we want an 'addslashes' version for the db
  1288      */
  1289     function getSid($fordb = false)
  1290     {
  1291         if ($fordb) {
  1292             return addslashes($this->_sid);
  1293         } else {
  1294             return $this->_sid;
  1295         }
  1296     }
  1297 
  1298     /**
  1299      * Get the access level
  1300      */
  1301     function getAccess()
  1302     {
  1303         return $this->_access;
  1304     }
  1305 
  1306     /**
  1307      * Provide access to story elements. For the editor.
  1308      *
  1309      * This is a pseudo-property, implementing a getter for story
  1310      * details as if as an associative array. Personally, I'd
  1311      * rather be able to assign getters and setters to actual
  1312      * properties to mask controlled access to private member
  1313      * variables. But, you get what you get with PHP. So here it
  1314      * is in all its nastiness.
  1315      *
  1316      * @param   string  $item   Item to fetch.
  1317      * @return  mixed   The clean and ready to use (in edit mode) value requested.
  1318      */
  1319     function EditElements($item = 'title')
  1320     {
  1321         global $_CONF;
  1322         switch (strtolower($item))
  1323         {
  1324         case 'unixdate':
  1325             $return = strtotime($this->_date);
  1326 
  1327             break;
  1328 
  1329         case 'expirestamp':
  1330             $return = strtotime($this->_expire);
  1331 
  1332             break;
  1333 
  1334         case 'publish_hour':
  1335             $return = date('H', $this->_date);
  1336 
  1337             break;
  1338 
  1339         case 'publish_month':
  1340             $return = date('m', $this->_date);
  1341 
  1342             break;
  1343 
  1344         case 'publish_day':
  1345             $return = date('d', $this->_date);
  1346 
  1347             break;
  1348 
  1349         case 'publish_year':
  1350             $return = date('Y', $this->_date);
  1351 
  1352             break;
  1353 
  1354         case 'public_hour':
  1355             $return = date('H', $this->_date);
  1356 
  1357             break;
  1358 
  1359         case 'publish_minute':
  1360             $return = date('i', $this->_date);
  1361 
  1362             break;
  1363 
  1364         case 'publish_second':
  1365             $return = date('s', $this->_date);
  1366 
  1367             break;
  1368 
  1369         case 'expire_second':
  1370             $return = date('s', $this->_expire);
  1371 
  1372             break;
  1373 
  1374         case 'expire_minute':
  1375             $return = date('i', $this->_expire);
  1376 
  1377             break;
  1378 
  1379         case 'expire_hour':
  1380             $return = date('H', $this->_expire);
  1381 
  1382             break;
  1383 
  1384         case 'expire_day':
  1385             $return = date('d', $this->_expire);
  1386 
  1387             break;
  1388 
  1389         case 'expire_month':
  1390             $return = date('m', $this->_expire);
  1391 
  1392             break;
  1393 
  1394         case 'expire_year':
  1395             $return = date('Y', $this->_expire);
  1396 
  1397             break;
  1398 
  1399         case 'cmt_close':
  1400             $return = ($this->_comment_expire == 0) ? false : true;
  1401 
  1402             break;
  1403 
  1404         case 'cmt_close_second':
  1405             if ($this->_comment_expire == 0) {
  1406                 $return = date('s', time() +
  1407                                ($_CONF['article_comment_close_days'] * 86400));
  1408             } else {
  1409                 $return = date('s', $this->_comment_expire);
  1410             }
  1411 
  1412             break;
  1413 
  1414         case 'cmt_close_minute':
  1415             if ($this->_comment_expire == 0) {
  1416                 $return = date('i', time() +
  1417                                ($_CONF['article_comment_close_days'] * 86400));
  1418             } else {
  1419                 $return = date('i', $this->_comment_expire);
  1420             }
  1421 
  1422             break;
  1423 
  1424         case 'cmt_close_hour':
  1425             if ($this->_comment_expire == 0) {
  1426                 $return = date('H', time() +
  1427                                ($_CONF['article_comment_close_days'] * 86400));
  1428             } else {
  1429                 $return = date('H', $this->_comment_expire);
  1430             }
  1431 
  1432             break;
  1433 
  1434         case 'cmt_close_day':
  1435             if ($this->_comment_expire == 0) {
  1436                 $return = date('d', time() +
  1437                                ($_CONF['article_comment_close_days'] * 86400));
  1438             } else {
  1439                 $return = date('d', $this->_comment_expire);
  1440             }
  1441 
  1442             break;
  1443 
  1444         case 'cmt_close_month':
  1445             if ($this->_comment_expire == 0) {
  1446                 $return = date('m', time() +
  1447                                ($_CONF['article_comment_close_days'] * 86400));
  1448             } else {
  1449                 $return = date('m', $this->_comment_expire);
  1450             }
  1451 
  1452             break;
  1453 
  1454         case 'cmt_close_year':
  1455             if ($this->_comment_expire == 0) {
  1456                 $return = date('Y', time() +
  1457                                ($_CONF['article_comment_close_days'] * 86400));
  1458             } else {
  1459                 $return = date('Y', $this->_comment_expire);
  1460             }
  1461 
  1462             break;
  1463 
  1464         case 'title':
  1465             $return = $this->_title; //htmlspecialchars($this->_title);
  1466 
  1467             break;
  1468             
  1469         case 'meta_description':
  1470             $return = $this->_meta_description;
  1471 
  1472             break;
  1473 
  1474         case 'meta_keywords':
  1475             $return = $this->_meta_keywords;
  1476 
  1477             break;
  1478                     
  1479         case 'draft_flag':
  1480             if (isset($this->_draft_flag) && ($this->_draft_flag == 1)) {
  1481                 $return = true;
  1482             } else {
  1483                 $return = false;
  1484             }
  1485 
  1486             break;
  1487 
  1488         case 'introtext':
  1489             $return = $this->_editText($this->_introtext);
  1490 
  1491             break;
  1492 
  1493         case 'bodytext':
  1494             $return = $this->_editText($this->_bodytext);
  1495 
  1496             break;
  1497 
  1498         default:
  1499             $varname = '_' . $item;
  1500 
  1501             if (isset($this->{$varname})) {
  1502                 $return = $this->{$varname};
  1503             } else {
  1504                 $return = '';
  1505             }
  1506 
  1507             break;
  1508         }
  1509 
  1510         return $return;
  1511     }
  1512 
  1513 
  1514     /**
  1515      * Provide access to story elements. For display.
  1516      *
  1517      * This is a peudo-property, implementing a getter for story
  1518      * details as if as an associative array. Personally, I'd
  1519      * rather be able to assign getters and setters to actual
  1520      * properties to mask controlled access to private member
  1521      * variables. But, you get what you get with PHP. So here it
  1522      * is in all it's nastyness.
  1523      *
  1524      * @param   string  $item   Item to fetch.
  1525      * @return  mixed   The clean and ready to use value requested.
  1526      */
  1527     function DisplayElements($item = 'title')
  1528     {
  1529         global $_CONF, $_TABLES;
  1530 
  1531         $return = '';
  1532 
  1533         switch (strtolower($item))
  1534         {
  1535         case 'introtext':
  1536             if ($this->_postmode == 'plaintext') {
  1537                 $return = nl2br($this->_introtext);
  1538             } elseif ($this->_postmode == 'wikitext') {
  1539                 $return = COM_renderWikiText($this->_editUnescape($this->_introtext));
  1540             } else {
  1541                 $return = $this->_introtext;
  1542             }
  1543 
  1544             $return = PLG_replaceTags($this->_displayEscape($return));
  1545             break;
  1546 
  1547         case 'bodytext':
  1548             if (($this->_postmode == 'plaintext') && !(empty($this->_bodytext))) {
  1549                 $return = nl2br($this->_bodytext);
  1550             } elseif (($this->_postmode == 'wikitext') && !(empty($this->_bodytext))) {
  1551                 $return = COM_renderWikiText($this->_editUnescape($this->_bodytext));
  1552             } elseif (!empty($this->_bodytext)) {
  1553                 $return = $this->_displayEscape($this->_bodytext);
  1554             }
  1555 
  1556             $return = PLG_replaceTags($return);
  1557             break;
  1558 
  1559         case 'title':
  1560             $return = $this->_displayEscape($this->_title);
  1561 
  1562             break;
  1563 
  1564         case 'meta_description':
  1565             $return = $this->_meta_description;
  1566 
  1567             break;
  1568 
  1569         case 'meta_keywords':
  1570             $return = $this->_meta_keywords;
  1571 
  1572             break;
  1573             
  1574         case 'shortdate':
  1575             $return = strftime($_CONF['shortdate'], $this->_date);
  1576 
  1577             break;
  1578 
  1579         case 'dateonly':
  1580             $return = strftime($_CONF['dateonly'], $this->_date);
  1581 
  1582             break;
  1583 
  1584         case 'date':
  1585             $return = COM_getUserDateTimeFormat($this->_date);
  1586 
  1587             $return = $return[0];
  1588             break;
  1589 
  1590         case 'unixdate':
  1591             $return = $this->_date;
  1592             break;
  1593 
  1594         case 'hits':
  1595             $return = COM_NumberFormat($this->_hits);
  1596 
  1597             break;
  1598 
  1599         case 'topic':
  1600             $return = htmlspecialchars($this->_topic);
  1601 
  1602             break;
  1603 
  1604         case 'expire':
  1605             if (empty($this->_expire)) {
  1606                 $return = time();
  1607             } else {
  1608                 $return = $this->_expire;
  1609             }
  1610 
  1611             break;
  1612             
  1613         case 'commentcode':
  1614             // check to see if comment_time has passed
  1615             if ($this->_comment_expire != 0 && (time() > $this->_comment_expire) && $this->_commentcode == 0 ) {
  1616                 // if comment code is not 1, change it to 1
  1617                 DB_query("UPDATE {$_TABLES['stories']} SET commentcode = '1' WHERE sid = '$this->_sid'");
  1618                 $return = 1;
  1619             } else {
  1620                 $return = $this->_commentcode;
  1621             }
  1622             break;
  1623 
  1624         default:
  1625             $varname = '_' . $item;
  1626 
  1627             if (isset($this->{$varname})) {
  1628                 $return = $this->{$varname};
  1629             }
  1630 
  1631             break;
  1632         }
  1633 
  1634         return $return;
  1635     }
  1636 
  1637     /**
  1638      * Set the TID to a new value.
  1639      *
  1640      * @param   $tid    int ID of the topic to set
  1641      */
  1642     function setTid($tid)
  1643     {
  1644         $this->_tid = $tid;
  1645     }
  1646 
  1647     /**
  1648      * Perform a security check and return permission level.
  1649      *
  1650      * saves the bother of accessing dozen's of vars.
  1651      *
  1652      * @return  int access level for this story
  1653      */
  1654     function checkAccess()
  1655     {
  1656         return SEC_hasAccess($this->_owner_id, $this->_group_id,
  1657                              $this->_perm_owner, $this->_perm_group,
  1658                              $this->_perm_members, $this->_perm_anon);
  1659     }
  1660 
  1661 
  1662     // End Public Methods.
  1663 
  1664     // Private Methods:
  1665 
  1666     /**
  1667      * Escapes certain HTML for nicely encoded HTML.
  1668      *
  1669      * @access Private
  1670      * @param   string     $in      Text to escpae
  1671      * @return  string     escaped string
  1672      */
  1673     function _displayEscape($in)
  1674     {
  1675         $return = str_replace('$', '&#36;', $in);
  1676         $return = str_replace('{', '&#123;', $return);
  1677         $return = str_replace('}', '&#125;', $return);
  1678         return $return;
  1679     }
  1680 
  1681     /**
  1682      * Unescapes certain HTML for editing again.
  1683      *
  1684      * @access Private
  1685      * @param   string  $in Text escaped to unescape for editing
  1686      * @return  string  Unescaped string
  1687      */
  1688     function _editUnescape($in)
  1689     {
  1690         if (($this->_postmode == 'html') || ($this->_postmode == 'wikitext')) {
  1691             /* Raw and code blocks need entity decoding. Other areas do not.
  1692              * otherwise, annoyingly, &lt; will end up as < on preview 1, on
  1693              * preview 2 it'll be stripped by KSES. Can't beleive I missed that
  1694              * in rewrite phase 1.
  1695              *
  1696              * First, raw
  1697              */
  1698             $inlower = MBYTE_strtolower($in);
  1699             $buffer = $in;
  1700             $start_pos = MBYTE_strpos($inlower, '[raw]');
  1701             if( $start_pos !== false ) {
  1702                 $out = '';
  1703                 while( $start_pos !== false ) {
  1704                     /* Copy in to start to out */
  1705                     $out .= MBYTE_substr($buffer, 0, $start_pos);
  1706                     /* Find end */
  1707                     $end_pos = MBYTE_strpos($inlower, '[/raw]');
  1708                     if( $end_pos !== false ) {
  1709                         /* Encode body and append to out */
  1710                         $encoded = html_entity_decode(MBYTE_substr($buffer, $start_pos, $end_pos - $start_pos));
  1711                         $out .= $encoded . '[/raw]';
  1712                         /* Nibble in */
  1713                         $inlower = MBYTE_substr($inlower, $end_pos + 6);
  1714                         $buffer = MBYTE_substr($buffer, $end_pos + 6);
  1715                     } else { // missing [/raw]
  1716                         // Treat the remainder as code, but this should have been
  1717                         // checked prior to calling:
  1718                         $out .= html_entity_decode(MBYTE_substr($buffer, $start_pos + 5));
  1719                         $inlower = '';
  1720                     }
  1721                     $start_pos = MBYTE_strpos($inlower, '[raw]');
  1722                 }
  1723                 // Append remainder:
  1724                 if( $buffer != '' ) {
  1725                     $out .= $buffer;
  1726                 }
  1727                 $in = $out;
  1728             }
  1729             /*
  1730              * Then, code
  1731              */
  1732             $inlower = MBYTE_strtolower($in);
  1733             $buffer = $in;
  1734             $start_pos = MBYTE_strpos($inlower, '[code]');
  1735             if( $start_pos !== false ) {
  1736                 $out = '';
  1737                 while( $start_pos !== false ) {
  1738                     /* Copy in to start to out */
  1739                     $out .= MBYTE_substr($buffer, 0, $start_pos);
  1740                     /* Find end */
  1741                     $end_pos = MBYTE_strpos($inlower, '[/code]');
  1742                     if( $end_pos !== false ) {
  1743                         /* Encode body and append to out */
  1744                         $encoded = html_entity_decode(MBYTE_substr($buffer, $start_pos, $end_pos - $start_pos));
  1745                         $out .= $encoded . '[/code]';
  1746                         /* Nibble in */
  1747                         $inlower = MBYTE_substr($inlower, $end_pos + 7);
  1748                         $buffer = MBYTE_substr($buffer, $end_pos + 7);
  1749                     } else { // missing [/code]
  1750                         // Treat the remainder as code, but this should have been
  1751                         // checked prior to calling:
  1752                         $out .= html_entity_decode(MBYTE_substr($buffer, $start_pos + 6));
  1753                         $inlower = '';
  1754                     }
  1755                     $start_pos = MBYTE_strpos($inlower, '[code]');
  1756                 }
  1757                 // Append remainder:
  1758                 if( $buffer != '' ) {
  1759                     $out .= $buffer;
  1760                 }
  1761                 $in = $out;
  1762             }
  1763             return $in;
  1764         } else {
  1765             // advanced editor or plaintext can handle themselves...
  1766             return $in;
  1767         }
  1768     }
  1769 
  1770     /**
  1771      * Returns text ready for the edit fields.
  1772      *
  1773      * @access Private
  1774      * @param   string  $in Text to prepare for editing
  1775      * @return  string  Escaped String
  1776      */
  1777     function _editText($in)
  1778     {
  1779         $out = '';
  1780 
  1781         $out = $this->replaceImages($in);
  1782 
  1783         if ($this->_postmode == 'plaintext') {
  1784             $out = COM_undoClickableLinks($out);
  1785             $out = $this->_displayEscape($out);
  1786         } elseif ($this->_postmode == 'wikitext') {
  1787             $out = $this->_editUnescape($in);
  1788         } else {
  1789             // html
  1790             $out = str_replace('<pre><code>', '[code]', $out);
  1791             $out = str_replace('</code></pre>', '[/code]', $out);
  1792             $out = str_replace('<!--raw--><span class="raw">', '[raw]', $out);
  1793             $out = str_replace('</span><!--/raw-->', '[/raw]', $out);
  1794             $out = $this->_editUnescape($out);
  1795             $out = $this->_displayEscape(htmlspecialchars($out));
  1796         }
  1797 
  1798         return $out;
  1799     }
  1800 
  1801     /**
  1802      * Loads the basic details of an article into the internal
  1803      * variables, cleaning them up nicely.
  1804      * @access Private
  1805      * @param $array Array of POST/GET data (by ref).
  1806      * @return Nothing.
  1807      */
  1808     function _loadBasics(&$array)
  1809     {
  1810         /* For the really, really basic stuff, we can very easily load them
  1811          * based on an array that defines how to COM_applyFilter them.
  1812          */
  1813         foreach ($this->_postFields as $key => $value) {
  1814             $vartype = $value[0];
  1815             $varname = $value[1];
  1816 
  1817             // If we have a value
  1818             if (array_key_exists($key, $array)) {
  1819                 // And it's alphanumeric or numeric, filter it and use it.
  1820                 if (($vartype == STORY_AL_ALPHANUM) || ($vartype == STORY_AL_NUMERIC)) {
  1821                     $this->{$varname} = COM_applyFilter($array[$key], $vartype);
  1822                 } elseif ($vartype == STORY_AL_ANYTHING) {
  1823                     $this->{$varname} = $array[$key];                
  1824                 } elseif (($array[$key] === 'on') || ($array[$key] === 1)) {
  1825                     // If it's a checkbox that is on
  1826                     $this->{$varname} = 1;
  1827                 } else {
  1828                     // Otherwise, it must be a checkbox that is off:
  1829                     $this->{$varname} = 0;
  1830                 }
  1831             } elseif (($vartype == STORY_AL_NUMERIC) || ($vartype == STORY_AL_CHECKBOX)) {
  1832                 // If we don't have a value, and have a numeric or text box, default to 0
  1833                 $this->{$varname} = 0;
  1834             }
  1835         }
  1836 
  1837         // SID's are a special case:
  1838         $sid = COM_sanitizeID($array['sid']);
  1839         if (isset($array['old_sid'])) {
  1840             $oldsid = COM_sanitizeID($array['old_sid'], false);
  1841         } else {
  1842             $oldsid = '';
  1843         }
  1844 
  1845         if (empty($sid)) {
  1846             $sid = $oldsid;
  1847         }
  1848 
  1849         if (empty($sid)) {
  1850             $sid = COM_makeSid();
  1851         }
  1852 
  1853         $this->_sid = $sid;
  1854         $this->_originalSid = $oldsid;
  1855 
  1856         /* Need to deal with the postdate and expiry date stuff */
  1857         $publish_ampm = '';
  1858         if (isset($array['publish_ampm'])) {
  1859             $publish_ampm = COM_applyFilter($array['publish_ampm']);
  1860         }
  1861         $publish_hour = 0;
  1862         if (isset($array['publish_hour'])) {
  1863             $publish_hour = COM_applyFilter($array['publish_hour'], true);
  1864         }
  1865         $publish_minute = 0;
  1866         if (isset($array['publish_minute'])) {
  1867             $publish_minute = COM_applyFilter($array['publish_minute'], true);
  1868         }
  1869         $publish_second = 0;
  1870         if (isset($array['publish_second'])) {
  1871             $publish_second = COM_applyFilter($array['publish_second'], true);
  1872         }
  1873 
  1874         if ($publish_ampm == 'pm') {
  1875             if ($publish_hour < 12) {
  1876                 $publish_hour = $publish_hour + 12;
  1877             }
  1878         }
  1879 
  1880         if ($publish_ampm == 'am' AND $publish_hour == 12) {
  1881             $publish_hour = '00';
  1882         }
  1883 
  1884         $publish_year = 0;
  1885         if (isset($array['publish_year'])) {
  1886             $publish_year = COM_applyFilter($array['publish_year'], true);
  1887         }
  1888         $publish_month = 0;
  1889         if (isset($array['publish_month'])) {
  1890             $publish_month = COM_applyFilter($array['publish_month'], true);
  1891         }
  1892         $publish_day = 0;
  1893         if (isset($array['publish_day'])) {
  1894             $publish_day = COM_applyFilter($array['publish_day'], true);
  1895         }
  1896         $this->_date = strtotime(
  1897                            "$publish_month/$publish_day/$publish_year $publish_hour:$publish_minute:$publish_second");
  1898 
  1899         $archiveflag = 0;
  1900 
  1901         if (isset($array['archiveflag'])) {
  1902             $archiveflag = COM_applyFilter($array['archiveflag'], true);
  1903         }
  1904         /* Override status code if no archive flag is set: */
  1905         if ($archiveflag != 1) {
  1906             $this->_statuscode = 0;
  1907         }
  1908 
  1909         if (array_key_exists('expire_ampm', $array)) {
  1910             $expire_ampm = COM_applyFilter($array['expire_ampm']);
  1911             $expire_hour = COM_applyFilter($array['expire_hour'], true);
  1912             $expire_minute = COM_applyFilter($array['expire_minute'], true);
  1913             $expire_second = COM_applyFilter($array['expire_second'], true);
  1914             $expire_year = COM_applyFilter($array['expire_year'], true);
  1915             $expire_month = COM_applyFilter($array['expire_month'], true);
  1916             $expire_day = COM_applyFilter($array['expire_day'], true);
  1917 
  1918             if ($expire_ampm == 'pm') {
  1919                 if ($expire_hour < 12) {
  1920                     $expire_hour = $expire_hour + 12;
  1921                 }
  1922             }
  1923 
  1924             if ($expire_ampm == 'am' AND $expire_hour == 12) {
  1925                 $expire_hour = '00';
  1926             }
  1927 
  1928             $expiredate
  1929             = strtotime("$expire_month/$expire_day/$expire_year $expire_hour:$expire_minute:$expire_second");
  1930         } else {
  1931             $expiredate = time();
  1932         }
  1933 
  1934         $this->_expire = $expiredate;
  1935         
  1936         // comment expire time
  1937         if (isset($array['cmt_close_flag'])) {
  1938             $cmt_close_ampm = COM_applyFilter($array['cmt_close_ampm']);
  1939             $cmt_close_hour = COM_applyFilter($array['cmt_close_hour'], true);
  1940             $cmt_close_minute = COM_applyFilter($array['cmt_close_minute'], true);
  1941             $cmt_close_second = COM_applyFilter($array['cmt_close_second'], true);
  1942             $cmt_close_year = COM_applyFilter($array['cmt_close_year'], true);
  1943             $cmt_close_month = COM_applyFilter($array['cmt_close_month'], true);
  1944             $cmt_close_day = COM_applyFilter($array['cmt_close_day'], true);
  1945             
  1946             if ($cmt_close_ampm == 'pm') {
  1947                 if ($cmt_close_hour < 12) {
  1948                     $cmt_close_hour = $cmt_close_hour + 12;
  1949                 }
  1950             }
  1951 
  1952             if ($cmt_close_ampm == 'am' AND $cmt_close_hour == 12) {
  1953                 $cmt_close_hour = '00';
  1954             }
  1955 
  1956             $cmt_close_date
  1957             = strtotime("$cmt_close_month/$cmt_close_day/$cmt_close_year $cmt_close_hour:$cmt_close_minute:$cmt_close_second");
  1958 
  1959             $this->_comment_expire = $cmt_close_date;
  1960         } else {
  1961             $this->_comment_expire = 0;
  1962         }
  1963 
  1964 
  1965         /* Then grab the permissions */
  1966 
  1967         // Convert array values to numeric permission values
  1968         if (is_array($array['perm_owner']) || is_array($array['perm_group']) ||
  1969                 is_array($array['perm_members']) ||
  1970                 is_array($array['perm_anon'])) {
  1971 
  1972             list($this->_perm_owner, $this->_perm_group, $this->_perm_members, $this->_perm_anon) = SEC_getPermissionValues($array['perm_owner'], $array['perm_group'], $array['perm_members'], $array['perm_anon']);
  1973 
  1974         } else {
  1975             $this->_perm_owner   = $array['perm_owner'];
  1976             $this->_perm_group   = $array['perm_group'];
  1977             $this->_perm_members = $array['perm_members'];
  1978             $this->_perm_anon    = $array['perm_anon'];
  1979         }
  1980     }
  1981 
  1982     /**
  1983      * This is the importantest bit. This function must load the title, intro
  1984      * and body of the article from the post array, providing all appropriate
  1985      * conversions of HTML mode content into the nice safe form that geeklog
  1986      * can then (simply) spit back out into the page on render. After doing a
  1987      * magic tags replacement.
  1988      *
  1989      * This DOES NOT ADDSLASHES! We do that on DB store, because we want to
  1990      * keep our internal variables in "display mode", not in db mode or anything.
  1991      *
  1992      * @param $title    string  posttitle, only had stripslashes if necessary
  1993      * @param $intro    string  introtext, only had stripslashes if necessary
  1994      * @param $body     string   bodytext, only had stripslashes if necessary
  1995      * @return nothing
  1996      * @access private
  1997      */
  1998     function _htmlLoadStory($title, $intro, $body)
  1999     {
  2000         global $_CONF;
  2001 
  2002         // fix for bug in advanced editor
  2003         if ($_CONF['advanced_editor'] && ($body == '<br' . XHTML . '>')) {
  2004             $body = '';
  2005         }
  2006 
  2007         $this->_title = htmlspecialchars(strip_tags(COM_checkWords($title)));
  2008         $this->_introtext = COM_checkHTML(COM_checkWords($intro), 'story.edit');
  2009         $this->_bodytext = COM_checkHTML(COM_checkWords($body), 'story.edit');
  2010     }
  2011 
  2012 
  2013     /**
  2014      * This is the second most importantest bit. This function must load the
  2015      * title, intro and body of the article from the post array, removing all
  2016      * HTML mode content into the nice safe form that geeklog can then (simply)
  2017      * spit back out into the page on render. After doing a magic tags
  2018      * replacement. And nl2br.
  2019      *
  2020      * This DOES NOT ADDSLASHES! We do that on DB store, because we want to
  2021      * keep our internal variables in "display mode", not in db mode or anything.
  2022      *
  2023      * @param $title    string  posttitle, only had stripslashes if necessary
  2024      * @param $intro    string  introtext, only had stripslashes if necessary
  2025      * @param $body     string   bodytext, only had stripslashes if necessary
  2026      * @return nothing
  2027      * @access private
  2028      */
  2029     function _plainTextLoadStory($title, $intro, $body)
  2030     {
  2031         $this->_title = htmlspecialchars(strip_tags(COM_checkWords($title)));
  2032         $this->_introtext = COM_makeClickableLinks(htmlspecialchars(COM_checkWords($intro)));
  2033         $this->_bodytext = COM_makeClickableLinks(htmlspecialchars(COM_checkWords($body)));
  2034     }
  2035 
  2036     /**
  2037      * Perform some basic cleanups of data, dealing with empty required,
  2038      * defaultable fields.
  2039      */
  2040     function _sanitizeData()
  2041     {
  2042         global $_CONF;
  2043 
  2044         if (empty($this->_hits)) {
  2045             $this->_hits = 0;
  2046         }
  2047 
  2048         if (empty($this->_commentcount)) {
  2049             $this->_comments = 0;
  2050         }
  2051 
  2052         if (empty($this->_numemails)) {
  2053             $this->_numemails = 0;
  2054         }
  2055 
  2056         if (empty($this->_trackbacks)) {
  2057             $this->_trackbacks = 0;
  2058         }
  2059 
  2060         if ($this->_draft_flag === 'on') {
  2061             $this->_draft_flag = 1;
  2062         } elseif ($this->_draft_flag != 1) {
  2063             $this->_draft_flag = 0;
  2064         }
  2065 
  2066         if ($this->_show_topic_icon === 'on') {
  2067             $this->_show_topic_icon = 1;
  2068         } elseif ($this->_show_topic_icon != 1) {
  2069             $this->_show_topic_icon = 0;
  2070         }
  2071     }
  2072 
  2073 // End Private Methods.
  2074 
  2075 /**************************************************************************/
  2076 }
  2077 
  2078 ?>