Skip to content
This repository was archived by the owner on Feb 21, 2019. It is now read-only.

Commit 76c3ee5

Browse files
authored
Merge a2954d7 into 1683fc4
2 parents 1683fc4 + a2954d7 commit 76c3ee5

File tree

11 files changed

+1060
-130
lines changed

11 files changed

+1060
-130
lines changed

backend/tracim_backend/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,14 @@ class UserCreationFailed(TracimException):
201201
pass
202202

203203

204+
class InconsistentDatabase(TracimException):
205+
pass
206+
207+
208+
class ContentLabelAlreadyUsedHere(TracimException):
209+
pass
210+
211+
204212
class ParentNotFound(NotFound):
205213
pass
206214

backend/tracim_backend/lib/core/content.py

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import re
55
import typing
66
from contextlib import contextmanager
7-
from operator import itemgetter
87

98
import sqlalchemy
109
import transaction
@@ -25,6 +24,7 @@
2524
from tracim_backend.app_models.contents import CONTENT_STATUS
2625
from tracim_backend.app_models.contents import CONTENT_TYPES
2726
from tracim_backend.app_models.contents import ContentType
27+
from tracim_backend.exceptions import ContentLabelAlreadyUsedHere
2828
from tracim_backend.exceptions import ContentNotFound
2929
from tracim_backend.exceptions import ContentTypeNotExist
3030
from tracim_backend.exceptions import EmptyCommentContentNotAllowed
@@ -36,6 +36,7 @@
3636
from tracim_backend.exceptions import UnallowedSubContent
3737
from tracim_backend.exceptions import WorkspacesDoNotMatch
3838
from tracim_backend.lib.core.notifications import NotifierFactory
39+
from tracim_backend.lib.utils.logger import logger
3940
from tracim_backend.lib.utils.translation import DEFAULT_FALLBACK_LANG
4041
from tracim_backend.lib.utils.translation import Translator
4142
from tracim_backend.lib.utils.utils import cmp_to_key
@@ -414,6 +415,91 @@ def get_base_query(
414415
#
415416
# return result
416417

418+
def _is_content_label_is_free(
419+
self,
420+
label: str,
421+
workspace: Workspace,
422+
parent: Content = None,
423+
exclude_content_id: int = None,
424+
) -> bool:
425+
"""
426+
Check if content label is free
427+
:param label: content label
428+
:param workspace: workspace of the content
429+
:param parent: parent of the content
430+
:param exclude_content_id: exclude a specific content_id (useful
431+
to verify other content for content update)
432+
:return: True if content label is available
433+
"""
434+
# INFO - G.M - 2018-09-04 - Method should not be used by special content
435+
# with empty label like comment.
436+
assert label
437+
assert workspace
438+
query = self.get_base_query(workspace)
439+
440+
if parent:
441+
query = query.filter(Content.parent_id == parent.content_id)
442+
else:
443+
query = query.filter(Content.parent_id == None)
444+
445+
if exclude_content_id:
446+
query = query.filter(Content.content_id != exclude_content_id)
447+
query = query.filter(Content.workspace_id == workspace.workspace_id)
448+
449+
nb_content_with_the_label = query.filter(Content.label == label).count()
450+
if nb_content_with_the_label == 0:
451+
return True
452+
elif nb_content_with_the_label == 1:
453+
return False
454+
else:
455+
critical_error_text = 'Something is wrong in the database ! '\
456+
'Content label should be unique ' \
457+
'in a same folder in database' \
458+
'but you have {nb} content with ' \
459+
'label {label} in workspace {workspace_id}'
460+
461+
critical_error_text = critical_error_text.format(
462+
nb=nb_content_with_the_label,
463+
label=label,
464+
workspace_id=workspace.workspace_id,
465+
)
466+
if parent:
467+
critical_error_text = '{text} and parent as content {parent_id}'.format( # nopep8
468+
text=critical_error_text,
469+
parent_id=parent.parent_id
470+
)
471+
logger.critical(self, critical_error_text)
472+
return False
473+
474+
def _verify_content_label_is_free(
475+
self,
476+
label: str,
477+
workspace: Workspace,
478+
parent: Content = None,
479+
exclude_content_id: int = None,
480+
) -> None:
481+
"""
482+
Same as _is_content_label_is_free but raise exception instead of
483+
returning boolean if content label is already used
484+
"""
485+
if not self._is_content_label_is_free(
486+
label,
487+
workspace,
488+
parent,
489+
exclude_content_id
490+
):
491+
text = 'A Content already exist with the same label {label} ' \
492+
' in workspace {workspace_id}'.format(
493+
label=label,
494+
workspace_id=workspace.workspace_id,
495+
)
496+
if parent:
497+
text = '{text} and parent as content {parent_id}'.format(
498+
text=text,
499+
parent_id=parent.parent_id
500+
)
501+
raise ContentLabelAlreadyUsedHere(text)
502+
417503
def create(self, content_type_slug: str, workspace: Workspace, parent: Content=None, label: str = '', filename: str = '', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
418504
# TODO - G.M - 2018-07-16 - raise Exception instead of assert
419505
assert content_type_slug != CONTENT_TYPES.Any_SLUG
@@ -446,9 +532,16 @@ def create(self, content_type_slug: str, workspace: Workspace, parent: Content=N
446532
content_id=workspace.workspace_id,
447533
)
448534
)
535+
if filename:
536+
label = os.path.splitext(filename)[0]
537+
if label:
538+
self._verify_content_label_is_free(
539+
label,
540+
workspace,
541+
parent,
542+
)
449543

450544
content = Content()
451-
452545
if filename:
453546
# INFO - G.M - 2018-07-04 - File_name setting automatically
454547
# set label and file_extension
@@ -593,6 +686,7 @@ def get_one_revision_filepath(self, revision_id: int = None) -> str:
593686
depot_file_path = depot_stored_file._file_path # type: str
594687
return depot_file_path
595688

689+
# TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
596690
def get_one_by_label_and_parent(
597691
self,
598692
content_label: str,
@@ -612,6 +706,8 @@ def get_one_by_label_and_parent(
612706

613707
file_name, file_extension = os.path.splitext(content_label)
614708

709+
# TODO - G.M - 2018-09-04 - If this method is needed, it should be
710+
# rewritten in order to avoid content_type hardcoded code there
615711
return query.filter(
616712
or_(
617713
and_(
@@ -634,6 +730,7 @@ def get_one_by_label_and_parent(
634730
)
635731
).one()
636732

733+
# TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
637734
def get_one_by_label_and_parent_labels(
638735
self,
639736
content_label: str,
@@ -731,6 +828,7 @@ def get_folder_with_workspace_path_labels(
731828

732829
return folder
733830

831+
# TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
734832
def filter_query_for_content_label_as_path(
735833
self,
736834
query: Query,
@@ -759,6 +857,8 @@ def filter_query_for_content_label_as_path(
759857
file_extension_filter = func.lower(Content.file_extension) == \
760858
func.lower(file_extension)
761859

860+
# TODO - G.M - 2018-09-04 - If this method is needed, it should be
861+
# rewritten in order to avoid content_type hardcoded code there
762862
return query.filter(or_(
763863
and_(
764864
Content.type == CONTENT_TYPES.File.slug,
@@ -1190,6 +1290,12 @@ def move(self,
11901290
if new_parent:
11911291
item.workspace = new_parent.workspace
11921292

1293+
self._verify_content_label_is_free(
1294+
item.label,
1295+
item.workspace,
1296+
item.parent,
1297+
exclude_content_id=item.content_id
1298+
)
11931299
item.revision_type = ActionDescription.MOVE
11941300

11951301
def copy(
@@ -1220,6 +1326,7 @@ def copy(
12201326
parent = item.parent
12211327
label = new_label or item.label
12221328

1329+
self._verify_content_label_is_free(label, workspace, parent)
12231330
content = item.copy(parent)
12241331
# INFO - GM - 15-03-2018 - add "copy" revision
12251332
with new_revision(
@@ -1259,12 +1366,20 @@ def move_recursively(self, item: Content,
12591366
return
12601367

12611368
def update_content(self, item: Content, new_label: str, new_content: str=None) -> Content:
1262-
if item.label==new_label and item.description==new_content:
1369+
if item.label == new_label and item.description == new_content:
12631370
# TODO - G.M - 20-03-2018 - Fix internatization for webdav access.
12641371
# Internatization disabled in libcontent for now.
12651372
raise SameValueError('The content did not changed')
12661373
if not new_label:
12671374
raise EmptyLabelNotAllowed()
1375+
1376+
self._verify_content_label_is_free(
1377+
new_label,
1378+
item.workspace,
1379+
item.parent,
1380+
exclude_content_id=item.content_id
1381+
)
1382+
12681383
item.owner = self._user
12691384
item.label = new_label
12701385
item.description = new_content if new_content else item.description # TODO: convert urls into links

backend/tracim_backend/lib/utils/logger.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,10 @@ def warning(self, instance_or_class, message):
4343
Logger.TPL.format(cls=self._txt(instance_or_class), msg=message)
4444
)
4545

46+
def critical(self, instance_or_class, message):
47+
self._logger.critical(
48+
Logger.TPL.format(cls=self._txt(instance_or_class), msg=message)
49+
)
50+
4651

4752
logger = Logger('tracim')

0 commit comments

Comments
 (0)