This allows API users to process document content, enabling the
use of Docs as a headless CMS for instance, or any kind of document
processing. Fixes#1206.
An editor who created a subpages should be allowed to delete it.
We change the abilities to be coherent between the creation and the
deletion.
Fixes#1193
Once users have visited a document to which they have access,
they can't remove it from their list view anymore. Several
users reported that this is annoying because a document that
gets a lot of updates keeps popping up at the top of their list
view.
They want to be able to mask the document in a click. We propose
to add a "masked documents" section in the left side bar where the
masked documents can still be found.
With the soft delete feature, relying on the is_leaf method from the
treebeard is not accurate anymore. To determine if a node is a leaf, it
checks if the number of numchild is equal to 0. But a node can have soft
deleted children, then numchild is equal to 0, but it is not a leaf
because if we want to add a child we have to look for the last child to
compute a correct path. Otherwise we will have an error saying that the
path already exists.
We were returning the list of roles a user has on a document (direct
and inherited). Now that we introduced priority on roles, we are able
to determine what is the max role and return only this one.
This commit also changes the role that is returned for the restricted
reach: we now return None because the role is not relevant in this
case.
The frontend requires this information about the ancestor document
to which each access is related. We make sure it does not generate
more db queries and does not fetch useless and heavy fields from
the document like "excerpt".
If root documents are guaranteed to have a owner, non-root documents
will automatically have them as owner by inheritance. We should not
require non-root documents to have their own direct owner because
this will make it difficult to manage access rights when we move
documents around or when we want to remove access rights for someone
on a document subtree... There should be as few overrides as possible.
This field is set only on the list view when all accesses for a given
document and all its ancestors are listed. It gives the highest role
among all accesses related to each document.
The latest refactoring in a445278 kept some factorizations that are
not legit anymore after the refactoring.
It is also cleaner to not make serializer choice in the list view if
the reason for this choice is related to something else b/c other
views would then use the wrong serializer and that would be a
security leak.
This commit also fixes a bug in the access rights inheritance: if a
user is allowed to see accesses on a document, he should see all
acesses related to ancestors, even the ancestors that he can not
read. This is because the access that was granted on all ancestors
also apply on the current document... so it must be displayed.
Lastly, we optimize database queries because the number of accesses
we fetch is going up with multi-pages and we were generating a lot
of useless queries.
On a document, we need to display the status of the link (reach and
role) taking into account the ancestors link reach/role as well as
the current document.
We were returning the list of roles a user has on a document (direct
and inherited). Now that we introduced priority on roles, we are able
to determine what is the max role and return only this one.
This commit also changes the role that is returned for the restricted
reach: we now return None because the role is not relevant in this
case.
This will allow us to simplify the get_abilities method. It is also
more efficient because we have computed this definitions dict and
the the get_select_options method was doing the conversion again.
We are going to need to compare choices to materialize the fact that
choices are ordered. For example an admin role is higer than an
editor role but lower than an owner role.
We will need this to compute the reach and role resulting from all
the document accesses (resp. link accesses) assigned on a document's
ancestors.
If anonymous users have reader access on a parent, we were considering
that an edge use case was interesting: allowing an authenticated user
to still be editor on the child.
Although this use case could be interesting, we consider, as a first
approach, that the value it carries is not big enough to justify the
complexity for the user to understand this complex access right heritage.
We were returning too many select options for the restricted link reach:
- when the "restricted" reach is an option (key present in the returned
dictionary), the possible values for link roles are now always None to
make it clearer that they don't matter and no select box should be
shown for roles.
- Never propose "restricted" as option for link reach when the ancestors
already offer a public access. Indeed, restricted/editor was shown when
the ancestors had public/read access. The logic was to propose editor
role on more restricted reaches... but this does not make sense for
restricted since the role does is not taken into account for this reach.
Roles are set by each access line assign to users/teams.
The endpoint can_edit is added to the DocumentViewset, it will give the
information to the frontend application id the current user can edit the
Docs based on the no-websocket rules.
The duplicate was also able for anonynous user if they can read it. We
have to restrict it to at least reader authenticated otherwise no access
will be created on the duplicated document.
Add the action accepting a request to access a document. It is possible
to override the role from the request and also update an existing
DocumentAccess
We introduce a new model for user wanted to access a document or upgrade
their role if they already have access.
The viewsets does not implement PUT and PATCH, we don't need it for now.
Django 5.2 is now mature enough and we can use it in production.
In some tests the number of sql queries is increasing. This is because
the `full_clean` method called in the `save` method on all our models is
creating a transaction, so a savepoint and release is added.
We also fix deprecated warning in this commit.
With the usage of a malware detection system, we need a way to know the
file status. The front will use it to display a loader while the analyse
is not ended.
Every user having an access to a document, no matter its role have
access to the entire accesses list with all the user details. Only
owner or admin should be able to have the entire list, for the other
roles, they have access to the list containing only owner and
administrator with less information on the username. The email and its
id is removed
During a code review, I saw we are overriding the MP_NodeManager and
redefine the queryset filters:
- The MP_NodeManager sorts the queryset by `path` by default and it's
not done on our side, is it on purpose?
- The fact we need to redefine `readable_per_se` as a boilerplate is
surprising.
I suggest we use the Django mechanism to generate the manager from the
queryset.
The refactor made in the tree view caching the ancestors_links to not
compute them again in the document.get_abilities method lead to a bug.
If the get_abilities method is called without ancestors_links, then they
are computed on all the ancestors but not from the highest readable
ancestor for the current user. We have to compute them with this
constraint.
The idea behind wrapping choices in `lazy` function was to allow
overriding the list of languages in tests with `override_settings`.
This was causin makemigrations to keep on including the field in
migrations when it is not needed. Since we finally don't override
the LANGUAGES setting in tests, we can remove it to fix the problem.
We took this opportunity to refactor the way access is controlled on
media attachments. We now add the media key to a list on the document
instance each time a media is uploaded to a document. This list is
passed along when a document is duplicated, allowing us to grant
access to readers on the new document, even if they don't have or
lost access to the original document.
We also propose an option to reproduce the same access rights on the
duplicate document as what was in place on the original document.
This can be requested by passing the "with_accesses=true" option in
the query string.
The tricky point is that we need to extract attachment keys from the
existing documents and set them on the new "attachments" field that is
now used to track access rights on media files.
Some providers with S3-compatible APIs have slightly different
implementations. In this case, Scaleway didn't accept version_id=""
and has a different version ID scheme. This was tested successfully
and should remain compatible with any other provider.
When exporting a document in PDF and if the doc contains external
resources, we want to fetch them using a proxy bypassing CORS
restrictions. To ensure this endpoint is not used for something else
than fetching urls contains in the doc, we use access control and check
if the url really exists in the document.
The numchild attribute must be incremented/decremented manually
when we soft delete a document if we want it to remain accurate,
which is important to display the tree structure in the frontend.
The "nb_accesses" field was displaying the number of access instances
related to a document or any of its ancestors. Some features on the
frontend require to know how many of these access instances are related
to the document directly.
If a document already gets a link reach/role inheriting from one of its
ancestors, we should not propose setting link reach/role on the
document that would be more restrictive than what we inherited from
ancestors.
We want to be able to make a search query inside a hierchical document.
It's elegant to do it as a document detail action so that we benefit
from access control.
the "filter_queryset" method is called in the middle of the
"get_object" method. We use the "get_object" in actions like
"children", "tree", etc. which start by calling "get_object"
but return lists of documents.
We would like to apply filters to these views but the it didn't
work because the "get_object" method was also impacted by the
filters...
In a future PR, we should take control of the "get_object" method
and decouple all this. We need a quick solution to allow releasing
the hierchical documents feature in the frontend.
We want to display the tree structure to which a document belongs
on the left side panel of its detail view. For this, we need an
endpoint to retrieve the list view of the document's ancestors
opened.
By opened, we mean that when display the document, we also need to
display its siblings. When displaying the parent of the current
document, we also need to display the siblings of the parent...
- allow the language on the user to be unset
- set the default language to be unset
- helps us determine that the user has yet to set a language preference
To determine the descendant to restore or not, we were looking building
a complex exclude clause. This can be simplify focusing only on data we
already have without making an extra query to fetch the list of
descendant to exclude.
In the restore method, all the ancestors with a deleted_at date set are
extracted from the database and then the oldest value is extracted using
the min python function. This usage of min can be removed by sorting
directly the deleted_at at the databse level and then fetching the first
one. It's faster and easier to maintain.
We want to be able to define whether AI features are available to
anonymous users who gained editor access on a document, or if we
demand that they be authenticated or even if we demand that they
gained their editor access via a specific document access.
Being authenticated is now the default value. This will change the
default behavior on your existing instance (see UPGRADE.md)
Only owners can see and restore deleted documents. They can only do
it during the grace period before the document is considered hard
deleted and hidden from everybody on the API.
Now that we have introduced a document tree structure, it is not
possible to allow deleting documents anymore as it impacts the whole
subtree below the deleted document and the consequences are too big.
We introduce soft delete in order to give a second thought to the
document's owner (who is the only one to be allowed to delete a
document). After a document is soft deleted, the owner can still
see it in the trashbin (/api/v1.0/documents/trashbin).
After a grace period (30 days be default) the document disappears
from the trashbin and can't be restored anymore. Note that even
then it is still kept in database. Cleaning the database to erase
deleted documents after the grace period can be done as a maintenance
script.
Only administrators or owners of a document can move it to a target
document for which they are also administrator or owner.
We allow different moving modes:
- first-child: move the document as the first child of the target
- last-child: move the document as the last child of the target
- first-sibling: move the document as the first sibling of the target
- last-sibling: move the document as the last sibling of the target
- left: move the document as sibling ordered just before the target
- right: move the document as sibling ordered just after the target
The whole subtree below the document that is being moved, moves as
well and remains below the document after it is moved.
Including the content field in the list view is not efficient as we need
to query the object storage to retrieve it. We want to display an excerpt
of the content on the list view so we should store it in database. We
let the frontend compute it and save it for us in the new "excerpt" field
because we are not supposed to have access to the content (E2EE feature coming)