1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/documents/models/documents_share.py
2024-12-10 09:04:09 +07:00

272 lines
11 KiB
Python

# -*- coding: utf-8 -*-
from ast import literal_eval
from odoo import models, fields, api, exceptions
from odoo.tools.translate import _
from odoo.tools import consteq
from odoo.osv import expression
import uuid
class DocumentShare(models.Model):
_name = 'documents.share'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Documents Share'
folder_id = fields.Many2one('documents.folder', string="Workspace", required=True, ondelete='cascade')
include_sub_folders = fields.Boolean()
name = fields.Char(string="Name")
access_token = fields.Char(required=True, default=lambda x: str(uuid.uuid4()), groups="documents.group_documents_user")
full_url = fields.Char(string="URL", compute='_compute_full_url')
links_count = fields.Integer(string="Number of Links", compute='_compute_links_count')
date_deadline = fields.Date(string="Valid Until")
state = fields.Selection([
('live', "Live"),
('expired', "Expired"),
], default='live', compute='_compute_state', string="Status")
can_upload = fields.Boolean(compute='_compute_can_upload')
type = fields.Selection([
('ids', "Document list"),
('domain', "Domain"),
], default='ids', string="Share type")
# type == 'ids'
document_ids = fields.Many2many('documents.document', string='Shared Documents')
# type == 'domain'
domain = fields.Char()
action = fields.Selection([
('download', "Download"),
('downloadupload', "Download and Upload"),
], default='download', string="Allows to", inverse="_inverse_action")
tag_ids = fields.Many2many('documents.tag', string="Shared Tags")
partner_id = fields.Many2one('res.partner', string="Contact")
owner_id = fields.Many2one('res.partner', string="Document Owner", default=lambda self: self.env.user.partner_id.id)
email_drop = fields.Boolean(compute='_compute_email_drop', string='Upload by Email', store=True, readonly=False)
# Activity
activity_option = fields.Boolean(string='Create a new activity')
activity_type_id = fields.Many2one('mail.activity.type', string="Activity type")
activity_summary = fields.Char('Summary')
activity_date_deadline_range = fields.Integer(string='Due Date In')
activity_date_deadline_range_type = fields.Selection([
('days', 'Days'),
('weeks', 'Weeks'),
('months', 'Months'),
], string='Due type', default='days')
activity_note = fields.Html(string="Note")
activity_user_id = fields.Many2one('res.users', string='Responsible')
_sql_constraints = [
('share_unique', 'unique (access_token)', "This access token already exists"),
]
def _compute_display_name(self):
for record in self:
record.display_name = record.name or "unnamed link"
def _get_documents_domain(self):
"""
Allows overriding the domain in customizations for modifying the search() domain
"""
if self.type == 'ids':
return []
domains_list = [[('folder_id', 'child_of' if self.include_sub_folders else '=', self.folder_id.id)]]
if self.tag_ids:
domains_list = [expression.AND([domains_list[0], [('tag_ids', 'in', self.tag_ids.ids)]])]
return domains_list
def _get_documents(self, document_ids=None):
"""
:param list[int] document_ids: limit to the list of documents to fetch.
:return: recordset of the documents that can be accessed by the create_uid based on the settings
of the share link.
"""
self.ensure_one()
limited_self = self.with_user(self.create_uid)
Documents = limited_self.env['documents.document']
search_ids = set()
domains = self._get_documents_domain()
if document_ids is not None:
if not document_ids:
return Documents
search_ids = set(document_ids)
if self.type == 'domain':
record_domain = []
if self.domain:
record_domain = literal_eval(self.domain)
domains.append(record_domain)
if self.action == 'download':
domains.append([('type', '!=', 'empty')])
else:
share_ids = limited_self.document_ids.ids
search_ids = search_ids.intersection(share_ids) if search_ids else share_ids
if search_ids or self.type != 'domain':
domains.append([('id', 'in', list(search_ids))])
search_domain = expression.AND(domains)
return Documents.search(search_domain)
def _get_writable_documents(self, documents):
"""
:param documents:
:return: the recordset of documents for which the create_uid has write access
False only if no write right.
"""
self.ensure_one()
try:
# checks the rights first in case of empty recordset
documents.with_user(self.create_uid).check_access_rights('write')
except exceptions.AccessError:
return False
return documents.with_user(self.create_uid)._filter_access_rules('write')
def _check_token(self, access_token):
if not access_token:
return False
try:
return consteq(access_token, self.access_token)
except:
return False
def _get_documents_and_check_access(self, access_token, document_ids=None, operation='write'):
"""
:param str access_token: the access_token to be checked with the share link access_token
:param list[int] document_ids: limit to the list of documents to fetch and check from the share link.
:param str operation: access right to check on documents (read/write).
:return: Recordset[documents.document]: all the accessible requested documents
False if it fails access checks: False always means "no access right", if there are no documents but
the rights are valid, it still returns an empty recordset.
"""
self.ensure_one()
if not self._check_token(access_token):
return False
if self.state == 'expired':
return False
documents = self._get_documents(document_ids)
if operation == 'write':
return self._get_writable_documents(documents)
else:
return documents
@api.depends('folder_id')
def _compute_can_upload(self):
for record in self:
folder = record.folder_id
folder_has_groups = folder.group_ids.ids or folder.read_group_ids.ids
in_write_group = set(folder.group_ids.ids) & set(record.create_uid.groups_id.ids)
record.can_upload = in_write_group or not folder_has_groups
@api.depends('date_deadline')
def _compute_state(self):
"""
changes the state based on the expiration date,
an expired share link cannot be used to upload or download files.
"""
for record in self:
record.state = 'live'
if record.date_deadline:
today = fields.Date.from_string(fields.Date.today())
exp_date = fields.Date.from_string(record.date_deadline)
diff_time = (exp_date - today).days
if diff_time <= 0:
record.state = 'expired'
@api.depends('action', 'alias_name')
def _compute_email_drop(self):
for record in self:
record.email_drop = record.action == 'downloadupload' and bool(record.alias_name)
@api.depends('access_token')
def _compute_full_url(self):
for record in self:
record.full_url = (f'{record.get_base_url()}/document/share/'
f'{record._origin.id or record.id}/{record.access_token}')
@api.depends('type', 'document_ids', 'domain', 'tag_ids')
def _compute_links_count(self):
domains = [record._get_documents_domain()[0] for record in self if record.type == "domain"]
documents_from_domain = self.env['documents.document'].search(expression.OR(domains))
for record in self:
documents = []
if record.type == "ids":
documents = record.document_ids
elif record.type == "domain":
documents = documents_from_domain.filtered_domain(record._get_documents_domain()[0])
record.links_count = sum(1 for document in documents if document.type == 'url')
def _inverse_action(self):
# Prevent the alias from existing if the option is removed
for record in self:
if record.action != 'downloadupload' and record.alias_name:
record.alias_name = False
def _alias_get_creation_values(self):
values = super(DocumentShare, self)._alias_get_creation_values()
values['alias_model_id'] = self.env['ir.model']._get('documents.document').id
if self.id:
values['alias_defaults'] = defaults = literal_eval(self.alias_defaults or "{}")
defaults.update({
'tag_ids': [(6, 0, self.tag_ids.ids)],
'folder_id': self.folder_id.id,
'partner_id': self.partner_id.id,
'create_share_id': self.id,
})
return values
def _get_share_popup(self, context, vals):
view_id = self.env.ref('documents.share_view_form_popup').id
return {
'context': context,
'res_model': 'documents.share',
'target': 'new',
'name': _('Share selected files') if vals.get('type') == 'ids' else _('Share selected workspace'),
'res_id': self.id if self else False,
'type': 'ir.actions.act_window',
'views': [[view_id, 'form']],
}
def send_share_by_mail(self, template_xmlid):
self.ensure_one()
request_template = self.env.ref(template_xmlid, raise_if_not_found=False)
if request_template:
self.message_mail_with_source(request_template)
@api.model
def open_share_popup(self, vals):
"""
returns a view.
:return: a form action that opens the share window to display the settings.
"""
new_context = dict(self.env.context)
# TOOD: since the share is created directly do we really need to set the context?
new_context.update({
'default_owner_id': self.env.user.partner_id.id,
'default_folder_id': vals.get('folder_id'),
'default_tag_ids': vals.get('tag_ids'),
'default_type': vals.get('type', 'domain'),
'default_domain': vals.get('domain') if vals.get('type', 'domain') == 'domain' else False,
'default_document_ids': vals.get('document_ids', False),
})
return self.create(vals)._get_share_popup(new_context, vals)
@api.model
def action_get_share_url(self, vals):
"""
Creates a new share directly and return it's url
"""
return self.create(vals).full_url
def action_delete_shares(self):
self.unlink()