<commit_before><?php
/**
 * Stories Peer.
 *
 * Methods:
 *   getStory($userId, $ucsId)
 *   updateStory($userId, $ucsId, $data)
 *   deleteStory($userId, $ucsId)
 *   getFormattedStory($story, $keyword, $bSubstituteLinks = true, $linebreaks = true)
 *   getFormattedKanjiLink($matches)
 *   getSharedStories($ucsId, $keyword, $userId)
 *   getStoriesCounts($userId)
 *   getMyStoriesSelect($userId)
 *   getSelectForExport($userId)
 *
 * 
 * @author  <NAME>
 */

class StoriesPeer extends coreDatabaseTable
{
  protected
    $tableName = 'stories',
    $columns = array('updated_on'); // timestamp columns must be declared for insert/update/replace

  /**
   * This function must be copied in each peer class.
   */
  public static function getInstance()
  {
    return coreDatabaseTable::_getInstance(__CLASS__);
  }

  /**
   *
   * @return int|boolean   Number of rows, or FALSE
   */
  public static function getNumStories($userId)
  {
    return self::getInstance()->count('userid = ?', $userId);
  }

  /**
   * Returns story and story settings for given user.
   * 
   * @return object  Stories row data as object, or false.
   */
  public static function getStory($userId, $ucsId)
  {
    assert('(int)$ucsId >= 0x3000');

    $select = self::getInstance()->select()
      ->where('userid = ? AND ucs_id = ?', array($userId, $ucsId))
      ->query();
    return self::$db->fetchObject();
  }

  /**
   * Returns ONE starred story for the user, and given character (no order is
   * guaranteed).
   *
   * @return object  Stories row data as object, or false.
   */
  public static function getFavouriteStory($userId, $ucsId)
  {
    $authorId = StoryVotesPeer::getStarredStory($userId, $ucsId);

    return false !== $authorId ? StoriesPeer::getStory($authorId, $ucsId) : false;
  }

  /**
   * Create/Update story and story settings for user.
   * 
   * @param  int    $userId    User id
   * @param  int    $ucsId     UCS-2 code value.
   * @param  array  $data      Cols: text, public
   *
   * @return  bool    true if the story update (stories table) is succesful
   */
  public static function updateStory($userId, $ucsId, $data)
  {
    $result = true;

    assert('(int)$ucsId >= 0x3000');

    $storyId = self::$db->fetchOne(
      self::$db->select('sid')->from('stories')->where('userid = ? AND ucs_id = ?', array($userId, $ucsId))
    );

    // either false (no row), or a positive auto increment number > 0 
    assert('$storyId !== 0');

    if (false === $storyId)
    {
      $data = array_merge($data, array('userid' => $userId, 'ucs_id' => $ucsId));
      self::getInstance()->insert($data);
      $storyId = self::$db->lastInsertId();
    }
    else
    {
      $result = $result && self::getInstance()->update($data, 'sid = ?', $storyId);
    }

    // after insert or update we need a valid story id > 0
    assert('$storyId > 0', 'updateStory()');

    // if this table isn't updated it's not crucial (but the other way around is)
    StoriesSharedPeer::updateStoryRef($storyId, $ucsId, $userId, $data['public'] === 1);

    return $result;
  }
  
  /**
   * Delete a story.
   * 
   * @param  int    $userId   User id
   * @param  int    $ucsId    UCS-2 character code.
   *
   * @return
   */
  public static function deleteStory($userId, $ucsId)
  {
    assert('(int)$ucsId >= 0x3000');

    return self::getInstance()->delete('userid = ? AND ucs_id = ?', array($userId, $ucsId));
  }

  /**
   * Return a story formatted for display, with automatic keyword bolding.
   * 
   * The input story is ESCAPED before html tags are inserted for the formatting.
   * It is assumed strip_tags() was used previously. The returned string should not be escaped
   * again in the view template.
   * 
   * @param  String   $story
   * @param  String   $keyword
   * @param  Boolean  $bSubstituteLinks    True to show frame number references as links otherwise plain text.
   * @param  Boolean  $linebreaks          Replace CR/LF with BR tags.
   *
   * @return String
   */
  public static function getFormattedStory($story, $keyword, $bSubstituteLinks = true, $linebreaks = true)
  {
    // Links helper is used by getFormattedKanjiLink() call
    sfProjectConfiguration::getActive()->loadHelpers(array('Tag', 'Url'));

    // minimal punctuation : upper case first beginning of text
    $s = phpToolkit::mb_ucfirst($story);

//echo error_reporting();exit;

    // minimal punctuation : end sentence with dot (accepts "quote." spelling)
    if (preg_match ('/[^.!?][^.!?]$/', $s))
    {
      $s = $s . '.';
    }<commit_msg>Fix #95 "Capitalizing first word of story".<commit_after><?php
/**
 * Stories Peer.
 *
 * Methods:
 *   getStory($userId, $ucsId)
 *   updateStory($userId, $ucsId, $data)
 *   deleteStory($userId, $ucsId)
 *   getFormattedStory($story, $keyword, $bSubstituteLinks = true, $linebreaks = true)
 *   getFormattedKanjiLink($matches)
 *   getSharedStories($ucsId, $keyword, $userId)
 *   getStoriesCounts($userId)
 *   getMyStoriesSelect($userId)
 *   getSelectForExport($userId)
 *
 * 
 * @author  <NAME>
 */

class StoriesPeer extends coreDatabaseTable
{
  protected
    $tableName = 'stories',
    $columns = array('updated_on'); // timestamp columns must be declared for insert/update/replace

  /**
   * This function must be copied in each peer class.
   */
  public static function getInstance()
  {
    return coreDatabaseTable::_getInstance(__CLASS__);
  }

  /**
   *
   * @return int|boolean   Number of rows, or FALSE
   */
  public static function getNumStories($userId)
  {
    return self::getInstance()->count('userid = ?', $userId);
  }

  /**
   * Returns story and story settings for given user.
   * 
   * @return object  Stories row data as object, or false.
   */
  public static function getStory($userId, $ucsId)
  {
    assert('(int)$ucsId >= 0x3000');

    $select = self::getInstance()->select()
      ->where('userid = ? AND ucs_id = ?', array($userId, $ucsId))
      ->query();
    return self::$db->fetchObject();
  }

  /**
   * Returns ONE starred story for the user, and given character (no order is
   * guaranteed).
   *
   * @return object  Stories row data as object, or false.
   */
  public static function getFavouriteStory($userId, $ucsId)
  {
    $authorId = StoryVotesPeer::getStarredStory($userId, $ucsId);

    return false !== $authorId ? StoriesPeer::getStory($authorId, $ucsId) : false;
  }

  /**
   * Create/Update story and story settings for user.
   * 
   * @param  int    $userId    User id
   * @param  int    $ucsId     UCS-2 code value.
   * @param  array  $data      Cols: text, public
   *
   * @return  bool    true if the story update (stories table) is succesful
   */
  public static function updateStory($userId, $ucsId, $data)
  {
    $result = true;

    assert('(int)$ucsId >= 0x3000');

    $storyId = self::$db->fetchOne(
      self::$db->select('sid')->from('stories')->where('userid = ? AND ucs_id = ?', array($userId, $ucsId))
    );

    // either false (no row), or a positive auto increment number > 0 
    assert('$storyId !== 0');

    if (false === $storyId)
    {
      $data = array_merge($data, array('userid' => $userId, 'ucs_id' => $ucsId));
      self::getInstance()->insert($data);
      $storyId = self::$db->lastInsertId();
    }
    else
    {
      $result = $result && self::getInstance()->update($data, 'sid = ?', $storyId);
    }

    // after insert or update we need a valid story id > 0
    assert('$storyId > 0', 'updateStory()');

    // if this table isn't updated it's not crucial (but the other way around is)
    StoriesSharedPeer::updateStoryRef($storyId, $ucsId, $userId, $data['public'] === 1);

    return $result;
  }
  
  /**
   * Delete a story.
   * 
   * @param  int    $userId   User id
   * @param  int    $ucsId    UCS-2 character code.
   *
   * @return
   */
  public static function deleteStory($userId, $ucsId)
  {
    assert('(int)$ucsId >= 0x3000');

    return self::getInstance()->delete('userid = ? AND ucs_id = ?', array($userId, $ucsId));
  }

  /**
   * Return a story formatted for display, with automatic keyword bolding.
   * 
   * The input story is ESCAPED before html tags are inserted for the formatting.
   * It is assumed strip_tags() was used previously. The returned string should not be escaped
   * again in the view template.
   * 
   * @param  String   $story
   * @param  String   $keyword
   * @param  Boolean  $bSubstituteLinks    True to show frame number references as links otherwise plain text.
   * @param  Boolean  $linebreaks          Replace CR/LF with BR tags.
   *
   * @return String
   */
  public static function getFormattedStory($story, $keyword, $bSubstituteLinks = true, $linebreaks = true)
  {
    // Links helper is used by getFormattedKanjiLink() call
    sfProjectConfiguration::getActive()->loadHelpers(array('Tag', 'Url'));

    $s = $story;

    // minimal punctuation : upper case first word (ignore non-alphabet & multibyte)
    if (preg_match('/[a-z]/i', $s, $match, PREG_OFFSET_CAPTURE))
    {
      $i = $match[0][1];
      $s = substr($s, 0, $i) . ucfirst(substr($s, $i));
    }

    // minimal punctuation : end sentence with dot (accepts "quote." spelling)
    if (preg_match ('/[^.!?][^.!?]$/', $s))
    {
      $s = $s . '.';
    }
{'fraction_non_alphanumeric': 0.1464548205793342, 'fraction_numerical': 0.005296152183311717, 'mean_word_length': 3.0108365843086258, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1844543', 'n_tokens_mistral': 3084, 'n_tokens_neox': 2990, 'n_words': 826}