getObject(); if ( !$this->Application->isAdminUser ) { $where_clause = '(Archived = 0) AND (StartDate < ' . time() . ' OR StartDate = 0) AND (EndOn > ' . time() . ' OR EndOn IS NULL)'; $object->addFilter('archived_filter', $where_clause); } } /** * Return type clauses for list bulding on front * * @param kEvent $event * @return Array */ function getTypeClauses($event) { $type_clauses = parent::getTypeClauses($event); $type_clauses['site_lead']['include']='%1$s.LeadStory = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['site_lead']['except']='%1$s.LeadStory <> 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['site_lead']['having_filter'] = false; $type_clauses['cat_lead']['include']='%1$s.LeadCatStory = 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['cat_lead']['except']='%1$s.LeadCatStory <> 1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['cat_lead']['having_filter'] = false; return $type_clauses; } /** * [CRON] Deletes expired articles + update existing articles from rss feed with new data (key - article url) * * @param kEvent $event */ function OnUpdateRSSArticles($event) { if ( defined('IS_INSTALL') && IS_INSTALL ) { return; } $category_table = $this->Application->getUnitConfig('c')->getTableName(); $custom_table = $this->Application->getUnitConfig('c-cdata')->getTableName(); $category_custom_fields = $this->getCustomColumns('c'); $article_custom_fields = $this->getCustomColumns($event->Prefix); // update categories which should be updated $sql = 'SELECT cd.*, c.CategoryId FROM '.$category_table.' c LEFT JOIN '.$custom_table.' cd ON c.ResourceId = cd.ResourceId WHERE (IF(cd.'.$category_custom_fields['RssLastUpdated'].' IS NULL, 0, cd.'.$category_custom_fields['RssLastUpdated'].') + cd.'.$category_custom_fields['RssUpdateInterval'].' * cd.'.$category_custom_fields['RssUpdateIntervalType'].' <= '.time().') AND (LENGTH('.$category_custom_fields['RssSource'].') > 0)'; $categories = $this->Conn->Query($sql, 'CategoryId'); if ($categories) { $resource_ids = Array(); foreach ($categories as $category_id => $category_data) { $resource_ids[] = $category_data['ResourceId']; $event->setEventParam('source_url', $category_data[ $category_custom_fields['RssSource'] ]); $event->setEventParam('category_id', $category_id); $event->setEventParam('custom_fields', $article_custom_fields); $event->setEventParam('life_time', $category_data[ $category_custom_fields['RssDefaultExpiration'] ] * $category_data[ $category_custom_fields['RssDefaultExpirationType'] ]); $this->parseFeed($event); } $sql = 'UPDATE '.$custom_table.' SET '.$category_custom_fields['RssLastUpdated'].' = '.time().' WHERE ResourceId IN ('.implode(',', $resource_ids).')'; $this->Conn->Query($sql); } // delete expired articles from feed categories $sql = 'SELECT c.CategoryId, c.ResourceId FROM '.$category_table.' c LEFT JOIN '.$custom_table.' cd ON c.ResourceId = cd.ResourceId WHERE ( IF(cd.'.$category_custom_fields['RssLastExpired'].' IS NULL, 0, cd.'.$category_custom_fields['RssLastExpired'].') + cd.'.$category_custom_fields['RssExpireInterval'].' * cd.'.$category_custom_fields['RssExpireIntervalType'].' <= '.time().') AND (cd.'.$category_custom_fields['RssDeleteExpired'].' = 1)'; $categories = $this->Conn->GetCol($sql, 'ResourceId'); $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table = $config->getTableName(); $ci_table = $this->Application->getUnitConfig($event->Prefix . '-ci')->getTableName(); if ($categories) { $article_custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $sql = 'SELECT main_table.'.$id_field.' FROM '.$table.' main_table LEFT JOIN '.$ci_table.' ci ON main_table.ResourceId = ci.ItemResourceId LEFT JOIN '.$article_custom_table.' cd ON main_table.ResourceId = cd.ResourceId WHERE (ci.PrimaryCat = 1) AND (ci.CategoryId IN ('.implode(',', $categories).')) AND (main_table.EndOn < '.time().' AND main_table.EndOn IS NOT NULL) AND (LENGTH(cd.'.$article_custom_fields['RssOriginalURL'].') > 0)'; $article_ids = $this->Conn->GetCol($sql); if ($article_ids) { /** @var kTempTablesHandler $temp_handler */ $temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); $temp_handler->DeleteItems($event->Prefix, $event->Special, $article_ids); } $sql = 'UPDATE '.$custom_table.' SET '.$category_custom_fields['RssLastExpired'].' = '.time().' WHERE ResourceId IN ('.implode(',', array_keys($categories)).')'; $this->Conn->Query($sql); } } /** * Returns article ids & crc, that are created during feed import * * @param kEvent $event * @return Array */ function getFeedArticles($event) { $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table = $config->getTableName(); $custom_table = $this->Application->getUnitConfig($event->Prefix . '-cdata')->getTableName(); $crc_field = $event->getEventParam('custom_fields', 'RssArticleCRC'); $sql = 'SELECT main_table.' . $id_field . ', cd.' . $crc_field . ' FROM ' . $table . ' main_table LEFT JOIN ' . $custom_table . ' cd ON cd.ResourceId = main_table.ResourceId WHERE LENGTH(cd.' . $crc_field . ') > 0'; return $this->Conn->GetCol($sql, $crc_field); } /** * Creates new, updates existing articles from feed url specified * * @param kEvent $event */ function parseFeed($event) { $source_urls = explode(',', $event->getEventParam('source_url')); if (count($source_urls) > 1) { foreach ($source_urls as $source_url) { $event->setEventParam('source_url', $source_url); $this->parseFeed($event); } return true; } /** @var kCurlHelper $curl_helper */ $curl_helper = $this->Application->recallObject('CurlHelper'); $curl_helper->followLocation = true; $curl_helper->setOptions( Array (CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows; U; Windows NT 5.2; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)') ); // otherwise FeedBurner will return HTML $xml_data = $curl_helper->Send($event->getEventParam('source_url')); if (!$xml_data) { return false; } /** @var kXMLHelper $xml_helper */ $xml_helper = $this->Application->recallObject('kXMLHelper'); $root_node =& $xml_helper->Parse($xml_data, kXMLHelper::XML_WITH_TEXT_NODES); $feed_types = Array ( 'rss_2.0' => 'channel', 'atom' => 'feed', ); foreach ($feed_types as $feed_type => $node_name) { $article_node =& $root_node->FindChild($node_name); if (is_object($article_node)) { break; } } if (!$article_node) { return false; } switch ($feed_type) { case 'rss_2.0': $this->parseRssFeed($article_node, $event); break; case 'atom': $this->parseAtomFeed($article_node, $event); break; } } /** * Returns ML field names for article record * * @param kCatDBItem $object * @return Array */ function _getMLFields(&$object) { /** @var kMultiLanguage $ml_formatter */ $ml_formatter = $this->Application->recallObject('kMultiLanguage'); $title_field = 'Title'; $title_formatter = $object->GetFieldOption($title_field, 'formatter'); if ( $title_formatter == 'kMultiLanguage' ) { $title_field = $ml_formatter->LangFieldName($title_field); } $body_field = 'Body'; $body_formatter = $object->GetFieldOption($body_field, 'formatter'); if ( $body_formatter == 'kMultiLanguage' ) { $body_field = $ml_formatter->LangFieldName($body_field); } return Array ($title_field, $body_field); } /** * Parses RSS 2.0 feed * * @param kXMLNode $root_node * @param kEvent $event */ function parseRssFeed(&$root_node, $event) { $current_node = $root_node->firstChild; $feed_articles = $this->getFeedArticles($event); /** @var kDBItem $object */ $object = $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true)); $category_id = $event->getEventParam('category_id'); list ($title_field, $body_field) = $this->_getMLFields($object); do { // IMAGE is information about channel and is not useful here if ($current_node->Name != 'ITEM') continue; // collect item data $data = Array(); /** @var kXMLNode $sub_node */ $sub_node =& $current_node->firstChild; do { if ($sub_node->Name == 'ATOM:SUMMARY') { $data[$sub_node->Name] = $this->getNodeContent($sub_node); } else { $data[$sub_node->Name] = $this->getNodeContent($sub_node, 'xhtml'); // $sub_node->firstChild->Data; // was $sub_node->Data; } } while ( ($sub_node =& $sub_node->NextSibling()) ); // create/update article $article_crc = kUtil::crc32($data['LINK'].$data['TITLE']); $article_id = getArrayValue($feed_articles, $article_crc); if ($article_id) { $object->Load($article_id); } else { $object->Clear(); } $object->SetDBField('CategoryId', $category_id); $object->SetDBField($title_field, $data['TITLE']); $object->SetDBField('cust_RssOriginalURL', $data['LINK']); $object->SetDBField('cust_RssArticleCRC', $article_crc); $object->SetDBField($body_field, !array_key_exists('DESCRIPTION', $data) ? $data['ATOM:SUMMARY'] : $data['DESCRIPTION']); $expiration_time = time() + $event->getEventParam('life_time'); $object->SetDBField('EndOn_date', $expiration_time); $object->SetDBField('EndOn_time', $expiration_time); $object->SetDBField('Status', STATUS_ACTIVE); $object->SetDBField('Author', 'root'); $object->SetDBField('CreatedById', USER_ROOT); $status = $object->isLoaded() ? $object->Update() : $object->Create(); } while (($current_node =& $current_node->NextSibling())); } /** * Returns parsed node content * * @param kXMLNode $node * @param string $content_type * @return string */ function getNodeContent(&$node, $content_type = null) { if ( !isset($content_type) ) { $content_type = $node->GetAttribute('TYPE'); } switch ($content_type) { case 'xhtml': $data = $node->GetXML(true); break; case 'html': $data = kUtil::unescape($node->GetXML(true), kUtil::ESCAPE_HTML); // $node->firstChild->Data // $node->Data break; default: $data = $node->GetXML(true); // $node->firstChild->Data; // $node->Data; also for 'text' break; } return trim($data); } /** * Parses ATOM feed * * @param kXMLNode $root_node * @param kEvent $event */ function parseAtomFeed(&$root_node, $event) { $current_node = $root_node->firstChild; $feed_articles = $this->getFeedArticles($event); /** @var kDBItem $object */ $object = $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true)); $category_id = $event->getEventParam('category_id'); list ($title_field, $body_field) = $this->_getMLFields($object); do { if ($current_node->Name != 'ENTRY') continue; // collect item data $data = Array(); /** @var kXMLNode $sub_node */ $sub_node =& $current_node->firstChild; do { if ($sub_node->Name == 'LINK') { if ($sub_node->GetAttribute('REL') === false || $sub_node->GetAttribute('REL') == 'alternate') { $data[$sub_node->Name] = $sub_node->GetAttribute('HREF'); } } elseif ($sub_node->Name == 'CONTENT' || $sub_node->Name == 'SUMMARY' || $sub_node->Name == 'TITLE') { $data[$sub_node->Name] = $this->getNodeContent($sub_node); } else { $data[$sub_node->Name] = $sub_node->GetXML(true); // firstChild->Data; // $sub_node->Data } } while ( ($sub_node =& $sub_node->NextSibling()) ); // create/update article $article_crc = kUtil::crc32($data['LINK'].$data['TITLE']); $article_id = getArrayValue($feed_articles, $article_crc); if ($article_id) { $object->Load($article_id); } else { $object->Clear(); } $object->SetDBField('CategoryId', $category_id); $object->SetDBField($title_field, $data['TITLE']); $object->SetDBField('cust_RssOriginalURL', $data['LINK']); $object->SetDBField('cust_RssArticleCRC', $article_crc); $object->SetDBField($body_field, !array_key_exists('CONTENT', $data) ? $data['SUMMARY'] : $data['CONTENT']); $expiration_time = time() + $event->getEventParam('life_time'); $object->SetDBField('EndOn_date', $expiration_time); $object->SetDBField('EndOn_time', $expiration_time); $object->SetDBField('Status', STATUS_ACTIVE); $object->SetDBField('Author', 'root'); $object->SetDBField('CreatedById', USER_ROOT); $status = $object->isLoaded() ? $object->Update() : $object->Create(); } while (($current_node =& $current_node->NextSibling())); } function getCustomColumns($prefix) { /** @var kMultiLanguage $ml_formatter */ $ml_formatter = $this->Application->recallObject('kMultiLanguage'); $custom_fields = array_flip($this->Application->getUnitConfig($prefix)->getCustomFields()); foreach ($custom_fields as $custom_name => $custom_id) { $custom_fields[$custom_name] = $ml_formatter->LangFieldName('cust_' . $custom_id); } return $custom_fields; } /** * Create missing excerpt * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->createExcerpt($event); $this->cacheItemOwner($event, 'CreatedById', 'Author'); } /** * Create missing excerpt * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->createExcerpt($event); $this->cacheItemOwner($event, 'CreatedById', 'Author'); } /** * Create excerpt if missing * * @param kEvent $event */ function createExcerpt($event) { /** @var kDBItem $object */ $object = $event->getObject(); if ( !$object->GetField('Excerpt') || $object->GetDBField('GenerateExcerpt') ) { $excerpt = strip_tags($object->GetField('Body')); $length = mb_strlen($excerpt); if ( $length > 100 ) { $excerpt = mb_substr(strip_tags($excerpt), 0, 100); if ( mb_substr($excerpt, -1) != ' ' ) { $pos = mb_strrpos($excerpt, ' '); if ( $pos ) { $excerpt = mb_substr($excerpt, 0, $pos); } } $excerpt .= '...'; } $excerpt_field = 'Excerpt'; $excerpt_formatter = $object->GetFieldOption('Excerpt', 'formatter'); if ( $excerpt_formatter == 'kMultiLanguage' ) { /** @var kMultiLanguage $ml_formatter */ $ml_formatter = $this->Application->recallObject('kMultiLanguage'); $excerpt_field = $ml_formatter->LangFieldName($excerpt_field); } $object->SetDBField($excerpt_field, $excerpt); } } /** * [HOOK] Updates category custom fields options in config * * @param kEvent $event */ function OnUpdateCategoryCustomFields($event) { $this->Application->getUnitConfig('c')->addVirtualFields(Array ( 'cust_RssSource' => Array ('type' => 'string', 'default' => ''), 'cust_RssDefaultExpiration' => Array ('type' => 'int', 'default' => ''), 'cust_RssDefaultExpirationType' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month', 29030400 => 'la_opt_year'), 'use_phrases' => 1, 'default' => 60 ), 'cust_RssExpireInterval' => Array ('type' => 'int', 'default' => ''), 'cust_RssExpireIntervalType' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month'), 'use_phrases' => 1, 'default' => 60 ), 'cust_RssDeleteExpired' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'default' => 0 ), 'cust_RssUpdateInterval' => Array ('type' => 'int', 'default' => ''), 'cust_RssUpdateIntervalType' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month'), 'use_phrases' => 1, 'default' => 60 ), 'cust_RssLastUpdated' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => ''), 'cust_RssLastExpired' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => ''), )); } /** * Sets default expiration based on module setting * * @param kEvent $event * @return void * @access protected */ protected function OnPreCreate(kEvent $event) { parent::OnPreCreate($event); if ( $event->status != kEvent::erSUCCESS ) { return ; } /** @var kDBItem $object */ $object = $event->getObject(); $archive_days = $this->Application->ConfigValue('News_Archive'); if ( $archive_days ) { $expire_date = time() + $archive_days * 3600 * 24; $object->SetDBField('EndOn_date', $expire_date); $object->SetDBField('EndOn_time', $expire_date); } } /** * [HOOK] Allows to add cloned subitem to given prefix * * @param kEvent $event * @return void * @access protected */ protected function OnCloneSubItem(kEvent $event) { parent::OnCloneSubItem($event); if ( $event->MasterEvent->Prefix == 'rev' ) { $master_config = $event->MasterEvent->getUnitConfig(); $clones = $master_config->getClones('Clones', Array ()); $sub_item_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix; $clones[$sub_item_prefix]['ConfigMapping'] = Array ( 'PerPage' => 'Perpage_NewsReviews', 'ShortListPerPage' => 'Perpage_NewsReviews_Short', 'DefaultSorting1Field' => 'News_SortReviews', 'DefaultSorting2Field' => 'News_SortReviews2', 'DefaultSorting1Dir' => 'News_SortReviewsOrder', 'DefaultSorting2Dir' => 'News_SortReviewsOrder2', 'ReviewDelayInterval' => 'News_ReviewDelay_Interval', 'ReviewDelayValue' => 'News_ReviewDelay_Value', ); $master_config->setClones($clones); } } }