diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php
index a640128d26..bf7da13dcb 100644
--- a/calendar/inc/class.calendar_so.inc.php
+++ b/calendar/inc/class.calendar_so.inc.php
@@ -1218,6 +1218,11 @@ class calendar_so
{
foreach(isset($data[1]) ? $data : [$data] as $key => $data)
{
+ // Skip if user turned the app off
+ if(!in_array($data['selects'][0]['app'], $GLOBALS['egw_info']['user']['preferences']['calendar']['integration_toggle']))
+ {
+ continue;
+ }
// create a flat array, if app implementes multiple hooks using given app-name
self::$integration_data[$data['selects'][0]['app']] = $data;
diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php
index 3505f7c1ca..37c4a442da 100644
--- a/calendar/inc/class.calendar_uiviews.inc.php
+++ b/calendar/inc/class.calendar_uiviews.inc.php
@@ -383,6 +383,41 @@ class calendar_uiviews extends calendar_ui
'toolbarDefault' => true,
),
);
+
+ // Add integrated app options
+ $integration_data = Api\Hooks::process(array('location' => 'calendar_search_union'));
+ foreach($integration_data as $app => $app_hooks)
+ {
+ foreach ($app_hooks as $data)
+ {
+ // App might have multiple hooks, let it specify something else
+ $app = $data['selects']['app'] ?: $app;
+
+ // Don't add if no access or app already added
+ if (!array_key_exists($app, $GLOBALS['egw_info']['user']['apps']) || array_key_exists($app, $actions['integration']['children']))
+ {
+ continue;
+ }
+ // Don't show infolog if there are no configured types
+ if($app == 'infolog' && empty($GLOBALS['egw_info']['user']['preferences']['infolog']['calendar_integration']))
+ {
+ continue;
+ }
+ $img = self::integration_get_icons($app, null, [])[0];
+ preg_match('//', $img, $results);
+ $actions['integration_'.$app] = array(
+ 'caption' => $app,
+ 'iconUrl' => $results[1] ?: "$app\navbar",
+ 'checkbox' => true,
+ 'hint' => lang("show %1 from %2",lang(Link::get_registry($app,'entries') ?: 'entries'),lang(Link::get_registry($app,'name'))),
+ 'group' => 'integration',
+ 'onExecute' => 'javaScript:app.calendar.toolbar_integration_action',
+ 'checked' => in_array($app,$this->cal_prefs['integration_toggle']),
+ 'data' => array('toggle_off' => '0', 'toggle_on' => '1')
+ );
+
+ }
+ }
if (Api\Header\UserAgent::mobile())
{
foreach (array_keys($actions) as $key)
diff --git a/calendar/js/app.js b/calendar/js/app.js
index c92a4c4f7a..433bdade52 100644
--- a/calendar/js/app.js
+++ b/calendar/js/app.js
@@ -781,6 +781,36 @@ var CalendarApp = /** @class */ (function (_super) {
break;
}
};
+ /**
+ * Handle integration actions from the toolbar
+ *
+ * @param action {egwAction} Integration action from the toolbar
+ */
+ CalendarApp.prototype.toolbar_integration_action = function (action) {
+ var app = action.id.replace("integration_", "");
+ var integration_preference = egw.preference("integration_toggle", "calendar");
+ if (typeof integration_preference === "undefined") {
+ integration_preference = [];
+ }
+ if (typeof integration_preference == "string") {
+ integration_preference = integration_preference.split(",");
+ }
+ // Make sure it's an array, not an object
+ integration_preference = jQuery.extend([], integration_preference);
+ if (action.checked) {
+ integration_preference.push(app);
+ }
+ else {
+ var index = integration_preference.indexOf(app);
+ if (index > -1) {
+ integration_preference.splice(index, 1);
+ }
+ }
+ egw.set_preference("calendar", "integration_toggle", integration_preference);
+ // Force redraw to current state with new info, but wait a bit to let preference change get there first
+ this._clear_cache();
+ window.setTimeout(function () { this.setState({ state: this.state }); }.bind(this), 500);
+ };
/**
* Set the app header
*
diff --git a/calendar/js/app.ts b/calendar/js/app.ts
index 52c67bf88e..c7ba09d0d0 100644
--- a/calendar/js/app.ts
+++ b/calendar/js/app.ts
@@ -769,6 +769,44 @@ class CalendarApp extends EgwApp
}
}
+ /**
+ * Handle integration actions from the toolbar
+ *
+ * @param action {egwAction} Integration action from the toolbar
+ */
+ toolbar_integration_action(action : egwAction)
+ {
+ let app = action.id.replace("integration_","");
+ let integration_preference = egw.preference("integration_toggle","calendar");
+ if(typeof integration_preference === "undefined")
+ {
+ integration_preference = [];
+ }
+ if(typeof integration_preference == "string")
+ {
+ integration_preference = integration_preference.split(",");
+ }
+ // Make sure it's an array, not an object
+ integration_preference = jQuery.extend([],integration_preference);
+
+ if(action.checked)
+ {
+ integration_preference.push(app);
+ }
+ else
+ {
+ const index = integration_preference.indexOf(app);
+ if (index > -1) {
+ integration_preference.splice(index, 1);
+ }
+ }
+ egw.set_preference("calendar","integration_toggle",integration_preference);
+
+ // Force redraw to current state with new info, but wait a bit to let preference change get there first
+ this._clear_cache();
+ window.setTimeout(function() {this.setState({state: this.state})}.bind(this),500);
+ }
+
/**
* Set the app header
*
diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang
index 1caa0f1edf..c035c97465 100644
--- a/calendar/lang/egw_en.lang
+++ b/calendar/lang/egw_en.lang
@@ -531,6 +531,7 @@ show birthdays as all day non-blocking events as well as via mouseover of the da
show birthdays as events calendar en Show as events
show birthdays from addressbook admin en Show birthdays from address book
show empty rows in planner calendar en Show empty rows in planner
+show %1 from %2 calendar en Show %1 from %2
show events that have been deleted calendar en Show events that have been deleted
show list of upcoming events calendar en Show list of upcoming events
show only accepted events calendar en Show only accepted events
diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css
index f142bfc459..4196b2b25a 100644
--- a/calendar/templates/default/app.css
+++ b/calendar/templates/default/app.css
@@ -1497,6 +1497,21 @@ img.calendar_print_button, img.calendar_print_appicon {
position: absolute;
left:44%;
}
+/* Integration slide switches in toolbar */
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"] {
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ filter: grayscale(1) contrast(0.1);
+}
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"].switchOn {
+ filter: none;
+}
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"] span.slideSwitch_container {
+ background: none;
+ min-width: 24px;
+}
+
#calendar-toolbar_toolbar .et2_toolbar_more {
margin-top: 0;
}
diff --git a/calendar/templates/pixelegg/app.css b/calendar/templates/pixelegg/app.css
index f1cf8248ea..2c298fd8fa 100755
--- a/calendar/templates/pixelegg/app.css
+++ b/calendar/templates/pixelegg/app.css
@@ -1459,6 +1459,20 @@ img.calendar_print_appicon {
position: absolute;
left: 44%;
}
+/* Integration slide switches in toolbar */
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"] {
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ filter: grayscale(1) contrast(0.1);
+}
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"].switchOn {
+ filter: none;
+}
+#calendar-toolbar_toolbar span[id^="toolbar-integration_"] span.slideSwitch_container {
+ background: none;
+ min-width: 24px;
+}
#calendar-toolbar_toolbar .et2_toolbar_more {
margin-top: 0;
}
@@ -2737,7 +2751,6 @@ div#calendar-container div.calendar table tbody tr.rowhilite td {
padding: 0px;
}
#calendar-toolbar_toolbar span.et2_checkbox_slideSwitch span.slideSwitch_container span.on {
- background-image: url(../../../pixelegg/images/7_day_view.svg);
background-size: 18px 18px;
background-repeat: no-repeat;
background-position: center;
@@ -2746,7 +2759,6 @@ div#calendar-container div.calendar table tbody tr.rowhilite td {
filter: invert(1);
}
#calendar-toolbar_toolbar span.et2_checkbox_slideSwitch span.slideSwitch_container span.off {
- background-image: url(../../../pixelegg/images/5_day_view.svg);
background-size: 18px 18px;
background-repeat: no-repeat;
background-position: center;
@@ -2759,5 +2771,11 @@ div#calendar-container div.calendar table tbody tr.rowhilite td {
#calendar-toolbar_toolbar span.et2_checkbox_slideSwitch span.slideSwitch_container a {
background: none;
}
+#calendar-toolbar_toolbar #toolbar-weekend span.slideSwitch_container span.on {
+ background-image: url(../../../pixelegg/images/7_day_view.svg);
+}
+#calendar-toolbar_toolbar #toolbar-weekend span.slideSwitch_container span.off {
+ background-image: url(../../../pixelegg/images/5_day_view.svg);
+}
/* ########################################################################################
/* * Calendar END */
diff --git a/calendar/templates/pixelegg/app.less b/calendar/templates/pixelegg/app.less
index 15f07088e8..2861e088a8 100755
--- a/calendar/templates/pixelegg/app.less
+++ b/calendar/templates/pixelegg/app.less
@@ -1236,7 +1236,6 @@ div#calendar-container {
height: 22px;
span.slideSwitch_container {
span.on {
- background-image: url(../../../pixelegg/images/7_day_view.svg);
background-size: 18px 18px;
background-repeat: no-repeat;
background-position: center;
@@ -1245,7 +1244,6 @@ div#calendar-container {
filter: invert(1);
}
span.off {
- background-image: url(../../../pixelegg/images/5_day_view.svg);
background-size: 18px 18px;
background-repeat: no-repeat;
background-position: center;
@@ -1258,6 +1256,12 @@ div#calendar-container {
a {background: none;}
}
}
+ #toolbar-weekend span.slideSwitch_container span.on {
+ background-image: url(../../../pixelegg/images/7_day_view.svg);
+ }
+ #toolbar-weekend span.slideSwitch_container span.off {
+ background-image: url(../../../pixelegg/images/5_day_view.svg);
+ }
}
/* ########################################################################################