diff --git a/requirements.txt b/requirements.txt index cb5940a30..4d5fb1dbb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ django~=1.9.0 django-crispy-forms~=1.6.0 +django-import-export>=0.5.1 django-reversion~=2.0.0 django-formtools==1.0 future==0.15.2 diff --git a/xadmin/locale/zh_Hans/LC_MESSAGES/django.mo b/xadmin/locale/zh_Hans/LC_MESSAGES/django.mo index 1e34f6bc0..2107c5b29 100644 Binary files a/xadmin/locale/zh_Hans/LC_MESSAGES/django.mo and b/xadmin/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/xadmin/locale/zh_Hans/LC_MESSAGES/django.po b/xadmin/locale/zh_Hans/LC_MESSAGES/django.po index be333dfc8..cd41423f6 100644 --- a/xadmin/locale/zh_Hans/LC_MESSAGES/django.po +++ b/xadmin/locale/zh_Hans/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: xadmin-core\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-07-20 13:28+0800\n" +"POT-Creation-Date: 2017-09-04 15:12+0800\n" "PO-Revision-Date: 2013-11-20 10:21+0000\n" "Last-Translator: sshwsfc \n" "Language-Team: Chinese (China) (http://www.transifex.com/projects/p/xadmin/" @@ -20,59 +20,60 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: adminx.py:19 +#: .\adminx.py:20 msgid "Admin Object" msgstr "管理对象" -#: apps.py:11 +#: .\apps.py:11 msgid "Administration" msgstr "管理" -#: filters.py:159 filters.py:191 filters.py:407 filters.py:493 filters.py:531 +#: .\filters.py:169 .\filters.py:207 .\filters.py:427 .\filters.py:513 +#: .\filters.py:551 msgid "All" msgstr "全部" -#: filters.py:160 plugins/export.py:165 +#: .\filters.py:170 .\plugins\export.py:167 msgid "Yes" msgstr "是" -#: filters.py:161 plugins/export.py:165 +#: .\filters.py:171 .\plugins\export.py:167 msgid "No" msgstr "否" -#: filters.py:175 +#: .\filters.py:191 msgid "Unknown" msgstr "未知" -#: filters.py:267 +#: .\filters.py:287 msgid "Any date" msgstr "任意日期" -#: filters.py:268 +#: .\filters.py:288 msgid "Has date" msgstr "有日期" -#: filters.py:271 +#: .\filters.py:291 msgid "Has no date" msgstr "无日期" -#: filters.py:274 widgets.py:30 +#: .\filters.py:294 .\widgets.py:35 .\widgets.py:81 msgid "Today" msgstr "今天" -#: filters.py:278 +#: .\filters.py:298 msgid "Past 7 days" msgstr "过去7天" -#: filters.py:282 +#: .\filters.py:302 msgid "This month" msgstr "本月" -#: filters.py:286 +#: .\filters.py:306 msgid "This year" msgstr "今年" -#: forms.py:10 +#: .\forms.py:10 msgid "" "Please enter the correct username and password for a staff account. Note " "that both fields are case-sensitive." @@ -80,541 +81,583 @@ msgstr "" "请输入正确的用户名和密码来登陆您的管理账户。请注意用户名和密码均为大小写相" "关。" -#: forms.py:21 +#: .\forms.py:21 msgid "Please log in again, because your session has expired." msgstr "请重新登录,因为你的会话已经过期。" -#: forms.py:41 +#: .\forms.py:42 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "你的 e-mail 地址不是你的用户名。换 '%s' 试试。" -#: models.py:48 +#: .\models.py:46 msgid "Title" msgstr "标题" -#: models.py:49 models.py:88 models.py:107 models.py:149 +#: .\models.py:47 .\models.py:87 .\models.py:107 .\models.py:151 msgid "user" msgstr "用户" -#: models.py:50 +#: .\models.py:48 msgid "Url Name" msgstr "URL名字" -#: models.py:52 +#: .\models.py:50 msgid "Query String" msgstr "Query参数" -#: models.py:53 +#: .\models.py:51 msgid "Is Shared" msgstr "是否共享" -#: models.py:66 plugins/bookmark.py:50 plugins/bookmark.py:180 +#: .\models.py:64 .\plugins\bookmark.py:59 .\plugins\bookmark.py:194 msgid "Bookmark" msgstr "书签" -#: models.py:67 +#: .\models.py:65 msgid "Bookmarks" msgstr "书签" -#: models.py:89 +#: .\models.py:88 msgid "Settings Key" msgstr "设置KEY" -#: models.py:90 +#: .\models.py:89 msgid "Settings Content" msgstr "设置内容" -#: models.py:102 +#: .\models.py:101 msgid "User Setting" msgstr "用户设置" -#: models.py:103 +#: .\models.py:102 msgid "User Settings" msgstr "用户设置" -#: models.py:108 +#: .\models.py:108 msgid "Page" msgstr "页面" -#: models.py:109 views/dashboard.py:82 views/dashboard.py:92 +#: .\models.py:109 .\views\dashboard.py:85 .\views\dashboard.py:95 msgid "Widget Type" msgstr "Widget类型" -#: models.py:110 views/dashboard.py:83 +#: .\models.py:110 .\views\dashboard.py:86 msgid "Widget Params" msgstr "Widget参数" -#: models.py:137 +#: .\models.py:137 msgid "User Widget" msgstr "用户小组件" -#: models.py:138 +#: .\models.py:138 msgid "User Widgets" msgstr "用户小组件" -#: models.py:142 +#: .\models.py:144 msgid "action time" msgstr "记录时间" -#: models.py:151 +#: .\models.py:153 msgid "action ip" msgstr "操作IP" -#: models.py:155 +#: .\models.py:157 msgid "content type" msgstr "数据类型" -#: models.py:158 +#: .\models.py:160 msgid "object id" msgstr "数据ID" -#: models.py:159 +#: .\models.py:161 msgid "object repr" msgstr "数据描述" -#: models.py:160 +#: .\models.py:162 msgid "action flag" msgstr "操作标示" -#: models.py:161 +#: .\models.py:163 msgid "change message" msgstr "修改信息" -#: models.py:164 +#: .\models.py:166 msgid "log entry" msgstr "日志" -#: models.py:165 +#: .\models.py:167 msgid "log entries" msgstr "日志" -#: models.py:173 +#: .\models.py:175 #, python-format msgid "Added \"%(object)s\"." msgstr "添加 \"%(object)s\"。" -#: models.py:175 +#: .\models.py:177 +#, python-format msgid "Changed \"%(object)s\" - %(changes)s" msgstr "修改 \"%(object)s\" - %(changes)s" -#: models.py:180 +#: .\models.py:182 +#, python-format msgid "Deleted \"%(object)s.\"" msgstr "删除 \"%(object)s.\"" -#: plugins/actions.py:57 +#: .\plugins\actions.py:58 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "删除所选的 %(verbose_name_plural)s" -#: plugins/actions.py:72 +#: .\plugins\actions.py:73 +#, python-format msgid "Batch delete %(count)d %(items)s." msgstr "批量删除 %(count)d 个 %(items)s" -#: plugins/actions.py:78 +#: .\plugins\actions.py:79 #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "成功删除了 %(count)d 个 %(items)s" -#: plugins/actions.py:110 views/delete.py:70 +#: .\plugins\actions.py:111 .\views\delete.py:71 #, python-format msgid "Cannot delete %(name)s" msgstr "无法删除 %(name)s" -#: plugins/actions.py:112 views/delete.py:73 +#: .\plugins\actions.py:113 .\views\delete.py:74 msgid "Are you sure?" msgstr "你确定吗?" -#: plugins/actions.py:158 +#: .\plugins\actions.py:159 #, python-format msgid "%(total_count)s selected" msgid_plural "All %(total_count)s selected" msgstr[0] "选中了 %(total_count)s 个" -#: plugins/actions.py:162 +#: .\plugins\actions.py:163 #, python-format msgid "0 of %(cnt)s selected" msgstr "%(cnt)s 个中 0 个被选" -#: plugins/actions.py:179 plugins/actions.py:189 +#: .\plugins\actions.py:180 .\plugins\actions.py:190 msgid "" "Items must be selected in order to perform actions on them. No items have " "been changed." msgstr "条目必须选中以对其进行操作。没有任何条目被更改。" -#: plugins/aggregation.py:14 +#: .\plugins\aggregation.py:14 msgid "Min" msgstr "最小" -#: plugins/aggregation.py:14 +#: .\plugins\aggregation.py:14 msgid "Max" msgstr "最大" -#: plugins/aggregation.py:14 +#: .\plugins\aggregation.py:14 msgid "Avg" msgstr "平均" -#: plugins/aggregation.py:14 +#: .\plugins\aggregation.py:14 msgid "Sum" msgstr "总和" -#: plugins/aggregation.py:14 +#: .\plugins\aggregation.py:14 msgid "Count" msgstr "总数" -#: plugins/auth.py:21 +#: .\plugins\auth.py:25 #, python-format msgid "Can add %s" msgstr "添加:%s" -#: plugins/auth.py:22 +#: .\plugins\auth.py:26 #, python-format msgid "Can change %s" msgstr "修改:%s" -#: plugins/auth.py:23 +#: .\plugins\auth.py:27 #, python-format msgid "Can edit %s" msgstr "编辑:%s" -#: plugins/auth.py:24 +#: .\plugins\auth.py:28 #, python-format msgid "Can delete %s" msgstr "删除:%s" -#: plugins/auth.py:25 +#: .\plugins\auth.py:29 #, python-format msgid "Can view %s" msgstr "查看:%s" -#: plugins/auth.py:87 +#: .\plugins\auth.py:91 msgid "Personal info" msgstr "个人信息" -#: plugins/auth.py:91 +#: .\plugins\auth.py:95 msgid "Permissions" msgstr "权限" -#: plugins/auth.py:94 +#: .\plugins\auth.py:98 msgid "Important dates" msgstr "重要日期" -#: plugins/auth.py:99 +#: .\plugins\auth.py:103 msgid "Status" msgstr "状态" -#: plugins/auth.py:111 +#: .\plugins\auth.py:115 msgid "Permission Name" msgstr "权限" -#: plugins/auth.py:167 +#: .\plugins\auth.py:171 msgid "Change Password" msgstr "更改密码" -#: plugins/auth.py:198 +#: .\plugins\auth.py:202 #, python-format msgid "Change password: %s" msgstr "更改密码:%s" -#: plugins/auth.py:223 plugins/auth.py:255 +#: .\plugins\auth.py:227 .\plugins\auth.py:259 msgid "Password changed successfully." msgstr "密码更改成功" -#: plugins/auth.py:242 templates/xadmin/auth/user/change_password.html:11 -#: templates/xadmin/auth/user/change_password.html:22 -#: templates/xadmin/auth/user/change_password.html:55 +#: .\plugins\auth.py:246 .\templates\xadmin\auth\user\change_password.html:11 +#: .\templates\xadmin\auth\user\change_password.html:22 +#: .\templates\xadmin\auth\user\change_password.html:55 msgid "Change password" msgstr "修改密码" -#: plugins/batch.py:44 +#: .\plugins\batch.py:44 msgid "Change this field" msgstr "修改该字段" -#: plugins/batch.py:65 +#: .\plugins\batch.py:65 #, python-format msgid "Batch Change selected %(verbose_name_plural)s" msgstr "批量修改选择的%(verbose_name_plural)s" -#: plugins/batch.py:89 +#: .\plugins\batch.py:89 #, python-format msgid "Successfully change %(count)d %(items)s." msgstr "成功修改了 %(count)d 个 %(items)s" -#: plugins/batch.py:138 +#: .\plugins\batch.py:138 #, python-format msgid "Batch change %s" msgstr "批量修改 %s" -#: plugins/bookmark.py:173 +#: .\plugins\bookmark.py:187 msgid "bookmark" msgstr "书签" -#: plugins/bookmark.py:176 +#: .\plugins\bookmark.py:190 msgid "Bookmark Widget, can show user's bookmark list data in widget." msgstr "书签组件,展示用户书签页内容。" -#: plugins/chart.py:25 +#: .\plugins\chart.py:24 msgid "Show models simple chart." msgstr "展示简单数据图表" -#: plugins/chart.py:51 +#: .\plugins\chart.py:50 #, python-format msgid "%s Charts" msgstr "%s图表" -#: plugins/comments.py:33 +#: .\plugins\comments.py:33 msgid "Metadata" msgstr "" -#: plugins/comments.py:60 +#: .\plugins\comments.py:60 msgid "flagged" msgid_plural "flagged" msgstr[0] "" -#: plugins/comments.py:61 +#: .\plugins\comments.py:61 msgid "Flag selected comments" msgstr "" -#: plugins/comments.py:66 +#: .\plugins\comments.py:66 msgid "approved" msgid_plural "approved" msgstr[0] "" -#: plugins/comments.py:67 +#: .\plugins\comments.py:67 msgid "Approve selected comments" msgstr "" -#: plugins/comments.py:72 +#: .\plugins\comments.py:72 msgid "removed" msgid_plural "removed" msgstr[0] "删除" -#: plugins/comments.py:73 +#: .\plugins\comments.py:73 msgid "Remove selected comments" msgstr "恢复删除的%(name)s" -#: plugins/comments.py:86 +#: .\plugins\comments.py:86 #, python-format msgid "1 comment was successfully %(action)s." msgid_plural "%(count)s comments were successfully %(action)s." msgstr[0] "" -#: plugins/details.py:52 views/list.py:578 +#: .\plugins\details.py:52 .\views\list.py:577 #, python-format msgid "Details of %s" msgstr "%s详情" -#: plugins/editable.py:46 +#: .\plugins\editable.py:46 #, python-format msgid "Enter %s" msgstr "输入%s" -#: plugins/editable.py:73 views/dashboard.py:649 views/delete.py:27 -#: views/detail.py:145 views/edit.py:454 +#: .\plugins\editable.py:73 .\views\dashboard.py:652 .\views\delete.py:28 +#: .\views\detail.py:145 .\views\edit.py:460 #, python-format msgid "%(name)s object with primary key %(key)r does not exist." msgstr "具有主键 %(key)r 的对象 %(name)s 不存在。" -#: plugins/export.py:98 plugins/export.py:135 +#: .\plugins\export.py:100 .\plugins\export.py:137 msgid "Sheet" msgstr "表" -#: plugins/filters.py:133 plugins/quickfilter.py:141 +#: .\plugins\filters.py:137 .\plugins\quickfilter.py:143 #, python-format msgid "Filtering error: %s" msgstr "过滤器错误: %s" -#: plugins/images.py:29 +#: .\plugins\images.py:29 msgid "Previous" msgstr "上一个" -#: plugins/images.py:29 +#: .\plugins\images.py:29 msgid "Next" msgstr "下一个" -#: plugins/images.py:29 +#: .\plugins\images.py:29 msgid "Slideshow" msgstr "幻灯片" -#: plugins/images.py:29 +#: .\plugins\images.py:29 msgid "Download" msgstr "下载" -#: plugins/images.py:50 +#: .\plugins\images.py:50 msgid "Change:" msgstr "修改:" -#: plugins/layout.py:16 +#: .\plugins\importexport.py:174 .\plugins\importexport.py:244 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.import.html:4 +#: .\templates\xadmin\import_export\import.html:6 +#: .\templates\xadmin\import_export\import.html:13 +#, fuzzy +#| msgid "Export" +msgid "Import" +msgstr "导出" + +#: .\plugins\importexport.py:227 +#, python-format +msgid "

Imported file has a wrong encoding: %s

" +msgstr "

导入文件编码错误: %s

" + +#: .\plugins\importexport.py:229 +#, python-format +msgid "

%s encountered while trying to read file: %s

" +msgstr "

%s读取文件是发生错误: %s

" + +#: .\plugins\importexport.py:300 +msgid "Import finished" +msgstr "导入完成" + +#: .\plugins\importexport.py:300 +#: .\templates\xadmin\blocks\comm.top.topnav.html:23 +msgid "Add" +msgstr "增加" + +#: .\plugins\importexport.py:301 +#: .\templates\xadmin\import_export\import.html:109 +msgid "Update" +msgstr "更新" + +#: .\plugins\importexport.py:435 +msgid "You must select an export format." +msgstr "必须选择一种导入格式" + +#: .\plugins\layout.py:16 msgid "Table" msgstr "表格" -#: plugins/layout.py:22 +#: .\plugins\layout.py:22 msgid "Thumbnails" msgstr "图标" -#: plugins/passwords.py:64 +#: .\plugins\passwords.py:62 msgid "Forgotten your password or username?" msgstr "忘记了您的密码或用户名?" -#: plugins/quickform.py:79 +#: .\plugins\quickform.py:80 #, python-format msgid "Create New %s" msgstr "创建新的 %s" -#: plugins/relate.py:104 +#: .\plugins\relate.py:105 msgid "Related Objects" msgstr "关联数据" -#: plugins/relfield.py:29 plugins/topnav.py:38 +#: .\plugins\relfield.py:29 .\plugins\topnav.py:38 #, python-format msgid "Search %s" msgstr "搜索%s" -#: plugins/relfield.py:67 +#: .\plugins\relfield.py:77 #, python-format msgid "Select %s" msgstr "选择%s" -#: plugins/themes.py:47 +#: .\plugins\themes.py:59 msgid "Default" msgstr "默认" -#: plugins/themes.py:48 +#: .\plugins\themes.py:59 msgid "Default bootstrap theme" msgstr "默认Bootstrap主题" -#: plugins/themes.py:49 +#: .\plugins\themes.py:60 msgid "Bootstrap2" msgstr "Bootstrap2" -#: plugins/themes.py:49 +#: .\plugins\themes.py:60 msgid "Bootstrap 2.x theme" msgstr "Bootstrap2主题" -#: plugins/topnav.py:62 views/dashboard.py:465 views/edit.py:387 -#: views/edit.py:396 +#: .\plugins\topnav.py:62 .\views\dashboard.py:468 .\views\edit.py:393 +#: .\views\edit.py:402 #, python-format msgid "Add %s" msgstr "增加 %s" -#: plugins/xversion.py:106 +#: .\plugins\xversion.py:107 msgid "Initial version." msgstr "初始化版本" -#: plugins/xversion.py:108 +#: .\plugins\xversion.py:109 msgid "Change version." msgstr "修改版本" -#: plugins/xversion.py:110 +#: .\plugins\xversion.py:111 msgid "Revert version." msgstr "还原版本" -#: plugins/xversion.py:112 +#: .\plugins\xversion.py:113 msgid "Rercover version." msgstr "恢复版本" -#: plugins/xversion.py:114 +#: .\plugins\xversion.py:115 #, python-format msgid "Deleted %(verbose_name)s." msgstr "删除%(verbose_name)s。" -#: plugins/xversion.py:127 templates/xadmin/views/recover_form.html:26 +#: .\plugins\xversion.py:128 .\templates\xadmin\views\recover_form.html:26 msgid "Recover" msgstr "还原" -#: plugins/xversion.py:143 templates/xadmin/views/model_history.html:11 -#: templates/xadmin/views/revision_diff.html:11 -#: templates/xadmin/views/revision_form.html:15 +#: .\plugins\xversion.py:144 .\templates\xadmin\views\model_history.html:11 +#: .\templates\xadmin\views\revision_diff.html:11 +#: .\templates\xadmin\views\revision_form.html:15 msgid "History" msgstr "历史" -#: plugins/xversion.py:194 templates/xadmin/views/recover_form.html:14 -#: templates/xadmin/views/recover_list.html:10 +#: .\plugins\xversion.py:195 .\templates\xadmin\views\recover_form.html:14 +#: .\templates\xadmin\views\recover_list.html:10 #, python-format msgid "Recover deleted %(name)s" msgstr "恢复删除的%(name)s" -#: plugins/xversion.py:238 +#: .\plugins\xversion.py:239 #, python-format msgid "Change history: %s" msgstr "变更历史: %s" -#: plugins/xversion.py:288 +#: .\plugins\xversion.py:289 msgid "Must select two versions." msgstr "必须选择两个版本。" -#: plugins/xversion.py:296 +#: .\plugins\xversion.py:297 msgid "Please select two different versions." msgstr "请选择两个不同的版本。" -#: plugins/xversion.py:383 plugins/xversion.py:500 +#: .\plugins\xversion.py:384 .\plugins\xversion.py:501 #, python-format msgid "Current: %s" msgstr "当前:%s" -#: plugins/xversion.py:424 +#: .\plugins\xversion.py:425 #, python-format msgid "Revert %s" msgstr "还原%s" -#: plugins/xversion.py:440 +#: .\plugins\xversion.py:441 #, python-format msgid "" "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " "below." msgstr "%(model)s “%(name)s”成功还原,您可以继续编辑。" -#: plugins/xversion.py:461 +#: .\plugins\xversion.py:462 #, python-format msgid "Recover %s" msgstr "恢复%s" -#: plugins/xversion.py:477 +#: .\plugins\xversion.py:478 #, python-format msgid "" "The %(model)s \"%(name)s\" was recovered successfully. You may edit it again " "below." msgstr "%(model)s “%(name)s”成功恢复,您可以继续编辑。" -#: templates/xadmin/404.html:4 templates/xadmin/404.html:8 +#: .\templates\xadmin\404.html:4 .\templates\xadmin\404.html:8 msgid "Page not found" msgstr "页面没有找到" -#: templates/xadmin/404.html:10 +#: .\templates\xadmin\404.html:10 msgid "We're sorry, but the requested page could not be found." msgstr "很报歉,请求页面无法找到。" -#: templates/xadmin/500.html:7 -#: templates/xadmin/auth/user/change_password.html:10 -#: templates/xadmin/auth/user/change_password.html:15 -#: templates/xadmin/base_site.html:53 -#: templates/xadmin/includes/sitemenu_default.html:7 -#: templates/xadmin/views/app_index.html:9 -#: templates/xadmin/views/batch_change_form.html:9 -#: templates/xadmin/views/invalid_setup.html:7 -#: templates/xadmin/views/model_dashboard.html:7 -#: templates/xadmin/views/model_delete_selected_confirm.html:8 -#: templates/xadmin/views/model_history.html:8 -#: templates/xadmin/views/recover_form.html:8 -#: templates/xadmin/views/recover_list.html:8 -#: templates/xadmin/views/revision_diff.html:8 -#: templates/xadmin/views/revision_form.html:8 views/base.py:473 +#: .\templates\xadmin\500.html:7 +#: .\templates\xadmin\auth\user\change_password.html:10 +#: .\templates\xadmin\auth\user\change_password.html:15 +#: .\templates\xadmin\base_site.html:53 +#: .\templates\xadmin\import_export\export_action.html:13 +#: .\templates\xadmin\import_export\import.html:11 +#: .\templates\xadmin\includes\sitemenu_default.html:7 +#: .\templates\xadmin\views\app_index.html:9 +#: .\templates\xadmin\views\batch_change_form.html:9 +#: .\templates\xadmin\views\invalid_setup.html:7 +#: .\templates\xadmin\views\model_dashboard.html:7 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:8 +#: .\templates\xadmin\views\model_history.html:8 +#: .\templates\xadmin\views\recover_form.html:8 +#: .\templates\xadmin\views\recover_list.html:8 +#: .\templates\xadmin\views\revision_diff.html:8 +#: .\templates\xadmin\views\revision_form.html:8 .\views\base.py:478 msgid "Home" msgstr "首页" -#: templates/xadmin/500.html:8 +#: .\templates\xadmin\500.html:8 msgid "Server error" msgstr "服务器错误" -#: templates/xadmin/500.html:12 +#: .\templates\xadmin\500.html:12 msgid "Server error (500)" msgstr "服务器错误(500)" -#: templates/xadmin/500.html:15 +#: .\templates\xadmin\500.html:15 msgid "Server Error (500)" msgstr "服务器错误 (500)" -#: templates/xadmin/500.html:16 +#: .\templates\xadmin\500.html:16 msgid "" "There's been an error. It's been reported to the site administrators via e-" "mail and should be fixed shortly. Thanks for your patience." @@ -622,44 +665,44 @@ msgstr "" "发生了一个错误。系统已将错误通过电子邮件报告给了站点管理员,相信问题应该会很" "快得到解决。感谢您的耐心。" -#: templates/xadmin/auth/password_reset/complete.html:11 -#: templates/xadmin/auth/password_reset/done.html:11 +#: .\templates\xadmin\auth\password_reset\complete.html:11 +#: .\templates\xadmin\auth\password_reset\done.html:11 msgid "Password reset successful" msgstr "密码重设成功" -#: templates/xadmin/auth/password_reset/complete.html:14 +#: .\templates\xadmin\auth\password_reset\complete.html:14 msgid "Your password has been set. You may go ahead and log in now." msgstr "你的口令己经设置。现在你可以继续进行登录。" -#: templates/xadmin/auth/password_reset/complete.html:15 +#: .\templates\xadmin\auth\password_reset\complete.html:15 msgid "Log in" msgstr "登录" -#: templates/xadmin/auth/password_reset/confirm.html:12 +#: .\templates\xadmin\auth\password_reset\confirm.html:12 msgid "Enter new password" msgstr "输入新密码" -#: templates/xadmin/auth/password_reset/confirm.html:17 +#: .\templates\xadmin\auth\password_reset\confirm.html:17 msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "请输入两遍新密码,以便我们校验你输入的是否正确。" -#: templates/xadmin/auth/password_reset/confirm.html:19 +#: .\templates\xadmin\auth\password_reset\confirm.html:19 msgid "Change my password" msgstr "修改我的密码" -#: templates/xadmin/auth/password_reset/confirm.html:24 +#: .\templates\xadmin\auth\password_reset\confirm.html:24 msgid "Password reset unsuccessful" msgstr "密码重设失败" -#: templates/xadmin/auth/password_reset/confirm.html:27 +#: .\templates\xadmin\auth\password_reset\confirm.html:27 msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "密码重置链接无效,可能是因为它已使用。可以请求一次新的密码重置。" -#: templates/xadmin/auth/password_reset/done.html:14 +#: .\templates\xadmin\auth\password_reset\done.html:14 msgid "" "We've e-mailed you instructions for setting your password to the e-mail " "address you submitted. You should be receiving it shortly." @@ -667,35 +710,35 @@ msgstr "" "我们已经按你所提交的电子邮箱地址发送了密码设置说明。你应该很快就能收到这封邮" "件。" -#: templates/xadmin/auth/password_reset/email.html:2 +#: .\templates\xadmin\auth\password_reset\email.html:2 #, python-format msgid "" "You're receiving this e-mail because you requested a password reset for your " "user account at %(site_name)s." msgstr "因为你要求重置 %(site_name)s 上的账户密码, 所以收到了这封邮件." -#: templates/xadmin/auth/password_reset/email.html:4 +#: .\templates\xadmin\auth\password_reset\email.html:4 msgid "Please go to the following page and choose a new password:" msgstr "请访问该页面并选择一个新密码:" -#: templates/xadmin/auth/password_reset/email.html:8 +#: .\templates\xadmin\auth\password_reset\email.html:8 msgid "Your username, in case you've forgotten:" msgstr "你的用户名,如果已忘记的话:" -#: templates/xadmin/auth/password_reset/email.html:10 +#: .\templates\xadmin\auth\password_reset\email.html:10 msgid "Thanks for using our site!" msgstr "感谢使用我们的站点!" -#: templates/xadmin/auth/password_reset/email.html:12 +#: .\templates\xadmin\auth\password_reset\email.html:12 #, python-format msgid "The %(site_name)s team" msgstr "%(site_name)s 团队" -#: templates/xadmin/auth/password_reset/form.html:13 +#: .\templates\xadmin\auth\password_reset\form.html:13 msgid "Password reset" msgstr "密码重设" -#: templates/xadmin/auth/password_reset/form.html:17 +#: .\templates\xadmin\auth\password_reset\form.html:17 msgid "" "Forgotten your password? Enter your e-mail address below, and we'll e-mail " "instructions for setting a new one." @@ -703,351 +746,414 @@ msgstr "" "忘记了你的密码?请在下面输入你的 e-mail 地址,我们将把新密码设置说明通过邮件" "发送给你。" -#: templates/xadmin/auth/password_reset/form.html:25 +#: .\templates\xadmin\auth\password_reset\form.html:25 msgid "E-mail address:" msgstr "E-mail 地址:" -#: templates/xadmin/auth/password_reset/form.html:33 +#: .\templates\xadmin\auth\password_reset\form.html:33 msgid "Reset my password" msgstr "重设我的密码" -#: templates/xadmin/auth/user/add_form.html:6 +#: .\templates\xadmin\auth\user\add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "首先,输入一个用户名和密码。然后,你就可以编辑更多的用户选项。" -#: templates/xadmin/auth/user/add_form.html:8 +#: .\templates\xadmin\auth\user\add_form.html:8 msgid "Enter a username and password." msgstr "输入用户名和" -#: templates/xadmin/auth/user/change_password.html:31 -#: templates/xadmin/views/batch_change_form.html:24 -#: templates/xadmin/views/form.html:18 -#: templates/xadmin/views/model_form.html:20 +#: .\templates\xadmin\auth\user\change_password.html:31 +#: .\templates\xadmin\views\batch_change_form.html:24 +#: .\templates\xadmin\views\form.html:18 +#: .\templates\xadmin\views\model_form.html:20 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "请修正下面的错误。" -#: templates/xadmin/auth/user/change_password.html:38 +#: .\templates\xadmin\auth\user\change_password.html:38 msgid "Enter your new password." msgstr "输入你的新密码" -#: templates/xadmin/auth/user/change_password.html:40 +#: .\templates\xadmin\auth\user\change_password.html:40 #, python-format msgid "Enter a new password for the user %(username)s." msgstr "为用户 %(username)s 输入一个新的密码。" -#: templates/xadmin/base_site.html:18 +#: .\templates\xadmin\base_site.html:18 msgid "Welcome," msgstr "欢迎," -#: templates/xadmin/base_site.html:24 +#: .\templates\xadmin\base_site.html:24 msgid "Log out" msgstr "注销" -#: templates/xadmin/base_site.html:36 +#: .\templates\xadmin\base_site.html:36 msgid "You don't have permission to edit anything." msgstr "你无权修改任何东西。" -#: templates/xadmin/blocks/comm.top.theme.html:4 +#: .\templates\xadmin\blocks\comm.top.theme.html:4 msgid "Themes" msgstr "主题" -#: templates/xadmin/blocks/comm.top.topnav.html:9 -#: templates/xadmin/blocks/model_list.nav_form.search_form.html:8 -#: templates/xadmin/filters/char.html:7 -#: templates/xadmin/filters/fk_search.html:7 -#: templates/xadmin/filters/fk_search.html:16 -#: templates/xadmin/filters/number.html:7 +#: .\templates\xadmin\blocks\comm.top.topnav.html:9 +#: .\templates\xadmin\blocks\model_list.nav_form.search_form.html:8 +#: .\templates\xadmin\filters\char.html:7 +#: .\templates\xadmin\filters\fk_search.html:7 +#: .\templates\xadmin\filters\fk_search.html:16 +#: .\templates\xadmin\filters\number.html:7 msgid "Search" msgstr "搜索" -#: templates/xadmin/blocks/comm.top.topnav.html:23 -msgid "Add" -msgstr "增加" - -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:9 -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:26 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:9 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:26 msgid "Prev step" msgstr "上一步" -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:13 -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:29 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:13 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:29 msgid "Next step" msgstr "下一步" -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:15 -#: templates/xadmin/blocks/model_form.submit_line.wizard.html:31 -#: templates/xadmin/includes/submit_line.html:10 -#: templates/xadmin/includes/submit_line.html:13 -#: templates/xadmin/views/form.html:30 templates/xadmin/views/form.html:31 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:15 +#: .\templates\xadmin\blocks\model_form.submit_line.wizard.html:31 +#: .\templates\xadmin\includes\submit_line.html:10 +#: .\templates\xadmin\includes\submit_line.html:13 +#: .\templates\xadmin\views\form.html:30 .\templates\xadmin\views\form.html:31 msgid "Save" msgstr "保存" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:7 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:7 msgid "Clean Bookmarks" msgstr "清除书签" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:18 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:18 msgid "No Bookmarks" msgstr "没有书签" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:22 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:22 msgid "New Bookmark" msgstr "新建书签" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:26 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:26 msgid "Save current page as Bookmark" msgstr "将当前页面保存为书签" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:32 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:32 msgid "Enter bookmark title" msgstr "输入书签标题" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:33 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:33 msgid "Waiting" msgstr "请稍侯" -#: templates/xadmin/blocks/model_list.nav_menu.bookmarks.html:33 +#: .\templates\xadmin\blocks\model_list.nav_menu.bookmarks.html:33 msgid "Save Bookmark" msgstr "保存书签" -#: templates/xadmin/blocks/model_list.nav_menu.filters.html:4 +#: .\templates\xadmin\blocks\model_list.nav_menu.filters.html:4 msgid "Filters" msgstr "过滤器" -#: templates/xadmin/blocks/model_list.nav_menu.filters.html:8 +#: .\templates\xadmin\blocks\model_list.nav_menu.filters.html:8 msgid "Clean Filters" msgstr "清除过滤器" -#: templates/xadmin/blocks/model_list.results_bottom.actions.html:19 +#: .\templates\xadmin\blocks\model_list.results_bottom.actions.html:19 msgid "Click here to select the objects across all pages" msgstr "点击此处选择所有页面中包含的对象。" -#: templates/xadmin/blocks/model_list.results_bottom.actions.html:19 +#: .\templates\xadmin\blocks\model_list.results_bottom.actions.html:19 #, python-format msgid "Select all %(total_count)s %(model_name)s" msgstr "选中所有的 %(total_count)s 个 %(model_name)s" -#: templates/xadmin/blocks/model_list.results_bottom.actions.html:20 +#: .\templates\xadmin\blocks\model_list.results_bottom.actions.html:20 msgid "Clear selection" msgstr "清除选中" -#: templates/xadmin/blocks/model_list.results_top.charts.html:4 +#: .\templates\xadmin\blocks\model_list.results_top.charts.html:4 msgid "Charts" msgstr "图表" -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:4 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:8 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:19 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:47 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:4 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:8 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:19 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:47 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:4 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:12 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:41 +#: .\templates\xadmin\import_export\export_action.html:7 +#: .\templates\xadmin\import_export\export_action.html:15 msgid "Export" msgstr "导出" -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:26 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:29 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:32 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:26 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:29 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:32 msgid "Export with table header." msgstr "导出表头" -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:35 -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:38 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:35 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:38 msgid "Export with format." msgstr "导出格式" -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:42 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:42 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:35 msgid "Export all data." msgstr "导出全部数据" -#: templates/xadmin/blocks/model_list.top_toolbar.exports.html:46 -#: templates/xadmin/widgets/base.html:41 +#: .\templates\xadmin\blocks\model_list.top_toolbar.exports.html:46 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:39 +#: .\templates\xadmin\widgets\base.html:41 msgid "Close" msgstr "关闭" -#: templates/xadmin/blocks/model_list.top_toolbar.layouts.html:4 +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:33 +msgid "Export current page data." +msgstr "导出当前页数据" + +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:34 +msgid "Export selected data." +msgstr "导出所选择数据" + +#: .\templates\xadmin\blocks\model_list.top_toolbar.importexport.export.html:36 +msgid "Export header only." +msgstr "仅导出表头" + +#: .\templates\xadmin\blocks\model_list.top_toolbar.layouts.html:4 msgid "Layout" msgstr "布局" -#: templates/xadmin/blocks/model_list.top_toolbar.refresh.html:8 +#: .\templates\xadmin\blocks\model_list.top_toolbar.refresh.html:8 msgid "Clean Refresh" msgstr "清除自动刷新" -#: templates/xadmin/blocks/model_list.top_toolbar.refresh.html:14 +#: .\templates\xadmin\blocks\model_list.top_toolbar.refresh.html:14 #, python-format msgid "Every %(t)s seconds" msgstr "每 %(t)s 秒" -#: templates/xadmin/blocks/model_list.top_toolbar.saveorder.html:4 +#: .\templates\xadmin\blocks\model_list.top_toolbar.saveorder.html:4 msgid "Save Order" msgstr "保存排序" -#: templates/xadmin/edit_inline/blank.html:5 views/detail.py:23 -#: views/edit.py:102 views/list.py:29 +#: .\templates\xadmin\edit_inline\blank.html:5 .\views\detail.py:25 +#: .\views\edit.py:104 .\views\list.py:31 msgid "Null" msgstr "空" -#: templates/xadmin/filters/char.html:13 +#: .\templates\xadmin\filters\char.html:13 msgid "Enter" msgstr "输入" -#: templates/xadmin/filters/date.html:10 templates/xadmin/filters/date.html:13 +#: .\templates\xadmin\filters\date.html:10 +#: .\templates\xadmin\filters\date.html:13 msgid "Choice Date" msgstr "选择日期" -#: templates/xadmin/filters/date.html:18 +#: .\templates\xadmin\filters\date.html:18 msgid "YY" msgstr "年" -#: templates/xadmin/filters/date.html:19 +#: .\templates\xadmin\filters\date.html:19 msgid "year" msgstr "年" -#: templates/xadmin/filters/date.html:22 +#: .\templates\xadmin\filters\date.html:22 msgid "MM" msgstr "月" -#: templates/xadmin/filters/date.html:23 +#: .\templates\xadmin\filters\date.html:23 msgid "month" msgstr "月" -#: templates/xadmin/filters/date.html:26 +#: .\templates\xadmin\filters\date.html:26 msgid "DD" msgstr "日" -#: templates/xadmin/filters/date.html:27 +#: .\templates\xadmin\filters\date.html:27 msgid "day" msgstr "日" -#: templates/xadmin/filters/date.html:29 templates/xadmin/filters/date.html:46 -#: templates/xadmin/filters/date.html:54 -#: templates/xadmin/filters/fk_search.html:24 -#: templates/xadmin/filters/number.html:37 +#: .\templates\xadmin\filters\date.html:29 +#: .\templates\xadmin\filters\date.html:46 +#: .\templates\xadmin\filters\date.html:54 +#: .\templates\xadmin\filters\fk_search.html:24 +#: .\templates\xadmin\filters\number.html:37 msgid "Apply" msgstr "应用" -#: templates/xadmin/filters/date.html:34 +#: .\templates\xadmin\filters\date.html:34 msgid "Date Range" msgstr "日期范围" -#: templates/xadmin/filters/date.html:41 +#: .\templates\xadmin\filters\date.html:41 msgid "Select Date" msgstr "选择日期" -#: templates/xadmin/filters/date.html:42 +#: .\templates\xadmin\filters\date.html:42 msgid "From" msgstr "从" -#: templates/xadmin/filters/date.html:44 +#: .\templates\xadmin\filters\date.html:44 msgid "To" msgstr "到" -#: templates/xadmin/filters/fk_search.html:14 +#: .\templates\xadmin\filters\fk_search.html:14 msgid "Select" msgstr "选择" -#: templates/xadmin/filters/fk_search.html:26 -#: templates/xadmin/filters/number.html:39 +#: .\templates\xadmin\filters\fk_search.html:26 +#: .\templates\xadmin\filters\number.html:39 msgid "Clean" msgstr "清除" -#: templates/xadmin/filters/number.html:17 -#: templates/xadmin/filters/number.html:25 -#: templates/xadmin/filters/number.html:33 +#: .\templates\xadmin\filters\number.html:17 +#: .\templates\xadmin\filters\number.html:25 +#: .\templates\xadmin\filters\number.html:33 msgid "Enter Number" msgstr "输入数字" -#: templates/xadmin/filters/rel.html:3 +#: .\templates\xadmin\filters\rel.html:3 #, python-format msgid " By %(filter_title)s " msgstr " 以 %(filter_title)s" -#: templates/xadmin/forms/transfer.html:4 +#: .\templates\xadmin\forms\transfer.html:4 msgid "Available" msgstr "可用项" -#: templates/xadmin/forms/transfer.html:12 +#: .\templates\xadmin\forms\transfer.html:12 msgid "Click to choose all at once." msgstr "点击一次性选择全部" -#: templates/xadmin/forms/transfer.html:12 +#: .\templates\xadmin\forms\transfer.html:12 msgid "Choose all" msgstr "选择全部" -#: templates/xadmin/forms/transfer.html:16 +#: .\templates\xadmin\forms\transfer.html:16 msgid "Choose" msgstr "选择" -#: templates/xadmin/forms/transfer.html:19 -#: templates/xadmin/widgets/base.html:40 +#: .\templates\xadmin\forms\transfer.html:19 +#: .\templates\xadmin\widgets\base.html:40 msgid "Remove" msgstr "删除" -#: templates/xadmin/forms/transfer.html:23 +#: .\templates\xadmin\forms\transfer.html:23 msgid "Chosen" msgstr "已选项" -#: templates/xadmin/forms/transfer.html:27 +#: .\templates\xadmin\forms\transfer.html:27 msgid "Click to remove all chosen at once." msgstr "点击一次性删除全部" -#: templates/xadmin/forms/transfer.html:27 +#: .\templates\xadmin\forms\transfer.html:27 msgid "Remove all" msgstr "删除全部" -#: templates/xadmin/includes/pagination.html:9 +#: .\templates\xadmin\import_export\export_action.html:43 +#: .\templates\xadmin\import_export\import.html:59 +msgid "Submit" +msgstr "" + +#: .\templates\xadmin\import_export\import.html:26 +msgid "" +"Below is a preview of data to be imported. If you are satisfied with the " +"results, click 'Confirm import'" +msgstr "" + +#: .\templates\xadmin\import_export\import.html:29 +msgid "Confirm import" +msgstr "" + +#: .\templates\xadmin\import_export\import.html:38 +msgid "This importer will import the following fields: " +msgstr "" + +#: .\templates\xadmin\import_export\import.html:67 +msgid "Errors" +msgstr "" + +#: .\templates\xadmin\import_export\import.html:78 +#, fuzzy +#| msgid "Enter Number" +msgid "Line number" +msgstr "输入数字" + +#: .\templates\xadmin\import_export\import.html:88 +#, fuzzy +#| msgid "Previous" +msgid "Preview" +msgstr "上一个" + +#: .\templates\xadmin\import_export\import.html:103 +#, fuzzy +#| msgid "Now" +msgid "New" +msgstr "现在" + +#: .\templates\xadmin\import_export\import.html:105 +msgid "Skipped" +msgstr "" + +#: .\templates\xadmin\import_export\import.html:107 +#: .\templates\xadmin\includes\submit_line.html:24 +#: .\templates\xadmin\views\model_detail.html:28 .\views\delete.py:94 +msgid "Delete" +msgstr "删除" + +#: .\templates\xadmin\includes\pagination.html:9 msgid "Show all" msgstr "显示全部" -#: templates/xadmin/includes/submit_line.html:10 -#: templates/xadmin/includes/submit_line.html:13 -#: templates/xadmin/views/form.html:30 templates/xadmin/views/form.html:31 +#: .\templates\xadmin\includes\submit_line.html:10 +#: .\templates\xadmin\includes\submit_line.html:13 +#: .\templates\xadmin\views\form.html:30 .\templates\xadmin\views\form.html:31 msgid "Saving.." msgstr "保存中.." -#: templates/xadmin/includes/submit_line.html:17 +#: .\templates\xadmin\includes\submit_line.html:17 msgid "Save as new" msgstr "保存为新的" -#: templates/xadmin/includes/submit_line.html:18 +#: .\templates\xadmin\includes\submit_line.html:18 msgid "Save and add another" msgstr "保存并增加另一个" -#: templates/xadmin/includes/submit_line.html:19 +#: .\templates\xadmin\includes\submit_line.html:19 msgid "Save and continue editing" msgstr "保存并继续编辑" -#: templates/xadmin/includes/submit_line.html:24 -#: templates/xadmin/views/model_detail.html:28 views/delete.py:93 -msgid "Delete" -msgstr "删除" - -#: templates/xadmin/views/app_index.html:13 +#: .\templates\xadmin\views\app_index.html:13 #, python-format msgid "%(name)s" msgstr "%(name)s" -#: templates/xadmin/views/batch_change_form.html:11 +#: .\templates\xadmin\views\batch_change_form.html:11 msgid "Change multiple objects" msgstr "修改多个数据" -#: templates/xadmin/views/batch_change_form.html:16 +#: .\templates\xadmin\views\batch_change_form.html:16 #, python-format msgid "Change one %(objects_name)s" msgid_plural "Batch change %(counter)s %(objects_name)s" msgstr[0] "批量修改 %(counter)s 个 %(objects_name)s" -#: templates/xadmin/views/batch_change_form.html:38 +#: .\templates\xadmin\views\batch_change_form.html:38 msgid "Change Multiple" msgstr "修改多个数据" -#: templates/xadmin/views/dashboard.html:15 -#: templates/xadmin/views/dashboard.html:22 -#: templates/xadmin/views/dashboard.html:23 +#: .\templates\xadmin\views\dashboard.html:15 +#: .\templates\xadmin\views\dashboard.html:22 +#: .\templates\xadmin\views\dashboard.html:23 msgid "Add Widget" msgstr "添加小组件" -#: templates/xadmin/views/invalid_setup.html:13 +#: .\templates\xadmin\views\invalid_setup.html:13 msgid "" "Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " @@ -1056,44 +1162,44 @@ msgstr "" "你的数据库安装有误。确保已经创建了相应的数据库表,并确保数据库可被相关的用户" "读取。" -#: templates/xadmin/views/logged_out.html:16 +#: .\templates\xadmin\views\logged_out.html:16 msgid "Logout Success" msgstr "成功退出" -#: templates/xadmin/views/logged_out.html:17 +#: .\templates\xadmin\views\logged_out.html:17 msgid "Thanks for spending some quality time with the Web site today." msgstr "感谢您今天在本站花费了一些宝贵时间。" -#: templates/xadmin/views/logged_out.html:19 +#: .\templates\xadmin\views\logged_out.html:19 msgid "Close Window" msgstr "关闭窗口" -#: templates/xadmin/views/logged_out.html:20 +#: .\templates\xadmin\views\logged_out.html:20 msgid "Log in again" msgstr "重新登录" -#: templates/xadmin/views/login.html:39 views/website.py:38 +#: .\templates\xadmin\views\login.html:39 .\views\website.py:39 msgid "Please Login" msgstr "请登录" -#: templates/xadmin/views/login.html:52 +#: .\templates\xadmin\views\login.html:52 msgid "Username" msgstr "用户名" -#: templates/xadmin/views/login.html:64 +#: .\templates\xadmin\views\login.html:64 msgid "Password" msgstr "密码" -#: templates/xadmin/views/login.html:75 +#: .\templates\xadmin\views\login.html:75 msgid "log in" msgstr "登录" -#: templates/xadmin/views/model_dashboard.html:26 -#: templates/xadmin/views/model_detail.html:25 +#: .\templates\xadmin\views\model_dashboard.html:26 +#: .\templates\xadmin\views\model_detail.html:25 msgid "Edit" msgstr "编辑" -#: templates/xadmin/views/model_delete_confirm.html:11 +#: .\templates\xadmin\views\model_delete_confirm.html:11 #, python-format msgid "" "Deleting the %(verbose_name)s '%(escaped_object)s' would result in deleting " @@ -1103,7 +1209,7 @@ msgstr "" "要删除所选的%(verbose_name)s '%(escaped_object)s' 结果会删除相关对象, 但你的" "账户没有权限删除这类对象:" -#: templates/xadmin/views/model_delete_confirm.html:19 +#: .\templates\xadmin\views\model_delete_confirm.html:19 #, python-format msgid "" "Deleting the %(verbose_name)s '%(escaped_object)s' would require deleting " @@ -1112,7 +1218,7 @@ msgstr "" "要删除所选的%(verbose_name)s '%(escaped_object)s' 将要求删除以下受保护的相关" "对象:" -#: templates/xadmin/views/model_delete_confirm.html:27 +#: .\templates\xadmin\views\model_delete_confirm.html:27 #, python-format msgid "" "Are you sure you want to delete the %(verbose_name)s \"%(escaped_object)s\"? " @@ -1121,21 +1227,21 @@ msgstr "" "请确认要删除选中的%(verbose_name)s \"%(escaped_object)s\"吗?以下所有对象和余" "它们相关的条目将都会被删除:" -#: templates/xadmin/views/model_delete_confirm.html:34 -#: templates/xadmin/views/model_delete_selected_confirm.html:49 +#: .\templates\xadmin\views\model_delete_confirm.html:34 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:49 msgid "Yes, I'm sure" msgstr "是的,我确定" -#: templates/xadmin/views/model_delete_confirm.html:35 -#: templates/xadmin/views/model_delete_selected_confirm.html:50 +#: .\templates\xadmin\views\model_delete_confirm.html:35 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:50 msgid "Cancel" msgstr "取消" -#: templates/xadmin/views/model_delete_selected_confirm.html:10 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:10 msgid "Delete multiple objects" msgstr "删除多个对象" -#: templates/xadmin/views/model_delete_selected_confirm.html:18 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:18 #, python-format msgid "" "Deleting the selected %(objects_name)s would result in deleting related " @@ -1145,14 +1251,14 @@ msgstr "" "要删除所选的 %(objects_name)s 结果会删除相关对象, 但你的账户没有权限删除这类" "对象:" -#: templates/xadmin/views/model_delete_selected_confirm.html:26 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:26 #, python-format msgid "" "Deleting the selected %(objects_name)s would require deleting the following " "protected related objects:" msgstr "要删除所选的 %(objects_name)s, 将要求删除以下受保护的相关对象:" -#: templates/xadmin/views/model_delete_selected_confirm.html:34 +#: .\templates\xadmin\views\model_delete_selected_confirm.html:34 #, python-format msgid "" "Are you sure you want to delete the selected %(objects_name)s? All of the " @@ -1161,261 +1267,262 @@ msgstr "" "请确认要删除选中的 %(objects_name)s 吗?以下所有对象和余它们相关的条目将都会" "被删除:" -#: templates/xadmin/views/model_history.html:26 +#: .\templates\xadmin\views\model_history.html:26 msgid "Diff" msgstr "不同" -#: templates/xadmin/views/model_history.html:27 -#: templates/xadmin/views/recover_list.html:25 +#: .\templates\xadmin\views\model_history.html:27 +#: .\templates\xadmin\views\recover_list.html:25 msgid "Date/time" msgstr "日期/时间" -#: templates/xadmin/views/model_history.html:28 +#: .\templates\xadmin\views\model_history.html:28 msgid "User" msgstr "用户" -#: templates/xadmin/views/model_history.html:29 +#: .\templates\xadmin\views\model_history.html:29 msgid "Comment" msgstr "注释" -#: templates/xadmin/views/model_history.html:54 +#: .\templates\xadmin\views\model_history.html:54 msgid "Diff Select Versions" msgstr "查看所选版本的不同" -#: templates/xadmin/views/model_history.html:58 +#: .\templates\xadmin\views\model_history.html:58 msgid "" "This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "该对象没有变更历史记录。可能从未通过这个管理站点添加。" -#: templates/xadmin/views/model_list.html:29 +#: .\templates\xadmin\views\model_list.html:29 #, python-format msgid "Add %(name)s" msgstr "增加 %(name)s" -#: templates/xadmin/views/model_list.html:39 +#: .\templates\xadmin\views\model_list.html:39 msgid "Columns" msgstr "显示列" -#: templates/xadmin/views/model_list.html:42 +#: .\templates\xadmin\views\model_list.html:42 msgid "Restore Selected" msgstr "恢复显示列" -#: templates/xadmin/views/model_list.html:147 -#: templates/xadmin/widgets/list.html:33 +#: .\templates\xadmin\views\model_list.html:147 +#: .\templates\xadmin\widgets\list.html:33 msgid "Empty list" msgstr "空列表" -#: templates/xadmin/views/recover_form.html:20 +#: .\templates\xadmin\views\recover_form.html:20 msgid "Press the recover button below to recover this version of the object." msgstr "点击下方的“还原”按钮还原到该版本。" -#: templates/xadmin/views/recover_list.html:19 +#: .\templates\xadmin\views\recover_list.html:19 msgid "" "Choose a date from the list below to recover a deleted version of an object." msgstr "从以下列表中选择一个日期来恢复删除掉的数据。" -#: templates/xadmin/views/recover_list.html:39 +#: .\templates\xadmin\views\recover_list.html:39 msgid "There are no deleted objects to recover." msgstr "没有已删除的数据。" -#: templates/xadmin/views/revision_diff.html:12 -#: templates/xadmin/views/revision_diff.html:17 +#: .\templates\xadmin\views\revision_diff.html:12 +#: .\templates\xadmin\views\revision_diff.html:17 #, python-format msgid "Diff %(verbose_name)s" msgstr "%(verbose_name)s的不同" -#: templates/xadmin/views/revision_diff.html:25 +#: .\templates\xadmin\views\revision_diff.html:25 msgid "Field" msgstr "字段" -#: templates/xadmin/views/revision_diff.html:26 +#: .\templates\xadmin\views\revision_diff.html:26 msgid "Version A" msgstr "版本A" -#: templates/xadmin/views/revision_diff.html:27 +#: .\templates\xadmin\views\revision_diff.html:27 msgid "Version B" msgstr "版本B" -#: templates/xadmin/views/revision_diff.html:39 +#: .\templates\xadmin\views\revision_diff.html:39 msgid "Revert to" msgstr "还原到" -#: templates/xadmin/views/revision_diff.html:40 -#: templates/xadmin/views/revision_diff.html:41 +#: .\templates\xadmin\views\revision_diff.html:40 +#: .\templates\xadmin\views\revision_diff.html:41 msgid "Revert" msgstr "还原" -#: templates/xadmin/views/revision_form.html:16 +#: .\templates\xadmin\views\revision_form.html:16 #, python-format msgid "Revert %(verbose_name)s" msgstr "还原%(verbose_name)s" -#: templates/xadmin/views/revision_form.html:21 +#: .\templates\xadmin\views\revision_form.html:21 msgid "Press the revert button below to revert to this version of the object." msgstr "点击下方的“还原”按钮还原数据到该版本。" -#: templates/xadmin/views/revision_form.html:27 +#: .\templates\xadmin\views\revision_form.html:27 msgid "Revert this revision" msgstr "还原该版本" -#: templates/xadmin/widgets/addform.html:14 +#: .\templates\xadmin\widgets\addform.html:14 msgid "Success" msgstr "成功" -#: templates/xadmin/widgets/addform.html:14 +#: .\templates\xadmin\widgets\addform.html:14 msgid "Add success, click edit to edit." msgstr "添加成功,点击 编辑 可以继续修改该数据。" -#: templates/xadmin/widgets/addform.html:17 +#: .\templates\xadmin\widgets\addform.html:17 msgid "Quick Add" msgstr "快速添加" -#: templates/xadmin/widgets/base.html:31 +#: .\templates\xadmin\widgets\base.html:31 msgid "Widget Options" msgstr "小组件设置项" -#: templates/xadmin/widgets/base.html:42 +#: .\templates\xadmin\widgets\base.html:42 msgid "Save changes" msgstr "保存变更" -#: views/base.py:315 +#: .\views\base.py:320 msgid "Django Xadmin" msgstr "Django Xadmin" -#: views/base.py:316 +#: .\views\base.py:321 msgid "my-company.inc" msgstr "我的公司" -#: views/dashboard.py:186 +#: .\views\dashboard.py:189 msgid "Widget ID" msgstr "小组件ID" -#: views/dashboard.py:187 +#: .\views\dashboard.py:190 msgid "Widget Title" msgstr "小组件标题" -#: views/dashboard.py:252 +#: .\views\dashboard.py:255 msgid "Html Content Widget, can write any html content in widget." msgstr "HTML内容小组件,可以编写任意HTML内容。" -#: views/dashboard.py:255 +#: .\views\dashboard.py:258 msgid "Html Content" msgstr "HTML内容" -#: views/dashboard.py:318 +#: .\views\dashboard.py:321 msgid "Target Model" msgstr "目标数据" -#: views/dashboard.py:369 +#: .\views\dashboard.py:372 msgid "Quick button Widget, quickly open any page." msgstr "快捷按钮小组件,可以快速打开任意页面。" -#: views/dashboard.py:371 +#: .\views\dashboard.py:374 msgid "Quick Buttons" msgstr "快速按钮" -#: views/dashboard.py:416 +#: .\views\dashboard.py:419 msgid "Any Objects list Widget." msgstr "数据列表小组件" -#: views/dashboard.py:456 +#: .\views\dashboard.py:459 msgid "Add any model object Widget." msgstr "添加任意数据的小组件" -#: views/dashboard.py:492 +#: .\views\dashboard.py:495 msgid "Dashboard" msgstr "仪表盘" -#: views/dashboard.py:633 +#: .\views\dashboard.py:636 #, python-format msgid "%s Dashboard" msgstr "%s 主页" -#: views/delete.py:103 +#: .\views\delete.py:104 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." msgstr "%(name)s \"%(obj)s\" 删除成功。" -#: views/detail.py:173 views/edit.py:211 views/form.py:72 +#: .\views\detail.py:173 .\views\edit.py:216 .\views\form.py:74 msgid "Other Fields" msgstr "其它字段" -#: views/detail.py:235 +#: .\views\detail.py:235 #, python-format msgid "%s Detail" msgstr "%s详情" -#: views/edit.py:253 +#: .\views\edit.py:258 msgid "Added." msgstr "已添加" -#: views/edit.py:255 +#: .\views\edit.py:260 +#, python-format msgid "Changed %s." msgstr "修改 %s" -#: views/edit.py:255 +#: .\views\edit.py:260 msgid "and" msgstr "和" -#: views/edit.py:258 +#: .\views\edit.py:263 msgid "No fields changed." msgstr "没有数据变化" -#: views/edit.py:420 +#: .\views\edit.py:426 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "%(name)s \"%(obj)s\" 添加成功。" -#: views/edit.py:425 views/edit.py:520 +#: .\views\edit.py:431 .\views\edit.py:526 msgid "You may edit it again below." msgstr "你可以在下面再次编辑它。" -#: views/edit.py:429 views/edit.py:523 +#: .\views\edit.py:435 .\views\edit.py:529 #, python-format msgid "You may add another %s below." msgstr "你可以在下面增加另一个 %s 。" -#: views/edit.py:471 +#: .\views\edit.py:477 #, python-format msgid "Change %s" msgstr "修改 %s" -#: views/edit.py:516 +#: .\views\edit.py:522 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." msgstr "%(name)s \"%(obj)s\" 修改成功。" -#: views/form.py:165 +#: .\views\form.py:168 #, python-format msgid "The %s was changed successfully." msgstr "%s 修改成功。" -#: views/list.py:199 +#: .\views\list.py:192 msgid "Database error" msgstr "数据库错误" -#: views/list.py:373 +#: .\views\list.py:369 #, python-format msgid "%s List" msgstr "%s列表" -#: views/list.py:499 +#: .\views\list.py:498 msgid "Sort ASC" msgstr "正序" -#: views/list.py:500 +#: .\views\list.py:499 msgid "Sort DESC" msgstr "倒序" -#: views/list.py:504 +#: .\views\list.py:503 msgid "Cancel Sort" msgstr "取消排序" -#: views/website.py:16 +#: .\views\website.py:17 msgid "Main Dashboard" msgstr "主页面" -#: widgets.py:48 +#: .\widgets.py:53 .\widgets.py:81 msgid "Now" msgstr "现在" diff --git a/xadmin/plugins/__init__.py b/xadmin/plugins/__init__.py index c4d2efc49..e570dfe67 100644 --- a/xadmin/plugins/__init__.py +++ b/xadmin/plugins/__init__.py @@ -27,7 +27,8 @@ 'sitemenu', 'language', 'quickfilter', - 'sortablelist' + 'sortablelist', + 'importexport' ) diff --git a/xadmin/plugins/importexport.py b/xadmin/plugins/importexport.py new file mode 100644 index 000000000..90fe10c47 --- /dev/null +++ b/xadmin/plugins/importexport.py @@ -0,0 +1,460 @@ +#!/usr/bin/env python +# encoding=utf-8 +""" +Author:zcyuefan +Topic:django-import-export plugin for xadmin to help importing and exporting data using .csv/.xls/.../.json files + +Use: ++++ settings.py +++ +INSTALLED_APPS = ( + ... + 'import_export', +) + ++++ model.py +++ +from django.db import models + +class Foo(models.Model): + name = models.CharField(max_length=64) + description = models.TextField() + ++++ adminx.py +++ +import xadmin +from import_export import resources +from .models import Foo + +class FooResource(resources.ModelResource): + + class Meta: + model = Foo + # fields = ('name', 'description',) + # exclude = () + + +@xadmin.sites.register(Foo) +class FooAdmin(object): + import_export_args = {'import_resource_class': FooResource, 'export_resource_class': FooResource} + +++++++++++++++++ +More info about django-import-export please refer https://github.com/django-import-export/django-import-export +""" +from datetime import datetime +from django.template import loader +from xadmin.plugins.utils import get_context_dict +from xadmin.sites import site +from xadmin.views import BaseAdminPlugin, ListAdminView, ModelAdminView +from xadmin.views.base import csrf_protect_m, filter_hook +from django.db import transaction +from import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS +from import_export.resources import modelresource_factory +from import_export.forms import ( + ImportForm, + ConfirmImportForm, + ExportForm, +) +from import_export.results import RowResult +from import_export.signals import post_export, post_import +try: + from django.utils.encoding import force_text +except ImportError: + from django.utils.encoding import force_unicode as force_text +from django.utils.translation import ugettext_lazy as _ +from django.template.response import TemplateResponse +from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION +from django.contrib.contenttypes.models import ContentType +from django.contrib import messages +from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied +from django.http import HttpResponseRedirect, HttpResponse + + +class ImportMenuPlugin(BaseAdminPlugin): + import_export_args = {} + + def init_request(self, *args, **kwargs): + return bool(self.import_export_args.get('import_resource_class')) + + def block_top_toolbar(self, context, nodes): + has_change_perm = self.has_model_perm(self.model, 'change') + has_add_perm = self.has_model_perm(self.model, 'add') + if has_change_perm and has_add_perm: + model_info = (self.opts.app_label, self.opts.model_name) + import_url = reverse('xadmin:%s_%s_import' % model_info, current_app=self.admin_site.name) + context = get_context_dict(context or {}) # no error! + context.update({ + 'import_url': import_url, + }) + nodes.append(loader.render_to_string('xadmin/blocks/model_list.top_toolbar.importexport.import.html', + context=context)) + + +class ImportBaseView(ModelAdminView): + """ + """ + resource_class = None + import_export_args = {} + #: template for import view + import_template_name = 'xadmin/import_export/import.html' + #: resource class + #: available import formats + formats = DEFAULT_FORMATS + #: import data encoding + from_encoding = "utf-8" + skip_admin_log = None + # storage class for saving temporary files + tmp_storage_class = None + + def get_skip_admin_log(self): + if self.skip_admin_log is None: + return SKIP_ADMIN_LOG + else: + return self.skip_admin_log + + def get_tmp_storage_class(self): + if self.tmp_storage_class is None: + return TMP_STORAGE_CLASS + else: + return self.tmp_storage_class + + def get_resource_kwargs(self, request, *args, **kwargs): + return {} + + def get_import_resource_kwargs(self, request, *args, **kwargs): + return self.get_resource_kwargs(request, *args, **kwargs) + + def get_resource_class(self, usage): + if usage == 'import': + return self.import_export_args.get('import_resource_class') if self.import_export_args.get( + 'import_resource_class') else modelresource_factory(self.model) + elif usage == 'export': + return self.import_export_args.get('export_resource_class') if self.import_export_args.get( + 'export_resource_class') else modelresource_factory(self.model) + else: + return modelresource_factory(self.model) + + def get_import_resource_class(self): + """ + Returns ResourceClass to use for import. + """ + return self.process_import_resource(self.get_resource_class(usage='import')) + + def process_import_resource(self, resource): + """ + Returns processed ResourceClass to use for import. + Override to custom your own process + """ + return resource + + def get_import_formats(self): + """ + Returns available import formats. + """ + return [f for f in self.formats if f().can_import()] + + +class ImportView(ImportBaseView): + def get_media(self): + media = super(ImportView, self).get_media() + media = media + self.vendor('xadmin.plugin.importexport.css') + return media + + @filter_hook + def get(self, request, *args, **kwargs): + if not (self.has_change_permission() and self.has_add_permission()): + raise PermissionDenied + + resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs)) + + context = super(ImportView, self).get_context() + + import_formats = self.get_import_formats() + form = ImportForm(import_formats, + request.POST or None, + request.FILES or None) + + context['title'] = _("Import") + ' ' + self.opts.verbose_name + context['form'] = form + context['opts'] = self.model._meta + context['fields'] = [f.column_name for f in resource.get_user_visible_fields()] + + request.current_app = self.admin_site.name + return TemplateResponse(request, [self.import_template_name], + context) + + @filter_hook + @csrf_protect_m + @transaction.atomic + def post(self, request, *args, **kwargs): + """ + Perform a dry_run of the import to make sure the import will not + result in errors. If there where no error, save the user + uploaded file to a local temp file that will be used by + 'process_import' for the actual import. + """ + if not (self.has_change_permission() and self.has_add_permission()): + raise PermissionDenied + + resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs)) + + context = super(ImportView, self).get_context() + + import_formats = self.get_import_formats() + form = ImportForm(import_formats, + request.POST or None, + request.FILES or None) + + if request.POST and form.is_valid(): + input_format = import_formats[ + int(form.cleaned_data['input_format']) + ]() + import_file = form.cleaned_data['import_file'] + # first always write the uploaded file to disk as it may be a + # memory file or else based on settings upload handlers + tmp_storage = self.get_tmp_storage_class()() + data = bytes() + for chunk in import_file.chunks(): + data += chunk + + tmp_storage.save(data, input_format.get_read_mode()) + + # then read the file, using the proper format-specific mode + # warning, big files may exceed memory + try: + data = tmp_storage.read(input_format.get_read_mode()) + if not input_format.is_binary() and self.from_encoding: + data = force_text(data, self.from_encoding) + dataset = input_format.create_dataset(data) + except UnicodeDecodeError as e: + return HttpResponse(_(u"

Imported file has a wrong encoding: %s

" % e)) + except Exception as e: + return HttpResponse(_(u"

%s encountered while trying to read file: %s

" % (type(e).__name__, + import_file.name))) + result = resource.import_data(dataset, dry_run=True, + raise_errors=False, + file_name=import_file.name, + user=request.user) + + context['result'] = result + + if not result.has_errors(): + context['confirm_form'] = ConfirmImportForm(initial={ + 'import_file_name': tmp_storage.name, + 'original_file_name': import_file.name, + 'input_format': form.cleaned_data['input_format'], + }) + + context['title'] = _("Import") + ' ' + self.opts.verbose_name + context['form'] = form + context['opts'] = self.model._meta + context['fields'] = [f.column_name for f in resource.get_user_visible_fields()] + + request.current_app = self.admin_site.name + return TemplateResponse(request, [self.import_template_name], + context) + + +class ImportProcessView(ImportBaseView): + @filter_hook + @csrf_protect_m + @transaction.atomic + def post(self, request, *args, **kwargs): + """ + Perform the actual import action (after the user has confirmed he + wishes to import) + """ + resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs)) + + confirm_form = ConfirmImportForm(request.POST) + if confirm_form.is_valid(): + import_formats = self.get_import_formats() + input_format = import_formats[ + int(confirm_form.cleaned_data['input_format']) + ]() + tmp_storage = self.get_tmp_storage_class()(name=confirm_form.cleaned_data['import_file_name']) + data = tmp_storage.read(input_format.get_read_mode()) + if not input_format.is_binary() and self.from_encoding: + data = force_text(data, self.from_encoding) + dataset = input_format.create_dataset(data) + + result = resource.import_data(dataset, dry_run=False, + raise_errors=True, + file_name=confirm_form.cleaned_data['original_file_name'], + user=request.user) + + if not self.get_skip_admin_log(): + # Add imported objects to LogEntry + logentry_map = { + RowResult.IMPORT_TYPE_NEW: ADDITION, + RowResult.IMPORT_TYPE_UPDATE: CHANGE, + RowResult.IMPORT_TYPE_DELETE: DELETION, + } + content_type_id = ContentType.objects.get_for_model(self.model).pk + for row in result: + if row.import_type != row.IMPORT_TYPE_ERROR and row.import_type != row.IMPORT_TYPE_SKIP: + LogEntry.objects.log_action( + user_id=request.user.pk, + content_type_id=content_type_id, + object_id=row.object_id, + object_repr=row.object_repr, + action_flag=logentry_map[row.import_type], + change_message="%s through import_export" % row.import_type, + ) + success_message = str(_(u'Import finished')) + ' , ' + str(_(u'Add')) + ' : %d' % result.totals[ + RowResult.IMPORT_TYPE_NEW] + ' , ' + str(_(u'Update')) + ' : %d' % result.totals[ + RowResult.IMPORT_TYPE_UPDATE] + + messages.success(request, success_message) + tmp_storage.remove() + + post_import.send(sender=None, model=self.model) + model_info = (self.opts.app_label, self.opts.model_name) + url = reverse('xadmin:%s_%s_changelist' % model_info, + current_app=self.admin_site.name) + return HttpResponseRedirect(url) + + +class ExportMixin(object): + #: resource class + resource_class = None + #: template for change_list view + change_list_template = None + import_export_args = {} + #: template for export view + # export_template_name = 'xadmin/import_export/export.html' + #: available export formats + formats = DEFAULT_FORMATS + #: export data encoding + to_encoding = "utf-8" + list_select_related = None + + def get_resource_kwargs(self, request, *args, **kwargs): + return {} + + def get_export_resource_kwargs(self, request, *args, **kwargs): + return self.get_resource_kwargs(request, *args, **kwargs) + + def get_resource_class(self, usage): + if usage == 'import': + return self.import_export_args.get('import_resource_class') if self.import_export_args.get( + 'import_resource_class') else modelresource_factory(self.model) + elif usage == 'export': + return self.import_export_args.get('export_resource_class') if self.import_export_args.get( + 'export_resource_class') else modelresource_factory(self.model) + else: + return modelresource_factory(self.model) + + def get_export_resource_class(self): + """ + Returns ResourceClass to use for export. + """ + return self.get_resource_class(usage='export') + + def get_export_formats(self): + """ + Returns available export formats. + """ + return [f for f in self.formats if f().can_export()] + + def get_export_filename(self, file_format): + date_str = datetime.now().strftime('%Y-%m-%d-%H%M%S') + filename = "%s-%s.%s" % (self.opts.verbose_name.encode('utf-8'), + date_str, + file_format.get_extension()) + return filename + + def get_export_queryset(self, request, context): + """ + Returns export queryset. + + Default implementation respects applied search and filters. + """ + # scope = self.request.POST.get('_select_across', False) == '1' + scope = request.GET.get('scope') + select_across = request.GET.get('_select_across', False) == '1' + selected = request.GET.get('_selected_actions', '') + if scope == 'all': + queryset = self.admin_view.queryset() + elif scope == 'header_only': + queryset = [] + elif scope == 'selected': + if not select_across: + selected_pk = selected.split(',') + queryset = self.admin_view.queryset().filter(pk__in=selected_pk) + else: + queryset = self.admin_view.queryset() + else: + queryset = [r['object'] for r in context['results']] + return queryset + + def get_export_data(self, file_format, queryset, *args, **kwargs): + """ + Returns file_format representation for given queryset. + """ + request = kwargs.pop("request") + resource_class = self.get_export_resource_class() + data = resource_class(**self.get_export_resource_kwargs(request)).export(queryset, *args, **kwargs) + export_data = file_format.export_data(data) + return export_data + + +class ExportMenuPlugin(ExportMixin, BaseAdminPlugin): + import_export_args = {} + + # Media + def get_media(self, media): + return media + self.vendor('xadmin.plugin.importexport.css', 'xadmin.plugin.importexport.js') + + def init_request(self, *args, **kwargs): + return bool(self.import_export_args.get('export_resource_class')) + + def block_top_toolbar(self, context, nodes): + formats = self.get_export_formats() + form = ExportForm(formats) + + context = get_context_dict(context or {}) # no error! + context.update({ + 'form': form, + 'opts': self.opts, + 'form_params': self.admin_view.get_form_params({'_action_': 'export'}), + }) + nodes.append(loader.render_to_string('xadmin/blocks/model_list.top_toolbar.importexport.export.html', + context=context)) + + +class ExportPlugin(ExportMixin, BaseAdminPlugin): + + def init_request(self, *args, **kwargs): + return self.request.GET.get('_action_') == 'export' + + def get_response(self, response, context, *args, **kwargs): + has_view_perm = self.has_model_perm(self.model, 'view') + if not has_view_perm: + raise PermissionDenied + + export_format = self.request.GET.get('file_format') + + if not export_format: + messages.warning(self.request, _('You must select an export format.')) + else: + formats = self.get_export_formats() + file_format = formats[int(export_format)]() + queryset = self.get_export_queryset(self.request, context) + export_data = self.get_export_data(file_format, queryset, request=self.request) + content_type = file_format.get_content_type() + # Django 1.7 uses the content_type kwarg instead of mimetype + try: + response = HttpResponse(export_data, content_type=content_type) + except TypeError: + response = HttpResponse(export_data, mimetype=content_type) + response['Content-Disposition'] = 'attachment; filename=%s' % ( + self.get_export_filename(file_format), + ) + post_export.send(sender=None, model=self.model) + return response + + +site.register_modelview(r'^import/$', ImportView, name='%s_%s_import') +site.register_modelview(r'^process_import/$', ImportProcessView, name='%s_%s_process_import') +site.register_plugin(ImportMenuPlugin, ListAdminView) +site.register_plugin(ExportMenuPlugin, ListAdminView) +site.register_plugin(ExportPlugin, ListAdminView) diff --git a/xadmin/static/xadmin/css/xadmin.plugin.importexport.css b/xadmin/static/xadmin/css/xadmin.plugin.importexport.css new file mode 100644 index 000000000..72d8cd6f4 --- /dev/null +++ b/xadmin/static/xadmin/css/xadmin.plugin.importexport.css @@ -0,0 +1,21 @@ +/* FORM ROWS */ + +.form-row { + overflow: hidden; + padding: 10px; + font-size: 13px; + border-bottom: 1px solid #eee; +} + +.form-row img, .form-row input { + vertical-align: middle; +} + +.form-row label input[type="checkbox"] { + margin-top: 0; + vertical-align: 0; +} + +form .form-row p { + padding-left: 0; +} diff --git a/xadmin/static/xadmin/js/xadmin.plugin.importexport.js b/xadmin/static/xadmin/js/xadmin.plugin.importexport.js new file mode 100644 index 000000000..4f76c4d41 --- /dev/null +++ b/xadmin/static/xadmin/js/xadmin.plugin.importexport.js @@ -0,0 +1,12 @@ +$("#export-menu").click(function () { + $("input[name='_select_across']").val($("input[name='select_across']").val()); + if (0 == $("input[name='select_across']").val()) { + var selectedRecords = []; + $.each($('.action-select'), function () { + if (true == $(this).prop('checked')) { + selectedRecords.push($(this).val()); + } + }); + $("input[name='_selected_actions']").val(selectedRecords.join(',')); + } +}); diff --git a/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.export.html b/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.export.html new file mode 100644 index 000000000..07d301d3d --- /dev/null +++ b/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.export.html @@ -0,0 +1,47 @@ +{% load i18n %} +
+ + {% trans "Export" %} + + +
\ No newline at end of file diff --git a/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.import.html b/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.import.html new file mode 100644 index 000000000..ee358aedc --- /dev/null +++ b/xadmin/templates/xadmin/blocks/model_list.top_toolbar.importexport.import.html @@ -0,0 +1,6 @@ +{% load i18n %} +
+ + {% trans "Import" %} + +
\ No newline at end of file diff --git a/xadmin/templates/xadmin/import_export/export_action.html b/xadmin/templates/xadmin/import_export/export_action.html new file mode 100644 index 000000000..7907fa7e2 --- /dev/null +++ b/xadmin/templates/xadmin/import_export/export_action.html @@ -0,0 +1,46 @@ +{% extends 'xadmin/base_site.html' %} +{% load i18n %} +{% load admin_urls %} +{% load import_export_tags %} + +{% block breadcrumbs_last %} +{% trans "Export" %} +{% endblock %} +{% load xadmin_tags %} + +{% block breadcrumbs %} + +{% endblock %} +{% block nav_title %} + {{title}} +{% endblock %} + +{% block content %} +
+ {% csrf_token %} + +
+ {% for field in form %} +
+ {{ field.errors }} + + {{ field.label_tag }} + + {{ field }} + + {% if field.field.help_text %} +

{{ field.field.help_text|safe }}

+ {% endif %} +
+ {% endfor %} +
+ +
+ +
+
+{% endblock %} diff --git a/xadmin/templates/xadmin/import_export/import.html b/xadmin/templates/xadmin/import_export/import.html new file mode 100644 index 000000000..ce6276d45 --- /dev/null +++ b/xadmin/templates/xadmin/import_export/import.html @@ -0,0 +1,123 @@ +{% extends 'xadmin/base_site.html' %} +{% load i18n %} +{% load admin_urls %} +{% load import_export_tags %} + +{% trans "Import" %} +{% load xadmin_tags %} + +{% block breadcrumbs %} + +{% endblock %} +{% block nav_title %} + {{title}} +{% endblock %} + +{% block content %} +{% if confirm_form %} +
+ {% csrf_token %} + {{ confirm_form.as_p }} +

+ {% trans "Below is a preview of data to be imported. If you are satisfied with the results, click 'Confirm import'" %} +

+
+ +
+
+ +{% else %} +
+ {% csrf_token %} + +

+ {% trans "This importer will import the following fields: " %} + {{ fields|join:", " }} +

+ +
+ {% for field in form %} +
+ {{ field.errors }} + + {{ field.label_tag }} + + {{ field }} + + {% if field.field.help_text %} +

{{ field.field.help_text|safe }}

+ {% endif %} +
+ {% endfor %} +
+ +
+ +
+
+{% endif %} + +{% if result %} + + {% if result.has_errors %} +

{% trans "Errors" %}

+ + {% else %} + +

+ {% trans "Preview" %} +

+ + + + + {% for field in result.diff_headers %} + + {% endfor %} + + + {% for row in result.rows %} + + + {% for field in row.diff %} + + {% endfor %} + + {% endfor %} +
{{ field }}
+ {% if row.import_type == 'new' %} + {% trans "New" %} + {% elif row.import_type == 'skip' %} + {% trans "Skipped" %} + {% elif row.import_type == 'delete' %} + {% trans "Delete" %} + {% elif row.import_type == 'update' %} + {% trans "Update" %} + {% endif %} + + {{ field }} +
+ {% endif %} + + {% endif %} +{% endblock %}