4
4
import re
5
5
import typing
6
6
from contextlib import contextmanager
7
- from operator import itemgetter
8
7
9
8
import sqlalchemy
10
9
import transaction
25
24
from tracim_backend .app_models .contents import CONTENT_STATUS
26
25
from tracim_backend .app_models .contents import CONTENT_TYPES
27
26
from tracim_backend .app_models .contents import ContentType
27
+ from tracim_backend .exceptions import ContentLabelAlreadyUsedHere
28
28
from tracim_backend .exceptions import ContentNotFound
29
29
from tracim_backend .exceptions import ContentTypeNotExist
30
30
from tracim_backend .exceptions import EmptyCommentContentNotAllowed
36
36
from tracim_backend .exceptions import UnallowedSubContent
37
37
from tracim_backend .exceptions import WorkspacesDoNotMatch
38
38
from tracim_backend .lib .core .notifications import NotifierFactory
39
+ from tracim_backend .lib .utils .logger import logger
39
40
from tracim_backend .lib .utils .translation import DEFAULT_FALLBACK_LANG
40
41
from tracim_backend .lib .utils .translation import Translator
41
42
from tracim_backend .lib .utils .utils import cmp_to_key
@@ -414,6 +415,91 @@ def get_base_query(
414
415
#
415
416
# return result
416
417
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
+
417
503
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 :
418
504
# TODO - G.M - 2018-07-16 - raise Exception instead of assert
419
505
assert content_type_slug != CONTENT_TYPES .Any_SLUG
@@ -446,9 +532,16 @@ def create(self, content_type_slug: str, workspace: Workspace, parent: Content=N
446
532
content_id = workspace .workspace_id ,
447
533
)
448
534
)
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
+ )
449
543
450
544
content = Content ()
451
-
452
545
if filename :
453
546
# INFO - G.M - 2018-07-04 - File_name setting automatically
454
547
# set label and file_extension
@@ -593,6 +686,7 @@ def get_one_revision_filepath(self, revision_id: int = None) -> str:
593
686
depot_file_path = depot_stored_file ._file_path # type: str
594
687
return depot_file_path
595
688
689
+ # TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
596
690
def get_one_by_label_and_parent (
597
691
self ,
598
692
content_label : str ,
@@ -612,6 +706,8 @@ def get_one_by_label_and_parent(
612
706
613
707
file_name , file_extension = os .path .splitext (content_label )
614
708
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
615
711
return query .filter (
616
712
or_ (
617
713
and_ (
@@ -634,6 +730,7 @@ def get_one_by_label_and_parent(
634
730
)
635
731
).one ()
636
732
733
+ # TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
637
734
def get_one_by_label_and_parent_labels (
638
735
self ,
639
736
content_label : str ,
@@ -731,6 +828,7 @@ def get_folder_with_workspace_path_labels(
731
828
732
829
return folder
733
830
831
+ # TODO - G.M - 2018-09-04 - [Cleanup] Is this method already needed ?
734
832
def filter_query_for_content_label_as_path (
735
833
self ,
736
834
query : Query ,
@@ -759,6 +857,8 @@ def filter_query_for_content_label_as_path(
759
857
file_extension_filter = func .lower (Content .file_extension ) == \
760
858
func .lower (file_extension )
761
859
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
762
862
return query .filter (or_ (
763
863
and_ (
764
864
Content .type == CONTENT_TYPES .File .slug ,
@@ -1190,6 +1290,12 @@ def move(self,
1190
1290
if new_parent :
1191
1291
item .workspace = new_parent .workspace
1192
1292
1293
+ self ._verify_content_label_is_free (
1294
+ item .label ,
1295
+ item .workspace ,
1296
+ item .parent ,
1297
+ exclude_content_id = item .content_id
1298
+ )
1193
1299
item .revision_type = ActionDescription .MOVE
1194
1300
1195
1301
def copy (
@@ -1220,6 +1326,7 @@ def copy(
1220
1326
parent = item .parent
1221
1327
label = new_label or item .label
1222
1328
1329
+ self ._verify_content_label_is_free (label , workspace , parent )
1223
1330
content = item .copy (parent )
1224
1331
# INFO - GM - 15-03-2018 - add "copy" revision
1225
1332
with new_revision (
@@ -1259,12 +1366,20 @@ def move_recursively(self, item: Content,
1259
1366
return
1260
1367
1261
1368
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 :
1263
1370
# TODO - G.M - 20-03-2018 - Fix internatization for webdav access.
1264
1371
# Internatization disabled in libcontent for now.
1265
1372
raise SameValueError ('The content did not changed' )
1266
1373
if not new_label :
1267
1374
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
+
1268
1383
item .owner = self ._user
1269
1384
item .label = new_label
1270
1385
item .description = new_content if new_content else item .description # TODO: convert urls into links
0 commit comments