8 Commits

Author SHA1 Message Date
Milos Sen
84c7b31dfb update comment 2026-08-08 06:53:11 +03:00
1499347b7b added workbench to list of skins 2026-08-07 19:41:40 +00:00
1d39bc03d8 Update SQL/postgres.initial.sql
makes the script safe to re-run against an already-migrated database
2026-08-06 05:10:47 +00:00
Laurent Dinclaux
8f45736b1b fix(skins): keep menu links visible and align native select look
The wrapper had width:100% which pushed the right-side menu links
(mail/contacts/settings/logout) out of #taskbar in Classic, and the
global custom select styling defeated Roundcube's native select
rendering — even when reset — because any background-image on a
<select> disables the native gradient/rounded corners.

- Scope the custom appearance/SVG-chevron CSS to Elastic and Larry
- Add a Classic-specific rule (width:auto, max-width, -2px nudge) that
  preserves Roundcube's native select look in #taskbar
- Drop the inline padding:0 imposed by the PHP renderer; let CSS own it
- Cap the Larry wrapper width to keep .button-logout in flow, and move
  the Larry-only styling out of the JS into the scoped CSS rule

Fixes #1
2026-05-12 17:02:32 +11:00
Laurent Dinclaux
8385197f11 chore: refresh composer.lock 2026-05-06 20:26:54 +11:00
Laurent Dinclaux
ca16e3ca39 ci: bump actions/checkout to v5 (Node 24) 2026-05-06 20:21:04 +11:00
Laurent Dinclaux
c194661fa3 fix: prevent username flash on load and remove select max-width 2026-05-06 20:11:10 +11:00
Laurent Dinclaux
e539c60e9b feat: use primary identity display name in switcher dropdown
The account switcher dropdown previously showed the IMAP username
(typically the email address) for the primary account. Now it falls
back to the identity's display name when available, matching the
behavior of secondary accounts which use their custom label.

Priority order: global_alias session var > primary identity display
name > IMAP username.
2026-05-06 17:37:50 +11:00
8 changed files with 95 additions and 28 deletions

View File

@@ -13,7 +13,7 @@ jobs:
matrix:
php-version: ['8.2', '8.3', '8.4']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2

View File

@@ -10,7 +10,7 @@ jobs:
phpcs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@@ -1,4 +1,13 @@
CREATE TABLE ident_switch
-- Patched version of SQL/postgres.initial.sql for gecka/ident-switch.
-- Commit this into your Gitea fork at the same path, replacing the
-- upstream file. Only two changes from upstream:
-- 1. CREATE TABLE / CREATE INDEX -> IF NOT EXISTS
-- 2. INSERT INTO system -> upsert via ON CONFLICT
-- This makes the script safe to re-run against an already-migrated
-- database, which happens on every container restart unless
-- /var/www/html is persisted (composer re-installs "fresh" each time).
CREATE TABLE IF NOT EXISTS ident_switch
(
id
serial
@@ -82,8 +91,9 @@ CREATE TABLE ident_switch
UNIQUE (user_id, label)
);
CREATE INDEX IX_ident_switch_user_id ON ident_switch(user_id);
CREATE INDEX IX_ident_switch_iid ON ident_switch(iid);
CREATE INDEX IX_ident_switch_parent_id ON ident_switch(parent_id);
CREATE INDEX IF NOT EXISTS IX_ident_switch_user_id ON ident_switch(user_id);
CREATE INDEX IF NOT EXISTS IX_ident_switch_iid ON ident_switch(iid);
CREATE INDEX IF NOT EXISTS IX_ident_switch_parent_id ON ident_switch(parent_id);
INSERT INTO system (name, value) VALUES ('ident_switch-version', '2026021000');
INSERT INTO system (name, value) VALUES ('ident_switch-version', '2026021000')
ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value;

4
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "46b2d3558cb304fde0539d1b85971382",
"content-hash": "c1389b5e002c6014eeddc8105bf77db1",
"packages": [
{
"name": "roundcube/plugin-installer",
@@ -154,5 +154,5 @@
"ext-ctype": "*"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}

View File

@@ -2,7 +2,7 @@
* ident_switch - Account switcher UI and new mail notifications.
*
* Places the hidden <select> from the footer into the appropriate
* skin location (Larry, Classic, Elastic), shows it, and registers
* skin location (Larry, Classic, Elastic, Workbench), shows it, and registers
* notification listeners for background mail checking.
*
* Copyright (C) 2016-2018 Boris Gulay
@@ -22,7 +22,6 @@ $(function() {
var $sw = $wrapper.find('#plugin-ident_switch-account');
var placed = false;
switch (rcmail.env.skin) {
case 'larry':
placed = plugin_switchIdent_addCbLarry($wrapper, $sw);
@@ -33,6 +32,9 @@ $(function() {
case 'elastic':
placed = plugin_switchIdent_addCbElastic($wrapper, $sw);
break;
case 'workbench':
placed = plugin_switchIdent_addCbElastic($wrapper, $sw);
break;
}
if (!placed) {
@@ -57,7 +59,7 @@ $(function() {
});
/**
* Place switcher in Larry skin: replace username in top-right corner.
* Place switcher in Larry skin: replace username in top-right corner of #topline.
*/
function plugin_switchIdent_addCbLarry($wrapper, $sw) {
var $topRight = $('#topline .topright');
@@ -65,12 +67,6 @@ function plugin_switchIdent_addCbLarry($wrapper, $sw) {
return false;
}
$sw.css({
'font-weight': 'bold',
'max-width': '200px',
'text-overflow': 'ellipsis'
});
$topRight.find('.username').hide();
$topRight.prepend($wrapper);
return true;
@@ -104,7 +100,6 @@ function plugin_switchIdent_addCbElastic($wrapper, $sw) {
'font-weight': 'bold',
'color': 'inherit',
'box-shadow': 'none',
'max-width': '200px',
'text-overflow': 'ellipsis',
'padding': '0 1.2em 0 0.25em'
});

View File

@@ -15,8 +15,12 @@
width: 100%;
}
/* Account switcher <select>: hide native arrow, show SVG chevron */
.ident-switch-wrapper select {
/* Account switcher <select>: hide native arrow, show SVG chevron.
Scoped to Elastic (.header-title.username) and Larry (#topline .topright) where
the switcher replaces a styled header label; Classic uses Roundcube's native
select rendering instead (see #taskbar rule below). */
.header-title.username .ident-switch-wrapper select,
#topline .topright .ident-switch-wrapper select {
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
@@ -27,6 +31,62 @@
background-size: 10px 6px;
}
/*
* Prevent flash of original username before JS prepends the switcher.
* The JS hides these elements at DOMReady, but the CSS rules below hide them
* earlier (during initial render) — only loaded when the switcher will actually
* be shown, so users without secondary accounts keep the default username display.
*/
/* Elastic skin: switcher is prepended inside .header-title.username.
Hide the original username via visibility on the parent (affects the text
node), then re-show the wrapper. Unlike color/font-size, this preserves the
parent's flex/line-height context, keeping the wrapper vertically centered. */
.header-title.username {
visibility: hidden;
}
.header-title.username > *:not(.ident-switch-wrapper) {
display: none;
}
.header-title.username > .ident-switch-wrapper {
visibility: visible;
}
/* Larry skin: switcher is prepended next to .username inside #topline .topright */
#topline .topright > .username {
display: none;
}
/* Larry skin: blend the <select> into the dark #topline header (Larry styles a
<span> here, not a <select>, so without these the native white control sticks
out on the dark background). */
#topline .topright .ident-switch-wrapper {
width: auto;
}
#topline .topright .ident-switch-wrapper select {
background-color: transparent;
border: none;
box-shadow: none;
color: inherit;
font-weight: bold;
padding: 0 1.2em 0 0.25em;
text-overflow: ellipsis;
}
/* Classic skin: switcher is prepended inside #taskbar (right-aligned, nowrap, 24px tall).
Drop width:100% so it sits inline before the menu links instead of pushing them
off-screen. The select keeps Roundcube's default native rendering. */
#taskbar > .ident-switch-wrapper {
width: auto;
vertical-align: middle;
margin-right: 8px;
}
#taskbar > .ident-switch-wrapper select {
max-width: 180px;
position: relative;
top: -2px;
}
/* Unread badge */
.ident-switch-badge {
flex-shrink: 0;

View File

@@ -158,10 +158,6 @@ class ident_switch extends rcube_plugin
default => null,
};
if ($rc->task === 'mail') {
$this->include_stylesheet('ident_switch.css');
}
return $args;
}
@@ -188,7 +184,9 @@ class ident_switch extends rcube_plugin
$iid = intval($iid_s);
}
$accNames = [$_SESSION['global_alias'] ?? $rc->user->data['username']];
$primaryIdentity = $rc->user->get_identity();
$primaryName = !empty($primaryIdentity['name']) ? $primaryIdentity['name'] : null;
$accNames = [$_SESSION['global_alias'] ?? $primaryName ?? $rc->user->data['username']];
$accValues = [-1];
$accSelected = -1;
$iidMap = [0 => -1]; // primary account: iid 0 → select value -1
@@ -216,6 +214,7 @@ class ident_switch extends rcube_plugin
return;
}
$this->include_stylesheet('ident_switch.css');
$this->include_script('ident_switch-switch.js');
// Pass config to JS environment
@@ -223,7 +222,7 @@ class ident_switch extends rcube_plugin
$select = new html_select([
'id' => 'plugin-ident_switch-account',
'style' => 'display: none; padding: 0;',
'style' => 'display: none;',
'onchange' => 'plugin_switchIdent_switch(this.value);',
]);
$select->add($accNames, $accValues);

View File

@@ -177,6 +177,9 @@ class IdentSwitchChecker
$host = 'tls://' . $host;
}
$primaryIdentity = $rc->user->get_identity();
$primaryName = !empty($primaryIdentity['name']) ? $primaryIdentity['name'] : null;
return [
'iid' => 0,
'imap_host' => $host,
@@ -185,7 +188,7 @@ class IdentSwitchChecker
'username' => $rc->user->data['username'],
'password' => $_SESSION['password' . $postfix],
'email' => $rc->user->data['username'],
'label' => $_SESSION['global_alias'] ?? $rc->user->data['username'],
'label' => $_SESSION['global_alias'] ?? $primaryName ?? $rc->user->data['username'],
'notify_basic' => null,
'notify_sound' => null,
'notify_desktop' => null,