Skip to content

Commit

Permalink
v2-Beta13 release
Browse files Browse the repository at this point in the history
  • Loading branch information
kid1194 committed Aug 23, 2023
1 parent f8285cc commit e719795
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 44 deletions.
54 changes: 30 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ It supports RTL layout and dark mode out of the box.

---

**If you would like to buy me a cup of coffee 😎**

[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZWZ4TK5PG7G8W)
<p align="center">
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_1.png?raw=true" alt="Better Attach Control"/>
</p>
<p align="center">
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_2.png?raw=true" alt="Better Attach Control"/>
</p>
<p align="center">
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_3.png?raw=true" alt="Better Attach Control"/>
</p>

---

Expand Down Expand Up @@ -51,7 +57,7 @@ It supports RTL layout and dark mode out of the box.

### Setup

⚠️ *Do not forget to replace [sitename] with the name of your site in all commands.* ⚠️
⚠️ **Do not forget to replace [sitename] with the name of your site in all commands.** ⚠️

#### Install
1. Go to bench directory
Expand Down Expand Up @@ -155,39 +161,39 @@ bench restart
4. Create an **Attach** or **Attach Image** field or edit an existing custom field
5. Inside the field's **Options** property, add the options you want as a JSON string.

Ex: `{"allowed_file_types": [".jpg", ".png", ".gif"]}`
Ex: ```{"allowed_file_types": [".jpg", ".png", ".gif"]}```

##### Remember
##### ⚠️ Remember
You can't modify the original fields of a doctype, so create a new field or clone and modify the entire doctype.

---

### Available Field Options
| Option | Description |
| :--- | :--- |
| `dialog_title` 🔴 | Upload dialog title to be displayed (✴️Frappe >= v14.0.0).<br/><br/>- Example: `"Upload Images"`<br/>- Default: `"Upload"` |
| `upload_notes` | Upload text to be displayed.<br/><br/>- Example: `"Only images and videos, with maximum size of 2MB, are allowed to be uploaded"`<br/>- Default: `""` |
| `disable_file_browser` 🔴 | Disable file browser uploads.<br/><br/>⚠️ *(File browser is always disabled in Web Form)*<br/><br/>- Default: `false` |
| `allow_multiple` | Allow multiple uploads.<br/><br/>⚠️ *(Field value is a JSON array of files url)*<br/><br/>- Default: `false` |
| `max_file_size` | Maximum file size (in bytes) that is allowed to be uploaded.<br/><br/>- Example: `2048` for `2KB`<br/>- Default: `Value of maximum file size in Frappe's settings` |
| `allowed_file_types` | Array of allowed file types (mimes) or extensions to upload. Prefix escaped RegExp string types with **$**.<br/><br/>⚠️ *(File extensions must have a leading dot ".")*<br/>⚠️ *(RegExp string types will not be used to in HTML accept attribute)*<br/><br/>- Example: `["image/*", "video/*", ".pdf", ".doc", "$audio\/([a-z]+)"]`<br/>- Default: `null` or `["image/*"]` |
| `max_number_of_files` | Maximum number of files allowed to be uploaded if multiple upload is allowed.<br/><br/>⚠️ *(Bypassing the maximum attachments of doctype might not work)*<br/><br/>- Example: `4`<br/>- Default: `Value of maximum attachments set for the doctype` |
| `crop_image_aspect_ratio` | Crop aspect ratio for images (✴️Frappe >= v14.0.0).<br/><br/>- Example: `1` or `16/9` or `4/3`<br/>- Default: `null` |
| `as_public` | Force uploads to be saved in public folder by default.<br/><br/>- Default: `false` |
| `allowed_filename` 🔴 | Only allow files that match a specific file name to be uploaded.<br/><br/>- Example: (String)`"picture.png"` or (RegExp String)`"/picture\-([0-9]+)\.png/"`<br/>- Default: `null` |
| `allow_reload` | Allow reloading attachments (✴️Frappe >= v13.0.0).<br/><br/>ℹ️ Affect the visibility of the reload button.ℹ️<br/><br/>- Default: `true` |
| `allow_remove` | Allow removing and clearing attachments.<br/><br/>ℹ️ Affect the visibility of the remove and clear buttons.ℹ️<br/><br/>- Default: `true` |
| **dialog_title** 🔴 | Upload dialog title to be displayed ️(🔶Frappe >= v14.0.0).<br /><br />🔹Example: **"Upload Images"**<br />🔹Default: **"Upload"** |
| **upload_notes** | Upload text to be displayed.<br /><br />🔹Example: **"Only images and videos, with maximum size of 2MB, are allowed to be uploaded"**<br />🔹Default: **""** |
| **disable_file_browser** 🔴 | Disable file browser uploads.<br /><br />⚠️ *(File browser is always disabled in Web Form)*<br /><br />🔹Default: **false** |
| **allow_multiple** | Allow multiple uploads.<br /><br />⚠️ *(Field value is a JSON array of files url)*<br /><br />🔹Default: **false** |
| **max_file_size** | Maximum file size (in bytes) that is allowed to be uploaded.<br /><br />🔹Example: **2048** for **2KB**<br />🔹Default: **Value of maximum file size in Frappe's settings** |
| **allowed_file_types** | Array of allowed file types (mimes) or extensions to upload. Prefix escaped RegExp string types with **$**.<br /><br />⚠️ *(File extensions must have a leading dot ".")*<br />⚠️ *(RegExp string types will not be used to in HTML accept attribute)*<br /><br />🔹Example: **["image/*", "video/*", ".pdf", ".doc", "$audio\/([a-z]+)"]**<br />🔹Default: **null** or **["image/*"]** |
| **max_number_of_files** | Maximum number of files allowed to be uploaded if multiple upload is allowed.<br /><br />⚠️ *(Bypassing the maximum attachments of doctype might not work)*<br /><br />🔹Example: **4**<br />🔹Default: **Value of maximum attachments set for the doctype** |
| **crop_image_aspect_ratio** | Crop aspect ratio for images (🔶Frappe >= v14.0.0).<br /><br />🔹Example: **1** or **16/9** or **4/3**<br />🔹Default: **null** |
| **as_public** | Force uploads to be saved in public folder by default.<br /><br />🔹Default: **false** |
| **allowed_filename** 🔴 | Only allow files that match a specific file name to be uploaded.<br /><br />🔹Example: (String)**"picture.png"** or (RegExp String)**"/picture\-([0-9]+)\.png/"**<br />🔹Default: **null** |
| **allow_reload** | Allow reloading attachments (🔶Frappe >= v13.0.0).<br /><br />🔶 Affect the visibility of the reload button.🔶<br /><br />🔹Default: **true** |
| **allow_remove** | Allow removing and clearing attachments.<br /><br />🔶 Affect the visibility of the remove and clear buttons.🔶<br /><br />🔹Default: **true** |

---

### Available JavaScript Methods
| Method | Description |
| :--- | :--- |
| `enable_reload()` | Allow reloading attachments and show the reload button (Frappe >= v13.0.0). |
| `disable_reload()` | Deny reloading attachments and hide reload button (Frappe >= v13.0.0). |
| `enable_remove()` | Allow removing and clearing attachments and show the clear and remove buttons. |
| `disable_remove()` | Deny removing and clearing attachments and hide the clear and remove buttons. |
| `set_options(JSON)` | Set or change the plugin current options. |
| **enable_reload()** | Allow reloading attachments and show the reload button (🔶Frappe >= v13.0.0). |
| **disable_reload()** | Deny reloading attachments and hide reload button (🔶Frappe >= v13.0.0). |
| **enable_remove()** | Allow removing and clearing attachments and show the clear and remove buttons. |
| **disable_remove()** | Deny removing and clearing attachments and hide the clear and remove buttons. |
| **set_options(JSON)** | Set or change the plugin current options. |

---

Expand All @@ -203,4 +209,4 @@ If you find bug in the plugin, please create a [bug report](https://github.com/k
---

### License
This repository has been released under the [MIT License](https://github.com/kid1194/frappe-better-attach-control/blob/main/LICENSE).
This repository has been released under the [MIT License](https://github.com/kid1194/frappe-better-attach-control/blob/main/LICENSE).
162 changes: 142 additions & 20 deletions frappe_better_attach_control/api/attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@


import frappe
from frappe import (
_,
is_whitelisted,
__version__ as frappe_version
)
from frappe.utils import cint

from .common import (
parse_json_if_valid,
Expand All @@ -13,6 +19,111 @@


_FILE_DOCTYPE_ = "File"
# For version > 13
_ALLOWED_MIMETYPES_ = (
"image/png",
"image/jpeg",
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.spreadsheet",
"text/plain",
)


@frappe.whitelist(allow_guest=True)
def upload_file():
version = int(frappe_version.split('.')[0])

user = None
ignore_permissions = False

if version > 12:
if frappe.session.user == "Guest":
if frappe.get_system_settings("allow_guests_to_upload_files"):
ignore_permissions = True
else:
raise frappe.PermissionError
else:
user = frappe.get_doc("User", frappe.session.user)
ignore_permissions = False

files = frappe.request.files
is_private = frappe.form_dict.is_private
doctype = frappe.form_dict.doctype
docname = frappe.form_dict.docname
fieldname = frappe.form_dict.fieldname
file_url = frappe.form_dict.file_url
folder = frappe.form_dict.folder or "Home"
method = frappe.form_dict.method
filename = None
optimize = False
content = None

if version > 13:
filename = frappe.form_dict.file_name
optimize = frappe.form_dict.optimize

if version > 12:
import mimetypes

if "file" in files:
file = files["file"]
content = file.stream.read()
filename = file.filename

if version > 13:
content_type = mimetypes.guess_type(filename)[0]
if optimize and content_type.startswith("image/"):
args = {"content": content, "content_type": content_type}
if frappe.form_dict.max_width:
args["max_width"] = int(frappe.form_dict.max_width)
if frappe.form_dict.max_height:
args["max_height"] = int(frappe.form_dict.max_height)

from frappe.utils.image import optimize_image
content = optimize_image(**args)

frappe.local.uploaded_file = content
frappe.local.uploaded_filename = filename

if version > 13:
if not file_url and content is not None and (
frappe.session.user == "Guest" or (user and not user.has_desk_access())
):
filetype = mimetypes.guess_type(filename)[0]
if filetype not in _ALLOWED_MIMETYPES_:
frappe.throw(_("You can only upload JPG, PNG, PDF, TXT or Microsoft documents."))

elif version > 12:
if not file_url and frappe.session.user == "Guest" or (user and not user.has_desk_access()):
filetype = mimetypes.guess_type(filename)[0]

if method:
method = frappe.get_attr(method)
is_whitelisted(method)
return method()
else:
ret = frappe.get_doc({
"doctype": _FILE_DOCTYPE_,
"attached_to_doctype": doctype,
"attached_to_name": docname,
"attached_to_field": fieldname,
"folder": folder,
"file_name": filename,
"file_url": file_url,
"is_private": cint(is_private),
"content": content,
})
if version > 12:
ret.save(ignore_permissions=ignore_permissions)
else:
ret.save()

return ret


@frappe.whitelist(methods=["POST"], allow_guest=True)
Expand All @@ -30,6 +141,9 @@ def remove_files(files):
file_urls = []
file_names = []
for file in files:
if file.startswith("http"):
pass

if file.startswith(("files/", "private/files/")):
file = "/" + file

Expand All @@ -38,29 +152,37 @@ def remove_files(files):
else:
file_names.append(file)

if file_urls or file_names:
or_filters = None
if file_urls:
filters = {"file_url": ["in", file_urls]}
if file_names:
or_filters = {"file_name": ["in", file_names]}
else:
filters = {"file_name": ["in", file_names]}

if (names := frappe.get_all(
_FILE_DOCTYPE_,
fields=["name"],
filters=filters,
or_filters=or_filters,
pluck="name"
)):
for name in names:
frappe.delete_doc(_FILE_DOCTYPE_, name)
if not file_urls and not file_names:
send_console_log({
"message": "Invalid files path",
"data": files
})
return 2

filters = None
or_filters = None
if file_urls:
filters = {"file_url": ["in", file_urls]}
if file_names:
or_filters = {"file_name": ["in", file_names]}
else:
filters = {"file_name": ["in", file_names]}

names = frappe.get_all(
_FILE_DOCTYPE_,
fields=["name"],
filters=filters,
or_filters=or_filters,
pluck="name"
)
if names:
for name in names:
frappe.delete_doc(_FILE_DOCTYPE_, name)

return 1
return 1

send_console_log({
"message": "Files not found",
"data": files
})
return 2
return 3
Binary file added images/screenshot_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/screenshot_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/screenshot_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e719795

Please sign in to comment.