Compare commits

...

80 commits

Author SHA1 Message Date
Benedikt Willi
765ac7a19d Rewrote JavaScript to be compatible with EcmaScript < 6. Clarified js docs in version_compare.js 2019-11-11 10:41:14 +01:00
Benedikt Willi
57d0a348bf Updated to work with Wagtail 2.7. 2019-11-08 16:39:17 +01:00
Benedikt Willi
154acd006c Added tests for new Wagtail 2.7 and Python 3.8 2019-11-08 16:39:00 +01:00
Diogo Marques
18ed10a2a9
Merge pull request #258 from easherma/bindtomodel-version
different bindto model function depending on wagtail version
2019-10-25 19:26:16 +01:00
Eric Sherman
a92d287a6e nested if 2019-10-25 13:17:44 -05:00
Eric Sherman
187e524ed2 import verision constant 2019-10-25 13:14:29 -05:00
Eric Sherman
e742a93f4c different bindto model function depending on wagtail version 2019-10-25 13:06:15 -05:00
Diogo Marques
1366c86151
Merge pull request #252 from Hopiu/master
Fixed missing copy buttons for Wagtail 2.6.x.
2019-10-23 12:27:43 +01:00
Diogo Marques
8b945cec37
Merge branch 'master' into master 2019-10-23 12:22:29 +01:00
Diogo Marques
40b2b01648 Bumped version 0.10.5 2019-09-09 15:20:50 +01:00
Diogo Marques
a170f7a213 Hotfix: include templates in instalation 2019-09-09 15:16:14 +01:00
Diogo Marques
4456f6f902 Bumped version 0.10.4 2019-09-09 14:42:06 +01:00
Diogo Marques
80def8ecde
Merge pull request #253 from floese/master
fixed import statement to match wagtails reorganised wagtail.admin.fo…
2019-09-09 14:21:55 +01:00
Floroni
29404348cb fixed import statement to match wagtails reorganised wagtail.admin.forms module 2019-09-09 11:40:44 +02:00
Miguel Silva
1495f7278a Added the templates directory to the installation 2019-09-09 10:19:27 +01:00
Miguel Silva
6fb4725f65 Merge remote-tracking branch 'upstream/master' 2019-09-06 15:39:38 +01:00
Miguel Silva
3191abde46 Merge branch 'ghost-pull_request-fix_page_copy' 2019-09-06 15:32:35 +01:00
Miguel Silva
86c4711e48 Raise ValidationError if save has errors 2019-09-06 15:15:41 +01:00
Miguel Silva
6cbd98ee54 Fixed imports 2019-09-06 14:19:57 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
3034d5807c Corrected template rendering "can publish" field twice 2019-09-06 12:43:40 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
07e0e98b47 Use SlugField and old slug to fill new page slugs 2019-09-06 12:43:40 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
88e5895c5b Removed hardcoded language codes, replaced by settings.LANGUAGES 2019-09-06 12:43:40 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
55fff643d3 Patched copy view from wagtail in before_page_copy hook 2019-09-06 12:43:40 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
d630f9c988 Created new form to override Wagtail's CopyForm 2019-09-06 12:42:24 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
fe0cd4c0b6 Template now load titles and slugs fields in all languages 2019-09-06 12:42:24 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
8a1a9eb94b Added copy form template to override the Wagtail one 2019-09-06 12:42:24 +01:00
Erwhann-Rouge Guilhem MAS-PAITRAULT
425438acd1 Added primitive hook to before_page_copy 2019-09-06 12:42:24 +01:00
Benedikt Willi
ff8a75479c Added tox.ini environments 2019-09-05 17:06:06 +02:00
Benedikt Willi
af888092bf Fixed missing copy buttons for Wagtail 2.6.x. 2019-09-05 16:33:04 +02:00
Diogo Marques
34e67bd0ed
Merge pull request #251 from Hopiu/master
Updated travis-ci configuration
2019-09-05 14:48:50 +01:00
Benedikt Willi
da68dd1e40 Updated travis-ci configuration 2019-09-05 14:54:47 +02:00
Miguel Silva
eb5eee8092
Merge pull request #248 from onkruid/static-tag
Replace direct use of STATIC_URL with static templatetag
2019-07-23 15:59:15 +01:00
Thomas Lagae
5ec163b020 Replace direct use of STATIC_URL with static templatetag 2019-07-20 17:53:39 +02:00
Alexandre Silva
75facc5f22 Bump version: 0.10.1 → 0.10.2 2019-06-06 17:22:18 +01:00
Alexandre Silva
f9afc8dd17 Adds dependencies for release process 2019-06-06 17:18:27 +01:00
Diogo Marques
f52993d73c
Merge pull request #239 from lucasmoeskops/fix-edit-handler-iterator
Fix invalid iterator
2019-06-06 16:29:31 +01:00
Diogo Marques
58fdc7148b
Merge pull request #246 from msilvapor/master
Allows patching admin interface for all models registered for translation
2019-06-06 16:23:29 +01:00
Miguel Silva
f1b04f9202 Hotfix: fixes psycopg2 binary installation 2019-06-06 16:14:48 +01:00
Miguel Silva
30a6ada4de Fixes psycopg2 bug 2019-06-06 16:06:43 +01:00
Miguel Silva
bbe19792c9 Allows patching admin interface for all models registered for translation 2019-06-06 15:59:25 +01:00
Miguel Silva
640d5202c9 Linter fix 2019-06-06 15:58:12 +01:00
Diogo Marques
5ef04f30f2 Fixes #242
Set a default url_path for old instances
2019-05-09 11:41:34 +01:00
Lucas Moeskops
b883f051f3 Fix invalid iterator 2019-04-01 15:46:37 +02:00
Diogo Marques
8deb0424f1 Updated docs;
bump bersion 0.10.1
2019-03-21 17:10:58 +00:00
Diogo Marques
7d9b42f542
Merge pull request #235 from Pomax/patch-1
Fixes #234
2019-03-21 12:04:09 +00:00
Diogo Marques
4f0b07789c Fixes error on apps.py: TypeError: can only concatenate list (not "tuple") to list 2019-03-21 10:47:32 +00:00
Pomax
372a2f31df
Update apps.py 2019-03-19 11:36:42 -07:00
Pomax
304554f405 Update apps.py
From https://github.com/infoportugal/wagtail-modeltranslation/issues/234#issuecomment-474096604, the timing between django-modeltranslation and wagtail-modeltranslation leads to wmt updating the settings variable for dmt too late (namely, after dmt already read it out). This PR ensures that the `CUSTOM_FIELDS` value is set directly on dmt's settings. This fixes the (original) issue reported in https://github.com/infoportugal/wagtail-modeltranslation/issues/234
2019-03-19 11:18:48 -07:00
Diogo Marques
d0034364d2
Merge pull request #229 from infoportugal/populate_slug_not_translated
Populate slug not translated
2019-03-12 17:18:36 +00:00
Diogo Marques
89d285555e .gitignore updated 2019-02-05 11:00:01 +00:00
Diogo Marques
1db9f3947c Auto populate slug when not translated 2019-02-05 10:59:51 +00:00
Diogo Marques
1193528e75 Merge branch 'django2' 2019-02-01 17:00:32 +00:00
Diogo Marques
466d88fdf2 Version 0.10b1 2019-02-01 16:59:31 +00:00
Diogo Marques
2b09b06eb3
Merge pull request #223 from Sempiternal02/patch-1
Update translation.py
2019-02-01 16:17:05 +00:00
Diogo Marques
b0a4e61d4a
Merge pull request #227 from tomazursic/fix-doc-setup
Add migrate module to INSTALLED_APPS setting list
2019-02-01 15:14:46 +00:00
Diogo Marques
3f9ac92bc8
Merge pull request #228 from infoportugal/django2
Django2
2019-02-01 15:14:19 +00:00
Diogo Marques
24df39a0d8 Hotfix: django 2.1 only supports postgres 9.4+, so we need to override
version 9.2 of travis
2019-02-01 14:53:56 +00:00
Diogo Marques
2c10234f80 Hotfix: travis does not have python 3.7 2019-02-01 14:38:06 +00:00
Diogo Marques
3d84c4a78f Hotfix: missing import 2019-02-01 14:36:48 +00:00
Diogo Marques
1cb2a41a08 Hotfix: missing import 2019-02-01 14:28:41 +00:00
Diogo Marques
8b8f64a7cc
Merge branch 'master' into django2 2019-02-01 14:22:48 +00:00
Diogo Marques
293d518f8a v0.10b1, wagtail support 1.12+ 2019-02-01 14:15:14 +00:00
Tomaz
46059f791b Add migrate module to INSTALLED_APPS setting list 2019-02-01 09:48:22 +01:00
Alexandre Silva
d9a8c2c06d
Merge pull request #224 from OndrejSodek/patch-2
Fix diacritics downcoding in slugs
2018-12-13 17:21:48 +00:00
Ondřej Šodek
4614b5faa4
Fix diacritics downcoding in slugs
This change correctly downcodes diacritics to ASCII if user has WAGTAIL_ALLOW_UNICODE_SLUGS = False (e.g. abčďéfg -> abcdefg)
Without this change, the slug simply strips all diacritics which is not ideal (e.g. abčďěfg -> abfg)
2018-12-12 19:09:56 +01:00
Sempiternal02
dcf2651ee7
Update translation.py 2018-10-16 23:42:50 +03:00
Diogo Marques
e7adf51d2a
Merge pull request #214 from infoportugal/210_hide_old_fields
Hide old fields for snippets without panels defenition
2018-08-21 17:18:49 +01:00
Diogo Marques
060491a75e
Merge pull request #215 from infoportugal/213_slug_translation_optional
Adds setting to translate slugs (default True)
2018-08-21 17:17:48 +01:00
DiogoMarques
7658d2e8d8 Merge branch 'master' into 213_slug_translation_optional 2018-08-21 15:33:07 +01:00
Diogo Marques
cd8a9ad5f0
Merge pull request #220 from infoportugal/python_accepted_version_cleanup
Droped python version 3.3 and added 3.6
2018-08-21 15:31:39 +01:00
DiogoMarques
54a2729283 Droped python version 3.3 and added 3.6 2018-08-21 15:08:23 +01:00
DiogoMarques
f7d748b7ee Moving translatable Page fields definition to the class 2018-08-21 14:20:10 +01:00
Diogo Marques
d5ecd24d70
Merge pull request #217 from infoportugal/document-migrations
Update README.rst
2018-06-26 12:55:55 +01:00
nulopes
e84cdc6acb
Update README.rst
Fixes #193
2018-06-26 11:46:46 +00:00
DiogoMarques
531a7494c2 Adds setting to translate slugs (default True) 2018-06-21 12:10:17 +01:00
DiogoMarques
7929589e7a Hotfix: import for wagtail >= 2.0 2018-06-14 11:27:06 +01:00
DiogoMarques
49246457cb Hide old fields for snippets without panels defenition 2018-06-14 11:20:12 +01:00
Dario Marcelino
11cbedfe13 #191, drop support for Wagtail < 1.8
And inherently Django < 1.8
2018-04-24 16:52:15 +01:00
Dario Marcelino
7d69a7135d #191, Django 2 compatibility 2018-04-24 16:42:07 +01:00
Dario Marcelino
adeb645a69 #191, update django-modeltranslation to 0.13b1 and update version to 0.10b1 2018-04-24 15:40:17 +01:00
31 changed files with 888 additions and 269 deletions

6
.gitignore vendored
View file

@ -41,3 +41,9 @@ _build
# vim
*~
*.swp
# vscode
.vscode
# pyenv
.python-version

View file

@ -1,47 +1,54 @@
language: python
python:
- "3.6"
- "3.8"
env:
- WAGTAIL="wagtail>=2.0,<2.1" DB=sqlite
- WAGTAIL="wagtail>=2.7,<2.8" DB=sqlite
matrix:
include:
# Latest Wagtail version
- env: WAGTAIL="wagtail>=2.0,<2.1" DB=sqlite
- env: WAGTAIL="wagtail>=2.0,<2.1" DB=postgres
- env: WAGTAIL="wagtail>=2.0,<2.1" DB=mysql
- python: "3.5"
- python: "3.4"
# Past Wagtail versions
- env: WAGTAIL="wagtail>=2.7,<2.8" DB=postgres
- env: WAGTAIL="wagtail>=2.7,<2.8" DB=mysql
- python: "3.8"
- python: "3.7"
- python: "3.6"
env: WAGTAIL="wagtail>=1.13,<1.14"
- python: "3.5"
# Past Wagtail versions
- python: "3.7"
env: WAGTAIL="wagtail>=2.6,<2.7"
- python: "3.7"
env: WAGTAIL="wagtail>=2.5,<2.6"
- python: "3.4" # Wagtail 2.5 was the last to support python 3.4
env: WAGTAIL="wagtail>=2.5,<2.6"
- python: "3.7"
env: WAGTAIL="wagtail>=2.4,<2.5"
- python: "3.7"
env: WAGTAIL="wagtail>=2.3,<2.4"
- python: "3.7"
env: WAGTAIL="wagtail>=2.2,<2.3"
- python: "3.7"
env: WAGTAIL="wagtail>=2.1,<2.2"
- python: "3.7"
env: WAGTAIL="wagtail>=2.0,<2.1"
- python: "2.7" # Wagtail 1.13 was the latest tested against 2.7
env: WAGTAIL="wagtail>=1.13,<1.14"
- python: "3.6"
- python: "3.7"
env: WAGTAIL="wagtail>=1.13,<1.14"
- python: "2.7"
env: WAGTAIL="wagtail>=1.12,<1.13"
- python: "3.6"
env: WAGTAIL="wagtail>=1.11,<1.12"
- python: "3.6"
env: WAGTAIL="wagtail>=1.10,<1.11"
- python: "3.5"
env: WAGTAIL="wagtail>=1.9,<1.10"
- python: "3.3" # Wagtail 1.9 was the latest tested against 3.3
env: WAGTAIL="wagtail>=1.9,<1.10"
- python: "3.5"
env: WAGTAIL="wagtail>=1.8,<1.9"
- python: "3.5"
env: WAGTAIL="wagtail>=1.6,<1.7"
- python: "3.5"
env: WAGTAIL="wagtail>=1.5,<1.6"
- python: "3.5"
env: WAGTAIL="wagtail>=1.4,<1.5"
- python: "3.7"
env: WAGTAIL="wagtail>=1.12,<1.13"
services:
- mysql
- postgresql
addons:
postgresql: "9.4"
before_script:
- mysql -e 'create database wagtail_modeltranslation;'
- psql -c 'create database wagtail_modeltranslation;' -U postgres
install:
- pip install --upgrade -q pip setuptools
- if [[ $DB == mysql ]] && [[ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]]; then pip install -q mysql-python; elif [[ $DB == mysql ]] && [[ ${TRAVIS_PYTHON_VERSION:0:1} == "3" ]]; then pip install -q mysqlclient; fi
- if [[ $DB == postgres ]]; then pip install -q psycopg2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install 'Django>=1.8,<1.9'; fi
- if [[ $DB == postgres ]]; then pip install -q 'psycopg2-binary'; fi
- pip install $WAGTAIL
- pip install -e .
script:

View file

@ -22,6 +22,7 @@ Contributors
* Raphael Grill
* Tom Dyson
* Tim Tan
* Benedikt Willi
.. _Django-modeltranslation: https://github.com/deschler/django-modeltranslation

View file

@ -1,3 +1,11 @@
v0.6.0rc2:
- added RichTextFieldPanel to the default list of patched panels
- added settings to allow the patching of custom panels
- slug auto-population is now made the same way as wagtail (no changes in live pages)
- Fixed: When adding a page link in a translated RichTextField the link was always to the default language version of that page
- Fixed: Copy content of streamfield fails with 414 Request-URI Too Long
- Fixed: Panel patching failed with the error "AttributeError: 'list' object has no attribute 'children'"
v0.6.0rc1:
- django-modeltranslation is now a dependency.
- added compatibility with Python 3 (3.3, 3.4, 3.5).

View file

@ -3,5 +3,6 @@ recursive-include docs *.rst conf.py Makefile make.bat
recursive-include wagtail_modeltranslation/static *
recursive-include wagtail_modeltranslation/management *
recursive-include wagtail_modeltranslation/templatetags *
recursive-include wagtail_modeltranslation/templates *
global-exclude *.pyc
global-exclude *.DS_Store

View file

@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: wagtail-modeltranslation
Version: 0.8.1
Version: 0.10.6
Summary: Translates Wagtail CMS models using a registration approach.
Home-page: https://github.com/infoportugal/wagtail-modeltranslation
Author: InfoPortugal S.A.

View file

@ -8,3 +8,4 @@ bumpversion = "*"
wheel = "*"
[dev-packages]
twine = "*"

122
Pipfile.lock generated
View file

@ -1,12 +1,10 @@
{
"_meta": {
"hash": {
"sha256": "8a50cf92fd274fbd58348130a7ab3568a8f694ad373cb5a67d45da7a46b96827"
"sha256": "191a6f860a13836c57a16587784ceca36f791bfae1270cf937286a496e891114"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"requires": {},
"sources": [
{
"name": "pypi",
@ -26,12 +24,120 @@
},
"wheel": {
"hashes": [
"sha256:1ae8153bed701cb062913b72429bcf854ba824f973735427681882a688cb55ce",
"sha256:9cdc8ab2cc9c3c2e2727a4b67c22881dbb0e1c503d592992594c5e131c867107"
"sha256:5e79117472686ac0c4aef5bad5172ea73a1c2d1646b808c35926bd26bdfb0c08",
"sha256:62fcfa03d45b5b722539ccbc07b190e4bfff4bb9e3a4d470dd9f6a0981002565"
],
"index": "pypi",
"version": "==0.31.0"
"version": "==0.33.4"
}
},
"develop": {}
"develop": {
"bleach": {
"hashes": [
"sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16",
"sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"
],
"version": "==3.1.0"
},
"certifi": {
"hashes": [
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
],
"version": "==2019.3.9"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"docutils": {
"hashes": [
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
],
"version": "==0.14"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"pkginfo": {
"hashes": [
"sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb",
"sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"
],
"version": "==1.5.0.1"
},
"pygments": {
"hashes": [
"sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
"sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
],
"version": "==2.4.2"
},
"readme-renderer": {
"hashes": [
"sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f",
"sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d"
],
"version": "==24.0"
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"version": "==2.22.0"
},
"requests-toolbelt": {
"hashes": [
"sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f",
"sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
],
"version": "==0.9.1"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"tqdm": {
"hashes": [
"sha256:0a860bf2683fdbb4812fe539a6c22ea3f1777843ea985cb8c3807db448a0f7ab",
"sha256:e288416eecd4df19d12407d0c913cbf77aa8009d7fddb18f632aded3bdbdda6b"
],
"version": "==4.32.1"
},
"twine": {
"hashes": [
"sha256:0fb0bfa3df4f62076cab5def36b1a71a2e4acb4d1fa5c97475b048117b1a6446",
"sha256:d6c29c933ecfc74e9b1d9fa13aa1f87c5d5770e119f5a4ce032092f0ff5b14dc"
],
"index": "pypi",
"version": "==1.13.0"
},
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
],
"version": "==1.25.3"
},
"webencodings": {
"hashes": [
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
],
"version": "==0.5.1"
}
}
}

View file

@ -58,6 +58,7 @@ Quick start
...
'wagtail_modeltranslation',
'wagtail_modeltranslation.makemigrations',
'wagtail_modeltranslation.migrate',
)
3. Add 'django.middleware.locale.LocaleMiddleware' to ``MIDDLEWARE`` on your ``settings.py``::

View file

@ -7,7 +7,7 @@ Installation
Requirements
============
* Wagtail >= 1.4
* Wagtail >= 1.12
@ -48,6 +48,7 @@ To setup the application please follow these steps:
...
'wagtail_modeltranslation',
'wagtail_modeltranslation.makemigrations',
'wagtail_modeltranslation.migrate',
)
- Add 'django.middleware.locale.LocaleMiddleware' to ``MIDDLEWARE`` (``MIDDLEWARE_CLASSES`` before django 1.10).
@ -88,7 +89,7 @@ To setup the application please follow these steps:
To learn more about preparing Wagtail for Internationalisation check the `Wagtail i18n docs <http://docs.wagtail.io/en/latest/advanced_topics/i18n/>`_.
2. Create a ``translation.py`` file in your app directory and register ``TranslationOptions`` for every model you want to translate.
2. Create a ``translation.py`` file in your app directory and register ``TranslationOptions`` for every model you want to translate and for all subclasses of Page model.
.. code-block:: console

View file

@ -28,3 +28,15 @@ This setting behaves as the above but should be used for panels that are compose
.. code-block:: python
WAGTAILMODELTRANSLATION_CUSTOM_COMPOSED_PANELS = ['app_x.module_y.PanelZ']
``WAGTAILMODELTRANSLATION_TRANSLATE_SLUGS``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Default: ``True``
This setting makes slug and url_path localized. If True, each page will have a slug and url_path per language.
.. code-block:: python
WAGTAILMODELTRANSLATION_TRANSLATE_SLUGS = True

View file

@ -5,10 +5,7 @@ import sys
import django
from django.conf import settings
from django.core.management import call_command
try:
from wagtail import VERSION
except ImportError:
VERSION = 1, 6, 3 # assume it's 1.6.3, the latest version without VERSION
from wagtail import VERSION
def runtests():
@ -36,7 +33,7 @@ def runtests():
# Configure test environment
import wagtail
if VERSION[0] < 2:
if VERSION < (2,):
WAGTAIL_MODULES = [
'wagtail.wagtailcore',
'wagtail.wagtailadmin',
@ -71,7 +68,7 @@ def runtests():
]
WAGTAIL_CORE = 'wagtail.core'
settings.configure(
DATABASES=DATABASES,
INSTALLED_APPS=[
@ -93,8 +90,7 @@ def runtests():
MIDDLEWARE_CLASSES=(),
)
if django.VERSION >= (1, 7):
django.setup()
django.setup()
failures = call_command(
'test', 'wagtail_modeltranslation', interactive=False, failfast=False, verbosity=2)

View file

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.9.0
current_version = 0.10.6
commit = True
tag = True

View file

@ -44,17 +44,18 @@ setup(
'wagtail_modeltranslation.migrate.management',
'wagtail_modeltranslation.migrate.management.commands'],
package_data={'wagtail_modeltranslation': ['static/wagtail_modeltranslation/css/*.css',
'static/wagtail_modeltranslation/js/*.js']},
install_requires=['Django<2.0', 'wagtail>=1.4', 'django-modeltranslation>=0.12.2'],
download_url='https://github.com/infoportugal/wagtail-modeltranslation/archive/v0.8.tar.gz',
'static/wagtail_modeltranslation/js/*.js',
'templates/*.html']},
install_requires=['wagtail>=1.12', 'django-modeltranslation>=0.13'],
classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Operating System :: OS Independent',
'Environment :: Web Environment',
'Intended Audience :: Developers',

261
tox.ini
View file

@ -1,158 +1,217 @@
[tox]
envlist =
py35-1.9.X,
py34-1.9.X,
py33-1.9.X,
py27-1.9.X,
py35-1.8.X,
py34-1.8.X,
py33-1.8.X,
py27-1.8.X,
py35-1.7.X,
py34-1.7.X,
py33-1.7.X,
py27-1.7.X,
py35-1.6.X,
py34-1.6.X,
py33-1.6.X,
py27-1.6.X,
py35-1.5.X,
py34-1.5.X,
py33-1.5.X,
py27-1.5.X,
py35-1.4.X,
py34-1.4.X,
py33-1.4.X,
py27-1.4.X,
py38-2.7.X,
py37-2.7.X,
py36-2.7.X,
py35-2.7.X,
py34-2.7.X,
py37-2.6.X,
py36-2.6.X,
py35-2.6.X,
py37-2.5.X,
py36-2.5.X,
py35-2.5.X,
py34-2.5.X,
py37-2.4.X,
py36-2.4.X,
py35-2.4.X,
py34-2.4.X,
py36-2.3.X,
py35-2.3.X,
py34-2.3.X,
py36-2.2.X,
py35-2.2.X,
py34-2.2.X,
py36-2.1.X,
py35-2.1.X,
py34-2.1.X,
py36-2.0.X,
py35-2.0.X,
py34-2.0.X,
py36-1.13.X,
py35-1.13.X,
py34-1.13.X,
py27-1.13.X,
py36-1.12.X,
py35-1.12.X,
py34-1.12.X,
py27-1.12.X,
[testenv]
commands =
{envpython} runtests.py
[testenv:py35-1.9.X]
[testenv:py38-2.7.X]
basepython = python3.8
deps =
wagtail>=2.7,<2.8
[testenv:py37-2.7.X]
basepython = python3.7
deps =
wagtail>=2.7,<2.8
[testenv:py36-2.7.X]
basepython = python3.6
deps =
wagtail>=2.7,<2.8
[testenv:py35-2.7.X]
basepython = python3.5
deps =
wagtail>=1.9,<1.10
wagtail>=2.7,<2.8
[testenv:py34-1.9.X]
basepython = python3.4
[testenv:py37-2.6.X]
basepython = python3.7
deps =
wagtail>=1.9,<1.10
wagtail>=2.6,<2.7
[testenv:py33-1.9.X]
basepython = python3.3
[testenv:py36-2.6.X]
basepython = python3.6
deps =
Django>=1.8,<1.9
wagtail>=1.9,<1.10
wagtail>=2.6,<2.7
[testenv:py27-1.9.X]
basepython = python2.7
deps =
wagtail>=1.9,<1.10
[testenv:py35-1.8.X]
[testenv:py35-2.6.X]
basepython = python3.5
deps =
wagtail>=1.8,<1.9
wagtail>=2.6,<2.7
[testenv:py34-1.8.X]
basepython = python3.4
[testenv:py37-2.5.X]
basepython = python3.7
deps =
wagtail>=1.8,<1.9
wagtail>=2.5,<2.6
[testenv:py33-1.8.X]
basepython = python3.3
[testenv:py36-2.5.X]
basepython = python3.6
deps =
Django>=1.8,<1.9
wagtail>=1.8,<1.9
wagtail>=2.5,<2.6
[testenv:py27-1.8.X]
basepython = python2.7
deps =
wagtail>=1.8,<1.9
[testenv:py35-1.7.X]
[testenv:py35-2.5.X]
basepython = python3.5
deps =
wagtail>=1.7,<1.8
wagtail>=2.5,<2.6
[testenv:py34-1.7.X]
[testenv:py34-2.5.X]
basepython = python3.4
deps =
wagtail>=1.7,<1.8
wagtail>=2.5,<2.6
[testenv:py33-1.7.X]
basepython = python3.3
[testenv:py37-2.4.X]
basepython = python3.7
deps =
Django>=1.8,<1.9
wagtail>=1.7,<1.8
wagtail>=2.4,<2.5
[testenv:py27-1.7.X]
basepython = python2.7
[testenv:py36-2.4.X]
basepython = python3.6
deps =
wagtail>=1.7,<1.8
wagtail>=2.4,<2.5
[testenv:py35-1.6.X]
[testenv:py35-2.4.X]
basepython = python3.5
deps =
wagtail>=1.6,<1.7
wagtail>=2.4,<2.5
[testenv:py34-1.6.X]
[testenv:py34-2.4.X]
basepython = python3.4
deps =
wagtail>=1.6,<1.7
wagtail>=2.4,<2.5
[testenv:py33-1.6.X]
basepython = python3.3
[testenv:py36-2.3.X]
basepython = python3.6
deps =
Django>=1.8,<1.9
wagtail>=1.6,<1.7
wagtail>=2.3,<2.4
[testenv:py27-1.6.X]
basepython = python2.7
deps =
wagtail>=1.6,<1.7
[testenv:py35-1.5.X]
[testenv:py35-2.3.X]
basepython = python3.5
deps =
wagtail>=1.5,<1.6
wagtail>=2.3,<2.4
[testenv:py34-1.5.X]
[testenv:py34-2.3.X]
basepython = python3.4
deps =
wagtail>=1.5,<1.6
wagtail>=2.3,<2.4
[testenv:py33-1.5.X]
basepython = python3.3
[testenv:py36-2.2.X]
basepython = python3.6
deps =
Django>=1.8,<1.9
wagtail>=1.5,<1.6
wagtail>=2.2,<2.3
[testenv:py27-1.5.X]
basepython = python2.7
deps =
wagtail>=1.5,<1.6
[testenv:py35-1.4.X]
[testenv:py35-2.2.X]
basepython = python3.5
deps =
wagtail>=1.4,<1.5
wagtail>=2.2,<2.3
[testenv:py34-1.4.X]
[testenv:py34-2.2.X]
basepython = python3.4
deps =
wagtail>=1.4,<1.5
wagtail>=2.2,<2.3
[testenv:py33-1.4.X]
basepython = python3.3
[testenv:py36-2.1.X]
basepython = python3.6
deps =
Django>=1.8,<1.9
wagtail>=1.4,<1.5
wagtail>=2.1,<2.2
[testenv:py27-1.4.X]
[testenv:py35-2.1.X]
basepython = python3.5
deps =
wagtail>=2.1,<2.2
[testenv:py34-2.1.X]
basepython = python3.4
deps =
wagtail>=2.1,<2.2
[testenv:py36-2.0.X]
basepython = python3.6
deps =
wagtail>=2.0,<2.1
[testenv:py35-2.0.X]
basepython = python3.5
deps =
wagtail>=2.0,<2.1
[testenv:py34-2.0.X]
basepython = python3.4
deps =
wagtail>=2.0,<2.1
[testenv:py36-1.13.X]
basepython = python3.6
deps =
wagtail>=1.13,<2.0
[testenv:py35-1.13.X]
basepython = python3.5
deps =
wagtail>=1.13,<2.0
[testenv:py34-1.13.X]
basepython = python3.4
deps =
wagtail>=1.13,<2.0
[testenv:py27-1.13.X]
basepython = python2.7
deps =
wagtail>=1.4,<1.5
wagtail>=1.13,<2.0
[testenv:py36-1.12.X]
basepython = python3.6
deps =
wagtail>=1.12,<1.13
[testenv:py35-1.12.X]
basepython = python3.5
deps =
wagtail>=1.12,<1.13
[testenv:py34-1.12.X]
basepython = python3.4
deps =
wagtail>=1.12,<1.13
[testenv:py27-1.12.X]
basepython = python2.7
deps =
wagtail>=1.12,<1.13

View file

@ -1,3 +1,3 @@
# coding: utf-8
__version__ = '0.9.0'
__version__ = '0.10.6'
default_app_config = 'wagtail_modeltranslation.apps.ModeltranslationConfig'

View file

@ -10,9 +10,22 @@ class ModeltranslationConfig(AppConfig):
def ready(self):
from django.conf import settings
from modeltranslation import settings as mt_settings
# Add Wagtail defined fields as modeltranslation custom fields
setattr(settings, 'MODELTRANSLATION_CUSTOM_FIELDS', getattr(settings, 'MODELTRANSLATION_CUSTOM_FIELDS', ()) + (
'StreamField', 'RichTextField'))
wagtail_fields = (
'StreamField',
'RichTextField',
)
# update both the standard settings and the modeltranslation settings,
# as we cannot guarantee the load order, and so django_modeltranslation
# may bootstrap itself either before, or after, our ready() gets called.
custom_fields = getattr(settings, 'MODELTRANSLATION_CUSTOM_FIELDS', tuple())
setattr(settings, 'MODELTRANSLATION_CUSTOM_FIELDS', tuple(set(custom_fields + wagtail_fields)))
mt_custom_fields = getattr(mt_settings, 'CUSTOM_FIELDS', tuple())
setattr(mt_settings, 'CUSTOM_FIELDS', tuple(set(mt_custom_fields + wagtail_fields)))
from modeltranslation.models import handle_translation_registrations
handle_translation_registrations()

View file

@ -5,7 +5,10 @@ import types
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
from django.db import transaction, connection
from django.db.models import Q, Value
from django.db.models.functions import Concat, Substr
@ -15,39 +18,34 @@ from django.utils.translation import ugettext_lazy as _
from modeltranslation import settings as mt_settings
from modeltranslation.translator import translator, NotRegistered
from modeltranslation.utils import build_localized_fieldname, get_language
from wagtail.contrib.settings.models import BaseSetting
from wagtail.contrib.settings.views import get_setting_edit_handler
try:
from wagtail.contrib.routable_page.models import RoutablePageMixin
from wagtail.admin.edit_handlers import FieldPanel, \
MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel, RichTextFieldPanel
MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel, RichTextFieldPanel,\
extract_panel_definitions_from_model_class, ObjectList
from wagtail.core.models import Page, Site
from wagtail.core.fields import StreamField, StreamValue
from wagtail.core.url_routing import RouteResult
from wagtail.core.utils import WAGTAIL_APPEND_SLASH
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search.index import SearchField
from wagtail.snippets.models import get_snippet_models
from wagtail.snippets.views.snippets import SNIPPET_EDIT_HANDLERS
except ImportError:
from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin
from wagtail.wagtailadmin.edit_handlers import FieldPanel, \
MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel, RichTextFieldPanel
MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel, RichTextFieldPanel,\
extract_panel_definitions_from_model_class, ObjectList
from wagtail.wagtailcore.models import Page, Site
from wagtail.wagtailcore.fields import StreamField, StreamValue
from wagtail.wagtailcore.url_routing import RouteResult
from wagtail.wagtailcore.utils import WAGTAIL_APPEND_SLASH
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailsearch.index import SearchField
from wagtail.wagtailsnippets.models import get_snippet_models
from wagtail.wagtailsnippets.views.snippets import SNIPPET_EDIT_HANDLERS
try:
from wagtail.core.utils import WAGTAIL_APPEND_SLASH
except ImportError:
try:
from wagtail.wagtailcore.utils import WAGTAIL_APPEND_SLASH
except ImportError:
WAGTAIL_APPEND_SLASH = True # Wagtail<1.5
from wagtail_modeltranslation.settings import CUSTOM_SIMPLE_PANELS, CUSTOM_COMPOSED_PANELS
from wagtail_modeltranslation.settings import CUSTOM_SIMPLE_PANELS, CUSTOM_COMPOSED_PANELS, TRANSLATE_SLUGS
from wagtail_modeltranslation.utils import compare_class_tree_depth
from wagtail import VERSION
logger = logging.getLogger('wagtail.core')
@ -119,29 +117,35 @@ class WagtailTranslator(object):
_patch_stream_field_meaningful_value(descriptor)
# OVERRIDE PAGE METHODS
model.set_url_path = _new_set_url_path
model.route = _new_route
model._update_descendant_url_paths = _new_update_descendant_url_paths
if not hasattr(model, '_get_site_root_paths'):
model.get_url_parts = _new_get_url_parts # Wagtail<1.11
model._get_site_root_paths = _new_get_site_root_paths
_patch_clean(model)
if TRANSLATE_SLUGS:
model.set_url_path = _new_set_url_path
model.route = _new_route
model._update_descendant_url_paths = _new_update_descendant_url_paths
if not hasattr(model, '_get_site_root_paths'):
model.get_url_parts = _new_get_url_parts # Wagtail<1.11
model._get_site_root_paths = _new_get_site_root_paths
_patch_clean(model)
if not model.save.__name__.startswith('localized'):
setattr(model, 'save', LocalizedSaveDescriptor(model.save))
if not model.save.__name__.startswith('localized'):
setattr(model, 'save', LocalizedSaveDescriptor(model.save))
def _patch_other_models(self, model):
if hasattr(model, 'edit_handler'):
edit_handler = model.edit_handler
for tab in edit_handler:
for tab in edit_handler.children:
tab.children = self._patch_panels(tab.children)
elif hasattr(model, 'panels'):
model.panels = self._patch_panels(model.panels)
if model in get_snippet_models() and model in SNIPPET_EDIT_HANDLERS:
del SNIPPET_EDIT_HANDLERS[model]
else:
get_setting_edit_handler.cache_clear()
panels = extract_panel_definitions_from_model_class(model)
translation_registered_fields = translator.get_options_for_model(model).fields
panels = filter(lambda field: field.field_name not in translation_registered_fields, panels)
edit_handler = ObjectList(panels)
if VERSION < (2, 5):
SNIPPET_EDIT_HANDLERS[model] = edit_handler.bind_to_model(model)
else:
SNIPPET_EDIT_HANDLERS[model] = edit_handler.bind_to(model=model)
def _patch_panels(self, panels_list, related_model=None):
"""
@ -258,9 +262,9 @@ def _localized_set_url_path(page, parent, language):
# for the current language. If the value for the current language is invalid we get the one
# for the default fallback language
slug = getattr(page, localized_slug_field, None) or \
getattr(page, default_localized_slug_field, None) or page.slug
getattr(page, default_localized_slug_field, None) or page.slug
parent_url_path = getattr(parent, localized_url_path_field, None) or \
getattr(parent, default_localized_url_path_field, None) or parent.url_path
getattr(parent, default_localized_url_path_field, None) or parent.url_path
setattr(page, localized_url_path_field, parent_url_path + slug + '/')
@ -455,7 +459,7 @@ def _update_translation_descendant_url_paths(old_record, page):
default_localized_url_path = build_localized_fieldname('url_path', mt_settings.DEFAULT_LANGUAGE)
for language in mt_settings.AVAILABLE_LANGUAGES:
localized_url_path = build_localized_fieldname('url_path', language)
old_url_path = getattr(old_record, localized_url_path) or getattr(old_record, default_localized_url_path)
old_url_path = getattr(old_record, localized_url_path) or getattr(old_record, default_localized_url_path) or ''
new_url_path = getattr(page, localized_url_path) or getattr(page, default_localized_url_path)
if old_url_path == new_url_path:
@ -569,5 +573,4 @@ def patch_wagtail_models():
registered_models.sort(key=compare_class_tree_depth)
for model_class in registered_models:
if issubclass(model_class, Page) or model_class in get_snippet_models() or issubclass(model_class, BaseSetting):
WagtailTranslator(model_class)
WagtailTranslator(model_class)

View file

@ -0,0 +1,107 @@
# coding: utf-8
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
try:
from wagtail.core.models import Page
from wagtail.admin import widgets
from wagtail.admin.forms.pages import CopyForm
except ImportError:
from wagtail.wagtailcore.models import Page
from wagtail.wagtailadmin import widgets
from wagtail.wagtailadmin.forms import CopyForm
class PatchedCopyForm(CopyForm):
def __init__(self, *args, **kwargs):
# CopyPage must be passed a 'page' kwarg indicating the page to be copied
self.page = kwargs.pop('page')
self.user = kwargs.pop('user', None)
can_publish = kwargs.pop('can_publish')
super(CopyForm, self).__init__(*args, **kwargs)
#self.fields['new_title'] = forms.CharField(initial=self.page.title, label=_("New title"))
for code, name in settings.LANGUAGES:
locale_title = "new_title_{}".format(code)
locale_label = "{} [{}]".format(_("New title"), code)
self.fields[locale_title] = forms.CharField(initial=self.page.title, label=locale_label)
#self.fields['new_slug'] = forms.SlugField(initial=self.page.slug, label=_("New slug"))
for code, name in settings.LANGUAGES:
locale_title = "new_slug_{}".format(code)
locale_label = "{} [{}]".format(_("New slug"), code)
self.fields[locale_title] = forms.SlugField(initial=self.page.slug, label=locale_label)
self.fields['new_parent_page'] = forms.ModelChoiceField(
initial=self.page.get_parent(),
queryset=Page.objects.all(),
widget=widgets.AdminPageChooser(can_choose_root=True, user_perms='copy_to'),
label=_("New parent page"),
help_text=_("This copy will be a child of this given parent page.")
)
pages_to_copy = self.page.get_descendants(inclusive=True)
subpage_count = pages_to_copy.count() - 1
if subpage_count > 0:
self.fields['copy_subpages'] = forms.BooleanField(
required=False, initial=True, label=_("Copy subpages"),
help_text=ungettext(
"This will copy %(count)s subpage.",
"This will copy %(count)s subpages.",
subpage_count) % {'count': subpage_count})
if can_publish:
pages_to_publish_count = pages_to_copy.live().count()
if pages_to_publish_count > 0:
# In the specific case that there are no subpages, customise the field label and help text
if subpage_count == 0:
label = _("Publish copied page")
help_text = _("This page is live. Would you like to publish its copy as well?")
else:
label = _("Publish copies")
help_text = ungettext(
"%(count)s of the pages being copied is live. Would you like to publish its copy?",
"%(count)s of the pages being copied are live. Would you like to publish their copies?",
pages_to_publish_count) % {'count': pages_to_publish_count}
self.fields['publish_copies'] = forms.BooleanField(
required=False, initial=True, label=label, help_text=help_text
)
def clean(self):
cleaned_data = super(CopyForm, self).clean()
# Make sure the slug isn't already in use
# slug = cleaned_data.get('new_slug')
# New parent page given in form or parent of source, if parent_page is empty
parent_page = cleaned_data.get('new_parent_page') or self.page.get_parent()
# check if user is allowed to create a page at given location.
if not parent_page.permissions_for_user(self.user).can_add_subpage():
raise ValidationError({
'new_parent_page': _("You do not have permission to copy to page \"%(page_title)s\"") % {'page_title': parent_page.get_admin_display_title()}
})
# Count the pages with the same slug within the context of our copy's parent page
for code, name in settings.LANGUAGES:
locale_slug = "new_slug_{}".format(code)
slug = cleaned_data.get(locale_slug)
param = 'slug_' + code
query = {param: slug}
if slug and parent_page.get_children().filter(**query).count():
raise ValidationError({
locale_slug: _("This slug is already in use within the context of its parent page \"%s\"" % parent_page)
})
# Don't allow recursive copies into self
if cleaned_data.get('copy_subpages') and (self.page == parent_page or parent_page.is_descendant_of(self.page)):
raise ValidationError({
'new_parent_page': _("You cannot copy a page into itself when copying subpages")
})
return cleaned_data

View file

@ -10,3 +10,4 @@ CUSTOM_SIMPLE_PANELS = [import_from_string(panel_class) for panel_class in
getattr(settings, 'WAGTAILMODELTRANSLATION_CUSTOM_SIMPLE_PANELS', [])]
CUSTOM_COMPOSED_PANELS = [import_from_string(panel_class) for panel_class in
getattr(settings, 'WAGTAILMODELTRANSLATION_CUSTOM_COMPOSED_PANELS', [])]
TRANSLATE_SLUGS = getattr(settings, 'WAGTAILMODELTRANSLATION_TRANSLATE_SLUGS', True)

View file

@ -7,22 +7,47 @@ $(document).ready(function(){
for (var i = 0; i < allStreamFields.length; i++) {
//Current Field with all content
var currentStreamField = allStreamFields[i];
//Current Field header
var header = $(currentStreamField).children('h2')[0];
//Search for the input field so that we can get is id to know the field's name.
var streamFieldDiv = $(currentStreamField).find('div.sequence-container.sequence-type-stream')[0];
var fieldInfos = $(streamFieldDiv).children('input')[0].id.split('-')[0];
var lastUnderscore = fieldInfos.lastIndexOf("_");
var fieldName = fieldInfos.substring(0, lastUnderscore);
var fieldLang = fieldInfos.substring(lastUnderscore + 1, fieldInfos.length);
//Current field header
var header;
//Current field name
var fieldLang = "";
//Current field language
var fieldName = "";
if(versionCompare(WAGTAIL_VERSION,'2.6.0', {zeroExtend: true})===-1){
// Wagtail < 2.6
header = $(currentStreamField).children('h2')[0];
//Search for the input field so that we can get is id to know the field's name.
var streamFieldDiv = $(currentStreamField).find('div.sequence-container.sequence-type-stream')[0];
var fieldInfos = $(streamFieldDiv).find('input')[0].id.split('-')[0];
var lastUnderscore = fieldInfos.lastIndexOf("_");
fieldName = fieldInfos.substring(0, lastUnderscore);
fieldLang = fieldInfos.substring(lastUnderscore + 1, fieldInfos.length);
} else if(versionCompare(WAGTAIL_VERSION,'2.7.0', {zeroExtend: true})===-1){
// Wagtail < 2.7
header = $(currentStreamField).children('.title-wrapper')[0];
//Search for the input field so that we can get is id to know the field's name.
var streamFieldDiv = $(currentStreamField).find('div.sequence-container.sequence-type-stream')[0];
var fieldInfos = $(streamFieldDiv).find('input')[0].id.split('-')[0];
var lastUnderscore = fieldInfos.lastIndexOf("_");
fieldName = fieldInfos.substring(0, lastUnderscore);
fieldLang = fieldInfos.substring(lastUnderscore + 1, fieldInfos.length);
} else {
// Wagtail >= 2.7
header = $(currentStreamField).children('.title-wrapper')[0];
//Search for the input field so that we can get is id to know the field's name.
var streamFieldDiv = $(currentStreamField).find('.field-content')[0];
var fieldInfos = $(streamFieldDiv).find('input')[0].id.split('-')[0];
var lastUnderscore = fieldInfos.lastIndexOf("_");
fieldName = fieldInfos.substring(0, lastUnderscore);
fieldLang = fieldInfos.substring(lastUnderscore + 1, fieldInfos.length);
}
//The cycle to create the buttons for copy each language field
var copyContentString = 'Copy content from';
header.innerHTML += '<div class="translation-field-copy-wrapper">'+copyContentString+': </div>';
header.innerHTML += '<div class="translation-field-copy-wrapper">Copy content from: </div>';
for (var j = 0; j < langs.length; j++) {
if (fieldLang != langs[j]) {
var currentFieldID = fieldName + '_' + fieldLang;
var targetFieldID = fieldName + '_' + langs [j];
$(header).children('.translation-field-copy-wrapper')[0].innerHTML += '<button class="translation-field-copy" current-lang-code="'+ currentFieldID +'" data-lang-code="'+ targetFieldID +'">'+langs[j]+'</button>';
$(header).children('.translation-field-copy-wrapper')[0].innerHTML += '<button class="button translation-field-copy" current-lang-code="' + currentFieldID + '" data-lang-code="' + targetFieldID + '">' + langs[j] + '</button>';
};
};
};
@ -55,11 +80,11 @@ function requestCopyField(originID, targetID) {
})
.done(function(data) {
/* Put the html data in the targetID field */
var wrapperDiv = $("#"+targetID+"-count").parents('.input')[0];
var wrapperDiv = $('#' + targetID + '-count').parents('.input')[0];
$(wrapperDiv).html(data);
})
.fail(function(error) {
console.log("wagtail-modeltranslation error: %s", error.responseText);
console.log('wagtail-modeltranslation error: ' + error.responseText);
})
}

View file

@ -0,0 +1,78 @@
/**
* Compares two software version numbers (e.g. "1.7.1" or "1.2b").
*
* This function was born in http://stackoverflow.com/a/6832721.
*
* @param {string} v1 The first version to be compared.
* @param {string} v2 The second version to be compared.
* @param {object} [options] Optional flags that affect comparison behavior.
* @param {boolean} [options.lexicographical = false] Switch to compare version strings lexicographically instead of naturally.
* @param {boolean} [options.zeroExtend = false] Switch to pad version with "zero" parts instead to be considered smaller.
* <ul>
* <li>
* <tt>lexicographical: true</tt> compares each part of the version strings lexicographically instead of
* naturally; this allows suffixes such as "b" or "dev" but will cause "1.10" to be considered smaller than
* "1.2".
* </li>
* <li>
* <tt>zeroExtend: true</tt> changes the result if one version string has less parts than the other. In
* this case the shorter string will be padded with "zero" parts instead of being considered smaller.
* </li>
* </ul>
* @returns {number|NaN}
* <ul>
* <li>0 if the versions are equal</li>
* <li>a negative integer (-1) if v1 < v2</li>
* <li>a positive integer (1) if v1 > v2</li>
* <li>NaN if either version string is in the wrong format</li>
* </ul>
*
* @copyright by Jon Papaioannou (["john", "papaioannou"].join(".") + "@gmail.com")
* @license This function is in the public domain. Do what you want with it, no strings attached.
*/
function versionCompare(v1, v2, options) {
var lexicographical = options && options.lexicographical,
zeroExtend = options && options.zeroExtend,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) {
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
}
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return NaN;
}
if (zeroExtend) {
while (v1parts.length < v2parts.length) v1parts.push("0");
while (v2parts.length < v1parts.length) v2parts.push("0");
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length == i) {
return 1;
}
if (v1parts[i] == v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return 1;
}
else {
return -1;
}
}
if (v1parts.length != v2parts.length) {
return -1;
}
return 0;
}

View file

@ -1,23 +1,36 @@
$(document).ready(function () {
/* Only non-live pages should auto-populate the slug from the title */
if (!$('body').hasClass('page-is-live')) {
var slugFollowsTitle = false;
$.each(langs, function (idx, lang_code) {
lang_code = lang_code.replace("-", "_");
$('#id_title_' + lang_code).on('focus', function () {
/* slug should only follow the title field if its value matched the title's value at the time of focus */
var currentSlug = $('#id_slug_' + lang_code).val();
var slugifiedTitle = cleanForSlug(this.value);
slugFollowsTitle = (currentSlug == slugifiedTitle);
if(!translate_slugs) {
lang_code = default_lang.replace("-", "_");
title_selector = '#id_title_' + lang_code;
slug_selector = '#id_slug';
slugAutoPopulateTranslation(title_selector, slug_selector);
} else {
$.each(langs, function (idx, lang_code) {
lang_code = lang_code.replace("-", "_");
title_selector = '#id_title_' + lang_code;
slug_selector = '#id_slug_' + lang_code;
slugAutoPopulateTranslation(title_selector, slug_selector);
});
$('#id_title_' + lang_code).on('keyup keydown keypress blur', function () {
if (slugFollowsTitle) {
var slugifiedTitle = cleanForSlug(this.value);
$('#id_slug_' + lang_code).val(slugifiedTitle);
}
});
});
}
}
});
function slugAutoPopulateTranslation(title_selector, slug_selector) {
var slugFollowsTitle = false;
$(title_selector).on('focus', function () {
/* slug should only follow the title field if its value matched the title's value at the time of focus */
var currentSlug = $(slug_selector).val();
var slugifiedTitle = cleanForSlug(this.value, true);
slugFollowsTitle = (currentSlug == slugifiedTitle);
});
$(title_selector).on('keyup keydown keypress blur', function () {
if (slugFollowsTitle) {
var slugifiedTitle = cleanForSlug(this.value, true);
$(slug_selector).val(slugifiedTitle);
}
});
}

View file

@ -0,0 +1,31 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% block titletag %}{% blocktrans with title=page.get_admin_display_title %}Copy {{ title }}{% endblocktrans %}{% endblock %}
{% block content %}
{% trans "Copy" as copy_str %}
{% include "wagtailadmin/shared/header.html" with title=copy_str subtitle=page.get_admin_display_title icon="doc-empty-inverse" %}
<div class="nice-padding">
<form action="{% url 'wagtailadmin_pages:copy' page.id %}" method="POST" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<ul class="fields">
{% for field in form.visible_fields %}
{% include "wagtailadmin/shared/field_as_li.html" with field=field %}
{% endfor %}
{% if form.copy_subpages %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.copy_subpages %}
{% endif %}
</ul>
<input type="submit" value="{% trans 'Copy this page' %}" class="button">
</form>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
{% include "wagtailadmin/pages/_editor_js.html" %}
{% endblock %}

View file

@ -6,9 +6,9 @@ from django import template
from django.utils.translation import activate, get_language
try:
from django.core.urlresolvers import resolve
except ImportError:
from django.urls import resolve
except ImportError:
from django.core.urlresolvers import resolve
from six import iteritems

View file

@ -52,9 +52,13 @@ class PatchTestPage(WagtailPage):
@register_snippet
class PatchTestSnippet(models.Model):
class PatchTestSnippetNoPanels(models.Model):
name = models.CharField(max_length=10)
@register_snippet
class PatchTestSnippet(PatchTestSnippetNoPanels):
panels = [
FieldPanel('name')
]
@ -74,7 +78,8 @@ class FieldPanelSnippet(models.Model):
@register_snippet
class ImageChooserPanelSnippet(models.Model):
image = models.ForeignKey(
'wagtailimages.Image'
'wagtailimages.Image',
on_delete=models.CASCADE,
)
panels = [
@ -122,7 +127,10 @@ class MultiFieldPanelSnippet(FieldPanelSnippet, ImageChooserPanelSnippet, FieldR
class BaseInlineModel(MultiFieldPanelSnippet):
field_name = models.CharField(max_length=10)
image_chooser = models.ForeignKey('wagtailimages.Image')
image_chooser = models.ForeignKey(
'wagtailimages.Image',
on_delete=models.CASCADE,
)
fieldrow_name = models.CharField(max_length=10)
@ -158,7 +166,8 @@ class FieldPanelPage(WagtailPage):
class ImageChooserPanelPage(WagtailPage):
image = models.ForeignKey(
'wagtailimages.Image'
'wagtailimages.Image',
on_delete=models.CASCADE,
)
content_panels = [

View file

@ -21,3 +21,5 @@ MODELTRANSLATION_AUTO_POPULATE = False
MODELTRANSLATION_FALLBACK_LANGUAGES = {'default': (MODELTRANSLATION_DEFAULT_LANGUAGE,)}
ROOT_URLCONF = 'wagtail_modeltranslation.tests.urls'
TRANSLATE_SLUGS = True

View file

@ -13,9 +13,10 @@ from django.test.utils import override_settings
from django.utils.translation import get_language, trans_real
from modeltranslation import settings as mt_settings, translator
try:
from wagtail import VERSION
from wagtail.snippets.views.snippets import get_snippet_edit_handler
except ImportError:
VERSION = 1, 6, 3 # assume it's 1.6.3, the latest version without VERSION
from wagtail.wagtailsnippets.views.snippets import get_snippet_edit_handler
from wagtail import VERSION
from .util import page_factory
from wagtail_modeltranslation.tests.test_settings import TEST_SETTINGS
@ -47,8 +48,7 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
if not WagtailModeltranslationTransactionTestBase.synced:
# In order to perform only one syncdb
WagtailModeltranslationTransactionTestBase.synced = True
mgr = (override_settings(**TEST_SETTINGS) if django.VERSION < (1, 8)
else dummy_context_mgr())
mgr = dummy_context_mgr()
with mgr:
# 1. Reload translation in case USE_I18N was False
from django.utils import translation as dj_trans
@ -99,15 +99,14 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
# 5. makemigrations
from django.db import connections, DEFAULT_DB_ALIAS
call_command('makemigrations', verbosity=2, interactive=False,
database=connections[DEFAULT_DB_ALIAS].alias)
call_command('makemigrations', verbosity=2, interactive=False)
# 6. Syncdb
call_command('migrate', verbosity=0, migrate=False, interactive=False, run_syncdb=True,
database=connections[DEFAULT_DB_ALIAS].alias, load_initial_data=False)
call_command('migrate', verbosity=0, interactive=False, run_syncdb=True,
database=connections[DEFAULT_DB_ALIAS].alias)
# 7. Make sure Page translation fields are created
call_command('sync_page_translation_fields', interactive=False, verbosity=0, database=connections[DEFAULT_DB_ALIAS].alias)
call_command('sync_page_translation_fields', interactive=False, verbosity=0)
# 8. patch wagtail models
from wagtail_modeltranslation.patch_wagtailadmin import patch_wagtail_models
@ -308,6 +307,8 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
def test_snippet_patching(self):
self.check_fieldpanel_patching(panels=models.FieldPanelSnippet.panels)
self.check_panels_patching(models.FieldPanelSnippet, ['name_de', 'name_en'])
self.check_imagechooserpanel_patching(panels=models.ImageChooserPanelSnippet.panels)
self.check_fieldrowpanel_patching(panels=models.FieldRowPanelSnippet.panels)
self.check_streamfieldpanel_patching(panels=models.StreamFieldPanelSnippet.panels)
@ -317,6 +318,24 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
# which is the SnippetInlineModel
self.check_inlinepanel_patching(panels=models.SnippetInlineModel.panels)
# Case we don't define panels on snippet
self.check_panels_patching(models.PatchTestSnippetNoPanels, ['name_de', 'name_en'])
def check_panels_patching(self, model, model_fields):
patched_edit_handler = get_snippet_edit_handler(model)
if VERSION[0] < 2:
form = patched_edit_handler.get_form_class(model)
else:
form = patched_edit_handler.get_form_class()
try:
# python 3
self.assertEqual(model_fields, list(form.base_fields.keys()))
except AttributeError:
# python 2.7
self.assertItemsEqual(model_fields, form.base_fields.keys())
def test_page_form(self):
"""
In this test we use the InlinePanelPage model because it has all the possible "patchable" fields
@ -325,7 +344,7 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
page_edit_handler = models.InlinePanelPage.get_edit_handler()
if VERSION[0] < 2:
if VERSION < (2,):
form = page_edit_handler.get_form_class(models.InlinePanelPage)
else:
form = page_edit_handler.get_form_class()
@ -358,13 +377,9 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
In this test we use the InlinePanelSnippet model because it has all the possible "patchable" fields
so if the created form has all fields the the form was correctly patched
"""
try:
from wagtail.snippets.views.snippets import get_snippet_edit_handler
except ImportError:
from wagtail.wagtailsnippets.views.snippets import get_snippet_edit_handler
snippet_edit_handler = get_snippet_edit_handler(models.InlinePanelSnippet)
if VERSION[0] < 2:
if VERSION < (2,):
form = snippet_edit_handler.get_form_class(models.InlinePanelSnippet)
else:
form = snippet_edit_handler.get_form_class()

View file

@ -1,12 +1,29 @@
# coding: utf-8
from modeltranslation.translator import translator, register, TranslationOptions
from wagtail_modeltranslation.tests.models import TestRootPage, TestSlugPage1, TestSlugPage2, PatchTestPage, \
PatchTestSnippet, FieldPanelPage, ImageChooserPanelPage, FieldRowPanelPage, MultiFieldPanelPage, InlinePanelPage, \
FieldPanelSnippet, ImageChooserPanelSnippet, FieldRowPanelSnippet, MultiFieldPanelSnippet, PageInlineModel, \
BaseInlineModel, StreamFieldPanelPage, StreamFieldPanelSnippet, SnippetInlineModel, InlinePanelSnippet, \
TestSlugPage1Subclass, RoutablePageTest
from modeltranslation.translator import (TranslationOptions, register,
translator)
from wagtail_modeltranslation.tests.models import (BaseInlineModel,
FieldPanelPage,
FieldPanelSnippet,
FieldRowPanelPage,
FieldRowPanelSnippet,
ImageChooserPanelPage,
ImageChooserPanelSnippet,
InlinePanelPage,
InlinePanelSnippet,
MultiFieldPanelPage,
MultiFieldPanelSnippet,
PageInlineModel,
PatchTestPage,
PatchTestSnippet,
PatchTestSnippetNoPanels,
RoutablePageTest,
SnippetInlineModel,
StreamFieldPanelPage,
StreamFieldPanelSnippet,
TestRootPage, TestSlugPage1,
TestSlugPage1Subclass,
TestSlugPage2)
# Wagtail Models
@ -35,11 +52,14 @@ class PatchTestPageTranslationOptions(TranslationOptions):
fields = ('description',)
class PatchTestSnippetTranslationOptions(TranslationOptions):
@register(PatchTestSnippetNoPanels)
class PatchTestSnippetNoPanelsTranslationOptions(TranslationOptions):
fields = ('name',)
translator.register(PatchTestSnippet, PatchTestSnippetTranslationOptions)
@register(PatchTestSnippet)
class PatchTestSnippetTranslationOptions(TranslationOptions):
pass
# Panel Patching Models

View file

@ -2,17 +2,23 @@
from modeltranslation.decorators import register
from modeltranslation.translator import TranslationOptions
from wagtail_modeltranslation import settings
try:
from wagtail.core.models import Page
except ImportError:
from wagtail.wagtailcore.models import Page
@register(Page)
class PageTR(TranslationOptions):
fields = (
'title',
'slug',
'seo_title',
'search_description',
'url_path',
)
if settings.TRANSLATE_SLUGS:
fields += (
'slug',
'url_path',
)

View file

@ -2,21 +2,35 @@
import json
from django.core.exceptions import PermissionDenied
from six import iteritems
from django.conf import settings
from django.conf.urls import url
from django.http import HttpResponse
from django.http import QueryDict
from django.utils.html import format_html, format_html_join, escape
from django.http import HttpResponse, QueryDict
from django.shortcuts import redirect, render
from django.templatetags.static import static
from django.utils.html import escape, format_html, format_html_join
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from six import iteritems
from wagtail_modeltranslation import settings as wmt_settings
from modeltranslation import settings as mt_settings
from .patch_wagtailadmin_forms import PatchedCopyForm
try:
from wagtail.core import hooks
from wagtail.core.models import Page
from wagtail.core.rich_text.pages import PageLinkHandler
from wagtail.core import __version__ as WAGTAIL_VERSION
from wagtail.admin import messages
from wagtail.admin.views.pages import get_valid_next_url_from_request
except ImportError:
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore.rich_text import PageLinkHandler
from wagtail.wagtailcore import __version__ as WAGTAIL_VERSION
from wagtail.wagtailadmin import messages
from wagtail.wagtailadmin.views.pages import get_valid_next_url_from_request
@hooks.register('insert_editor_js')
@ -25,15 +39,25 @@ def translated_slugs():
'wagtail_modeltranslation/js/wagtail_translated_slugs.js',
]
js_includes = format_html_join('\n', '<script src="{0}{1}"></script>', (
(settings.STATIC_URL, filename) for filename in js_files)
)
js_includes = format_html_join('\n', '<script src="{0}"></script>', (
(static(filename),) for filename in js_files)
)
lang_codes = []
for lang in settings.LANGUAGES:
lang_codes.append("'%s'" % lang[0])
js_languages = "<script>var langs=[%s];</script>" % (", ".join(lang_codes))
js_languages = """
<script>
var langs=[{langs}];
var default_lang='{default_lang}';
var translate_slugs={translate_slugs};
</script>
""".format(
langs=", ".join(lang_codes),
default_lang=mt_settings.DEFAULT_LANGUAGE,
translate_slugs='true' if wmt_settings.TRANSLATE_SLUGS else 'false'
)
return format_html(js_languages) + js_includes
@ -107,21 +131,26 @@ def streamfields_translation_copy():
# includes the javascript file in the html file
js_files = [
'wagtail_modeltranslation/js/version_compare.js',
'wagtail_modeltranslation/js/copy_stream_fields.js',
]
js_includes = format_html_join('\n', '<script src="{0}{1}"></script>', (
(settings.STATIC_URL, filename) for filename in js_files)
)
js_includes = format_html_join('\n', '<script src="{0}"></script>', (
(static(filename),) for filename in js_files)
)
return js_includes
js_wagtail_version = """
<script>
var WAGTAIL_VERSION='{wagtail_version}';
</script>
""".format(wagtail_version=WAGTAIL_VERSION)
return format_html(js_wagtail_version) + js_includes
@hooks.register('insert_editor_css')
def modeltranslation_page_editor_css():
return format_html('<link rel="stylesheet" href="'
+ settings.STATIC_URL
+ 'wagtail_modeltranslation/css/page_editor_modeltranslation.css" >')
filename = 'wagtail_modeltranslation/css/page_editor_modeltranslation.css'
return format_html('<link rel="stylesheet" href="{}" >'.format(static(filename)))
@hooks.register('register_rich_text_link_handler')
@ -147,3 +176,70 @@ def register_localized_page_link_handler():
return "<a>"
return ('page', LocalizedPageLinkHandler)
@hooks.register('before_copy_page')
def before_copy_page(request, page):
parent_page = page.get_parent()
can_publish = parent_page.permissions_for_user(request.user).can_publish_subpage()
form = PatchedCopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish)
next_url = get_valid_next_url_from_request(request)
if request.method == 'POST':
# Prefill parent_page in case the form is invalid (as prepopulated value for the form field,
# because ModelChoiceField seems to not fall back to the user given value)
parent_page = Page.objects.get(id=request.POST['new_parent_page'])
if form.is_valid():
# Receive the parent page (this should never be empty)
if form.cleaned_data['new_parent_page']:
parent_page = form.cleaned_data['new_parent_page']
if not page.permissions_for_user(request.user).can_copy_to(parent_page,
form.cleaned_data.get('copy_subpages')):
raise PermissionDenied
# Re-check if the user has permission to publish subpages on the new parent
can_publish = parent_page.permissions_for_user(request.user).can_publish_subpage()
update_attrs = {}
for code, name in settings.LANGUAGES:
slug = "slug_{}".format(code)
title = "title_{}".format(code)
update_attrs[slug] = form.cleaned_data["new_{}".format(slug)]
update_attrs[title] = form.cleaned_data["new_{}".format(title)]
# Copy the page
new_page = page.copy(
recursive=form.cleaned_data.get('copy_subpages'),
to=parent_page,
update_attrs=update_attrs,
keep_live=(can_publish and form.cleaned_data.get('publish_copies')),
user=request.user,
)
# Give a success message back to the user
if form.cleaned_data.get('copy_subpages'):
messages.success(
request,
_("Page '{0}' and {1} subpages copied.").format(
page.get_admin_display_title(), new_page.get_descendants().count())
)
else:
messages.success(request, _("Page '{0}' copied.").format(page.get_admin_display_title()))
for fn in hooks.get_hooks('after_copy_page'):
result = fn(request, page, new_page)
if hasattr(result, 'status_code'):
return result
# Redirect to explore of parent page
if next_url:
return redirect(next_url)
return redirect('wagtailadmin_explore', parent_page.id)
return render(request, 'modeltranslation_copy.html', {
'page': page,
'form': form,
'next': next_url
})