From cd0d450829efa3fb848c275b5ca7f97dc3f712ed Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Fri, 14 Apr 2017 20:29:11 +0200 Subject: [PATCH] Allow deleting books (fixes #119) --- cps/gdriveutils.py | 156 +++++----- cps/static/css/style.css | 1 + cps/templates/book_edit.html | 50 ++- cps/templates/config_edit.html | 4 + cps/templates/user_edit.html | 4 + cps/translations/de/LC_MESSAGES/messages.mo | Bin 299323 -> 300001 bytes cps/translations/de/LC_MESSAGES/messages.po | 256 ++++++++------- cps/translations/es/LC_MESSAGES/messages.po | 250 ++++++++------- cps/translations/fr/LC_MESSAGES/messages.po | 250 ++++++++------- cps/translations/pl/LC_MESSAGES/messages.po | 250 ++++++++------- .../zh_Hans_CN/LC_MESSAGES/messages.po | 250 ++++++++------- cps/ub.py | 12 +- cps/web.py | 293 +++++++++++------- messages.pot | 250 ++++++++------- 14 files changed, 1167 insertions(+), 859 deletions(-) diff --git a/cps/gdriveutils.py b/cps/gdriveutils.py index 09cb7987..550426ca 100644 --- a/cps/gdriveutils.py +++ b/cps/gdriveutils.py @@ -25,8 +25,9 @@ Session = sessionmaker() Session.configure(bind=engine) session = scoped_session(Session) + class GdriveId(Base): - __tablename__='gdrive_ids' + __tablename__ = 'gdrive_ids' id = Column(Integer, primary_key=True) gdrive_id = Column(Integer, unique=True) @@ -36,8 +37,9 @@ class GdriveId(Base): def __repr__(self): return str(self.path) + class PermissionAdded(Base): - __tablename__='permissions_added' + __tablename__ = 'permissions_added' id = Column(Integer, primary_key=True) gdrive_id = Column(Integer, unique=True) @@ -45,15 +47,16 @@ class PermissionAdded(Base): def __repr__(self): return str(self.gdrive_id) + def migrate(): if not engine.dialect.has_table(engine.connect(), "permissions_added"): PermissionAdded.__table__.create(bind = engine) for sql in session.execute("select sql from sqlite_master where type='table'"): if 'CREATE TABLE gdrive_ids' in sql[0]: - currUniqueConstraint='UNIQUE (gdrive_id)' + currUniqueConstraint = 'UNIQUE (gdrive_id)' if currUniqueConstraint in sql[0]: sql=sql[0].replace(currUniqueConstraint, 'UNIQUE (gdrive_id, path)') - sql=sql.replace(GdriveId.__tablename__, GdriveId.__tablename__+ '2') + sql=sql.replace(GdriveId.__tablename__, GdriveId.__tablename__ + '2') session.execute(sql) session.execute('INSERT INTO gdrive_ids2 (id, gdrive_id, path) SELECT id, gdrive_id, path FROM gdrive_ids;') session.commit() @@ -69,9 +72,10 @@ if not os.path.exists(dbpath): migrate() + def getDrive(gauth=None): if not gauth: - gauth=GoogleAuth(settings_file='settings.yaml') + gauth = GoogleAuth(settings_file='settings.yaml') # Try to load saved client credentials gauth.LoadCredentialsFile("gdrive_credentials") if gauth.access_token_expired: @@ -83,126 +87,134 @@ def getDrive(gauth=None): # Save the current credentials to a file return GoogleDrive(gauth) + def getEbooksFolder(drive=None): if not drive: drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - ebooksFolder= "title = '%s' and 'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % config.config_google_drive_folder + ebooksFolder = "title = '%s' and 'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % config.config_google_drive_folder fileList = drive.ListFile({'q': ebooksFolder}).GetList() return fileList[0] + def getEbooksFolderId(drive=None): - storedPathName=session.query(GdriveId).filter(GdriveId.path == '/').first() + storedPathName = session.query(GdriveId).filter(GdriveId.path == '/').first() if storedPathName: return storedPathName.gdrive_id else: - gDriveId=GdriveId() - gDriveId.gdrive_id=getEbooksFolder(drive)['id'] - gDriveId.path='/' + gDriveId = GdriveId() + gDriveId.gdrive_id = getEbooksFolder(drive)['id'] + gDriveId.path = '/' session.merge(gDriveId) session.commit() return + def getFolderInFolder(parentId, folderName, drive=None): if not drive: drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - folder= "title = '%s' and '%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % (folderName.replace("'", "\\'"), parentId) + folder = "title = '%s' and '%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % (folderName.replace("'", "\\'"), parentId) fileList = drive.ListFile({'q': folder}).GetList() return fileList[0] + def getFile(pathId, fileName, drive=None): if not drive: drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - metaDataFile="'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'")) + metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'")) fileList = drive.ListFile({'q': metaDataFile}).GetList() return fileList[0] + def getFolderId(path, drive=None): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - currentFolderId=getEbooksFolderId(drive) - sqlCheckPath=path if path[-1] =='/' else path + '/' - storedPathName=session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first() + currentFolderId = getEbooksFolderId(drive) + sqlCheckPath = path if path[-1] == '/' else path + '/' + storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first() if not storedPathName: - dbChange=False - s=path.split('/') + dbChange = False + s = path.split('/') for i, x in enumerate(s): if len(x) > 0: - currentPath="/".join(s[:i+1]) + currentPath = "/".join(s[:i+1]) if currentPath[-1] != '/': currentPath = currentPath + '/' - storedPathName=session.query(GdriveId).filter(GdriveId.path == currentPath).first() + storedPathName = session.query(GdriveId).filter(GdriveId.path == currentPath).first() if storedPathName: - currentFolderId=storedPathName.gdrive_id + currentFolderId = storedPathName.gdrive_id else: - currentFolderId=getFolderInFolder(currentFolderId, x, drive)['id'] - gDriveId=GdriveId() - gDriveId.gdrive_id=currentFolderId - gDriveId.path=currentPath + currentFolderId = getFolderInFolder(currentFolderId, x, drive)['id'] + gDriveId = GdriveId() + gDriveId.gdrive_id = currentFolderId + gDriveId.path = currentPath session.merge(gDriveId) - dbChange=True + dbChange = True if dbChange: session.commit() else: - currentFolderId=storedPathName.gdrive_id + currentFolderId = storedPathName.gdrive_id return currentFolderId def getFileFromEbooksFolder(drive, path, fileName): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() if path: # sqlCheckPath=path if path[-1] =='/' else path + '/' - folderId=getFolderId(path, drive) + folderId = getFolderId(path, drive) else: - folderId=getEbooksFolderId(drive) + folderId = getEbooksFolderId(drive) return getFile(folderId, fileName, drive) + def copyDriveFileRemote(drive, origin_file_id, copy_title): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() copied_file = {'title': copy_title} try: file_data = drive.auth.service.files().copy( - fileId=origin_file_id, body=copied_file).execute() + fileId = origin_file_id, body=copied_file).execute() return drive.CreateFile({'id': file_data['id']}) except errors.HttpError as error: print ('An error occurred: %s' % error) return None + def downloadFile(drive, path, filename, output): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - f=getFileFromEbooksFolder(drive, path, filename) + f = getFileFromEbooksFolder(drive, path, filename) f.GetContentFile(output) + def backupCalibreDbAndOptionalDownload(drive, f=None): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - metaDataFile="'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId() + metaDataFile = "'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId() fileList = drive.ListFile({'q': metaDataFile}).GetList() - databaseFile=fileList[0] + databaseFile = fileList[0] if f: databaseFile.GetContentFile(f) @@ -212,64 +224,65 @@ def copyToDrive(drive, uploadFile, createRoot, replaceFiles, ignoreFiles=[], parent=None, prevDir=''): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - isInitial=not bool(parent) + isInitial = not bool(parent) if not parent: - parent=getEbooksFolder(drive) + parent = getEbooksFolder(drive) if os.path.isdir(os.path.join(prevDir,uploadFile)): - existingFolder=drive.ListFile({'q' : "title = '%s' and '%s' in parents and trashed = false" % (os.path.basename(uploadFile), parent['id'])}).GetList() + existingFolder = drive.ListFile({'q': "title = '%s' and '%s' in parents and trashed = false" % (os.path.basename(uploadFile), parent['id'])}).GetList() if len(existingFolder) == 0 and (not isInitial or createRoot): - parent = drive.CreateFile({'title': os.path.basename(uploadFile), 'parents' : [{"kind": "drive#fileLink", 'id' : parent['id']}], - "mimeType": "application/vnd.google-apps.folder" }) + parent = drive.CreateFile({'title': os.path.basename(uploadFile), 'parents': [{"kind": "drive#fileLink", 'id': parent['id']}], + "mimeType": "application/vnd.google-apps.folder"}) parent.Upload() else: if (not isInitial or createRoot) and len(existingFolder) > 0: - parent=existingFolder[0] - for f in os.listdir(os.path.join(prevDir,uploadFile)): + parent = existingFolder[0] + for f in os.listdir(os.path.join(prevDir, uploadFile)): if f not in ignoreFiles: - copyToDrive(drive, f, True, replaceFiles, ignoreFiles, parent, os.path.join(prevDir,uploadFile)) + copyToDrive(drive, f, True, replaceFiles, ignoreFiles, parent, os.path.join(prevDir, uploadFile)) else: if os.path.basename(uploadFile) not in ignoreFiles: - existingFiles=drive.ListFile({'q' : "title = '%s' and '%s' in parents and trashed = false" % (os.path.basename(uploadFile), parent['id'])}).GetList() + existingFiles = drive.ListFile({'q': "title = '%s' and '%s' in parents and trashed = false" % (os.path.basename(uploadFile), parent['id'])}).GetList() if len(existingFiles) > 0: - driveFile=existingFiles[0] + driveFile = existingFiles[0] else: - driveFile = drive.CreateFile({'title': os.path.basename(uploadFile), 'parents' : [{"kind": "drive#fileLink", 'id' : parent['id']}], }) - driveFile.SetContentFile(os.path.join(prevDir,uploadFile)) + driveFile = drive.CreateFile({'title': os.path.basename(uploadFile), 'parents': [{"kind":"drive#fileLink", 'id': parent['id']}], }) + driveFile.SetContentFile(os.path.join(prevDir, uploadFile)) driveFile.Upload() + def uploadFileToEbooksFolder(drive, destFile, f): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() - parent=getEbooksFolder(drive) - splitDir=destFile.split('/') + parent = getEbooksFolder(drive) + splitDir = destFile.split('/') for i, x in enumerate(splitDir): if i == len(splitDir)-1: - existingFiles=drive.ListFile({'q' : "title = '%s' and '%s' in parents and trashed = false" % (x, parent['id'])}).GetList() + existingFiles = drive.ListFile({'q': "title = '%s' and '%s' in parents and trashed = false" % (x, parent['id'])}).GetList() if len(existingFiles) > 0: - driveFile=existingFiles[0] + driveFile = existingFiles[0] else: - driveFile = drive.CreateFile({'title': x, 'parents' : [{"kind": "drive#fileLink", 'id' : parent['id']}], }) + driveFile = drive.CreateFile({'title': x, 'parents': [{"kind": "drive#fileLink", 'id': parent['id']}],}) driveFile.SetContentFile(f) driveFile.Upload() else: - existingFolder=drive.ListFile({'q' : "title = '%s' and '%s' in parents and trashed = false" % (x, parent['id'])}).GetList() + existingFolder = drive.ListFile({'q': "title = '%s' and '%s' in parents and trashed = false" % (x, parent['id'])}).GetList() if len(existingFolder) == 0: - parent = drive.CreateFile({'title': x, 'parents' : [{"kind": "drive#fileLink", 'id' : parent['id']}], - "mimeType": "application/vnd.google-apps.folder" }) + parent = drive.CreateFile({'title': x, 'parents': [{"kind": "drive#fileLink", 'id': parent['id']}], + "mimeType": "application/vnd.google-apps.folder"}) parent.Upload() else: - parent=existingFolder[0] + parent = existingFolder[0] def watchChange(drive, channel_id, channel_type, channel_address, channel_token=None, expiration=None): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() # Watch for all changes to a user's Drive. @@ -286,15 +299,16 @@ def watchChange(drive, channel_id, channel_type, channel_address, # Raises: # apiclient.errors.HttpError: if http request to create channel fails. body = { - 'id': channel_id, - 'type': channel_type, - 'address': channel_address + 'id': channel_id, + 'type': channel_type, + 'address': channel_address } if channel_token: body['token'] = channel_token if expiration: body['expiration'] = expiration - return drive.auth.service.changes().watch(body=body).execute() + return drive.auth.service.changes().watch(body=body).execute() + def watchFile(drive, file_id, channel_id, channel_type, channel_address, channel_token=None, expiration=None): @@ -314,7 +328,7 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address, apiclient.errors.HttpError: if http request to create channel fails. """ if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() @@ -329,6 +343,7 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address, body['expiration'] = expiration return drive.auth.service.files().watch(fileId=file_id, body=body).execute() + def stopChannel(drive, channel_id, resource_id): """Stop watching to a specific channel. Args: @@ -339,19 +354,20 @@ def stopChannel(drive, channel_id, resource_id): apiclient.errors.HttpError: if http request to create channel fails. """ if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() # service=drive.auth.service body = { - 'id': channel_id, - 'resourceId': resource_id + 'id': channel_id, + 'resourceId': resource_id } return drive.auth.service.channels().stop(body=body).execute() + def getChangeById (drive, change_id): if not drive: - drive=getDrive() + drive = getDrive() if drive.auth.access_token_expired: drive.auth.Refresh() # Print a single Change resource information. diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 921a35ff..e136fedf 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -38,6 +38,7 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te } .navbar-default .navbar-toggle .icon-bar {background-color: #000;} .navbar-default .navbar-toggle {border-color: #000;} +.cover { margin-bottom: 10px;} .btn-file {position: relative; overflow: hidden;} .btn-file input[type=file] {position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; font-size: 100px; text-align: right; filter: alpha(opacity=0); opacity: 0; outline: none; background: white; cursor: inherit; display: block;} diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index b94ddbb2..594aff22 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -1,17 +1,23 @@ {% extends "layout.html" %} {% block body %} {% if book %} -
-
- {% if book.has_cover %} - - {% else %} - - {% endif %} +
+ +
+
+ {% if book.has_cover %} + + {% else %} + + {% endif %} +
+{% if g.user.role_delete_books() %} +
+ +
+{% endif %}
-
-
- +
@@ -107,9 +113,31 @@ {{_('Get metadata')}} {{_('Back')}} - +
+ + +{% endif %} +{% if g.user.role_delete_books() %} + {% endif %} +