diff --git a/modules/weko-admin/weko_admin/config.py b/modules/weko-admin/weko_admin/config.py index 7f773dd66d..307ab0ecd3 100644 --- a/modules/weko-admin/weko_admin/config.py +++ b/modules/weko-admin/weko_admin/config.py @@ -1240,8 +1240,10 @@ WEKO_ADMIN_RESTRICTED_ACCESS_SETTINGS = { "secret_URL_file_download": { "secret_expiration_date": 30, + "max_secret_expiration_date":30, "secret_expiration_date_unlimited_chk": False, "secret_download_limit": 10, + "max_secret_download_limit":10, "secret_download_limit_unlimited_chk": False, }, "content_file_download": { diff --git a/modules/weko-admin/weko_admin/static/js/weko_admin/restricted_access.js b/modules/weko-admin/weko_admin/static/js/weko_admin/restricted_access.js index 0e6d25e58a..1f9cb325be 100644 --- a/modules/weko-admin/weko_admin/static/js/weko_admin/restricted_access.js +++ b/modules/weko-admin/weko_admin/static/js/weko_admin/restricted_access.js @@ -2,12 +2,18 @@ const {useState, useEffect} = React; const CONTENT_FILE_DOWNLOAD_LABEL = document.getElementById('content_file_download_label').value; const DOWNLOAD_LIMIT_LABEL = document.getElementById('download_limit_label').value; const EXPIRATION_DATE_LABEL = document.getElementById('expiration_date_label').value; +const MAX_DOWNLOAD_LIMIT_LABEL= document.getElementById('max_download_limit_label').value +const MAX_EXPIRATION_DATE_LABEL= document.getElementById('max_expiration_date_label').value +const EXPIRATION_DATE_INITIAL_LABEL = document.getElementById('expiration_date_initial_label').value; +const DOWNLOAD_LIMIT_INITIAL_LABEL = document.getElementById('download_limit_initial_label').value; const UNLIMITED_LABEL = document.getElementById('unlimited_label').value; const SAVE_LABEL = document.getElementById('save_label').value; const CHECK_INPUT_DOWNLOAD = document.getElementById('check_input_download').value; const CHECK_INPUT_EXPIRATION_DATE = document.getElementById('check_input_expiration_date').value; const EMPTY_DOWNLOAD = document.getElementById('empty_download').value; const EMPTY_EXPIRATION_DATE = document.getElementById('empty_expiration_date').value; +const EMPTY_MAX_EXPIRATION_DATE = document.getElementById('empty_max_expiration_date').value; +const EMPTY_MAX_DOWNLOAD_LIMIT = document.getElementById('empty_max_download_limit').value; const USAGE_REPORT_WORKFLOW_ACCESS_LABEL = document.getElementById('usage_report_workflow_access_label').value const MAXINT = Number(document.getElementById('maxint').value) const MAX_DOWNLOAD_LIMIT = MAXINT; @@ -64,45 +70,65 @@ function InputComponent({ label, currentValue, checkboxValue, + canSetUnlimited, value, setValue, inputId, checkboxId, - disabledAll=false + disabledAll=false, + maxLength=String(MAXINT).length, + max=MAXINT }) { - const style = {marginRight: "5px", marginLeft: "15px"} + const style = {marginRight: "5px", marginLeft: "15px"}; + const containerStyle ={display: 'flex',gap:'15px',marginBottom:'2px'}; function handleChange(event) { event.preventDefault(); let target = event.target; let key = target.id; - let updateValue = target.type === 'checkbox' ? target.checked : target.value; + let updateValue = target.value; - if (target.type !== 'checkbox') { - if (!event.target.validity.valid) { - updateValue = value[key]; - } - if (isNaN(updateValue)) { - try { - updateValue = parseInt(updateValue); - } catch (e) { - console.log(e); - } + if (parseInt(updateValue) > max) { + updateValue = max; + } + + if (!event.target.validity.valid) { + updateValue = value[key]; + } + if (isNaN(updateValue)) { + try { + updateValue = parseInt(updateValue); + } catch (e) { + console.log(e); } } setValue({...value, ...{[key]: updateValue}}); } + function handleUnlimited(event) { + let target = event.target; + let key = target.id; + let updateValue = target.type === 'checkbox' ? target.checked : target.value; + if (canSetUnlimited) { + if (!event.target.validity.valid) { + updateValue = value[key]; + } + setValue({...value, ...{[key]: updateValue}}); + } + } return ( -
- - + + + {canSetUnlimited && ( + )}
) } @@ -121,9 +148,9 @@ function InputComponent({ function SecretURLFileDownloadLayout({value, setValue}) { const { secret_download_limit, - secret_download_limit_unlimited_chk, secret_expiration_date, - secret_expiration_date_unlimited_chk, + max_secret_expiration_date, + max_secret_download_limit, secret_enable } = value; @@ -162,26 +189,50 @@ function SecretURLFileDownloadLayout({value, setValue}) { {/* end enabled checkbox */} - - + + + + @@ -193,9 +244,7 @@ function SecretURLFileDownloadLayout({value, setValue}) { function ContentFileDownloadLayout({value, setValue}) { const { download_limit, - download_limit_unlimited_chk, expiration_date, - expiration_date_unlimited_chk } = value; return ( @@ -210,18 +259,16 @@ function ContentFileDownloadLayout({value, setValue}) { @@ -250,6 +297,7 @@ function UsageReportWorkflowAccessLayout({value, setValue}) { checkboxValue={expiration_date_access_unlimited_chk} inputId="expiration_date_access" checkboxId="expiration_date_access_unlimited_chk" + canSetUnlimited={true} value={value} setValue={setValue} /> @@ -480,13 +528,22 @@ function RestrictedAccessLayout({ showErrorMessage(MESSAGE_MISSING_DATA); return false; } - + + // Ensure the values are integers + const parsedSecretURLFileDownload = { + ...secretURLFileDownload, + secret_download_limit: parseInt(secretURLFileDownload.secret_download_limit, 10), + max_secret_expiration_date: parseInt(secretURLFileDownload.max_secret_expiration_date, 10), + max_secret_download_limit: parseInt(secretURLFileDownload.max_secret_download_limit, 10), + secret_expiration_date: parseInt(secretURLFileDownload.secret_expiration_date, 10) + }; + let data = { - secret_URL_file_download:secretURLFileDownload, + secret_URL_file_download:parsedSecretURLFileDownload, content_file_download: contentFileDownload, usage_report_workflow_access: usageReportWorkflowAccess, terms_and_conditions: terms_data["data"] - } + }; $.ajax({ url: URL, @@ -510,46 +567,51 @@ function RestrictedAccessLayout({ function validateSecretURLFileDownload() { const { secret_download_limit, - secret_download_limit_unlimited_chk, secret_expiration_date, - secret_expiration_date_unlimited_chk + max_secret_expiration_date, + max_secret_download_limit } = secretURLFileDownload; let errorMessage; - if (secret_expiration_date === "" && !secret_expiration_date_unlimited_chk) { + if (secret_expiration_date === "" ) { errorMessage = EMPTY_EXPIRATION_DATE; - } else if (secret_download_limit === "" && !secret_download_limit_unlimited_chk) { + } else if (secret_download_limit === "" ) { errorMessage = EMPTY_DOWNLOAD; - } else if ((secret_expiration_date < 1 && !secret_expiration_date_unlimited_chk) - || secret_expiration_date > MAX_EXPIRATION_DATE) { + } else if ((secret_expiration_date < 1 ) + || secret_expiration_date > max_secret_expiration_date) { errorMessage = CHECK_INPUT_EXPIRATION_DATE; - } else if ((secret_download_limit < 1 && !secret_download_limit_unlimited_chk) - || secret_download_limit > MAX_DOWNLOAD_LIMIT) { + } else if ((secret_download_limit < 1 ) + || secret_download_limit > max_secret_download_limit) { errorMessage = CHECK_INPUT_DOWNLOAD; + } else if (max_secret_expiration_date === ""){ + errorMessage = EMPTY_MAX_EXPIRATION_DATE; + } else if (max_secret_download_limit === ""){ + errorMessage = EMPTY_DOWNLOAD; + } else if (max_secret_expiration_date < 1 ){ + errorMessage = EMPTY_MAX_DOWNLOAD_LIMIT; + } else if (max_secret_download_limit < 1 ){ + errorMessage = EMPTY_DOWNLOAD; } - return errorMessage; } function validateContentFileDownload() { const { download_limit, - download_limit_unlimited_chk, expiration_date, - expiration_date_unlimited_chk } = contentFileDownload; let errorMessage; - if (expiration_date === "" && !expiration_date_unlimited_chk) { + if (expiration_date === "" ) { errorMessage = EMPTY_EXPIRATION_DATE; - } else if (download_limit === "" && !download_limit_unlimited_chk) { + } else if (download_limit === "" ) { errorMessage = EMPTY_DOWNLOAD; - } else if ((expiration_date < 1 && !expiration_date_unlimited_chk) + } else if ((expiration_date < 1 ) || expiration_date > MAX_EXPIRATION_DATE) { errorMessage = CHECK_INPUT_EXPIRATION_DATE; - } else if ((download_limit < 1 && !download_limit_unlimited_chk) + } else if ((download_limit < 1 ) || download_limit > MAX_DOWNLOAD_LIMIT) { errorMessage = CHECK_INPUT_DOWNLOAD; } diff --git a/modules/weko-admin/weko_admin/templates/weko_admin/admin/restricted_access_settings.html b/modules/weko-admin/weko_admin/templates/weko_admin/admin/restricted_access_settings.html index 715e652a74..3efa7d2f0c 100644 --- a/modules/weko-admin/weko_admin/templates/weko_admin/admin/restricted_access_settings.html +++ b/modules/weko-admin/weko_admin/templates/weko_admin/admin/restricted_access_settings.html @@ -44,20 +44,29 @@ {% from "weko_theme/macros/modal_page.html" import all_modal %} {% set download_limit = _("Download Limit") %} {% set expiration_date = _("Expiration Date") %} + {% set max_download_limit = _("Max Download Limit") %} + {% set max_expiration_date = _("Max Expiration Date") %} + {% set expiration_date_initial = _("Expiration Date Initial Value") %} + {% set download_limit_initial = _("Download Limit Initial Value") %} + + + + + value='{{ _("Must set a positive integer and less than %(name1)s for %(name2)s.", name1=max_download_limit, name2=download_limit_initial) }}'/> + value='{{ _("Must set a positive integer and less than %(name1)s for %(name2)s.", name1=max_expiration_date, name2=expiration_date_initial) }}'/> - + + diff --git a/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.mo b/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.mo index a9b27e75fd..c57d163986 100644 Binary files a/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.mo and b/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.mo differ diff --git a/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.po b/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.po index ee0d04ea03..1cf47d373c 100644 --- a/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.po +++ b/modules/weko-admin/weko_admin/translations/en/LC_MESSAGES/messages.po @@ -1105,6 +1105,14 @@ msgstr "" msgid "Expiration Date" msgstr "" +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:47 +msgid "Max Expiration Date" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:488 +msgid "Max Download Limit" +msgstr "" + #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:48 msgid "Content File Download" msgstr "" @@ -1118,10 +1126,14 @@ msgid "Usage Report Workflow Access" msgstr "Usage Report Workflow Access" #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:55 +#, python-format +msgid "Must set a positive integer and less than %(name1)s for %(name2)s." +msgstr "" + #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:57 #, python-format -msgid "Must set a positive integer for %(name)s." -msgstr "Must set a positive integer for %(name)s." +msgid "Must set a positive integer and less than %(name1)s for %(name2)s." +msgstr "" #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:58 #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:59 diff --git a/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.mo b/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.mo index f653912324..1c1e4a342a 100644 Binary files a/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.mo and b/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.mo differ diff --git a/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.po b/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.po index 16f7cff6c3..1b102bbcf8 100644 --- a/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.po +++ b/modules/weko-admin/weko_admin/translations/ja/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: weko-admin 0.1.0.dev20170000\n" "Report-Msgid-Bugs-To: wekosoftware@nii.ac.jp\n" -"POT-Creation-Date: 2021-07-29 21:12+0900\n" +"POT-Creation-Date: 2025-02-25 09:34+0900\n" "PO-Revision-Date: 2018-01-19 19:54+0900\n" "Last-Translator: FULL NAME \n" "Language: ja\n" @@ -18,625 +18,635 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.5.1\n" -#: weko_admin/admin.py:105 +#: tests/test_admin.py:1669 tests/test_admin.py:1675 tests/test_admin.py:1677 +#: tests/test_admin.py:1694 tests/test_admin.py:1702 tests/test_admin.py:1710 +#: tests/test_admin.py:1718 weko_admin/admin.py:134 +msgid "completed" +msgstr "処理が完了しました。" + +#: tests/test_admin.py:1727 weko_admin/admin.py:127 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:43 +msgid "executing..." +msgstr "実行中..." + +#: tests/test_admin.py:1737 weko_admin/admin.py:125 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:42 +msgid "haserror" +msgstr "エラー発生中につき実行できません。" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:152 +msgid "15 mins" +msgstr "15分" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:153 +msgid "30 mins" +msgstr "30分" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:154 +msgid "45 mins" +msgstr "45分" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:155 +msgid "60 mins" +msgstr "1時間" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:156 +msgid "180 mins" +msgstr "3時間" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:157 +msgid "360 mins" +msgstr "6時間" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:158 +msgid "720 mins" +msgstr "12時間" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:159 +msgid "1440 mins" +msgstr "1日" + +#: weko_admin/admin.py:220 msgid "deny access" msgstr "アクセス拒否" -#: weko_admin/admin.py:124 +#: weko_admin/admin.py:239 #, fuzzy msgid "Successfully update color." msgstr "スケジュールを変更しました" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Monday" msgstr "月曜日" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Tuesday" msgstr "火曜日" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Wednesday" msgstr "水曜日" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Thursday" msgstr "木曜日" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Friday" msgstr "金曜日" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Saturday" msgstr "土曜日" -#: weko_admin/admin.py:312 +#: weko_admin/admin.py:440 msgid "Sunday" msgstr "日曜日" -#: weko_admin/admin.py:342 weko_admin/tasks.py:92 +#: weko_admin/admin.py:469 weko_admin/tasks.py:161 msgid " Log report." msgstr "ログレポート" -#: weko_admin/admin.py:353 +#: weko_admin/admin.py:480 msgid "Successfully sent the reports to the recepients." msgstr "レポートを送信しました" -#: weko_admin/admin.py:363 +#: weko_admin/admin.py:490 msgid "Unexpected error occurred." msgstr "予期しないエラーが発生しました" -#: weko_admin/admin.py:397 +#: weko_admin/admin.py:521 msgid "Successfully Changed Schedule." msgstr "スケジュールを変更しました" -#: weko_admin/admin.py:399 +#: weko_admin/admin.py:523 msgid "Could Not Save Changes." msgstr "変更を保存できませんでした" -#: weko_admin/admin.py:464 weko_admin/admin.py:572 weko_admin/admin.py:806 +#: weko_admin/admin.py:578 weko_admin/admin.py:604 weko_admin/admin.py:705 +#: weko_admin/admin.py:957 msgid "Successfully Changed Settings." msgstr "設定を変更しました" -#: weko_admin/admin.py:485 weko_admin/views.py:564 +#: weko_admin/admin.py:608 weko_admin/views.py:665 msgid "Could not save data." msgstr "データを保存できませんでした" -#: weko_admin/admin.py:496 -msgid "Could not get restricted data: " +#: weko_admin/admin.py:619 +#, fuzzy, python-format +msgid "Could not get restricted data: %s" msgstr "制限付きデータを取得できませんでした" -#: weko_admin/admin.py:497 +#: weko_admin/admin.py:620 msgid "Could not get restricted data." msgstr "制限付きデータを取得できませんでした" -#: weko_admin/admin.py:576 weko_admin/admin.py:809 weko_admin/admin.py:812 +#: weko_admin/admin.py:709 weko_admin/admin.py:960 weko_admin/admin.py:963 msgid "Failurely Changed Settings." msgstr "設定変更に失敗しました" -#: weko_admin/admin.py:849 +#: weko_admin/admin.py:1000 #, fuzzy msgid "Successfully Changed Settings" msgstr "設定を変更しました" -#: weko_admin/admin.py:853 +#: weko_admin/admin.py:1004 #, fuzzy msgid "Failed To Change Settings" msgstr "設定変更に失敗しました" -#: weko_admin/admin.py:913 +#: weko_admin/admin.py:1066 msgid "Prefix" msgstr "" -#: weko_admin/admin.py:919 +#: weko_admin/admin.py:1072 msgid "Suffix" msgstr "" -#: weko_admin/admin.py:921 +#: weko_admin/admin.py:1074 msgid "Enable/Disable" msgstr "" -#: weko_admin/admin.py:931 +#: weko_admin/admin.py:1084 msgid "Repository" msgstr "" -#: weko_admin/admin.py:931 +#: weko_admin/admin.py:1084 msgid "JaLC DOI" msgstr "" -#: weko_admin/admin.py:932 +#: weko_admin/admin.py:1085 msgid "JaLC CrossRef DOI" msgstr "" -#: weko_admin/admin.py:933 +#: weko_admin/admin.py:1086 msgid "JaLC DataCite DOI" msgstr "" -#: weko_admin/admin.py:934 +#: weko_admin/admin.py:1087 msgid "NDL JaLC DOI" msgstr "" -#: weko_admin/admin.py:935 +#: weko_admin/admin.py:1088 msgid "Semi-automatic Suffix" msgstr "" -#: weko_admin/admin.py:952 +#: weko_admin/admin.py:1105 msgid "Only allow half with 1-bytes character in input" msgstr "" -#: weko_admin/admin.py:1017 +#: weko_admin/admin.py:1170 msgid "Specified repository is already registered." msgstr "指定したリポジトリが既に登録されています。" -#: weko_admin/admin.py:1137 +#: weko_admin/admin.py:1298 msgid "ID" msgstr "" -#: weko_admin/admin.py:1138 +#: weko_admin/admin.py:1299 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:50 msgid "Item Name(EN)" msgstr "項目名(英)" -#: weko_admin/admin.py:1139 +#: weko_admin/admin.py:1300 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:51 msgid "Item Name(JP)" msgstr "項目名(日)" -#: weko_admin/admin.py:1140 +#: weko_admin/admin.py:1301 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:52 msgid "Mapping" msgstr "マッピング" -#: weko_admin/admin.py:1141 +#: weko_admin/admin.py:1302 +msgid "UI" +msgstr "" + +#: weko_admin/admin.py:1303 #, fuzzy msgid "Active" msgstr "保存" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:59 -msgid "UiType" -msgstr "UI" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:60 -msgid "DisplayNumber" -msgstr "表示件数" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:61 -msgid "OpenClose" -msgstr "開閉状態" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:62 -msgid "OpenClose Open" -msgstr "開" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:63 -msgid "OpenClose Close" -msgstr "閉" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 -msgid "DisplayNumber Validation1" -msgstr "表示件数は1以上99以下の整数値で入力する必要があります。" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:65 -msgid "DisplayNumber Validation2" -msgstr "CheckboxListを選択した場合は表示件数を入力する必要があります。" - -#: weko_admin/admin.py:1210 weko_admin/admin.py:1237 weko_admin/admin.py:1246 -#: weko_admin/admin.py:1255 weko_admin/admin.py:1264 weko_admin/admin.py:1273 -#: weko_admin/admin.py:1282 weko_admin/admin.py:1291 weko_admin/admin.py:1309 -#: weko_admin/admin.py:1318 weko_admin/admin.py:1327 weko_admin/admin.py:1336 -#: weko_admin/admin.py:1344 weko_admin/admin.py:1352 +#: weko_admin/admin.py:1372 weko_admin/admin.py:1399 weko_admin/admin.py:1408 +#: weko_admin/admin.py:1417 weko_admin/admin.py:1426 weko_admin/admin.py:1435 +#: weko_admin/admin.py:1444 weko_admin/admin.py:1453 weko_admin/admin.py:1471 +#: weko_admin/admin.py:1480 weko_admin/admin.py:1489 weko_admin/admin.py:1498 +#: weko_admin/admin.py:1506 weko_admin/admin.py:1514 msgid "Setting" msgstr "設定" -#: weko_admin/admin.py:1211 +#: weko_admin/admin.py:1373 msgid "Style" msgstr "様式" -#: weko_admin/admin.py:1219 weko_admin/admin.py:1228 weko_admin/admin.py:1300 +#: weko_admin/admin.py:1381 weko_admin/admin.py:1390 weko_admin/admin.py:1462 msgid "Statistics" msgstr "統計" -#: weko_admin/admin.py:1220 +#: weko_admin/admin.py:1382 msgid "Report" msgstr "レポート" -#: weko_admin/admin.py:1229 +#: weko_admin/admin.py:1391 msgid "Feedback Mail" msgstr "" -#: weko_admin/admin.py:1238 +#: weko_admin/admin.py:1400 msgid "Stats" msgstr "統計" -#: weko_admin/admin.py:1247 +#: weko_admin/admin.py:1409 #: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:45 msgid "Log Analysis" msgstr "ログ分析" -#: weko_admin/admin.py:1256 -#: weko_admin/templates/weko_admin/admin/site_info.html:48 +#: weko_admin/admin.py:1418 +#: weko_admin/templates/weko_admin/admin/site_info.html:50 #, fuzzy msgid "Language" msgstr "言語" -#: weko_admin/admin.py:1265 +#: weko_admin/admin.py:1427 msgid "WebAPI Account" msgstr "" -#: weko_admin/admin.py:1274 +#: weko_admin/admin.py:1436 msgid "Ranking" msgstr "ランキング" -#: weko_admin/admin.py:1283 +#: weko_admin/admin.py:1445 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:61 msgid "Search" msgstr "検索" -#: weko_admin/admin.py:1292 weko_admin/admin.py:1301 weko_admin/config.py:154 -#: weko_admin/config.py:159 +#: weko_admin/admin.py:1454 weko_admin/admin.py:1463 weko_admin/config.py:168 +#: weko_admin/config.py:173 msgid "Site License" msgstr "サイトライセンス" -#: weko_admin/admin.py:1310 +#: weko_admin/admin.py:1472 #, fuzzy msgid "File Preview" msgstr "ファイルプレビュー" -#: weko_admin/admin.py:1319 +#: weko_admin/admin.py:1481 #, fuzzy msgid "Item Export" msgstr "定型レポート" -#: weko_admin/admin.py:1328 +#: weko_admin/admin.py:1490 msgid "Site Info" msgstr "" -#: weko_admin/admin.py:1337 +#: weko_admin/admin.py:1499 #, fuzzy msgid "Restricted Access" msgstr "サイトアクセス" -#: weko_admin/admin.py:1345 +#: weko_admin/admin.py:1507 msgid "Identifier" msgstr "" -#: weko_admin/admin.py:1353 +#: weko_admin/admin.py:1515 #, fuzzy msgid "Faceted Search" msgstr "ファセット検索一覧" -#: weko_admin/api.py:82 +#: weko_admin/admin.py:1522 +msgid "Maintenance" +msgstr "メンテナンス" + +#: weko_admin/admin.py:1523 +msgid "ElasticSearch Index" +msgstr "インデックス再作成" + +#: weko_admin/api.py:127 #, fuzzy msgid "statistics report" msgstr "統計設定" -#: weko_admin/config.py:129 +#: weko_admin/config.py:143 msgid "No. Of File Downloads" msgstr "ファイルダウンロード数" -#: weko_admin/config.py:130 +#: weko_admin/config.py:144 msgid "No. Of File Previews" msgstr "ファイルプレビュー数" -#: weko_admin/config.py:131 +#: weko_admin/config.py:145 #, fuzzy msgid "No. Of Paid File Downloads" msgstr "ファイルダウンロード数" -#: weko_admin/config.py:132 +#: weko_admin/config.py:146 #, fuzzy msgid "No. Of Paid File Previews" msgstr "ファイルプレビュー数" -#: weko_admin/config.py:133 +#: weko_admin/config.py:147 msgid "Detail Views Per Index" msgstr "インデックス毎の詳細ビュー" -#: weko_admin/config.py:134 +#: weko_admin/config.py:148 msgid "Detail Views Count" msgstr "詳細ビュー数" -#: weko_admin/config.py:135 +#: weko_admin/config.py:149 msgid "Usage Count By User" msgstr "ユーザー別使用数" -#: weko_admin/config.py:136 +#: weko_admin/config.py:150 msgid "Search Keyword Ranking" msgstr "検索キーワードランキング" -#: weko_admin/config.py:137 +#: weko_admin/config.py:151 msgid "Number Of Access By Host" msgstr "ホスト別アクセス数" -#: weko_admin/config.py:138 +#: weko_admin/config.py:152 msgid "User Affiliation Information" msgstr "ユーザー所属情報" -#: weko_admin/config.py:139 +#: weko_admin/config.py:153 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:21 msgid "Access Count By Site License" msgstr "サイトライセンス別アクセス数" -#: weko_admin/config.py:144 +#: weko_admin/config.py:158 msgid "Open-Access No. Of File Downloads" msgstr "オープンアクセスファイルダウンロード数" -#: weko_admin/config.py:145 +#: weko_admin/config.py:159 msgid "Open-Access No. Of File Previews" msgstr "オープンアクセスファイルプレビュー数" -#: weko_admin/config.py:146 +#: weko_admin/config.py:160 msgid "Access Number Breakdown By Site License" msgstr "サイトライセンス別アクセス番号内訳" -#: weko_admin/config.py:152 weko_admin/config.py:157 +#: weko_admin/config.py:166 weko_admin/config.py:171 msgid "File Name" msgstr "ファイル名" -#: weko_admin/config.py:152 weko_admin/config.py:157 weko_admin/config.py:163 +#: weko_admin/config.py:166 weko_admin/config.py:171 weko_admin/config.py:177 msgid "Registered Index Name" msgstr "登録インデックス名" -#: weko_admin/config.py:153 +#: weko_admin/config.py:167 #, fuzzy msgid "No. Of Times Downloaded" msgstr "ダウンロード/表示回数" -#: weko_admin/config.py:153 weko_admin/config.py:158 +#: weko_admin/config.py:167 weko_admin/config.py:172 msgid "Non-Logged In User" msgstr "非ログインユーザー" -#: weko_admin/config.py:154 weko_admin/config.py:159 +#: weko_admin/config.py:168 weko_admin/config.py:173 msgid "Logged In User" msgstr "ログインユーザー" -#: weko_admin/config.py:154 weko_admin/config.py:159 +#: weko_admin/config.py:168 weko_admin/config.py:173 msgid "Admin" msgstr "管理者" -#: weko_admin/config.py:155 weko_admin/config.py:160 +#: weko_admin/config.py:169 weko_admin/config.py:174 msgid "Registrar" msgstr "登録者" -#: weko_admin/config.py:158 +#: weko_admin/config.py:172 #, fuzzy msgid "No. Of Times Viewed" msgstr "ファイルプレビュー数" -#: weko_admin/config.py:161 +#: weko_admin/config.py:175 msgid "Index" msgstr "インデックス" -#: weko_admin/config.py:161 +#: weko_admin/config.py:175 msgid "No. Of Views" msgstr "ビュー数" -#: weko_admin/config.py:163 +#: weko_admin/config.py:177 msgid "Title" msgstr "タイトル" -#: weko_admin/config.py:163 +#: weko_admin/config.py:177 msgid "View Count" msgstr "ビュー数" -#: weko_admin/config.py:164 +#: weko_admin/config.py:178 msgid "Non-logged-in User" msgstr "非ログインユーザー" -#: weko_admin/config.py:165 +#: weko_admin/config.py:179 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:49 msgid "Mail address" msgstr "メールアドレス" -#: weko_admin/config.py:166 +#: weko_admin/config.py:180 msgid "Username" msgstr "ユーザー名" -#: weko_admin/config.py:167 +#: weko_admin/config.py:181 msgid "File download count" msgstr "ファイルダウンロード数" -#: weko_admin/config.py:168 +#: weko_admin/config.py:182 msgid "File playing count" msgstr "ファイル再生回数" -#: weko_admin/config.py:169 +#: weko_admin/config.py:183 #: weko_admin/templates/weko_admin/admin/report.html:116 msgid "Search Keyword" msgstr "検索キーワード" -#: weko_admin/config.py:169 weko_admin/config.py:174 +#: weko_admin/config.py:183 weko_admin/config.py:188 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:25 msgid "Number Of Searches" msgstr "検索数" -#: weko_admin/config.py:170 +#: weko_admin/config.py:184 msgid "Host" msgstr "ホスト" -#: weko_admin/config.py:170 +#: weko_admin/config.py:184 msgid "IP Address" msgstr "IPアドレス" -#: weko_admin/config.py:171 weko_admin/config.py:173 +#: weko_admin/config.py:185 weko_admin/config.py:187 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:24 msgid "WEKO Top Page Access Count" msgstr "WEKOトップページアクセス数" -#: weko_admin/config.py:172 +#: weko_admin/config.py:186 msgid "Role" msgstr "ロール" -#: weko_admin/config.py:172 +#: weko_admin/config.py:186 msgid "Number Of Users" msgstr "利用者数" -#: weko_admin/config.py:174 +#: weko_admin/config.py:188 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:26 msgid "Number Of Views" msgstr "ビュー数" -#: weko_admin/config.py:175 +#: weko_admin/config.py:189 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:27 msgid "Number Of File download" msgstr "ファイルダウンロード数" -#: weko_admin/config.py:176 +#: weko_admin/config.py:190 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:28 msgid "Number Of File Regeneration" msgstr "ファイル再生回数" -#: weko_admin/config.py:181 +#: weko_admin/config.py:195 msgid "FileDownload_" msgstr "ファイルダウンロード" -#: weko_admin/config.py:182 +#: weko_admin/config.py:196 msgid "FilePreview_" msgstr "ファイルプレビュー" -#: weko_admin/config.py:183 +#: weko_admin/config.py:197 #, fuzzy msgid "PayFileDownload_" msgstr "ファイルダウンロード" -#: weko_admin/config.py:184 +#: weko_admin/config.py:198 #, fuzzy msgid "PayFilePreview_" msgstr "ファイルプレビュー" -#: weko_admin/config.py:185 +#: weko_admin/config.py:199 msgid "IndexAccess_" msgstr "索引アクセス" -#: weko_admin/config.py:186 +#: weko_admin/config.py:200 msgid "DetailView_" msgstr "詳細ビュー" -#: weko_admin/config.py:187 +#: weko_admin/config.py:201 msgid "FileUsingPerUser_" msgstr "ユーザー毎のファイル使用" -#: weko_admin/config.py:188 +#: weko_admin/config.py:202 msgid "SearchCount_" msgstr "検索回数" -#: weko_admin/config.py:189 +#: weko_admin/config.py:203 msgid "UserAffiliate_" msgstr "加入ユーザー" -#: weko_admin/config.py:190 +#: weko_admin/config.py:204 msgid "SiteAccess_" msgstr "サイトアクセス" -#: weko_admin/config.py:191 +#: weko_admin/config.py:205 msgid "TopPageAccess_" msgstr "トップページへのアクセス" -#: weko_admin/ext.py:74 +#: weko_admin/ext.py:75 msgid "A translation string" msgstr "翻訳文字列" -#: weko_admin/utils.py:235 +#: weko_admin/utils.py:234 msgid "Input type is invalid. Please check again." msgstr "入力タイプが無効です。再度確認してください" -#: weko_admin/utils.py:238 weko_admin/views.py:284 weko_admin/views.py:289 +#: weko_admin/utils.py:237 weko_admin/views.py:326 weko_admin/views.py:331 msgid "Account information is invalid. Please check again." msgstr "アカウント情報が無効です。再度確認してください" -#: weko_admin/utils.py:331 +#: weko_admin/utils.py:328 #, fuzzy msgid "Registered Users" msgstr "登録ユーザー" -#: weko_admin/utils.py:373 +#: weko_admin/utils.py:372 msgid "Aggregation Month" msgstr "月別集計" -#: weko_admin/utils.py:388 +#: weko_admin/utils.py:389 msgid "Total Detail Views" msgstr "詳細ビュー合計" -#: weko_admin/utils.py:396 +#: weko_admin/utils.py:398 msgid "Site license member" msgstr "サイトライセンスメンバー" -#: weko_admin/utils.py:400 +#: weko_admin/utils.py:402 msgid "Other than site license" msgstr "サイトライセンス外" -#: weko_admin/utils.py:411 +#: weko_admin/utils.py:413 #, fuzzy msgid "Institution Name" msgstr "機関名" -#: weko_admin/utils.py:1198 +#: weko_admin/utils.py:1219 msgid "Cannot update Feedback email settings." msgstr "" -#: weko_admin/utils.py:1225 +#: weko_admin/utils.py:1246 msgid "Author is duplicated." msgstr " 著者が重複しています。" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:48 -#: weko_admin/utils.py:1232 +#: weko_admin/utils.py:1253 msgid "Duplicate Email Addresses." msgstr "メールアドレスが重複しています。" -#: weko_admin/views.py:102 +#: weko_admin/views.py:116 #, python-format msgid "%(icon)s Session" msgstr "%(icon)s セッション" -#: weko_admin/views.py:108 +#: weko_admin/views.py:122 msgid "Session" msgstr "セッション" -#: weko_admin/views.py:132 +#: weko_admin/views.py:147 msgid "Session lifetime was updated." msgstr "セッションライフタイムは更新されました" -#: weko_admin/views.py:137 -msgid "15 mins" -msgstr "15分" - -#: weko_admin/views.py:138 -msgid "30 mins" -msgstr "30分" - -#: weko_admin/views.py:139 -msgid "45 mins" -msgstr "45分" - -#: weko_admin/views.py:140 -msgid "60 mins" -msgstr "1時間" - -#: weko_admin/views.py:141 -msgid "180 mins" -msgstr "3時間" - -#: weko_admin/views.py:142 -msgid "360 mins" -msgstr "6時間" - -#: weko_admin/views.py:143 -msgid "720 mins" -msgstr "12時間" - -#: weko_admin/views.py:144 -msgid "1440 mins" -msgstr "1日" - -#: weko_admin/views.py:277 +#: weko_admin/views.py:319 msgid "Header Error" msgstr "ヘッダエラー" -#: weko_admin/views.py:559 +#: weko_admin/views.py:660 msgid "Restricted Access was successfully updated." msgstr "制限公開の設定を変更しました。" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:54 -#: weko_admin/views.py:618 weko_admin/views.py:651 +#: weko_admin/views.py:725 weko_admin/views.py:760 msgid "Success" msgstr "成功" -#: weko_admin/views.py:627 +#: weko_admin/views.py:734 msgid "Failed to update due to server error." msgstr "サーバーエラーのため、更新に失敗しました。" -#: weko_admin/views.py:632 +#: weko_admin/views.py:739 msgid "Failed to create due to server error." msgstr "サーバーエラーのため、作成に失敗しました。" -#: weko_admin/views.py:635 +#: weko_admin/views.py:742 msgid "" "The item name/mapping is already exists. Please input other faceted " "item/mapping." msgstr "既に存在する項目名・マッピングです。別のファセット項目名・マッピングを入力してください。" -#: weko_admin/views.py:658 weko_admin/views.py:661 +#: weko_admin/views.py:767 weko_admin/views.py:770 msgid "Failed to delete due to server error." msgstr "サーバーエラーのため、削除に失敗しました。" @@ -644,21 +654,21 @@ msgstr "サーバーエラーのため、削除に失敗しました。" msgid "Welcome to" msgstr "ようこそ" -#: weko_admin/templates/weko_admin/admin/block_style.html:87 -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:67 +#: weko_admin/templates/weko_admin/admin/block_style.html:89 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:74 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:44 #: weko_admin/templates/weko_admin/admin/file_preview_settings.html:62 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:94 #: weko_admin/templates/weko_admin/admin/lang_settings.html:86 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:169 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:191 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:145 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:170 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:192 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:147 #: weko_admin/templates/weko_admin/admin/report.html:224 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:52 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:422 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:459 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:562 -#: weko_admin/templates/weko_admin/admin/site_info.html:49 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:60 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:442 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:479 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:582 +#: weko_admin/templates/weko_admin/admin/site_info.html:51 #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:174 #: weko_admin/templates/weko_admin/admin/site_license_settings.html:204 #: weko_admin/templates/weko_admin/admin/stats_settings.html:71 @@ -667,11 +677,11 @@ msgstr "ようこそ" msgid "Save" msgstr "保存" -#: weko_admin/templates/weko_admin/admin/block_style.html:91 +#: weko_admin/templates/weko_admin/admin/block_style.html:93 msgid "Color Setting" msgstr "カラー設定" -#: weko_admin/templates/weko_admin/admin/block_style.html:97 +#: weko_admin/templates/weko_admin/admin/block_style.html:99 msgid "Background1" msgstr "背景1" @@ -700,58 +710,95 @@ msgid "_Display" msgstr "表示" #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:59 +msgid "UiType" +msgstr "UI" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:60 +msgid "DisplayNumber" +msgstr "表示件数" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:61 +msgid "OpenClose" +msgstr "開閉状態" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:62 +msgid "OpenClose Open" +msgstr "開" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:63 +msgid "OpenClose Close" +msgstr "閉" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 +msgid "DisplayNumber Validation1" +msgstr "表示件数は1以上99以下の整数値で入力する必要があります。" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:65 +msgid "DisplayNumber Validation2" +msgstr "CheckboxListを選択した場合は表示件数を入力する必要があります。" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:66 msgid "_Hide" msgstr "非表示" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:61 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:68 msgid "List" msgstr "一覧" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:62 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:69 msgid "Create" msgstr "作成" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:63 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:70 #: weko_admin/templates/weko_admin/admin/search_management_settings.html:230 msgid "Edit" msgstr "編集" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:71 #: weko_admin/templates/weko_admin/partials/email_schedule.html:23 msgid "Details" msgstr "詳細" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:66 -#: weko_admin/templates/weko_admin/settings/lifetime.html:55 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:73 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:48 +#: weko_admin/templates/weko_admin/settings/lifetime.html:56 msgid "Cancel" msgstr "キャンセル" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:68 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:75 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:45 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:149 -#: weko_admin/templates/weko_admin/admin/site_info.html:57 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:151 +#: weko_admin/templates/weko_admin/admin/site_info.html:59 #: weko_admin/templates/weko_admin/admin/site_license_settings.html:70 msgid "Delete" msgstr "削除" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:69 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:64 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:541 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:76 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:73 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:561 msgid "Add" msgstr "追加" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:70 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:78 msgid "Please input all required item." msgstr "必須項目は全て入力して下さい。" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:77 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:183 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:79 +msgid "Please select one aggregation mapping." +msgstr "集計マッピングを1つ選択してください。" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:80 +msgid "Already exists." +msgstr "既に存在しています。" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:87 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:184 #: weko_admin/templates/weko_admin/admin/report.html:247 #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:197 msgid "Confirmation" msgstr "確認" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:78 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:88 msgid "Are you sure you want to delete it?" msgstr "削除してよろしいですか?" @@ -822,9 +869,9 @@ msgid "Resend" msgstr "再送信" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:59 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:195 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:196 #: weko_admin/templates/weko_admin/admin/report.html:273 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:75 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:84 msgid "Close" msgstr "閉じる" @@ -863,7 +910,7 @@ msgstr "" #: weko_admin/templates/weko_admin/admin/item_export_settings.html:59 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:78 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:47 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:48 #: weko_admin/templates/weko_admin/admin/stats_settings.html:59 #: weko_admin/templates/weko_admin/partials/email_schedule.html:75 msgid "On" @@ -871,7 +918,7 @@ msgstr "オン" #: weko_admin/templates/weko_admin/admin/item_export_settings.html:65 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:84 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:52 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:53 #: weko_admin/templates/weko_admin/admin/stats_settings.html:64 #: weko_admin/templates/weko_admin/partials/email_schedule.html:80 msgid "Off" @@ -889,70 +936,105 @@ msgstr "対象言語" msgid "Registered language" msgstr "登録言語" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:56 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:57 msgid "Addresses to Filter" msgstr "フィルタリングするアドレス" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:64 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:65 msgid "Enter the IP Addresses to Filter" msgstr "フィルタリングするIPアドレスの入力" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:128 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:129 msgid "Shared Crawler Lists" msgstr "共有クローラーリスト" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:186 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:187 msgid "Are you sure you want to block the given addresses?" msgstr "与えられたアドレスをブロックしてもよろしいですか?" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:41 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:42 msgid "Show/Hide Ranking" msgstr "ランキングの表示/非表示" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:58 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:59 msgid "Period To Judge As New Item" msgstr "新着アイテムとして判断する期間" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:64 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:65 msgid "Day (Range : 1~30)" msgstr "日(範囲:1-30)" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:68 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:69 msgid "Statistical Period" msgstr "統計期間" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:73 -msgid "Day" -msgstr "日" +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:74 +#, fuzzy +msgid "Day (Range : 1~3650)" +msgstr "日(範囲:1-30)" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:77 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:78 msgid "Display Rank" msgstr "表示する順位" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:85 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:83 +#, fuzzy +msgid "(Range : 1~100)" +msgstr "日(範囲:1-30)" + +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:87 msgid "Rankings" msgstr "ランキング" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:91 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:93 msgid "Most Viewed Items" msgstr "最も閲覧されたアイテム" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:102 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:104 msgid "Most Downloaded Items" msgstr "最もダウンロードされたアイテム" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:113 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:115 msgid "User Who Created The Most Items" msgstr "最もアイテムを作成したユーザー" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:124 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:126 msgid "Most Searched Keywords" msgstr "最も検索されたキーワード" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:135 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:137 msgid "New Items" msgstr "新着アイテム" +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:38 +msgid "reindex item_index" +msgstr "アイテムインデックスの再作成" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:39 +msgid "reindex item" +msgstr "アイテムの再インデックス" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:40 +msgid "execute" +msgstr "実行" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:41 +msgid "waiting..." +msgstr "実行可能" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:47 +#, fuzzy +msgid "Execute" +msgstr "実行" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:49 +msgid "validationMsg1" +msgstr "実行モードが選ばれていません。" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:50 +msgid "confirmMessage" +msgstr "本処理の実行にはかなりの時間がかかることが予想されます。インデックスの再作成処理を実行してよいですか?" + #: weko_admin/templates/weko_admin/admin/report.html:54 msgid "Number of items registered" msgstr "登録件数" @@ -1074,7 +1156,7 @@ msgid "Are you sure you want to save changes?" msgstr "変更してもよろしいですか?" #: weko_admin/templates/weko_admin/admin/report.html:255 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:76 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:85 msgid "Send Mail" msgstr "メール送信" @@ -1094,99 +1176,117 @@ msgstr "ダウンロード回数" msgid "Expiration Date" msgstr "有効期限日数" +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:47 +msgid "Max Download Limit" +msgstr "ダウンロード回数上限" + #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:48 +msgid "Max Expiration Date" +msgstr "有効期限日数上限" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:49 +msgid "Expiration Date Initial Value" +msgstr "ダウンロード回数初期値" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:50 +msgid "Download Limit Initial Value" +msgstr "有効期限日数初期値" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:52 msgid "Content File Download" msgstr "コンテンツファイルのダウンロード" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:51 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:59 msgid "Unlimited" msgstr "無制限" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:53 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:61 msgid "Usage Report Workflow Access" msgstr "利用報告ワークフローへのアクセス" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:55 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:57 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:63 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:65 #, python-format -msgid "Must set a positive integer for %(name)s." -msgstr "%(name)sは1以上の整数を設定する必要があります。" +msgid "Must set a positive integer and less than %(name1)s for %(name2)s." +msgstr "%(name2)sは1以上の整数かつ%(name1)s以下の値を設定する必要があります。" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:58 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:59 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:66 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:67 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:68 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:69 #, python-format msgid "Please set %(name)s." msgstr "%(name)sを設定してください。" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:61 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:444 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:451 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:70 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:464 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:471 msgid "English" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:62 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:445 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:452 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:71 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:465 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:472 msgid "Japanese" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:63 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:72 msgid "Terms and Conditions" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:65 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:74 msgid "Please input the Terms and Conditions in English." msgstr "英語の利用規約を入力してください。" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:68 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:77 msgid "Activity" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:69 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:78 #, fuzzy msgid "Item" msgstr "タイトル" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:70 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:79 msgid "WorkFlow" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:71 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:80 #, fuzzy msgid "Status" msgstr "統計" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:72 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:81 #, fuzzy msgid "User" msgstr "ユーザー" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:73 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:82 msgid "Usage Report Reminder Email" msgstr "利用報告督促メール" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:74 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:83 msgid "Confirm Usage Mail" msgstr "確認" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:77 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:86 #, fuzzy msgid "Email is sent successfully." msgstr "サイト情報が正常に保存されました。" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:78 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:87 msgid "Failed to send mail." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:79 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:88 msgid "action_doing" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:80 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:89 msgid "Secret URL Download" msgstr "シークレットURLダウンロード" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:81 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:90 msgid "SecretURL Enabled" msgstr "機能有効化" @@ -1311,7 +1411,6 @@ msgstr "座標" msgid "Search item setting" msgstr "検索項目設定" -#: weko_admin/template/weko_admin/admin/search_management_setting.html #: weko_admin/templates/weko_admin/admin/search_management_settings.html:279 msgid "Index Tree/Facet Display Setting" msgstr "インデックスツリー/ファセット表示設定" @@ -1322,6 +1421,7 @@ msgstr "インデックスツリー" #: weko_admin/templates/weko_admin/admin/search_management_settings.html:295 #: weko_admin/templates/weko_admin/admin/search_management_settings.html:336 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:356 #, fuzzy msgid "Display" msgstr "表示" @@ -1338,32 +1438,36 @@ msgstr "" msgid "Facet" msgstr "ファセット" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:356 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:349 +msgid "Community" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:376 msgid "Main Screen Initial Display Setting" msgstr "初期表示設定" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:366 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:386 msgid "Default Contents to Display" msgstr "初期表示画面設定" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:380 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:400 msgid "Default Index to Display" msgstr "初期表示インデックス表示方法" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:397 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:417 msgid "Initial Display Index" msgstr "初期表示インデックス" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:400 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:420 msgid "An index which is not open in public cannot be selected" msgstr "非公開インデックス以下は選択できません" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:484 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:504 msgid "Item Type List" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:524 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:531 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:544 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:551 msgid "Enter Input Value" msgstr "" @@ -1392,78 +1496,93 @@ msgid "Selected icon" msgstr "" #: weko_admin/templates/weko_admin/admin/site_info.html:45 -msgid "Select icon file" +#, fuzzy +msgid "Select File" msgstr "アイコンファイルの選択" #: weko_admin/templates/weko_admin/admin/site_info.html:46 +msgid "Select icon file" +msgstr "アイコンファイルの選択" + +#: weko_admin/templates/weko_admin/admin/site_info.html:47 +msgid "Selected file name" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/site_info.html:48 msgid "Add site name" msgstr "サイト名の追加" -#: weko_admin/templates/weko_admin/admin/site_info.html:47 +#: weko_admin/templates/weko_admin/admin/site_info.html:49 msgid "Site name is not set" msgstr "サイト名未設定" -#: weko_admin/templates/weko_admin/admin/site_info.html:50 +#: weko_admin/templates/weko_admin/admin/site_info.html:52 msgid "Must set at least 1 site name." msgstr "サイト名は少くとも1つ設定する必要があります。" -#: weko_admin/templates/weko_admin/admin/site_info.html:51 +#: weko_admin/templates/weko_admin/admin/site_info.html:53 msgid "Please input site information for empty field." msgstr "サイト情報を空のフィールドに入力してください。" -#: weko_admin/templates/weko_admin/admin/site_info.html:52 +#: weko_admin/templates/weko_admin/admin/site_info.html:54 msgid "The same language is set for many site names." msgstr "サイト名が重複しています" -#: weko_admin/templates/weko_admin/admin/site_info.html:53 +#: weko_admin/templates/weko_admin/admin/site_info.html:55 #, fuzzy msgid "Site name is required." msgstr "サイト名未設定" -#: weko_admin/templates/weko_admin/admin/site_info.html:54 +#: weko_admin/templates/weko_admin/admin/site_info.html:56 msgid "Error when get languages." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:55 +#: weko_admin/templates/weko_admin/admin/site_info.html:57 msgid "Error when get site infomation." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:56 +#: weko_admin/templates/weko_admin/admin/site_info.html:58 msgid "Language is deleted from Registered Language of system." msgstr "削除された言語は利用できません" -#: weko_admin/templates/weko_admin/admin/site_info.html:58 +#: weko_admin/templates/weko_admin/admin/site_info.html:60 msgid "Site info is saved successfully." msgstr "サイト情報が正常に保存されました。" -#: weko_admin/templates/weko_admin/admin/site_info.html:59 +#: weko_admin/templates/weko_admin/admin/site_info.html:61 msgid "Log in Instructions" msgstr "ログインの注意書き" -#: weko_admin/templates/weko_admin/admin/site_info.html:60 +#: weko_admin/templates/weko_admin/admin/site_info.html:62 msgid "Add Instructions" msgstr "注意書きの追加" -#: weko_admin/templates/weko_admin/admin/site_info.html:61 +#: weko_admin/templates/weko_admin/admin/site_info.html:63 msgid "The same language is set for many instructions." msgstr " 同一言語を複数の注意書きに設定されています。" -#: weko_admin/templates/weko_admin/admin/site_info.html:62 +#: weko_admin/templates/weko_admin/admin/site_info.html:64 msgid "Maximum length of instruction is 1000 characters. " msgstr "注意書きの字数上限は1000字です。" -#: weko_admin/template/weko_admin/admin/site_info.html:64 +#: weko_admin/templates/weko_admin/admin/site_info.html:66 msgid "Tracking ID" msgstr "トラッキングID" -#: weko_admin/template/weko_admin/admin/site_info.html:65 +#: weko_admin/templates/weko_admin/admin/site_info.html:67 msgid "AddThis ID" msgstr "AddThis ID" -#: weko_admin/template/weko_admin/admin/site_info.html:66 +#: weko_admin/templates/weko_admin/admin/site_info.html:68 msgid "OGP Image" msgstr "OGPイメージ" +#: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:37 +msgid "" +"Please classify a check into the organization send a use of site license " +"statistics email." +msgstr "" + #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:42 #, fuzzy msgid "Organization" @@ -1586,71 +1705,19 @@ msgstr "頻度" msgid "Life Time" msgstr "有効期間" -#: weko_admin/templates/weko_admin/settings/lifetime.html:42 -#: weko_admin/templates/weko_admin/settings/lifetime.html:49 +#: weko_admin/templates/weko_admin/settings/lifetime.html:43 +#: weko_admin/templates/weko_admin/settings/lifetime.html:50 msgid "Set lifetime for" msgstr "有効期間を設定" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html -msgid "Please select one aggregation mapping." -msgstr "集計マッピングを1つ選択してください。" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html -msgid "Already exists." -msgstr "既に存在しています。" - -#: weko_admin/templates/weko_admin/settings/lifetime.html:42 +#: weko_admin/templates/weko_admin/settings/lifetime.html:43 msgid "Current" msgstr "現行" -#: weko_admin/templates/weko_admin/settings/lifetime.html:58 +#: weko_admin/templates/weko_admin/settings/lifetime.html:59 msgid "Update" msgstr "更新" -#: weko_admin/admin/reindex_elasticsearch.html -msgid "reindex item_index" -msgstr "アイテムインデックスの再作成" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "reindex item" -msgstr "アイテムの再インデックス" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "execute" -msgstr "実行" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "Maintenance" -msgstr "メンテナンス" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "ElasticSearch Index" -msgstr "インデックス再作成" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "waiting..." -msgstr "実行可能" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "executing..." -msgstr "実行中..." - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "haserror" -msgstr "エラー発生中につき実行できません。" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "validationMsg1" -msgstr "実行モードが選ばれていません。" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "confirmMessage" -msgstr "本処理の実行にはかなりの時間がかかることが予想されます。インデックスの再作成処理を実行してよいですか?" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "completed" -msgstr "処理が完了しました。" - #~ msgid "Title_Asc" #~ msgstr "タイトル(昇順)" @@ -1780,3 +1847,6 @@ msgstr "処理が完了しました。" #~ msgid "Facet Search List" #~ msgstr "ファセット検索一覧" +#~ msgid "Day" +#~ msgstr "日" + diff --git a/modules/weko-admin/weko_admin/translations/messages.pot b/modules/weko-admin/weko_admin/translations/messages.pot index 1bbbdcf65b..d496f3c7af 100644 --- a/modules/weko-admin/weko_admin/translations/messages.pot +++ b/modules/weko-admin/weko_admin/translations/messages.pot @@ -1,623 +1,633 @@ # Translations template for weko-admin. -# Copyright (C) 2021 National Institute of Informatics +# Copyright (C) 2025 National Institute of Informatics # This file is distributed under the same license as the weko-admin project. -# FIRST AUTHOR , 2021. +# FIRST AUTHOR , 2025. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: weko-admin 0.1.0.dev20170000\n" "Report-Msgid-Bugs-To: wekosoftware@nii.ac.jp\n" -"POT-Creation-Date: 2021-07-29 21:12+0900\n" +"POT-Creation-Date: 2025-02-25 09:34+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.5.1\n" -#: weko_admin/admin.py:105 +#: tests/test_admin.py:1669 tests/test_admin.py:1675 tests/test_admin.py:1677 +#: tests/test_admin.py:1694 tests/test_admin.py:1702 tests/test_admin.py:1710 +#: tests/test_admin.py:1718 weko_admin/admin.py:134 +msgid "completed" +msgstr "" + +#: tests/test_admin.py:1727 weko_admin/admin.py:127 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:43 +msgid "executing..." +msgstr "" + +#: tests/test_admin.py:1737 weko_admin/admin.py:125 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:42 +msgid "haserror" +msgstr "" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:152 +msgid "15 mins" +msgstr "" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:153 +msgid "30 mins" +msgstr "" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:154 +msgid "45 mins" +msgstr "" + +#: tests/test_views.py:97 tests/test_views.py:110 tests/test_views.py:124 +#: weko_admin/views.py:155 +msgid "60 mins" +msgstr "" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:156 +msgid "180 mins" +msgstr "" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:157 +msgid "360 mins" +msgstr "" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:158 +msgid "720 mins" +msgstr "" + +#: tests/test_views.py:98 tests/test_views.py:111 tests/test_views.py:125 +#: weko_admin/views.py:159 +msgid "1440 mins" +msgstr "" + +#: weko_admin/admin.py:220 msgid "deny access" msgstr "" -#: weko_admin/admin.py:124 +#: weko_admin/admin.py:239 msgid "Successfully update color." msgstr "" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Monday" msgstr "" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Tuesday" msgstr "" -#: weko_admin/admin.py:310 +#: weko_admin/admin.py:438 msgid "Wednesday" msgstr "" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Thursday" msgstr "" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Friday" msgstr "" -#: weko_admin/admin.py:311 +#: weko_admin/admin.py:439 msgid "Saturday" msgstr "" -#: weko_admin/admin.py:312 +#: weko_admin/admin.py:440 msgid "Sunday" msgstr "" -#: weko_admin/admin.py:342 weko_admin/tasks.py:92 +#: weko_admin/admin.py:469 weko_admin/tasks.py:161 msgid " Log report." msgstr "" -#: weko_admin/admin.py:353 +#: weko_admin/admin.py:480 msgid "Successfully sent the reports to the recepients." msgstr "" -#: weko_admin/admin.py:363 +#: weko_admin/admin.py:490 msgid "Unexpected error occurred." msgstr "" -#: weko_admin/admin.py:397 +#: weko_admin/admin.py:521 msgid "Successfully Changed Schedule." msgstr "" -#: weko_admin/admin.py:399 +#: weko_admin/admin.py:523 msgid "Could Not Save Changes." msgstr "" -#: weko_admin/admin.py:464 weko_admin/admin.py:572 weko_admin/admin.py:806 +#: weko_admin/admin.py:578 weko_admin/admin.py:604 weko_admin/admin.py:705 +#: weko_admin/admin.py:957 msgid "Successfully Changed Settings." msgstr "" -#: weko_admin/admin.py:485 weko_admin/views.py:564 +#: weko_admin/admin.py:608 weko_admin/views.py:665 msgid "Could not save data." msgstr "" -#: weko_admin/admin.py:496 -msgid "Could not get restricted data: " +#: weko_admin/admin.py:619 +#, python-format +msgid "Could not get restricted data: %s" msgstr "" -#: weko_admin/admin.py:497 +#: weko_admin/admin.py:620 msgid "Could not get restricted data." msgstr "" -#: weko_admin/admin.py:576 weko_admin/admin.py:809 weko_admin/admin.py:812 +#: weko_admin/admin.py:709 weko_admin/admin.py:960 weko_admin/admin.py:963 msgid "Failurely Changed Settings." msgstr "" -#: weko_admin/admin.py:849 +#: weko_admin/admin.py:1000 msgid "Successfully Changed Settings" msgstr "" -#: weko_admin/admin.py:853 +#: weko_admin/admin.py:1004 msgid "Failed To Change Settings" msgstr "" -#: weko_admin/admin.py:913 +#: weko_admin/admin.py:1066 msgid "Prefix" msgstr "" -#: weko_admin/admin.py:919 +#: weko_admin/admin.py:1072 msgid "Suffix" msgstr "" -#: weko_admin/admin.py:921 +#: weko_admin/admin.py:1074 msgid "Enable/Disable" msgstr "" -#: weko_admin/admin.py:931 +#: weko_admin/admin.py:1084 msgid "Repository" msgstr "" -#: weko_admin/admin.py:931 +#: weko_admin/admin.py:1084 msgid "JaLC DOI" msgstr "" -#: weko_admin/admin.py:932 +#: weko_admin/admin.py:1085 msgid "JaLC CrossRef DOI" msgstr "" -#: weko_admin/admin.py:933 +#: weko_admin/admin.py:1086 msgid "JaLC DataCite DOI" msgstr "" -#: weko_admin/admin.py:934 +#: weko_admin/admin.py:1087 msgid "NDL JaLC DOI" msgstr "" -#: weko_admin/admin.py:935 +#: weko_admin/admin.py:1088 msgid "Semi-automatic Suffix" msgstr "" -#: weko_admin/admin.py:952 +#: weko_admin/admin.py:1105 msgid "Only allow half with 1-bytes character in input" msgstr "" -#: weko_admin/admin.py:1017 +#: weko_admin/admin.py:1170 msgid "Specified repository is already registered." msgstr "" -#: weko_admin/admin.py:1137 +#: weko_admin/admin.py:1298 msgid "ID" msgstr "" -#: weko_admin/admin.py:1138 +#: weko_admin/admin.py:1299 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:50 msgid "Item Name(EN)" msgstr "" -#: weko_admin/admin.py:1139 +#: weko_admin/admin.py:1300 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:51 msgid "Item Name(JP)" msgstr "" -#: weko_admin/admin.py:1140 +#: weko_admin/admin.py:1301 #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:52 msgid "Mapping" msgstr "" -#: weko_admin/admin.py:1141 -msgid "Active" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:59 -msgid "UiType" +#: weko_admin/admin.py:1302 +msgid "UI" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:60 -msgid "DisplayNumber" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:61 -msgid "OpenClose" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:62 -msgid "OpenClose Open" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:63 -msgid "OpenClose Close" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 -msgid "DisplayNumber Validation1" -msgstr "" - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:65 -msgid "DisplayNumber Validation2" +#: weko_admin/admin.py:1303 +msgid "Active" msgstr "" -#: weko_admin/admin.py:1210 weko_admin/admin.py:1237 weko_admin/admin.py:1246 -#: weko_admin/admin.py:1255 weko_admin/admin.py:1264 weko_admin/admin.py:1273 -#: weko_admin/admin.py:1282 weko_admin/admin.py:1291 weko_admin/admin.py:1309 -#: weko_admin/admin.py:1318 weko_admin/admin.py:1327 weko_admin/admin.py:1336 -#: weko_admin/admin.py:1344 weko_admin/admin.py:1352 +#: weko_admin/admin.py:1372 weko_admin/admin.py:1399 weko_admin/admin.py:1408 +#: weko_admin/admin.py:1417 weko_admin/admin.py:1426 weko_admin/admin.py:1435 +#: weko_admin/admin.py:1444 weko_admin/admin.py:1453 weko_admin/admin.py:1471 +#: weko_admin/admin.py:1480 weko_admin/admin.py:1489 weko_admin/admin.py:1498 +#: weko_admin/admin.py:1506 weko_admin/admin.py:1514 msgid "Setting" msgstr "" -#: weko_admin/admin.py:1211 +#: weko_admin/admin.py:1373 msgid "Style" msgstr "" -#: weko_admin/admin.py:1219 weko_admin/admin.py:1228 weko_admin/admin.py:1300 +#: weko_admin/admin.py:1381 weko_admin/admin.py:1390 weko_admin/admin.py:1462 msgid "Statistics" msgstr "" -#: weko_admin/admin.py:1220 +#: weko_admin/admin.py:1382 msgid "Report" msgstr "" -#: weko_admin/admin.py:1229 +#: weko_admin/admin.py:1391 msgid "Feedback Mail" msgstr "" -#: weko_admin/admin.py:1238 +#: weko_admin/admin.py:1400 msgid "Stats" msgstr "" -#: weko_admin/admin.py:1247 +#: weko_admin/admin.py:1409 #: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:45 msgid "Log Analysis" msgstr "" -#: weko_admin/admin.py:1256 -#: weko_admin/templates/weko_admin/admin/site_info.html:48 +#: weko_admin/admin.py:1418 +#: weko_admin/templates/weko_admin/admin/site_info.html:50 msgid "Language" msgstr "" -#: weko_admin/admin.py:1265 +#: weko_admin/admin.py:1427 msgid "WebAPI Account" msgstr "" -#: weko_admin/admin.py:1274 +#: weko_admin/admin.py:1436 msgid "Ranking" msgstr "" -#: weko_admin/admin.py:1283 +#: weko_admin/admin.py:1445 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:61 msgid "Search" msgstr "" -#: weko_admin/admin.py:1292 weko_admin/admin.py:1301 weko_admin/config.py:154 -#: weko_admin/config.py:159 +#: weko_admin/admin.py:1454 weko_admin/admin.py:1463 weko_admin/config.py:168 +#: weko_admin/config.py:173 msgid "Site License" msgstr "" -#: weko_admin/admin.py:1310 +#: weko_admin/admin.py:1472 msgid "File Preview" msgstr "" -#: weko_admin/admin.py:1319 +#: weko_admin/admin.py:1481 msgid "Item Export" msgstr "" -#: weko_admin/admin.py:1328 +#: weko_admin/admin.py:1490 msgid "Site Info" msgstr "" -#: weko_admin/admin.py:1337 +#: weko_admin/admin.py:1499 msgid "Restricted Access" msgstr "" -#: weko_admin/admin.py:1345 +#: weko_admin/admin.py:1507 msgid "Identifier" msgstr "" -#: weko_admin/admin.py:1353 +#: weko_admin/admin.py:1515 msgid "Faceted Search" msgstr "" -#: weko_admin/api.py:82 +#: weko_admin/admin.py:1522 +msgid "Maintenance" +msgstr "" + +#: weko_admin/admin.py:1523 +msgid "ElasticSearch Index" +msgstr "" + +#: weko_admin/api.py:127 msgid "statistics report" msgstr "" -#: weko_admin/config.py:129 +#: weko_admin/config.py:143 msgid "No. Of File Downloads" msgstr "" -#: weko_admin/config.py:130 +#: weko_admin/config.py:144 msgid "No. Of File Previews" msgstr "" -#: weko_admin/config.py:131 +#: weko_admin/config.py:145 msgid "No. Of Paid File Downloads" msgstr "" -#: weko_admin/config.py:132 +#: weko_admin/config.py:146 msgid "No. Of Paid File Previews" msgstr "" -#: weko_admin/config.py:133 +#: weko_admin/config.py:147 msgid "Detail Views Per Index" msgstr "" -#: weko_admin/config.py:134 +#: weko_admin/config.py:148 msgid "Detail Views Count" msgstr "" -#: weko_admin/config.py:135 +#: weko_admin/config.py:149 msgid "Usage Count By User" msgstr "" -#: weko_admin/config.py:136 +#: weko_admin/config.py:150 msgid "Search Keyword Ranking" msgstr "" -#: weko_admin/config.py:137 +#: weko_admin/config.py:151 msgid "Number Of Access By Host" msgstr "" -#: weko_admin/config.py:138 +#: weko_admin/config.py:152 msgid "User Affiliation Information" msgstr "" -#: weko_admin/config.py:139 +#: weko_admin/config.py:153 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:21 msgid "Access Count By Site License" msgstr "" -#: weko_admin/config.py:144 +#: weko_admin/config.py:158 msgid "Open-Access No. Of File Downloads" msgstr "" -#: weko_admin/config.py:145 +#: weko_admin/config.py:159 msgid "Open-Access No. Of File Previews" msgstr "" -#: weko_admin/config.py:146 +#: weko_admin/config.py:160 msgid "Access Number Breakdown By Site License" msgstr "" -#: weko_admin/config.py:152 weko_admin/config.py:157 +#: weko_admin/config.py:166 weko_admin/config.py:171 msgid "File Name" msgstr "" -#: weko_admin/config.py:152 weko_admin/config.py:157 weko_admin/config.py:163 +#: weko_admin/config.py:166 weko_admin/config.py:171 weko_admin/config.py:177 msgid "Registered Index Name" msgstr "" -#: weko_admin/config.py:153 +#: weko_admin/config.py:167 msgid "No. Of Times Downloaded" msgstr "" -#: weko_admin/config.py:153 weko_admin/config.py:158 +#: weko_admin/config.py:167 weko_admin/config.py:172 msgid "Non-Logged In User" msgstr "" -#: weko_admin/config.py:154 weko_admin/config.py:159 +#: weko_admin/config.py:168 weko_admin/config.py:173 msgid "Logged In User" msgstr "" -#: weko_admin/config.py:154 weko_admin/config.py:159 +#: weko_admin/config.py:168 weko_admin/config.py:173 msgid "Admin" msgstr "" -#: weko_admin/config.py:155 weko_admin/config.py:160 +#: weko_admin/config.py:169 weko_admin/config.py:174 msgid "Registrar" msgstr "" -#: weko_admin/config.py:158 +#: weko_admin/config.py:172 msgid "No. Of Times Viewed" msgstr "" -#: weko_admin/config.py:161 +#: weko_admin/config.py:175 msgid "Index" msgstr "" -#: weko_admin/config.py:161 +#: weko_admin/config.py:175 msgid "No. Of Views" msgstr "" -#: weko_admin/config.py:163 +#: weko_admin/config.py:177 msgid "Title" msgstr "" -#: weko_admin/config.py:163 +#: weko_admin/config.py:177 msgid "View Count" msgstr "" -#: weko_admin/config.py:164 +#: weko_admin/config.py:178 msgid "Non-logged-in User" msgstr "" -#: weko_admin/config.py:165 +#: weko_admin/config.py:179 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:49 msgid "Mail address" msgstr "" -#: weko_admin/config.py:166 +#: weko_admin/config.py:180 msgid "Username" msgstr "" -#: weko_admin/config.py:167 +#: weko_admin/config.py:181 msgid "File download count" msgstr "" -#: weko_admin/config.py:168 +#: weko_admin/config.py:182 msgid "File playing count" msgstr "" -#: weko_admin/config.py:169 +#: weko_admin/config.py:183 #: weko_admin/templates/weko_admin/admin/report.html:116 msgid "Search Keyword" msgstr "" -#: weko_admin/config.py:169 weko_admin/config.py:174 +#: weko_admin/config.py:183 weko_admin/config.py:188 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:25 msgid "Number Of Searches" msgstr "" -#: weko_admin/config.py:170 +#: weko_admin/config.py:184 msgid "Host" msgstr "" -#: weko_admin/config.py:170 +#: weko_admin/config.py:184 msgid "IP Address" msgstr "" -#: weko_admin/config.py:171 weko_admin/config.py:173 +#: weko_admin/config.py:185 weko_admin/config.py:187 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:24 msgid "WEKO Top Page Access Count" msgstr "" -#: weko_admin/config.py:172 +#: weko_admin/config.py:186 msgid "Role" msgstr "" -#: weko_admin/config.py:172 +#: weko_admin/config.py:186 msgid "Number Of Users" msgstr "" -#: weko_admin/config.py:174 +#: weko_admin/config.py:188 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:26 msgid "Number Of Views" msgstr "" -#: weko_admin/config.py:175 +#: weko_admin/config.py:189 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:27 msgid "Number Of File download" msgstr "" -#: weko_admin/config.py:176 +#: weko_admin/config.py:190 #: weko_admin/templates/weko_admin/email_templates/site_license_report.html:28 msgid "Number Of File Regeneration" msgstr "" -#: weko_admin/config.py:181 +#: weko_admin/config.py:195 msgid "FileDownload_" msgstr "" -#: weko_admin/config.py:182 +#: weko_admin/config.py:196 msgid "FilePreview_" msgstr "" -#: weko_admin/config.py:183 +#: weko_admin/config.py:197 msgid "PayFileDownload_" msgstr "" -#: weko_admin/config.py:184 +#: weko_admin/config.py:198 msgid "PayFilePreview_" msgstr "" -#: weko_admin/config.py:185 +#: weko_admin/config.py:199 msgid "IndexAccess_" msgstr "" -#: weko_admin/config.py:186 +#: weko_admin/config.py:200 msgid "DetailView_" msgstr "" -#: weko_admin/config.py:187 +#: weko_admin/config.py:201 msgid "FileUsingPerUser_" msgstr "" -#: weko_admin/config.py:188 +#: weko_admin/config.py:202 msgid "SearchCount_" msgstr "" -#: weko_admin/config.py:189 +#: weko_admin/config.py:203 msgid "UserAffiliate_" msgstr "" -#: weko_admin/config.py:190 +#: weko_admin/config.py:204 msgid "SiteAccess_" msgstr "" -#: weko_admin/config.py:191 +#: weko_admin/config.py:205 msgid "TopPageAccess_" msgstr "" -#: weko_admin/ext.py:74 +#: weko_admin/ext.py:75 msgid "A translation string" msgstr "" -#: weko_admin/utils.py:235 +#: weko_admin/utils.py:234 msgid "Input type is invalid. Please check again." msgstr "" -#: weko_admin/utils.py:238 weko_admin/views.py:284 weko_admin/views.py:289 +#: weko_admin/utils.py:237 weko_admin/views.py:326 weko_admin/views.py:331 msgid "Account information is invalid. Please check again." msgstr "" -#: weko_admin/utils.py:331 +#: weko_admin/utils.py:328 msgid "Registered Users" msgstr "" -#: weko_admin/utils.py:373 +#: weko_admin/utils.py:372 msgid "Aggregation Month" msgstr "" -#: weko_admin/utils.py:388 +#: weko_admin/utils.py:389 msgid "Total Detail Views" msgstr "" -#: weko_admin/utils.py:396 +#: weko_admin/utils.py:398 msgid "Site license member" msgstr "" -#: weko_admin/utils.py:400 +#: weko_admin/utils.py:402 msgid "Other than site license" msgstr "" -#: weko_admin/utils.py:411 +#: weko_admin/utils.py:413 msgid "Institution Name" msgstr "" -#: weko_admin/utils.py:1198 +#: weko_admin/utils.py:1219 msgid "Cannot update Feedback email settings." msgstr "" -#: weko_admin/utils.py:1225 +#: weko_admin/utils.py:1246 msgid "Author is duplicated." msgstr "" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:48 -#: weko_admin/utils.py:1232 +#: weko_admin/utils.py:1253 msgid "Duplicate Email Addresses." msgstr "" -#: weko_admin/views.py:102 +#: weko_admin/views.py:116 #, python-format msgid "%(icon)s Session" msgstr "" -#: weko_admin/views.py:108 +#: weko_admin/views.py:122 msgid "Session" msgstr "" -#: weko_admin/views.py:132 +#: weko_admin/views.py:147 msgid "Session lifetime was updated." msgstr "" -#: weko_admin/views.py:137 -msgid "15 mins" -msgstr "" - -#: weko_admin/views.py:138 -msgid "30 mins" -msgstr "" - -#: weko_admin/views.py:139 -msgid "45 mins" -msgstr "" - -#: weko_admin/views.py:140 -msgid "60 mins" -msgstr "" - -#: weko_admin/views.py:141 -msgid "180 mins" -msgstr "" - -#: weko_admin/views.py:142 -msgid "360 mins" -msgstr "" - -#: weko_admin/views.py:143 -msgid "720 mins" -msgstr "" - -#: weko_admin/views.py:144 -msgid "1440 mins" -msgstr "" - -#: weko_admin/views.py:277 +#: weko_admin/views.py:319 msgid "Header Error" msgstr "" -#: weko_admin/views.py:559 +#: weko_admin/views.py:660 msgid "Restricted Access was successfully updated." msgstr "" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:54 -#: weko_admin/views.py:618 weko_admin/views.py:651 +#: weko_admin/views.py:725 weko_admin/views.py:760 msgid "Success" msgstr "" -#: weko_admin/views.py:627 +#: weko_admin/views.py:734 msgid "Failed to update due to server error." msgstr "" -#: weko_admin/views.py:632 +#: weko_admin/views.py:739 msgid "Failed to create due to server error." msgstr "" -#: weko_admin/views.py:635 +#: weko_admin/views.py:742 msgid "" "The item name/mapping is already exists. Please input other faceted " "item/mapping." msgstr "" -#: weko_admin/views.py:658 weko_admin/views.py:661 +#: weko_admin/views.py:767 weko_admin/views.py:770 msgid "Failed to delete due to server error." msgstr "" @@ -625,21 +635,21 @@ msgstr "" msgid "Welcome to" msgstr "" -#: weko_admin/templates/weko_admin/admin/block_style.html:87 -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:67 +#: weko_admin/templates/weko_admin/admin/block_style.html:89 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:74 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:44 #: weko_admin/templates/weko_admin/admin/file_preview_settings.html:62 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:94 #: weko_admin/templates/weko_admin/admin/lang_settings.html:86 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:169 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:191 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:145 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:170 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:192 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:147 #: weko_admin/templates/weko_admin/admin/report.html:224 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:52 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:422 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:459 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:562 -#: weko_admin/templates/weko_admin/admin/site_info.html:49 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:60 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:442 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:479 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:582 +#: weko_admin/templates/weko_admin/admin/site_info.html:51 #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:174 #: weko_admin/templates/weko_admin/admin/site_license_settings.html:204 #: weko_admin/templates/weko_admin/admin/stats_settings.html:71 @@ -648,11 +658,11 @@ msgstr "" msgid "Save" msgstr "" -#: weko_admin/templates/weko_admin/admin/block_style.html:91 +#: weko_admin/templates/weko_admin/admin/block_style.html:93 msgid "Color Setting" msgstr "" -#: weko_admin/templates/weko_admin/admin/block_style.html:97 +#: weko_admin/templates/weko_admin/admin/block_style.html:99 msgid "Background1" msgstr "" @@ -681,58 +691,95 @@ msgid "_Display" msgstr "" #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:59 -msgid "_Hide" +msgid "UiType" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:60 +msgid "DisplayNumber" msgstr "" #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:61 -msgid "List" +msgid "OpenClose" msgstr "" #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:62 -msgid "Create" +msgid "OpenClose Open" msgstr "" #: weko_admin/templates/weko_admin/admin/facet_search_setting.html:63 +msgid "OpenClose Close" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 +msgid "DisplayNumber Validation1" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:65 +msgid "DisplayNumber Validation2" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:66 +msgid "_Hide" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:68 +msgid "List" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:69 +msgid "Create" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:70 #: weko_admin/templates/weko_admin/admin/search_management_settings.html:230 msgid "Edit" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:64 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:71 #: weko_admin/templates/weko_admin/partials/email_schedule.html:23 msgid "Details" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:66 -#: weko_admin/templates/weko_admin/settings/lifetime.html:55 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:73 +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:48 +#: weko_admin/templates/weko_admin/settings/lifetime.html:56 msgid "Cancel" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:68 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:75 #: weko_admin/templates/weko_admin/admin/feedback_mail.html:45 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:149 -#: weko_admin/templates/weko_admin/admin/site_info.html:57 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:151 +#: weko_admin/templates/weko_admin/admin/site_info.html:59 #: weko_admin/templates/weko_admin/admin/site_license_settings.html:70 msgid "Delete" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:69 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:64 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:541 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:76 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:73 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:561 msgid "Add" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:70 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:78 msgid "Please input all required item." msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:77 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:183 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:79 +msgid "Please select one aggregation mapping." +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:80 +msgid "Already exists." +msgstr "" + +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:87 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:184 #: weko_admin/templates/weko_admin/admin/report.html:247 #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:197 msgid "Confirmation" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:78 +#: weko_admin/templates/weko_admin/admin/facet_search_setting.html:88 msgid "Are you sure you want to delete it?" msgstr "" @@ -803,9 +850,9 @@ msgid "Resend" msgstr "" #: weko_admin/templates/weko_admin/admin/feedback_mail.html:59 -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:195 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:196 #: weko_admin/templates/weko_admin/admin/report.html:273 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:75 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:84 msgid "Close" msgstr "" @@ -843,7 +890,7 @@ msgstr "" #: weko_admin/templates/weko_admin/admin/item_export_settings.html:59 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:78 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:47 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:48 #: weko_admin/templates/weko_admin/admin/stats_settings.html:59 #: weko_admin/templates/weko_admin/partials/email_schedule.html:75 msgid "On" @@ -851,7 +898,7 @@ msgstr "" #: weko_admin/templates/weko_admin/admin/item_export_settings.html:65 #: weko_admin/templates/weko_admin/admin/item_export_settings.html:84 -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:52 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:53 #: weko_admin/templates/weko_admin/admin/stats_settings.html:64 #: weko_admin/templates/weko_admin/partials/email_schedule.html:80 msgid "Off" @@ -869,70 +916,102 @@ msgstr "" msgid "Registered language" msgstr "" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:56 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:57 msgid "Addresses to Filter" msgstr "" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:64 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:65 msgid "Enter the IP Addresses to Filter" msgstr "" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:128 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:129 msgid "Shared Crawler Lists" msgstr "" -#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:186 +#: weko_admin/templates/weko_admin/admin/log_analysis_settings.html:187 msgid "Are you sure you want to block the given addresses?" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:41 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:42 msgid "Show/Hide Ranking" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:58 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:59 msgid "Period To Judge As New Item" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:64 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:65 msgid "Day (Range : 1~30)" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:68 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:69 msgid "Statistical Period" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:73 -msgid "Day" +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:74 +msgid "Day (Range : 1~3650)" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:77 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:78 msgid "Display Rank" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:85 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:83 +msgid "(Range : 1~100)" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:87 msgid "Rankings" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:91 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:93 msgid "Most Viewed Items" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:102 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:104 msgid "Most Downloaded Items" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:113 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:115 msgid "User Who Created The Most Items" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:124 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:126 msgid "Most Searched Keywords" msgstr "" -#: weko_admin/templates/weko_admin/admin/ranking_settings.html:135 +#: weko_admin/templates/weko_admin/admin/ranking_settings.html:137 msgid "New Items" msgstr "" +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:38 +msgid "reindex item_index" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:39 +msgid "reindex item" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:40 +msgid "execute" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:41 +msgid "waiting..." +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:47 +msgid "Execute" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:49 +msgid "validationMsg1" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/reindex_elasticsearch.html:50 +msgid "confirmMessage" +msgstr "" + #: weko_admin/templates/weko_admin/admin/report.html:54 msgid "Number of items registered" msgstr "" @@ -1052,7 +1131,7 @@ msgid "Are you sure you want to save changes?" msgstr "" #: weko_admin/templates/weko_admin/admin/report.html:255 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:76 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:85 msgid "Send Mail" msgstr "" @@ -1072,95 +1151,113 @@ msgstr "" msgid "Expiration Date" msgstr "" +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:47 +msgid "Max Download Limit" +msgstr "" + #: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:48 +msgid "Max Expiration Date" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:49 +msgid "Expiration Date Initial Value" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:50 +msgid "Download Limit Initial Value" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:52 msgid "Content File Download" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:51 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:59 msgid "Unlimited" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:53 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:61 msgid "Usage Report Workflow Access" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:55 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:57 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:63 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:65 #, python-format -msgid "Must set a positive integer for %(name)s." +msgid "Must set a positive integer and less than %(name1)s for %(name2)s." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:58 -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:59 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:66 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:67 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:68 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:69 #, python-format msgid "Please set %(name)s." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:61 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:444 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:451 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:70 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:464 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:471 msgid "English" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:62 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:445 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:452 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:71 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:465 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:472 msgid "Japanese" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:63 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:72 msgid "Terms and Conditions" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:65 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:74 msgid "Please input the Terms and Conditions in English." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:68 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:77 msgid "Activity" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:69 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:78 msgid "Item" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:70 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:79 msgid "WorkFlow" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:71 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:80 msgid "Status" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:72 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:81 msgid "User" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:73 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:82 msgid "Usage Report Reminder Email" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:74 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:83 msgid "Confirm Usage Mail" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:77 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:86 msgid "Email is sent successfully." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:78 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:87 msgid "Failed to send mail." msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:79 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:88 msgid "action_doing" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:80 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:89 msgid "Secret URL Download" msgstr "" -#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:81 +#: weko_admin/templates/weko_admin/admin/restricted_access_settings.html:90 msgid "SecretURL Enabled" msgstr "" @@ -1293,6 +1390,7 @@ msgstr "" #: weko_admin/templates/weko_admin/admin/search_management_settings.html:295 #: weko_admin/templates/weko_admin/admin/search_management_settings.html:336 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:356 msgid "Display" msgstr "" @@ -1308,32 +1406,36 @@ msgstr "" msgid "Facet" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:356 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:349 +msgid "Community" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:376 msgid "Main Screen Initial Display Setting" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:366 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:386 msgid "Default Contents to Display" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:380 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:400 msgid "Default Index to Display" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:397 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:417 msgid "Initial Display Index" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:400 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:420 msgid "An index which is not open in public cannot be selected" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:484 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:504 msgid "Item Type List" msgstr "" -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:524 -#: weko_admin/templates/weko_admin/admin/search_management_settings.html:531 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:544 +#: weko_admin/templates/weko_admin/admin/search_management_settings.html:551 msgid "Enter Input Value" msgstr "" @@ -1362,79 +1464,89 @@ msgid "Selected icon" msgstr "" #: weko_admin/templates/weko_admin/admin/site_info.html:45 -msgid "Select icon file" +msgid "Select File" msgstr "" #: weko_admin/templates/weko_admin/admin/site_info.html:46 -msgid "Add site name" +msgid "Select icon file" msgstr "" #: weko_admin/templates/weko_admin/admin/site_info.html:47 +msgid "Selected file name" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/site_info.html:48 +msgid "Add site name" +msgstr "" + +#: weko_admin/templates/weko_admin/admin/site_info.html:49 msgid "Site name is not set" msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:50 +#: weko_admin/templates/weko_admin/admin/site_info.html:52 msgid "Must set at least 1 site name." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:51 +#: weko_admin/templates/weko_admin/admin/site_info.html:53 msgid "Please input site information for empty field." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:52 +#: weko_admin/templates/weko_admin/admin/site_info.html:54 msgid "The same language is set for many site names." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:53 +#: weko_admin/templates/weko_admin/admin/site_info.html:55 msgid "Site name is required." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:54 +#: weko_admin/templates/weko_admin/admin/site_info.html:56 msgid "Error when get languages." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:55 +#: weko_admin/templates/weko_admin/admin/site_info.html:57 msgid "Error when get site infomation." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:56 +#: weko_admin/templates/weko_admin/admin/site_info.html:58 msgid "Language is deleted from Registered Language of system." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:58 +#: weko_admin/templates/weko_admin/admin/site_info.html:60 msgid "Site info is saved successfully." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:59 +#: weko_admin/templates/weko_admin/admin/site_info.html:61 msgid "Log in Instructions" msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:60 +#: weko_admin/templates/weko_admin/admin/site_info.html:62 msgid "Add Instructions" msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:61 +#: weko_admin/templates/weko_admin/admin/site_info.html:63 msgid "The same language is set for many instructions." msgstr "" -#: weko_admin/templates/weko_admin/admin/site_info.html:62 +#: weko_admin/templates/weko_admin/admin/site_info.html:64 msgid "Maximum length of instruction is 1000 characters. " msgstr "" -#: weko_admin/template/weko_admin/admin/site_info.html:64 +#: weko_admin/templates/weko_admin/admin/site_info.html:66 msgid "Tracking ID" msgstr "" -#: weko_admin/template/weko_admin/admin/site_info.html:65 +#: weko_admin/templates/weko_admin/admin/site_info.html:67 msgid "AddThis ID" msgstr "" -#: weko_admin/template/weko_admin/admin/site_info.html:66 +#: weko_admin/templates/weko_admin/admin/site_info.html:68 msgid "OGP Image" msgstr "" -#: weko_admin/template/weko_admin/admin/search_management_setting.html -msgid "Index Tree/Facet Display Setting" +#: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:37 +msgid "" +"Please classify a check into the organization send a use of site license " +"statistics email." msgstr "" #: weko_admin/templates/weko_admin/admin/site_license_send_mail_settings.html:42 @@ -1555,71 +1667,16 @@ msgstr "" msgid "Life Time" msgstr "" -#: weko_admin/templates/weko_admin/settings/lifetime.html:42 -#: weko_admin/templates/weko_admin/settings/lifetime.html:49 +#: weko_admin/templates/weko_admin/settings/lifetime.html:43 +#: weko_admin/templates/weko_admin/settings/lifetime.html:50 msgid "Set lifetime for" msgstr "" -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html -msgid "Please select one aggregation mapping." -msgstr "Please select one aggregation mapping." - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html -msgid "Already exists." -msgstr "Already exists." - -#: weko_admin/templates/weko_admin/admin/facet_search_setting.html: -msgid "Failed to create due to server error." -msgstr "Failed to create due to server error." - -#: weko_admin/templates/weko_admin/settings/lifetime.html:42 +#: weko_admin/templates/weko_admin/settings/lifetime.html:43 msgid "Current" msgstr "" -#: weko_admin/templates/weko_admin/settings/lifetime.html:58 +#: weko_admin/templates/weko_admin/settings/lifetime.html:59 msgid "Update" msgstr "" -#: weko_admin/admin/reindex_elasticsearch.html -msgid "reindex item_index" -msgstr "アイテムインデックスの再作成" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "reindex item" -msgstr "アイテムの再インデックス" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "execute" -msgstr "実行" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "Maintenance" -msgstr "メンテナンス" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "ElasticSearch Index" -msgstr "インデックス再作成" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "waiting..." -msgstr "実行可能" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "executing..." -msgstr "実行中..." - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "haserror" -msgstr "エラー発生中につき実行できません" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "validationMsg1" -msgstr "実行モードが選ばれていません。" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "confirmMessage" -msgstr "本処理の実行にはかなりの時間がかかることが予想されます。インデックスの再作成処理を実行してよいですか?" - -#: weko_admin/admin/reindex_elasticsearch.html -msgid "completed" -msgstr "処理が完了しました。" \ No newline at end of file diff --git a/modules/weko-records-ui/tests/conftest.py b/modules/weko-records-ui/tests/conftest.py index d90d5fd8f1..08dbbf7041 100644 --- a/modules/weko-records-ui/tests/conftest.py +++ b/modules/weko-records-ui/tests/conftest.py @@ -25,11 +25,12 @@ from os.path import dirname, exists, join import mimetypes import os +import re import shutil import tempfile import time import uuid -from datetime import datetime +from datetime import datetime, timezone from collections import OrderedDict from unittest.mock import patch from datetime import timedelta @@ -108,7 +109,8 @@ from weko_records.models import ItemType, ItemTypeMapping, ItemTypeName, SiteLicenseInfo, FeedbackMailList,SiteLicenseIpAddress from weko_records.utils import get_options_and_order_list from weko_records_ui.config import WEKO_ADMIN_PDFCOVERPAGE_TEMPLATE,RECORDS_UI_ENDPOINTS,WEKO_RECORDS_UI_SECRET_KEY,WEKO_RECORDS_UI_ONETIME_DOWNLOAD_PATTERN -from weko_records_ui.models import PDFCoverPageSettings,FileOnetimeDownload, FilePermission #RocrateMapping +from weko_records_ui.models import FileSecretDownload, PDFCoverPageSettings,FileOnetimeDownload, FilePermission #RocrateMapping +from weko_records_ui.utils import create_download_url from weko_schema_ui.config import ( WEKO_SCHEMA_DDI_SCHEMA_NAME, WEKO_SCHEMA_JPCOAR_V1_SCHEMA_NAME, @@ -4330,15 +4332,16 @@ def site_license_ipaddr(app, db,site_license_info): return record1 @pytest.fixture() -def db_fileonetimedownload(app, db): - record = FileOnetimeDownload( +def db_fileonetimedownload(app, users): + record = FileOnetimeDownload.create( + approver_id=1, + record_id='1', file_name="helloworld.pdf", + expiration_date=datetime.now(timezone.utc) + timedelta(days=30), + download_limit=10, user_mail="wekosoftware@nii.ac.jp", - record_id='1', - download_count=10, - expiration_date=0) - with db.session.begin_nested(): - db.session.add(record) + is_guest=False, + extra_info={}) return record @@ -4483,3 +4486,59 @@ def db_rocrate_mapping(db): with db.session.begin_nested(): db.session.add(rocrate_mapping) db.session.commit() + + +@pytest.fixture +def secret_url(users, params=None): + """Fixture that creates secret URL object and provides the token of it. + + Args: + params (dict, optional): Custom parameters for the secret object. + """ + params = params or {} + ex_date = datetime.now(timezone.utc) + timedelta(days=30) + secret_obj = FileSecretDownload.create( + creator_id =params.get('creator_id' , 1 ), + record_id =params.get('record_id' , '1' ), + file_name =params.get('file_name' , 'test.txt'), + label_name =params.get('label_name' , 'test_url'), + expiration_date =params.get('expiration_date', ex_date ), + download_limit =params.get('download_limit' , 10 ) + ) + secret_url = create_download_url(secret_obj) + match = re.search(r'[?&]token=([^&]+)', secret_url) + secret_token = match.group(1) + return { + 'secret_obj' : secret_obj, + 'secret_token': secret_token, + 'secret_url' : secret_url + } + + +@pytest.fixture +def onetime_url(users, params=None): + """Fixture that creates onetime URL object and provides the token of it. + + Args: + params (dict, optional): Custom parameters for the onetime object. + """ + params = params or {} + ex_date = datetime.now(timezone.utc) + timedelta(days=30) + onetime_obj = FileOnetimeDownload.create( + approver_id = params.get('approver_id' , 1 ), + record_id = params.get('record_id' , '1' ), + file_name = params.get('file_name' , 'test.txt' ), + expiration_date = params.get('expiration_date', ex_date ), + download_limit = params.get('download_limit' , 10 ), + user_mail = params.get('user_mail' , 'test@example.org'), + is_guest = params.get('is_guest' , False ), + extra_info = params.get('extra_info' , {'activity_id': 1}) + ) + onetime_url = create_download_url(onetime_obj) + match = re.search(r'[?&]token=([^&]+)', onetime_url) + onetime_token = match.group(1) + return { + 'onetime_obj' : onetime_obj, + 'onetime_token': onetime_token, + 'onetime_url' : onetime_url + } diff --git a/modules/weko-records-ui/tests/test_fd.py b/modules/weko-records-ui/tests/test_fd.py index ba45e32d74..f8cdc99b18 100644 --- a/modules/weko-records-ui/tests/test_fd.py +++ b/modules/weko-records-ui/tests/test_fd.py @@ -5,7 +5,7 @@ from requests import Response from weko_deposit.api import WekoFileObject #from weko_records_ui.errors import AvailableFilesNotFoundRESTError -from weko_records_ui.fd import _is_terms_of_use_only, file_download_secret, prepare_response,file_download_onetime,_download_file,add_signals_info,weko_view_method,file_ui,file_preview_ui,file_download_ui # ,file_list_ui +from weko_records_ui.fd import _is_terms_of_use_only, error_response, file_download_secret, prepare_response,file_download_onetime,_download_file,add_signals_info,weko_view_method,file_ui,file_preview_ui,file_download_ui # ,file_list_ui from weko_records_ui.config import WEKO_RECORDS_UI_DETAIL_TEMPLATE from unittest.mock import MagicMock from invenio_theme.config import THEME_ERROR_TEMPLATE @@ -19,9 +19,10 @@ from invenio_accounts.testutils import login_user_via_session from mock import patch from invenio_records_files.utils import record_file_factory +from weko_schema_ui.models import PublishStatus from werkzeug.exceptions import NotFound ,Forbidden -from weko_records_ui.models import FileSecretDownload +from weko_records_ui.models import AccessStatus, FileSecretDownload from sqlalchemy.exc import SQLAlchemyError # def weko_view_method(pid, record, template=None, **kwargs): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test_weko_view_method -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @@ -260,46 +261,84 @@ def all_func(): add_signals_info(record,obj) -# def file_download_onetime(pid, record, _record_file_factory=None, **kwargs): +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test_error_response -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.fd.render_template') +def test_error_response(mock_render): + mock_render.return_value = 'Error Test' + error_template = 'weko_theme/error.html' + render, status_code = error_response('Error Test', 500) + assert render == 'Error Test' + assert status_code == 500 + mock_render.assert_called_once_with(error_template, error='Error Test') + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test_file_download_onetime -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_file_download_onetime(app, records, itemtypes, users, db_fileonetimedownload): - indexer, results = records - recid = results[0]["recid"] - record = results[0]["record"] - app.config["THEME_ERROR_TEMPLATE"]=THEME_ERROR_TEMPLATE - with app.test_request_context('?token=MSB1c2VyQGV4YW1wbGUub3JnIDIwMjItMDktMjcgNDBDRkNGODFGM0FFRUI0Ng=='): - with patch("flask_login.utils._get_user", return_value=users[1]["obj"]): - with patch("flask.templating._render", return_value=""): - with patch("weko_records_ui.fd.get_onetime_download", return_value=db_fileonetimedownload): - with patch("weko_records_ui.fd.parse_one_time_download_token", return_value=(True, [1])): - assert file_download_onetime(recid,record,record_file_factory)=="" - - with patch("weko_records_ui.fd.parse_one_time_download_token", return_value=(False, ("","","",""))): - - with patch("weko_records_ui.fd.validate_onetime_download_token", return_value=(False, [1])): - assert file_download_onetime(recid,record,record_file_factory)=="" - - _rv = (True, "") - with patch("weko_records_ui.fd.validate_onetime_download_token", return_value=_rv): - assert file_download_onetime(recid,record,record_file_factory)=="" - - with patch("weko_records_ui.fd.record_file_factory", return_value=False): - assert file_download_onetime(recid,record,None)=="" - - file_object = MagicMock() - file_object.obj = {"foo" : "hoge"} - file_object.get = lambda x : 'open_restricted' - with patch("weko_records_ui.fd.record_file_factory", return_value=file_object): - with patch('weko_records_ui.fd.check_and_send_usage_report',return_value ="error"): - assert file_download_onetime(recid,record,None)=="" - with patch('weko_records_ui.fd.check_and_send_usage_report',side_effect = BaseException ): - assert file_download_onetime(recid,record,None)=="" - with patch('weko_records_ui.fd.check_and_send_usage_report',side_effect =SQLAlchemyError): - assert file_download_onetime(recid,record,None)=="" - with patch('weko_records_ui.fd.check_and_send_usage_report',return_value =""): - with patch('weko_records_ui.fd.update_onetime_download',return_value =True): - with patch('weko_records_ui.fd._download_file',return_value ="downloaded"): - assert file_download_onetime(recid,record,None)=="downloaded" +@patch('weko_records_ui.fd.request.args.get') +@patch('weko_records_ui.fd.validate_url_download') +@patch('weko_records_ui.fd.error_response') +@patch('weko_records_ui.fd.check_and_send_usage_report') +@patch('weko_records_ui.fd.save_download_log') +@patch('weko_records_ui.fd._download_file') +def test_file_download_onetime(dl_file, save_log, chk_and_send, err_res, + val_url, token, onetime_url): + # Setup arguments of sut + pid = None + record = {} + filename = 'test.txt' + _record_file_factory = MagicMock() + _record_file_factory.return_value = file_obj = MagicMock() + file_obj.obj = {'filename': filename} + file_obj.get.return_value = 'open_restricted' + + # Setup default return values of mock objects + token.return_value = onetime_url['onetime_token'] + val_url.return_value = (True, '') + chk_and_send.return_value = None + dl_file.return_value = 'SUCCESS' + err_res.return_value = 'ERROR' + + # Happy path + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'SUCCESS' + save_log.assert_called_once_with( + record, filename, token.return_value, is_secret_url=False) + + # Invalid token + with patch('weko_records_ui.fd.validate_url_download', + return_value=(False, 'Invalid token')): + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'ERROR' + + # File object is not found + with patch('weko_records_ui.fd.record_file_factory', + return_value=None): + assert file_download_onetime( + pid, record, filename) == 'ERROR' + + # check_and_send_usage_report() returns an error + with patch('weko_records_ui.fd.check_and_send_usage_report', + return_value='ERROR'): + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'ERROR' + + # check_and_send_usage_report() raises an exception + with patch('weko_records_ui.fd.check_and_send_usage_report', + side_effect=BaseException): + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'ERROR' + + # update_extra_info() raises an SQLAlchemyError + with patch('weko_records_ui.models.FileOnetimeDownload.update_extra_info', + side_effect=SQLAlchemyError): + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'ERROR' + + # save_download_log() raises an exception + with patch('weko_records_ui.fd.save_download_log', + side_effect=Exception): + assert file_download_onetime( + pid, record, filename, _record_file_factory) == 'ERROR' + # def _is_terms_of_use_only(file_obj:dict , req :dict) -> bool: # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test__is_terms_of_use_only -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @@ -358,53 +397,69 @@ def test__is_terms_of_use_only(app, records_restricted, users, db_file_permissio with patch("flask_login.utils._get_user", return_value=users[0]["obj"]): assert not _is_terms_of_use_only(provide_not,{'terms_of_use_only': True}) -# def file_download_secret(pid, record, _record_file_factory=None, **kwargs): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test_file_download_secret -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_file_download_secret(app,db, itemtypes, users, records): - indexer, results = records - recid = results[1]["recid"] - record = results[1]["record"] - - # token:str = re.sub("^.+?token=" ,"" , urls[0]) - with app.test_request_context("?token=MSA1IDIwMjMtMDMtMDggMDA6NTI6MTkuNjI0NTUyIDZGQTdEMzIxQTk0OTU1MEQ="): - p = FileSecretDownload.create(file_name="helloworld.docx", record_id=recid.pid_value, user_mail=users[0]["email"] ,download_count = 1) - factory = MagicMock() - factory.obj = {"":""} - with patch("flask_login.utils._get_user", return_value=users[1]["obj"]): - with patch("weko_records_ui.fd.render_template", side_effect=lambda x,error: (x,error)): #return param - with patch("weko_records_ui.fd._download_file", return_value="_download_file"): - with patch("weko_records_ui.fd.parse_secret_download_token", return_value=( "parse_secret_download_token",("token"))): - #28 - assert file_download_secret(recid,record,record_file_factory) == ('weko_theme/error.html', "parse_secret_download_token") - with patch("weko_records_ui.fd.parse_secret_download_token", return_value=(False , (results[1]["recid"].pid_value,1,p.created,""))): - with patch("weko_records_ui.fd.validate_download_record", return_value=None): - with patch("weko_records_ui.fd.validate_secret_download_token", return_value=(False , "validate_secret_download_token")): - # 29 - assert file_download_secret(recid,record,record_file_factory,filename="helloworld.docx")==('weko_theme/error.html' , "validate_secret_download_token") - with patch("weko_records_ui.fd.validate_secret_download_token", return_value=(True , "")): - with patch("weko_records_ui.fd.record_file_factory", return_value=""): - # 30 - assert file_download_secret(recid,record,record_file_factory=lambda x ,y ,z: "",filename="helloworld.docx")==('weko_theme/error.html' , "{} does not exist.".format(results[1]["filename"])) - with patch("weko_records_ui.fd.record_file_factory", return_value=factory): - with patch("weko_records_ui.fd.get_secret_download", return_value=p): - with patch("weko_records_ui.fd.update_secret_download", return_value=None): - assert file_download_secret(results[0]["recid"],results[0]["record"],record_file_factory=None,filename="helloworld.docx")==('weko_theme/error.html' , "Unexpected error occurred.") - #31 - assert file_download_secret(recid,record,record_file_factory,filename="helloworld.docx")=="_download_file" - assert db.session.query(FileSecretDownload).one_or_none().download_count == 0 - with patch("weko_records_ui.fd.get_secret_download", return_value=None): - with pytest.raises(Forbidden): - file_download_secret(recid,record,record_file_factory,filename="helloworld.docx") - with patch("weko_records_ui.fd.render_template", side_effect=lambda x,error: (x,error)): #return param - with patch("weko_records_ui.fd.parse_secret_download_token", return_value=(False , (results[1]["recid"].pid_value,1,p.created,""))): - with patch("weko_records_ui.fd.validate_secret_download_token", return_value=(True , "")): - with patch("weko_records_ui.fd._download_file", return_value="_download_file"): - with patch("weko_records_ui.fd.record_file_factory", return_value=factory): - with patch("weko_records_ui.fd.get_secret_download", return_value=p): - assert file_download_secret(recid,record,record_file_factory,filename="helloworld.docx")=="_download_file" - - with patch("weko_records_ui.fd.record_file_factory", return_value=False): - assert file_download_onetime(recid,record,record_file_factory)==('weko_theme/error.html', 'Token is invalid.') +@patch('weko_records_ui.fd.request.args.get') +@patch('weko_records_ui.fd.validate_url_download') +@patch('weko_records_ui.fd.error_response') +@patch('weko_records_ui.fd.current_user') +@patch('weko_records_ui.fd.save_download_log') +@patch('weko_records_ui.fd._download_file') +def test_file_download_secret(dl_file, save_log, current_user, err_res, + val_url, token, secret_url): + # Setup arguments + pid = None + record = {} + filename = 'test.txt' + _record_file_factory = MagicMock() + _record_file_factory.return_value = file_obj = MagicMock() + file_obj.obj = {'filename': filename} + file_obj.get.return_value = 'open_no' + + # Setup default return values of mock objects + token.return_value = secret_url['secret_token'] + val_url.return_value = (True, '') + current_user.is_authenticated = False + err_res.return_value = 'ERROR' + dl_file.return_value = 'SUCCESS' + + # Happy path + assert file_download_secret( + pid, record, filename, _record_file_factory) == 'SUCCESS' + save_log.assert_called_once_with( + record, filename, token.return_value, is_secret_url=True) + dl_file.assert_called_once_with( + file_obj, False, 'en', file_obj.obj, pid, record) + + # Happy path with authenticated user + mock_user = MagicMock() + mock_user.language = 'ja' + with patch('weko_user_profiles.models.UserProfile.get_by_userid', + return_value=mock_user): + with patch('weko_records_ui.fd.current_user.is_authenticated', True): + assert file_download_secret( + pid, record, filename, _record_file_factory) == 'SUCCESS' + save_log.assert_called_with( + record, filename, token.return_value, is_secret_url=True) + dl_file.assert_called_with( + file_obj, False, 'ja', file_obj.obj, pid, record) + + # Invalid token + with patch('weko_records_ui.fd.validate_url_download', + return_value=(False, 'Invalid token')): + assert file_download_secret( + pid, record, filename, _record_file_factory) == 'ERROR' + + # File object is not found + with patch('weko_records_ui.fd.record_file_factory', + return_value=None): + assert file_download_secret( + pid, record, filename) == 'ERROR' + + # save_download_log() raises an exception + with patch('weko_records_ui.fd.save_download_log', + side_effect=Exception): + assert file_download_secret( + pid, record, filename, _record_file_factory) == 'ERROR' # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_fd.py::test_file_list_ui -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp diff --git a/modules/weko-records-ui/tests/test_models.py b/modules/weko-records-ui/tests/test_models.py index c8b72a5140..b58b743035 100644 --- a/modules/weko-records-ui/tests/test_models.py +++ b/modules/weko-records-ui/tests/test_models.py @@ -1,4 +1,5 @@ import io +import time from datetime import datetime, timedelta, timezone from unittest import mock # python3 #from unittest.mock import MagicMock @@ -13,14 +14,12 @@ from invenio_accounts.models import Role, User from invenio_accounts.testutils import create_test_user, login_user_via_session from mock import patch +from sqlalchemy.exc import IntegrityError + +from weko_records_ui.models import (AccessStatus, FileUrlDownloadLog, + InstitutionName, FileSecretDownload, FilePermission ,FileOnetimeDownload, + UrlType) -from weko_records_ui.models import ( - InstitutionName - ,FileSecretDownload - ,FilePermission - ,FileOnetimeDownload -) - institution_name = InstitutionName( name="test" @@ -106,17 +105,6 @@ def test_FilePermission_delete_object(app, db, db_FilePermission): assert db.session.query(FilePermission).count() == 0 -def test_FileOnetimeDownload_update_download(app, db, db_FileOneTimeDownload): - data1 = { - "file_name": db_FileOneTimeDownload.file_name, - "user_mail": db_FileOneTimeDownload.user_mail, - "record_id": db_FileOneTimeDownload.record_id, - } - - db_FileOneTimeDownload.update_download( - data=data1 - ) - # def find_list_permission_approved(record_id, file_name): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::test_find_list_permission_approved -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp def test_find_list_permission_approved(app, records_restricted, users,db_file_permission): @@ -135,6 +123,285 @@ def test_find_by_activity(db_file_permission): sorted_list = sorted(listpermission, key= lambda x: x.id ,reverse=True) assert listpermission == sorted_list + +class TestFileOnetimeDownload: + expiration_date = datetime.now(timezone.utc) + timedelta(hours=24) + no_tz = expiration_date.replace(tzinfo=None) + base_data = { + 'approver_id': 1, + 'record_id': '1', + 'file_name': 'test file', + 'expiration_date': expiration_date, + 'download_limit': 1, + 'user_mail': 'test@example.org', + 'is_guest': False, + 'extra_info': {'info': 'value'} + } + expected_data = { + **base_data, + 'expiration_date': no_tz + } + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_init -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_init(self, db): + obj = FileOnetimeDownload(**self.base_data) + for key, value in self.base_data.items(): + assert getattr(obj, key) == value + for key in self.base_data.keys(): + bad_data = self.base_data.copy() + bad_data.pop(key) + with pytest.raises(TypeError): + FileOnetimeDownload(**bad_data) + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_create -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_create(self, users): + assert FileOnetimeDownload.query.count() == 0 + obj = FileOnetimeDownload.create(**self.base_data) + assert isinstance(obj, FileOnetimeDownload) + assert FileOnetimeDownload.query.count() == 1 + rec = FileOnetimeDownload.query.first() + for key, value in self.expected_data.items(): + assert getattr(rec, key) == value + + bad_data1 = self.base_data.copy() + bad_data1['expiration_date'] = (datetime.now(timezone.utc) + - timedelta(hours=24)) + with pytest.raises(Exception): + FileOnetimeDownload.create(**bad_data1) + assert FileOnetimeDownload.query.count() == 1 + + bad_data2 = self.base_data.copy() + bad_data2['download_limit'] = -1 + with pytest.raises(Exception): + FileOnetimeDownload.create(**bad_data2) + assert FileOnetimeDownload.query.count() == 1 + + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + FileOnetimeDownload.create(**self.base_data) + mock_commit.assert_called_once() + assert FileOnetimeDownload.query.count() == 1 + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_get_by_id -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_get_by_id(self, users): + FileOnetimeDownload.create(**self.base_data) + record = FileOnetimeDownload.get_by_id(1) + assert isinstance(record, FileOnetimeDownload) + not_found = FileOnetimeDownload.get_by_id(100) + assert not_found is None + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_find_downloadable_only -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_find_downloadable_only(self, db, users): + # Setups + expiring = datetime.now(timezone.utc)+timedelta(seconds=3) + ex_data = {**self.base_data, 'expiration_date': expiring} + FileOnetimeDownload.create(**ex_data) + for _ in range(2): + FileOnetimeDownload.create(**self.base_data) + query = {'file_name': self.base_data['file_name'], + 'record_id': self.base_data['record_id'], + 'user_mail': self.base_data['user_mail']} + + # Test + recs = FileOnetimeDownload.find_downloadable_only(**query) + assert len(recs) == 3 + recs[0].increment_download_count() + recs = FileOnetimeDownload.find_downloadable_only(**query) + assert len(recs) == 2 + recs[0].delete_logically() + recs = FileOnetimeDownload.find_downloadable_only(**query) + assert len(recs) == 1 + time.sleep(3) + recs = FileOnetimeDownload.find_downloadable_only(**query) + assert len(recs) == 0 + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_update_extra_info -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_update_extra_info(self, users): + obj = FileOnetimeDownload.create(**self.base_data) + obj.update_extra_info({'new': 'value'}) + assert obj.extra_info == {'new': 'value'} + + with pytest.raises(ValueError): + obj.update_extra_info('invalid') + assert obj.extra_info == {'new': 'value'} + + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + obj.update_extra_info({'new': 'value2'}) + mock_commit.assert_called_once() + assert obj.extra_info == {'new': 'value'} + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_increment_download_count -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_increment_download_count(self, users): + rec = FileOnetimeDownload.create(**self.base_data) + assert rec.download_count == 0 + rec.increment_download_count() + assert rec.download_count == 1 + + with pytest.raises(ValueError): + rec.increment_download_count() + assert rec.download_count == 1 + + rec2 = FileOnetimeDownload.create(**self.base_data) + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + rec2.increment_download_count() + mock_commit.assert_called_once() + assert rec2.download_count == 0 + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_delete_logically -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_delete_logically(self, users): + rec = FileOnetimeDownload.create(**self.base_data) + assert rec.is_deleted is False + rec.delete_logically() + assert rec.is_deleted is True + + rec.delete_logically() + assert rec.is_deleted is True + + rec2 = FileOnetimeDownload.create(**self.base_data) + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + rec2.delete_logically() + mock_commit.assert_called_once() + assert rec2.is_deleted is False + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileOnetimeDownload::test_fetch_active_urls -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_fetch_active_urls(self, users): + recid = self.base_data['record_id'] + filename = self.base_data['file_name'] + assert FileOnetimeDownload.fetch_active_urls(recid, filename) == [] + FileOnetimeDownload.create(**self.base_data) + assert len(FileOnetimeDownload.fetch_active_urls(recid, filename)) == 1 + FileOnetimeDownload.create(**self.base_data) + assert len(FileOnetimeDownload.fetch_active_urls(recid, filename)) == 2 + assert FileOnetimeDownload.fetch_active_urls( + recid, filename, ascending=True)[0].id == 1 + assert FileOnetimeDownload.fetch_active_urls( + recid, filename, ascending=False)[0].id == 2 + + +class TestFileSecretDownload: + expiration_date = datetime.now(timezone.utc) + timedelta(hours=24) + no_tz = expiration_date.replace(tzinfo=None) + base_data = { + 'creator_id': 1, + 'record_id': '1', + 'file_name': 'test file', + 'label_name': 'test label', + 'expiration_date': expiration_date, + 'download_limit': 1 + } + expected_data = { + **base_data, + 'expiration_date': no_tz + } + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_init -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_init(self, db): + obj = FileSecretDownload(**self.base_data) + for key, value in self.base_data.items(): + assert getattr(obj, key) == value + for key in self.base_data.keys(): + bad_data = self.base_data.copy() + bad_data.pop(key) + with pytest.raises(TypeError): + FileSecretDownload(**bad_data) + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_create -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_create(self, users): + assert FileSecretDownload.query.count() == 0 + obj = FileSecretDownload.create(**self.base_data) + assert isinstance(obj, FileSecretDownload) + assert FileSecretDownload.query.count() == 1 + rec = FileSecretDownload.query.first() + for key, value in self.expected_data.items(): + assert getattr(rec, key) == value + + bad_data1 = self.base_data.copy() + bad_data1['expiration_date'] = (datetime.now(timezone.utc) + - timedelta(hours=24)) + with pytest.raises(Exception): + FileSecretDownload.create(**bad_data1) + assert FileSecretDownload.query.count() == 1 + + bad_data2 = self.base_data.copy() + bad_data2['download_limit'] = -1 + with pytest.raises(Exception): + FileSecretDownload.create(**bad_data2) + assert FileSecretDownload.query.count() == 1 + + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + FileSecretDownload.create(**self.base_data) + mock_commit.assert_called_once() + assert FileSecretDownload.query.count() == 1 + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_get_by_id -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_get_by_id(self, users): + FileSecretDownload.create(**self.base_data) + record = FileSecretDownload.get_by_id(1) + assert isinstance(record, FileSecretDownload) + not_found = FileSecretDownload.get_by_id(100) + assert not_found is None + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_increment_download_count -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_increment_download_count(self, users): + rec = FileSecretDownload.create(**self.base_data) + assert rec.download_count == 0 + rec.increment_download_count() + assert rec.download_count == 1 + + with pytest.raises(ValueError): + rec.increment_download_count() + assert rec.download_count == 1 + + rec2 = FileSecretDownload.create(**self.base_data) + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + rec2.increment_download_count() + mock_commit.assert_called_once() + assert rec2.download_count == 0 + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_delete_logically -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_delete_logically(self, users): + rec = FileSecretDownload.create(**self.base_data) + assert rec.is_deleted is False + rec.delete_logically() + assert rec.is_deleted is True + + rec.delete_logically() + assert rec.is_deleted is True + + rec2 = FileSecretDownload.create(**self.base_data) + with patch('weko_records_ui.models.db.session.commit') as mock_commit: + mock_commit.side_effect = Exception('DB error test') + with pytest.raises(Exception): + rec2.delete_logically() + mock_commit.assert_called_once() + assert rec2.is_deleted is False + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileSecretDownload::test_fetch_active_urls -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_fetch_active_urls(self, users): + recid = self.base_data['record_id'] + filename = self.base_data['file_name'] + assert FileSecretDownload.fetch_active_urls(recid, filename) == [] + FileSecretDownload.create(**self.base_data) + assert len(FileSecretDownload.fetch_active_urls(recid, filename)) == 1 + FileSecretDownload.create(**self.base_data) + assert len(FileSecretDownload.fetch_active_urls(recid, filename)) == 2 + assert FileSecretDownload.fetch_active_urls( + recid, filename, ascending=True)[0].id == 1 + assert FileSecretDownload.fetch_active_urls( + recid, filename, ascending=False)[0].id == 2 + + # def find_downloadable_only(cls, **obj) -> list: # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::test_find_downloadable_only -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @pytest.mark.skip(reason="'from sqlalchemy.dialects.postgresql import INTERVAL' can't tests on SQLite.") @@ -155,114 +422,146 @@ def test_find_downloadable_only(app,db): assert len(recs) == 2 -# def find_by_activity: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::test_find_by_activity -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_init(): - # 36 - dl = FileSecretDownload("a","b","c",1,2) - assert dl.file_name == "a" - assert dl.user_mail == "b" - assert dl.record_id == "c" - assert dl.download_count == 1 - assert dl.expiration_date == 2 - -# def create(cls, **data): -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::test_create -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_create(app,db): - # 37 42 - with app.test_request_context(): - dl = FileSecretDownload.create( - file_name = "a" - ,user_mail ="b" - ,record_id = "c" - ,download_count = 1 - ,expiration_date= 2 - ) - assert dl.id - assert dl.created - rec = FileSecretDownload.find( - id = dl.id - ,file_name = "a" - ,record_id = "c" - ,created = dl.created - ) - assert len(rec) == 1 - rec = rec[0] - assert rec.file_name == "a" - assert rec.user_mail == "b" - assert rec.record_id == "c" - assert rec.download_count == 1 - assert rec.expiration_date == 2 - - # 38 - with app.test_request_context(): - with patch("weko_records_ui.models.db.session.add", side_effect=Exception("test_error")): - defaultlength = len(FileSecretDownload.query.filter_by().all()) - with pytest.raises(Exception): - dl = FileSecretDownload.create( - file_name = "a" - ,user_mail ="b" - ,record_id = "c" - ) - assert defaultlength == len(FileSecretDownload.query.filter_by().all()) - -# def update_download(cls, **data): -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::test_update_download -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_update_download(app,db): - with app.test_request_context(): - # 39 - dl = FileSecretDownload.create( - file_name = "a" - ,user_mail ="b" - ,record_id = "c" - ,download_count = 1 - ,expiration_date= 2 - ) - result = FileSecretDownload.update_download( - id = dl.id - ,file_name = "a" - ,record_id = "c" - ,created = dl.created - ,download_count = 100 - ) - if result: - assert result[0].download_count == 100 - else: - assert False - - # 40 - assert FileSecretDownload.update_download( - id = dl.id + 1 - ,file_name = "a" - ,record_id = "c" - ,created = dl.created - ) == None - assert FileSecretDownload.update_download( - id = dl.id - ,file_name = "a" - ,record_id = "c" - ,created = dl.created - ) -def test_update_download2(app,db): - with app.test_request_context(): - # 41 - dl = FileSecretDownload.create( - file_name = "a" - ,user_mail ="b" - ,record_id = "c" - ,download_count = 1 - ,expiration_date= 2 - ) - with patch("weko_records_ui.models.db.session.merge", side_effect=Exception("test_error")): - before = FileSecretDownload.query.filter_by().one_or_none().download_count - try: - dl = FileSecretDownload.update_download( - id = dl.id - ,file_name = "a" - ,record_id = "c" - ,created = dl.created - ,download_count = 200 - ) - assert False - except: - assert before == FileSecretDownload.query.filter_by().one_or_none().download_count +class TestFileUrlDownloadLog: + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileUrlDownloadLog::test_init -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_init(self): + secret_dl_data = { + 'url_type': UrlType.SECRET, + 'secret_url_id': 1, + 'onetime_url_id': None, + 'ip_address': '192.168.56.1', + 'access_status': AccessStatus.OPEN_NO, + 'used_token': 'test token' + } + # When each key is present + log_obj = FileUrlDownloadLog(**secret_dl_data) + for key, value in secret_dl_data.items(): + assert getattr(log_obj, key) == value + # When each key is missing + for key in secret_dl_data.keys(): + bad_data = secret_dl_data.copy() + bad_data.pop(key) + with pytest.raises(TypeError): + FileUrlDownloadLog(**bad_data) + + onetime_dl_data = { + 'url_type': UrlType.ONETIME, + 'secret_url_id': None, + 'onetime_url_id': 1, + 'ip_address': None, + 'access_status': AccessStatus.OPEN_RESTRICTED, + 'used_token': 'test token' + } + # When each key is present + log_obj = FileUrlDownloadLog(**onetime_dl_data) + for key, value in onetime_dl_data.items(): + assert getattr(log_obj, key) == value + # When each key is missing + for key in onetime_dl_data.keys(): + bad_data = onetime_dl_data.copy() + bad_data.pop(key) + with pytest.raises(TypeError): + FileUrlDownloadLog(**bad_data) + + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_models.py::TestFileUrlDownloadLog::test_create -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp + def test_create(self, secret_url, onetime_url): + # Happy path with secret URL(open_no) + open_no = { + 'url_type': UrlType.SECRET, + 'secret_url_id': secret_url['secret_obj'].id, + 'onetime_url_id': None, + 'ip_address': '192.168.56.1', + 'access_status': AccessStatus.OPEN_NO, + 'used_token': secret_url['secret_token'] + } + assert FileUrlDownloadLog.query.count() == 0 + log_obj = FileUrlDownloadLog.create(**open_no) + assert isinstance(log_obj, FileUrlDownloadLog) + assert FileUrlDownloadLog.query.count() == 1 + log_rec = FileUrlDownloadLog.query.filter_by(id=1).first() + for key, value in open_no.items(): + assert getattr(log_rec, key) == value + + # Happy path with secret URL(open_date) + open_date = { + 'url_type': UrlType.SECRET, + 'secret_url_id': secret_url['secret_obj'].id, + 'onetime_url_id': None, + 'ip_address': '192.168.56.1', + 'access_status': AccessStatus.OPEN_DATE, + 'used_token': secret_url['secret_token'] + } + assert FileUrlDownloadLog.query.count() == 1 + log_obj = FileUrlDownloadLog.create(**open_date) + assert isinstance(log_obj, FileUrlDownloadLog) + assert FileUrlDownloadLog.query.count() == 2 + log_rec = FileUrlDownloadLog.query.filter_by(id=2).first() + for key, value in open_date.items(): + assert getattr(log_rec, key) == value + + # Happy path with onetime URL(open_restricted) + open_restricted = { + 'url_type': UrlType.ONETIME, + 'secret_url_id': None, + 'onetime_url_id': onetime_url['onetime_obj'].id, + 'ip_address': None, + 'access_status': AccessStatus.OPEN_RESTRICTED, + 'used_token': onetime_url['onetime_token'] + } + assert FileUrlDownloadLog.query.count() == 2 + log_obj = FileUrlDownloadLog.create(**open_restricted) + assert isinstance(log_obj, FileUrlDownloadLog) + assert FileUrlDownloadLog.query.count() == 3 + log_rec = FileUrlDownloadLog.query.filter_by(id=3).first() + for key, value in open_restricted.items(): + assert getattr(log_rec, key) == value + + # Either secret_url_id or onetime_url_id must be null + secret_id_err = open_no.copy() + secret_id_err['onetime_url_id'] = onetime_url['onetime_obj'].id + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**secret_id_err) + onetime_id_err = open_restricted.copy() + onetime_id_err['secret_url_id'] = secret_url['secret_obj'].id + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**onetime_id_err) + + # Either secret_url_id or onetime_url_id must be present + no_id_err = open_no.copy() + no_id_err['secret_url_id'] = None + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**no_id_err) + no_id_err = open_restricted.copy() + no_id_err['onetime_url_id'] = None + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**no_id_err) + + # ip_address must be present if url_type is secret + secret_ip_err = open_no.copy() + secret_ip_err['ip_address'] = None + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**secret_ip_err) + + # ip_address must be null if url_type is onetime + onetime_ip_err = open_restricted.copy() + onetime_ip_err['ip_address'] = open_no['ip_address'] + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**onetime_ip_err) + + # access_status must be 'open_no' or 'open_date' if url_type is secret + secret_access_err = open_no.copy() + secret_access_err['access_status'] = AccessStatus.OPEN_RESTRICTED + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**secret_access_err) + + # access_status must be 'open_restricted' if url_type is onetime + onetime_access_err = open_restricted.copy() + onetime_access_err['access_status'] = AccessStatus.OPEN_NO + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**onetime_access_err) + + # used_token must be present + token_err = open_no.copy() + token_err['used_token'] = None + with pytest.raises(IntegrityError): + FileUrlDownloadLog.create(**token_err) diff --git a/modules/weko-records-ui/tests/test_utils.py b/modules/weko-records-ui/tests/test_utils.py index 579ecfb0be..e2e1dbefee 100644 --- a/modules/weko-records-ui/tests/test_utils.py +++ b/modules/weko-records-ui/tests/test_utils.py @@ -1,20 +1,27 @@ +import re import pytest from weko_records_ui.utils import ( + can_manage_onetime_url, + convert_token_into_obj, + has_permission_to_manage_onetime_url, + is_onetime_file, + save_download_log, + to_utc_datetime, + create_download_url, + create_onetime_url_record, + create_secret_url_record, + generate_sha256_hash, is_future, create_usage_report_for_user, get_data_usage_application_data, + send_secret_url_mail, send_usage_report_mail_for_user, check_and_send_usage_report, - update_onetime_download, - create_onetime_download_url, get_onetime_download, - validate_onetime_download_token, get_license_pdf, hide_item_metadata, get_pair_value, get_min_price_billing_file_download, - parse_one_time_download_token, - generate_one_time_download_url, validate_download_record, is_private_index, get_file_info_list, @@ -33,25 +40,29 @@ get_record_permalink, get_google_detaset_meta, get_google_scholar_meta, - create_secret_url, - parse_secret_download_token, - validate_secret_download_token, - get_secret_download, - update_secret_download, get_valid_onetime_download, display_oaiset_path, get_terms, get_roles, check_items_settings, + validate_expiration_date, + validate_file_access, + validate_secret_url_generation_request, #RoCrateConverter, #create_tsv + is_secret_url_feature_enabled, + has_permission_to_manage_secret_url, + is_secret_file, + can_manage_secret_url, + validate_token, + validate_url_download, ) import base64 from unittest.mock import MagicMock import copy import pytest import io -from datetime import datetime as dt +from datetime import date, datetime as dt, time, timezone from datetime import timedelta from lxml import etree from fpdf import FPDF @@ -62,7 +73,8 @@ from invenio_pidstore.models import PersistentIdentifier, PIDStatus from mock import patch from weko_deposit.api import WekoRecord -from weko_records_ui.models import FileOnetimeDownload, FileSecretDownload +from weko_records_ui.models import (AccessStatus, FileOnetimeDownload, + FileSecretDownload, FileUrlDownloadLog, UrlType) from weko_records.api import ItemTypes,Mapping from werkzeug.exceptions import NotFound from weko_admin.models import AdminSettings @@ -71,6 +83,8 @@ from flask_babelex import gettext as _ from datetime import datetime ,timedelta +from weko_schema_ui.models import PublishStatus + # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp # def is_future(settings=None): @@ -544,55 +558,6 @@ def send_reminder_mail_2(x, y, z): with patch("weko_records_ui.utils.check_create_usage_report",return_value=None): check_and_send_usage_report({"is_guest": False, "send_usage_report": True, "usage_application_activity_id": "A-20230101-00001"},users[7]['email'],data1, data2) -# def generate_one_time_download_url( -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_generate_one_time_download_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_generate_one_time_download_url(app): - file_name = "003.jpg" - record_id = "1" - guest_mail= "user@example.org" - with app.test_request_context(): - ret = generate_one_time_download_url(file_name,record_id,guest_mail) - rets = ret.split('token=') - token_str =base64.b64decode(rets[1]) - token = (token_str.decode('utf-8')).split(' ') - assert token[0] == record_id - assert token[1] == guest_mail - - -# def parse_one_time_download_token(token: str) -> Tuple[str, Tuple]: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_parse_one_time_download_token -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_parse_one_time_download_token(app): - token = "MSB1c2VyQGV4YW1wbGUub3JnIDIwMjItMDktMjcgNDBDRkNGODFGM0FFRUI0Ng==" - with app.test_request_context(): - assert parse_one_time_download_token(token)==('', ('1', 'user@example.org', '2022-09-27', '40CFCF81F3AEEB46')) - assert parse_one_time_download_token("test") != None - assert parse_one_time_download_token(None) != None - - -# def validate_onetime_download_token( -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_onetime_download_token -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_validate_onetime_download_token(app,db_fileonetimedownload): - file_name='helloworld.pdf' - record_id='1' - user_email='wekosoftware@nii.ac.jp' - token = "9948A41F46456DF5" - date = "2022-09-28" - with app.test_request_context(): - file_downloads = FileOnetimeDownload.find( - file_name=file_name, record_id=record_id, user_mail=user_email - ) - assert validate_onetime_download_token(file_downloads[0],file_name,record_id,user_email,date,token)== (True, '') - - data1 = MagicMock() - data1.download_count = 0 - - with patch('passlib.handlers.oracle.oracle10.verify', return_value=False): - assert validate_onetime_download_token(file_downloads[0],file_name,record_id,user_email,date,token)== (False, 'Token is invalid.') - - assert validate_onetime_download_token(False,file_name,record_id,user_email,date,token)== (False, 'Token is invalid.') - - assert validate_onetime_download_token(data1,file_name,record_id,user_email,date,token)== (False, 'Token is invalid.') - # def is_private_index(record): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_is_private_index -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @@ -624,25 +589,204 @@ def test_is_private_index(app,records): # def validate_download_record(record: dict): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_download_record -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_validate_download_record(app, records): - indexer, results = records - record = results[0]["record"] - assert validate_download_record(record)==None - - data1 = { - "publish_status": 1 - } +def test_validate_download_record(): + record = {'publish_status': PublishStatus.PUBLIC.value} + with patch('weko_records_ui.utils.is_private_index', return_value=False): + assert validate_download_record(record) is True + record['publish_status'] = None + assert validate_download_record(record) is False + record['publish_status'] = PublishStatus.PUBLIC.value + with patch('weko_records_ui.utils.is_private_index', return_value=True): + assert validate_download_record(record) is False + record['publish_status'] = None + assert validate_download_record(record) is False + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_is_secret_url_feature_enabled -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_is_secret_url_feature_enabled(app): + with app.app_context(): + # Case 1: secret_enableがTrueを返す + with patch('weko_records_ui.utils.AdminSettings.get') as mock_get: + mock_get.return_value = { + 'secret_URL_file_download': { + 'secret_enable': True, + } + } + assert is_secret_url_feature_enabled() is True - try: - validate_download_record(data1) - except: - pass + # Case 2: secret_enableがFalseを返す + with patch('weko_records_ui.utils.AdminSettings.get') as mock_get: + mock_get.return_value = { + 'secret_URL_file_download': { + 'secret_enable': False, + } + } + assert is_secret_url_feature_enabled() is False - with patch("weko_records_ui.utils.is_private_index", return_value=True): - try: - validate_download_record(record) - except: - pass + # Case 3: AdminSettingsがNoneでcurrent_app.configが存在し、期待するデフォルト設定がある場合 + with patch('weko_records_ui.utils.AdminSettings.get', return_value=None): + with patch('weko_records_ui.utils.current_app.config', { + 'WEKO_ADMIN_RESTRICTED_ACCESS_SETTINGS': { + 'secret_URL_file_download': {} + } + }): + # secret_enable は存在しないのでデフォルト値の False を返すことを検証 + assert is_secret_url_feature_enabled() is False + + # Case 4: AdminSettingsがNoneでcurrent_app.configが存在しない場合 + with patch('weko_records_ui.utils.AdminSettings.get', return_value=None): + with patch('weko_records_ui.utils.current_app.config', {'WEKO_ADMIN_RESTRICTED_ACCESS_SETTINGS': {}}): + # 設定がない場合も False を返すことを検証 + assert is_secret_url_feature_enabled() is False + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_has_permission_to_manage_secret_url -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@pytest.mark.parametrize( + "user_id, expected", + [ + (0, True), # Owner + (4, True), # Shared user + (1, True), # Superuser + (2, True), # Superuser + (3, False), # No permission + (5, False), # No superuser role + ], +) +def test_has_permission_to_manage_secret_url(user_id, expected, app, users): + # レコードに必要なデータを設定 + # 'owner'と'weko_shared_id'は、usersリストから取り出した値を使用 + record = {'owner': str(users[0]["id"]), 'weko_shared_id': users[4]["id"]} + + # アプリケーションコンテキスト内でテスト実行 + with app.app_context(): + # has_permission_to_manage_secret_url関数を実行し、 + # 結果が期待される値 (expected) と一致するかを検証 + assert has_permission_to_manage_secret_url(record, users[user_id]["id"]) == expected + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_has_permission_to_manage_onetime_url -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@pytest.mark.parametrize( + "user_id, expected", + [ + (0, True), # Owner + (4, False), # Shared user + (1, True), # Superuser + (2, True), # Superuser + (3, False), # No permission + (5, False), # No superuser role + ], +) +def test_has_permission_to_manage_onetime_url(user_id, expected, app, users): + record = {'owner': str(users[0]["id"]), 'weko_shared_id': users[4]["id"]} + with app.app_context(): + assert has_permission_to_manage_onetime_url( + record, users[user_id]["id"]) is expected + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_is_secret_file -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@pytest.mark.parametrize( + "file_data, filename, expected", + [ + # ケース1: accessroleが 'open_no' の場合 + ([{'filename': 'testfile.txt', 'accessrole': 'open_no', 'date': [{'dateValue': '2999-12-31'}]}], 'testfile.txt', True), + # ケース2: accessroleが 'open_date' で公開日が未来の場合 + ([{'filename': 'testfile.txt', 'accessrole': 'open_date', 'date': [{'dateValue': '2999-12-31'}]}], 'testfile.txt', True), + # ケース3: accessroleが 'open_date' で公開日が過去の場合 + ([{'filename': 'testfile.txt', 'accessrole': 'open_date', 'date': [{'dateValue': '2000-01-01'}]}], 'testfile.txt', False), + # ケース4: accessroleが 'open_no' や 'open_date' でない場合 + ([{'filename': 'testfile.txt', 'accessrole': 'open_test', 'date': [{'dateValue': '2999-12-31'}]}], 'testfile.txt', False), + # ケース5: ファイル名が一致しない場合 + ([{'filename': 'otherfile.txt', 'accessrole': 'open_no', 'date': [{'dateValue': '2999-12-31'}]}], 'testfile.txt', False), + # ケース6: file_dataが空の場合 + ([], 'testfile.txt', False), + ], +) +def test_is_secret_file(file_data, filename, expected): + # WekoRecordのモックを作成し、get_file_dataメソッドをファイルデータでモックする + mock_record = MagicMock(spec=WekoRecord) + mock_record.get_file_data.return_value = file_data # モックのget_file_dataメソッドが返す値を設定 + + # dt(日時関連)をモックして、現在の日付や日付文字列の変換を制御する + with patch('weko_records_ui.utils.dt') as mock_dt: + mock_dt.utcnow.return_value = dt(2024, 1, 1) # 現在の日付を2024年1月1日に設定 + mock_dt.strptime.side_effect = lambda *args, **kwargs: dt.strptime(*args, **kwargs) # strptimeの動作をモック + + # is_secret_file関数を実行して、結果が期待される値と一致するかを確認 + result = is_secret_file(mock_record, filename) + + # 実際の結果が期待値と一致することを確認 + assert result == expected + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_is_onetime_file -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_is_onetime_file(): + mock_record = MagicMock() + mock_record.get_file_data.return_value = [ + {"filename": "file1.txt", "accessrole": "open_restricted"}, + {"filename": "file2.txt", "accessrole": "public"} + ] + assert is_onetime_file(mock_record, "file1.txt") is True + assert is_onetime_file(mock_record, "file2.txt") is False + assert is_onetime_file(mock_record, "file3.txt") is False + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_can_manage_secret_url -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@pytest.mark.parametrize( + "is_authenticated, feature_enabled, has_permission, is_secret, expected", + [ + # Case 1: ユーザーが認証されていない場合 + (False, True, True, True, False), + # Case 2: 機能が有効でない場合 + (True, False, True, True, False), + # Case 3: ユーザーに権限がない場合 + (True, True, False, True, False), + # Case 4: ファイルが秘密でない場合 + (True, True, True, False, False), + # Case 5: すべての条件を満たす場合 + (True, True, True, True, True), + ], +) +def test_can_manage_secret_url(is_authenticated, feature_enabled, has_permission, is_secret, expected): + # WekoRecordのモックを作成 + mock_record = MagicMock(spec=WekoRecord) + + # ユーザーのモックを作成 + mock_user = MagicMock() + mock_user.is_authenticated = is_authenticated # ユーザーが認証されているかどうかを設定 + + # current_userのモックを作成して、`mock_user`を返すように設定 + with patch('weko_records_ui.utils.current_user', mock_user): + # is_secret_url_feature_enabledのモックを作成して、`feature_enabled`を返すように設定 + with patch('weko_records_ui.utils.is_secret_url_feature_enabled', return_value=feature_enabled): + # has_permission_to_manage_secret_urlのモックを作成して、`has_permission`を返すように設定 + with patch('weko_records_ui.utils.has_permission_to_manage_secret_url', return_value=has_permission): + # is_secret_fileのモックを作成して、`is_secret`を返すように設定 + with patch('weko_records_ui.utils.is_secret_file', return_value=is_secret): + # can_manage_secret_url関数を実行し、結果が期待される値と一致するかを確認 + assert can_manage_secret_url(mock_record, 'testfile.txt') == expected + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_can_manage_onetime_url -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@pytest.mark.parametrize( + "is_authenticated, has_permission, is_onetime, expected", + [ + # Case 1: ユーザーが認証されていない場合 + (False, True, True, False), + # Case 2: ユーザーに権限がない場合 + (True, False, True, False), + # Case 3: ファイルが制限公開アイテムでない場合 + (True, True, False, False), + # Case 4: すべての条件を満たす場合 + (True, True, True, True), + ], +) +def test_can_manage_onetime_url(is_authenticated, has_permission, is_onetime, expected): + mock_record = MagicMock(spec=WekoRecord) + mock_user = MagicMock() + mock_user.is_authenticated = is_authenticated + with patch('weko_records_ui.utils.current_user', mock_user): + with patch('weko_records_ui.utils.has_permission_to_manage_onetime_url', return_value=has_permission): + with patch('weko_records_ui.utils.is_onetime_file', return_value=is_onetime): + assert can_manage_onetime_url(mock_record, 'testfile.txt') == expected # def get_onetime_download(file_name: str, record_id: str, @@ -665,24 +809,6 @@ def test_get_valid_onetime_download(): with patch("weko_records_ui.models.FileOnetimeDownload.find_downloadable_only",return_value=["a","b"]): assert "a" == get_valid_onetime_download(file_name= "str", record_id= "str",user_mail= "str") -# def create_onetime_download_url( -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_create_onetime_download_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_create_onetime_download_url(app): - with app.test_request_context(): - assert create_onetime_download_url('ACT','helloworld.pdf','1','wekosoftware@nii.ac.jp') == None - - data1 = [] - - with patch('weko_records_ui.utils.get_restricted_access', return_value=data1): - assert create_onetime_download_url('ACT','helloworld.pdf','1','wekosoftware@nii.ac.jp') == False - - -# def update_onetime_download(**kwargs) -> NoReturn: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_update_onetime_download -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_update_onetime_download(app): - with app.test_request_context(): - assert update_onetime_download(file_name="helloworld.pdf", user_mail="wekosoftware@nii.ac.jp", record_id="1", download_count=0)==None - # def get_workflows(): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_get_workflows -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @@ -765,178 +891,603 @@ def test_get_google_detaset_meta(app, records, itemtypes, oaischema, oaiidentify with patch("lxml.etree", return_value=data1): assert get_google_detaset_meta(record) == None -#def create_secret_url(record_id:str ,file_name:str ,user_mail:str ,restricted_fullname='',restricted_data_name='') -> dict: -# def _generate_secret_download_url(file_name: str, record_id: str, id: str ,created :dt) -> str: -# _create_secret_download_url(file_name: str, record_id: str, user_mail: str) -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_create_secret_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_create_secret_url(app,db,users,records): - url , results = records - file_name= results[1]["filename"] - record_id=results[1]["recid"].pid_value - user_mail = users[0]["email"] - - db.session.add(AdminSettings(id=6,name='restricted_access',settings={"secret_URL_file_download": - {"secret_enable": True, - "secret_download_limit": 1, - "secret_expiration_date": 9999999, - "secret_download_limit_unlimited_chk": False, - "secret_expiration_date_unlimited_chk": False}})) - - with app.test_request_context(): - - #60 - #76 - # with db.session.begin_nested(): - return_dict = create_secret_url(file_name= file_name, record_id=record_id, user_mail=user_mail) - - assert return_dict["restricted_download_count"] == '1' - assert return_dict["restricted_download_count_ja"] == "" - assert return_dict["restricted_download_count_en"] == "" - assert return_dict['restricted_expiration_date'] == "" - assert return_dict['restricted_expiration_date_ja'] == "無制限" - assert return_dict['restricted_expiration_date_en'] == "Unlimited" - - #61 - # with db.session.begin_nested(): - db.session.merge(AdminSettings(id=6,name='restricted_access',settings={"secret_URL_file_download": - {"secret_enable": True, - "secret_download_limit": 9999999, - "secret_expiration_date": 1, - "secret_download_limit_unlimited_chk": False, - "secret_expiration_date_unlimited_chk": False}})) - return_dict = create_secret_url(file_name= file_name - , record_id=record_id - , user_mail=user_mail) - assert return_dict["restricted_download_count"] == "" - assert return_dict["restricted_download_count_ja"] == "無制限" - assert return_dict["restricted_download_count_en"] == "Unlimited" - assert return_dict['restricted_expiration_date'] == (datetime.today() + timedelta(1)).strftime("%Y-%m-%d") - assert return_dict['restricted_expiration_date_ja'] == "" - assert return_dict['restricted_expiration_date_en'] == "" - - #62 - #63 - from re import match - assert match("^.+record\/" + record_id + "\/file\/secret\/"+file_name+"\?token=.+=$",return_dict["restricted_download_link"]) - assert return_dict["restricted_download_link"] != "" - assert return_dict["mail_recipient"] == user_mail - -# def parse_secret_download_token(token: str) -> Tuple[str, Tuple]: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_parse_secret_download_token -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_parse_secret_download_token(app ,db): - #64 - assert parse_secret_download_token(None) == (_("Token is invalid."),()) - assert parse_secret_download_token("") == (_("Token is invalid."),()) - #65 - assert parse_secret_download_token("random_string sajfosijdfasodfjv") == (_("Token is invalid."),()) +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_to_utc_datetime -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_to_utc_datetime(app): + assert to_utc_datetime('2025-1-1') == datetime( + 2025, 1, 1, 0, 0, tzinfo=timezone.utc) + assert to_utc_datetime('2025-01-01') == datetime( + 2025, 1, 1, 0, 0, tzinfo=timezone.utc) + assert to_utc_datetime('2025-1-1', 720) == datetime( + 2025, 1, 1, 12, 0, tzinfo=timezone.utc) + assert to_utc_datetime('2025-1-1', -720) == datetime( + 2024, 12, 31, 12, 0, tzinfo=timezone.utc) + assert to_utc_datetime('2025-1-1', -540) == datetime( + 2024, 12, 31, 15, 0, tzinfo=timezone.utc) + assert to_utc_datetime('2025/01/01') is None + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_secret_url_generation_request -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.validate_expiration_date') +def test_validate_secret_url_generation_request(mock_date, app): + mock_date.return_value = True + base_case = {'link_name' : '', + 'expiration_date' : 'mocked', + 'download_limit' : 1, + 'send_email' : False, + 'timezone_offset_minutes': 0} + test_cases = [ + (None, + False), + # When all fields are valid + ({'link_name' : '123', + 'expiration_date' : 'mocked', + 'download_limit' : 1, + 'send_email' : False, + 'timezone_offset_minutes': 0}, + True), + # When all fields are invalid + ({'link_name' : 123, + 'expiration_date' : 123, + 'download_limit' : 0, + 'send_email' : None, + 'timezone_offset_minutes': '0'}, + False), + # When each keys do not exist + ({key: value for key, value in base_case.items() + if key != 'link_name'}, False), + ({key: value for key, value in base_case.items() + if key != 'expiration_date'}, False), + ({key: value for key, value in base_case.items() + if key != 'download_limit'}, False), + ({key: value for key, value in base_case.items() + if key != 'send_email'}, False), + ({key: value for key, value in base_case.items() + if key != 'timezone_offset_minutes'}, False), + # For link_name + ({**base_case, 'link_name': '123' }, True), + ({**base_case, 'link_name': 123 }, False), + ({**base_case, 'link_name': 'a' * 256}, False), + # For expiration_date + ({**base_case, 'expiration_date': '2025-01-00'}, True), + ({**base_case, 'expiration_date': 20250101}, False), + # For download_limit + ({**base_case, 'download_limit': 1 }, True), + ({**base_case, 'download_limit': 0 }, False), + ({**base_case, 'download_limit': -1 }, False), + ({**base_case, 'download_limit': 1.1 }, False), + ({**base_case, 'download_limit': 'abc'}, False), + # For send_email + ({**base_case, 'send_email': False}, True), + ({**base_case, 'send_email': True }, True), + ({**base_case, 'send_email': None }, False), + # For timezone_offset_minutes + ({**base_case, 'timezone_offset_minutes': 0 }, True), + ({**base_case, 'timezone_offset_minutes': 720 }, True), + ({**base_case, 'timezone_offset_minutes': -720 }, True), + ({**base_case, 'timezone_offset_minutes': 800 }, False), + ({**base_case, 'timezone_offset_minutes': -800 }, False), + ({**base_case, 'timezone_offset_minutes': '100'}, False), + ] + for request_data, expected in test_cases: + assert validate_secret_url_generation_request(request_data) is expected + + # if validate_expiration_date is False + mock_date.return_value = False + assert validate_secret_url_generation_request(base_case) is False + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_expiration_date -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_validate_expiration_date(app): + assert validate_expiration_date('1999-12-31', 0) is False + + yesterday = (datetime.now() - timedelta(1)).strftime("%Y-%m-%d") + assert validate_expiration_date(yesterday, 0) is False + + tomorrow = (datetime.now() + timedelta(1)).strftime("%Y-%m-%d") + with patch('weko_records_ui.utils.get_restricted_access') as mock_settings: + mock_settings.return_value = None + assert validate_expiration_date(tomorrow, 0) is False + + in_a_week = (datetime.now() + timedelta(7)).strftime("%Y-%m-%d") + with patch('weko_records_ui.utils.get_restricted_access') as mock_settings: + mock_settings.return_value = {'secret_expiration_date': 1} + assert validate_expiration_date(in_a_week, 0) is False + + in_an_year = (datetime.now() + timedelta(365)).strftime("%Y-%m-%d") + with patch('weko_records_ui.utils.get_restricted_access') as mock_settings: + mock_settings.return_value = {} + assert validate_expiration_date(in_an_year, 0) is False + + invalid_date = '2025-01-00' + assert validate_expiration_date(invalid_date, 0) is False + + assert validate_expiration_date(tomorrow, 0) is True + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_create_secret_url_record -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.get_restricted_access') +@patch('weko_records_ui.utils.current_user') +def test_create_secret_url_record(mock_user, mock_settings, users): + mock_settings.return_value = {'expiration_date': 30, 'download_limit': 10} + mock_user.id = 1 + record_id = 1 + file_name = 'test.txt' + + # Test request data + request = {'link_name': '', 'expiration_date': '', 'download_limit': None, + 'timezone_offset_minutes': 0} + url_obj = create_secret_url_record(record_id, file_name, request) + assert isinstance(url_obj, FileSecretDownload) + assert url_obj.creator_id == 1 + assert url_obj.record_id == str(record_id) + assert url_obj.file_name == file_name + expected_name = 'test.txt_' + datetime.now(timezone.utc).strftime('%Y-%m-%d') + assert url_obj.label_name == expected_name + expected_date = datetime.combine((datetime.now(timezone.utc).date()+timedelta(days=31)), time(0, 0, 0)) + assert url_obj.expiration_date == expected_date + assert url_obj.download_limit == 10 + + # If request is valid + request = { + 'link_name': 'test', + 'expiration_date': (datetime.now(timezone.utc).date()).strftime('%Y-%m-%d'), + 'download_limit': 5, + 'timezone_offset_minutes': 720 + } + url_obj2 = create_secret_url_record(record_id, file_name, request) + assert url_obj2.creator_id == 1 + assert url_obj2.record_id == str(record_id) + assert url_obj2.file_name == file_name + assert url_obj2.label_name == 'test' + expected_date2 = datetime.combine((datetime.now(timezone.utc).date()+timedelta(days=1)), time(12,0,0)) + assert url_obj2.expiration_date == expected_date2 + assert url_obj2.download_limit == 5 + request = {'link_name': '', + 'expiration_date': '2022-10-10', + 'download_limit': '', + 'timezone_offset_minutes': 0} + with pytest.raises(ValueError): + create_secret_url_record(record_id, file_name, request) + request = {'link_name': '', + 'expiration_date': '', + 'download_limit': 0, + 'timezone_offset_minutes': 0} + with pytest.raises(ValueError): + create_secret_url_record(record_id, file_name, request) + + # If settings is invalid + mock_settings.return_value = {} + assert create_secret_url_record(record_id, file_name, request) is None + mock_settings.return_value = 'invalid data' + assert create_secret_url_record(record_id, file_name, request) is None + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_create_onetime_download_record -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.get_restricted_access') +@patch('weko_records_ui.utils.current_user') +def test_create_onetime_download_record(mock_user, mock_get, users): + mock_get.return_value = {'expiration_date': 30} + mock_user.id = 1 + activity_id = 1 + record_id = 1 + file_name = 'test.txt' + user_mail = 'test@example.org' + + assert FileOnetimeDownload.query.count() == 0 + url_obj = create_onetime_url_record( + activity_id, record_id, file_name, user_mail) + assert FileOnetimeDownload.query.count() == 1 + assert isinstance(url_obj, FileOnetimeDownload) + assert url_obj.approver_id == 1 + assert url_obj.record_id == str(record_id) + assert url_obj.file_name == file_name + now = (dt.now(timezone.utc) + timedelta(days=31)).replace(tzinfo=None) + tolerance = timedelta(seconds=1) + assert now - url_obj.expiration_date <= tolerance + assert url_obj.download_limit == 10 + assert url_obj.user_mail == user_mail + assert url_obj.is_guest is False + + mock_get.return_value = {} + assert create_onetime_url_record( + activity_id, record_id, file_name, user_mail) is None + mock_get.return_value = 'invalid data' + assert create_onetime_url_record( + activity_id, record_id, file_name, user_mail) is None + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_create_download_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_create_download_url(app): + with patch('weko_records_ui.utils.base64.urlsafe_b64encode') as encoded: + encoded.return_value = b'test' + with app.test_request_context(): + secret_obj = FileSecretDownload( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=dt.now() + timedelta(days=30), + download_limit=10) + url = create_download_url(secret_obj) + assert url == (f'http://TEST_SERVER/record/1/file/secret/test.txt' + f'?token={b"test".decode()}') + with app.test_request_context(): + onetime_obj = FileOnetimeDownload( + approver_id=1, + record_id=1, + file_name='test.txt', + expiration_date=dt.now() + timedelta(days=30), + download_limit=10, + user_mail='test@example.org', + is_guest=False, + extra_info={'activity_id': 1}) + url = create_download_url(onetime_obj) + assert url == (f'http://TEST_SERVER/record/1/file/onetime/test.txt' + f'?token={b"test".decode()}') + with app.test_request_context(): + invalid_obj = MagicMock() + url = create_download_url(invalid_obj) + assert url is None + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_generate_sha256_hash -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_generate_sha256_hash(app): + app.config['WEKO_RECORDS_UI_SECRET_KEY'] = 'secret' + secret_ubj = FileSecretDownload( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=dt.now().date() + timedelta(days=30), + download_limit=10) + secret_url = generate_sha256_hash(secret_ubj) + assert len(secret_url) == 32 + secret_obj2 = FileSecretDownload( + creator_id=2, + record_id=2, + file_name='test2.txt', + label_name='test_url2', + expiration_date=dt.now().date() + timedelta(days=10), + download_limit=5) + secret_url2 = generate_sha256_hash(secret_obj2) + assert len(secret_url2) == 32 + assert secret_url != secret_url2 + same_url = generate_sha256_hash(secret_ubj) + assert secret_url == same_url + + onetime_obj = FileOnetimeDownload( + approver_id=1, + record_id=1, + file_name='test.txt', + expiration_date=dt.now().date() + timedelta(days=30), + download_limit=10, + user_mail='test@example.org', + is_guest=False, + extra_info={'activity_id': 1}) + onetime_url = generate_sha256_hash(onetime_obj) + assert len(onetime_url) == 32 + onetime_obj2 = FileOnetimeDownload( + approver_id=2, + record_id=2, + file_name='test2.txt', + expiration_date=dt.now().date() + timedelta(days=10), + download_limit=5, + user_mail='test2@example.org', + is_guest=True, + extra_info={'activity_id': 2}) + onetime_url2 = generate_sha256_hash(onetime_obj2) + assert len(onetime_url2) == 32 + assert onetime_url != onetime_url2 + same_url = generate_sha256_hash(onetime_obj) + assert onetime_url == same_url + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_send_secret_url_mail -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.UserProfile.get_by_userid') +@patch('weko_records_ui.utils.current_user') +@patch('weko_records_ui.utils.set_mail_info', return_value={}) +@patch('weko_records_ui.utils.process_send_mail', return_value=True) +def test_send_secret_url_mail(mock_send, mock_set_info, mock_user, + mock_profile, app): + app.config['WEKO_RECORDS_UI_MAIL_TEMPLATE_SECRET_URL'] = 'test_template' + mock_profile_obj = MagicMock() + mock_profile_obj._displayname = 'test_user' + mock_profile.return_value = mock_profile_obj + mock_user.id = 1 + mock_user.email = 'test@example.org' + + uuid = 'test_uuid' + url_obj = FileSecretDownload( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=datetime(2125, 1, 1, 0, 0), + download_limit=10) + item_title = 'test_title' + mock_user.id = 1 + expected_info = { + 'restricted_download_link' : create_download_url(url_obj), + 'mail_recipient' : 'test@example.org', + 'file_name' : url_obj.file_name, + 'restricted_expiration_date': '2125-01-01 23:59:59(JST)', + 'restricted_download_count' : str(url_obj.download_limit), + 'restricted_fullname' : 'test_user', + 'restricted_data_name' : item_title, + } + expected_pattern = 'test_template' + with app.test_request_context(): + assert send_secret_url_mail(uuid, url_obj, item_title) is True + mock_send.assert_called_once_with(expected_info, expected_pattern) + mock_send.reset_mock() - # 66 - # onetime_download pattern - assert parse_secret_download_token("MSB1c2VyQGV4YW1wbGUub3JnIDIwMjItMDktMjcgNDBDRkNGODFGM0FFRUI0Ng==") == ('', ('1', 'user@example.org', '2022-09-27', '40CFCF81F3AEEB46')) + mock_profile.return_value = None + with app.test_request_context(): + assert send_secret_url_mail(uuid, url_obj, item_title) is True + expected_info['restricted_fullname'] = '' + mock_send.assert_called_once_with(expected_info, expected_pattern) - # 67 - # secret_download pattern - error , res = parse_secret_download_token("MSA1IDIwMjMtMDMtMDggMDA6NTI6MTkuNjI0NTUyIDZGQTdEMzIxQTk0OTU1MEQ=") - assert error == 'Token is invalid.' - assert res == () + mock_send.return_value = False + with app.test_request_context(): + assert send_secret_url_mail(uuid, url_obj, item_title) is False -# def validate_secret_download_token( -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_secret_download_token -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_validate_secret_download_token(app): +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_token -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_validate_token(app, users): with app.test_request_context(): - secret_download=FileSecretDownload( - file_name= "eee.txt", record_id= '1',user_mail="repoadmin@example.org",expiration_date=999999,download_count=10 - ) - secret_download.created = datetime(2023,3,8,0,52,19,624552) - secret_download.id = 5 - # 68 - res = validate_secret_download_token(secret_download=None , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - - #69 - res = validate_secret_download_token(secret_download=secret_download , file_name= "aaa.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - res = validate_secret_download_token(secret_download=secret_download , file_name= "eee.txt", record_id= '5', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - res = validate_secret_download_token(secret_download=secret_download , file_name= "eee.txt", record_id= '1', id= '1', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - res = validate_secret_download_token(secret_download=secret_download , file_name= "eee.txt", record_id= '1', id= '5', date= '2099-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - res = validate_secret_download_token(secret_download=secret_download , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '7FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - - # 70 - secret_download2=FileSecretDownload( - file_name= "eee.txt", record_id= '5',user_mail="repoadmin@example.org",expiration_date=-1,download_count=10 - ) - secret_download2.created = datetime(2023,3,8,0,52,19,624552) - secret_download2.id = 5 - res = validate_secret_download_token(secret_download=secret_download2 , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("The expiration date for download has been exceeded.")) - - #71 - secret_download2.expiration_date = 99999999 - res = validate_secret_download_token(secret_download=secret_download2 , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (True ,"") - - # 72 - secret_download2.expiration_date = 9999999 - secret_download2.download_count = 0 - res = validate_secret_download_token(secret_download=secret_download2 , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("The download limit has been exceeded.")) - - # 73 - res = validate_secret_download_token(secret_download=secret_download , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (True ,"") - - secret_download2.expiration_date = "hoge" - res = validate_secret_download_token(secret_download=secret_download2 , file_name= "eee.txt", record_id= '1', id= '5', date= '2023-03-08 00:52:19.624552', token= '6FA7D321A949550D') - assert res == (False , _("Token is invalid.")) - -# def get_secret_download(file_name: str, record_id: str, -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_get_secret_download -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_get_secret_download(app ,db ): + secret_obj = FileSecretDownload.create( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=dt.now(timezone.utc) + timedelta(days=30), + download_limit=10) + url = create_download_url(secret_obj) + match = re.search(r'[?&]token=([^&]+)', url) + secret_token = match.group(1) + assert validate_token(secret_token, is_secret_url=True) is True with app.test_request_context(): - with db.session.begin_nested(): - secret_download=FileSecretDownload( - file_name= "eee.txt", record_id= '1',user_mail="repoadmin@example.org",expiration_date=999999,download_count=10 - ) - db.session.add(secret_download) - + onetime_obj = FileOnetimeDownload.create( + approver_id=1, + record_id=1, + file_name='test.txt', + expiration_date=dt.now(timezone.utc) + timedelta(days=30), + download_limit=10, + user_mail='test@example.org', + is_guest=False, + extra_info={'activity_id': 1}) + url = create_download_url(onetime_obj) + match = re.search(r'[?&]token=([^&]+)', url) + onetime_token = match.group(1) + assert validate_token(onetime_token, is_secret_url=False) is True + invalid_bytes = b'\xb2q\xff\x19\xaf\xfc\xc6T\x8bt\xd6\xf6\xc6 \ + \x08D\xe7\xf3G;cN\x1bn|\xa2\x88\x01v\xed\x1cA_1' + with app.test_request_context(): + secret_token = base64.urlsafe_b64decode(secret_token.encode()) + assert secret_token.split(b'_')[-1] == invalid_bytes.split(b'_')[-1] + invalid_token = base64.urlsafe_b64encode(invalid_bytes).decode() + assert validate_token(invalid_token, is_secret_url=True) is False + with app.test_request_context(): + onetime_token = base64.urlsafe_b64decode(onetime_token.encode()) + assert onetime_token.split(b'_')[-1] == invalid_bytes.split(b'_')[-1] + invalid_token = base64.urlsafe_b64encode(invalid_bytes).decode() + assert validate_token(invalid_token, is_secret_url=False) is False + with app.test_request_context(): + assert validate_token('', is_secret_url=True) is False + assert validate_token(123, is_secret_url=True) is False - - assert get_secret_download(file_name= secret_download.file_name - , record_id= secret_download.record_id - , id= secret_download.id - , created =secret_download.created) - - assert not get_secret_download(file_name= secret_download.file_name - , record_id= secret_download.record_id - , id= secret_download.id + 1 - , created =secret_download.created) -# def update_secret_download(**kwargs) -> Optional[List[FileSecretDownload]]: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_get_data_usage_application_data -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_get_data_usage_application_data(app ,db): +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_convert_token_into_obj -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.validate_token') +def test_convert_token_into_obj(vldt_token, app, users): + vldt_token.return_value = True + created_at = dt.now(timezone.utc) with app.test_request_context(): - with db.session.begin_nested(): - secret_download=FileSecretDownload( - file_name= "eee.txt", record_id= '1',user_mail="repoadmin@example.org",expiration_date=999999,download_count=10 - ) - db.session.add(secret_download) - update_data = dict( - file_name = secret_download.file_name - , record_id = secret_download.record_id - , download_count = 100 - , created = secret_download.created - , id = secret_download.id - ) - res = update_secret_download(**update_data) - assert len(res) == 1 - assert res[0].download_count == 100 + secret_obj = FileSecretDownload.create( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=created_at + timedelta(days=30), + download_limit=10) + assert FileSecretDownload.query.count() == 1 + url = create_download_url(secret_obj) + match = re.search(r'[?&]token=([^&]+)', url) + secret_token = match.group(1) + secret_obj = convert_token_into_obj(secret_token, is_secret_url=True) + assert isinstance(secret_obj, FileSecretDownload) + assert secret_obj.id == 1 + assert secret_obj.creator_id == 1 + assert secret_obj.record_id == '1' + assert secret_obj.file_name == 'test.txt' + assert secret_obj.label_name == 'test_url' + expected_date = (created_at + timedelta(days=30)).replace(tzinfo=None) + assert secret_obj.expiration_date == expected_date + assert secret_obj.download_limit == 10 + vldt_token.assert_called_once_with(secret_token, True) + vldt_token.reset_mock() + with app.test_request_context(): + onetime_obj = FileOnetimeDownload.create( + approver_id=1, + record_id=1, + file_name='test.txt', + expiration_date=created_at + timedelta(days=30), + download_limit=10, + user_mail='test@example.org', + is_guest=False, + extra_info={'activity_id': 1}) + assert FileOnetimeDownload.query.count() == 1 + url = create_download_url(onetime_obj) + match = re.search(r'[?&]token=([^&]+)', url) + onetime_token = match.group(1) + onetime_obj = convert_token_into_obj(onetime_token, is_secret_url=False) + assert isinstance(onetime_obj, FileOnetimeDownload) + assert onetime_obj.id == 1 + assert onetime_obj.approver_id == 1 + assert onetime_obj.record_id == '1' + assert onetime_obj.file_name == 'test.txt' + expected_date = (created_at + timedelta(days=30)).replace(tzinfo=None) + assert onetime_obj.expiration_date == expected_date + assert onetime_obj.download_limit == 10 + assert onetime_obj.is_guest == False + assert onetime_obj.extra_info == {'activity_id': 1} + vldt_token.assert_called_once_with(onetime_token, False) + vldt_token.return_value = False + with app.test_request_context(): + assert convert_token_into_obj(secret_token, True) is None + assert convert_token_into_obj(onetime_token, False) is None + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_url_download -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.validate_token') +@patch('weko_records_ui.utils.is_secret_url_feature_enabled') +@patch('weko_records_ui.utils.validate_file_access') +@patch('weko_records_ui.utils.validate_download_record') +def test_validate_url_download(vldt_record, vldt_file, is_enabled, vldt_token, + app, db, users): + with app.test_request_context(): + secret_obj = FileSecretDownload.create( + creator_id=1, + record_id=1, + file_name='test.txt', + label_name='test_url', + expiration_date=dt.now(timezone.utc) + timedelta(days=30), + download_limit=10) + db.session.flush() + match = re.search(r'[?&]token=([^&]+)', create_download_url(secret_obj)) + secret_token = match.group(1) + vldt_token.return_value = True + is_enabled.return_value = True + vldt_file.return_value = True + vldt_record.return_value = True + assert validate_url_download('', '', secret_token, True) == (True, '') + + with app.test_request_context(): + onetime_obj = FileOnetimeDownload.create( + approver_id=1, + record_id=1, + file_name='test.txt', + expiration_date=dt.now(timezone.utc) + timedelta(days=30), + download_limit=10, + user_mail='test@example.org', + is_guest=False, + extra_info={'activity_id': 1}) + db.session.flush() + match = re.search(r'[?&]token=([^&]+)', create_download_url(onetime_obj)) + onetime_token = match.group(1) + assert validate_url_download('', '', onetime_token, False) == (True, '') + + with patch('weko_records_ui.utils.validate_token', + return_value=False): + assert validate_url_download('', '', secret_token, True) == ( + False, 'The provided token is invalid.') + with patch('weko_records_ui.utils.is_secret_url_feature_enabled', + return_value=False): + assert validate_url_download('', '', secret_token, True) == ( + False, 'This feature is currently disabled.') + with patch('weko_records_ui.utils.validate_file_access', + return_value=False): + assert validate_url_download('', '', secret_token, True) == ( + False, 'This file is currently not available for this feature.') + with patch('weko_records_ui.utils.validate_download_record', + return_value=False): + assert validate_url_download('', '', secret_token, True) == ( + False, 'This file is currently not available for this feature.') + + secret_obj.is_deleted = True + db.session.commit() + assert validate_url_download('', '', secret_token, True) == ( + False, 'This URL has been deactivated.') + secret_obj.is_deleted = False + secret_obj.download_count = 10 + db.session.commit() + assert validate_url_download('', '', secret_token, True) == ( + False, 'The download limit has been exceeded.') + secret_obj.download_count = 0 + db.session.commit() + with patch('weko_records_ui.utils.dt') as mock_dt: + mock_dt.now.return_value = dt.now(timezone.utc) + timedelta(days=31) + assert validate_url_download('', '', secret_token, True) == ( + False, 'The expiration date for download has been exceeded.') + + onetime_obj.is_deleted = True + db.session.commit() + assert validate_url_download('', '', onetime_token, False) == ( + False, 'This URL has been deactivated.') + onetime_obj.is_deleted = False + onetime_obj.download_count = 10 + db.session.commit() + assert validate_url_download('', '', onetime_token, False) == ( + False, 'The download limit has been exceeded.') + onetime_obj.download_count = 0 + db.session.commit() + with patch('weko_records_ui.utils.dt') as mock_dt: + mock_dt.now.return_value = dt.now(timezone.utc) + timedelta(days=31) + assert validate_url_download('', '', onetime_token, False) == ( + False, 'The expiration date for download has been exceeded.') + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_validate_file_access -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_validate_file_access(): + with patch('weko_records_ui.utils.is_secret_file', return_value=True): + assert validate_file_access('', '', is_secret_url=True) is True + with patch('weko_records_ui.utils.is_secret_file', return_value=False): + assert validate_file_access('', '', is_secret_url=True) is False + with patch('weko_records_ui.utils.is_onetime_file', return_value=True): + assert validate_file_access('', '', is_secret_url=False) is True + with patch('weko_records_ui.utils.is_onetime_file', return_value=False): + assert validate_file_access('', '', is_secret_url=False) is False + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_save_download_log -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.utils.request') +def test_save_download_log(request, secret_url, onetime_url): + file_name = 'test.txt' + record = MagicMock() + request.remote_addr = '192.168.56.1' + secret_token = secret_url['secret_token'] + + # When accessrole is open_no + record.get_file_data.return_value = [ + {'filename': 'other_file', 'accessrole': 'open_yes'}, + {'filename': file_name, 'accessrole': 'open_no'} + ] + open_no_dl = save_download_log( + record, file_name, secret_token, is_secret_url=True) + assert isinstance(open_no_dl, FileUrlDownloadLog) + assert open_no_dl.url_type is UrlType.SECRET + assert open_no_dl.secret_url_id == 1 + assert open_no_dl.onetime_url_id is None + assert open_no_dl.ip_address == '192.168.56.1' + assert open_no_dl.access_status is AccessStatus.OPEN_NO + assert open_no_dl.used_token == secret_token + + # When accessrole is open_date + record.get_file_data.return_value = [ + {'filename': file_name, 'accessrole': 'open_date'} + ] + open_date_dl = save_download_log( + record, file_name, secret_token, is_secret_url=True) + assert isinstance(open_date_dl, FileUrlDownloadLog) + assert open_date_dl.url_type is UrlType.SECRET + assert open_date_dl.secret_url_id == 1 + assert open_date_dl.onetime_url_id is None + assert open_date_dl.ip_address == '192.168.56.1' + assert open_date_dl.access_status is AccessStatus.OPEN_DATE + assert open_date_dl.used_token == secret_token + + # When accessrole is open_restricted + onetime_token = onetime_url['onetime_token'] + open_restricted = save_download_log( + record, file_name, onetime_token, is_secret_url=False) + assert isinstance(open_restricted, FileUrlDownloadLog) + assert open_restricted.url_type is UrlType.ONETIME + assert open_restricted.secret_url_id is None + assert open_restricted.onetime_url_id == 1 + assert open_restricted.ip_address is None + assert open_restricted.access_status is AccessStatus.OPEN_RESTRICTED + assert open_restricted.used_token == onetime_token # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_utils.py::test_RoCrateConverter_convert -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp @@ -1034,3 +1585,4 @@ def test_create_tsv(app, records): res_tsv = create_tsv(record.files) for field in WEKO_RECORDS_UI_TSV_FIELD_NAMES_DEFAULT: assert field in res_tsv.getvalue() + diff --git a/modules/weko-records-ui/tests/test_views.py b/modules/weko-records-ui/tests/test_views.py index e51f6488a6..3d463e2ba3 100644 --- a/modules/weko-records-ui/tests/test_views.py +++ b/modules/weko-records-ui/tests/test_views.py @@ -1,8 +1,9 @@ +from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock import uuid import pytest import io -from flask import Flask, json, jsonify, session, url_for ,make_response +from flask import Flask, json, jsonify, session, url_for ,make_response, current_app from flask_security.utils import login_user from invenio_accounts.testutils import login_user_via_session from invenio_files_rest.models import ObjectVersion @@ -22,9 +23,10 @@ FlowDefine, WorkFlow, ) -from weko_records_ui.models import PDFCoverPageSettings, FilePermission +from weko_records_ui.models import ( + FileOnetimeDownload, FileSecretDownload, PDFCoverPageSettings, + FilePermission) from weko_records_ui.views import ( - _get_show_secret_url_button, check_permission, citation, escape_newline, @@ -53,7 +55,7 @@ preview_able, get_uri, ) - +from weko_records_ui.utils import create_download_url # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp # def record_from_pid(pid_value): @@ -446,7 +448,7 @@ def test_get_workflow_detail(app,workflows): # .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_default_view_method -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp # """Display default view. # def _get_rights_title(result, rights_key, rights_values, current_lang, meta_options): -def test_default_view_method(app, records, itemtypes, indexstyle ,users): +def test_default_view_method(app, records, itemtypes, indexstyle, users): indexer, results = records record = results[0]["record"] recid = results[0]["recid"] @@ -456,16 +458,41 @@ def test_default_view_method(app, records, itemtypes, indexstyle ,users): with patch("weko_records_ui.views.get_index_link_list", return_value=[]): with patch("weko_records_ui.views.render_template", return_value=make_response()): assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 - # # need to fix - # with pytest.raises(Exception) as e: - # res = default_view_method(recid, record, 'helloworld.pdf') - # assert e.type==TemplatesNotFound - default_view_method(recid, record ) - with pytest.raises(NotFound) : #404 - default_view_method(recid, record ,'notfound.pdf') - with pytest.raises(NotFound) : #404 - default_view_method(recid, record ,'[No FileName]') + #メソッド_get_show_secret_url_buttonからcan_manage_secret_urlに変更に伴うケース追加: show_secret_URLのテスト + with patch('weko_records_ui.views.can_manage_secret_url', return_value=True): + response = default_view_method(recid, record, 'helloworld.pdf') + assert response.status_code == 200 + # show_secret_URLがTrueの場合の動作確認 + with patch('weko_records_ui.views.render_template') as mock_render_template: + default_view_method(recid, record, 'helloworld.pdf') + args, kwargs = mock_render_template.call_args + assert kwargs['show_secret_URL'] is True + + with patch('weko_records_ui.views.can_manage_secret_url', return_value=False): + response = default_view_method(recid, record, 'helloworld.pdf') + assert response.status_code == 200 + # show_secret_URLがFalseの場合の動作確認 + with patch('weko_records_ui.views.render_template') as mock_render_template: + default_view_method(recid, record, 'helloworld.pdf') + args, kwargs = mock_render_template.call_args + assert kwargs['show_secret_URL'] is False + + with patch('weko_records_ui.views.can_manage_secret_url', return_value=None): + response = default_view_method(recid, record, 'helloworld.pdf') + assert response.status_code == 200 + # show_secret_URLがNoneの場合の動作確認 + with patch('weko_records_ui.views.render_template') as mock_render_template: + default_view_method(recid, record, 'helloworld.pdf') + args, kwargs = mock_render_template.call_args + assert kwargs['show_secret_URL'] is None + + # 既存のテストケース + default_view_method(recid, record) + with pytest.raises(NotFound): # 404 + default_view_method(recid, record, 'notfound.pdf') + with pytest.raises(NotFound): # 404 + default_view_method(recid, record, '[No FileName]') def cannnot(): return False @@ -473,38 +500,35 @@ def cannnot(): file_permission_factory.can = cannnot with patch('weko_records_ui.views.file_permission_factory', return_value=file_permission_factory): with patch('weko_records_ui.views._redirect_method', return_value="redirect"): - assert default_view_method(recid, record ,'helloworld.pdf') == "redirect" + assert default_view_method(recid, record, 'helloworld.pdf') == "redirect" with patch("flask_login.utils._get_user", return_value=users[3]["obj"]): - with pytest.raises(Forbidden) : #404 - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - with patch('weko_records_ui.views.AdminSettings.get' - , side_effect=lambda name , dict_to_object : {'display_stats' : False} if name == 'display_stats_settings' else None): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - with patch('weko_records_ui.views.AdminSettings.get' - , side_effect=lambda name , dict_to_object : {'items_search_author' : "author"} if name == 'items_display_settings' else None): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - with patch('weko_search_ui.utils.get_data_by_property', return_value=(False,False)): - with patch('weko_records_ui.views.selected_value_by_language' ,return_value="helloworld.pdf"): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 + with pytest.raises(Forbidden): # 404 + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + with patch('weko_records_ui.views.AdminSettings.get', + side_effect=lambda name, dict_to_object: {'display_stats': False} if name == 'display_stats_settings' else None): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + with patch('weko_records_ui.views.AdminSettings.get', + side_effect=lambda name, dict_to_object: {'items_search_author': "author"} if name == 'items_display_settings' else None): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + with patch('weko_search_ui.utils.get_data_by_property', return_value=(False, False)): + with patch('weko_records_ui.views.selected_value_by_language', return_value="helloworld.pdf"): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 with patch('weko_records_ui.views.get_record_permalink', return_value=False): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - - record.update( - {'system_identifier_doi' : - {"attribute_value_mlt" :[{'subitem_systemidt_identifier':"permalink_uri"}]}}) - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + record.update( + {'system_identifier_doi': + {"attribute_value_mlt": [{'subitem_systemidt_identifier': "permalink_uri"}]}}) + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 def side_effect(arg): values = ['a', 'b'] return values[arg] - # with patch('weko_search_ui.utils.get_sub_item_value', side_effect=side_effect): - # default_view_method(recid, record ,'helloworld.pdf') pid_ver = MagicMock pid_ver.exists = False - with patch('weko_records_ui.views.PIDVersioning',return_value=pid_ver): - with pytest.raises(NotFound) : #404 - assert default_view_method(recid, record ,'helloworld.pdf') + with patch('weko_records_ui.views.PIDVersioning', return_value=pid_ver): + with pytest.raises(NotFound): # 404 + assert default_view_method(recid, record, 'helloworld.pdf') pid_ver = MagicMock pid_ver.exists = True @@ -512,22 +536,257 @@ def side_effect(arg): mock = MagicMock mock.object_uuid = uuid.uuid4() pid_ver.children = [mock] - pid_ver.get_children = lambda ordered,pid_status : [mock] - with patch('weko_records_ui.views.PIDVersioning',return_value=pid_ver): - with patch('weko_records_ui.views.WekoRecord.get_record',return_value={'_deposit':{'status':'draft'}}): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - - with patch('weko_records_ui.views.WekoRecord.get_record',side_effect=Exception): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - with patch('weko_records_ui.views.ItemLink.get_item_link_info',return_value={"relation":"res"}): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - + pid_ver.get_children = lambda ordered, pid_status: [mock] + with patch('weko_records_ui.views.PIDVersioning', return_value=pid_ver): + with patch('weko_records_ui.views.WekoRecord.get_record', return_value={'_deposit': {'status': 'draft'}}): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + + with patch('weko_records_ui.views.WekoRecord.get_record', side_effect=Exception): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + with patch('weko_records_ui.views.ItemLink.get_item_link_info', return_value={"relation": "res"}): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + index = MagicMock() index.index_name = "" - index.index_name_english ="index" - with patch('weko_records_ui.views.Indexes.get_index',return_value=index): - assert default_view_method(recid, record ,'helloworld.pdf').status_code == 200 - + index.index_name_english = "index" + with patch('weko_records_ui.views.Indexes.get_index', return_value=index): + assert default_view_method(recid, record, 'helloworld.pdf').status_code == 200 + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_create_secret_url_and_send_mail -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +@patch('weko_records_ui.views.validate_secret_url_generation_request') +@patch('weko_records_ui.views.can_manage_secret_url') +@patch('weko_records_ui.utils.current_user') +@patch('weko_records_ui.views.send_secret_url_mail') +def test_create_secret_url_and_send_mail(send_mail, login_user, can_manage, + vldt_req, client, users, records): + vldt_req.return_value = True + can_manage.return_value = True + login_user.id = 1 + tmp, results = records + url = url_for('invenio_records_ui.recid_secret_url', + pid_value=results[1]["recid"].pid_value, + filename=results[1]["filename"]) + base_data = { + 'link_name': '', + 'expiration_date': '', + 'download_limit': None, + 'send_email': False, + 'timezone_offset_minutes': 0 + } + + # Success + res = client.post(url, data=json.dumps(base_data), + content_type='application/json') + assert res.status_code == 200 + assert 'Secret URL generated successfully' in res.get_data(as_text=True) + send_mail.return_value = True + data = {**base_data, 'send_email': True} + res = client.post(url, data=json.dumps(data), + content_type='application/json') + assert res.status_code == 200 + assert 'Secret URL generated successfully' in res.get_data(as_text=True) + assert 'please check your email inbox' in res.get_data(as_text=True) + send_mail.return_value = False + res = client.post(url, data=json.dumps(data), + content_type='application/json') + assert res.status_code == 200 + assert 'Secret URL generated successfully' in res.get_data(as_text=True) + assert 'there was an error' in res.get_data(as_text=True) + + # Fail + with patch('weko_records_ui.views.create_secret_url_record', + side_effect=Exception('Test DB Error')): + with pytest.raises(Exception): + res = client.post(url, data=json.dumps(data), + content_type='application/json') + assert res.status_code == 500 + can_manage.return_value = False + with pytest.raises(Exception): + res = client.post(url, data=json.dumps(data), + content_type='application/json') + assert res.status_code == 403 + vldt_req.return_value = False + res = client.post(url, data=json.dumps(data), + content_type='application/json') + assert res.status_code == 400 + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_copy_secret_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_copy_secret_url(client, records): + _, records = records + url = url_for('invenio_records_ui.recid_copy_secret_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + secret_url_id=1) + secret_obj = FileSecretDownload.create( + creator_id=1, + record_id=records[1]['recid'].pid_value, + file_name=records[1]['filename'], + label_name='test link', + expiration_date=datetime.now(timezone.utc) + timedelta(days=1), + download_limit=1, + ) + expected_secret_url = create_download_url(secret_obj) + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=True): + res = client.get(url) + assert res.status_code == 200 + assert ('The secret URL copied to your clipboard.' + in res.get_data(as_text=True)) + assert res.json['url'] == expected_secret_url + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=False): + with pytest.raises(Exception): + res = client.get(url) + assert res.status_code == 403 + with patch('weko_records_ui.views.create_download_url', + side_effect=Exception('Test Error')): + with pytest.raises(Exception): + res = client.get(url) + assert res.status_code == 500 + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=True): + url = url_for('invenio_records_ui.recid_copy_secret_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + secret_url_id=99) # invalid secret_url_id + res = client.get(url) + assert res.json['url'] is None + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_copy_onetime_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_copy_onetime_url(client, records): + _, records = records + url = url_for('invenio_records_ui.recid_copy_onetime_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + onetime_url_id=1) + onetime_obj = FileOnetimeDownload.create( + approver_id=1, + record_id=records[1]['recid'].pid_value, + file_name=records[1]['filename'], + expiration_date=datetime.now(timezone.utc) + timedelta(days=1), + download_limit=1, + user_mail='test@example.org', + is_guest=False, + extra_info={} + ) + expected_onetime_url = create_download_url(onetime_obj) + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=True): + res = client.get(url) + assert res.status_code == 200 + assert ('The onetime URL copied to your clipboard.' + in res.get_data(as_text=True)) + assert res.json['url'] == expected_onetime_url + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=False): + with pytest.raises(Exception): + res = client.get(url) + assert res.status_code == 403 + with patch('weko_records_ui.views.create_download_url', + side_effect=Exception('Test Error')): + with pytest.raises(Exception): + res = client.get(url) + assert res.status_code == 500 + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=True): + url = url_for('invenio_records_ui.recid_copy_onetime_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + onetime_url_id=99) # invalid onetime_url_id + res = client.get(url) + assert res.json['url'] is None + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_delete_secret_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_delete_secret_url(client, records): + _, records = records + url = url_for('invenio_records_ui.recid_delete_secret_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + secret_url_id=1) + secret_obj = FileSecretDownload.create( + creator_id=1, + record_id=records[1]['recid'].pid_value, + file_name=records[1]['filename'], + label_name='test link', + expiration_date=datetime.now(timezone.utc) + timedelta(days=1), + download_limit=1, + ) + assert secret_obj.is_deleted == False + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=True): + res = client.delete(url) + assert res.status_code == 200 + assert ('The secret URL has been successfully deleted.' + in res.get_data(as_text=True)) + assert secret_obj.is_deleted == True + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=False): + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 403 + with patch('weko_records_ui.models.FileSecretDownload.delete_logically', + side_effect=Exception('Test Error')): + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 500 + with patch('weko_records_ui.views.can_manage_secret_url', + return_value=True): + url = url_for('invenio_records_ui.recid_delete_secret_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + secret_url_id=99) # invalid secret_url_id + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 404 + + +# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_delete_onetime_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp +def test_delete_onetime_url(client, records): + _, records = records + url = url_for('invenio_records_ui.recid_delete_onetime_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + onetime_url_id=1) + onetime_obj = FileOnetimeDownload.create( + approver_id=1, + record_id=records[1]['recid'].pid_value, + file_name=records[1]['filename'], + expiration_date=datetime.now(timezone.utc) + timedelta(days=1), + download_limit=1, + user_mail='test@example.org', + is_guest=False, + extra_info={} + ) + assert onetime_obj.is_deleted == False + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=True): + res = client.delete(url) + assert res.status_code == 200 + assert ('The one-time URL has been successfully deleted.' + in res.get_data(as_text=True)) + assert onetime_obj.is_deleted == True + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=False): + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 403 + with patch('weko_records_ui.models.FileOnetimeDownload.delete_logically', + side_effect=Exception('Test Error')): + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 500 + with patch('weko_records_ui.views.can_manage_onetime_url', + return_value=True): + url = url_for('invenio_records_ui.recid_delete_onetime_url', + pid_value=records[1]['recid'].pid_value, + filename=records[1]['filename'], + onetime_url_id=99) + with pytest.raises(Exception): + res = client.delete(url) + assert res.status_code == 404 # def doi_ish_view_method(parent_pid_value=0, version=0): @@ -968,135 +1227,3 @@ def test_default_view_method_fix35133(app, records, itemtypes, indexstyle,mocker {'name': 'citation_abstract_html_url','data': 'http://TEST_SERVER/records/1'}, ] assert kwargs["google_dataset_meta"] == '{"@context": "https://schema.org/", "@type": "Dataset", "citation": ["http://hdl.handle.net/2261/0002005680", "https://repository.dl.itc.u-tokyo.ac.jp/records/2005680"], "creator": [{"@type": "Person", "alternateName": "creator alternative name", "familyName": "creator family name", "givenName": "creator given name", "identifier": "123", "name": "creator name"}], "description": "『史料編纂掛備用寫眞畫像圖畫類目録』(1905年)の「画像」(肖像画模本)の部に著録する資料の架番号の新旧対照表。史料編纂所所蔵肖像画模本データベースおよび『目録』版面画像へのリンク付き。『画像史料解析センター通信』98(2022年10月)に解説記事あり。", "distribution": [{"@type": "DataDownload", "contentUrl": "https://repository.dl.itc.u-tokyo.ac.jp/record/2005680/files/comparison_table_of_preparation_image_catalog.xlsx", "encodingFormat": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, {"@type": "DataDownload", "contentUrl": "https://raw.githubusercontent.com/RCOSDP/JDCat-base/main/apt.txt", "encodingFormat": "text/plain"}, {"@type": "DataDownload", "contentUrl": "https://raw.githubusercontent.com/RCOSDP/JDCat-base/main/environment.yml", "encodingFormat": "application/x-yaml"}, {"@type": "DataDownload", "contentUrl": "https://raw.githubusercontent.com/RCOSDP/JDCat-base/main/postBuild", "encodingFormat": "text/x-shellscript"}], "includedInDataCatalog": {"@type": "DataCatalog", "name": "https://localhost"}, "license": ["CC BY"], "name": "『史料編纂掛備用写真画像図画類目録』画像の部:新旧架番号対照表", "spatialCoverage": [{"@type": "Place", "geo": {"@type": "GeoCoordinates", "latitude": "point latitude test", "longitude": "point longitude test"}}, {"@type": "Place", "geo": {"@type": "GeoShape", "box": "1 3 2 4"}}, "geo location place test"]}' -# def create_secret_url_and_send_mail(pid:PersistentIdentifier, record:WekoRecord, filename:str, **kwargs) -> str: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test_create_secret_url_and_send_mail -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -def test_create_secret_url_and_send_mail(app,client,db,users,records): - app.config['WEKO_WORKFLOW_DATE_FORMAT'] = "%Y-%m-%d" - indexer, results = records - record = results[1] - - # 79 - id = 1 #repoadmin - secret_file_url = url_for("invenio_records_ui.recid_secret_url" - ,pid_value=results[1]["recid"].pid_value - ,filename=results[1]["filename"]) - login_user_via_session(client=client, user=users[id]["obj"] ,email=users[id]["email"]) - with patch('weko_records_ui.views._get_show_secret_url_button',return_value = True): - with patch('weko_records_ui.views.process_send_mail',return_value = True): - # with app.test_request_context(): - res = client.get(secret_file_url) - assert res.status_code == 405 - - res = client.post(secret_file_url ,data=json.dumps({}), content_type='application/json') - assert res.status_code == 200 - with patch('weko_records_ui.views.process_send_mail',return_value = False): - with patch("flask.templating._render", return_value=""): - res = client.post(secret_file_url ,data=json.dumps({}), content_type='application/json') - assert res.status_code == 500 - with patch('weko_records_ui.views._get_show_secret_url_button',return_value = False): - with patch('weko_records_ui.views.process_send_mail',return_value = True): - with patch("flask.templating._render", return_value=""): - res = client.post(secret_file_url ,data=json.dumps({}), content_type='application/json') - assert res.status_code == 403 - -# def create_secret_url_and_send_mail(pid:PersistentIdentifier, record:WekoRecord, filename:str, **kwargs) -> str: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test__get_show_secret_url_button -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -@pytest.mark.parametrize( - "id, is_show", - [ - (0, False), #contributor - (1, False), #repoadmin - (2, False), #sysadmin - (3, False), #comadmin - (4, False), #generaluser - (5, False), #originalroleuser (owner) - (6, False), #originalroleuser2 (repoadmin) - (7, False), #user (weko_shared owner) - ], -) -def test__get_show_secret_url_button(users,records,id ,is_show): - indexer, results = records - # 80 - i = 0 - role = ["open_access" , "open_no" ,"open_date"] - for record in results: - record["record"]['owner'] = users[5]["id"] - record["record"]['weko_shared_id'] = users[7]["id"] - file_data = record["record"].get_file_data() - if len(file_data) > 0: - file_data[0].update({'accessrole':role[i%3]}) - file_data[0].update({'date':[{"dateValue" :'2999-12-31'}]}) - i = i + 1 - - with patch("flask_login.utils._get_user", return_value=users[id]["obj"]): - res = [] - for record in results: - if 'filename' in record: - res.append( _get_show_secret_url_button(record["record"] , record["filename"]) ) - - assert not res[0] - assert res[1] == is_show - assert res[2] == is_show - -# def create_secret_url_and_send_mail(pid:PersistentIdentifier, record:WekoRecord, filename:str, **kwargs) -> str: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test__get_show_secret_url_button2 -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -@pytest.mark.parametrize( - "id, is_show", - [ - (1, True), #repoadmin - ], -) -def test__get_show_secret_url_button2(users,records ,id,is_show): - indexer, results = records - # 80 - # pattern of not db_restricted_access_secret - i = 0 - role = ["open_access" , "open_no" ,"open_date"] - for record in results: - record["record"]['owner'] = users[5]["id"] - record["record"]['weko_shared_id'] = users[7]["id"] - file_data = record["record"].get_file_data() - if len(file_data) > 0: - file_data[0].update({'accessrole':role[i%3]}) - file_data[0].update({'date':[{"dateValue" :'2999-12-31'}]}) - i = i + 1 - with patch("flask_login.utils._get_user", return_value=users[id]["obj"]): - res = [] - for record in results: - if 'filename' in record: - res.append( _get_show_secret_url_button(record["record"] , record["filename"]) ) - - assert res[0] == False - assert res[1] == False - assert res[2] == False - -# def create_secret_url_and_send_mail(pid:PersistentIdentifier, record:WekoRecord, filename:str, **kwargs) -> str: -# .tox/c1/bin/pytest --cov=weko_records_ui tests/test_views.py::test__get_show_secret_url_button3 -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-records-ui/.tox/c1/tmp -@pytest.mark.parametrize( - "id, is_show", - [ - (1, False), #repoadmin - ], -) -def test__get_show_secret_url_button3(users,records,id,is_show): - indexer, results = records - # 80 - i = 0 - role = ["open_access" , "open_no" ,"open_date"] - for record in results: - record["record"]['owner'] = users[5]["id"] - record["record"]['weko_shared_id'] = users[7]["id"] - file_data = record["record"].get_file_data() - if len(file_data) > 0: - file_data[0].update({'accessrole':role[i%3]}) - file_data[0].update({'date':[{"dateValue" :'2999-12-31'}]}) - i = i + 1 - with patch("flask_login.utils._get_user", return_value=users[id]["obj"]): - res = [] - for record in results: - if 'filename' in record: - res.append( _get_show_secret_url_button(record["record"] , record["filename"]) ) - - assert res[0] == False - assert res[1] == is_show - assert res[2] == False diff --git a/modules/weko-records-ui/weko_records_ui/alembic/2750aa1ddc76_create_weko_records_ui_branch.py b/modules/weko-records-ui/weko_records_ui/alembic/2750aa1ddc76_create_weko_records_ui_branch.py new file mode 100644 index 0000000000..2622bdff59 --- /dev/null +++ b/modules/weko-records-ui/weko_records_ui/alembic/2750aa1ddc76_create_weko_records_ui_branch.py @@ -0,0 +1,28 @@ +# +# This file is part of Invenio. +# Copyright (C) 2016-2018 CERN. +# +# Invenio is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +"""Create weko-records-ui branch.""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2750aa1ddc76' +down_revision = None +branch_labels = ('weko_records_ui',) +depends_on = 'invenio_accounts' + + +def upgrade(): + """Upgrade database.""" + pass + + +def downgrade(): + """Downgrade database.""" + pass diff --git a/modules/weko-records-ui/weko_records_ui/alembic/e0b1ef08d08c_create_file_url_download_log_table.py b/modules/weko-records-ui/weko_records_ui/alembic/e0b1ef08d08c_create_file_url_download_log_table.py new file mode 100644 index 0000000000..5fc43128b4 --- /dev/null +++ b/modules/weko-records-ui/weko_records_ui/alembic/e0b1ef08d08c_create_file_url_download_log_table.py @@ -0,0 +1,138 @@ +# +# This file is part of Invenio. +# Copyright (C) 2016-2018 CERN. +# +# Invenio is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +"""Create file_url_download_log table.""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import INET +from sqlalchemy.orm import Session + + +# revision identifiers, used by Alembic. +revision = 'e0b1ef08d08c' +down_revision = '2750aa1ddc76' +branch_labels = () +depends_on = 'invenio_accounts' + + +def upgrade(): + """Upgrade database.""" + + # Recreate 'file_onetime_download' table + op.drop_table('file_onetime_download') + op.create_table( + 'file_onetime_download', + sa.Column('created', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('approver_id', sa.Integer(), nullable=False), + sa.Column('record_id', sa.String(255), nullable=False), + sa.Column('file_name', sa.String(255), nullable=False), + sa.Column('expiration_date', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('download_limit', sa.Integer(), nullable=False), + sa.Column('download_count', sa.Integer(), nullable=False, server_default=sa.text('0')), + sa.Column('user_mail', sa.String(255), nullable=False), + sa.Column('is_guest', sa.Boolean(), nullable=False), + sa.Column('is_deleted', sa.Boolean(), nullable=False, server_default=sa.text('FALSE')), + sa.Column('extra_info', sa.JSON(), nullable=False, server_default=sa.text("'{}'")), + sa.ForeignKeyConstraint(['approver_id'], ['accounts_user.id'], name='fk_file_onetime_download_approver_id'), + sa.CheckConstraint('created < expiration_date', name='check_expiration_date'), + sa.CheckConstraint('download_limit > 0', name='check_download_limit_positive'), + sa.CheckConstraint('download_count <= download_limit', name='check_download_count_limit') + ) + + # Recreate 'file_secret_download' table + op.drop_table('file_secret_download') + op.create_table( + 'file_secret_download', + sa.Column('created', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('creator_id', sa.Integer(), nullable=False), + sa.Column('record_id', sa.String(255), nullable=False), + sa.Column('file_name', sa.String(255), nullable=False), + sa.Column('label_name', sa.String(255), nullable=False), + sa.Column('expiration_date', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('download_limit', sa.Integer(), nullable=False), + sa.Column('download_count', sa.Integer(), nullable=False, server_default=sa.text('0')), + sa.Column('is_deleted', sa.Boolean(), nullable=False, server_default=sa.text('FALSE')), + sa.ForeignKeyConstraint(['creator_id'], ['accounts_user.id'], name='fk_file_secret_download_creator_id'), + sa.CheckConstraint('created < expiration_date', name='check_expiration_date'), + sa.CheckConstraint('download_limit > 0', name='check_download_limit_positive'), + sa.CheckConstraint('download_count <= download_limit', name='check_download_count_limit') + ) + + # Add 'file_url_download_log' table + op.create_table( + 'file_url_download_log', + sa.Column('created', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('url_type', sa.Enum('SECRET', 'ONETIME', name='urltype'), nullable=False), + sa.Column('secret_url_id', sa.Integer(), sa.ForeignKey('file_secret_download.id'), nullable=True), + sa.Column('onetime_url_id', sa.Integer(), sa.ForeignKey('file_onetime_download.id'), nullable=True), + sa.Column('ip_address', INET().with_variant(sa.String(255), 'sqlite').with_variant(sa.String(255), 'mysql'), nullable=True), + sa.Column('access_status', sa.Enum('OPEN_NO', 'OPEN_DATE', 'OPEN_RESTRICTED', name='accessstatus'), nullable=False), + sa.Column('used_token', sa.String(255), nullable=False), + sa.CheckConstraint( + """ + (url_type = 'SECRET' AND secret_url_id IS NOT NULL AND onetime_url_id IS NULL) + OR + (url_type = 'ONETIME' AND onetime_url_id IS NOT NULL AND secret_url_id IS NULL) + """, + name="chk_url_id" + ), + sa.CheckConstraint( + """ + (url_type = 'SECRET' AND ip_address IS NOT NULL) + OR + (url_type = 'ONETIME' AND ip_address IS NULL) + """, + name="chk_ip_address" + ), + sa.CheckConstraint( + """ + (url_type = 'SECRET' AND (access_status = 'OPEN_NO' OR access_status = 'OPEN_DATE')) + OR + (url_type = 'ONETIME' AND access_status = 'OPEN_RESTRICTED') + """, + name="chk_access_status" + ) + ) + +def downgrade(): + """Downgrade database.""" + + op.drop_table('file_url_download_log') + op.execute("DROP TYPE IF EXISTS urltype;") + op.execute("DROP TYPE IF EXISTS accessstatus;") + op.drop_table('file_onetime_download') + op.create_table( + 'file_onetime_download', + sa.Column('created', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('file_name', sa.String(255), nullable=False), + sa.Column('user_mail', sa.String(255), nullable=False), + sa.Column('record_id', sa.String(255), nullable=False), + sa.Column('download_count', sa.Integer(), nullable=False, server_default=sa.text('0')), + sa.Column('expiration_date', sa.Integer(), nullable=False, server_default=sa.text('0')), + sa.Column('extra_info', sa.JSON(), nullable=True, server_default=sa.text("'{}'")), + ) + op.drop_table('file_secret_download') + op.create_table( + 'file_secret_download', + sa.Column('created', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=False), nullable=False), + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('file_name', sa.String(255), nullable=False), + sa.Column('user_mail', sa.String(255), nullable=False), + sa.Column('record_id', sa.String(255), nullable=False), + sa.Column('download_count', sa.Integer(), nullable=False, server_default=sa.text('0')), + sa.Column('expiration_date', sa.Integer(), nullable=False, server_default=sa.text('0')), + ) diff --git a/modules/weko-records-ui/weko_records_ui/config.py b/modules/weko-records-ui/weko_records_ui/config.py index 3f0e997164..369892fb19 100644 --- a/modules/weko-records-ui/weko_records_ui/config.py +++ b/modules/weko-records-ui/weko_records_ui/config.py @@ -178,6 +178,42 @@ ':page_permission_factory', methods=['POST'], ), + recid_copy_secret_url=dict( + pid_type='recid', + route='/records//secret//', + view_imp='weko_records_ui.views.copy_secret_url', + record_class='weko_deposit.api:WekoRecord', + permission_factory_imp='weko_records_ui.permissions' + ':page_permission_factory', + methods=['GET'], + ), + recid_copy_onetime_url=dict( + pid_type='recid', + route='/records//onetime//', + view_imp='weko_records_ui.views.copy_onetime_url', + record_class='weko_deposit.api:WekoRecord', + permission_factory_imp='weko_records_ui.permissions' + ':page_permission_factory', + methods=['GET'], + ), + recid_delete_secret_url=dict( + pid_type='recid', + route='/records//secret//', + view_imp='weko_records_ui.views.delete_secret_url', + record_class='weko_deposit.api:WekoRecord', + permission_factory_imp='weko_records_ui.permissions' + ':page_permission_factory', + methods=['DELETE'], + ), + recid_delete_onetime_url=dict( + pid_type='recid', + route='/records//onetime//', + view_imp='weko_records_ui.views.delete_onetime_url', + record_class='weko_deposit.api:WekoRecord', + permission_factory_imp='weko_records_ui.permissions' + ':page_permission_factory', + methods=['DELETE'], + ), recid_secret_file_download=dict( pid_type='recid', route='/record//file/secret/', @@ -193,10 +229,6 @@ "filename={} record_id={} user_mail={} date={}" """Onetime download pattern.""" -WEKO_RECORDS_UI_SECRET_DOWNLOAD_PATTERN = \ - "filename={} record_id={} id={} date={}" -"""Secret URL download pattern.""" - WEKO_RECORDS_UI_MAIL_TEMPLATE_SECRET_URL = "email_pattern_send_secret_url.tpl" RECORDS_UI_EXPORT_FORMATS = { diff --git a/modules/weko-records-ui/weko_records_ui/fd.py b/modules/weko-records-ui/weko_records_ui/fd.py index 5a66280464..2f59e403b7 100644 --- a/modules/weko-records-ui/weko_records_ui/fd.py +++ b/modules/weko-records-ui/weko_records_ui/fd.py @@ -20,11 +20,12 @@ """Utilities for download file.""" +import base64 import json import mimetypes import traceback import unicodedata -from datetime import datetime +from datetime import datetime, timezone from flask import abort, current_app, render_template, request ,redirect ,url_for from flask_babelex import gettext as _ @@ -40,21 +41,21 @@ from weko_deposit.api import WekoRecord from weko_groups.api import Group from weko_records.api import FilesMetadata, ItemTypes -from weko_records_ui.utils import generate_one_time_download_url from weko_user_profiles.models import UserProfile from weko_workflow.utils import is_terms_of_use_only from werkzeug.datastructures import Headers from werkzeug.urls import url_quote -from .models import FileOnetimeDownload, FileSecretDownload, PDFCoverPageSettings +from .models import FileOnetimeDownload, FileSecretDownload, \ + PDFCoverPageSettings from .pdf import make_combined_pdf from .permissions import check_original_pdf_download_permission, \ file_permission_factory, is_owners_or_superusers -from .utils import check_and_send_usage_report, get_billing_file_download_permission, \ +from .utils import check_and_send_usage_report, convert_token_into_obj, \ + create_download_url, get_billing_file_download_permission, \ get_groups_price, get_min_price_billing_file_download, \ - get_onetime_download, get_secret_download, is_billing_item, parse_one_time_download_token, parse_secret_download_token, \ - update_onetime_download, update_secret_download, validate_download_record, \ - validate_onetime_download_token, validate_secret_download_token + get_onetime_download, is_billing_item, save_download_log, \ + validate_url_download def weko_view_method(pid, record, template=None, **kwargs): @@ -248,8 +249,7 @@ def file_ui( current_app.logger.info('onetime_download is None') abort(403) - onetime_file_url = generate_one_time_download_url( - file_name, record_id, user_mail ) + onetime_file_url = create_download_url(onetime_download) return redirect(onetime_file_url) #redirect to file_download_onetime() # #Check permissions @@ -408,80 +408,68 @@ def add_signals_info(record, obj): obj.item_id = record['_deposit']['id'] -def file_download_onetime(pid, record, _record_file_factory=None, **kwargs): - """File download onetime. +def error_response(error_message, status_code=400): + error_template = "weko_theme/error.html" + return render_template(error_template, error=error_message), status_code - :param pid: - :param record: Record json - :param _record_file_factory: - :param kwargs: - :return: + +def file_download_onetime(pid, record, filename, _record_file_factory=None, + **kwargs): + """Download a file using a one-time download URL. + + Args: + pid (PersistentIdentifier): The identifier for the item. + record (WekoRecord): The record metadata of the item. + filename (str): The name of the file to download. + + Returns: + Response: The Flask wrapper object for the file download """ + # Validate the download request token = request.args.get('token', type=str) - filename = kwargs.get("filename") - error_template = "weko_theme/error.html" - # Parse token - error, token_data = \ - parse_one_time_download_token(token) - if error: - return render_template(error_template, error=error) - record_id, user_mail, date, secret_token = token_data - - # Validate record status - validate_download_record(record) - - # Get one time download record. - onetime_download = get_onetime_download( - file_name=filename, record_id=record_id, user_mail=user_mail - ) - - # Validate token - is_valid, error = validate_onetime_download_token( - onetime_download, filename, record_id, user_mail, date, secret_token) - if not is_valid: - return render_template(error_template, error=error) + is_validated, error_msg = validate_url_download( + record, filename, token, is_secret_url=False) + if not is_validated: + return error_response(error_msg, 403) + # Locate the file object _record_file_factory = _record_file_factory or record_file_factory - - # Get file object file_object = _record_file_factory(pid, record, filename) if not file_object or not file_object.obj: - return render_template(error_template, - error="{} does not exist.".format(filename)) - - # Create updated data - update_data = dict( - file_name=filename, record_id=record_id, user_mail=user_mail, - download_count=onetime_download.download_count - 1, - ) + return error_response(_('The file "%s" does not exist.') % filename, 404) - # Check and send usage report for Guest User. - if onetime_download.extra_info and 'open_restricted' == file_object.get( - 'accessrole'): - extra_info = onetime_download.extra_info + # Update 'extra_info' of the one-time URL object + url_obj = convert_token_into_obj(token, is_secret_url=False) + extra_info = url_obj.extra_info + if extra_info: try: - error_msg = check_and_send_usage_report(extra_info, user_mail ,record, file_object) - if error_msg: - return render_template(error_template, error=error_msg) + # 'extra_info' can be changed by this method + error = check_and_send_usage_report( + extra_info, url_obj.user_mail ,record, file_object) + if error: + return error_response(error, 403) + url_obj.update_extra_info(extra_info) db.session.commit() except SQLAlchemyError as ex: - current_app.logger.error("sqlalchemy error: {}".format(ex)) + current_app.logger.error(f'SQLAlchemy error: {ex}') db.session.rollback() - return render_template(error_template, error=_("Unexpected error occurred.")) + return error_response('Unexpected error occurred.', 500) except BaseException as ex: - current_app.logger.error("Unexpected error: {}".format(ex)) + current_app.logger.error(f'Unexpected error: {ex}') db.session.rollback() - return render_template(error_template, error=_("Unexpected error occurred.")) + return error_response('Unexpected error occurred.', 500) - update_data['extra_info'] = extra_info + # Increase the download count and save the download log + try: + save_download_log(record, filename, token, is_secret_url=False) + url_obj.increment_download_count() + except Exception as e: + current_app.logger.error(e) + return error_response(_('Unexpected error occurred.'), 500) - # Update download data - if not update_onetime_download(**update_data): - return render_template(error_template, - error=_("Unexpected error occurred.")) + return _download_file( + file_object, False, 'en', file_object.obj, pid, record) - return _download_file(file_object, False, 'en', file_object.obj, pid, - record) def _is_terms_of_use_only(file_obj:dict , req :dict) -> bool: """ @@ -516,70 +504,46 @@ def _is_terms_of_use_only(file_obj:dict , req :dict) -> bool: return is_terms_of_use_only(workflow_id) if workflow_id != "" else False -def file_download_secret(pid, record, _record_file_factory=None, **kwargs): - """File download secret. - :param pid: - :param record: Record json - :param _record_file_factory: - :param kwargs: - :return: - """ - token = request.args.get('token', type=str) - filename:str = str(kwargs.get("filename")) - error_template = "weko_theme/error.html" - # Parse token - error, token_data = \ - parse_secret_download_token(token) - if error: - return render_template(error_template, error=error) - record_id, id, date, secret_token = token_data - - # Validate record status - validate_download_record(record) - - if isinstance(date,str): - date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%f") - - # Get secret download record. - secret_download :FileSecretDownload = get_secret_download( - file_name=filename, record_id=pid.pid_value, id=id , created=date - ) +def file_download_secret(pid, record, filename, _record_file_factory=None, + **kwargs): + """Download a file using a secret URL. - if not secret_download: - abort(403) + Args: + pid (PersistentIdentifier): The identifier for the item. + record (WekoRecord): The record metadata of the item. + filename (str): The name of the file to download. - # Validate token - is_valid, error = validate_secret_download_token( - secret_download, filename, pid.pid_value, id, date.isoformat(), secret_token) - current_app.logger.debug("is_valid: {}, error: {}".format(is_valid,error)) - - if not is_valid: - return render_template(error_template, error=error) + Returns: + Response: The Flask wrapper object for the file download. + """ + # Validate the download request + token = request.args.get('token', type=str) + is_validated, error_msg = ( + validate_url_download(record, filename, token, is_secret_url=True)) + if not is_validated: + return error_response(error_msg, 403) + # Locate the file object _record_file_factory = _record_file_factory or record_file_factory - - # Get file object file_object = _record_file_factory(pid, record, filename) if not file_object or not file_object.obj: - return render_template(error_template, - error="{} does not exist.".format(filename)) - - # Create updated data - update_data = dict( - file_name=filename, record_id=record_id, id=id, - download_count=secret_download.download_count - 1,created=str(date) - ) + return error_response(_('The file "%s" does not exist.') % filename, 404) - # Update download data - if not update_secret_download(**update_data): - return render_template(error_template, - error=_("Unexpected error occurred.")) - - # Get user's language and defautl language for PDF coverpage. + # Set language for PDF cover page lang = 'en' if current_user.is_authenticated : user_profile = UserProfile.get_by_userid(current_user.get_id()) - lang = user_profile.language if user_profile and user_profile.language \ - else 'en' - return _download_file(file_object, False, lang, file_object.obj, pid, record) \ No newline at end of file + lang = user_profile.language if user_profile else 'en' + + # Increase the download count and save the download log + url_obj = convert_token_into_obj(token, is_secret_url=True) + try: + save_download_log(record, filename, token, is_secret_url=True) + url_obj.increment_download_count() + except Exception as e: + current_app.logger.error(e) + return error_response(_('Unexpected error occurred.'), 500) + + return _download_file( + file_object, False, lang, file_object.obj, pid, record) diff --git a/modules/weko-records-ui/weko_records_ui/models.py b/modules/weko-records-ui/weko_records_ui/models.py index 2343c5712c..ce9da6adbb 100644 --- a/modules/weko-records-ui/weko_records_ui/models.py +++ b/modules/weko-records-ui/weko_records_ui/models.py @@ -21,16 +21,15 @@ """Database models for weko-admin.""" -from datetime import datetime -from datetime import timedelta -import traceback +from datetime import datetime, timezone +import enum from typing import List from flask import current_app from invenio_db import db -from sqlalchemy import desc, or_ ,func +from sqlalchemy import CheckConstraint, desc, func, asc from sqlalchemy.dialects import postgresql -from sqlalchemy.dialects.postgresql import INTERVAL +from sqlalchemy.dialects.postgresql import INET from sqlalchemy.sql.functions import concat ,now from sqlalchemy_utils.models import Timestamp from sqlalchemy_utils.types import JSONType @@ -289,66 +288,181 @@ def delete_object(cls, permission): db.session.delete(permission) -class FileOnetimeDownload(db.Model, Timestamp): - """File onetime download.""" +class DownloadMixin: + """A mixin class that provides common methods for managing download-related + functionality. - __tablename__ = 'file_onetime_download' + This mixin class is specifically designed for managing URL-related + downloads, particularly one-time URLs and secret URLs. - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - """Identifier""" + Note: + To use this mixin, the model class must have the following attributes: + - download_limit (int): The maximum number of downloads allowed. + - download_count (int): The number the URL has been downloaded. + - is_deleted (bool): Indicates whether the record is deleted. + """ - file_name = db.Column(db.String(255), nullable=False) - """File name""" + def increment_download_count(self): + """Increment the 'download_count' attribute by 1 and commit the change. - user_mail = db.Column(db.String(255), nullable=False) - """User mail""" + This method increases the download count for the instance by one + and persists the change to the database. - record_id = db.Column(db.String(255), nullable=False) - """Record identifier.""" - - download_count = db.Column(db.Integer, nullable=False, default=0) - """Download count""" - - expiration_date = db.Column(db.Integer, nullable=False, default=0) - """Expiration Date""" - - extra_info = db.Column( - db.JSON().with_variant( - postgresql.JSONB(none_as_null=True), - 'postgresql', - ).with_variant( - JSONType(), - 'sqlite', - ).with_variant( - JSONType(), - 'mysql', - ), + Raises: + ValueError: If the download limit has been reached. + Exception: If an unexpected error occurs during the update. + """ + if self.download_count >= self.download_limit: + raise ValueError('Download limit has been reached.') + try: + self.download_count += 1 + db.session.commit() + except Exception as ex: + db.session.rollback() + current_app.logger.error(ex) + raise ex + + def delete_logically(self): + """Execute logical deletion by setting the 'is_deleted' flag to True. + + This marks the record as deleted without removing it from the database. + + Raises: + Exception: If an unexpected error occurs during the deletion. + """ + try: + self.is_deleted = True + db.session.commit() + except Exception as ex: + db.session.rollback() + current_app.logger.error(ex) + raise ex + + @classmethod + def fetch_active_urls(cls, record_id, file_name, ascending=False): + """Fetch the active URLs for a specified file from the database. + + Args: + record_id (str): The ID of the record to which the file belongs. + file_name (str): The name of the file. + ascending (bool): A flag indicating how the results are ordered. + + Returns: + List[cls]: A list of active URLs. + """ + query = cls.query.filter( + cls.record_id == record_id, + cls.file_name == file_name, + cls.expiration_date > datetime.utcnow(), + cls.download_count < cls.download_limit, + cls.is_deleted == False + ) + if ascending: + return query.order_by(asc(cls.id)).all() + else: + return query.order_by(desc(cls.id)).all() + + +class FileOnetimeDownload(db.Model, Timestamp, DownloadMixin): + """A model class for the 'file_onetime_download' table. + + This class stores information about one-time URLs used for file access. + + Note: + Despite being called 'one-time', the download limit can be set to more + than once. + + Attributes: + id (int): The unique identifier of the record. + approver_id (int): The ID of the user who approved the application. + record_id (str): The ID of the associated file record. + file_name (str): The name of the file. + expiration_date (datetime): The date and time when the URL expires. + download_limit (int): The maximum number of downloads allowed. + download_count (int): The number of times the URL has been downloaded. + user_mail (str): The email address of the user who applied. + is_guest (bool): Indicates whether the user is a guest. + is_deleted (bool): Indicates whether the record is deleted. + extra_info (dict): Additional information stored in JSON format. + """ + __tablename__ = 'file_onetime_download' + id = db.Column(db.Integer,primary_key=True,autoincrement=True) + approver_id = db.Column( + db.Integer, + db.ForeignKey( + 'accounts_user.id', + name='fk_file_onetime_download_approver_id'), + nullable=False) + record_id = db.Column(db.String(255), nullable=False) + file_name = db.Column(db.String(255), nullable=False) + expiration_date = db.Column(db.DateTime, nullable=False) + download_limit = db.Column(db.Integer, nullable=False) + download_count = db.Column(db.Integer, nullable=False, default=0) + user_mail = db.Column(db.String(255), nullable=False) + is_guest = db.Column(db.Boolean, nullable=False, default=False) + is_deleted = db.Column(db.Boolean, nullable=False, default=False) + extra_info = db.Column(db.JSON() + .with_variant(postgresql.JSONB(none_as_null=True), 'postgresql') + .with_variant(JSONType(), 'sqlite') + .with_variant(JSONType(), 'mysql'), default=lambda: dict(), - nullable=True + nullable=True,) + __table_args__ = ( + CheckConstraint('created < expiration_date', + name='check_expiration_date'), + CheckConstraint('download_limit > 0', + name='check_download_limit_positive'), + CheckConstraint('download_count <= download_limit', + name='check_download_count_limit'), ) - """Extra info.""" - - def __init__(self, file_name, user_mail, record_id, download_count=0, - expiration_date=0, extra_info=None): - """Init. - - :param file_name: File name - :param user_mail: User mail - :param record_id: Record identifier - :param download_count: Download count - :param expiration_date: Expiration date - :param extra_info: Extra info want to store + + def __init__( + self, approver_id, record_id, file_name, expiration_date, + download_limit, user_mail, is_guest, extra_info + ): + """Initialize the instance. + + Note: + The 'id', 'download_count', and 'is_deleted' fields are not part of + the initialization. + + Args: + approver_id (int): The ID of the user who approved the application. + record_id (str): The ID of the file's associated record. + file_name (str): The name of the file. + expiration_date (datetime): The date and time when the URL expires. + download_limit (int): The download limit of the URL. + user_mail (str): The email address of the user who applied. + is_guest (bool): A flag indicating whether the user is a guest. + extra_info (dict): Additional information stored in JSON format. """ - self.file_name = file_name - self.user_mail = user_mail - self.record_id = record_id - self.download_count = download_count + self.approver_id = approver_id + self.record_id = record_id + self.file_name = file_name self.expiration_date = expiration_date - self.extra_info = extra_info + self.download_limit = download_limit + self.user_mail = user_mail + self.is_guest = is_guest + self.extra_info = extra_info @classmethod def create(cls, **data): - """Create data.""" + """Create a new instance and save it to the database. + + Args: + **data: The attributes for the new instance. + + Returns: + FileOnetimeDownload: The created instance. + + Raises: + ValueError: If the arguments are invalid. + Exception: If an unexpected error occurs during the creation. + """ + if data['expiration_date'] < datetime.now(tz=timezone.utc): + raise ValueError('The expiration date must be in the future.') + if data['download_limit'] <= 0: + raise ValueError('The download limit must be greater than 0.') try: file_download = cls(**data) db.session.add(file_download) @@ -357,37 +471,19 @@ def create(cls, **data): except Exception as ex: db.session.rollback() current_app.logger.error(ex) - return None + raise ex @classmethod - def update_download(cls, **data): - """Update download count. + def get_by_id(cls, id): + """Get a record by its ID. - :param data: - :return: + Args: + id (int): The ID of the record to retrieve. + + Returns: + FileOnetimeDownload: The record instance, or None if not found. """ - try: - file_name = data.get("file_name") - user_mail = data.get("user_mail") - record_id = data.get("record_id") - file_permission = cls.find(file_name=file_name, user_mail=user_mail, - record_id=record_id) - if file_permission and len(file_permission) > 0: - for file in file_permission: - if data.get("download_count") is not None: - file.download_count = data.get("download_count") - if data.get("expiration_date") is not None: - file.expiration_date = data.get("expiration_date") - if data.get("extra_info"): - file.extra_info = data.get("extra_info") - db.session.merge(file) - db.session.commit() - return file_permission - return None - except Exception as ex: - db.session.rollback() - current_app.logger.error(ex) - return None + return cls.query.get(id) @classmethod def find(cls, **obj) -> list: @@ -402,7 +498,7 @@ def find(cls, **obj) -> list: cls.user_mail == obj.get("user_mail"), ) return query.order_by(desc(cls.id)).all() - + @classmethod def find_downloadable_only(cls, **obj) -> list: """If the user can download ,find file onetime download. @@ -414,59 +510,118 @@ def find_downloadable_only(cls, **obj) -> list: cls.file_name == obj.get("file_name"), cls.record_id == obj.get("record_id"), cls.user_mail == obj.get("user_mail"), - cls.download_count > 0 , - now() < cls.created + func.cast( concat( cls.expiration_date , ' days' ) , INTERVAL) + cls.download_count < cls.download_limit, + cls.expiration_date > datetime.now(timezone.utc), + cls.is_deleted == False ) return query.order_by(desc(cls.id)).all() - -class FileSecretDownload(db.Model, Timestamp): - """File secret download.""" + def update_extra_info(self, new_info: dict): + """Update the 'extra_info' field with the provided new data. - __tablename__ = 'file_secret_download' + Args: + new_info (dict): A dictionary containing the new info to update. - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - """Identifier""" + Raises: + ValueError: If the new info is not a dictionary. + Exception: If an unexpected error occurs during the update. + """ + if not isinstance(new_info, dict): + raise ValueError('The new info must be a dictionary.') + try: + self.extra_info = new_info + db.session.commit() + except Exception as ex: + db.session.rollback() + current_app.logger.error(ex) + raise ex - file_name = db.Column(db.String(255), nullable=False) - """File name""" - user_mail = db.Column(db.String(255), nullable=False) - """User mail""" +class FileSecretDownload(db.Model, Timestamp, DownloadMixin): + """A model class for 'file_secret_download' table. - record_id = db.Column(db.String(255), nullable=False) - """Record identifier.""" + This class stores information about secret URLs, which used for private + file access. - download_count = db.Column(db.Integer, nullable=False, default=0) - """Download count""" + Attributes: + id (int): The identifier of the record. + creator_id (int): The ID of the user who issued the secret URL. + record_id (str): The ID of the record that has the file. + file_name (str): The name of the file. + label_name (str): The label of the secret URL. + expiration_date (datetime): The date and time when the URL expires. + download_limit (int): The download limit of the URL. + download_count (int): The number of times the URL has been downloaded. + is_deleted (bool): A flag indicating whether the record is deleted. + """ + __tablename__ = 'file_secret_download' + id = db.Column(db.Integer,primary_key=True,autoincrement=True) + creator_id = db.Column(db.Integer, + db.ForeignKey( + 'accounts_user.id', + name='fk_file_secret_download_creator_id'), + nullable=False) + record_id = db.Column(db.String(255), nullable=False) + file_name = db.Column(db.String(255), nullable=False) + label_name = db.Column(db.String(255), nullable=False) + expiration_date = db.Column(db.DateTime, nullable=False) + download_limit = db.Column(db.Integer, nullable=False) + download_count = db.Column(db.Integer, nullable=False, default=0) + is_deleted = db.Column(db.Boolean, nullable=False, default=False) + __table_args__ = ( + CheckConstraint('created < expiration_date', + name='check_expiration_date'), + CheckConstraint('download_limit > 0', + name='check_download_limit_positive'), + CheckConstraint('download_count <= download_limit', + name='check_download_count_limit'), + ) - expiration_date = db.Column(db.Integer, nullable=False, default=0) - """Expiration Date""" + def __init__(self, creator_id, record_id, file_name, label_name, + expiration_date, download_limit): + """Initialize the instance. - def __init__(self, file_name, user_mail, record_id, download_count=0, - expiration_date=0): - """Init. + Note: + The 'id', 'download_count', and 'is_deleted' fields are not part of + the initialization. - :param file_name: File name - :param user_mail: User mail - :param record_id: Record identifier - :param download_count: Download count - :param expiration_date: Expiration date + Args: + creator_id (int): The ID of the user who issued the secret URL. + record_id (str): The ID of the record that has the file. + file_name (str): The name of the file. + label_name (str): The label of the secret URL. + expiration_date (date): The date when the URL expires. + download_limit (int): The download limit of the URL. """ - self.file_name = file_name - self.user_mail = user_mail - self.record_id = record_id - self.download_count = download_count + self.creator_id = creator_id + self.record_id = record_id + self.file_name = file_name + self.label_name = label_name self.expiration_date = expiration_date + self.download_limit = download_limit @classmethod def create(cls, **data): - """Create data.""" + """Create a new instance and save it to the database. + + Args: + **data: The attributes for the new instance. + + Returns: + FileSecretDownload: The created instance. + + Raises: + ValueError: If the arguments are invalid. + Exception: If an unexpected error occurs during the creation. + """ + if data['expiration_date'] < datetime.now(tz=timezone.utc): + raise ValueError('The expiration date must be in the future.') + if data['download_limit'] <= 0: + raise ValueError('The download limit must be greater than 0.') try: file_download = cls(**data) db.session.add(file_download) db.session.commit() - db.session.flush() return file_download except Exception as ex: db.session.rollback() @@ -474,34 +629,16 @@ def create(cls, **data): raise ex @classmethod - def update_download(cls, **data): - """Update download count. + def get_by_id(cls, id): + """Get a record by its ID. - :param data: - :return: + Args: + id (int): The ID of the record to retrieve. + + Returns: + FileSecretDownload: The record instance, or None if not found. """ - try: - file_name = data.get("file_name") - id = data.get("id") - record_id = data.get("record_id") - created = data.get("created") - current_app.logger.debug("data: {}".format(data)) - file_permission = cls.find(file_name=file_name, id=id, - record_id=record_id,created=created) - current_app.logger.debug("file_permission: {}".format(file_permission)) - if len(file_permission) == 1: - file = file_permission[0] - if data.get("download_count") is not None: - file.download_count = data.get("download_count") - db.session.merge(file) - db.session.commit() - return file_permission - else: - return None - except Exception as ex: - db.session.rollback() - current_app.logger.error(traceback.format_exc()) - raise ex + return cls.query.get(id) @classmethod def find(cls, **obj) -> list: @@ -518,4 +655,121 @@ def find(cls, **obj) -> list: ) return query.order_by(desc(cls.id)).all() -__all__ = ('PDFCoverPageSettings', 'FilePermission', 'FileOnetimeDownload' ,'FileSecretDownload') + +class UrlType(enum.Enum): + """An ENUM data type for the used URL.""" + SECRET = 'SECRET' + ONETIME = 'ONETIME' + + +class AccessStatus(enum.Enum): + """An ENUM data type for the access status of the downloaded file.""" + OPEN_NO = 'OPEN_NO' + OPEN_DATE = 'OPEN_DATE' + OPEN_RESTRICTED = 'OPEN_RESTRICTED' + + +class FileUrlDownloadLog(db.Model, Timestamp): + """Stores information of the executed download by download-URLs. + + This class(table) is used to store information of the executed download of + a file using either secret URL or onetime URL. + + Attributes: + id (int): The identifier of each download information. + url_type (UrlType): The used URL type('SECRET' or 'ONETIME'). + secret_url_id (int): The secret URL record ID. + onetime_url_id (int): The onetime URL record ID. + ip_address (str): The IP address of the downloader. + access_status (AccessStatus): The access status of the downloaded file. + used_token (str): The URL token used to access the file. + """ + __tablename__ = 'file_url_download_log' + id = db.Column(db.Integer(), + primary_key=True, + autoincrement=True) + url_type = db.Column(db.Enum(UrlType), nullable=False) + secret_url_id = db.Column(db.Integer(), + db.ForeignKey(FileSecretDownload.id)) + onetime_url_id = db.Column(db.Integer(), + db.ForeignKey(FileOnetimeDownload.id)) + ip_address = db.Column(INET() + .with_variant(db.String(255), 'sqlite') + .with_variant(db.String(255), 'mysql')) + access_status = db.Column(db.Enum(AccessStatus), nullable=False) + used_token = db.Column(db.String(255), nullable=False) + __table_args__ = ( + CheckConstraint( + """ + (url_type = 'SECRET' AND secret_url_id IS NOT NULL AND + onetime_url_id IS NULL) + OR + (url_type = 'ONETIME' AND onetime_url_id IS NOT NULL AND + secret_url_id IS NULL) + """, + name="chk_url_id"), + CheckConstraint( + """ + (url_type = 'SECRET' AND ip_address IS NOT NULL) + OR + (url_type = 'ONETIME' AND ip_address IS NULL) + """, + name="chk_ip_address"), + CheckConstraint( + """ + (url_type = 'SECRET' AND + (access_status = 'OPEN_NO' OR access_status = 'OPEN_DATE')) + OR + (url_type = 'ONETIME' AND access_status = 'OPEN_RESTRICTED') + """, + name="chk_access_status") + ) + + def __init__(self, url_type, secret_url_id, onetime_url_id, ip_address, + access_status, used_token): + """Initializes the FileUrlDownloadLog instance. + + Args: + url_type (UrlType): The used URL type. + secret_url_id (int): The secret URL record ID. + onetime_url_id (int): The onetime URL record ID. + ip_address (str): The IP address of the downloader. + access_status (AccessStatus): The status of the downloaded file. + used_token (str): The URL token used to access the file. + """ + self.url_type = url_type + self.secret_url_id = secret_url_id + self.onetime_url_id = onetime_url_id + self.ip_address = ip_address + self.access_status = access_status + self.used_token = used_token + + @classmethod + def create(cls, **data): + """Create a new instance and save it to the database. + + Args: + **data: The attributes for the new instance. + + Returns: + FileUrlDownloadLog: The created instance. + + Raises: + Exception: If an unexpected error occurs during the creation. + """ + try: + file_download = cls(**data) + db.session.add(file_download) + db.session.commit() + return file_download + except Exception as ex: + db.session.rollback() + current_app.logger.error(ex) + raise ex + + +__all__ = ('PDFCoverPageSettings', + 'FilePermission', + 'FileOnetimeDownload', + 'FileSecretDownload', + 'FileUrlDownloadLog',) diff --git a/modules/weko-records-ui/weko_records_ui/static/css/weko_records_ui/style.css b/modules/weko-records-ui/weko_records_ui/static/css/weko_records_ui/style.css index 6635439bd9..d0b5d325d6 100644 --- a/modules/weko-records-ui/weko_records_ui/static/css/weko_records_ui/style.css +++ b/modules/weko-records-ui/weko_records_ui/static/css/weko_records_ui/style.css @@ -76,12 +76,12 @@ .action-button { margin-top: 5px; margin-bottom: 5px; -// display: block; +/* display: block;*/ margin-right: auto; margin-left: auto; font-size: 13px; line-height: 12px; -// background-color: #D3D3D3; +/* background-color: #D3D3D3;*/ height: 27px; } @@ -100,3 +100,146 @@ background-color: #f7f7f7 !important; border: 1px solid #ebebeb; } + +.icon-space { + padding-right: 8px; +} +#secret_url_form { + display: flex; /* フォーム全体を横一列に配置 */ + flex-direction: row; + gap: 20px; /* 各要素間のスペース */ + align-items: flex-start; /* 上揃え */ +} + +#secret_url_form #link_name, +#secret_url_form #expiration_date, +#secret_url_form #download_limit { + display: flex; /* ラベルとフィールドを縦並びに */ + flex-direction: column; /* ラベルを上に配置 */ + align-items: flex-start; /* 左揃え */ +} + +#secret_url_form #link_name label, +#secret_url_form #expiration_date label, +#secret_url_form #download_limit label { + margin-bottom: 5px; /* ラベルとフィールドの間のスペース */ + font-weight: bold; +} + +#secret_url_form input[type="text"], +#secret_url_form input[type="date"], +#secret_url_form input[type="number"] { + width: 150px; /* 各入力フィールドの幅を調整 */ + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + box-sizing: border-box; +} + +#secret_url_form button { + align-self: center; /* ボタンを中央揃え */ + padding: 10px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 16px; +} + +#secret_url_form button:hover { + background-color: #0056b3; +} + +#secret_url_section { + border: 1px solid #ccc; + margin-bottom: 20px; + padding: 15px 5px; +} + +#max_date_display, +#max_download_display { + font-size: 14px; + color: #555; + margin-top: 5px; /* フィールドとテキストの間にスペース */ +} + +#secret_url_form #checkbox-container { + display: flex; + flex-direction: row-reverse; /* 順番を逆にする */ + align-items: center; +} + +#secret_url_form #checkbox-container label { + margin-left: 5px; /* チェックボックスとラベルの間にスペース */ + font-weight: normal; /* チェックボックスラベルのフォントを通常に */ +} + +.error-message { + color: red; +} + +/* 削除ボタンのスタイル */ +.delete_secret_url { + background-color: #dc3545; + width: 130px; + color: white; +} + +/* コピーボタンのスタイル */ +.copy_secret_url { + background-color: #F0F8FF; + width: 130px; + color: black; +} + +.secret-url-area table { + width: 100%; + border-collapse: collapse; +} + +.secret-url-area th, .secret-url-area td { + padding: 10px; + text-align: left; + border: 1px solid #ddd; + vertical-align: middle; /* 縦方向の中央揃え */ +} + +.secret-url-area th { + background-color: #f2f2f2; +} + +/* 削除ボタンのスタイル */ +.delete_onetime_url { + background-color: #dc3545; + width: 130px; + color: white; +} + +/* コピーボタンのスタイル */ +.copy_onetime_url { + background-color: #F0F8FF; + width: 130px; + color: black; +} + +.onetime-url-area table { + width: 100%; + border-collapse: collapse; +} + +.onetime-url-area th, .onetime-url-area td { + padding: 10px; + text-align: left; + border: 1px solid #ddd; + vertical-align: middle; /* 縦方向の中央揃え */ +} + +.onetime-url-area th { + background-color: #f2f2f2; +} + +.button-group { + margin-right: 5px; + display: inline-block; /* ボタンを横並びにする */ +} \ No newline at end of file diff --git a/modules/weko-records-ui/weko_records_ui/static/js/weko_records_ui/detail.js b/modules/weko-records-ui/weko_records_ui/static/js/weko_records_ui/detail.js index 64087b48d8..ec6d93f14f 100644 --- a/modules/weko-records-ui/weko_records_ui/static/js/weko_records_ui/detail.js +++ b/modules/weko-records-ui/weko_records_ui/static/js/weko_records_ui/detail.js @@ -272,25 +272,303 @@ require([ } }); - $('#secret_url')?.on('click', function(){ - const webelement = $('#secret_url'); - if (webelement){ + // シークレットURL作成フォーム + // link_name フィールドの入力を検証 + const MAX_LENGTH=50; + const linkNameInput = document.querySelector('#link_name'); + if (linkNameInput) { + linkNameInput.maxLength = MAX_LENGTH; + } + + // APIエンドポイントから設定を取得 + fetch('/get-secret-settings') + .then(response => { + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.statusText}`); + } + return response.json(); + }) + .then(data => { + // APIからのデータをフォームに反映 + const secretDownloadLimit = data.secret_download_limit; + const secretExpirationDate = data.secret_expiration_date; + const maxSecretExpirationDate = data.max_secret_expiration_date; + const maxSecretDownloadLimit = data.max_secret_download_limit; + + // 今日の日付を取得 + const today = new Date(); + const todayFormatted = today.toISOString().split('T')[0]; + + // secret_expiration_date を使用して日付を計算 + const expirationDate = new Date(); + expirationDate.setDate(today.getDate() + parseInt(secretExpirationDate, 10)); + const formattedDate = expirationDate.toISOString().split('T')[0]; + + // maxSecretExpirationDate を適切な日付形式に変換 + const maxDate = new Date(); + maxDate.setDate(maxDate.getDate() + parseInt(maxSecretExpirationDate, 10)); + const maxFormattedDate = maxDate.toISOString().split('T')[0]; + + // 要素に設定 + const expirationDateInput = document.querySelector('#expiration_date'); + if (expirationDateInput) { + expirationDateInput.value = formattedDate; + expirationDateInput.max = maxFormattedDate; + expirationDateInput.min = todayFormatted; + } + // フォームのフィールドに値を設定 + const downloadLimitInput = document.querySelector('#download_limit'); + if (downloadLimitInput) { + downloadLimitInput.value = secretDownloadLimit; + // maxSecretDownloadLimit 以上の入力を制御 + downloadLimitInput.addEventListener('input', function () { + if (parseInt(downloadLimitInput.value, 10) > maxSecretDownloadLimit) { + downloadLimitInput.value = maxSecretDownloadLimit; + } + }); + } + // 有効期限の下に表示するメッセージを設定 + const maxExpirationData = document.querySelector('#max_date_display'); + if (maxExpirationData) { + const translationDate = maxExpirationData.dataset.expirationDate; + maxExpirationData.textContent = ` ※${translationDate} ${maxFormattedDate}`; + } + // ダウンロード制限の下に表示するメッセージを設定 + const maxDownloadDisplay = document.querySelector('#max_download_display'); + if (maxDownloadDisplay) { + const downloadcount = maxDownloadDisplay.dataset.downloadCount; + maxDownloadDisplay.textContent = ` ※${downloadcount} ${maxSecretDownloadLimit}`; + } + }) + .catch(error => { + console.error('Error fetching secret settings:', error); + }); + }); + + + $("#secret_url").click(function() { + $("#secret_url_section").toggle(); + }); + + $('#create_secret_url')?.on('click', function(event) { + event.preventDefault(); + const webelement = $('#create_secret_url'); + if (webelement) { const url = webelement.attr('url'); - webelement.prop('disabled' ,true); + const linkName = $('#link_name').val(); + const expirationDate = $('#expiration_date').val(); + const downloadLimit = $('#download_limit').val(); + const sendEmail = $('#send_email').is(':checked'); + const timezoneOffsetMinutes = new Date().getTimezoneOffset(); + const linkNameError = $('#link_name_error'); + const expirationDateError = $('#expiration_date_error'); + const downloadLimitError = $('#download_limit_error'); + + let hasError = false; + + // エラーチェック関数 + function checkError(input, errorElement) { + if (!input) { + errorElement.css('display', 'block'); // エラーメッセージを表示 + hasError = true; + } else { + errorElement.css('display', 'none'); // エラーメッセージを非表示 + } + } + + // 各項目のエラーチェック + checkError(linkName, linkNameError); + checkError(expirationDate, expirationDateError); + checkError(downloadLimit, downloadLimitError); + + // エラーがある場合は処理を停止 + if (hasError) { + return; + } + $.ajax({ url: url, method: 'POST', contentType: 'application/json', - data: null, - success: function (responce) { - webelement.prop('disabled',false); - alert(responce); + dataType: 'json', + data: JSON.stringify({ + link_name: linkName, + expiration_date: expirationDate, + download_limit: parseInt(downloadLimit), + send_email: sendEmail, + timezone_offset_minutes: timezoneOffsetMinutes, + }), + success: function(response) { + webelement.prop('disabled', false); + alert(response.message || "Success!"); + location.reload(); }, - error: function (jqXHE, status ,msg) { - webelement.prop('disabled',false); - alert(msg); + error: function(jqXHR, status, msg) { + webelement.prop('disabled', false); + alert("Error: " + (jqXHR.responseJSON?.message || msg)); } }); } }); + + $('#print-btn')?.on('click',() => { + const iframe = document.createElement('iframe'); + iframe.srcdoc = "" + document.body.appendChild(iframe); + iframe.onload = () => { + iframe.contentDocument.body.appendChild(document.getElementById('terms').cloneNode(true)); + iframe.contentWindow.print(); + } + }) + + +$('#close_btn, #modal_close_btn').on('click', function () { + document.location.href = location.pathname; + }) +$('#mailcheck_download_modal').on('hidden.bs.modal', function () { + document.location.href = location.pathname; +}) +$('#mailaddress_confirm_download').click(function () { + let mailaddress = document.getElementById('mail_form').value; + let password_checkflag = document.getElementById("password_checkflag").value; + var input_password; + var post_data = {}; + if(password_checkflag == "True"){ + input_password = document.getElementById('input_password').value; + post_data = {'input_password': input_password}; +} + let input_error = document.getElementById('input_error_messsge').value; + let url_element = document.getElementById('url_element'); + let onetime_file_url = url_element.dataset.onetime_file_url; + const get_uri = onetime_file_url + '&mailaddress='+ mailaddress + '&isajax=true'; + let item_detailes_url = location.pathname; + if(mailaddress == null || mailaddress == ""){ + alert(input_error); + document.location.href = onetime_file_url; + }else{ + $.ajax({ + url: get_uri, + method: 'POST', + async: true, + data: JSON.stringify(post_data), + contentType: 'application/json', + success: function (response) { + let link = document.createElement("a"); + link.download = ""; + if (!!response.guest_token) { + link.href = get_uri + "&guest-token=" + response.guest_token; + } else { + link.href = get_uri; + } + link.click(); + $('#mailcheck_download_modal').modal('hide'); + document.location.href = item_detailes_url; + }, + error: function (error) { + response_text = error['responseText']; + alert(response_text); + $('#mailcheck_download_modal').modal('hide'); + document.location.href = item_detailes_url; + } + }) + } +}); + + +document.addEventListener('DOMContentLoaded', function() { + // 日付をフォーマットする関数 + function formatDate(dateString) { + // Dateオブジェクトを作成 + const date = new Date(dateString); + const year = date.getFullYear(); + const month = ('0' + (date.getMonth() + 1)).slice(-2); + const day = ('0' + date.getDate()).slice(-2); + return `${year}-${month}-${day}`; + } + + // .date-format クラスを持つすべての要素を取得 + const dateElements = document.querySelectorAll('.date-format'); + + // 各要素の日付をフォーマット + dateElements.forEach(function(element) { + const originalDate = element.textContent; + element.textContent = formatDate(originalDate); + }); +}); + +// 確認メッセージを取得 +const deleteConfirmationMessage = $('#delete-confirmation-message').data('message'); +const deleteSuccssesMessage = $('#delete-success-message').data('message'); +const copyMessage = $('#copy-message').data('message'); + +$(document).ready(function() { + // 共通のAJAXリクエスト関数 + function handleAjaxRequest($button, url, method, successMessage, clipboardText, finalMessage) { + $button.prop('disabled', true); + $.ajax({ + url: url, + method: method, + contentType: 'application/json', + dataType: 'json', + success: function(response) { + $button.prop('disabled', false); + if (clipboardText) { + navigator.clipboard.writeText(response.url) + .catch(function(err) { + alert('Could not copy URL: ', err); + }); + } else { + location.reload(); // 画面をリロード + } + if (finalMessage) { + alert(finalMessage); + } + }, + error: function(jqXHR, status, msg) { + $button.prop('disabled', false); + alert("Error: " + (jqXHR.responseJSON?.message || msg)); + } + }); + } + + // シークレットURLの削除 + $('.delete_secret_url').on('click', function(event) { + event.preventDefault(); + const $button = $(this); + const url = $button.attr('url'); + + // 確認ポップアップを表示 + if (confirm(deleteConfirmationMessage)) { + handleAjaxRequest($button, url, 'DELETE', "Success!", false, deleteSuccssesMessage); + } + }); + + // シークレットURLのコピー + $('.copy_secret_url').on('click', function(event) { + event.preventDefault(); + const $button = $(this); + const url = $button.attr('url'); + handleAjaxRequest($button, url, 'GET', "Success!", true, copyMessage); + }); + + // ワンタイムURLの削除 + $('.delete_onetime_url').on('click', function(event) { + event.preventDefault(); + const $button = $(this); + const url = $button.attr('url'); + + // 確認ポップアップを表示 + if (confirm(deleteConfirmationMessage)) { + handleAjaxRequest($button, url, 'DELETE', "Success!", false, deleteSuccssesMessage); + } + }); + + // ワンタイムURLのコピー + $('.copy_onetime_url').on('click', function(event) { + event.preventDefault(); + const $button = $(this); + const url = $button.attr('url'); + handleAjaxRequest($button, url, 'GET', "Success!", true, copyMessage); + }); }); + diff --git a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html index 129c88d50e..5d2de3323b 100644 --- a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html +++ b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html @@ -167,6 +167,51 @@

{{ filename.
+ {%- if show_secret_URL -%} + + {%- endif -%} {% from "weko_records_ui/output_detail_data.html" import output_attribute_value_mlt %} {%- set display_file_info = record.display_file_info -%} @@ -179,7 +224,100 @@

{{ filename. {%- endfor -%}

- + {%- if show_secret_URL -%} + {%- if active_secret_URLs -%} +
+ + + + + + + + + + + + + + + {%- for url in active_secret_URLs -%} + + + + + + + + {%- endfor -%} + +
{{_('Secret URL')}}
{{_('Label Name')}}{{_('Create Date')}}{{_('Expiration Date')}}{{_('Download Count')}}{{_('Action')}}
{{ url.label_name }}{{ url.created }}{{ url.expiration_date }}{{ url.download_count }}/{{url.download_limit}} +
+ + +
+
+
+
+
+
+ {%- endif -%} + {%- endif -%} + {%- if show_onetime_URL -%} + {%- if active_onetime_URLs -%} +
+ + + + + + + + + + + + + + + {%- for url in active_onetime_URLs -%} + + + + + + + + {%- endfor -%} + +
{{_('Onetime URL')}}
{{_('User Name')}}{{_('Create Date')}}{{_('Expiration Date')}}{{_('Download Count')}}{{_('Action')}}
{{ url.user_mail }}{{ url.created }}{{ url.expiration_date }}{{ url.download_count }}/{{url.download_limit}} +
+ + +
+
+
+
+
+
+ {%- endif -%} + {%- endif -%}