iTop Newsroom implementation - painfully merged manually...

This commit is contained in:
Denis Flaven
2018-11-21 19:36:51 +01:00
parent a659de9c9b
commit 5ccfa24b27
20 changed files with 1118 additions and 95 deletions

View File

@@ -16,6 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
require_once(APPROOT.'application/newsroomprovider.class.inc.php');
/**
* Management of application plugins
*

View File

@@ -105,6 +105,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_script('../js/jquery.magnific-popup.min.js');
$this->add_linked_script('../js/breadcrumb.js');
$this->add_linked_script('../js/moment-with-locales.min.js');
$this->add_linked_script('../js/showdown.min.js');
$this->add_linked_script('../js/newsroom_menu.js');
$this->add_dict_entry('UI:FillAllMandatoryFields');
@@ -257,6 +259,10 @@ EOF;
}
$this->add_script(
<<< EOF
function GetUserLanguage()
{
return $sJSLangShort;
}
function PrepareWidgets()
{
// note: each action implemented here must be idempotent,
@@ -794,6 +800,69 @@ EOF
ApplicationMenu::DisplayMenu($this, $oAppContext->GetAsHash());
}
/**
* Handles the "newsroom" menu at the top-right of the screen
*/
protected function InitNewsroom()
{
$sNewsroomInitialImage = '';
if (MetaModel::GetConfig()->Get('newsroom_enabled') !== false)
{
$oUser = UserRights::GetUserObject();
/**
* @var iNewsroomProvider[] $aProviders
*/
$aProviders = MetaModel::EnumPlugins('iNewsroomProvider');
$aProviderParams = array();
foreach($aProviders as $oProvider)
{
$oProvider->SetConfig(MetaModel::GetConfig());
$bProviderEnabled = appUserPreferences::GetPref('newsroom_provider_'.get_class($oProvider), true);
if ($bProviderEnabled && $oProvider->IsApplicable($oUser))
{
$aProviderParams[] = array(
'label' => $oProvider->GetLabel(),
'fetch_url' => $oProvider->GetFetchURL(),
'view_all_url' => $oProvider->GetViewAllURL(),
'mark_all_as_read_url' => $oProvider->GetMarkAllAsReadURL(),
'ttl' => $oProvider->GetTTL(),
);
}
}
if (count($aProviderParams) > 0)
{
$sImageUrl= '../images/newsroom_menu.png';
$sPlaceholderImageUrl= '../images/news-32x32.png';
$aParams = array(
'image_url' => $sImageUrl,
'placeholder_image_url' => $sPlaceholderImageUrl,
'cache_uuid' => 'itop-newsroom-'.md5(APPROOT),
'providers' => $aProviderParams,
'display_limit' => (int)appUserPreferences::GetPref('newsroom_display_size', 7),
'labels' => array(
'no_message' => Dict::S('UI:Newsroom:NoNewMessage'),
'mark_all_as_read' => Dict::S('UI:Newsroom:MarkAllAsRead'),
'view_all' => Dict::S('UI:Newsroom:ViewAllMessages'),
),
);
$sParams = json_encode($aParams);
$this->add_ready_script(
<<<EOF
$('#top-left-newsroom-cell').newsroom_menu($sParams);
EOF
);
$sNewsroomInitialImage = '<img style="opacity:0.4" src="../images/newsroom_menu.png">';
}
else
{
// No newsroom menu at all
}
}
// else no newsroom menu
return $sNewsroomInitialImage;
}
/**
* Outputs (via some echo) the complete HTML page by assembling all its elements
*/
@@ -891,6 +960,8 @@ EOF
);
}
$sNewsRoomInitialImage = $this->InitNewsroom();
$this->outputCollapsibleSectionInit();
if ($this->GetOutputFormat() == 'html')
@@ -1273,6 +1344,7 @@ EOF;
$sHtml .= ' <table id="top-left-buttons-area"><tr>';
$sHtml .= ' <td id="top-left-global-search-cell"><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sDefaultPlaceHolder.'" value="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"><input type="hidden" name="operation" value="full_text"/></div></div></td>';
$sHtml .= ' <td id="top-left-help-cell"><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank"><img title="'.Dict::S('UI:Help').'" src="../images/help.png?t='.utils::GetCacheBusterTimestamp().'"/></td>';
$sHtml .= ' <td id="top-left-newsroom-cell">'.$sNewsRoomInitialImage.'</td>';
$sHtml .= ' <td id="top-left-logoff-cell">'.self::FilterXSS($sLogOffMenu).'</td>';
$sHtml .= ' </tr></table></form></div>';
$sHtml .= ' </td>';

View File

@@ -0,0 +1,143 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* A provider for messages to be displayed in the iTop Newsroom
*/
interface iNewsroomProvider
{
/**
* Inject the current configuration in the provider
* @param Config $oConfig
* @return void
*/
public function SetConfig(Config $oConfig);
/**
* Tells if this provider is enabled for the given user
* @param User $oUser The user for who to check if the provider is applicable.
* return bool
*/
public function IsApplicable(User $oUser = null);
/**
* The human readable (localized) label for this provider
* @return string
*/
public function GetLabel();
/**
* The URL to query (from the browser, using jsonp) to fetch all unread messages
* @return string
*/
public function GetFetchURL();
/**
* The URL to navigate to in order to display all messages
* @return string
*/
public function GetViewAllURL();
/**
* The URL to query(from the browser, using jsonp) to mark all unread messages as read
* @return string
*/
public function GetMarkAllAsReadURL();
/**
* Return the URL to configure the preferences for this provider or null is there is nothing to configure
* @return string|null
*/
public function GetPreferencesUrl();
/**
* The duration between to refreshes of the cache (in seconds)
* @return int
*/
public function GetTTL();
}
/**
* Basic implementation of a Newsroom provider, to be overloaded by your own provider implementation
*
*/
abstract class NewsroomProviderBase implements iNewsroomProvider
{
/**
* The current configuration parameters
* @var Config
*/
protected $oConfig;
public function __construct()
{
$this->oConfig = null;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::SetConfig()
*/
public function SetConfig(Config $oConfig)
{
$this->oConfig = $oConfig;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetPreferencesUrl()
*/
public function GetPreferencesUrl()
{
return null; // No preferences
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetLabel()
*/
public abstract function GetLabel();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetFetchURL()
*/
public abstract function GetFetchURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetMarkAllURL()
*/
public abstract function GetMarkAllAsReadURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetViewAllURL()
*/
public abstract function GetViewAllURL();
public function IsApplicable(User $oUser = null)
{
return false;
}
public function GetTTL()
{
return 10*60; // Refresh every 10 minutes
}
}

View File

@@ -1142,6 +1142,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'newsroom_enabled' => array(
'type' => 'bool',
'description' => 'Whether or not the whole newsroom is enabled',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)

View File

@@ -2668,7 +2668,7 @@ abstract class MetaModel
// Build the list of available extensions
//
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface');
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();

View File

@@ -552,6 +552,9 @@ div.actions_menu > ul > li {
padding: 0;
height: 25px;
}
.itop_popup > ul > li > ul, #logOffBtn > ul > li > ul {
box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.5);
}
.itop_popup li a, #logOffBtn li a {
display: block;
padding: 5px 12px;
@@ -2961,56 +2964,137 @@ table.listResults .originColor {
padding-top: 0.3em;
border: none;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 41px;
height: 23px;
vertical-align: baseline;
#newsroom_menu li span {
display: block;
padding: 5px 12px;
text-decoration: none;
white-space: nowrap;
}
/* Hide default HTML checkbox */
.switch input {
display: none;
}
/* The slider */
.slider {
position: absolute;
#newsroom_menu li li p {
text-align: left;
margin: 2px;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
background-color: transparent;
}
.slider:before {
position: absolute;
content: "";
height: 17px;
width: 17px;
#newsroom_menu > ul > li > ul {
margin-top: 8px;
}
#newsroom_menu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
}
#newsroom_menu > ul > li > ul > li:hover, #newsroom_menu > ul > li > ul > li:hover h1, #newsroom_menu > ul > li > ul > li:hover h2, #newsroom_menu > ul > li > ul > li:hover h3 {
background: #ea7d1e;
color: #fff;
}
#newsroom_menu li li:last-of-type {
border-bottom: 0;
}
#newsroom_menu li {
list-style: none;
cursor: pointer;
color: #000;
}
#newsroom_menu li ul {
width: 300px;
}
#top-left-newsroom-cell {
padding-right: 11px !important;
}
#newsroom_menu_icon {
position: relative;
top: 8px;
}
#newsroom_menu_counter_container {
position: relative;
top: -26px;
left: 4px;
bottom: 3px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: #ea7d1e;
#newsroom_menu_counter {
display: inline-block;
padding: 2px;
border-radius: 8px;
text-align: center;
width: 12px;
box-shadow: 1px 1px 2px 0px #777;
font-size: 8pt;
background-color: #1c94c4;
color: #fff;
}
input:focus + .slider {
box-shadow: 0 0 1px #ea7d1e;
.newsroom_extra_messages_counter {
display: inline-block !important;
padding: 2px !important;
border-radius: 8px;
text-align: center;
font-size: 8pt;
min-width: 12px;
background-color: #808080;
color: #fff;
float: right;
margin-right: 5px;
}
input:checked + .slider:before {
-webkit-transform: translateX(16px);
-ms-transform: translateX(16px);
transform: translateX(16px);
.newsroom_menu_item > div > img {
float: left;
width: 32px;
max-height: 32px;
margin-top: 0;
margin-bottom: 0;
margin-left: 5px;
margin-right: 5px;
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
#newsroom_menu_dismiss_all, #newsroom_menu_show_all {
text-align: center !important;
}
.slider.round:before {
border-radius: 50%;
#newsroom_show_all_submenu {
text-align: center;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#newsroom_show_all_submenu > ul {
padding-top: 10px;
padding-bottom: 10px;
}
#newsroom_show_all_submenu > ul > li > ul {
margin-top: 8px;
}
#newsroom_show_all_submenu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-left: 5px;
padding-top: 4px;
padding-bottom: 6px;
text-align: left;
}
#newsroom_show_all_submenu > ul > li > ul > li:hover {
background: #ea7d1e;
color: #fff;
}
#newsroom_show_all_submenu li ul {
width: 200px;
color: #000;
}
.no-padding {
padding: 0 !important;
}
.newsroom_menu_item_date {
padding: 0 !important;
margin-bottom: -10px;
font-size: 8pt;
text-align: right;
margin-top: -2px;
margin-right: 5px;
color: #fff;
}
#newsroom_display_size {
height: 1em;
width: 3em;
}

View File

@@ -628,7 +628,9 @@ div.actions_menu > ul > li {
padding: 0;
height: 25px;
}
.itop_popup > ul > li > ul, #logOffBtn > ul > li > ul {
box-shadow: 3px 3px 5px 0px rgba(0,0,0,0.5);
}
.itop_popup li a, #logOffBtn li a {
display: block;
padding: 5px 12px;
@@ -3496,4 +3498,139 @@ input:checked + .slider:before {
.slider.round:before {
border-radius: 50%;
}
}
///////////////////////////
// Newsroom menu
#newsroom_menu li span {
display: block;
padding: 5px 12px;
text-decoration: none;
white-space: nowrap;
}
#newsroom_menu li li p {
text-align: left;
margin: 2px;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
background-color: transparent;
}
#newsroom_menu > ul > li > ul {
margin-top: 8px;
}
#newsroom_menu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
}
#newsroom_menu > ul > li > ul > li:hover, #newsroom_menu > ul > li > ul > li:hover h1, #newsroom_menu > ul > li > ul > li:hover h2, #newsroom_menu > ul > li > ul > li:hover h3 {
background: $popup-menu-highlight-color;
color: $popup-menu-text-higlight-color;
}
#newsroom_menu li li:last-of-type {
border-bottom: 0;
}
#newsroom_menu li {
list-style: none;
cursor: pointer;
color: $popup-menu-text-color;
}
#newsroom_menu li ul {
width: 300px;
}
#top-left-newsroom-cell {
padding-right: 11px !important;
}
#newsroom_menu_icon {
position:relative;
top:8px;
}
#newsroom_menu_counter_container {
position:relative;
top:-26px;
left:4px;
}
#newsroom_menu_counter {
display:inline-block;
padding:2px;
border-radius:8px;
text-align:center;
width: 12px;
box-shadow: 1px 1px 2px 0px $gray;
font-size: 8pt;
background-color:$complement-color;
color: $white;
}
.newsroom_extra_messages_counter {
display:inline-block !important;
padding:2px !important;
border-radius:8px;
text-align:center;
font-size: 8pt;
min-width: 12px;
background-color:$gray-light;
color: $white;
float: right;
margin-right: 5px;
}
.newsroom_menu_item > div > img {
float:left;
width:32px;
max-height:32px;
margin-top: 0;
margin-bottom: 0;
margin-left:5px;
margin-right: 5px;
}
#newsroom_menu_dismiss_all, #newsroom_menu_show_all {
text-align: center !important;
}
#newsroom_show_all_submenu {
text-align: center;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#newsroom_show_all_submenu > ul {
padding-top: 10px;
padding-bottom: 10px;
}
#newsroom_show_all_submenu > ul > li > ul {
margin-top: 8px;
}
#newsroom_show_all_submenu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-left: 5px;
padding-top: 4px;
padding-bottom: 6px;
text-align: left;
}
#newsroom_show_all_submenu > ul > li > ul > li:hover {
background: $popup-menu-highlight-color;
color: $popup-menu-text-higlight-color;
}
#newsroom_show_all_submenu li ul {
width: 200px;
color:#000;
}
.no-padding {
padding: 0 !important;
}
.newsroom_menu_item_date {
padding: 0 !important;
margin-bottom: -10px;
font-size: 8pt;
text-align: right;
margin-top: -2px;
margin-right: 5px;
color: $white;
}
#newsroom_display_size {
height: 1em;
width: 3em;
}

View File

@@ -1,46 +1,3 @@
#hub_popup_menu li span {
display: block;
padding: 5px 12px;
text-decoration: none;
nowidth: 70px;
white-space: nowrap;
}
#hub_popup_menu li li p {
text-align: left;
margin: 2px;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
background-color: transparent;
color: #000;
}
#hub_popup_menu > ul > li > ul {
margin-top: 8px;
}
#hub_popup_menu > ul > li > ul > li {
display: block;
border-bottom: 1px #ddd solid;
padding-top: 10px;
padding-bottom: 10px;
}
#hub_popup_menu > ul > li > ul > li:hover {
color: #fff;
background-color: #E87C1E;
}
#hub_popup_menu li li:last-of-type {
border-bottom: 0;
}
#hub_popup_menu li {
list-style: none;
cursor: pointer;
}
#hub_popup_menu li ul {
width: 340px;
}
#top-left-hub-cell {
padding-right: 8px !important;
}
#hub_launch_container {
padding:10px;
overflow: visible;
@@ -209,7 +166,7 @@
#hub_launch_content {
width: 100%;
}
button {
#hub_launch_content button {
cursor: pointer;
font-weight: bold;
box-shadow: 0px 0px 0px 1px transparent inset, 0px 0em 0px 0px rgba(34, 36, 38, 0.15) inset;
@@ -219,27 +176,27 @@ button {
vertical-align: middle;
padding: 0;
}
button.positive {
#hub_launch_content button.positive {
background-color: rgb(234, 125, 30);
color: #FFFFFF;
}
button:disabled, button[disabled] {
#hub_launch_content button:disabled, button[disabled] {
background-color: #828487!important;
cursor: progress !important;
}
button > span {
#hub_launch_content button > span {
display: inline-block;
padding: 0.8em;
padding-left: 1.3em;
padding-right: 1.3em;
}
button:hover {
#hub_launch_content button:hover {
background-color: #D0D0D0;
}
button.positive:hover {
#hub_launch_content button.positive:hover {
background-color: rgb(221, 113, 27);
}
button > img {
#hub_launch_content button > img {
width: 16px;
height: 16px;
vertical-align: middle;
@@ -249,4 +206,4 @@ button > img {
.horiz-spacer {
display: inline-block;
width: 1.5em;
}
}

View File

@@ -17,7 +17,9 @@
<url>https://www.itophub.io</url>
<route_landing>/my-instances/landing-from-remote</route_landing>
<route_landing_stateless>/stateless-remote-itop/landing-from-remote-stateless</route_landing_stateless>
<route_notifications>/api/notifications</route_notifications>
<route_fetch_unread_messages>/api/notifications</route_fetch_unread_messages>
<route_mark_all_messages_as_read>/api/notifications/mark_all_as_read</route_mark_all_messages_as_read>
<route_view_all_messages>/api/notifications/view_all_messages</route_view_all_messages>
<setup_url>../pages/exec.php?exec_module=itop-hub-connector&amp;exec_page=launch.php&amp;target=inform_after_setup</setup_url>
</parameters>
</module_parameters>

View File

@@ -0,0 +1,74 @@
<?php
require_once(APPROOT.'application/newsroomprovider.class.inc.php');
class HubNewsroomProvider extends NewsroomProviderBase
{
/**
* {@inheritDoc}
* @see NewsroomProviderBase::GetTTL()
*/
public function GetTTL()
{
// TODO Auto-generated method stub
return 15*60; // Update every 15 minutes
}
/**
* {@inheritDoc}
* @see NewsroomProviderBase::IsApplicable()
*/
public function IsApplicable(User $oUser = null)
{
if ($oUser !== null)
{
return UserRights::IsAdministrator($oUser);
}
else
{
return false;
}
}
/**
* {@inheritDoc}
* @see NewsroomProviderBase::GetLabel()
*/
public function GetLabel()
{
return 'iTop Hub'; // No need to translate...
}
public function GetMarkAllAsReadURL()
{
return $this->MakeURL('route_mark_all_messages_as_read');
}
public function GetFetchURL()
{
return $this->MakeURL('route_fetch_unread_messages');
}
public function GetViewAllURL()
{
return $this->MakeURL('route_view_all_messages');
}
/**
* {@inheritDoc}
* @see NewsroomProviderBase::GetPreferencesUrl()
*/
public function GetPreferencesUrl()
{
return null;
}
private function MakeURL($sRouteCode)
{
$sBaseUrl = $this->oConfig->GetModuleSetting('itop-hub-connector', 'url').MetaModel::GetModuleSetting('itop-hub-connector', $sRouteCode);
$sParameters = 'uuid[bdd]='.urlencode((string) trim(DBProperty::GetProperty('database_uuid', ''), '{}'));
$sParameters .= '&uuid[file]='.urlencode((string) trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n"));
$sParameters .= '&uuid[user]='.urlencode(UserRights::GetUserId());
return $sBaseUrl.'?'.$sParameters;
}
}

View File

@@ -24,7 +24,8 @@ SetupWebPage::AddModule(
//
'datamodel' => array(
'menus.php',
'model.itop-hub-connector.php'
'hubnewsroomprovider.class.inc.php',
'model.itop-hub-connector.php'
),
'webservice' => array(

View File

@@ -1529,3 +1529,17 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Expression:Verb:NOW' => 'jetzt',
'Expression:Verb:ISNULL' => ': nicht definiert',
));
//
// iTop Newsroom menu
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Newsroom:NoNewMessage' => 'Keine neue Nachricht',
'UI:Newsroom:MarkAllAsRead' => 'Alle Nachrichten als gelesen markieren',
'UI:Newsroom:ViewAllMessages' => 'Alle Nachrichten anzeigen',
'UI:Newsroom:Preferences' => 'Newsroom-Einstellungen',
'UI:Newsroom:ConfigurationLink' => 'Konfiguration',
'UI:Newsroom:ResetCache' => 'Cache zurücksetzen',
'UI:Newsroom:DisplayMessagesFor_Provider' => 'Nachrichten von %1$s anzeigen',
'UI:Newsroom:DisplayAtMost_X_Messages' => 'Zeigen Sie höchstens %1$s Beiträge im Menü (%2$s) an.',
));

View File

@@ -1550,3 +1550,17 @@ Dict::Add('EN US', 'English', 'English', array(
'Expression:Verb:NOW' => 'now',
'Expression:Verb:ISNULL' => ': undefined',
));
//
// iTop Newsroom menu
//
Dict::Add('EN US', 'English', 'English', array(
'UI:Newsroom:NoNewMessage' => 'No new message',
'UI:Newsroom:MarkAllAsRead' => 'Mark all as messages read',
'UI:Newsroom:ViewAllMessages' => 'View all messages',
'UI:Newsroom:Preferences' => 'Newsroom preferences',
'UI:Newsroom:ConfigurationLink' => 'Configuration',
'UI:Newsroom:ResetCache' => 'Reset cache',
'UI:Newsroom:DisplayMessagesFor_Provider' => 'Display messages from %1$s',
'UI:Newsroom:DisplayAtMost_X_Messages' => 'Display up to %1$s messages in the %2$s menu.',
));

View File

@@ -1530,3 +1530,17 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Expression:Verb:NOW' => 'maintenant',
'Expression:Verb:ISNULL' => ' : non défini',
));
//
// iTop Newsroom menu
//
Dict::Add('FR FR', 'French', 'Français', array(
'UI:Newsroom:NoNewMessage' => 'Aucun nouveau message',
'UI:Newsroom:MarkAllAsRead' => 'Marquer tous les messages comme lus',
'UI:Newsroom:ViewAllMessages' => 'Voir tous les messages',
'UI:Newsroom:Preferences' => 'Préférences du centre d\'information',
'UI:Newsroom:ConfigurationLink' => 'Configuration',
'UI:Newsroom:ResetCache' => 'Ràz du cache',
'UI:Newsroom:DisplayMessagesFor_Provider' => 'Afficher les messages de %1$s',
'UI:Newsroom:DisplayAtMost_X_Messages' => 'Afficher au plus %1$s messages dans le menu %2$s.',
));

View File

@@ -1549,3 +1549,17 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Expression:Verb:NOW' => 'nu',
'Expression:Verb:ISNULL' => ': ongedefinieerd (NULL)',
));
//
// iTop Newsroom menu
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'UI:Newsroom:NoNewMessage' => 'Geen nieuw bericht',
'UI:Newsroom:MarkAllAsRead' => 'Markeer alle berichten als gelezen',
'UI:Newsroom:ViewAllMessages' => 'Bekijk alle berichten',
'UI:Newsroom:Preferences' => 'Newsroom preferences',
'UI:Newsroom:ConfigurationLink' => 'Configuratie',
'UI:Newsroom:ResetCache' => 'Reset cache',
'UI:Newsroom:DisplayMessagesFor_Provider' => 'Bekijk berichten van %1$s',
'UI:Newsroom:DisplayAtMost_X_Messages' => 'Toon maximaal %1$s berichten in het %2$s menu.',
));

View File

@@ -1521,3 +1521,17 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Expression:Verb:NOW' => 'now',
'Expression:Verb:ISNULL' => ': undefined~~',
));
//
// iTop Newsroom menu
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'UI:Newsroom:NoNewMessage' => 'Нет новых сообщений',
'UI:Newsroom:MarkAllAsRead' => 'Отметить все как прочитанные сообщения',
'UI:Newsroom:ViewAllMessages' => 'Посмотреть все сообщения',
'UI:Newsroom:Preferences' => 'Настройки новостей',
'UI:Newsroom:ConfigurationLink' => 'конфигурация',
'UI:Newsroom:ResetCache' => 'Сбросить кеш',
'UI:Newsroom:DisplayMessagesFor_Provider' => 'Показать сообщения от %1$s',
'UI:Newsroom:DisplayAtMost_X_Messages' => 'Отобразите не более %1$s сообщений в меню %2$s.',
));

BIN
images/newsroom_menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

350
js/newsroom_menu.js Normal file
View File

@@ -0,0 +1,350 @@
$(function()
{
// the widget definition, where "itop" is the namespace,
// "newsroom_menu" the widget name
$.widget( "itop.newsroom_menu",
{
// default options
options:
{
image_url: '',
cache_uuid: '',
display_limit: 7,
placeholder_image_url: '../images/transparent_32_32.png',
providers: [],
labels: {
'no_message': 'No new message',
'mark_all_as_read': 'Mark all as read',
'view_all': 'View all messages'
}
},
// the constructor
_create: function()
{
var me = this;
this.aMessageByProvider = [];
this.element
.addClass('itop-newsroom_menu');
this._load();
},
// called when created, and later when changing options
_refresh: function()
{
},
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function()
{
this.element
.removeClass('itop-newsroom_menu');
},
// _setOptions is called with a hash of all options that are changing
_setOptions: function()
{
this._superApply(arguments);
},
// _setOption is called for each individual option that is changing
_setOption: function(key, value)
{
if (this.options[key] != value)
{
// If any option changes, clear the cache BEFORE applying the new settings
this._clearCache();
}
this._superApply(arguments);
},
_load: function()
{
var me = this;
setTimeout(function() { me._getAllMessages(); }, 1000);
},
_getAllMessages: function()
{
this.aMessageByProvider = [];
this._getMessages(0); // start at the first provider (index == 0)
},
_getMessages: function(idxProvider)
{
var sKey = this._makeCacheKey(idxProvider);
var oJSONData = this._getCachedData(idxProvider);
if (oJSONData != null)
{
this._onMessagesFetched(idxProvider, oJSONData);
}
else
{
this._fetchMessages(idxProvider); // Asynchronous
}
},
_fetchMessages: function(idxProvider)
{
var sUrl = this.options.providers[idxProvider].fetch_url;
var me = this;
var idx = idxProvider;
$.ajax({ type: "GET",
url: sUrl,
async: true,
dataType : 'jsonp',
crossDomain: true,
jsonp: "callback"
})
.done(function(oJSONData) {
me._cacheData(idx, oJSONData);
me._onMessagesFetched(idx, oJSONData);
}).error(function() {
console.warn('Newsroom: failed to fetch data from the web for provider '+idx+' url: '+me.options.providers[idxProvider].fetch_url);
me._cacheData(idx, []);
me._onMessagesFetched(idx, []);
});
},
_onMessagesFetched: function(idxProvider, oJSONData)
{
this.aMessageByProvider[idxProvider] = oJSONData;
if ((1+idxProvider) < this.options.providers.length)
{
this._getMessages(idxProvider+1); // Process the next provider
}
else
{
this._onAllMessagesFetched(); // All messages retrieved
}
},
_onAllMessagesFetched: function()
{
var aAllMessages = [];
for(k in this.aMessageByProvider)
{
for(j in this.aMessageByProvider[k])
{
var oMsg = this.aMessageByProvider[k][j];
oMsg.id = ''+oMsg.id; // Stringify
oMsg.provider = k;
aAllMessages.push(oMsg);
}
}
aAllMessages.sort(function(msg1, msg2) {
if (msg1.priority < msg2.priority) return -1;
if (msg1.priority > msg2.priority) return 1;
var oDate1 = new Date(msg1.start_date);
var oDate2 = new Date(msg2.start_date);
if (oDate1 > oDate2) return -1;
if (oDate1 < oDate2) return 1;
return 1;
});
this._buildMenu(aAllMessages);
},
_buildMenu: function(aAllMessages)
{
var me = this;
var iTotalCount = aAllMessages.length;
var iCount = 0;
var sHtml = '<span id="newsroom_menu" class="itop_popup toolkit_menu"><ul><li><img id="newsroom_menu_icon" src="'+this.options.image_url+'"><ul>';
sHtml += '<li class="newsroom_menu_item" id="newsroom_menu_dismiss_all"><i class="fa fa-fw fa-check"></i>'+this.options.labels.mark_all_as_read+'</li>';
moment.locale(GetUserLanguage());
var aUnreadMessagesByProvider = [];
for(var k in this.options.providers)
{
aUnreadMessagesByProvider[k] = 0;
}
for(var k in aAllMessages)
{
var oMessage = aAllMessages[k];
aUnreadMessagesByProvider[oMessage.provider]++;
if (iCount < this.options.display_limit)
{
if (oMessage.image !== undefined)
{
sImageUrl = oMessage.image;
}
else
{
sImageUrl = this.options.placeholder_image_url;
}
var div = document.createElement("div");
div.textContent = oMessage.text;
var sDescription = div.innerHTML; // Escape HTML entities for XSS prevention
var converter = new showdown.Converter();
var sRichDescription = converter.makeHtml(sDescription);
sRichDescription += '<span class="newsroom_menu_item_date">'+this.options.providers[oMessage.provider].label+' - '+moment(oMessage.start_date).fromNow()+'</span>';
sHtml += '<li class="newsroom_menu_item" data-msg-id="'+oMessage.id+'" data-provider-id="'+oMessage.provider+'" data-url="'+oMessage.url+'" id="newsroom_menu_item_'+oMessage.id+'"><div><img src="'+sImageUrl+'"><p>'+sRichDescription+'</p><div style="clear:both"></div></div></li>';
}
iCount++;
}
if (this.options.providers.length == 1)
{
sHtml += '<li class="newsroom_menu_item" id="newsroom_menu_show_all">'+this.options.labels.view_all+'</li>';
}
else
{
sHtml += '<li class="no-padding"><span id="newsroom_show_all_submenu" class="itop_popup toolkit_menu"><ul><li>'+this.options.labels.view_all+'&nbsp;▾<ul>';
for(k in this.options.providers)
{
var sExtraMessages = '';
if (aUnreadMessagesByProvider[k] > 0)
{
sExtraMessages = ' <span class="newsroom_extra_messages_counter">'+aUnreadMessagesByProvider[k]+'</span>'
}
sHtml += '<li class="newsroom_sub_menu_item" data-provider-id="'+k+'">'+this.options.providers[k].label+sExtraMessages+'</li>';
}
sHtml += '</ul></li></ul></li></ul></span>';
}
if (iCount > 0)
{
sHtml += '</ul></li></ul></span><div id="newsroom_menu_counter_container"><span id="newsroom_menu_counter">'+iTotalCount+'</span></div></span>';
$(this.element).html(sHtml);
var me = this;
$('#newsroom_menu > ul').popupmenu();
$('.newsroom_menu_item[data-msg-id]').on('click', function(ev) { me._handleClick(this); });
$('#newsroom_menu_dismiss_all').on('click', function(ev) { me._markAllAsRead(); });
if (this.options.providers.length == 1)
{
$('#newsroom_menu_show_all').on('click', function(ev) { window.open(me.options.providers[0].view_all_url, '_blank'); });
}
else
{
$('#newsroom_show_all_submenu > ul').popupmenu();
$('.newsroom_sub_menu_item').on('click', function() { var idx = parseInt($(this).attr('data-provider-id'), 10); window.open(me.options.providers[idx].view_all_url, '_blank');});
}
}
else
{
$('#top-left-newsroom-cell > img').css({opacity: 0.4}).attr('title', this.options.labels.no_message);
}
},
_handleClick: function(elem)
{
var idxProvider = $(elem).attr('data-provider-id');
var msgId = $(elem).attr('data-msg-id');
var sUrl = $(elem).attr('data-url');
this._markOneMessageAsRead(idxProvider, msgId);
window.open(sUrl, '_blank');
$('#newsroom_menu').remove();
$('#newsroom_menu_counter_container').remove();
this._getAllMessages();
},
_resetUnseenCount: function()
{
var display = $('#newsroom_menu_counter').css('display');
$('#newsroom_menu_counter').fadeOut(500, function() {
$(this).css('visibility', 'hidden');
$(this).css('display', display);
});
},
clearCache: function(idx)
{
if (idx == undefined)
{
for(var k in this.options.providers)
{
var sKey = this._makeCacheKey(k);
localStorage.removeItem(sKey);
}
}
else
{
var sKey = this._makeCacheKey(idx);
localStorage.removeItem(sKey);
}
},
_makeCacheKey: function(idxProvider)
{
return this.options.cache_uuid+'_'+idxProvider;
},
_cacheData: function(idxProvider, oJSONData)
{
var sKey = this._makeCacheKey(idxProvider);
var bSuccess = true;
var oNow = new Date();
var oExpirationDate = new Date(oNow.getTime() + this.options.providers[idxProvider].ttl * 1000);
var oData = {value: JSON.stringify(oJSONData), expiration_date: oExpirationDate.getTime() };
try
{
localStorage.setItem(sKey, JSON.stringify(oData))
}
catch(e)
{
console.warn('Newsroom: Failed to store newsroom messages into local storage !! reason: '+e);
bSuccess = false;
}
return bSuccess;
},
_getCachedData: function(idxProvider)
{
var sKey = this._makeCacheKey(idxProvider);
var sData = localStorage.getItem(sKey);
if (sData == null) return null; // No entry in the local storage cache
try
{
var oData = JSON.parse(sData);
var oExpiration = new Date(oData.expiration_date);
var oNow = new Date();
if (oExpiration < oNow)
{
return null;
}
return JSON.parse(oData.value);
}
catch(e)
{
console.warn('Newsroom: Failed to fetch newsroom messages from local storage !! reason: '+e);
this.clearCache(idxProvider);
return null;
}
},
_markOneMessageAsRead: function(idxProvider, msgId)
{
// Remove the given message from the cache
var aData = this._getCachedData(idxProvider);
if (aData !== null)
{
var aRemainingData = [];
for(var k in aData)
{
var sId = aData[k].id.toString();
if(sId !== msgId)
{
aRemainingData.push(aData[k]);
}
}
this._cacheData(idxProvider, aRemainingData); // Also extends the TTL of the cache
}
},
_markAllMessagesAsRead: function(idxProvider)
{
this._cacheData(idxProvider, []); //Store an empty list in the cache
$.ajax({ type: "GET",
url: this.options.providers[idxProvider].mark_all_as_read_url,
async: true,
dataType : 'jsonp',
crossDomain: true,
jsonp: "callback"
})
.done(function(oJSONData) {
});
},
_markAllAsRead: function()
{
for(var k in this.options.providers)
{
this._markAllMessagesAsRead(k);
}
$('#newsroom_menu').html('<img src="'+this.options.image_url+'" style="opacity:0.4" title="'+this.options.labels.no_message+'">');
$('#newsroom_menu_counter_container').remove();
this._getAllMessages();
}
});
});

3
js/showdown.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -296,6 +296,79 @@ EOF
);
} // if count > 0
$oP->add('</fieldset>');
//////////////////////////////////////////////////////////////////////////
//
// Newsroom
//
//////////////////////////////////////////////////////////////////////////
$iCountProviders = 0;
$oUser = UserRights::GetUserObject();
$aProviders = MetaModel::EnumPlugins('iNewsroomProvider');
foreach($aProviders as $oProvider)
{
if ($oProvider->IsApplicable($oUser))
{
$iCountProviders++;
}
}
$bNewsroomEnabled = (MetaModel::GetConfig()->Get('newsroom_enabled') !== false);
if ($bNewsroomEnabled && ($iCountProviders > 0))
{
$oP->add('<fieldset><legend>'.Dict::S('UI:Newsroom:Preferences').'</legend>');
$oP->add('<form method="post">');
$iNewsroomDisplaySize = (int)appUserPreferences::GetPref('newsroom_display_size', 7);
if ($iNewsroomDisplaySize < 1) $iNewsroomDisplaySize = 1;
if ($iNewsroomDisplaySize > 20) $iNewsroomDisplaySize = 20;
$sInput = '<input min="1" max="20" id="newsroom_display_size" type="number" size="2" name="newsroom_display_size" value="'.$iNewsroomDisplaySize.'">';
$sIcon = '<img src="../images/newsroom_menu.png" style="vertical-align:middle">';
$oP->p(Dict::Format('UI:Newsroom:DisplayAtMost_X_Messages', $sInput, $sIcon));
/**
* @var iNewsroomProvider[] $aProviders
*/
$aProviderParams = array();
$iCountProviders = 0;
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
foreach($aProviders as $oProvider)
{
if ($oProvider->IsApplicable($oUser))
{
$sUrl = $oProvider->GetPreferencesUrl();
$sProviderClass = get_class($oProvider);
$sPreferencesLink = '';
if ($sUrl !== null)
{
if(substr($sUrl, 0, strlen($sAppRootUrl)) === $sAppRootUrl)
{
$sTarget = ''; // Internal link, open in the same window
}
else
{
$sTarget = ' target="_blank"'; // External link, open in new window
}
$sPreferencesLink = ' - <a class=".newsroom-configuration-link" href="'.$sUrl.'"'.$sTarget.'>'.Dict::S('UI:Newsroom:ConfigurationLink').'</a>';
}
$sChecked = appUserPreferences::GetPref('newsroom_provider_'.$sProviderClass, true) ? ' checked="" ' : '';
$oP->p('<input type="checkbox" id="newsroom_provider_'.$sProviderClass.'" value="on"'.$sChecked.'name="newsroom_provider_'.$sProviderClass.'"><label for="newsroom_provider_'.$sProviderClass.'">&nbsp;'.Dict::Format('UI:Newsroom:DisplayMessagesFor_Provider', $oProvider->GetLabel()).'</label> '.$sPreferencesLink);
}
}
$oP->p('<button style="float:right" onclick="$(\'.itop-newsroom_menu\').newsroom_menu(\'clearCache\');">'.htmlentities(Dict::S('UI:Newsroom:ResetCache')).'</button>');
$oP->add('<input type="hidden" name="operation" value="apply_newsroom_preferences"/>');
$oP->add($oAppContext->GetForForm());
$oP->add('<p><input type="button" onClick="window.location.href=\''.$sURL.'\'" value="'.Dict::S('UI:Button:Cancel').'"/>');
$oP->add('&nbsp;&nbsp;');
$oP->add('<input type="submit" value="'.Dict::S('UI:Button:Apply').'"/></p>');
$oP->add('</form>');
$oP->add('</fieldset>');
}
//////////////////////////////////////////////////////////////////////////
//
// Footer
@@ -365,6 +438,53 @@ try
DisplayPreferences($oPage);
break;
case 'apply_newsroom_preferences':
$iCountProviders = 0;
$oUser = UserRights::GetUserObject();
$aProviders = MetaModel::EnumPlugins('iNewsroomProvider');
foreach($aProviders as $oProvider)
{
if ($oProvider->IsApplicable($oUser))
{
$iCountProviders++;
}
}
$bNewsroomEnabled = (MetaModel::GetConfig()->Get('newsroom_enabled') !== false);
if ($bNewsroomEnabled && ($iCountProviders > 0))
{
$iNewsroomDisplaySize = (int)utils::ReadParam('newsroom_display_size', 7);
if ($iNewsroomDisplaySize < 1) $iNewsroomDisplaySize = 1;
if ($iNewsroomDisplaySize > 20) $iNewsroomDisplaySize = 20;
$iCurrentDisplaySize = (int)appUserPreferences::GetPref('newsroom_display_size', $iNewsroomDisplaySize);
if ($iCurrentDisplaySize != $iNewsroomDisplaySize)
{
// Save the preference only if it differs from the current (or default) value
appUserPreferences::SetPref('newsroom_display_size', $iNewsroomDisplaySize);
}
}
$bProvidersModified = false;
foreach($aProviders as $oProvider)
{
if ($oProvider->IsApplicable($oUser))
{
$sProviderClass = get_class($oProvider);
$bProviderEnabled = (utils::ReadParam('newsroom_provider_'.$sProviderClass, 'off') == 'on');
$bCurrentValue = appUserPreferences::GetPref('newsroom_provider_'.$sProviderClass, true);
if ($bCurrentValue != $bProviderEnabled)
{
// Save the preference only if it differs from the current value
$bProvidersModified = true;
appUserPreferences::SetPref('newsroom_provider_'.$sProviderClass, $bProviderEnabled);
}
}
}
if ($bProvidersModified)
{
$oPage->add_ready_script('$(".itop-newsroom_menu").newsroom_menu("clearCache");');
}
DisplayPreferences($oPage);
break;
case 'display':
default:
$oPage->SetBreadCrumbEntry('ui-tool-preferences', Dict::S('UI:Preferences'), Dict::S('UI:Preferences'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png');