/** Copyright (c) 2000 Microsoft Corporation ****************************************************************************** ** Module Name: ** ** NewsHeadlines.cpp ** ** Astract: ** ** Implementation of CNewsHeadlines ** ** Author: ** ** Martha Arellano (t-alopez) 03-Oct-2000 ** ** ** Revision History: ** ** Martha Arellano (t-alopez) 05-Oct-2000 Added timestamp, frequency and link ** to xml file format ** ** Added get_Provider_Frequency() to interface ** ** 06-Oct-2000 Added Delete_Provider(nBlockIndex) to interface ** ** Added get_Provider_URL(nBlockIndex) to interface ** ** 11-Oct-2000 Added date in newsver and timestamp in provider ** ** 15-Nov-2000 Added Provider Icon, Position and Headline Expires attr ** ** 07-Dec-2000 Added Get_UpdateHeadlines to get_Stream() ** ****************************************************************************** **/ #include "stdafx.h" ///////////////////////////////////////////////////////////////////////////// // CONFIG MAP ///////////////////////////////////////////////////////////////////////////// /* */ CFG_BEGIN_FIELDS_MAP(News::Headlines::Headline) CFG_ATTRIBUTE( L"ICON" , wstring, m_strIcon ), CFG_ATTRIBUTE( L"TITLE" , wstring, m_strTitle ), CFG_ATTRIBUTE( L"LINK" , wstring, m_strLink ), CFG_ATTRIBUTE( L"DESCRIPTION" , wstring, m_strDescription ), CFG_ATTRIBUTE( L"EXPIRES" , DATE_CIM, m_dtExpires ), CFG_ATTRIBUTE( L"UPDATEHEADLINES" , bool , m_fUpdateHeadlines ), CFG_END_FIELDS_MAP() CFG_BEGIN_CHILD_MAP(News::Headlines::Headline) CFG_END_CHILD_MAP() DEFINE_CFG_OBJECT(News::Headlines::Headline, L"HEADLINE") DEFINE_CONFIG_METHODS__NOCHILD(News::Headlines::Headline) //////////////////// CFG_BEGIN_FIELDS_MAP(News::Headlines::Newsblock) CFG_ATTRIBUTE( L"PROVIDER" , wstring , m_strProvider ), CFG_ATTRIBUTE( L"LINK" , wstring , m_strLink ), CFG_ATTRIBUTE( L"ICON" , wstring , m_strIcon ), CFG_ATTRIBUTE( L"POSITION" , wstring , m_strPosition ), CFG_ATTRIBUTE( L"TIMESTAMP", DATE_CIM, m_dtTimestamp ), CFG_ATTRIBUTE( L"FREQUENCY", int , m_nFrequency ), CFG_END_FIELDS_MAP() CFG_BEGIN_CHILD_MAP(News::Headlines::Newsblock) CFG_CHILD(News::Headlines::Headline) CFG_END_CHILD_MAP() DEFINE_CFG_OBJECT(News::Headlines::Newsblock,L"NEWSBLOCK") DEFINE_CONFIG_METHODS_CREATEINSTANCE_SECTION(News::Headlines::Newsblock,tag,defSubType) if(tag == _cfg_table_tags[0]) { defSubType = &(*(m_vecHeadlines.insert( m_vecHeadlines.end() ))); return S_OK; } DEFINE_CONFIG_METHODS_SAVENODE_SECTION(News::Headlines::Newsblock,xdn) hr = MPC::Config::SaveList( m_vecHeadlines, xdn ); DEFINE_CONFIG_METHODS_END(News::Headlines::Newsblock) //////////////////// CFG_BEGIN_FIELDS_MAP(News::Headlines) CFG_ATTRIBUTE( L"TIMESTAMP" , DATE_CIM, m_dtTimestamp ), CFG_ATTRIBUTE( L"DATE" , DATE_CIM, m_dtDate ), CFG_END_FIELDS_MAP() CFG_BEGIN_CHILD_MAP(News::Headlines) CFG_CHILD(News::Headlines::Newsblock) CFG_END_CHILD_MAP() DEFINE_CFG_OBJECT(News::Headlines,L"NEWSHEADLINES") DEFINE_CONFIG_METHODS_CREATEINSTANCE_SECTION(News::Headlines,tag,defSubType) if(tag == _cfg_table_tags[0]) { defSubType = &(*(m_vecNewsblocks.insert( m_vecNewsblocks.end() ))); return S_OK; } DEFINE_CONFIG_METHODS_SAVENODE_SECTION(News::Headlines,xdn) hr = MPC::Config::SaveList( m_vecNewsblocks, xdn ); DEFINE_CONFIG_METHODS_END(News::Headlines) ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // Convert a date from CIM format to number of milliseconds since January 1, 1970. // static HRESULT local_ConvertDate( /*[in]*/ MPC::XmlUtil& xml , /*[in]*/ LPCWSTR szTag , /*[in]*/ LPCWSTR szAttrib , /*[in]*/ IXMLDOMNode* pxdnNode ) { __HCP_FUNC_ENTRY( "local_ConvertDate" ); HRESULT hr; bool fFound; MPC::wstring strValue; DATE dDate; DATE dDateBase; CComVariant v; __MPC_EXIT_IF_METHOD_FAILS(hr, xml.GetAttribute( szTag, szAttrib, strValue, fFound, pxdnNode )); if(fFound) { __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::ConvertStringToDate( strValue, dDate, /*fGMT*/false, /*fCIM*/true, 0 )); { SYSTEMTIME st; st.wYear = (WORD)1970; st.wMonth = (WORD)1; st.wDay = (WORD)1; st.wHour = (WORD)0; st.wMinute = (WORD)0; st.wSecond = (WORD)0; st.wMilliseconds = (WORD)0; ::SystemTimeToVariantTime( &st, &dDateBase ); } v = (dDate - dDateBase) * 86400.0 * 1000.0; __MPC_EXIT_IF_METHOD_FAILS(hr, v.ChangeType( VT_BSTR )); __MPC_EXIT_IF_METHOD_FAILS(hr, xml.ModifyAttribute( szTag, szAttrib, v.bstrVal, fFound, pxdnNode )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } ///////////////////////////////////////////////////////////////////////////// News::Headlines::Headline::Headline() { // MPC::wstring m_strIcon; // MPC::wstring m_strTitle; // MPC::wstring m_strLink; // MPC::wstring m_strDescription; m_dtExpires = 0; // DATE m_dtExpires; m_fUpdateHeadlines = false; // bool m_fUpdateHeadlines; } News::Headlines::Headline::Headline( /*[in]*/ const MPC::wstring& strIcon , /*[in]*/ const MPC::wstring& strTitle , /*[in]*/ const MPC::wstring& strLink , /*[in]*/ const MPC::wstring& strDescription , /*[in]*/ DATE dtExpires , /*[in]*/ bool fUpdateHeadlines) { m_strIcon = strIcon; // MPC::wstring m_strIcon; m_strTitle = strTitle; // MPC::wstring m_strTitle; m_strLink = strLink; // MPC::wstring m_strLink; m_strDescription = strDescription; // MPC::wstring m_strDescription; m_dtExpires = dtExpires; // DATE m_dtExpires; m_fUpdateHeadlines = fUpdateHeadlines; // bool m_fUpdateHeadlines; } //////////////////// News::Headlines::Newsblock::Newsblock() { // MPC::wstring m_strProvider; // MPC::wstring m_strLink; // MPC::wstring m_strIcon; // MPC::wstring m_strPosition; m_dtTimestamp = 0; // DATE m_dtTimestamp; m_nFrequency = 0; // int m_nFrequency; // // HeadlineList m_vecHeadlines; } // // Routine Description: // // Determines if its time to update or not, the newsblock // // Arguments: // // nBlockIndex Newsblock index // // Return Value: // // returns TRUE if its time to update the newsblock // // bool News::Headlines::Newsblock::TimeToUpdate() { if(m_nFrequency) { DATE dtNow = MPC::GetLocalTime() - m_nFrequency; // then we check if its time to download newsver.xml if(dtNow >= m_dtTimestamp) return true; } return false; } HRESULT News::Headlines::Newsblock::Copy( /*[in]*/ const Newsblock& block , /*[in]*/ const MPC::wstring& strLangSKU , /*[in]*/ int nProvID ) { __HCP_FUNC_ENTRY( "News::Headlines::Newsblock::Copy" ); HRESULT hr; // // copy the member variables *this = block; // // set the time we are modifying the Newsblock m_dtTimestamp = MPC::GetLocalTime(); // check when we have incomplete information and we won't download the icon // when there is no Icon // when there is no Provider's name // when the Icon URL is missing a '/' if(!m_strIcon .empty() && !m_strProvider.empty() ) { MPC::wstring szEnd; GetFileName( m_strIcon, szEnd); if(!szEnd.empty()) { MPC::wstring strPath = HC_HELPSET_ROOT HC_HELPSET_SUB_SYSTEM L"\\News\\"; MPC::wstring strOthers; MPC::wstring strImgPath; WCHAR wzProvID[64]; bool fUseIcon = false; __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SubstituteEnvVariables( strPath )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MakeDir ( strPath )); // add Lang and SKU strOthers = strLangSKU + L'_'; strOthers += _itow( nProvID, wzProvID, 10 ); // add Provider's ID strOthers += L'_'; strOthers += szEnd; // add the icon's name // form the path to this image file strImgPath = strPath; strImgPath += strOthers; // we check if we have that file already if(MPC::FileSystemObject::IsFile( strImgPath.c_str() )) { fUseIcon = true; } else { CComPtr streamIn; // then, we download the new image if(SUCCEEDED(News::LoadFileFromServer( m_strIcon.c_str(), streamIn ))) { CComPtr streamImg; if(SUCCEEDED(MPC::CreateInstance ( &streamImg )) && SUCCEEDED(streamImg->InitForWrite( strImgPath.c_str() )) ) { if(SUCCEEDED(MPC::BaseStream::TransferData( streamIn, streamImg ))) { fUseIcon = true; } } } } if(fUseIcon) { m_strIcon = L"hcp://system/news/"; m_strIcon += strOthers; } else { m_strIcon .erase(); m_strPosition.erase(); } } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } void News::Headlines::Newsblock::GetFileName( MPC::wstring strURL, MPC::wstring& strFileName ) { MPC::wstring::size_type pos; if(!strURL.empty()) { if((pos = strURL.find_last_of( '/' )) == strURL.length() - 1) { strURL.resize( strURL.length() - 1 ); pos = strURL.find_last_of( '/' ); } if(pos != MPC::wstring::npos) { strFileName.assign( strURL, pos, strURL.length() - pos ); // Go thro the string and delete all invalid characters pos = 0; while(!strFileName.empty() && pos < strFileName.length()) { if((strFileName[pos] == '\\') || (strFileName[pos] == '/') || (strFileName[pos] == ':') || (strFileName[pos] == '*') || (strFileName[pos] == '?') || (strFileName[pos] == '"') || (strFileName[pos] == '<') || (strFileName[pos] == '>') || (strFileName[pos] == '|')) { strFileName.erase( pos, 1 ); } else { pos++; } } } } } //////////////////////////////////////////////////////////////////////////////// News::Headlines::Headlines() { m_dtTimestamp = 0; // DATE m_dtTimestamp; m_dtDate = 0; // DATE m_dtDate; // // NewsblockList m_vecNewsblocks; } // // Routine Description: // // Clears the News::Headlines Class' variables and lists, so it can be loaded againg // // Arguments: // // None // // HRESULT News::Headlines::Clear() { __HCP_FUNC_ENTRY( "News::Headlines::Clear" ); HRESULT hr; m_dtTimestamp = 0; m_dtDate = 0; m_vecNewsblocks.clear(); hr = S_OK; __HCP_FUNC_EXIT(hr); } // // Routine Description: // // Loads the specified file and validates it (from the local disk or from the server) // // Arguments: // // strPath the path for the news headlines file (or newsblock) // // HRESULT News::Headlines::Load( /*[in]*/ const MPC::wstring& strPath ) { __HCP_FUNC_ENTRY( "News::Headlines::Load" ); HRESULT hr; CComPtr stream; // we load the file __MPC_EXIT_IF_METHOD_FAILS(hr, News::LoadXMLFile( strPath.c_str(), stream )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::Config::LoadStream( this, stream )); //validate file // if(m_vecNewsblocks.size() == 0) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INVALID_DATA); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } // // Routine Description: // // The newsheadlines file is saved in the local user disk // // the timestamp is updated // // Arguments: // // strPath the path and name of the newsheadlines file // // HRESULT News::Headlines::Save( /*[in]*/ const MPC::wstring& strPath ) { __HCP_FUNC_ENTRY( "News::Headlines::Save" ); HRESULT hr; // the date is updated every time it is saved m_dtDate = MPC::GetLocalTime(); // we save the file __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::Config::SaveFile( this, strPath.c_str() )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } // // Routine Description: // // Reloads the newsheadlines file and then returns it as a stream // // We check the Expires attribute in each headline, if it has expired then its deleted // if a provider loses all its headlines, then the provider (newsblock) is deleted too // // If all Newsblocks are deleted, we return ERROR_INVALID_DATA, to display the offline message // // We save the changes in the News Headlines file // // Arguments: // // strPath path for the newsheadlines file // // Return Value: // // pVal IStream with the newsheadlines xml content // // HRESULT News::Headlines:: get_Stream( /*[in ]*/ long lLCID , /*[in ]*/ const MPC::wstring& strSKU , /*[in ]*/ const MPC::wstring& strPath , /*[out]*/ IUnknown* *pVal ) { __HCP_FUNC_ENTRY( "News::Headlines::get_Stream" ); HRESULT hr; MPC::XmlUtil xml; UpdateHeadlines uhUpdate; CComPtr stream; bool fModified = false; DATE dtNow = MPC::GetLocalTime(); NewsblockIter itNewsblock; // we clear the object Clear(); // we load the News Headlines file __MPC_EXIT_IF_METHOD_FAILS(hr, News::LoadXMLFile( strPath.c_str(), stream )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::Config::LoadStream( this, stream )); // ****** delete expired headlines // for each newsblock itNewsblock = m_vecNewsblocks.begin(); while(itNewsblock != m_vecNewsblocks.end()) { Newsblock& nb = *itNewsblock; HeadlineIter itHeadline = nb.m_vecHeadlines.begin(); while(itHeadline != nb.m_vecHeadlines.end()) { // check each headline if it has expired if(itHeadline->m_dtExpires < dtNow) { // if expired, deleted nb.m_vecHeadlines.erase( itHeadline ); fModified = true; } else { // we go to the next headline itHeadline++; } } // if the Newsblock has no headlines valid if(itNewsblock->m_vecHeadlines.empty()) { // we delete it m_vecNewsblocks.erase( itNewsblock ); } else { // we go to the next Newsblock itNewsblock++; } } if(fModified) { // if we deleted headlines or newsblocks we save the News Headlines file __MPC_EXIT_IF_METHOD_FAILS(hr, Save( strPath )); } // if there aren't Newsblocks left, we return an error if(m_vecNewsblocks.empty()) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INVALID_DATA); } __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::Config::SaveXmlUtil( this, xml )); __MPC_EXIT_IF_METHOD_FAILS(hr, local_ConvertDate( xml, NULL, L"TIMESTAMP", NULL )); __MPC_EXIT_IF_METHOD_FAILS(hr, local_ConvertDate( xml, NULL, L"DATE" , NULL )); __MPC_EXIT_IF_METHOD_FAILS(hr, xml.SaveAsStream( pVal )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } // // Routine Description: // // Checks to see if there atleast a single Newsblock which has an icon // // Arguments: // // None // // Return Value: // // true or false // bool News::Headlines::CheckIfImagesExist() { // Note that this function does NOT check to see if a headline has an icon. This is because headline icons are not // being processed (even though the ICON attribute exists in the HEADLINE tag). NewsblockIter itNewsblock; itNewsblock = m_vecNewsblocks.begin(); while(itNewsblock != m_vecNewsblocks.end()) { if(!itNewsblock->m_strIcon.empty()) { return true; } else { itNewsblock++; } } return false; } // // Routine Description: // // Sets the News Headlines timestamp to the current time // This method should be called when all the newsblocks are retrieved // // Arguments: // // None // // Return Value: // // None // // void News::Headlines::set_Timestamp() { m_dtTimestamp = MPC::GetLocalTime(); } News::Headlines::Newsblock* News::Headlines::get_Newsblock( /*[in]*/ size_t nBlockIndex ) { if(nBlockIndex >= m_vecNewsblocks.size()) return NULL; return &(m_vecNewsblocks[ nBlockIndex ]); } //////////////////////////////////////// // // Routine Description: // // From the provided newsblock this method gets the first two headlines // and adds it to the first newsblock // // Arguments: // // None // // Return Value: // // None // // HRESULT News::Headlines::AddHomepageHeadlines( /*[in]*/ const Headlines::Newsblock& block ) { __HCP_FUNC_ENTRY( "News::Headlines::AddHomepageHeadlines" ); HRESULT hr; size_t nIndex = 0; NewsblockIter itNewsblock; HeadlineIter itHeadline; // Add 2 headlines to the first newsblock itNewsblock = m_vecNewsblocks.begin(); while ( itNewsblock && ( ++nIndex <= NUMBER_OF_OEM_HEADLINES ) ) { // If a headline already exists delete it before adding a new one if ( nIndex < itNewsblock->m_vecHeadlines.size() ) { itHeadline = itNewsblock->m_vecHeadlines.begin(); std::advance( itHeadline, nIndex ); // Delete the existing headline before adding it itNewsblock->m_vecHeadlines.erase( itHeadline ); // Add the headline itNewsblock->m_vecHeadlines.insert( itHeadline, block.m_vecHeadlines[nIndex - 1] ); } else { // Insert the headline to the end of the list itNewsblock->m_vecHeadlines.insert( itNewsblock->m_vecHeadlines.end(), block.m_vecHeadlines[nIndex - 1] ); } } hr = S_OK; return S_OK; } //////////////////////////////////////// HRESULT News::Headlines::AddNewsblock( /*[in]*/ const Headlines::Newsblock& block , /*[in]*/ const MPC::wstring& strLangSKU ) { __HCP_FUNC_ENTRY( "News::Headlines::AddNewsblock" ); HRESULT hr; NewsblockIter itNewsblock; itNewsblock = m_vecNewsblocks.insert( m_vecNewsblocks.end() ); __MPC_EXIT_IF_METHOD_FAILS(hr, itNewsblock->Copy( block, strLangSKU, m_vecNewsblocks.size()-1 )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); }