getObject(); if (!$this->Application->isAdminUser) { $where_clause = '(Archived = 0) AND (StartDate < '.adodb_mktime().' OR StartDate = 0) AND (EndOn > '.adodb_mktime().' 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; } /** * [REGULAR EVENT] Deletes expired articles + update existing articles from rss feed with new data (key - article url) * * @param kEvent $event */ function OnUpdateRSSAtricles(&$event) { $category_table = $this->Application->getUnitOption('c', 'TableName'); $custom_table = $this->Application->getUnitOption('c-cdata', 'TableName'); $category_custom_fields = $this->getCustomColumns('c'); $article_custom_fields = $this->getCustomColumns($event->Prefix); // update categories which sould 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'].' <= '.adodb_mktime().') 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'].' = '.adodb_mktime().' 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'].' <= '.adodb_mktime().') AND (cd.'.$category_custom_fields['RssDeleteExpired'].' = 1)'; $categories = $this->Conn->GetCol($sql, 'ResourceId'); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table = $this->Application->getUnitOption($event->Prefix, 'TableName'); $ci_table = $this->Application->getUnitOption($event->Prefix.'-ci', 'TableName'); if ($categories) { $article_custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName'); $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 < '.adodb_mktime().' AND main_table.EndOn IS NOT NULL) AND (LENGTH(cd.'.$article_custom_fields['RssOriginalURL'].') > 0)'; $article_ids = $this->Conn->GetCol($sql); if ($article_ids) { $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $temp->DeleteItems($event->Prefix, $event->Special, $article_ids); } $sql = 'UPDATE '.$custom_table.' SET '.$category_custom_fields['RssLastExpired'].' = '.adodb_mktime().' 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) { $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table = $this->Application->getUnitOption($event->Prefix, 'TableName'); $custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName'); $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; } $curl_helper =& $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $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; } $xml_helper =& $this->Application->recallObject('kXMLHelper'); /* @var $xml_helper kXMLHelper */ $root_node =& $xml_helper->Parse($xml_data, 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; } $category_id = $event->getEventParam('category_id'); $backup_category_id = $this->Application->GetVar('m_cat_id'); $this->Application->SetVar('m_cat_id', $category_id); switch ($feed_type) { case 'rss_2.0': $this->parseRssFeed($article_node, $event); break; case 'atom': $this->parseAtomFeed($article_node, $event); break; } $this->Application->SetVar('m_cat_id', $backup_category_id); } /** * Returns ML field names for article record * * @return Array */ function _getMLFields() { $title_field = 'Title'; $body_field = 'Body'; $fields = $this->Application->getUnitOption($this->Prefix, 'Fields'); $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $title_formatter = array_key_exists('formatter', $fields[$title_field]) ? $fields[$title_field]['formatter'] : false; if ($title_formatter == 'kMultiLanguage') { $title_field = $ml_formatter->LangFieldName($title_field); } $body_formatter = array_key_exists('formatter', $fields[$body_field]) ? $fields[$body_field]['formatter'] : false; 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); $object =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true)); /* @var $object kDBItem */ list ($title_field, $body_field) = $this->_getMLFields(); do { // IMAGE is information about channel and is not useful here if ($current_node->Name != 'ITEM') continue; // collect item data $data = Array(); $sub_node =& $current_node->firstChild; /* @var $sub_node kXMLNode */ 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 = crc32($data['LINK'].$data['TITLE']); $article_id = getArrayValue($feed_articles, $article_crc); if ($article_id) { $object->Load($article_id); } else { $object->Clear(); } $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 = adodb_mktime() + $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', -1); $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 = array_key_exists('TYPE', $node->Attributes) ? $node->Attributes['TYPE'] : false; } switch ($content_type) { case 'xhtml': $data = $node->GetXML(true); break; case 'html': $data = unhtmlentities($node->firstChild->Data); // $node->Data break; default: $data = $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); $object =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true)); /* @var $object kDBItem */ list ($title_field, $body_field) = $this->_getMLFields(); do { if ($current_node->Name != 'ENTRY') continue; // collect item data $data = Array(); $sub_node =& $current_node->firstChild; /* @var $sub_node kXMLNode */ do { if ($sub_node->Name == 'LINK') { if ($sub_node->Attributes['REL'] == 'alternate') { $data[$sub_node->Name] = $sub_node->Attributes['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->firstChild->Data; // $sub_node->Data } } while ( ($sub_node =& $sub_node->NextSibling()) ); // create/update article $article_crc = crc32($data['LINK'].$data['TITLE']); $article_id = getArrayValue($feed_articles, $article_crc); if ($article_id) { $object->Load($article_id); } else { $object->Clear(); } $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 = adodb_mktime() + $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', -1); $status = $object->isLoaded() ? $object->Update() : $object->Create(); } while (($current_node =& $current_node->NextSibling())); } function getCustomColumns($prefix) { $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); $custom_fields = array_flip($this->Application->getUnitOption($prefix, 'CustomFields')); 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 */ function OnBeforeItemUpdate(&$event) { parent::OnBeforeItemUpdate($event); $this->createExcerpt($event); $this->cacheItemOwner($event, 'CreatedById', 'Author'); } /** * Create missing excerpt * * @param kEvent $event */ function OnBeforeItemCreate(&$event) { parent::OnBeforeItemCreate($event); $this->createExcerpt($event); $this->cacheItemOwner($event, 'CreatedById', 'Author'); } /** * Create excerpt if missing * * @param kEvent $event */ function createExcerpt(&$event) { $object =& $event->getObject(); if (!$object->GetField('Excerpt') || $this->Application->GetVar('generate_excerpt')) { $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 .= '...'; } $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $object->SetDBField($ml_formatter->LangFieldName('Excerpt'), $excerpt); } } /** * [HOOK] Updates category custom fields options in config * * @param kEvent $event */ function OnUpdateCategoryCustomFields(&$event) { $new_virtual_fields = Array( 'cust_RssSource' => Array('type' => 'string', 'default' => ''), 'cust_RssDefaultExpiration' => Array('type' => 'int', 'not_null' => 1, 'default' => ''), 'cust_RssDefaultExpirationType' => Array('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' => Array(60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month', 29030400 => 'la_opt_year'), 'default' => 60), 'cust_RssExpireInterval' => Array('type' => 'int', 'not_null' => 1, 'default' => ''), 'cust_RssExpireIntervalType' => Array('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' => Array(60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month'), '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', 'not_null' => 1, 'default' => ''), 'cust_RssUpdateIntervalType' => Array('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' => Array(60 => 'la_opt_min', 3600 => 'la_opt_hour', 86400 => 'la_opt_day', 2419200 => 'la_opt_month'), 'default' => 60), 'cust_RssLastUpdated' => Array('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => ''), 'cust_RssLastExpired' => Array('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => ''), ); $virtual_fields = $this->Application->getUnitOption('c', 'VirtualFields'); $virtual_fields = array_merge_recursive2($virtual_fields, $new_virtual_fields); $this->Application->setUnitOption('c', 'VirtualFields', $virtual_fields); } /** * Sets default expiration based on module setting * * @param kEvent $event */ function OnPreCreate(&$event) { parent::OnPreCreate($event); if ($event->status == erSUCCESS) { $object =& $event->getObject(); $archive_days = $this->Application->ConfigValue('News_Archive'); if ($archive_days) { $expire_date = adodb_mktime() + $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 */ function OnCloneSubItem(&$event) { parent::OnCloneSubItem($event); if ($event->MasterEvent->Prefix == 'rev') { $clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones'); $subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix; $clones[$subitem_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', ); $this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones); } } }