diff --git a/static/sass/main.scss b/static/sass/main.scss index ff3c0fb..ba982a0 100644 --- a/static/sass/main.scss +++ b/static/sass/main.scss @@ -10,7 +10,7 @@ } } -.asset-thumbnail { +.p-asset-card--thumbnail { max-height: 100px; min-height: 100px; } @@ -40,4 +40,10 @@ .p-filter-panel-section__chips-toggle { padding-top: 6px; margin-bottom: 18px; +} + +.p-asset-card { + padding:0.75rem; + background:#f7f7f7; + margin-top:2rem; } \ No newline at end of file diff --git a/templates/_asset-card-image.html b/templates/_asset-card-image.html new file mode 100644 index 0000000..b7a78c9 --- /dev/null +++ b/templates/_asset-card-image.html @@ -0,0 +1,51 @@ +
+
+ {% if asset.data.image %} + + + + {% endif %} +
+
+ {% if asset.name %} +

{{ asset.name }}

+ {% else %} +

{{ asset.file_path.split('.')[0] }}

+ {% endif %} +

File type: .{{ asset.file_path.split('.', 1)[1] }}

+

Image size: {% if asset.data.width and asset.data.height %}{{asset.data.width}} x {{asset.data.height}}{% endif %}

+

+ Tags: + {% for tag in asset.tags %} + {{ tag.name }}{% if not loop.last %},{% endif %} + {% endfor %} +

+

+ Date created: {{ asset.created.strftime('%d %B %Y') }} +

+
+
+

+ Full asset details › +

+
+
+

+ + + Edit asset + +

+
+
diff --git a/templates/_asset-card.html b/templates/_asset-card.html new file mode 100644 index 0000000..453df9c --- /dev/null +++ b/templates/_asset-card.html @@ -0,0 +1,48 @@ +
+
+ {% if asset.asset_type == "whitepaper" %} + + {% else %} + + {% endif %} +
+
+ {% if asset.name %} +

{{ asset.name }}

+ {% else %} +

{{ asset.file_path.split('.')[0] }}

+ {% endif %} +

File type: .{{ asset.file_path.split('.', 1)[1] }}

+

Asset type: {{ asset.asset_type }}

+

+ Tags: + {% for tag in asset.tags %} + {{ tag.name }}{% if not loop.last %},{% endif %} + {% endfor %} +

+

+ Date created: {{ asset.created.strftime('%d %B %Y') }} +

+
+
+

+ Full asset details › +

+
+
+

+ + + Edit asset + +

+
+
diff --git a/templates/_asset-list.html b/templates/_asset-list.html index 0f5974d..f561c60 100644 --- a/templates/_asset-list.html +++ b/templates/_asset-list.html @@ -1,3 +1,7 @@ {% for asset in assets %} - {% include "_asset.html" %} + {% if asset.data.image %} + {% include "_asset-card-image.html" %} + {% else %} + {% include "_asset-card.html" %} + {% endif %} {% endfor %} \ No newline at end of file diff --git a/templates/_asset.html b/templates/_asset.html deleted file mode 100644 index 4b1e1fa..0000000 --- a/templates/_asset.html +++ /dev/null @@ -1,57 +0,0 @@ -
- {% if asset.data.image %} - - - -
- {% endif %} -
-

{{ asset.file_path }}

-

- - Tags: {% for tag in asset.tags %} - - {{tag.name}} - - {% endfor %} - -

-

- Created: {{ asset.created.strftime('%d/%m/%Y, %H:%M') }} -

-

- Copy: - - {% if asset.data.image %} - - {% endif %} -

-

- - - Edit - - {% if asset.deprecated %} - - Deprecated - - {% endif %} -

-
-
diff --git a/templates/create-update.html b/templates/create-update.html index fb9c164..4091f9d 100644 --- a/templates/create-update.html +++ b/templates/create-update.html @@ -21,7 +21,7 @@

Upload asset

{% if is_update %} -

{{ asset.file_path}}

+

{{ asset.file_path}}

@@ -62,7 +62,7 @@

Upload asset

Use any relevant naming conventions for your asset.

- +
@@ -204,7 +204,7 @@

Authors

Link to the asset on Google Drive, if applicable.

- +
diff --git a/templates/created.html b/templates/created.html index 07c2b22..0ec2442 100644 --- a/templates/created.html +++ b/templates/created.html @@ -13,14 +13,14 @@

Failed to upload {{failed|length}} assets

{% endif %} {% if assets %} -
+

{{assets|length}} assets created

{% include "_asset-list.html" %}
{% endif %} {% if existing %} -
+
{% set assets = existing %}

{{existing|length}} existing assets

{% include "_asset-list.html" %} diff --git a/templates/details.html b/templates/details.html new file mode 100644 index 0000000..9f39c1b --- /dev/null +++ b/templates/details.html @@ -0,0 +1,58 @@ +{% extends "_layout.html" %} + +{% block title %}Asset details{% endblock %} + +{% block content %} +
+
+
+

Asset details

+ {% if asset.data.image %} + + + + {% endif %} +

File path:{{ asset.file_path }}

+

Name: {{ asset.name }}

+

File type: .{{ asset.file_path.split('.', 1)[1] }}

+

Asset type: {{ asset.asset_type }}

+

Image size: {% if asset.data.width and asset.data.height %}{{asset.data.width}} x {{asset.data.height}}{% else %}Unknown{% endif %}

+

+ Products: + {% for product in asset.products %} + {{ product.name }}{% if not loop.last %},{% endif %} + {% endfor %} +

+

+ Tags: + {% for tag in asset.tags %} + {{ tag.name }}{% if not loop.last %},{% endif %} + {% endfor %} +

+

Language: {{ asset.language }}

+

Salesforce campaign ID: {{ asset.salesforce_campaign_id }}

+

Google drive link: {{ asset.google_drive_link }}

+

Author email: {{ asset.author_email }}

+

Deprecated: {{ asset.deprecated }}

+

Created: {{ asset.created.strftime('%d %B %Y') }}

+

Updated: {{ asset.updated.strftime('%d %B %Y') }}

+

+ + + Edit asset + +

+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 9db02a0..d353c41 100644 --- a/templates/index.html +++ b/templates/index.html @@ -34,7 +34,7 @@
-
+
{% if assets %} {% include "_asset-list.html" %} {% elif request.values.get("q", None) %} diff --git a/templates/update.html b/templates/update.html deleted file mode 100644 index 9c7561a..0000000 --- a/templates/update.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "_layout.html" %} - -{% block title %}Update tags{% endblock %} - -{% block content %} -
-
-
-

{{ asset.file_path or request.args.get("file-path")}}

- {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} -
-

- {{message}} -

-
- {% endfor %} - {% endif %} - {% endwith %} - {% if asset %} -
- - {% if asset.data.image %} -
- - - -
- {% endif %} - - - - -
- -
-
- {% endif %} -
-
-
-{% endblock %} \ No newline at end of file diff --git a/webapp/routes.py b/webapp/routes.py index 353e91c..41c80ec 100644 --- a/webapp/routes.py +++ b/webapp/routes.py @@ -114,6 +114,7 @@ def create(): "first_name": author_first_name, "last_name": author_last_name, } + name = flask.request.form.get("name", "") for asset_file in flask.request.files.getlist("assets"): try: @@ -123,6 +124,7 @@ def create(): asset = asset_service.create_asset( file_content=content, friendly_name=filename, + name=name, optimize=optimize, tags=tags, products=products, @@ -176,19 +178,21 @@ def update(): google_drive_link = request.form.get("google_drive_link", "") salesforce_campaign_id = request.form.get("salesforce_campaign_id", "") language = request.form.get("language", "") - author_email = flask.request.form.get("author_email", "") - author_first_name = flask.request.form.get("author_first_name", "") - author_last_name = flask.request.form.get("author_last_name", "") + author_email = request.form.get("author_email", "") + author_first_name = request.form.get("author_first_name", "") + author_last_name = request.form.get("author_last_name", "") author = { "email": author_email, "first_name": author_first_name, "last_name": author_last_name, } + name = request.form.get("name", "") try: asset = asset_service.update_asset( file_path=file_path, tags=tags, + name=name, deprecated=deprecated, products=products, asset_type=asset_type, @@ -200,12 +204,25 @@ def update(): flask.flash("Asset updated", "positive") except AssetNotFound: flask.flash("Asset not found", "negative") + return flask.redirect("/manager/details?file-path=" + file_path) return flask.render_template( "create-update.html", products_list=products_list, asset=asset ) +@ui_blueprint.route("/details", methods=["GET"]) +@login_required +def details(): + file_path = request.args.get("file-path") + + asset = asset_service.find_asset(file_path) + if not asset: + flask.flash("Asset not found", "negative") + + return flask.render_template("details.html", asset=asset) + + # API Routes # === diff --git a/webapp/services.py b/webapp/services.py index 9f11e2f..10eac5e 100644 --- a/webapp/services.py +++ b/webapp/services.py @@ -1,11 +1,12 @@ # System import imghdr -from base64 import b64decode +from base64 import b64decode, b64encode from datetime import datetime, timezone +from io import BytesIO # Packages -from wand.image import Image from typing import List +from PIL import Image as PillowImage # Local from webapp.database import db_session @@ -80,6 +81,7 @@ def create_asset( file_content, friendly_name: str, optimize: bool, + name: str = None, url_path=None, tags: List[str] = [], products: List[str] = [], @@ -98,7 +100,11 @@ def create_asset( friendly_name = clean_unicode(friendly_name) url_path = clean_unicode(url_path) - encoded_file_content = (b64decode(file_content),) + # First we ensure it is b64 encoded + encoded_file_content = b64encode(file_content) + # Then we can decode it + decoded_file_content = b64decode(encoded_file_content) + if imghdr.what(None, h=file_content) is not None or is_svg( file_content ): @@ -107,29 +113,33 @@ def create_asset( # As it's not an image, there is no need for optimization data["optimized"] = False + if data.get("image"): + try: + # Use Pillow to open the image and get dimensions + with PillowImage.open(BytesIO(decoded_file_content)) as img: + data["width"] = img.width + data["height"] = img.height + except Exception as e: + print(f"Error opening image with Pillow: {e}") + data["width"] = None + data["height"] = None + # Try to optimize the asset if it's an image if data.get("image") and optimize: try: - image = ImageProcessor(encoded_file_content) + image = ImageProcessor(decoded_file_content) image.optimize(allow_svg_errors=True) - encoded_file_content = image.data + decoded_file_content = image.data data["optimized"] = True except Exception: # If optimisation failed, just don't bother optimising data["optimized"] = False + if not url_path: url_path = file_manager.generate_asset_path( file_content, friendly_name ) - if data.get("image"): - try: - with Image(blob=encoded_file_content) as image_info: - data["width"] = image_info.width - data["height"] = image_info.height - except Exception: - # Just don't worry if image reading fails - pass asset = ( db_session.query(Asset) .filter(Asset.file_path == url_path) @@ -155,7 +165,7 @@ def create_asset( # Save file info in Postgres asset = Asset( file_path=url_path, - name=friendly_name, + name=name, data=data, tags=tags, created=datetime.now(tz=timezone.utc), @@ -248,6 +258,7 @@ def normalize_tag_name(self, tag_name): def update_asset( self, file_path: str, + name: str = None, tags: List[str] = [], deprecated: bool = None, products: List[str] = [], @@ -268,6 +279,8 @@ def update_asset( if tags: tags = self.create_tags_if_not_exist(tags) asset.tags = tags + if name: + asset.name = name if products: products = self.create_products_if_not_exists(products) asset.products = products diff --git a/yarn.lock b/yarn.lock index b914ccf..4dee3ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1654,7 +1654,7 @@ util@^0.10.3: vanilla-framework@4.16.0: version "4.16.0" - resolved "https://registry.npmjs.org/vanilla-framework/-/vanilla-framework-4.16.0.tgz" + resolved "https://registry.yarnpkg.com/vanilla-framework/-/vanilla-framework-4.16.0.tgz#54e7a51e073de043d45a7bac37ffaa4f4351ac4a" integrity sha512-LrxZLiNOm0cTpG++1X4MMy2efg8Xhc08JUAwNAybeSQ5FaaBGiAodbV1Fx3QvJxlaPFQqsjOdT6uZDwuOD7YJg== verbalize@^0.1.2: