Skip to content

Commit

Permalink
#90 - File access by project working, introduced Level-Enum for autho…
Browse files Browse the repository at this point in the history
…rization
  • Loading branch information
Philipp Kraft committed Nov 28, 2023
1 parent 83d465e commit 5b19b91
Show file tree
Hide file tree
Showing 11 changed files with 352 additions and 186 deletions.
6 changes: 6 additions & 0 deletions odmf/db/person.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import typing

import sqlalchemy as sql
import sqlalchemy.orm as orm
from functools import total_ordering
Expand Down Expand Up @@ -51,6 +53,10 @@ def __lt__(self, other):
return self.surname < other.surname

def projects(self):
"""
Yields Project, access_level tuples
:return:
"""
from .project import ProjectMember
pm: ProjectMember
for pm in (
Expand Down
10 changes: 5 additions & 5 deletions odmf/db/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ def members_query(self):
"""Returns a query object with all ProjectMember object related to this project"""
return self.session().query(ProjectMember).filter(ProjectMember._project==self.id)
def members(self, access_level=0):
for pm in (
self.members_query.filter(ProjectMember.access_level>=access_level)
.order_by(ProjectMember.access_level.desc(), ProjectMember._member)
):
yield pm.member, pm.access_level
for pm in (
self.members_query.filter(ProjectMember.access_level>=access_level)
.order_by(ProjectMember.access_level.desc(), ProjectMember._member)
):
yield pm.member, pm.access_level

def add_member(self, person: Person|str, access_level: int=0):
if pm:=self[person]:
Expand Down
76 changes: 43 additions & 33 deletions odmf/static/templates/download.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ <h5>content</h5>
<a class="nav-link" href="${curdir.parent().href}" ><i class="fas fa-folder-open mr-2" />..</a>

</li>
<li py:for="d in sorted(directories)" class="nav-item border-top" >
<a class="nav-link" href="${d.href}">
<li py:for="d in sorted(directories)" class="nav-item border-top " >
<a class="nav-link ${'' if modes[d] else 'disabled'}" href="${d.href}">
<i class="fas fa-folder mr-2" /> ${d.basename}
</a>

</li>
</ul>
<div class="container m-1 p-1">
Expand All @@ -149,7 +150,6 @@ <h5>content</h5>
</div>
</div>
</div>

<div class="alert alert-success" role="alert" py:if="message" >
<div class="row">
<div class="col-lg-1">
Expand All @@ -160,14 +160,14 @@ <h5>content</h5>
</div>
</div>
</div>
<div class="row" py:if="curdir.isdir()">
<div class="row" py:if="curdir.isdir() and modes[curdir]">

<div class="col-xl">
<div class="row" py:if="is_member('logger')">
</div>
<div class="card p-3" >
<h5 class="card-title">
<i class="fas fa-folder-open mr-2"/><span py:for="bc in curdir.breadcrumbs()">/<a href="${bc.href}" py:content="bc.basename" /></span>
<i class="fas fa-folder-open mr-2"/>
<span py:for="bc in curdir.breadcrumbs()">/<a href="${bc.href}" py:content="bc.basename" /></span>
<span class="badge bg-warning ml-8" py:content="modes[curdir].name"/>
</h5>
<ul class="nav flex-column card-text mb-3" id="files">
<li class="nav-item flexbox mt-1 pt-1 border-top" py:if="not curdir.isroot()" >
Expand All @@ -179,21 +179,23 @@ <h5 class="card-title">
</span>
</li>
<li py:for="d in directories" class="nav-item flexbox mt-1 pt-1 border-top">
<span >
<a href="${d.href}" class="nav-link">
<span class="nav-link">
<a href="${d.href}" class="${'' if modes[d] else 'disabled'}">
<i class="fas fa-folder fa-lg" />
<span py:content="d.basename" />
</a>
</a>
<span class="badge bg-warning text-bg-warning" py:content="modes[d].name"/>

</span>
<span>
<button class="btn btn-danger remove-button" data-name="${d.basename}"
py:if="is_member('admin') and d.isempty()"
title="remove file" data-toggle="tooltip">
py:if="modes[d]>=Mode.admin and d.isempty()"
title="remove directory" data-toggle="tooltip">
<i class="fas fa-times-circle" />
</button>
</span>
</li>
<li py:for="f in files" class="nav-item flexbox mt-1 pt-1 border-top" py:if="not f.ishidden()">
<li py:for="f in files" class="nav-item flexbox mt-1 pt-1 border-top" >
<span >
<a href="${f.href}" class="nav-link">
<i class="fas fa-${handler[f].icon} fa-lg" />
Expand All @@ -204,45 +206,51 @@ <h5 class="card-title">
<span>
<a class="btn btn-success" href="${conf.root_url}/download/to_db?filename=${f.name}"
title="import in data base" data-toggle="tooltip"
py:if="is_member('editor')"
py:if="modes[curdir]>=Mode.write"
>
<i class="fas fa-file-import" />
</a>
<button class="btn btn-danger remove-button" data-name="${f.basename}" py:if="is_member('admin')"
<button class="btn btn-danger remove-button" data-name="${f.basename}" py:if="modes[curdir]>=Mode.admin"
title="remove file" data-toggle="tooltip">
<i class="fas fa-times-circle" />
</button>
<button class="btn btn-secondary copy-button" data-name="${f.basename}" py:if="is_member('editor')"
<button class="btn btn-secondary copy-button" data-name="${f.basename}" py:if="modes[curdir]>=Mode.write"
title="copy file" data-toggle="tooltip">
<i class="fas fa-copy" />
</button>
</span>

</li>
<li py:if="is_member('logger')" class="nav-item mt-4">

</li>
</ul>
<div class="container">
<button class="btn btn-secondary dropdown-toggle mr-3" type="button"
id="addFileDropdownButton" data-toggle="dropdown"
<div class="row">
<button class="btn btn-primary dropdown-toggle mr-3 col-sm" type="button"
id="addFileDropdownButton" data-toggle="dropdown" py:attrs="prop(disabled=modes[curdir]==Mode.read)"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-plus-circle" />
Add files and folders
</button>
<form method="post" action="${conf.root_url}/download/create_access_file" class="col-sm">
<button class="btn btn-warning mr-3" type="submit" name="uri" value="${curdir}"
py:if="modes[curdir]==Mode.admin"
>
<i class="fas fa-cog" /> manage access
</button>

</form>

<div class="dropdown-menu" aria-labelledby="addFileDropdownButton">
<div class="dropdown-item" >

<form method="post" action="${conf.root_url}/download/newfolder" class="form-group">
<div class="input-group">
<input type="text" name="newfolder" class="form-control" placeholder="new folder name"/>
<span class="input-group-append">
<button class="btn btn-secondary" type="submit"
title="Add new folder" data-toggle="tooltip" data-placement="right"
>
<i class="fa fa-folder-plus"></i>
</button>
</span>
<button class="btn btn-secondary" type="submit"
title="Add new folder" data-toggle="tooltip" data-placement="right"
>
<i class="fa fa-folder-plus"></i>
</button>
</span>
</div>
<input name="dir" type="hidden" value="${curdir}" />
</form>
Expand Down Expand Up @@ -287,12 +295,12 @@ <h5 class="card-title">
</div>
</form>
</div>

</div>
</div>

</div>
</div>
<div class="col-lg">
<div id="info-box-right-side" class="col-lg">
<div class="card help">
<div class="card-header">
<h3 class="card-title flexbox" >
Expand All @@ -304,6 +312,8 @@ <h3 class="card-title flexbox" >
<i class="fas fa-edit" title="Click to edit..." data-toggle="tooltip"/>
</a>
</h3>
<div py:content="markdown('folder created by user:' + str(owner))"/>

</div>
<div id="help" class="card-body card-text" />
<div id="help-edit" class="card-footer collapse">
Expand All @@ -325,13 +335,13 @@ <h3 class="card-title flexbox" >
</div>
</div>
</div>
<div class="row" py:if="defined('content')">
<div class="row" py:if="content and modes[curdir]">
<h5 class="col-lg-9">
<i class="fas fa-${handler[curdir].icon} fa-lg mr-2"/><span py:for="bc in curdir.breadcrumbs()">/<a href="${bc.href}" py:content="bc.basename" /></span>
</h5>
<div class="col-lg-3">
<a py:for="action_id, action in enumerate(handler[curdir].actions)"
py:if="has_level(action.access_level)"
py:if="modes[curdir]==Mode.admin"
class="btn btn-secondary action-button"
data-toggle="tooltip" title="${action.tooltip}"
data-actionid="${action_id}" data-path="${curdir}">
Expand All @@ -341,7 +351,7 @@ <h5 class="col-lg-9">
<a class="btn btn-outline-primary" href="${curdir.parent().href}"><i class="fas fa-folder-open mr-4"/>..</a>
</div>
</div>
<div class="row" py:if="defined('content')">
<div class="row" py:if="content and modes[curdir]">
<div class="container mt-4 h100" py:content="literal(content)"/>
</div>
</div>
Expand Down
25 changes: 13 additions & 12 deletions odmf/static/templates/project.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<py:block name="sidebar">
<!-- insert sidebar content -->
<div class="container-fluid">
<a class="btn btn-secondary w-100 mb-4" href="${conf.root_url}/project/new" py:if="is_member('supervisor')">
<a class="btn btn-secondary w-100 mb-4" href="${conf.root_url}/project/new" py:if="is_member(Level.admin)">
<i class="fas fa-plus mr-2"/>
new project
</a>
Expand Down Expand Up @@ -85,7 +85,7 @@ <h2 class="display-4 mr-8" py:content="f'{actproject.name}'"/>
</div>
<div id="edit" class="container collapse border rounded px-4">
<form action="save"
method="post" py:if="is_member('supervisor')"
method="post" py:if="is_member(Level.admin, actproject.id)"
>
<div class="form-row mt-2">
<div class="form-group">
Expand Down Expand Up @@ -136,33 +136,34 @@ <h2 class="border-bottom">Members</h2>
<ul>
<li py:for="member, level in actproject.members()" class="form-row">
<div class="col-sm">
<span py:content="member" class="ml-2"/>(<span py:content="level"/>)
<span class="badge rounded-pill bg-warning" py:content="Level(level).name" />
<span py:content="member" class="ml-2 mr-4"/>

</div>
<form method="post" action="remove_member">

<form class="col-sm" method="post" action="remove_member">
<button data-member="${member.username}"
py:if="is_member('admin')"
py:if="is_member(Level.admin, actproject.id)"
type="submit"
class="btn btn-danger btn-sm col-sm-1 pr-3 project-remove-member"
name="member_name" value="${member.username}"
title="remove" data-toggle="tooltip"
><i class="fas fa-times"/></button>
>
<i class="fas fa-times"/>
</button>

</form>
</li>
</ul>
<a href="#add-member" py:if="is_member('admin')" class="btn-sm btn-primary dropdown-toggle" data-toggle="collapse" aria-controls="add-member" aria-role="button"><i class="fas fa-plus"/> add member...</a>
<div py:if="is_member('admin')" class="collapse" id="add-member">
<a href="#add-member" py:if="is_member(Level.admin, actproject.id)" class="btn-sm btn-primary dropdown-toggle" data-toggle="collapse" aria-controls="add-member" aria-role="button"><i class="fas fa-plus"/> add member...</a>
<div py:if="is_member(Level.admin, actproject.id)" class="collapse" id="add-member">
<form class="form-row border" method="post" action="add_member">
<input type="hidden" value="${actproject.id}" xname="project_id"/>
<select id="add_who" class="form-control col-sm" name="member_name">
<option value="${p.username}" py:for="p in persons" py:content="p"/>
</select>
<select id="add_level" class="form-control col-sm-3" name="access_level">
<option value="1">Logger</option>
<option value="2">Editor</option>
<option value="3">Supervisor</option>
<option value="4">Admin</option>
<option py:for="l in Level" value="${l.value}" py:content="l.name"/>
</select>
<button type="submit" id="addmember"
class="btn btn-success btn-sm col-sm-1 project-add-member"><i class="fas fa-check"/></button>
Expand Down
40 changes: 29 additions & 11 deletions odmf/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@


class Path(object):
def __init__(self, *path: str, absolute=False):
def __init__(self, *path: str|Path|pathlib.Path, absolute=False):
self.datapath = op.realpath(conf.datafiles)
if path:
if str(path[0]).startswith('/') or absolute:
if type(path[0]) is pathlib.Path:
self.absolute = str(path[0].absolute())
elif type(path[0]) is Path:
self.absolute = path[0].absolute
elif str(path[0]).startswith('/') or absolute:
self.absolute = op.realpath(op.join(*path))
else:
self.absolute = op.realpath(op.join(self.datapath, *path))
Expand All @@ -23,6 +27,9 @@ def __init__(self, *path: str, absolute=False):
self.absolute = self.datapath
self.name = '/'

def __hash__(self):
return hash(self.datapath)

@property
def basename(self)->str:
return op.basename(self.absolute)
Expand All @@ -48,6 +55,9 @@ def __bool__(self):
def __str__(self):
return self.name

def __repr__(self):
return f"odmf.tools.Path('{self.name}')"

def formatsize(self)->str:
size = op.getsize(self.absolute)
unit = 0
Expand Down Expand Up @@ -76,10 +86,13 @@ def __gt__(self, other):
def __add__(self, fn):
return Path(op.join(self.absolute, fn))

def __truediv__(self, fn):
return Path(op.join(self.absolute, fn))

def make(self):
os.makedirs(self.absolute, mode=0o770)

def breadcrumbs(self) -> list[str]:
def breadcrumbs(self) -> list[Path]:
res = [self]
p = op.dirname(self.absolute)
while self.datapath in p:
Expand Down Expand Up @@ -108,7 +121,7 @@ def parent(self) -> Path:
def ishidden(self):
return self.basename.startswith('.') or self.basename == 'index.html'

def listdir(self) -> (typing.List[Path], typing.List[Path]):
def listdir(self, hidden=False) -> (typing.List[Path], typing.List[Path]):
"""
Lists all members of the path in
2 lists:
Expand All @@ -120,17 +133,22 @@ def listdir(self) -> (typing.List[Path], typing.List[Path]):
files = []
directories = []
if self.isdir() and self.islegal():
for fn in os.listdir(self.absolute):
if not fn.startswith('.'):
child = self.child(fn)
if child.isdir():
directories.append(child)
elif child.isfile():
files.append(child)
for child in self.iterdir(hidden):
if child.isdir():
directories.append(child)
elif child.isfile():
files.append(child)
return directories, files
else:
return [], []

def iterdir(self, hidden=False) -> typing.Generator[Path]:
if self.isdir() and self.islegal():
for fn in os.listdir(self.absolute):
if hidden or not fn.startswith('.'):
yield self.child(fn)


def isempty(self) -> bool:
"""
Returns True, if self isdir and has no entries
Expand Down
Loading

0 comments on commit 5b19b91

Please sign in to comment.