Give Users a display_name.
This commit is contained in:
		
							parent
							
								
									8562b355ce
								
							
						
					
					
						commit
						21bd211889
					
				
					 9 changed files with 84 additions and 11 deletions
				
			
		|  | @ -60,7 +60,6 @@ Here is a brief overview of the project to help you learn your way around: | |||
| - Add a `Photo.merge` to combine duplicate entries. | ||||
| - Generate thumbnails for vector files without falling victim to bombs. | ||||
| - Allow photos to have nonstandard, orderby-able properties like "release year". How? | ||||
| - When users have '%' or '#', etc. in their username, it is difficult to access their /user/ URL. I would prefer to fix it without simply blacklisting those characters. | ||||
| - Currently, the Jinja templates are having a tangling influence on the backend objects, because Jinja cannot import my other modules like bytestring, but it can access the methods of the objects I pass into the template. As a result, the objects have excess helper methods. Consider making them into Jinja filters instead. Which is also kind of ugly but will move that pollution out of the backend at least. | ||||
| - Perhaps instead of actually deleting objects, they should just have a `deleted` flag, to make easy restoration possible. Also consider regrouping the children of restored Groupables if those children haven't already been reassigned somewhere else. | ||||
| - Add a new table to store permanent history of add/remove of tags on photos, so that accidents or trolling can be reversed. | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ FILENAME_BADCHARS = '\\/:*?<>|"' | |||
| # Note: Setting user_version pragma in init sequence is safe because it only | ||||
| # happens after the out-of-date check occurs, so no chance of accidentally | ||||
| # overwriting it. | ||||
| DATABASE_VERSION = 12 | ||||
| DATABASE_VERSION = 13 | ||||
| DB_INIT = f''' | ||||
| PRAGMA cache_size = 10000; | ||||
| PRAGMA count_changes = OFF; | ||||
|  | @ -54,6 +54,7 @@ CREATE TABLE IF NOT EXISTS users( | |||
|     id TEXT PRIMARY KEY NOT NULL, | ||||
|     username TEXT NOT NULL COLLATE NOCASE, | ||||
|     password BLOB NOT NULL, | ||||
|     display_name TEXT, | ||||
|     created INT | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS index_users_id on users(id); | ||||
|  | @ -291,6 +292,7 @@ DEFAULT_CONFIGURATION = { | |||
|             'new': True, | ||||
|         }, | ||||
|         'user': { | ||||
|             'edit': True, | ||||
|             'login': True, | ||||
|             'new': True, | ||||
|         }, | ||||
|  | @ -305,8 +307,9 @@ DEFAULT_CONFIGURATION = { | |||
|     'user': { | ||||
|         'min_length': 2, | ||||
|         'min_password_length': 6, | ||||
|         'max_display_name_length': 24, | ||||
|         'max_length': 24, | ||||
|         'valid_chars': string.ascii_letters + string.digits + '~!@#$%^*()[]{}:;,.<>/\\-_+=', | ||||
|         'valid_chars': string.ascii_letters + string.digits + '_-', | ||||
|     }, | ||||
| 
 | ||||
|     'digest_exclude_files': [ | ||||
|  |  | |||
|  | @ -147,6 +147,9 @@ class UsernameTooLong(InvalidUsername): | |||
| class UsernameTooShort(InvalidUsername): | ||||
|     error_message = 'Username "{username}" is shorter than minimum of {min_length}.' | ||||
| 
 | ||||
| class DisplayNameTooLong(EtiquetteException): | ||||
|     error_message = 'Display name "{display_name}" is longer than maximum of {max_length}.' | ||||
| 
 | ||||
| class WrongLogin(EtiquetteException): | ||||
|     error_message = 'Wrong username-password combination.' | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,12 +49,16 @@ class ObjectBase: | |||
| 
 | ||||
|     @staticmethod | ||||
|     def normalize_author_id(author_id): | ||||
|         if author_id is None or author_id == '': | ||||
|         if author_id is None: | ||||
|             return None | ||||
| 
 | ||||
|         if not isinstance(author_id, str): | ||||
|             raise TypeError('author_id must be string, not %s' % type(author_id)) | ||||
| 
 | ||||
|         author_id = author_id.strip() | ||||
|         if author_id == '': | ||||
|             return None | ||||
| 
 | ||||
|         return author_id | ||||
| 
 | ||||
|     def get_author(self): | ||||
|  | @ -1476,6 +1480,7 @@ class User(ObjectBase): | |||
|         self.username = db_row['username'] | ||||
|         self.created = db_row['created'] | ||||
|         self.password_hash = db_row['password'] | ||||
|         self._display_name = self.normalize_display_name(db_row['display_name']) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         rep = f'User:{self.id}:{self.username}' | ||||
|  | @ -1485,6 +1490,50 @@ class User(ObjectBase): | |||
|         rep = f'User:{self.username}' | ||||
|         return rep | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def normalize_display_name(display_name, max_length=None): | ||||
|         if display_name is None: | ||||
|             return None | ||||
| 
 | ||||
|         if not isinstance(display_name, str): | ||||
|             raise TypeError('Display Name must be string, not %s' % type(display_name)) | ||||
| 
 | ||||
|         display_name = display_name.strip() | ||||
| 
 | ||||
|         if display_name == '': | ||||
|             return None | ||||
| 
 | ||||
|         if max_length is not None and len(display_name) > max_length: | ||||
|             raise exceptions.DisplayNameTooLong(display_name=display_name, max_length=max_length) | ||||
| 
 | ||||
|         return display_name | ||||
| 
 | ||||
|     @property | ||||
|     def display_name(self): | ||||
|         if self._display_name is None: | ||||
|             return self.username | ||||
|         else: | ||||
|             return self._display_name | ||||
| 
 | ||||
|     @decorators.required_feature('user.edit') | ||||
|     @decorators.transaction | ||||
|     def set_display_name(self, display_name, *, commit=True): | ||||
|         display_name = self.normalize_display_name( | ||||
|             display_name, | ||||
|             max_length=self.photodb.config['user']['max_display_name_length'], | ||||
|         ) | ||||
| 
 | ||||
|         data = { | ||||
|             'id': self.id, | ||||
|             'display_name': display_name, | ||||
|         } | ||||
|         self.photodb.sql_update(table='users', pairs=data, where_key='id') | ||||
| 
 | ||||
|         self._display_name = display_name | ||||
| 
 | ||||
|         if commit: | ||||
|             self.photodb.log.debug('Committing - set display name') | ||||
|             self.photodb.commit() | ||||
| 
 | ||||
| class WarningBag: | ||||
|     def __init__(self): | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|     <a class="header_element" href="/search">Search</a> | ||||
|     <a class="header_element" href="/tags">Tags</a> | ||||
|     {% if session.user %} | ||||
|     <a class="header_element" href="/user/{{session.user.username}}">{{session.user.username}}</a> | ||||
|     <a class="header_element" href="/user/{{session.user.username}}">{{session.user.display_name}}</a> | ||||
|     <a class="header_element" href="/logout" style="flex:0">Logout</a> | ||||
|     {% else %} | ||||
|     <a class="header_element" href="/login">Log in</a> | ||||
|  |  | |||
|  | @ -186,7 +186,7 @@ | |||
|         <li>Filename: {{photo.basename}}</li> | ||||
|         {% set author = photo.get_author() %} | ||||
|         {% if author is not none %} | ||||
|             <li>Author: <a href="/user/{{author.username}}">{{author.username}}</a></li> | ||||
|             <li>Author: <a href="/user/{{author.username}}">{{author.display_name}}</a></li> | ||||
|         {% endif %} | ||||
|         {% if photo.width %} | ||||
|             <li>Dimensions: {{photo.width}}x{{photo.height}} px</li> | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ body, .nice_link | |||
|     <a class="nice_link" href="/albums">Browse albums</a> | ||||
|     <a class="nice_link" href="/bookmarks">Bookmarks</a> | ||||
|     {% if session.user %} | ||||
|     <a class="nice_link" href="/user/{{session.user.username}}">{{session.user.username}}</a> | ||||
|     <a class="nice_link" href="/user/{{session.user.username}}">{{session.user.display_name}}</a> | ||||
|     {% else %} | ||||
|     <a class="nice_link" href="/login">Log in</a> | ||||
|     {% endif %} | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| <html> | ||||
| <head> | ||||
|     {% import "header.html" as header %} | ||||
|     <title>User {{user.username}}</title> | ||||
|     <title>User {{user.display_name}}</title> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | ||||
|     <link rel="stylesheet" href="/static/css/common.css"> | ||||
|  | @ -20,9 +20,9 @@ | |||
| <body> | ||||
|     {{header.make_header(session=session)}} | ||||
|     <div id="content_body"> | ||||
|         <h2>{{user.username}}</h2> | ||||
|         <h2>{{user.display_name}}</h2> | ||||
|         <p>ID: {{user.id}}</p> | ||||
|         <p><a href="/search?author={{user.username}}">Photos by {{user.username}}</a></p> | ||||
|         <p><a href="/search?author={{user.username}}">Photos by {{user.display_name}}</a></p> | ||||
|     </div> | ||||
| </body> | ||||
| 
 | ||||
|  |  | |||
|  | @ -238,6 +238,25 @@ def upgrade_11_to_12(photodb): | |||
|     ''' | ||||
|     photodb.sql.cursor().execute(query) | ||||
| 
 | ||||
| def upgrade_12_to_13(photodb): | ||||
|     ''' | ||||
|     Added display_name column to the User table. | ||||
|     ''' | ||||
|     cur = photodb.sql.cursor() | ||||
|     cur.execute('PRAGMA foreign_keys = OFF') | ||||
|     cur.execute('ALTER TABLE users RENAME TO users_old') | ||||
|     cur.execute(''' | ||||
|     CREATE TABLE users( | ||||
|         id TEXT PRIMARY KEY NOT NULL, | ||||
|         username TEXT NOT NULL COLLATE NOCASE, | ||||
|         password BLOB NOT NULL, | ||||
|         display_name TEXT, | ||||
|         created INT | ||||
|     )''') | ||||
|     cur.execute('INSERT INTO users SELECT id, username, password, NULL, created FROM users_old') | ||||
|     cur.execute('DROP TABLE users_old') | ||||
|     cur.execute('PRAGMA foreign_keys = ON') | ||||
| 
 | ||||
| def upgrade_all(data_directory): | ||||
|     ''' | ||||
|     Given the directory containing a phototagger database, apply all of the | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue