Skip to content

Commit e719795

Browse files
committed
v2-Beta13 release
1 parent f8285cc commit e719795

File tree

5 files changed

+172
-44
lines changed

5 files changed

+172
-44
lines changed

README.md

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ It supports RTL layout and dark mode out of the box.
77

88
---
99

10-
**If you would like to buy me a cup of coffee 😎**
11-
12-
[![](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)
10+
<p align="center">
11+
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_1.png?raw=true" alt="Better Attach Control"/>
12+
</p>
13+
<p align="center">
14+
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_2.png?raw=true" alt="Better Attach Control"/>
15+
</p>
16+
<p align="center">
17+
<img src="https://github.com/kid1194/frappe-better-attach-control/blob/main/images/screenshot_3.png?raw=true" alt="Better Attach Control"/>
18+
</p>
1319

1420
---
1521

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

5258
### Setup
5359

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

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

158-
Ex: `{"allowed_file_types": [".jpg", ".png", ".gif"]}`
164+
Ex: ```{"allowed_file_types": [".jpg", ".png", ".gif"]}```
159165

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

163169
---
164170

165171
### Available Field Options
166172
| Option | Description |
167173
| :--- | :--- |
168-
| `dialog_title` 🔴 | Upload dialog title to be displayed (✴️Frappe >= v14.0.0).<br/><br/>- Example: `"Upload Images"`<br/>- Default: `"Upload"` |
169-
| `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: `""` |
170-
| `disable_file_browser` 🔴 | Disable file browser uploads.<br/><br/>⚠️ *(File browser is always disabled in Web Form)*<br/><br/>- Default: `false` |
171-
| `allow_multiple` | Allow multiple uploads.<br/><br/>⚠️ *(Field value is a JSON array of files url)*<br/><br/>- Default: `false` |
172-
| `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` |
173-
| `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/*"]` |
174-
| `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` |
175-
| `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` |
176-
| `as_public` | Force uploads to be saved in public folder by default.<br/><br/>- Default: `false` |
177-
| `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` |
178-
| `allow_reload` | Allow reloading attachments (✴️Frappe >= v13.0.0).<br/><br/>ℹ️ Affect the visibility of the reload button.ℹ️<br/><br/>- Default: `true` |
179-
| `allow_remove` | Allow removing and clearing attachments.<br/><br/>ℹ️ Affect the visibility of the remove and clear buttons.ℹ️<br/><br/>- Default: `true` |
174+
| **dialog_title** 🔴 | Upload dialog title to be displayed ️(🔶Frappe >= v14.0.0).<br /><br />🔹Example: **"Upload Images"**<br />🔹Default: **"Upload"** |
175+
| **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: **""** |
176+
| **disable_file_browser** 🔴 | Disable file browser uploads.<br /><br />⚠️ *(File browser is always disabled in Web Form)*<br /><br />🔹Default: **false** |
177+
| **allow_multiple** | Allow multiple uploads.<br /><br />⚠️ *(Field value is a JSON array of files url)*<br /><br />🔹Default: **false** |
178+
| **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** |
179+
| **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/*"]** |
180+
| **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** |
181+
| **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** |
182+
| **as_public** | Force uploads to be saved in public folder by default.<br /><br />🔹Default: **false** |
183+
| **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** |
184+
| **allow_reload** | Allow reloading attachments (🔶Frappe >= v13.0.0).<br /><br />🔶 Affect the visibility of the reload button.🔶<br /><br />🔹Default: **true** |
185+
| **allow_remove** | Allow removing and clearing attachments.<br /><br />🔶 Affect the visibility of the remove and clear buttons.🔶<br /><br />🔹Default: **true** |
180186

181187
---
182188

183189
### Available JavaScript Methods
184190
| Method | Description |
185191
| :--- | :--- |
186-
| `enable_reload()` | Allow reloading attachments and show the reload button (Frappe >= v13.0.0). |
187-
| `disable_reload()` | Deny reloading attachments and hide reload button (Frappe >= v13.0.0). |
188-
| `enable_remove()` | Allow removing and clearing attachments and show the clear and remove buttons. |
189-
| `disable_remove()` | Deny removing and clearing attachments and hide the clear and remove buttons. |
190-
| `set_options(JSON)` | Set or change the plugin current options. |
192+
| **enable_reload()** | Allow reloading attachments and show the reload button (🔶Frappe >= v13.0.0). |
193+
| **disable_reload()** | Deny reloading attachments and hide reload button (🔶Frappe >= v13.0.0). |
194+
| **enable_remove()** | Allow removing and clearing attachments and show the clear and remove buttons. |
195+
| **disable_remove()** | Deny removing and clearing attachments and hide the clear and remove buttons. |
196+
| **set_options(JSON)** | Set or change the plugin current options. |
191197

192198
---
193199

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

205211
### License
206-
This repository has been released under the [MIT License](https://github.com/kid1194/frappe-better-attach-control/blob/main/LICENSE).
212+
This repository has been released under the [MIT License](https://github.com/kid1194/frappe-better-attach-control/blob/main/LICENSE).

frappe_better_attach_control/api/attachment.py

Lines changed: 142 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66

77
import frappe
8+
from frappe import (
9+
_,
10+
is_whitelisted,
11+
__version__ as frappe_version
12+
)
13+
from frappe.utils import cint
814

915
from .common import (
1016
parse_json_if_valid,
@@ -13,6 +19,111 @@
1319

1420

1521
_FILE_DOCTYPE_ = "File"
22+
# For version > 13
23+
_ALLOWED_MIMETYPES_ = (
24+
"image/png",
25+
"image/jpeg",
26+
"application/pdf",
27+
"application/msword",
28+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
29+
"application/vnd.ms-excel",
30+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
31+
"application/vnd.oasis.opendocument.text",
32+
"application/vnd.oasis.opendocument.spreadsheet",
33+
"text/plain",
34+
)
35+
36+
37+
@frappe.whitelist(allow_guest=True)
38+
def upload_file():
39+
version = int(frappe_version.split('.')[0])
40+
41+
user = None
42+
ignore_permissions = False
43+
44+
if version > 12:
45+
if frappe.session.user == "Guest":
46+
if frappe.get_system_settings("allow_guests_to_upload_files"):
47+
ignore_permissions = True
48+
else:
49+
raise frappe.PermissionError
50+
else:
51+
user = frappe.get_doc("User", frappe.session.user)
52+
ignore_permissions = False
53+
54+
files = frappe.request.files
55+
is_private = frappe.form_dict.is_private
56+
doctype = frappe.form_dict.doctype
57+
docname = frappe.form_dict.docname
58+
fieldname = frappe.form_dict.fieldname
59+
file_url = frappe.form_dict.file_url
60+
folder = frappe.form_dict.folder or "Home"
61+
method = frappe.form_dict.method
62+
filename = None
63+
optimize = False
64+
content = None
65+
66+
if version > 13:
67+
filename = frappe.form_dict.file_name
68+
optimize = frappe.form_dict.optimize
69+
70+
if version > 12:
71+
import mimetypes
72+
73+
if "file" in files:
74+
file = files["file"]
75+
content = file.stream.read()
76+
filename = file.filename
77+
78+
if version > 13:
79+
content_type = mimetypes.guess_type(filename)[0]
80+
if optimize and content_type.startswith("image/"):
81+
args = {"content": content, "content_type": content_type}
82+
if frappe.form_dict.max_width:
83+
args["max_width"] = int(frappe.form_dict.max_width)
84+
if frappe.form_dict.max_height:
85+
args["max_height"] = int(frappe.form_dict.max_height)
86+
87+
from frappe.utils.image import optimize_image
88+
content = optimize_image(**args)
89+
90+
frappe.local.uploaded_file = content
91+
frappe.local.uploaded_filename = filename
92+
93+
if version > 13:
94+
if not file_url and content is not None and (
95+
frappe.session.user == "Guest" or (user and not user.has_desk_access())
96+
):
97+
filetype = mimetypes.guess_type(filename)[0]
98+
if filetype not in _ALLOWED_MIMETYPES_:
99+
frappe.throw(_("You can only upload JPG, PNG, PDF, TXT or Microsoft documents."))
100+
101+
elif version > 12:
102+
if not file_url and frappe.session.user == "Guest" or (user and not user.has_desk_access()):
103+
filetype = mimetypes.guess_type(filename)[0]
104+
105+
if method:
106+
method = frappe.get_attr(method)
107+
is_whitelisted(method)
108+
return method()
109+
else:
110+
ret = frappe.get_doc({
111+
"doctype": _FILE_DOCTYPE_,
112+
"attached_to_doctype": doctype,
113+
"attached_to_name": docname,
114+
"attached_to_field": fieldname,
115+
"folder": folder,
116+
"file_name": filename,
117+
"file_url": file_url,
118+
"is_private": cint(is_private),
119+
"content": content,
120+
})
121+
if version > 12:
122+
ret.save(ignore_permissions=ignore_permissions)
123+
else:
124+
ret.save()
125+
126+
return ret
16127

17128

18129
@frappe.whitelist(methods=["POST"], allow_guest=True)
@@ -30,6 +141,9 @@ def remove_files(files):
30141
file_urls = []
31142
file_names = []
32143
for file in files:
144+
if file.startswith("http"):
145+
pass
146+
33147
if file.startswith(("files/", "private/files/")):
34148
file = "/" + file
35149

@@ -38,29 +152,37 @@ def remove_files(files):
38152
else:
39153
file_names.append(file)
40154

41-
if file_urls or file_names:
42-
or_filters = None
43-
if file_urls:
44-
filters = {"file_url": ["in", file_urls]}
45-
if file_names:
46-
or_filters = {"file_name": ["in", file_names]}
47-
else:
48-
filters = {"file_name": ["in", file_names]}
49-
50-
if (names := frappe.get_all(
51-
_FILE_DOCTYPE_,
52-
fields=["name"],
53-
filters=filters,
54-
or_filters=or_filters,
55-
pluck="name"
56-
)):
57-
for name in names:
58-
frappe.delete_doc(_FILE_DOCTYPE_, name)
155+
if not file_urls and not file_names:
156+
send_console_log({
157+
"message": "Invalid files path",
158+
"data": files
159+
})
160+
return 2
161+
162+
filters = None
163+
or_filters = None
164+
if file_urls:
165+
filters = {"file_url": ["in", file_urls]}
166+
if file_names:
167+
or_filters = {"file_name": ["in", file_names]}
168+
else:
169+
filters = {"file_name": ["in", file_names]}
170+
171+
names = frappe.get_all(
172+
_FILE_DOCTYPE_,
173+
fields=["name"],
174+
filters=filters,
175+
or_filters=or_filters,
176+
pluck="name"
177+
)
178+
if names:
179+
for name in names:
180+
frappe.delete_doc(_FILE_DOCTYPE_, name)
59181

60-
return 1
182+
return 1
61183

62184
send_console_log({
63185
"message": "Files not found",
64186
"data": files
65187
})
66-
return 2
188+
return 3

images/screenshot_1.png

19 KB
Loading

images/screenshot_2.png

18.9 KB
Loading

images/screenshot_3.png

71.9 KB
Loading

0 commit comments

Comments
 (0)