diff --git a/etiquette/constants.py b/etiquette/constants.py index 592d522..319bf90 100644 --- a/etiquette/constants.py +++ b/etiquette/constants.py @@ -154,6 +154,7 @@ DEFAULT_CONFIGURATION = { 'cache_size_tag': 1000, 'cache_size_user': 200, + 'enable_album_edit': True, 'enable_new_album': True, 'enable_new_bookmark': True, 'enable_new_photo': True, diff --git a/etiquette/decorators.py b/etiquette/decorators.py index 9c7f378..a267054 100644 --- a/etiquette/decorators.py +++ b/etiquette/decorators.py @@ -7,6 +7,30 @@ import warnings from . import jsonify +def required_feature(features): + ''' + Declare that the photodb or object method requires certain 'enable_*' + fields in the config. + ''' + from . import objects + if isinstance(features, str): + features = [features] + + def wrapper(function): + @functools.wraps(function) + def wrapped(self, *args, **kwargs): + if isinstance(self, objects.ObjectBase): + config = self.photodb.config + else: + config = self.config + + if not all(config[key] for key in features): + raise exceptions.FeatureDisabled(function.__name__) + + return function(self, *args, **kwargs) + return wrapped + return wrapper + def required_fields(fields, forbid_whitespace=False): ''' Declare that the endpoint requires certain POST body fields. Without them, diff --git a/etiquette/objects.py b/etiquette/objects.py index fd8f60b..b262463 100644 --- a/etiquette/objects.py +++ b/etiquette/objects.py @@ -307,6 +307,7 @@ class Album(ObjectBase, GroupableMixin): else: return self.id + @decorators.required_feature('enable_album_edit') @decorators.transaction def edit(self, title=None, description=None, *, commit=True): ''' @@ -488,11 +489,9 @@ class Photo(ObjectBase): def _uncache(self): self.photodb.caches['photo'].remove(self.id) + @decorators.required_feature('enable_photo_add_remove_tag') @decorators.transaction def add_tag(self, tag, *, commit=True): - if not self.photodb.config['enable_photo_add_remove_tag']: - raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag') - tag = self.photodb.get_tag(tag) existing = self.has_tag(tag, check_children=False) @@ -577,15 +576,13 @@ class Photo(ObjectBase): return helpers.seconds_to_hms(self.duration) #@decorators.time_me + @decorators.required_feature('enable_photo_generate_thumbnail') @decorators.transaction def generate_thumbnail(self, *, commit=True, **special): ''' special: For videos, you can provide a `timestamp` to take the thumbnail at. ''' - if not self.photodb.config['enable_photo_generate_thumbnail']: - raise exceptions.FeatureDisabled('photo.generate_thumbnail') - hopeful_filepath = self.make_thumbnail_filepath() hopeful_filepath = hopeful_filepath.relative_path #print(hopeful_filepath) @@ -714,14 +711,12 @@ class Photo(ObjectBase): return hopeful_filepath #@decorators.time_me + @decorators.required_feature('enable_photo_reload_metadata') @decorators.transaction def reload_metadata(self, *, commit=True): ''' Load the file's height, width, etc as appropriate for this type of file. ''' - if not self.photodb.config['enable_photo_reload_metadata']: - raise exceptions.FeatureDisabled('photo.reload_metadata') - self.bytes = os.path.getsize(self.real_filepath) self.width = None self.height = None @@ -806,11 +801,9 @@ class Photo(ObjectBase): self.photodb.log.debug('Commit - relocate photo') self.photodb.commit() + @decorators.required_feature('enable_photo_add_remove_tag') @decorators.transaction def remove_tag(self, tag, *, commit=True): - if not self.photodb.config['enable_photo_add_remove_tag']: - raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag') - tag = self.photodb.get_tag(tag) self.photodb.log.debug('Removing tag {t} from photo {p}'.format(t=repr(tag), p=repr(self))) diff --git a/etiquette/photodb.py b/etiquette/photodb.py index 161184f..bb69299 100644 --- a/etiquette/photodb.py +++ b/etiquette/photodb.py @@ -280,6 +280,7 @@ class PDBAlbumMixin: if album.parent() is None: yield album + @decorators.required_feature('enable_new_album') @decorators.transaction def new_album( self, @@ -293,9 +294,6 @@ class PDBAlbumMixin: ''' Create a new album. Photos can be added now or later. ''' - if not self.config['enable_new_album']: - raise exceptions.FeatureDisabled('new_album') - albumid = self.generate_id('albums') title = title or '' description = description or '' @@ -365,11 +363,9 @@ class PDBBookmarkMixin: def get_bookmarks(self): yield from self.get_things(thing_type='bookmark') + @decorators.required_feature('enable_new_bookmark') @decorators.transaction def new_bookmark(self, url, title=None, *, author=None, commit=True): - if not self.config['enable_new_bookmark']: - raise exceptions.FeatureDisabled('new_bookmark') - if not url: raise ValueError('Must provide a URL') @@ -440,6 +436,7 @@ class PDBPhotoMixin: if count <= 0: break + @decorators.required_feature('enable_new_photo') @decorators.transaction def new_photo( self, @@ -461,9 +458,6 @@ class PDBPhotoMixin: Returns the Photo object. ''' - if not self.config['enable_new_photo']: - raise exceptions.FeatureDisabled('new_photo') - filepath = pathclass.Path(filepath) if not filepath.is_file: raise FileNotFoundError(filepath.absolute_path) @@ -954,14 +948,12 @@ class PDBTagMixin: def get_tags(self): yield from self.get_things(thing_type='tag') + @decorators.required_feature('enable_new_tag') @decorators.transaction def new_tag(self, tagname, *, commit=True): ''' Register a new tag and return the Tag object. ''' - if not self.config['enable_new_tag']: - raise exceptions.FeatureDisabled('new_tag') - tagname = self.normalize_tagname(tagname) try: existing_tag = self.get_tag_by_name(tagname) @@ -1080,11 +1072,9 @@ class PDBUserMixin: return objects.User(self, fetch) + @decorators.required_feature('enable_new_user') @decorators.transaction def register_user(self, username, password, commit=True): - if not self.config['enable_new_user']: - raise exceptions.FeatureDisabled('new_user') - if len(username) < self.config['min_username_length']: raise exceptions.UsernameTooShort( username=username,