Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory listing access/visibility level badges #3680

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
60 changes: 58 additions & 2 deletions timApp/i18n/messages.fi.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8897,8 +8897,8 @@
</context-group>
</trans-unit>
<trans-unit id="187187500641108332" datatype="html">
<source><x id="INTERPOLATION" equiv-text="Folder() ?? &apos;Enter the location or leave empty&apos;}}"/></source>
<target state="new"><x id="INTERPOLATION" equiv-text="Folder() ?? &apos;Enter the location or leave empty&apos;}}"/></target>
<source><x id="INTERPOLATION" equiv-text="Folder() ?? 'Enter the location or leave empty'}}"/></source>
<target state="new"><x id="INTERPOLATION" equiv-text="Folder() ?? 'Enter the location or leave empty'}}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/user/user-group-dialog.component.ts</context>
<context context-type="linenumber">61,62</context>
Expand Down Expand Up @@ -10505,6 +10505,62 @@ Ole hyvä ja luo ensin kirjautumiskoodit.</target>
<context context-type="linenumber">41</context>
</context-group>
</trans-unit>
<trans-unit id="4343307932975048814" datatype="html">
<source>Item is visible publicly (ie. to everyone), including anonymous users.</source>
<target state="translated">Kohde näkyy julkisesti kaikille (myös anonyymeille käyttäjille).</target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/folder/directory-list.component.ts</context>
<context context-type="linenumber">23,21</context>
</context-group>
</trans-unit>
<trans-unit id="53962414988403367" datatype="html">
<source>Item is visible to logged-in users.</source>
<target state="translated">Kohde näkyy sisäänkirjautuneille käyttäjille.</target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/folder/directory-list.component.ts</context>
<context context-type="linenumber">24,21</context>
</context-group>
</trans-unit>
<trans-unit id="6175417143135898708" datatype="html">
<source>Item is visible to groups belonging to a Haka organization.</source>
<target state="translated">Kohde näkyy Haka-organisaatioon kuuluville ryhmille.</target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/folder/directory-list.component.ts</context>
<context context-type="linenumber">25,21</context>
</context-group>
</trans-unit>
<trans-unit id="5605266308588695513" datatype="html">
<source>Item is visible only to specific users, check the Manage-page for details.</source>
<target state="translated">Kohde näkyy vain valituille käyttäjille, katso lisätiedot Hallitse-sivulta.</target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/folder/directory-list.component.ts</context>
<context context-type="linenumber">26,21</context>
</context-group>
</trans-unit>
<trans-unit id="514896344625162208" datatype="html">
<source>Item is visible only to its owners.</source>
<target state="translated">Kohde näkyy vain omistajilleen.</target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/folder/directory-list.component.ts</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="3305807586292277903" datatype="html">
<source>Directory listing: Display access badges for folder items. </source>
<target state="translated">Hakemistolistaus: Näytä näkyvyysmerkinnät kansioille ja dokumenteille. </target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/user/settings.component.ts</context>
<context context-type="linenumber">499,500</context>
</context-group>
</trans-unit>
<trans-unit id="3616259126353021959" datatype="html">
<source>Directory listing: Display document tags for folder items. </source>
<target state="translated">Hakemistolistaus: Näytä dokumenttitagit. </target>
<context-group purpose="location">
<context context-type="sourcefile">static/scripts/tim/user/settings.component.ts</context>
<context context-type="linenumber">507,508</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>
140 changes: 98 additions & 42 deletions timApp/i18n/messages.sv.xlf

Large diffs are not rendered by default.

55 changes: 54 additions & 1 deletion timApp/item/item.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
from __future__ import annotations

from enum import Enum
from itertools import accumulate
from typing import TYPE_CHECKING

from flask import current_app
from sqlalchemy import tuple_, func, select
from sqlalchemy.orm import defaultload

from timApp.auth.accesstype import AccessType
from timApp.auth.auth_models import BlockAccess
from timApp.auth.get_user_rights_for_item import get_user_rights_for_item
from timApp.item.block import Block, BlockType
from timApp.item.blockrelevance import BlockRelevance
from timApp.timdb.exceptions import TimDbException
from timApp.timdb.sqa import include_if_loaded, db, run_sql
from timApp.user.usergroup import UserGroup
from timApp.util.utils import split_location, date_to_relative, cached_property

if TYPE_CHECKING:
from timApp.folder.folder import Folder
from timApp.user.user import User


class ItemVisibility(Enum):
"""
Represents the visibility level of an item.
Visibility level is determined by view access,
such that it always represents the least restrictive access level:
if both 'Anonymous users' and say a Haka group (= organisation) have view access,
the visibility level would be 'PUBLIC' (corresponding to 'Anonymous users').
"""

NONE = 0 # this has no use for now
PUBLIC = 1
LOGGED_IN = 2
ORGANIZATION = 3
LIMITED = 4
PRIVATE = 5


class ItemBase:
"""An item that can be assigned permissions."""

Expand Down Expand Up @@ -109,6 +129,38 @@ def short_name(self):
parts = self.path_without_lang.rsplit("/", 1)
return parts[len(parts) - 1]

@property
def visibility(self) -> ItemVisibility:
if self.block is None:
return ItemVisibility.NONE

block_access_ids = [
usergroup_id for (usergroup_id, accesstype) in self.block.accesses.keys()
]

# Special groups:
# Anon users == 1 == PUBLIC
# Logged-in users == 0 == (LOGGED_IN - 2)
if ItemVisibility.PUBLIC.value in block_access_ids:
return ItemVisibility.PUBLIC
if (ItemVisibility.LOGGED_IN.value - 2) in block_access_ids:
return ItemVisibility.LOGGED_IN

# There is currently no good way to avoid calling get_organizations()
# for each item when viewing folder items. However, we utilize caching
# for that method, so it shouldn't be a problem.
for group in UserGroup.get_organizations():
if group.id in block_access_ids:
return ItemVisibility.ORGANIZATION

# Specific rights that are not special groups
for ba in self.block.accesses.values():
if ba.access_type is not AccessType.owner:
return ItemVisibility.LIMITED

# If none of the above, it must be private.
return ItemVisibility.PRIVATE

def parents_to_root(self, include_root=True, eager_load_groups=False):
if not self.path_without_lang:
return []
Expand Down Expand Up @@ -180,6 +232,7 @@ def to_json(self, curr_user: User | None = None):
"rights": get_user_rights_for_item(self, curr_user),
"unpublished": self.block.is_unpublished() if self.block else False,
"public": self.public,
"visibility": self.visibility,
# We only add tags if they've already been loaded.
**include_if_loaded("tags", self.block),
**include_if_loaded("relevance", self.block),
Expand All @@ -195,7 +248,7 @@ def get_relative_path(self, path: str):
return self.path.replace(path + "/", "", 1)

@staticmethod
def find_by_id(item_id):
def find_by_id(item_id: int):
b = db.session.get(Block, item_id)
if b:
if b.type_id == BlockType.Document.value:
Expand Down
20 changes: 20 additions & 0 deletions timApp/item/routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Routes for document view."""
import dataclasses
import html
import logging
import time
from difflib import context_diff
from typing import Union, Any, ValuesView, Generator
Expand Down Expand Up @@ -1450,6 +1451,25 @@ def get_item(item_id: int):
return json_response(i)


@view_page.get("/items/getBadges/<int:folder_id>")
def get_item_access_badges(folder_id: int) -> Response:
"""Return access level information for items in the current directory"""
user = get_current_user_object()
folder = Folder.find_by_id(folder_id)
items: list[Item] = []
viewable_folders = [
f for f in folder.get_all_folders() if user.has_view_access(i=f)
]
items.extend(viewable_folders)
items.extend(folder.get_all_documents(filter_user=user))

badges = dict()
for item in items:
badges[int(item.id)] = item.visibility.value

return json_response(badges)


@view_page.post("/items/relevance/set/<int:item_id>")
def set_blockrelevance(item_id: int, value: int, update_translations: bool):
"""
Expand Down
30 changes: 30 additions & 0 deletions timApp/item/routes_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from timApp.auth.sessioninfo import get_current_user_object
from timApp.document.docentry import DocEntry, get_documents
from timApp.document.docinfo import DocInfo
from timApp.folder.folder import Folder
from timApp.item.block import Block
from timApp.item.tag import Tag, TagType, GROUP_TAG_PREFIX
from timApp.timdb.sqa import db, run_sql
Expand Down Expand Up @@ -219,6 +220,35 @@ def get_tags(doc: str) -> Response:
return json_response(tags, date_conversion=True)


@tags_blueprint.get("/getTags/<int:folder_id>")
def get_tags_for_folder_items(folder_id: int) -> Response:
"""
Gets the list of tags for each item in a TIM folder.

:param folder_id: The target folder id.
:returns A dict of the folder ids and TagInfo-objects, keyed by the folder id, converted into JSON.
"""
folder = Folder.find_by_id(folder_id)
if not folder:
raise NotExist()
verify_view_access(folder)
item_tags: dict[int, list[TagInfo]] = {}

# Tags are currently only defined for Documents
for item in folder.get_all_documents():
tags = item.block.tags
tag_infos: list[TagInfo] = [
TagInfo(
name=tag.name, type=tag.type, expires=tag.expires, block_id=tag.block_id
)
for tag in tags
]
if tag_infos:
item_tags[item.id] = tag_infos

return json_response(item_tags, date_conversion=True)


@tags_blueprint.get("/getAllTags")
def get_all_tags() -> Response:
"""
Expand Down
80 changes: 79 additions & 1 deletion timApp/static/scripts/tim/folder/directory-list.component.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
.icon-inline {
margin-left: 0.5em;
}
}

/*
Default colors are set according to JYU branding guidelines

Primary colors:
BLUE #002957 R0 G41 B87
BEIGE #EDE1CE R237 G225 B206
WHITE #FFFFFF R255 G255 B255

Secondary colors:
GOLD #C29A5B R194 G154 B91
ORANGE #F1563F R241 G86 B63
BLACK #020203 R2 G2 B3
GRAY_30 #EFF0F1 R239 G240 B241

Additional colors:
BLUE_60 #6B7F97 R107 G127 B151
BLUE_30 #B3BFCD R178 G191 B205
ORANGE_60 #EA9D90 R234 G157 B144
ORANGE_30 #F5D1C9 R245 G209 B201

*/


.accessbadge {
border-radius: 8px;
padding: 3px;
font-size: x-small;
}
.ab-public {
border: solid 2px #002957;
color: #002957;
}
.ab-logged-in {
border: solid 2px #6b7f97;
color: #6b7f97;
}
.ab-organization {
border: solid 2px #c29a5b;
color: #c29a5b;
}
.ab-limited {
border: solid 2px #ea9d90;
color: #ea9d90;
}
.ab-private {
border: solid 2px #f1563f;
color: #f1563f;
}

.itemtags {
margin-left: 0.2em;
display: inline-flex;
flex-wrap: wrap;
}
.itemtag {
/*margin-left: 3px;*/
border-radius: 4px;
padding: 3px;
font-size: x-small;
color: #fff;
}
.tagtype-group {
background-color: #004494;
border-color: #00387b;
}
.tagtype-coursecode {
background-color: #5cb85c;
border-color: #4cae4c;
}
.tagtype-regular {
background-color: #5cb85c;
border-color: #4cae4c;
}
.tagtype-subject {
background-color: #5cb85c;
border-color: #4cae4c;
}
Loading
Loading