django-file-finder 4.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. django_file_finder-4.0.dev0.dist-info/METADATA +552 -0
  2. django_file_finder-4.0.dev0.dist-info/RECORD +425 -0
  3. django_file_finder-4.0.dev0.dist-info/WHEEL +5 -0
  4. django_file_finder-4.0.dev0.dist-info/licenses/LICENSE-filer.txt +26 -0
  5. django_file_finder-4.0.dev0.dist-info/licenses/LICENSE-finder.txt +39 -0
  6. django_file_finder-4.0.dev0.dist-info/top_level.txt +2 -0
  7. filer/__init__.py +16 -0
  8. filer/admin/__init__.py +22 -0
  9. filer/admin/clipboardadmin.py +186 -0
  10. filer/admin/fileadmin.py +262 -0
  11. filer/admin/folderadmin.py +1350 -0
  12. filer/admin/forms.py +80 -0
  13. filer/admin/imageadmin.py +101 -0
  14. filer/admin/patched/__init__.py +0 -0
  15. filer/admin/patched/admin_utils.py +154 -0
  16. filer/admin/permissionadmin.py +56 -0
  17. filer/admin/permissions.py +61 -0
  18. filer/admin/thumbnailoptionadmin.py +5 -0
  19. filer/admin/tools.py +149 -0
  20. filer/admin/views.py +89 -0
  21. filer/apps.py +102 -0
  22. filer/cache.py +91 -0
  23. filer/contrib/__init__.py +0 -0
  24. filer/contrib/clamav/__init__.py +0 -0
  25. filer/contrib/django_cms/__init__.py +0 -0
  26. filer/contrib/django_cms/cms_toolbars.py +51 -0
  27. filer/fields/__init__.py +0 -0
  28. filer/fields/file.py +137 -0
  29. filer/fields/folder.py +135 -0
  30. filer/fields/image.py +15 -0
  31. filer/fields/multistorage_file.py +177 -0
  32. filer/locale/README +2 -0
  33. filer/locale/ar/LC_MESSAGES/django.mo +0 -0
  34. filer/locale/ar/LC_MESSAGES/django.po +1278 -0
  35. filer/locale/bg/LC_MESSAGES/django.mo +0 -0
  36. filer/locale/bg/LC_MESSAGES/django.po +1257 -0
  37. filer/locale/bn/LC_MESSAGES/django.mo +0 -0
  38. filer/locale/bn/LC_MESSAGES/django.po +1237 -0
  39. filer/locale/ca/LC_MESSAGES/django.mo +0 -0
  40. filer/locale/ca/LC_MESSAGES/django.po +1294 -0
  41. filer/locale/cs/LC_MESSAGES/django.mo +0 -0
  42. filer/locale/cs/LC_MESSAGES/django.po +1298 -0
  43. filer/locale/de/LC_MESSAGES/django.mo +0 -0
  44. filer/locale/de/LC_MESSAGES/django.po +1330 -0
  45. filer/locale/en/LC_MESSAGES/django.mo +0 -0
  46. filer/locale/en/LC_MESSAGES/django.po +1313 -0
  47. filer/locale/es/LC_MESSAGES/django.mo +0 -0
  48. filer/locale/es/LC_MESSAGES/django.po +1334 -0
  49. filer/locale/et/LC_MESSAGES/django.mo +0 -0
  50. filer/locale/et/LC_MESSAGES/django.po +1260 -0
  51. filer/locale/eu/LC_MESSAGES/django.mo +0 -0
  52. filer/locale/eu/LC_MESSAGES/django.po +1274 -0
  53. filer/locale/fa/LC_MESSAGES/django.mo +0 -0
  54. filer/locale/fa/LC_MESSAGES/django.po +1239 -0
  55. filer/locale/fi/LC_MESSAGES/django.mo +0 -0
  56. filer/locale/fi/LC_MESSAGES/django.po +1305 -0
  57. filer/locale/fr/LC_MESSAGES/django.mo +0 -0
  58. filer/locale/fr/LC_MESSAGES/django.po +1339 -0
  59. filer/locale/gl/LC_MESSAGES/django.mo +0 -0
  60. filer/locale/gl/LC_MESSAGES/django.po +1261 -0
  61. filer/locale/he/LC_MESSAGES/django.mo +0 -0
  62. filer/locale/he/LC_MESSAGES/django.po +1282 -0
  63. filer/locale/hi/LC_MESSAGES/django.mo +0 -0
  64. filer/locale/hi/LC_MESSAGES/django.po +1202 -0
  65. filer/locale/hr/LC_MESSAGES/django.mo +0 -0
  66. filer/locale/hr/LC_MESSAGES/django.po +1298 -0
  67. filer/locale/hu/LC_MESSAGES/django.mo +0 -0
  68. filer/locale/hu/LC_MESSAGES/django.po +1299 -0
  69. filer/locale/id/LC_MESSAGES/django.mo +0 -0
  70. filer/locale/id/LC_MESSAGES/django.po +1239 -0
  71. filer/locale/it/LC_MESSAGES/django.mo +0 -0
  72. filer/locale/it/LC_MESSAGES/django.po +1327 -0
  73. filer/locale/ja/LC_MESSAGES/django.mo +0 -0
  74. filer/locale/ja/LC_MESSAGES/django.po +1292 -0
  75. filer/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  76. filer/locale/ja_JP/LC_MESSAGES/django.po +1252 -0
  77. filer/locale/lt/LC_MESSAGES/django.mo +0 -0
  78. filer/locale/lt/LC_MESSAGES/django.po +1313 -0
  79. filer/locale/lt_LT/LC_MESSAGES/django.mo +0 -0
  80. filer/locale/lt_LT/LC_MESSAGES/django.po +1269 -0
  81. filer/locale/nb/LC_MESSAGES/django.mo +0 -0
  82. filer/locale/nb/LC_MESSAGES/django.po +1289 -0
  83. filer/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  84. filer/locale/nl_NL/LC_MESSAGES/django.po +1324 -0
  85. filer/locale/nn/LC_MESSAGES/django.mo +0 -0
  86. filer/locale/nn/LC_MESSAGES/django.po +1286 -0
  87. filer/locale/pl/LC_MESSAGES/django.mo +0 -0
  88. filer/locale/pl/LC_MESSAGES/django.po +1320 -0
  89. filer/locale/pt/LC_MESSAGES/django.mo +0 -0
  90. filer/locale/pt/LC_MESSAGES/django.po +1321 -0
  91. filer/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  92. filer/locale/pt_BR/LC_MESSAGES/django.po +1325 -0
  93. filer/locale/ru/LC_MESSAGES/django.mo +0 -0
  94. filer/locale/ru/LC_MESSAGES/django.po +1303 -0
  95. filer/locale/sk/LC_MESSAGES/django.mo +0 -0
  96. filer/locale/sk/LC_MESSAGES/django.po +1268 -0
  97. filer/locale/tr/LC_MESSAGES/django.mo +0 -0
  98. filer/locale/tr/LC_MESSAGES/django.po +1263 -0
  99. filer/locale/vi/LC_MESSAGES/django.mo +0 -0
  100. filer/locale/vi/LC_MESSAGES/django.po +1243 -0
  101. filer/locale/zh/LC_MESSAGES/django.mo +0 -0
  102. filer/locale/zh/LC_MESSAGES/django.po +1252 -0
  103. filer/locale/zh_CN/LC_MESSAGES/django.mo +0 -0
  104. filer/locale/zh_CN/LC_MESSAGES/django.po +1256 -0
  105. filer/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  106. filer/locale/zh_TW/LC_MESSAGES/django.po +1252 -0
  107. filer/management/__init__.py +0 -0
  108. filer/management/commands/__init__.py +0 -0
  109. filer/management/commands/filer_check.py +184 -0
  110. filer/management/commands/generate_thumbnails.py +28 -0
  111. filer/management/commands/import_files.py +134 -0
  112. filer/migrations/0001_initial.py +166 -0
  113. filer/migrations/0001_squashed_0016_alter_folder_index_together_remove_folder_level_and_more.py +155 -0
  114. filer/migrations/0002_auto_20150606_2003.py +18 -0
  115. filer/migrations/0003_thumbnailoption.py +27 -0
  116. filer/migrations/0004_auto_20160328_1434.py +17 -0
  117. filer/migrations/0005_auto_20160623_1425.py +29 -0
  118. filer/migrations/0006_auto_20160623_1627.py +18 -0
  119. filer/migrations/0007_auto_20161016_1055.py +16 -0
  120. filer/migrations/0008_auto_20171117_1313.py +19 -0
  121. filer/migrations/0009_auto_20171220_1635.py +17 -0
  122. filer/migrations/0010_auto_20180414_2058.py +17 -0
  123. filer/migrations/0011_auto_20190418_0137.py +31 -0
  124. filer/migrations/0012_file_mime_type.py +32 -0
  125. filer/migrations/0013_image_width_height_to_float.py +23 -0
  126. filer/migrations/0014_folder_permission_choices.py +28 -0
  127. filer/migrations/0015_alter_file_owner_alter_file_polymorphic_ctype_and_more.py +32 -0
  128. filer/migrations/0016_alter_folder_index_together_remove_folder_level_and_more.py +33 -0
  129. filer/migrations/0017_image__transparent.py +18 -0
  130. filer/migrations/0018_alter_file_options.py +17 -0
  131. filer/migrations/__init__.py +0 -0
  132. filer/models/__init__.py +6 -0
  133. filer/models/abstract.py +301 -0
  134. filer/models/clipboardmodels.py +57 -0
  135. filer/models/filemodels.py +444 -0
  136. filer/models/foldermodels.py +403 -0
  137. filer/models/imagemodels.py +66 -0
  138. filer/models/mixins.py +30 -0
  139. filer/models/thumbnailoptionmodels.py +61 -0
  140. filer/models/tools.py +37 -0
  141. filer/models/virtualitems.py +111 -0
  142. filer/server/__init__.py +0 -0
  143. filer/server/backends/__init__.py +0 -0
  144. filer/server/backends/base.py +45 -0
  145. filer/server/backends/default.py +34 -0
  146. filer/server/backends/nginx.py +27 -0
  147. filer/server/backends/xsendfile.py +17 -0
  148. filer/server/main_server_urls.py +8 -0
  149. filer/server/thumbnails_server_urls.py +8 -0
  150. filer/server/urls.py +12 -0
  151. filer/server/views.py +57 -0
  152. filer/settings.py +337 -0
  153. filer/static/filer/css/admin_filer.cms.icons.css +1 -0
  154. filer/static/filer/css/admin_filer.css +3 -0
  155. filer/static/filer/css/admin_filer.fa.icons.css +4 -0
  156. filer/static/filer/css/admin_folderpermissions.css +6 -0
  157. filer/static/filer/css/maps/admin_filer.cms.icons.css.map +1 -0
  158. filer/static/filer/css/maps/admin_filer.css.map +1 -0
  159. filer/static/filer/css/maps/admin_filer.fa.icons.css.map +1 -0
  160. filer/static/filer/fonts/FontAwesome.otf +0 -0
  161. filer/static/filer/fonts/django-filer-iconfont.eot +0 -0
  162. filer/static/filer/fonts/django-filer-iconfont.svg +51 -0
  163. filer/static/filer/fonts/django-filer-iconfont.ttf +0 -0
  164. filer/static/filer/fonts/django-filer-iconfont.woff +0 -0
  165. filer/static/filer/fonts/django-filer-iconfont.woff2 +0 -0
  166. filer/static/filer/fonts/fontawesome-webfont.eot +0 -0
  167. filer/static/filer/fonts/fontawesome-webfont.svg +640 -0
  168. filer/static/filer/fonts/fontawesome-webfont.ttf +0 -0
  169. filer/static/filer/fonts/fontawesome-webfont.woff +0 -0
  170. filer/static/filer/fonts/fontawesome-webfont.woff2 +0 -0
  171. filer/static/filer/fonts/src/arrow-down.svg +41 -0
  172. filer/static/filer/fonts/src/caret-down.svg +3 -0
  173. filer/static/filer/fonts/src/chevron-right.svg +3 -0
  174. filer/static/filer/fonts/src/download.svg +4 -0
  175. filer/static/filer/fonts/src/expand.svg +3 -0
  176. filer/static/filer/fonts/src/link.svg +4 -0
  177. filer/static/filer/fonts/src/move-to-folder.svg +3 -0
  178. filer/static/filer/fonts/src/picture.svg +4 -0
  179. filer/static/filer/fonts/src/remove-selection.svg +3 -0
  180. filer/static/filer/fonts/src/select.svg +3 -0
  181. filer/static/filer/fonts/src/th-large.svg +3 -0
  182. filer/static/filer/fonts/src/th-list.svg +5 -0
  183. filer/static/filer/fonts/src/upload.svg +3 -0
  184. filer/static/filer/icons/cloud-up.svg +8 -0
  185. filer/static/filer/icons/file-audio.svg +11 -0
  186. filer/static/filer/icons/file-empty.svg +8 -0
  187. filer/static/filer/icons/file-font.svg +11 -0
  188. filer/static/filer/icons/file-missing.svg +84 -0
  189. filer/static/filer/icons/file-pdf.svg +11 -0
  190. filer/static/filer/icons/file-picture.svg +11 -0
  191. filer/static/filer/icons/file-spreadsheet.svg +10 -0
  192. filer/static/filer/icons/file-unknown.svg +69 -0
  193. filer/static/filer/icons/file-video.svg +8 -0
  194. filer/static/filer/icons/file-zip.svg +14 -0
  195. filer/static/filer/icons/folder-dropdown.svg +85 -0
  196. filer/static/filer/icons/folder-root.svg +10 -0
  197. filer/static/filer/icons/folder-unfiled.svg +77 -0
  198. filer/static/filer/icons/folder.svg +9 -0
  199. filer/static/filer/img/button-bg.gif +0 -0
  200. filer/static/filer/img/close.gif +0 -0
  201. filer/static/filer/img/icon_changelink.gif +0 -0
  202. filer/static/filer/img/icon_deletelink.gif +0 -0
  203. filer/static/filer/img/loading_animation.gif +0 -0
  204. filer/static/filer/img/nav-bg.gif +0 -0
  205. filer/static/filer/img/select_item-hover.gif +0 -0
  206. filer/static/filer/img/select_item.gif +0 -0
  207. filer/static/filer/img/upload_button.png +0 -0
  208. filer/static/filer/js/addons/copy-move-files.js +32 -0
  209. filer/static/filer/js/addons/csrf.js +24 -0
  210. filer/static/filer/js/addons/dropdown-menu.js +249 -0
  211. filer/static/filer/js/addons/dropzone.init.js +342 -0
  212. filer/static/filer/js/addons/filer_popup_response.js +22 -0
  213. filer/static/filer/js/addons/focal-point.js +274 -0
  214. filer/static/filer/js/addons/popup_handling.js +137 -0
  215. filer/static/filer/js/addons/table-dropzone.js +289 -0
  216. filer/static/filer/js/addons/toggler.js +82 -0
  217. filer/static/filer/js/addons/tooltip.js +40 -0
  218. filer/static/filer/js/addons/upload-button.js +245 -0
  219. filer/static/filer/js/addons/widget.js +57 -0
  220. filer/static/filer/js/base.js +417 -0
  221. filer/static/filer/js/dist/LICENSES.txt +57 -0
  222. filer/static/filer/js/dist/admin-file-widget.bundle.js +1 -0
  223. filer/static/filer/js/dist/filer-base.bundle.js +13 -0
  224. filer/static/filer/js/widgets/admin-file-widget.js +30 -0
  225. filer/static/filer/js/widgets/admin-folder-widget.js +64 -0
  226. filer/storage.py +20 -0
  227. filer/templates/admin/filer/actions.html +16 -0
  228. filer/templates/admin/filer/base_site.html +17 -0
  229. filer/templates/admin/filer/breadcrumbs.html +44 -0
  230. filer/templates/admin/filer/change_form.html +36 -0
  231. filer/templates/admin/filer/delete_confirmation.html +10 -0
  232. filer/templates/admin/filer/delete_selected_files_confirmation.html +51 -0
  233. filer/templates/admin/filer/dismiss_popup.html +8 -0
  234. filer/templates/admin/filer/file/change_form.html +23 -0
  235. filer/templates/admin/filer/file/popup_response.html +10 -0
  236. filer/templates/admin/filer/folder/change_form.html +54 -0
  237. filer/templates/admin/filer/folder/choose_copy_destination.html +76 -0
  238. filer/templates/admin/filer/folder/choose_images_resize_options.html +45 -0
  239. filer/templates/admin/filer/folder/choose_move_destination.html +72 -0
  240. filer/templates/admin/filer/folder/choose_rename_format.html +65 -0
  241. filer/templates/admin/filer/folder/directory_listing.html +226 -0
  242. filer/templates/admin/filer/folder/directory_table_list.html +255 -0
  243. filer/templates/admin/filer/folder/directory_thumbnail_list.html +227 -0
  244. filer/templates/admin/filer/folder/legacy_listing.html +226 -0
  245. filer/templates/admin/filer/folder/list_type_switcher.html +1 -0
  246. filer/templates/admin/filer/folder/listing_breadcrumbs.html +64 -0
  247. filer/templates/admin/filer/folder/new_folder_form.html +38 -0
  248. filer/templates/admin/filer/image/change_form.html +22 -0
  249. filer/templates/admin/filer/image/expand.html +28 -0
  250. filer/templates/admin/filer/templatetags/file_icon.html +38 -0
  251. filer/templates/admin/filer/tools/clipboard/clipboard.html +54 -0
  252. filer/templates/admin/filer/tools/clipboard/clipboard_item_rows.html +19 -0
  253. filer/templates/admin/filer/tools/detail_info.html +55 -0
  254. filer/templates/admin/filer/tools/search_form.html +9 -0
  255. filer/templates/admin/filer/widgets/admin_file.html +64 -0
  256. filer/templates/admin/filer/widgets/admin_folder.html +24 -0
  257. filer/templatetags/__init__.py +0 -0
  258. filer/templatetags/filer_admin_tags.py +263 -0
  259. filer/templatetags/filer_image_tags.py +139 -0
  260. filer/templatetags/filer_tags.py +106 -0
  261. filer/thumbnail_processors.py +161 -0
  262. filer/urls.py +13 -0
  263. filer/utils/__init__.py +0 -0
  264. filer/utils/compatibility.py +33 -0
  265. filer/utils/filer_easy_thumbnails.py +204 -0
  266. filer/utils/files.py +174 -0
  267. filer/utils/generate_filename.py +33 -0
  268. filer/utils/loader.py +56 -0
  269. filer/utils/model_label.py +17 -0
  270. filer/utils/pil_exif.py +29 -0
  271. filer/utils/recursive_dictionary.py +79 -0
  272. filer/utils/svg.py +431 -0
  273. filer/utils/zip.py +24 -0
  274. filer/validation.py +205 -0
  275. filer/views.py +14 -0
  276. finder/__init__.py +1 -0
  277. finder/admin/__init__.py +4 -0
  278. finder/admin/ambit.py +146 -0
  279. finder/admin/file.py +138 -0
  280. finder/admin/folder.py +582 -0
  281. finder/admin/inode.py +330 -0
  282. finder/apps.py +19 -0
  283. finder/browser/__init__.py +0 -0
  284. finder/browser/urls.py +69 -0
  285. finder/browser/views.py +388 -0
  286. finder/checks.py +60 -0
  287. finder/contrib/__init__.py +0 -0
  288. finder/contrib/archive/__init__.py +0 -0
  289. finder/contrib/archive/admin.py +183 -0
  290. finder/contrib/archive/models.py +13 -0
  291. finder/contrib/audio/__init__.py +0 -0
  292. finder/contrib/audio/admin.py +17 -0
  293. finder/contrib/audio/forms.py +23 -0
  294. finder/contrib/audio/models.py +63 -0
  295. finder/contrib/common/__init__.py +0 -0
  296. finder/contrib/common/admin.py +33 -0
  297. finder/contrib/common/models.py +110 -0
  298. finder/contrib/image/__init__.py +0 -0
  299. finder/contrib/image/admin.py +44 -0
  300. finder/contrib/image/forms.py +40 -0
  301. finder/contrib/image/models.py +188 -0
  302. finder/contrib/image/pil/__init__.py +0 -0
  303. finder/contrib/image/pil/admin.py +29 -0
  304. finder/contrib/image/pil/forms.py +4 -0
  305. finder/contrib/image/pil/models.py +100 -0
  306. finder/contrib/image/svg/__init__.py +0 -0
  307. finder/contrib/image/svg/admin.py +7 -0
  308. finder/contrib/image/svg/forms.py +4 -0
  309. finder/contrib/image/svg/models.py +57 -0
  310. finder/contrib/image/svg/validators.py +71 -0
  311. finder/contrib/video/__init__.py +0 -0
  312. finder/contrib/video/admin.py +17 -0
  313. finder/contrib/video/forms.py +21 -0
  314. finder/contrib/video/models.py +95 -0
  315. finder/exceptions.py +13 -0
  316. finder/forms/__init__.py +0 -0
  317. finder/forms/fields.py +37 -0
  318. finder/forms/file.py +35 -0
  319. finder/forms/widgets.py +90 -0
  320. finder/locale/ar/LC_MESSAGES/django.mo +0 -0
  321. finder/locale/ar/LC_MESSAGES/django.po +406 -0
  322. finder/locale/bn/LC_MESSAGES/django.mo +0 -0
  323. finder/locale/bn/LC_MESSAGES/django.po +395 -0
  324. finder/locale/de/LC_MESSAGES/django.mo +0 -0
  325. finder/locale/de/LC_MESSAGES/django.po +416 -0
  326. finder/locale/en/LC_MESSAGES/django.mo +0 -0
  327. finder/locale/en/LC_MESSAGES/django.po +394 -0
  328. finder/locale/es/LC_MESSAGES/django.mo +0 -0
  329. finder/locale/es/LC_MESSAGES/django.po +408 -0
  330. finder/locale/fi/LC_MESSAGES/django.mo +0 -0
  331. finder/locale/fi/LC_MESSAGES/django.po +399 -0
  332. finder/locale/fr/LC_MESSAGES/django.mo +0 -0
  333. finder/locale/fr/LC_MESSAGES/django.po +416 -0
  334. finder/locale/hi/LC_MESSAGES/django.mo +0 -0
  335. finder/locale/hi/LC_MESSAGES/django.po +400 -0
  336. finder/locale/id/LC_MESSAGES/django.mo +0 -0
  337. finder/locale/id/LC_MESSAGES/django.po +405 -0
  338. finder/locale/ko/LC_MESSAGES/django.mo +0 -0
  339. finder/locale/ko/LC_MESSAGES/django.po +391 -0
  340. finder/locale/nl/LC_MESSAGES/django.mo +0 -0
  341. finder/locale/nl/LC_MESSAGES/django.po +410 -0
  342. finder/locale/ru/LC_MESSAGES/django.mo +0 -0
  343. finder/locale/ru/LC_MESSAGES/django.po +400 -0
  344. finder/locale/sq/LC_MESSAGES/django.mo +0 -0
  345. finder/locale/sq/LC_MESSAGES/django.po +399 -0
  346. finder/locale/vi/LC_MESSAGES/django.mo +0 -0
  347. finder/locale/vi/LC_MESSAGES/django.po +394 -0
  348. finder/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  349. finder/locale/zh_Hans/LC_MESSAGES/django.po +391 -0
  350. finder/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  351. finder/locale/zh_Hant/LC_MESSAGES/django.po +391 -0
  352. finder/lookups.py +73 -0
  353. finder/management/__init__.py +0 -0
  354. finder/management/commands/__init__.py +0 -0
  355. finder/management/commands/filer_to_finder.py +134 -0
  356. finder/management/commands/finder.py +241 -0
  357. finder/migrations/0001_initial.py +394 -0
  358. finder/migrations/__init__.py +0 -0
  359. finder/models/__init__.py +4 -0
  360. finder/models/ambit.py +62 -0
  361. finder/models/fields.py +111 -0
  362. finder/models/file.py +296 -0
  363. finder/models/filetag.py +32 -0
  364. finder/models/folder.py +480 -0
  365. finder/models/inode.py +412 -0
  366. finder/models/permission.py +279 -0
  367. finder/settings.py +63 -0
  368. finder/static/finder/css/finder-admin.css +1 -0
  369. finder/static/finder/css/finder-browser.css +1 -0
  370. finder/static/finder/css/finder-select.css +1 -0
  371. finder/static/finder/icons/file-audio.svg +11 -0
  372. finder/static/finder/icons/file-code.svg +13 -0
  373. finder/static/finder/icons/file-pdf.svg +11 -0
  374. finder/static/finder/icons/file-picture.svg +11 -0
  375. finder/static/finder/icons/file-spreadsheet.svg +10 -0
  376. finder/static/finder/icons/file-text.svg +9 -0
  377. finder/static/finder/icons/file-unknown.svg +69 -0
  378. finder/static/finder/icons/file-video.svg +8 -0
  379. finder/static/finder/icons/file-word.svg +15 -0
  380. finder/static/finder/icons/file-zip.svg +14 -0
  381. finder/static/finder/icons/folder-open.svg +8 -0
  382. finder/static/finder/icons/folder.svg +9 -0
  383. finder/static/finder/js/LICENSES.txt +713 -0
  384. finder/static/finder/js/Preview-O4JBFL6E.js +149 -0
  385. finder/static/finder/js/chunk-2N4KYP4W.js +68 -0
  386. finder/static/finder/js/chunk-325KA3A4.js +20 -0
  387. finder/static/finder/js/chunk-47NEGY7S.js +104 -0
  388. finder/static/finder/js/chunk-CP535C4P.js +97 -0
  389. finder/static/finder/js/chunk-D2Y5GODP.js +14 -0
  390. finder/static/finder/js/chunk-DT5UEGHU.js +4105 -0
  391. finder/static/finder/js/chunk-HEYVBD4V.js +99 -0
  392. finder/static/finder/js/chunk-IBFP2HMT.js +72 -0
  393. finder/static/finder/js/chunk-KHF6WAKM.js +4129 -0
  394. finder/static/finder/js/chunk-KNWCMEYY.js +179 -0
  395. finder/static/finder/js/chunk-LNYHHDVL.js +411 -0
  396. finder/static/finder/js/chunk-OTTSRNB4.js +97 -0
  397. finder/static/finder/js/chunk-QMKMWYMD.js +804 -0
  398. finder/static/finder/js/chunk-UQPTPEX7.js +1054 -0
  399. finder/static/finder/js/chunk-VG6P5O3O.js +25869 -0
  400. finder/static/finder/js/chunk-YJPL3M4U.js +113 -0
  401. finder/static/finder/js/components/browser/Image.js +43 -0
  402. finder/static/finder/js/components/editor/Archive.js +47 -0
  403. finder/static/finder/js/components/editor/Audio.js +2606 -0
  404. finder/static/finder/js/components/editor/Common.js +19 -0
  405. finder/static/finder/js/components/editor/Image.js +169 -0
  406. finder/static/finder/js/components/editor/Video.js +1817 -0
  407. finder/static/finder/js/components/folderitem/Audio.js +35 -0
  408. finder/static/finder/js/components/folderitem/Video.js +64 -0
  409. finder/static/finder/js/components/menuextension/Archive.js +52 -0
  410. finder/static/finder/js/file-admin.js +87 -0
  411. finder/static/finder/js/finder-select.js +1276 -0
  412. finder/static/finder/js/folder-admin.js +1963 -0
  413. finder/storages.py +198 -0
  414. finder/templates/finder/admin/base_site.html +27 -0
  415. finder/templates/finder/admin/base_site.legacy.html +27 -0
  416. finder/templates/finder/admin/change_file_form.html +49 -0
  417. finder/templates/finder/admin/change_folder_form.html +5 -0
  418. finder/templates/finder/forms/div.html +15 -0
  419. finder/templates/finder/widgets/finder_file_select.html +1 -0
  420. finder/templates/finder/widgets/finder_folder_select.html +1 -0
  421. finder/templatetags/__init__.py +0 -0
  422. finder/templatetags/finder_tags.py +16 -0
  423. finder/utils/__init__.py +0 -0
  424. finder/utils/svg.py +324 -0
  425. finder/validators.py +149 -0
@@ -0,0 +1,552 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-file-finder
3
+ Version: 4.0.dev0
4
+ Summary: A media asset management application for Django.
5
+ Author-email: Jacob Rief <jacob.rief@gmail.com>
6
+ Maintainer-email: Django CMS Association and contributors <info@django-cms.org>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/django-cms/django-filer
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Framework :: Django
19
+ Classifier: Framework :: Django :: 5.2
20
+ Classifier: Framework :: Django :: 6.0
21
+ Classifier: Topic :: Internet :: WWW/HTTP
22
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
23
+ Classifier: Topic :: Software Development
24
+ Classifier: Topic :: Software Development :: Libraries
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE-filer.txt
28
+ License-File: LICENSE-finder.txt
29
+ Requires-Dist: django>=5.2
30
+ Provides-Extra: cte
31
+ Requires-Dist: django-cte; extra == "cte"
32
+ Provides-Extra: heif
33
+ Requires-Dist: pillow-heif; extra == "heif"
34
+ Provides-Extra: audio
35
+ Requires-Dist: ffmpeg-python; extra == "audio"
36
+ Requires-Dist: django-entangled; extra == "audio"
37
+ Provides-Extra: image
38
+ Requires-Dist: Pillow; extra == "image"
39
+ Requires-Dist: django-entangled; extra == "image"
40
+ Provides-Extra: svg
41
+ Requires-Dist: django-entangled; extra == "svg"
42
+ Requires-Dist: py-svg-hush; extra == "svg"
43
+ Provides-Extra: video
44
+ Requires-Dist: ffmpeg-python; extra == "video"
45
+ Requires-Dist: django-entangled; extra == "video"
46
+ Provides-Extra: s3
47
+ Requires-Dist: django-storages[s3]; extra == "s3"
48
+ Provides-Extra: e2e
49
+ Requires-Dist: playwright; extra == "e2e"
50
+ Requires-Dist: pytest; extra == "e2e"
51
+ Requires-Dist: pytest-django; extra == "e2e"
52
+ Requires-Dist: Pillow; extra == "e2e"
53
+ Dynamic: license-file
54
+
55
+ # Django-Filer ("Finder" branch)
56
+
57
+ The "Finder" branch of django-filer is a complete rewrite of the original **django-filer** project.
58
+
59
+ A rewrite was necessary because the original codebase was not maintainable anymore. Please read this
60
+ [discussion](https://github.com/django-cms/django-filer/discussions/1348) for details. Apart from the maintainability issues, using the File and Folder
61
+ models was not possible in a multi-tenant environment. Extending the file model with a more
62
+ specialized implementation was possible in theory, but so complicated that apart from the
63
+ `ImageModel` no other implementations ever have been created.
64
+
65
+
66
+ ## Less third-party dependencies
67
+
68
+ The "Finder" branch of **django-filer** has less third-party dependencies. It does not depend on
69
+ [django-polymorphic](https://django-polymorphic.readthedocs.io/en/stable/),
70
+ [django-mptt](https://django-mptt.readthedocs.io/en/latest/) and
71
+ [easy-thumbnails](https://easy-thumbnails.readthedocs.io/en/latest/) anymore.
72
+
73
+ For large datasets [django-cte](https://github.com/dimagi/django-cte) is reccomended, in order to improve the speed
74
+ of tree travesals, which is important while searching.
75
+
76
+ Since each `FileModel` contains a `JSONField` to store arbitrary data, [django-entangled](https://github.com/jrief/django-entangled)
77
+ is reccomended, in order to give users the opportunity to edit the contents of that field.
78
+
79
+ The client part of the new admin user interface has no runtime dependencies. It is compiled into two
80
+ JavaScript files, which are included by the corresponding admin views. One of them is used for the
81
+ django-admin interface, the other one for the frontend. The frontend part is also used for the new
82
+ file selection widget.
83
+
84
+
85
+ ## Extensibility
86
+
87
+ The "Finder" branch of django-filer is designed to be extensible for every MIME-type one desires.
88
+ Therefore, those extensions might need their own dependencies in order to extract or visualize a
89
+ certain type of file. A minimal installation can be configured to only distinguish between files
90
+ and folders.
91
+
92
+
93
+ ## Multiple Ambits to hold Folder Roots
94
+
95
+ The "Finder" branch of **django-filer** allows multiple ambits, each able to hold multiple root
96
+ folders. Such an ambit must be created using the management command `manage.py finder add-ambit …`.
97
+ Each ambit has a slug and a name. The slug is used in the URL to access that ambit, while the name
98
+ is used for display purposes. All ambits are displayed in the left sidebar of the Django admin. For
99
+ each ambit, a different Django storage can be configured.
100
+
101
+
102
+ ## New Model Features
103
+
104
+ The "Finder" branch introduces a new model named `InodeModel`. This is the base class for all other
105
+ models, such as the `FileModel`, the `FolderModel` and all other specialized implementations. By
106
+ using a UUID instead of a numerical primary key, it is possible to use different database tables
107
+ while preserving the possibility to perform queries across all tables. This allows us to register
108
+ models inheriting from the `AbstractFileModel` without the need of **django-polymorphic**, and thus
109
+ a join between two or more tables for each query.
110
+
111
+ Multiple ambits are allowed, each containg one root folder, one trash folder per user and a list of
112
+ allowed tags. Each ambit must explictly be created using a management command. All ambits are
113
+ displayed in the left sidebar of the Django admin. For each ambit, a different Django storage can be
114
+ configured. It also is possible to restrict an ambit to a Site and/or to an AdminSite.
115
+
116
+ The Admin interface has also been completely rewritten. For instance, there is no more list view for
117
+ the `FolderModel` and the `FileModel` (or any specialized implementation). Instead, there are only
118
+ details views for each of those models. This is much nearer to what a user would expect from a file
119
+ system. Therefore, if a user wants to access the list view of a folder, he is redirected immediately
120
+ to the detail view of the root folder of the given ambit. From there, he can traverse the folder
121
+ tree in the same manner he's used to from his operating system.
122
+
123
+
124
+ ## New User Interface
125
+
126
+ The user interface has also been completely rewritten. It offers drag and drop functionality for
127
+ moving files and folders. It now is possible to select multiple files and folders at once by a
128
+ rectangular selection with the mouse.
129
+ Double-clicking on a folder opens it, by loading the detail view for that `FolderModel`. Selected
130
+ files or folders can be dragged into a target folder. They can also be cutted or copied to a
131
+ clipboard and pasted into another folder.
132
+
133
+
134
+ ### Trash Folder
135
+
136
+ Each user now has its *own private trash* folder. Whenever he deletes a file or folder, it is moved
137
+ to that trash folder. From there, he can restore the file or folder, or delete it permanently. The
138
+ trash folder can be emptied automatically after a given time period.
139
+
140
+
141
+ ## Permission System
142
+
143
+ The permission system of **django-filer** is using the concept of Access Control Lists (ACLs)
144
+ similar to Posix or NTFS ACLs. This allows granting fine-grained permissions to everybody,
145
+ individual users and/or groups for each file and folder.
146
+
147
+ Permissions are controlled through the model named `AccessControlEntry`. This model has a foreign
148
+ key onto `InodeModel` and a so-called "principal". A principal is either a user, a group or for
149
+ everyone. They are mutually exclusive and implemented as a nullable foreign key onto `User`, or a
150
+ foreign key onto `Group`. Either of them can be set, but not both. If neither is set, the permission
151
+ is considered for everyone.
152
+
153
+ By using a separate model `AccessControlEntry`, **django-filer** can now compute the permissions
154
+ using just one database (sub-)query while filtering all inodes. Until version 3, the permissions had
155
+ to be computed traversing all ancestors starting from the current folder up to the root of the
156
+ folder tree. This was a time-consuming operation and made **django-filer** slow for large datasets.
157
+
158
+ Each `AccessControlEntry` has a bitmap field used to represent:
159
+ * `write` set for a folder: Allows the user to upload a file, to change the name of that folder,
160
+ to reorder files in that folder, to move files out of that filer.
161
+ * `write` set for a file: Allows the user to replace a file, to edit the file's meta-data, and to
162
+ move the file to another folder, if the source and target folders have write permissions.
163
+ * `read` set for a folder: Allows the user to open that folder and view its content.
164
+ * `read` set for a file: Allows the user to view a thumbnail of that file inside its containing
165
+ folder and use that file, including copying.
166
+ * `admin`: Allows the user to change the permissions of that folder or file.
167
+ * A generic foreign key pointing onto the `InodeModel`. This creates a one-to-many relation between
168
+ different file types and folders on one side and the access control list on the other.
169
+
170
+ In addition to `AccessControlEntry` the permission system also provides a model named
171
+ `DefaultAccessControlEntry`. This acts as a template for newely files or folders created inside, or
172
+ move to another folder.
173
+ The `DefaultAccessControlEntry` is implemented as a separate model with a foreign key onto the
174
+ `FolderModel`, because it is not possible to use the `AccessControlEntry` for both purposes, since
175
+ the default permissions must be inherited by all children of a folder, while the regular
176
+ permissions only apply to the file or folder they are attached to.
177
+
178
+ A foreign key onto the folder model to set a permission template. Read below for details.
179
+
180
+ If a folder has `write` but no `read` permission, the user can upload files into that folder, but
181
+ doesn't see files from other users. This is named the "Dropbox" functionality.
182
+
183
+ Each file and folder has a foreign key named `owner`, pointing onto the `User` model. The owner of a
184
+ file or folder can change its permissions if he has the global permission to do so. When creating a
185
+ new file or folder, the currently loggedin user is set as the owner of that file or folder.
186
+
187
+ Only a superuser and the owner of a file or folder can change its permissions. The superuser can
188
+ change the permissions of any file or folder. Staff users can change the permissions only of files
189
+ they own themselves.
190
+
191
+ Only a superuser can change the owner of a file or folder.
192
+
193
+ In addition to the file and folder permissions, each folder requires a template of permissions on how to
194
+ inherit them to files and folders created as children of that specific folder. This can be achieved with
195
+ a separate foreign key in model `AccessControlEntry` pointing onto the `FolderModel`.
196
+
197
+ Microsoft gives a good explanation on the implementation of
198
+ [ACLs in their Data-Lake implementation](https://learn.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-access-control).
199
+
200
+
201
+ ### Alternative Views
202
+
203
+ The user interface offers four alternative views for the folder tree.
204
+
205
+ **Tiles** is a "flat" view, which shows all files and folders as a bunch of tiles.
206
+
207
+ **Mosaic** is very similar to the "Tiles" view, but shows a much smaller version of those file,
208
+ folder and image icons.
209
+
210
+ **List** is a view with detailed information about every file or folder. Here every item requires a
211
+ separate row.
212
+
213
+ **Column** is a view which shows columns for the currently selected folder and each of its
214
+ ancestors. This allows to easily move files between those folders.
215
+
216
+
217
+ ### Multiple Favrourite Folders
218
+
219
+ Each user can have multiple favourite folders. This allows him to quickly access those folders from
220
+ the navigation bar. It is also possible to drag a file from the current view into one of the tabs
221
+ for of the favorite folders.
222
+
223
+
224
+ ### Implementation Details
225
+
226
+ The new user interface is based on the [React](https://reactjs.org/) framework with the
227
+ [DnD-Kit](https://docs.dndkit.com/) as its main additional dependency.
228
+
229
+
230
+ ## Installation
231
+
232
+ The new version of **django-filer** is not yet available on PyPI. Therefore, you have to install it
233
+ from GitHub:
234
+
235
+ ```shell
236
+ git clone https://github.com/django-cms/django-filer.git
237
+ cd django-filer
238
+ git switch finder
239
+ pip install --no-deps -e .
240
+ ```
241
+
242
+ The new version of **django-filer** requires Django-5.2 or later.
243
+
244
+ The new version of **django-filer** currently has only been tested with SQLite and Postgres, but
245
+ should also work on MariaDB and MySQL.
246
+
247
+ In `settings.py` of your project, add these extra dependencies or those you really need:
248
+
249
+ ```python
250
+ INSTALLED_APPS = [
251
+ ...
252
+ 'finder',
253
+ 'finder.contrib.archive', # supports zip, tar, tar.gz, tar.bz2, tar.xz
254
+ 'finder.contrib.audio', # supports mp3, ogg, flac, wav
255
+ 'finder.contrib.common', # supports PDF, spreadsheets
256
+ 'finder.contrib.image.pil', # supports bmp, gif, jpeg, png, tiff
257
+ 'finder.contrib.image.svg', # supports svg
258
+ 'finder.contrib.video', # supports mp4, webm, ogv
259
+ ...
260
+ ]
261
+ ```
262
+
263
+ If you use:
264
+ * `finder.contrib.audio` or `finder.contrib.video`, assure that `ffmpeg-python` is installed.
265
+ * `finder.contrib.image.pil`, assure that `Pillow` is installed.
266
+ * `finder.contrib.image.svg` needs no extra dependency; it reads an SVG's size from the
267
+ document and thumbnails it by rewriting the root element. Documents that state their
268
+ size only in relative units (`width="100%"` without a `viewBox`) cannot be measured
269
+ that way and are shown with a generic icon.
270
+ * Postgres as a database, install `psycopg2` or `psycopg2-binary` if available for your platform.
271
+
272
+ Each root folder requires two storage backends. One for the public files and one for their
273
+ thumbnails and/or samples. Add these storage backends to the `STORAGES` setting in your
274
+ `settings.py`:
275
+
276
+ ```python
277
+ STORAGES = {
278
+ 'default': {
279
+ 'BACKEND': 'django.core.files.storage.FileSystemStorage',
280
+ },
281
+ 'staticfiles': {
282
+ 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage',
283
+ },
284
+ 'finder_public': {
285
+ 'BACKEND': 'finder.storage.FinderSystemStorage',
286
+ 'OPTIONS': {
287
+ 'location': '/path/to/your/media/filer_public',
288
+ 'base_url': '/media/filer_public/',
289
+ 'allow_overwrite': True,
290
+ },
291
+ },
292
+ 'finder_public_samples': {
293
+ 'BACKEND': 'finder.storage.FinderSystemStorage',
294
+ 'OPTIONS': {
295
+ 'location': '/path/to/your/media/filer_public_thumbnails',
296
+ 'base_url': '/media/filer_public_thumbnails/',
297
+ 'allow_overwrite': True,
298
+ },
299
+ },
300
+ }
301
+ ```
302
+
303
+ If instead of using the local file system you want to use another storage backend, such as MinIO,
304
+ Amazon S3 or Google Cloud Storage, configure the corresponding storage backend in the `STORAGES`
305
+ setting as:
306
+
307
+ ```python
308
+ STORAGES = {
309
+ ...
310
+ 'finder_public': {
311
+ 'BACKEND': 'storages.backends.s3.S3Storage',
312
+ 'OPTIONS': {...},
313
+ },
314
+ 'finder_public_samples': {
315
+ 'BACKEND': 'storages.backends.s3.S3Storage',
316
+ 'OPTIONS': {...},
317
+ },
318
+ }
319
+ ```
320
+
321
+ Note that multiple root folders can share the same storage location or bucket.
322
+
323
+ If you declare neither `finder_public` nor `finder_public_samples`, finder derives them
324
+ from your `default` storage on startup: same filesystem root and URL prefix, but a
325
+ subdirectory per alias, the `FinderSystemStorage` backend and `allow_overwrite` turned on.
326
+ With Django's defaults that gives you
327
+
328
+ ```
329
+ MEDIA_ROOT/finder_public/ served from MEDIA_URL + finder_public/
330
+ MEDIA_ROOT/finder_public_samples/ served from MEDIA_URL + finder_public_samples/
331
+ ```
332
+
333
+ which is enough to get started. Declare them yourself once you want the files somewhere
334
+ else — on S3, on another volume, or shared between projects.
335
+
336
+ > [!NOTE]
337
+ > The derivation needs a `FileSystemStorage` as your `default`. If that is a remote
338
+ > backend there is no location to put a subdirectory under, and `manage.py check` reports
339
+ > `finder.W001`. Declare the two storages explicitly in that case.
340
+
341
+ Run the migrations for app `finder`:
342
+
343
+ ```shell
344
+ python manage.py migrate finder
345
+ ```
346
+
347
+ This creates a first ambit for you, so that the admin and the form widgets work right away.
348
+ It is named by `FINDER_DEFAULT_AMBIT`, which is `"public"` — the slug the
349
+ `FinderFileField`s and `FinderFolderField`s of your models refer to when they declare no
350
+ ambit of their own. Override it if you want a different name:
351
+
352
+ ```python
353
+ FINDER_DEFAULT_AMBIT = 'assets'
354
+ ```
355
+
356
+ Its root folder grants read/write to everybody, which is the same thing
357
+ `manage.py finder add-ambit` does. Restrict it through the permission editor in the admin,
358
+ or set up the ambits yourself instead:
359
+
360
+ ```python
361
+ FINDER_CREATE_DEFAULT_AMBIT = False
362
+ ```
363
+
364
+ The setting is only read while `finder.0001_initial` is applied, so it has to be in
365
+ place before you migrate for the first time.
366
+
367
+ Create further ambits with the configuration above:
368
+
369
+ ```shell
370
+ python manage.py finder add-ambit private --values name="Private Folder" storage=finder_private sample_storage=finder_private_samples
371
+ ```
372
+
373
+ You can create multiple root folders, each with their own unique slug and name. You may configure a
374
+ storage backend for each root folder or share them between multiple root folders.
375
+
376
+ If you already have **django-filer** installed and that database is filled, you can migrate the
377
+ meta-data of those files and folders into the new database tables. The physical files on disk are
378
+ not affected by this migration. Remember to leave the `filer` app in `INSTALLED_APPS`.
379
+
380
+ ```shell
381
+ python manage.py filer_to_finder
382
+ ```
383
+
384
+ This does not affect the original database tables. You can still use the original **django-filer**
385
+ codebase in parallel to the new "Finder" branch. They both share the same underlying file system,
386
+ usually `media/filer_public` but store their meta information in completely independent database
387
+ tables. The only caveat is that you should not erase files in the trash folder, because then they
388
+ are also gone on disk.
389
+
390
+ The client part of the new admin user must be compiled before it can be used. This requires a modern
391
+ version (18 or later) of [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/), which usually are installed anyway.
392
+ The following commands installs the requirements and compiles the code:
393
+
394
+ ```shell
395
+ cd django-filer
396
+ npm install --include=dev
397
+ npm run buildall
398
+ ```
399
+
400
+
401
+ ## Usage
402
+
403
+ After restarting the development server, you can access the new admin user interface at the URL
404
+ http://localhost:8000/admin/finder/foldermodel/ . Clicking on that link will redirect you to the
405
+ root folder of the current site.
406
+
407
+
408
+ ## Admin Backend
409
+
410
+ The admin backend is implemented as a web interface mimicking the features of the Finder app of your
411
+ operating system's graphical user interface. It is possible to …
412
+
413
+ * Download, upload files and to view them as thumbnailed images.
414
+ * Navigate through the folder tree by double-clicking on a folder.
415
+ * Move files and folders by drag and drop.
416
+ * Select multiple files and folders by a rectangular selection with the mouse.
417
+ * Cut or copy files and folders to a clipboard and paste them into another folder.
418
+ * Delete files and folders, which are moved to a trash folder.
419
+ * Restore files and folders from the trash folder or to delete them permanently.
420
+ * Create new folders and to upload files.
421
+ * Rename files and folders by slowly double-clicking on their name.
422
+ * Create multiple tabs for favourite folders quick access. They also can be used to drag files from
423
+ the current view into one of the tabs.
424
+ * Search for files and folders.
425
+ * Tag files and filter them by their tags.
426
+
427
+
428
+ ## Interface to other Django projects
429
+
430
+ **django-filer** (Finder branch) ships with a new file selection field. This field can be used in
431
+ any Django model. It allows to select a file from the files stored inside **django-filer**. It also
432
+ allows to upload a local file and use it immediatly.
433
+
434
+ Example:
435
+
436
+ ```python
437
+ from django.db import models
438
+ from finder.models.fields import FinderFileField
439
+
440
+ class MyModel(models.Model):
441
+ my_file = FinderFileField(
442
+ null=True,
443
+ blank=True,
444
+ ambit='my-ambit', # this must be the slug of an existing ambit
445
+ )
446
+ ```
447
+
448
+ Forms generated from this model will have a file selection widget for the `my_file` field. When
449
+ rendered as HTML, this widget becomes the webcomponent `<finder-file-select …></finder-file-select>`
450
+ with a few additional attributes. The JavaScript part of the widget must be included using the
451
+ script tag `<script src="{% static 'finder/js/finder-select.js' %}"></script>`.
452
+
453
+ The demonstration app provides an example how this field can be used in a form. Just vist the URL
454
+ http://localhost:8000/demoapp/ and click on the blank area to either select an existing file or
455
+ upload a new one.
456
+
457
+
458
+ ## Thumbnailing
459
+
460
+ In addition to the x- and y-coordinates of the focal point, the `ImageModel` now also stores the
461
+ main area of interest as a square. This information then is used to create thumbnails using
462
+ [Art Direction](https://www.smashingmagazine.com/2016/01/responsive-image-breakpoints-generation/).
463
+
464
+ The method `finder.contrib.image.pil.models.ImageModel.crop()` creates a thumbnail of the given
465
+ image taking the focal point and the resolution into account. The following rules apply when
466
+ cropping and thumbnailing an image:
467
+
468
+ * The thumbnailed image always contains at least the main area of interrest, except for this
469
+ situation: If the thumbnail is narrower than the main area of interest, the thumbnail will be
470
+ cropped to the center of that area.
471
+ * The resolution of the area to be thumbnailed is always at least the same as the resolution of the
472
+ original image.
473
+
474
+
475
+ will take the resolution of the corresponsding image into
476
+ consideration. This will allow to create different versions of the same canonical image, depending
477
+ on the width of the device the image is displayed.
478
+
479
+ By extending the focal point to three degrees of freedom, it is possible to create a thumbnail
480
+ taking the resolution into account. This allows us to create thumbnails taking art direction into
481
+ consideration. For instance, if a user uploads a portrait image, the focal point can be set to the
482
+ head of the person in that image.
483
+
484
+
485
+ ## Validating Uploads
486
+
487
+ Every uploaded payload is validated before it is written to storage. A validator is a callable
488
+
489
+ ```python
490
+ def my_validator(file_name, file, owner, mime_type):
491
+ ...
492
+ ```
493
+
494
+ that raises `finder.exceptions.FileValidationError` to reject the upload. It is handed the uploaded
495
+ file rewound and opened for writing, so it may also **rewrite the content in place** rather than
496
+ reject it — that is how the bundled SVG sanitizer works. Rejecting a payload leaves nothing behind:
497
+ validation runs before the file reaches the storage backend.
498
+
499
+ This is deliberately the same contract django-filer uses for `FILER_ADD_FILE_VALIDATORS`, so
500
+ validators written for filer — including third-party ones — work here unchanged. A validator
501
+ signalling rejection with a plain `ValueError` is accepted too.
502
+
503
+ By default finder rejects the formats a browser executes in the media origin, and sanitizes SVG
504
+ uploads (which needs `py-svg-hush`, part of the `svg` extra):
505
+
506
+ | MIME-type | Validator |
507
+ |---|---|
508
+ | `text/html` | `finder.validators.deny_html` |
509
+ | `application/xhtml+xml`, `application/xml`, `text/xml`, `application/xslt+xml` | `finder.validators.deny` |
510
+ | `image/svg+xml` | `finder.contrib.image.svg.validators.sanitize_svg` |
511
+
512
+ Unlike django-filer, finder does **not** deny `application/octet-stream`: `FileModel` is the
513
+ documented fallback for everything that matches no other model, and browsers report that MIME-type
514
+ for plenty of harmless files. Add `{'application/octet-stream': ['finder.validators.deny']}` for
515
+ filer's behaviour.
516
+
517
+ Add your own validators with `FINDER_PAYLOAD_VALIDATORS`, a dict mapping a MIME-type to a list of
518
+ validators. The MIME-type may be exact, a subtype wildcard (`image/*`) or `*/*`; more general
519
+ matches run first. Values are dotted paths, callables or classes.
520
+
521
+ ```python
522
+ FINDER_PAYLOAD_VALIDATORS = {
523
+ 'image/*': ['myapp.validators.strip_exif'],
524
+ }
525
+ ```
526
+
527
+ Drop a default with `FINDER_REMOVE_PAYLOAD_VALIDATORS`, a list of MIME-types:
528
+
529
+ ```python
530
+ FINDER_REMOVE_PAYLOAD_VALIDATORS = ['image/svg+xml']
531
+ ```
532
+
533
+
534
+ ## Further Steps
535
+
536
+ * A quota system, which allows to limit the amount of disk space a user can use.
537
+
538
+
539
+ ## Further Readings
540
+
541
+ https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images
542
+ https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/
543
+ https://www.smashingmagazine.com/2016/01/responsive-image-breakpoints-generation/
544
+ https://www.smashingmagazine.com/2016/09/automating-art-direction-with-the-responsive-image-breakpoints-generator/
545
+ https://cloudfour.com/thinks/responsive-images-101-part-9-image-breakpoints/
546
+ https://cloudfour.com/thinks/sensible-jumps-in-responsive-image-file-sizes/
547
+
548
+ https://cloudfour.com/thinks/the-real-conflict-behind-picture-and-srcset/
549
+
550
+ ## License
551
+
552
+ This part of the code will be licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT).