Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions spreadsheet_dashboard_share_oca/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=============================
Spreadsheet Dashboard Share
=============================

.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This file is generated by oca-gen-addon-readme from
the readme fragments; do not edit it directly.

.. automodule:: spreadsheet_dashboard_share_oca
1 change: 1 addition & 0 deletions spreadsheet_dashboard_share_oca/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions spreadsheet_dashboard_share_oca/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 Volkan Tasci
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Spreadsheet Dashboard Share OCA",
"summary": "Manage dashboard share links",
"version": "19.0.1.0.0",
"license": "AGPL-3",
"author": "Volkan Tasci,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/spreadsheet",
"depends": ["spreadsheet_dashboard"],
"data": ["security/ir_rule.xml"],
"assets": {
"spreadsheet.o_spreadsheet": [
"spreadsheet_dashboard_share_oca/static/src/bundle/**/*.js",
"spreadsheet_dashboard_share_oca/static/src/bundle/**/*.xml",
],
"web.assets_unit_tests": [
"spreadsheet_dashboard_share_oca/static/tests/**/*",
],
},
"maintainers": ["volkantasci"],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * spreadsheet_dashboard_share_oca
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0-20260723\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-04 14:15+0000\n"
"PO-Revision-Date: 2026-08-04 14:15+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Actions"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/dashboard_badge.xml:0
msgid "Active shares"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Close"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.esm.js:0
msgid "Copied"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#: model:ir.model,name:spreadsheet_dashboard_share_oca.model_spreadsheet_dashboard_share
msgid "Copy of a shared dashboard"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Created by"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Created on"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Dashboard"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#: model:ir.model.fields,field_description:spreadsheet_dashboard_share_oca.field_spreadsheet_dashboard_share__display_name
msgid "Display Name"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#: model:ir.model.fields,field_description:spreadsheet_dashboard_share_oca.field_spreadsheet_dashboard_share__id
msgid "ID"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_button.xml:0
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_button_patch.esm.js:0
msgid "Manage shares"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "No shares yet."
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Revoke"
msgstr ""

#. module: spreadsheet_dashboard_share_oca
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_share_oca/static/src/bundle/share_manage/share_manage_dialog.xml:0
msgid "Share link"
msgstr ""
1 change: 1 addition & 0 deletions spreadsheet_dashboard_share_oca/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import spreadsheet_dashboard_share
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2026 Volkan Tasci
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class SpreadsheetDashboardShare(models.Model):
_inherit = "spreadsheet.dashboard.share"

@api.model
def action_get_dashboard_shares(self, dashboard_id):
"""Return the shares of a dashboard the current user can see."""
shares = self.search([("dashboard_id", "=", dashboard_id)])
return [
{
"id": share.id,
"full_url": share.full_url,
"create_date": (
share.create_date.isoformat() if share.create_date else False
),
"create_uid": (
share.create_uid.display_name if share.create_uid else False
),
"name": share.name,
}
for share in shares
]

@api.model
def action_unshare(self, share_ids):
"""Revoke shares. ir.rule limits the user to the shares they may access."""
self.browse(share_ids).unlink()
return True

@api.model
def action_get_share_counts(self):
"""Return {dashboard_id: share_count} for shares visible to the user."""
counts = self._read_group([], ["dashboard_id"], ["__count"])
return {count[0].id: count[1] for count in counts if count[0]}
3 changes: 3 additions & 0 deletions spreadsheet_dashboard_share_oca/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
3 changes: 3 additions & 0 deletions spreadsheet_dashboard_share_oca/readme/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Users can only see and revoke the shares they created. Users in the
"Dashboards / Admin" group (spreadsheet_dashboard.group_dashboard_manager)
can see and revoke the shares created by any user.
3 changes: 3 additions & 0 deletions spreadsheet_dashboard_share_oca/readme/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This module allows users to manage the public share links of their
spreadsheet dashboards: list, copy, and revoke them. Dashboard Admins can
manage the shares created by any user.
7 changes: 7 additions & 0 deletions spreadsheet_dashboard_share_oca/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- Open the "Dashboards" app (Rapor Panelleri).
- Open a dashboard and click on the **Share** button.
- Click on **Manage shares** to list every share link of the dashboard.
- Use the copy button to copy a link, or click **Revoke** to make a link
return a 404 error.
- A badge next to the dashboard name in the sidebar shows the number of
active shares.
12 changes: 12 additions & 0 deletions spreadsheet_dashboard_share_oca/security/ir_rule.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="spreadsheet_dashboard_share_manager_rule" model="ir.rule">
<field name="name">spreadsheet.dashboard.share: manager sees all</field>
<field name="model_id" ref="model_spreadsheet_dashboard_share" />
<field
name="groups"
eval="[(4, ref('spreadsheet_dashboard.group_dashboard_manager'))]"
/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
</odoo>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {onWillStart, useState} from "@odoo/owl";
import {SpreadsheetDashboardAction} from "@spreadsheet_dashboard/bundle/dashboard_action/dashboard_action";
import {patch} from "@web/core/utils/patch";
import {useBus} from "@web/core/utils/hooks";

patch(SpreadsheetDashboardAction.prototype, {
setup() {
super.setup();
this.shareCounts = useState({});
onWillStart(async () => {
await this._loadShareCounts();
});
useBus(this.env.bus, "dashboards-shares-updated", () =>
this._loadShareCounts()
);
},
async _loadShareCounts() {
try {
const counts = await this.orm.call(
"spreadsheet.dashboard.share",
"action_get_share_counts",
[]
);
Object.assign(this.shareCounts, counts);
} catch {
// Keep previous counts on failure
}
},
async shareSpreadsheet(data, excelExport) {
const url = await super.shareSpreadsheet(data, excelExport);
this.env.bus.trigger("dashboards-shares-updated");
return url;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t
t-inherit="spreadsheet_dashboard.DashboardAction.Expanded"
t-inherit-mode="extension"
>
<xpath expr="//div[hasclass('o_dashboard_name')]" position="inside">
<span
t-if="shareCounts[dashboard.data.id]"
t-esc="shareCounts[dashboard.data.id]"
class="badge text-bg-secondary ms-1"
title="Active shares"
/>
</xpath>
</t>
<t
t-inherit="spreadsheet_dashboard.DashboardAction.Collapsed"
t-inherit-mode="extension"
>
<xpath expr="//div[hasclass('mx-auto')]" position="inside">
<span
t-if="shareCounts[loader.getActiveDashboard().data.id]"
t-esc="shareCounts[loader.getActiveDashboard().data.id]"
class="badge text-bg-secondary ms-1"
title="Active shares"
/>
</xpath>
</t>
</templates>
15 changes: 15 additions & 0 deletions spreadsheet_dashboard_share_oca/static/src/bundle/share_button.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates xml:space="preserve">
<t t-inherit="spreadsheet.ShareButton" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_field_widget')][1]" position="after">
<button
t-if="isDashboard"
class="dropdown-item text-primary"
t-on-click="onManageShares"
>
<i class="fa fa-fw fa-gears me-1" />
Manage shares
</button>
</xpath>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {SpreadsheetShareButton} from "@spreadsheet/components/share_button/share_button";
import {_t} from "@web/core/l10n/translation";
import {patch} from "@web/core/utils/patch";
import {useService} from "@web/core/utils/hooks";

import {ShareManageDialog} from "./share_manage/share_manage_dialog.esm";

const MANAGE_SHARES = _t("Manage shares");

patch(SpreadsheetShareButton.prototype, {
setup() {
super.setup();
this.dialog = useService("dialog");
this.isDashboard = Boolean(this.env.services.spreadsheet_dashboard_loader);
},
onManageShares() {
if (!this.isDashboard) {
return;
}
const dashboard =
this.env.services.spreadsheet_dashboard_loader.getActiveDashboard();
if (!dashboard) {
return;
}
this.dialog.add(ShareManageDialog, {
dashboardId: dashboard.data.id,
title: `${dashboard.data.name} - ${MANAGE_SHARES}`,
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {Component, onWillStart, useState} from "@odoo/owl";
import {CopyButton} from "@web/core/copy_button/copy_button";
import {Dialog} from "@web/core/dialog/dialog";
import {_t} from "@web/core/l10n/translation";
import {useService} from "@web/core/utils/hooks";

export class ShareManageDialog extends Component {
static template = "spreadsheet_dashboard_share_oca.ShareManageDialog";
static components = {CopyButton, Dialog};
static props = {
dashboardId: {type: Number},
title: {type: String},
close: {type: Function, optional: true},
};

setup() {
this.copiedText = _t("Copied");
this.orm = useService("orm");
this.state = useState({shares: [], revoking: 0});
onWillStart(async () => {
await this._loadShares();
});
}

async _loadShares() {
this.state.shares = await this.orm.call(
"spreadsheet.dashboard.share",
"action_get_dashboard_shares",
[this.props.dashboardId]
);
}

async onRevoke(share) {
if (this.state.revoking === share.id) {
return;
}
this.state.revoking = share.id;
try {
await this.orm.call("spreadsheet.dashboard.share", "action_unshare", [
[share.id],
]);
this.state.shares = this.state.shares.filter((s) => s.id !== share.id);
this.env.bus.trigger("dashboards-shares-updated");
} finally {
this.state.revoking = 0;
}
}
}
Loading
Loading