mirror of
https://github.com/Hopiu/wagtail-modeltranslation.git
synced 2026-03-17 06:20:33 +00:00
Compare commits
228 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
765ac7a19d | ||
|
|
57d0a348bf | ||
|
|
154acd006c | ||
|
|
18ed10a2a9 | ||
|
|
a92d287a6e | ||
|
|
187e524ed2 | ||
|
|
e742a93f4c | ||
|
|
1366c86151 | ||
|
|
8b945cec37 | ||
|
|
40b2b01648 | ||
|
|
a170f7a213 | ||
|
|
4456f6f902 | ||
|
|
80def8ecde | ||
|
|
29404348cb | ||
|
|
1495f7278a | ||
|
|
6fb4725f65 | ||
|
|
3191abde46 | ||
|
|
86c4711e48 | ||
|
|
6cbd98ee54 | ||
|
|
3034d5807c | ||
|
|
07e0e98b47 | ||
|
|
88e5895c5b | ||
|
|
55fff643d3 | ||
|
|
d630f9c988 | ||
|
|
fe0cd4c0b6 | ||
|
|
8a1a9eb94b | ||
|
|
425438acd1 | ||
|
|
ff8a75479c | ||
|
|
af888092bf | ||
|
|
34e67bd0ed | ||
|
|
da68dd1e40 | ||
|
|
eb5eee8092 | ||
|
|
5ec163b020 | ||
|
|
75facc5f22 | ||
|
|
f9afc8dd17 | ||
|
|
f52993d73c | ||
|
|
58fdc7148b | ||
|
|
f1b04f9202 | ||
|
|
30a6ada4de | ||
|
|
bbe19792c9 | ||
|
|
640d5202c9 | ||
|
|
5ef04f30f2 | ||
|
|
b883f051f3 | ||
|
|
8deb0424f1 | ||
|
|
7d9b42f542 | ||
|
|
4f0b07789c | ||
|
|
372a2f31df | ||
|
|
304554f405 | ||
|
|
d0034364d2 | ||
|
|
89d285555e | ||
|
|
1db9f3947c | ||
|
|
1193528e75 | ||
|
|
466d88fdf2 | ||
|
|
2b09b06eb3 | ||
|
|
b0a4e61d4a | ||
|
|
3f9ac92bc8 | ||
|
|
24df39a0d8 | ||
|
|
2c10234f80 | ||
|
|
3d84c4a78f | ||
|
|
1cb2a41a08 | ||
|
|
8b8f64a7cc | ||
|
|
293d518f8a | ||
|
|
46059f791b | ||
|
|
d9a8c2c06d | ||
|
|
4614b5faa4 | ||
|
|
dcf2651ee7 | ||
|
|
e7adf51d2a | ||
|
|
060491a75e | ||
|
|
7658d2e8d8 | ||
|
|
cd8a9ad5f0 | ||
|
|
54a2729283 | ||
|
|
f7d748b7ee | ||
|
|
d5ecd24d70 | ||
|
|
e84cdc6acb | ||
|
|
531a7494c2 | ||
|
|
7929589e7a | ||
|
|
49246457cb | ||
|
|
11cbedfe13 | ||
|
|
7d69a7135d | ||
|
|
adeb645a69 | ||
|
|
0be5939cdc | ||
|
|
f795622109 | ||
|
|
77a86fb894 | ||
|
|
50d78a1826 | ||
|
|
e40c776da3 | ||
|
|
5d42d99430 | ||
|
|
eac95ec38a | ||
|
|
be57e57077 | ||
|
|
a91a158612 | ||
|
|
7613c7bd0f | ||
|
|
3f1882bad0 | ||
|
|
a92cf200a3 | ||
|
|
2621e6e407 | ||
|
|
c63eee4506 | ||
|
|
5648c0b164 | ||
|
|
c78cac792b | ||
|
|
16e08c30fc | ||
|
|
a61136e028 | ||
|
|
52e0c5aba5 | ||
|
|
7591c015c9 | ||
|
|
6ef46d6cdc | ||
|
|
a323535582 | ||
|
|
8c45ea32fd | ||
|
|
a8b2921b69 | ||
|
|
03e8b0e38c | ||
|
|
b26759205d | ||
|
|
f3ec60184a | ||
|
|
cc83d9881a | ||
|
|
45e7b4adf0 | ||
|
|
ef1e0bf5ee | ||
|
|
cf2bcc5a7f | ||
|
|
7cc4eece20 | ||
|
|
19ad0226f3 | ||
|
|
093b251008 | ||
|
|
f8c3dd369f | ||
|
|
51091640fa | ||
|
|
ebfd3a67dc | ||
|
|
1f747cfd6f | ||
|
|
69ae7dfbd8 | ||
|
|
2135d7d081 | ||
|
|
d542dacbb3 | ||
|
|
06fd1e8bfa | ||
|
|
b3d654eba5 | ||
|
|
080bd89b6f | ||
|
|
eec0108f33 | ||
|
|
77b352e53c | ||
|
|
358dc259e1 | ||
|
|
0b81be9d6e | ||
|
|
7d02d760cd | ||
|
|
2377538f1a | ||
|
|
b591fc1047 | ||
|
|
064625cebd | ||
|
|
feedd129bd | ||
|
|
4ed78783e6 | ||
|
|
b677f43dca | ||
|
|
3c70fd4a0a | ||
|
|
03cd69e8b9 | ||
|
|
3d95413a4c | ||
|
|
e859632484 | ||
|
|
a34e079305 | ||
|
|
539254316c | ||
|
|
2205fd719f | ||
|
|
ee3bda2248 | ||
|
|
8c246f7f89 | ||
|
|
7b05253a39 | ||
|
|
4e38d8b654 | ||
|
|
f02b9294c3 | ||
|
|
b62be23616 | ||
|
|
abeadb6871 | ||
|
|
8e3d549a66 | ||
|
|
6aaa9fbeda | ||
|
|
d6ed53c0fc | ||
|
|
0bca45dcbb | ||
|
|
45c7c9e2c5 | ||
|
|
487741ecbe | ||
|
|
787bbf4344 | ||
|
|
3b463f7ee7 | ||
|
|
8000594114 | ||
|
|
7a9750cbd7 | ||
|
|
238e265ec7 | ||
|
|
ff8a7f7afe | ||
|
|
7f028ffac8 | ||
|
|
90433d18b8 | ||
|
|
fd1cbe2432 | ||
|
|
9e2c2358ae | ||
|
|
066d00b20b | ||
|
|
a90d0faf29 | ||
|
|
f4d89944f1 | ||
|
|
bfb62c0c90 | ||
|
|
271920d6eb | ||
|
|
bd93e06adc | ||
|
|
17bac99fdd | ||
|
|
b58544400a | ||
|
|
127603cd92 | ||
|
|
acf20e2533 | ||
|
|
17b2fe375c | ||
|
|
d3cec5590d | ||
|
|
2af22eda36 | ||
|
|
e6488400c2 | ||
|
|
a1c90e9e97 | ||
|
|
39acb713dc | ||
|
|
5076788eb4 | ||
|
|
efd47b87e1 | ||
|
|
8250693208 | ||
|
|
2ab997dab5 | ||
|
|
4bcd6086e0 | ||
|
|
d22fc4c89c | ||
|
|
252f142493 | ||
|
|
93ffbb09ce | ||
|
|
51216693d5 | ||
|
|
e28c00668a | ||
|
|
d3cb322c33 | ||
|
|
333c392dc8 | ||
|
|
1a656b98da | ||
|
|
3a2c3f9b00 | ||
|
|
fe8bd7e9a4 | ||
|
|
931ee25931 | ||
|
|
eecab72275 | ||
|
|
a60d1888cc | ||
|
|
d10d56a202 | ||
|
|
8751517403 | ||
|
|
fc0e803dc5 | ||
|
|
6a483eae19 | ||
|
|
ffaa485d83 | ||
|
|
bd1dbc6758 | ||
|
|
53ad48e799 | ||
|
|
37eec32f7a | ||
|
|
1f36b55f0c | ||
|
|
1e03faf76e | ||
|
|
e91101977e | ||
|
|
7cf94069b6 | ||
|
|
271bbe513f | ||
|
|
27ac5491f2 | ||
|
|
dca220cb0c | ||
|
|
86caf9a816 | ||
|
|
421a52e155 | ||
|
|
da76ec44a2 | ||
|
|
c114d0d326 | ||
|
|
dd473f1497 | ||
|
|
de3797cc8f | ||
|
|
adf68c5a3b | ||
|
|
281e2f7002 | ||
|
|
791a9c003a | ||
|
|
5361ae777f | ||
|
|
500d9e7932 | ||
|
|
2950a8f75b | ||
|
|
b38d6b67a4 | ||
|
|
0c8f54e529 |
58 changed files with 2717 additions and 564 deletions
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -31,3 +31,19 @@ nosetests.xml
|
|||
|
||||
# Sphinx
|
||||
_build
|
||||
|
||||
# GitEye / Eclipse project file
|
||||
/.project
|
||||
|
||||
# PyCharm
|
||||
/.idea
|
||||
|
||||
# vim
|
||||
*~
|
||||
*.swp
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
|
|
|||
60
.travis.yml
60
.travis.yml
|
|
@ -1,20 +1,56 @@
|
|||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.8"
|
||||
env:
|
||||
- WAGTAIL="wagtail>=1.4,<1.5"
|
||||
- WAGTAIL="wagtail>=1.5,<1.6"
|
||||
- WAGTAIL="wagtail>=1.6,<1.7"
|
||||
- WAGTAIL="wagtail>=1.7,<1.8"
|
||||
- WAGTAIL="wagtail>=1.8,<1.9"
|
||||
- WAGTAIL="wagtail>=1.9,<1.10"
|
||||
- WAGTAIL="wagtail>=2.7,<2.8" DB=sqlite
|
||||
matrix:
|
||||
include:
|
||||
# Latest Wagtail version
|
||||
- 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"
|
||||
- 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.7"
|
||||
env: WAGTAIL="wagtail>=1.13,<1.14"
|
||||
- python: "2.7"
|
||||
env: WAGTAIL="wagtail>=1.12,<1.13"
|
||||
- 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 [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install 'Django>=1.8,<1.9'; fi
|
||||
- pip install -q $WAGTAIL
|
||||
- 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-binary'; fi
|
||||
- pip install $WAGTAIL
|
||||
- pip install -e .
|
||||
script:
|
||||
- echo "DJANGO VERSION = `python -m django --version`"
|
||||
- ./runtests.py
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Contributors
|
|||
* Raphael Grill
|
||||
* Tom Dyson
|
||||
* Tim Tan
|
||||
* Benedikt Willi
|
||||
|
||||
|
||||
.. _Django-modeltranslation: https://github.com/deschler/django-modeltranslation
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
43
Makefile
Normal file
43
Makefile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
.PHONY: clean-pyc clean-build help test
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
help: ## print this help screen
|
||||
@perl -nle'print $& if m{^[a-zA-Z0-9_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
clean: clean-build clean-pyc
|
||||
@echo "all clean now .."
|
||||
|
||||
clean-build: ## remove build artifacts
|
||||
@rm -fr build/
|
||||
@rm -fr dist/
|
||||
@rm -fr htmlcov/
|
||||
@rm -fr *.egg-info
|
||||
@rm -rf .coverage
|
||||
|
||||
clean-pyc: ## remove Python file artifacts
|
||||
@find . -name '*.pyc' -exec rm -f {} +
|
||||
@find . -name '*.pyo' -exec rm -f {} +
|
||||
@find . -name '*.orig' -exec rm -f {} +
|
||||
@find . -name '*~' -exec rm -f {} +
|
||||
|
||||
release: clean ## package and upload a release (working dir must be clean)
|
||||
@while true; do \
|
||||
CURRENT=`python -c "import wagtail_modeltranslation; print(wagtail_modeltranslation.__version__)"`; \
|
||||
echo ""; \
|
||||
echo "=== The current version is $$CURRENT - what's the next one?"; \
|
||||
echo "==========================================================="; \
|
||||
echo "1 - new major version"; \
|
||||
echo "2 - new minor version"; \
|
||||
echo "3 - patch"; \
|
||||
echo "4 - keep the current version"; \
|
||||
echo ""; \
|
||||
read yn; \
|
||||
case $$yn in \
|
||||
1 ) bumpversion major; break;; \
|
||||
2 ) bumpversion minor; break;; \
|
||||
3 ) bumpversion patch; break;; \
|
||||
4 ) break;; \
|
||||
* ) echo "Please answer 1-3.";; \
|
||||
esac \
|
||||
done
|
||||
@python setup.py bdist_wheel && twine upload dist/*
|
||||
2
PKG-INFO
2
PKG-INFO
|
|
@ -1,6 +1,6 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: wagtail-modeltranslation
|
||||
Version: 0.6.0rc1
|
||||
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.
|
||||
|
|
|
|||
11
Pipfile
Normal file
11
Pipfile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[[source]]
|
||||
url = "https://pypi.python.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
bumpversion = "*"
|
||||
wheel = "*"
|
||||
|
||||
[dev-packages]
|
||||
twine = "*"
|
||||
143
Pipfile.lock
generated
Normal file
143
Pipfile.lock
generated
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "191a6f860a13836c57a16587784ceca36f791bfae1270cf937286a496e891114"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.python.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"bumpversion": {
|
||||
"hashes": [
|
||||
"sha256:6744c873dd7aafc24453d8b6a1a0d6d109faf63cd0cd19cb78fd46e74932c77e",
|
||||
"sha256:6753d9ff3552013e2130f7bc03c1007e24473b4835952679653fb132367bdd57"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.3"
|
||||
},
|
||||
"wheel": {
|
||||
"hashes": [
|
||||
"sha256:5e79117472686ac0c4aef5bad5172ea73a1c2d1646b808c35926bd26bdfb0c08",
|
||||
"sha256:62fcfa03d45b5b722539ccbc07b190e4bfff4bb9e3a4d470dd9f6a0981002565"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.33.4"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
62
README.rst
62
README.rst
|
|
@ -35,6 +35,16 @@ Features
|
|||
- StreamFields are now supported!
|
||||
|
||||
|
||||
Caveats
|
||||
======
|
||||
|
||||
:code:`wagtail-modeltranslation` patches Wagtail's :code:`Page` model with translation fields
|
||||
:code:`title_xx`, :code:`slug_xx`, :code:`seo_title_xx`, :code:`search_description_xx` and :code:`url_path_xx` where "xx" represents the language code for each translated language. This
|
||||
is done without migrations through command :code:`sync_page_translation_fields`. Since :code:`Page` model belongs to
|
||||
Wagtail it's within the realm of possibility that one day Wagtail may add a conflicting field to :code:`Page` thus interfering with :code:`wagtail-modeltranslation`.
|
||||
|
||||
Wagtail's :code:`slugurl` tag does not work across languages. :code:`wagtail-modeltranslation` provides a drop-in replacement named :code:`slugurl_trans` which by default takes the slug parameter in the default language.
|
||||
|
||||
Quick start
|
||||
===========
|
||||
|
||||
|
|
@ -42,47 +52,69 @@ Quick start
|
|||
|
||||
pip install wagtail-modeltranslation
|
||||
|
||||
2. Add "wagtail_modeltranslation" to your INSTALLED_APPS setting like this (before all apps that you want to translate)::
|
||||
2. Add 'wagtail_modeltranslation' to your ``INSTALLED_APPS`` setting like this (before all apps that you want to translate)::
|
||||
|
||||
INSTALLED_APPS = (
|
||||
...
|
||||
'wagtail_modeltranslation',
|
||||
'wagtail_modeltranslation.makemigrations',
|
||||
'wagtail_modeltranslation.migrate',
|
||||
)
|
||||
|
||||
3. Add "django.middleware.locale.LocaleMiddleware" to MIDDLEWARE_CLASSES on your settings.py::
|
||||
3. Add 'django.middleware.locale.LocaleMiddleware' to ``MIDDLEWARE`` on your ``settings.py``::
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
MIDDLEWARE = (
|
||||
...
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware', # should be after SessionMiddleware and before CommonMiddleware
|
||||
)
|
||||
|
||||
4. Enable i18n on settings.py::
|
||||
4. Enable i18n on ``settings.py``::
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
5. Define available languages on settings.py::
|
||||
5. Define available languages on ``settings.py``::
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
LANGUAGES = (
|
||||
('pt', u'Português'),
|
||||
('es', u'Espanhol'),
|
||||
('fr', u'Francês'),
|
||||
('pt', _('Portuguese')),
|
||||
('es', _('Spanish')),
|
||||
('fr', _('French')),
|
||||
)
|
||||
|
||||
6. Create translation.py inside the root folder of the app where the model you want to translate exists::
|
||||
6. Create ``translation.py`` inside the root folder of the app where the model you want to translate exists::
|
||||
|
||||
from .models import Foo
|
||||
from wagtail_modeltranslation.translator import WagtailTranslationOptions
|
||||
from modeltranslation.translator import TranslationOptions
|
||||
from modeltranslation.decorators import register
|
||||
|
||||
|
||||
@register(Foo)
|
||||
class FooTR(WagtailTranslationOptions):
|
||||
class FooTR(TranslationOptions):
|
||||
fields = (
|
||||
'body',
|
||||
)
|
||||
|
||||
7. Run :code:`python manage.py makemigrations` followed by :code:`python manage.py migrate`
|
||||
7. Run :code:`python manage.py makemigrations` followed by :code:`python manage.py migrate` (repeat every time you add a new language or register a new model)
|
||||
|
||||
8. Run :code:`python manage.py sync_page_translation_fields` (repeat every time you add a new language)
|
||||
|
||||
9. If you're adding :code:`wagtail-modeltranslation` to an existing site run :code:`python manage.py update_translation_fields`
|
||||
|
||||
|
||||
Upgrade considerations (v0.8)
|
||||
======================
|
||||
|
||||
This version includes breaking changes as some key parts of the app have been re-written:
|
||||
|
||||
- The most important change is that ``Page`` is now patched with translation fields.
|
||||
- ``WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE`` setting has been deprecated.
|
||||
|
||||
To upgrade to this version you need to:
|
||||
|
||||
- Replace the ``WagtailTranslationOptions`` with ``TranslationOptions`` in all translation.py files
|
||||
- Run :code:`python manage.py sync_page_translation_fields` at least once to create ``Page``'s translation fields
|
||||
- Replace any usages of Wagtail's ``{% slugurl ... %}`` for :code:`wagtail-modeltranslation`'s own ``{% slugurl_trans ... %}``
|
||||
- While optional it's recommended to add ``'wagtail_modeltranslation.makemigrations'`` to your INSTALLED_APPS. This will override Django's ``makemigrations`` command to avoid creating spurious ``Page`` migrations.
|
||||
|
||||
Upgrade considerations (v0.6)
|
||||
======================
|
||||
|
|
@ -95,7 +127,7 @@ Most of the changes are related to imports as they change from wagtail-modeltran
|
|||
|
||||
To upgrade to this version you need to:
|
||||
|
||||
- Replace the ``TranslationOption`` with ``WagtailTranslationOptions`` in all translation.py files
|
||||
- Replace the ``TranslationOptions`` with ``WagtailTranslationOptions`` in all translation.py files
|
||||
- The import of the register decorator is now ``from modeltranslation.decorators import register``
|
||||
- The import of translator is now ``from modeltranslation.translator import translator``
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Installation
|
|||
Requirements
|
||||
============
|
||||
|
||||
* Wagtail >= 1.4
|
||||
* Wagtail >= 1.12
|
||||
|
||||
|
||||
|
||||
|
|
@ -34,76 +34,79 @@ Installing using the source
|
|||
|
||||
|
||||
Quick Setup
|
||||
=====
|
||||
===========
|
||||
|
||||
To setup the application please follow these steps:
|
||||
|
||||
1. In the settings/base.py file:
|
||||
1. In your settings file:
|
||||
|
||||
- Add wagtail_modeltranslation to the INSTALLED_APPS
|
||||
- Add 'wagtail_modeltranslation' to ``INSTALLED_APPS``
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: console
|
||||
|
||||
INSTALLED_APPS = (
|
||||
...
|
||||
'wagtail_modeltranslation',
|
||||
)
|
||||
INSTALLED_APPS = (
|
||||
...
|
||||
'wagtail_modeltranslation',
|
||||
'wagtail_modeltranslation.makemigrations',
|
||||
'wagtail_modeltranslation.migrate',
|
||||
)
|
||||
|
||||
- Add 'django.middleware.locale.LocaleMiddleware' to ``MIDDLEWARE`` (``MIDDLEWARE_CLASSES`` before django 1.10).
|
||||
|
||||
- Add django.middleware.locale.LocaleMiddleware to MIDDLEWARE_CLASSES.
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
...
|
||||
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
)
|
||||
.. code-block:: console
|
||||
|
||||
MIDDLEWARE = (
|
||||
...
|
||||
'django.middleware.locale.LocaleMiddleware', # should be after SessionMiddleware and before CommonMiddleware
|
||||
)
|
||||
|
||||
- Set ``USE_I18N = True``
|
||||
|
||||
.. _language_settings:
|
||||
|
||||
- Configure your LANGUAGES.
|
||||
- Configure your ``LANGUAGES`` setting.
|
||||
|
||||
The LANGUAGES variable must contain all languages you will use for translation. The first language is treated as the
|
||||
*default language*.
|
||||
The ``LANGUAGES`` variable must contain all languages you will use for translation. The first language is treated as the *default language*.
|
||||
|
||||
Modeltranslation uses the list of languages to add localized fields to the models registered for translation.
|
||||
For example, to use the languages Portuguese, Spanish and French in your project, set the LANGUAGES variable like this
|
||||
(where ``pt`` is the default language). In required fields the one for the default language is marked as required (for more advanced usage check `django-modeltranslation required_languages <http://django-modeltranslation.readthedocs.io/en/latest/registration.html#required-fields>`_.)
|
||||
Modeltranslation uses the list of languages to add localized fields to the models registered for translation.
|
||||
For example, to use the languages Portuguese, Spanish and French in your project, set the ``LANGUAGES`` variable like this
|
||||
(where ``pt`` is the default language). In required fields the one for the default language is marked as required (for more advanced usage check `django-modeltranslation required_languages <http://django-modeltranslation.readthedocs.io/en/latest/registration.html#required-fields>`_.)
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: console
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
LANGUAGES = (
|
||||
('pt', u'Portugese'),
|
||||
('es', u'Spanish'),
|
||||
('fr', u'French'),
|
||||
)
|
||||
LANGUAGES = (
|
||||
('pt', _('Portuguese')),
|
||||
('es', _('Spanish')),
|
||||
('fr', _('French')),
|
||||
)
|
||||
|
||||
.. warning::
|
||||
.. warning::
|
||||
|
||||
When the LANGUAGES setting isn't present in ``settings/base.py`` (and neither is ``MODELTRANSLATION_LANGUAGES``), it defaults to Django's global LANGUAGES setting instead, and there are quite a few languages in the default!
|
||||
When the ``LANGUAGES`` setting isn't present in ``settings.py`` (and neither is ``MODELTRANSLATION_LANGUAGES``), it defaults to Django's global LANGUAGES setting instead, and there are quite a few languages in the default!
|
||||
|
||||
.. note::
|
||||
|
||||
2. Create a ``translation.py`` file in your app directory and register ``WagtailTranslationOptions`` for every model you want to translate.
|
||||
To learn more about preparing Wagtail for Internationalisation check the `Wagtail i18n docs <http://docs.wagtail.io/en/latest/advanced_topics/i18n/>`_.
|
||||
|
||||
.. code-block:: console
|
||||
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.
|
||||
|
||||
from .models import foo
|
||||
from wagtail_modeltranslation.translation import WagtailTranslationOptions
|
||||
from modeltranslation.decorators import register
|
||||
.. code-block:: console
|
||||
|
||||
@register(foo)
|
||||
class FooTR(WagtailTranslationOptions):
|
||||
fields = (
|
||||
'body',
|
||||
)
|
||||
from .models import foo
|
||||
from modeltranslation.translator import TranslationOptions
|
||||
from modeltranslation.decorators import register
|
||||
|
||||
@register(foo)
|
||||
class FooTR(TranslationOptions):
|
||||
fields = (
|
||||
'body',
|
||||
)
|
||||
|
||||
3. Run ``python manage.py makemigrations`` followed by ``python manage.py migrate``. This will add extra fields in the database.
|
||||
3. Run ``python manage.py makemigrations`` followed by ``python manage.py migrate``. This will add the tranlation fields to the database, repeat every time you add a new language or register a new model.
|
||||
|
||||
4. Run ``python manage.py sync_page_translation_fields``. This will add translation fields to Wagtail's ``Page`` table, repeat every time you add a new language.
|
||||
|
||||
4. Define the panels for the original fields, as you normally would, as wagtail-modeltranslation would generate the panels for the translated fields.
|
||||
5. If you're adding ``wagtail-modeltranslation`` to an existing site run ``python manage.py update_translation_fields``.
|
||||
|
||||
6. Define the panels for the original fields, as you normally would, as wagtail-modeltranslation will generate the panels for the translated fields.
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ Registering models for translation
|
|||
Registering models and their fields used for translation requires the following steps:
|
||||
|
||||
1. Create **translation.py** in your app directory.
|
||||
2. Define the models you want to use, import wagtail-modeltranslation's **WagtailTranslationOptions** and the django-modeltranslation **register** decorator
|
||||
2. Define the models you want to use, import django-modeltranslation's **TranslationOptions** and the django-modeltranslation **register** decorator
|
||||
3. Create a translation option class for every model you want to translate and precede the class with the **@register** decorator.
|
||||
|
||||
The django-modeltranslation application reads the **translation.py** file in your app directory thereby triggering the registration
|
||||
of the translation options found in the file.
|
||||
|
||||
A translation option is a class that declares which model fields are needed for translation. The class must derive from
|
||||
**wagtail_modeltranslation.translator.WagtailTranslationOptions** and it must provide a **field** attribute storing the list of
|
||||
**modeltranslation.translator.TranslationOptions** and it must provide a **field** attribute storing the list of
|
||||
field names. The option class must be registered with the **modeltranslation.decorators.register** instance.
|
||||
|
||||
To illustrate this let's have a look at a simple example using a **Foo** model. The example only contains an **introduction**
|
||||
|
|
@ -27,11 +27,11 @@ Instead of a **Foo** model, this could be any Wagtail model class:
|
|||
.. code-block:: console
|
||||
|
||||
from .models import Foo
|
||||
from wagtail_modeltranslation.translation import WagtailTranslationOptions
|
||||
from modeltranslation.translator import TranslationOptions
|
||||
from modeltranslation.decorators import register
|
||||
|
||||
@register(Foo)
|
||||
class FooTR(WagtailTranslationOptions):
|
||||
class FooTR(TranslationOptions):
|
||||
fields = (
|
||||
'introduction',
|
||||
'body',
|
||||
|
|
@ -96,8 +96,8 @@ Committing fields to database
|
|||
Modeltranslation supports the migration system introduced by Django 1.7. Besides the normal workflow as described in Django's
|
||||
`Migration Docs <https://docs.djangoproject.com/en/1.8/topics/migrations/>`__, you should do a migration whenever one of the following changes have been made to your project:
|
||||
|
||||
- Added or removed a language through ``settings.LANGUAGES`` or ``settings.MODELTRANSLATION LANGUAGES``.
|
||||
- Registered or unregistered a field through ``WagtailTranslationOptions``.
|
||||
- Added or removed a language through ``settings.LANGUAGES`` or ``settings.MODELTRANSLATION LANGUAGES``.
|
||||
- Registered or unregistered a field through ``TranslationOptions``.
|
||||
|
||||
It doesn't matter if you are starting a fresh project or change an existing one, it's always:
|
||||
|
||||
|
|
@ -106,6 +106,8 @@ It doesn't matter if you are starting a fresh project or change an existing one,
|
|||
|
||||
2. ``python manage.py migrate`` to apply the changes.
|
||||
|
||||
3. If you've added a new language ``python manage.py sync_page_translation_fields`` to add `Page` translation fields.
|
||||
|
||||
|
||||
.. _required_langs:
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ Default: ``[]`` (empty list)
|
|||
This setting is used to add custom "simple panel" classes (all panels that contain directly a field value, like FieldPanel or ImageChooserPanel) that need patching but are not included by default, resulting in not being created translated versions of that panel in wagtail admin.
|
||||
If, for example, you're using wagtail-embedvideos the EmbedVideoChooserPanel is not patched by default so you'd need to include the fully qualified class name like the example below. This setting must be a list of fully qualified class names as strings.
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAILMODELTRANSLATION_CUSTOM_SIMPLE_PANELS = ['wagtail_embed_videos.edit_handlers.EmbedVideoChooserPanel']
|
||||
|
||||
|
||||
|
|
@ -22,7 +23,20 @@ If, for example, you're using wagtail-embedvideos the EmbedVideoChooserPanel is
|
|||
|
||||
Default: ``[]`` (empty list)
|
||||
|
||||
This settings behaves as the above but should be used for panels that are composed by other panels (MultiFieldPanel or FieldRowPanel for example).
|
||||
This setting behaves as the above but should be used for panels that are composed by other panels (MultiFieldPanel or FieldRowPanel for example).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
.. code-block:: console
|
||||
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
|
||||
|
|
|
|||
33
docs/caveats.rst
Normal file
33
docs/caveats.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
.. _caveats:
|
||||
|
||||
Caveats
|
||||
=======
|
||||
|
||||
Wagtail's ``Page`` patch
|
||||
------------------------
|
||||
|
||||
``wagtail-modeltranslation`` patches Wagtail's ``Page`` model with translation fields
|
||||
``title_xx``, ``slug_xx``, ``seo_title_xx``, ``search_description_xx`` and ``url_path_xx``
|
||||
where "xx" represents the language code for each translated language. This is done without
|
||||
migrations through :ref:`management_commands-sync_page_translation_fields`. Since
|
||||
``Page`` model belongs to Wagtail it's within the realm of possibility that one day Wagtail
|
||||
may add a conflicting field to ``Page`` thus interfering with ``wagtail-modeltranslation``.
|
||||
|
||||
See also :ref:`management_commands-makemigrations_translation` to better understand how
|
||||
migrations are managed with ``wagtail-modeltranslation``.
|
||||
|
||||
Wagtail's slugurl
|
||||
-----------------
|
||||
|
||||
Wagtail's ``slugurl`` tag does not work across languages. To work around this
|
||||
``wagtail-modeltranslation`` provides a drop-in replacement tag named
|
||||
:ref:`template tags-slugurl_trans` which by default takes the slug parameter in the
|
||||
default language.
|
||||
|
||||
Replace any usages of Wagtail's ``{% slugurl 'default_lang_slug' %}`` for
|
||||
|
||||
.. code-block:: django
|
||||
|
||||
{% load wagtail_modeltranslation %}
|
||||
...
|
||||
{% slugurl_trans 'default_lang_slug' %}
|
||||
|
|
@ -50,6 +50,8 @@ Contents
|
|||
Registering Models
|
||||
advanced settings
|
||||
template tags
|
||||
management commands
|
||||
caveats
|
||||
recommended reading
|
||||
releases/index
|
||||
AUTHORS
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
.. _management_commands:
|
||||
|
||||
Management Commands
|
||||
===================
|
||||
|
||||
.. _management_commands-wagtail_modeltranslation:
|
||||
|
||||
wagtail_modeltranslation
|
||||
------------------------
|
||||
|
||||
wagtail_modeltranslation module adds the following management commands.
|
||||
|
||||
.. _management_commands-update_translation_fields:
|
||||
|
||||
The ``update_translation_fields`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This command is a proxy to ``django-modeltranslation``'s own ``update_translation_fields``, for more details read the
|
||||
corresponding documentation on `django-modeltranslation docs
|
||||
<http://django-modeltranslation.readthedocs.io/en/latest/commands.html#the-update-translation-fields-command>`_.
|
||||
|
||||
In case modeltranslation was installed in an existing project and you
|
||||
have specified to translate fields of models which are already synced to the
|
||||
database, you have to update your database schema.
|
||||
|
||||
Unfortunately the newly added translation fields on the model will be empty
|
||||
then, and your templates will show the translated value of the fields which
|
||||
will be empty in this case. To correctly initialize the default translation
|
||||
field you can use the ``update_translation_fields`` command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py update_translation_fields
|
||||
|
||||
.. _management_commands-sync_page_translation_fields:
|
||||
|
||||
The ``sync_page_translation_fields`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
This command compares the database and translated Page model definition (finding new translation
|
||||
fields) and provides SQL statements to alter ``wagtailcore_page`` table. You should run this command
|
||||
after installation and after adding a new language to your ``settings.LANGUAGES``.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py sync_page_translation_fields
|
||||
|
||||
.. _management_commands-makemigrations_translation:
|
||||
|
||||
The ``makemigrations_translation`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
``wagtail-modeltranslation`` patches Wagtail's ``Page`` model and as consequence Django's original
|
||||
``makemigrations`` commmand will create migrations for ``Page`` which may create conflicts with
|
||||
other migrations. To circumvent this issue ``makemigrations_translation`` hides any ``Page`` model changes
|
||||
and creates all other migrations as usual. Use this command as an alternative to Django's own
|
||||
``makemigrations`` or consider using :ref:`management_commands-makemigrations`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py makemigrations_translation
|
||||
|
||||
.. _management_commands-migrate_translation:
|
||||
|
||||
The ``migrate_translation`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
Since :ref:`management_commands-makemigrations_translation` hides any ``Page`` model changes, Django's own
|
||||
``migrate`` command won't be able to update ``wagtailcore_page`` table with new translation fields. In order to
|
||||
correctly update the database schema a combination of ``migrate`` followed by :ref:`sync_page_translation_fields`
|
||||
is usually required. ``migrate_translation`` provides a shortcut to running these two commands. Use this
|
||||
as an alternative to Django's own ``migrate`` or consider using :ref:`management_commands-migrate`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py migrate_translation
|
||||
|
||||
.. _management_commands-set_translation_url_paths:
|
||||
|
||||
The ``set_translation_url_paths`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Updates url_path translation fields for all pages.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py set_translation_url_paths
|
||||
|
||||
|
||||
.. _management_commands-wagtail_modeltranslation.makemigrations:
|
||||
|
||||
wagtail_modeltranslation.makemigrations
|
||||
---------------------------------------
|
||||
|
||||
To use ``wagtail_modeltranslation.makemigrations`` module commands add ``'wagtail_modeltranslation.makemigrations,'``
|
||||
to ``INSTALLED_APPS``. This module adds the following management commands.
|
||||
|
||||
.. _management_commands-makemigrations:
|
||||
|
||||
The ``makemigrations`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This command is a proxy for :ref:`management_commands-makemigrations_translation`. It has the added benefit of
|
||||
overriding Django's own ``makemigrations`` allowing you to run ``makemigrations`` safely without creating
|
||||
spurious ``Page`` migrations.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py makemigrations
|
||||
|
||||
.. _management_commands-makemigrations_original:
|
||||
|
||||
The ``makemigrations_original`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since Django's ``makemigrations`` is overriden by ``wagtail-modeltranslation``'s version use
|
||||
``makemigrations_original`` to run the Django's original ``makemigrations`` command. Please note
|
||||
this will likely create invalid ``Page`` migrations, do this only if you know what you're doing.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py makemigrations_original
|
||||
|
||||
|
||||
.. _management_commands-wagtail_modeltranslation.migrate:
|
||||
|
||||
wagtail_modeltranslation.migrate
|
||||
---------------------------------
|
||||
|
||||
To use ``wagtail_modeltranslation.migrate`` module commands add ``'wagtail_modeltranslation.migrate,'``
|
||||
to ``INSTALLED_APPS``. This module adds the following management commands.
|
||||
|
||||
.. _management_commands-migrate:
|
||||
|
||||
The ``migrate`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This command is a proxy for :ref:`management_commands-migrate_translation`. It has the added benefit of
|
||||
overriding Django's own ``migrate`` saving the need to additionally run :ref:`sync_page_translation_fields`.
|
||||
See `issue #175
|
||||
<https://github.com/infoportugal/wagtail-modeltranslation/issues/175#issuecomment-368046055>`_ to understand
|
||||
how this command can be used to create translation fields in a test database.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py migrate
|
||||
|
||||
.. _management_commands-migrate_original:
|
||||
|
||||
The ``migrate_original`` Command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since Django's ``migrate`` is overriden by ``wagtail-modeltranslation``'s version use
|
||||
``migrate_original`` to run the Django's original ``migrate`` command. Please note
|
||||
this will not update ``wagtailcore_page`` table with new translation fields, use
|
||||
:ref:`sync_page_translation_fields` for that.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py migrate_original
|
||||
|
|
@ -1,9 +1,41 @@
|
|||
.. _template tags:
|
||||
|
||||
=============
|
||||
Template Tags
|
||||
=============
|
||||
|
||||
change_lang
|
||||
===========
|
||||
|
||||
Use this template tag to get the url of the current page in another language. The only parameter of this template tag is the language code the we want to get the url. Below is an example where we want to get the url of the current page in portuguese.
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: django
|
||||
|
||||
{% load wagtail_modeltranslation %}
|
||||
{% change_lang 'pt' %}
|
||||
|
||||
.. _template tags-slugurl_trans:
|
||||
|
||||
slugurl_trans
|
||||
=============
|
||||
|
||||
Use this template tag as a replacement for ``slugurl``.
|
||||
|
||||
.. code-block:: django
|
||||
|
||||
{% load wagtail_modeltranslation %}
|
||||
{% slugurl_trans 'default_lang_slug' %}
|
||||
{# or #}
|
||||
|
||||
get_available_languages_wmt
|
||||
===========================
|
||||
|
||||
Use this template tag to get the current languages from MODELTRANSLATION_LANGUAGES (or LANGUAGES) from your setting file (or the default settings).
|
||||
|
||||
.. code-block:: django
|
||||
|
||||
{% get_available_languages_wmt as languages %}
|
||||
{% for language in languages %}
|
||||
...
|
||||
{% endfor %}
|
||||
{% slugurl_trans 'pt_lang_slug' 'pt' %}
|
||||
|
|
|
|||
50
runtests.py
50
runtests.py
|
|
@ -5,6 +5,7 @@ import sys
|
|||
import django
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
from wagtail import VERSION
|
||||
|
||||
|
||||
def runtests():
|
||||
|
|
@ -31,14 +32,9 @@ def runtests():
|
|||
})
|
||||
|
||||
# Configure test environment
|
||||
settings.configure(
|
||||
DATABASES=DATABASES,
|
||||
INSTALLED_APPS=(
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
'taggit',
|
||||
'rest_framework',
|
||||
|
||||
import wagtail
|
||||
if VERSION < (2,):
|
||||
WAGTAIL_MODULES = [
|
||||
'wagtail.wagtailcore',
|
||||
'wagtail.wagtailadmin',
|
||||
'wagtail.wagtaildocs',
|
||||
|
|
@ -52,10 +48,41 @@ def runtests():
|
|||
'wagtail.wagtailsites',
|
||||
'wagtail.contrib.settings',
|
||||
'wagtail.contrib.wagtailapi',
|
||||
]
|
||||
WAGTAIL_CORE = 'wagtail.wagtailcore'
|
||||
else:
|
||||
WAGTAIL_MODULES = [
|
||||
'wagtail.core',
|
||||
'wagtail.admin',
|
||||
'wagtail.documents',
|
||||
'wagtail.snippets',
|
||||
'wagtail.users',
|
||||
'wagtail.images',
|
||||
'wagtail.embeds',
|
||||
'wagtail.search',
|
||||
'wagtail.contrib.redirects',
|
||||
'wagtail.contrib.forms',
|
||||
'wagtail.sites',
|
||||
'wagtail.contrib.settings',
|
||||
'wagtail.api'
|
||||
]
|
||||
WAGTAIL_CORE = 'wagtail.core'
|
||||
|
||||
|
||||
settings.configure(
|
||||
DATABASES=DATABASES,
|
||||
INSTALLED_APPS=[
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
'taggit',
|
||||
'rest_framework'] +
|
||||
WAGTAIL_MODULES + [
|
||||
'wagtail_modeltranslation.makemigrations',
|
||||
'wagtail_modeltranslation',
|
||||
|
||||
),
|
||||
],
|
||||
# remove wagtailcore from serialization as translation columns have not been created at this point
|
||||
# (which causes OperationalError: no such column)
|
||||
TEST_NON_SERIALIZED_APPS=[WAGTAIL_CORE],
|
||||
ROOT_URLCONF=None, # tests override urlconf, but it still needs to be defined
|
||||
LANGUAGES=(
|
||||
('en', 'English'),
|
||||
|
|
@ -63,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)
|
||||
|
||||
|
|
|
|||
7
setup.cfg
Normal file
7
setup.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[bumpversion]
|
||||
current_version = 0.10.6
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
[bumpversion:file:wagtail_modeltranslation/__init__.py]
|
||||
|
||||
40
setup.py
40
setup.py
|
|
@ -1,9 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
from distutils.core import setup
|
||||
import re
|
||||
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
def get_version(*file_paths):
|
||||
filename = os.path.join(os.path.dirname(__file__), *file_paths)
|
||||
version_file = open(filename).read()
|
||||
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
|
||||
if version_match:
|
||||
return version_match.group(1)
|
||||
raise RuntimeError('Please assure that the package version is defined as "__version__ = x.x.x" in ' + filename)
|
||||
|
||||
|
||||
version = get_version("wagtail_modeltranslation", "__init__.py")
|
||||
|
||||
setup(
|
||||
name='wagtail-modeltranslation',
|
||||
version='0.6.0rc2',
|
||||
version=version,
|
||||
description='Translates Wagtail CMS models using a registration approach.',
|
||||
long_description=(
|
||||
'The modeltranslation application can be used to translate dynamic '
|
||||
|
|
@ -21,19 +36,26 @@ setup(
|
|||
'wagtail_modeltranslation',
|
||||
'wagtail_modeltranslation.management',
|
||||
'wagtail_modeltranslation.management.commands',
|
||||
'wagtail_modeltranslation.templatetags'],
|
||||
'wagtail_modeltranslation.templatetags',
|
||||
'wagtail_modeltranslation.makemigrations',
|
||||
'wagtail_modeltranslation.makemigrations.management',
|
||||
'wagtail_modeltranslation.makemigrations.management.commands',
|
||||
'wagtail_modeltranslation.migrate',
|
||||
'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=['wagtail(>=1.4)', 'django-modeltranslation(<=0.12.99)'],
|
||||
download_url='https://github.com/infoportugal/wagtail-modeltranslation/archive/v0.6rc2.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
261
tox.ini
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
__version__ = '0.10.6'
|
||||
default_app_config = 'wagtail_modeltranslation.apps.ModeltranslationConfig'
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
21
wagtail_modeltranslation/contextlib.py
Normal file
21
wagtail_modeltranslation/contextlib.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from django.utils.translation import activate
|
||||
from modeltranslation.utils import get_language
|
||||
|
||||
|
||||
class use_language:
|
||||
"""
|
||||
Context manager to safely change language momentarily
|
||||
|
||||
Usage:
|
||||
with use_language('en'):
|
||||
en_url = obj.get_absolute_url()
|
||||
"""
|
||||
def __init__(self, lang):
|
||||
self.language = lang
|
||||
self.current_language = get_language()
|
||||
|
||||
def __enter__(self):
|
||||
activate(self.language)
|
||||
|
||||
def __exit__(self, exctype, excinst, exctb):
|
||||
activate(self.current_language)
|
||||
3
wagtail_modeltranslation/makemigrations/__init__.py
Normal file
3
wagtail_modeltranslation/makemigrations/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
default_app_config = 'wagtail_modeltranslation.makemigrations.apps.MakemigrationsConfig'
|
||||
7
wagtail_modeltranslation/makemigrations/apps.py
Normal file
7
wagtail_modeltranslation/makemigrations/apps.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MakemigrationsConfig(AppConfig):
|
||||
name = 'wagtail_modeltranslation.makemigrations'
|
||||
label = 'wagtail_modeltranslation_makemigrations'
|
||||
verbose_name = "Wagtail Modeltranslation makemigrations"
|
||||
0
wagtail_modeltranslation/makemigrations/management/commands/__init__.py
Executable file
0
wagtail_modeltranslation/makemigrations/management/commands/__init__.py
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
from wagtail_modeltranslation.management.commands.makemigrations_translation import Command as MakeMigrationsCommand
|
||||
|
||||
|
||||
class Command(MakeMigrationsCommand):
|
||||
pass
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from django.core.management.commands.makemigrations import Command as MakeMigrationsCommand
|
||||
|
||||
|
||||
class Command(MakeMigrationsCommand):
|
||||
pass
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from django.core.management.commands.makemigrations import Command as MakeMigrationsCommand
|
||||
from django.db.migrations.autodetector import MigrationAutodetector
|
||||
import copy
|
||||
|
||||
|
||||
def autodetector_decorator(func):
|
||||
def wrapper(self, from_state, to_state, questioner=None):
|
||||
# Replace to_state.app_configs.models and to_state.models' version of page with the old one
|
||||
# so no changes are detected by MigrationAutodetector
|
||||
from_state_page = from_state.concrete_apps.get_model('wagtailcore', 'page')
|
||||
new_to_state = copy.deepcopy(to_state)
|
||||
new_to_state.apps.app_configs['wagtailcore'].models['page'] = from_state_page
|
||||
new_to_state.models['wagtailcore', 'page'] = from_state.models['wagtailcore', 'page']
|
||||
|
||||
return func(self, from_state, new_to_state, questioner)
|
||||
return wrapper
|
||||
|
||||
|
||||
class Command(MakeMigrationsCommand):
|
||||
help = "Creates new migration(s) for apps except wagtailcore's Page."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
old_autodetector_init = MigrationAutodetector.__init__
|
||||
MigrationAutodetector.__init__ = autodetector_decorator(MigrationAutodetector.__init__)
|
||||
|
||||
try:
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
finally:
|
||||
MigrationAutodetector.__init__ = old_autodetector_init
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
from django.core.management.commands.migrate import Command as MigrateCommand
|
||||
from django.db.migrations.autodetector import MigrationAutodetector
|
||||
from .sync_page_translation_fields import Command as SyncPageTranslationFieldsCommand
|
||||
|
||||
|
||||
# decorate MigrationAutodetector.changes so we can silence any wagtailcore migrations missing warnings
|
||||
def changes_decorator(func):
|
||||
def wrapper(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
|
||||
changes = func(self, graph, trim_to_apps, convert_apps, migration_name)
|
||||
if 'wagtailcore' in changes:
|
||||
del changes['wagtailcore']
|
||||
return changes
|
||||
return wrapper
|
||||
|
||||
|
||||
class Command(MigrateCommand):
|
||||
help = "Updates database schema. Manages both apps with migrations and those without. " \
|
||||
"Updates Wagtail Page translation fields"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
old_autodetector_changes = MigrationAutodetector.changes
|
||||
MigrationAutodetector.changes = changes_decorator(MigrationAutodetector.changes)
|
||||
|
||||
try:
|
||||
super(Command, self).handle(*args, **options)
|
||||
finally:
|
||||
MigrationAutodetector.changes = old_autodetector_changes
|
||||
|
||||
# Run sync_page_translation_fields command
|
||||
sync_page_command = SyncPageTranslationFieldsCommand()
|
||||
# Update the dict of sync_page_command with the content of this one
|
||||
sync_page_command.__dict__.update(self.__dict__)
|
||||
sync_page_command.handle(*args, **options)
|
||||
|
|
@ -1,36 +1,31 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from modeltranslation import settings as mt_settings
|
||||
from modeltranslation.utils import build_localized_fieldname
|
||||
from wagtail_modeltranslation.contextlib import use_language
|
||||
try:
|
||||
from wagtail.core.models import Page
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def set_subtree(self, root, root_path, lang=None):
|
||||
update_fields = ['url_path_' + lang] if hasattr(root.specific, 'url_path_' + lang) else ['url_path']
|
||||
def __init__(self):
|
||||
super(Command, self).__init__()
|
||||
update_fields = ['url_path']
|
||||
for language in mt_settings.AVAILABLE_LANGUAGES:
|
||||
localized_url_path = build_localized_fieldname('url_path', language)
|
||||
update_fields.append(localized_url_path)
|
||||
self.update_fields = update_fields
|
||||
|
||||
if hasattr(root.specific, 'url_path_' + lang):
|
||||
setattr(root.specific, 'url_path_' + lang, root_path)
|
||||
else:
|
||||
setattr(root, 'url_path', root_path)
|
||||
|
||||
if lang == settings.LANGUAGE_CODE:
|
||||
setattr(root, 'url_path', root_path)
|
||||
update_fields.append('url_path')
|
||||
root.specific.save(update_fields=update_fields)
|
||||
def set_subtree(self, root, parent):
|
||||
root.set_url_path(parent)
|
||||
root.save(update_fields=self.update_fields)
|
||||
for child in root.get_children():
|
||||
slug = getattr(
|
||||
child.specific, 'slug_' + lang) if hasattr(
|
||||
child.specific, 'slug_' + lang) else getattr(child, 'slug')
|
||||
if not slug or slug == '':
|
||||
slug = getattr(
|
||||
child.specific, 'slug_' + settings.LANGUAGE_CODE) if hasattr(child.specific,
|
||||
'slug_' + settings.LANGUAGE_CODE) and getattr(
|
||||
child.specific, 'slug_' + settings.LANGUAGE_CODE) else getattr(child, 'slug')
|
||||
self.set_subtree(child, root)
|
||||
|
||||
self.set_subtree(child, root_path + slug + '/', lang)
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
for node in Page.get_root_nodes():
|
||||
for lang in settings.LANGUAGES:
|
||||
self.set_subtree(node, '/', lang=lang[0])
|
||||
def handle(self, **options):
|
||||
with use_language(mt_settings.DEFAULT_LANGUAGE):
|
||||
for node in Page.get_root_nodes():
|
||||
self.set_subtree(node, None)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
from modeltranslation.management.commands.sync_translation_fields import Command as SyncTranslationsFieldsCommand
|
||||
from modeltranslation.translator import translator
|
||||
try:
|
||||
from wagtail.core.models import Page
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
old_get_registered_models = translator.get_registered_models
|
||||
|
||||
# Monkey patching, only return a model if it's Page
|
||||
def get_page_model(self, abstract=True):
|
||||
models = old_get_registered_models(abstract)
|
||||
return [x for x in models if x is Page]
|
||||
|
||||
|
||||
class Command(SyncTranslationsFieldsCommand):
|
||||
help = ("Detect new translatable fields or new available languages and"
|
||||
" sync Wagtail's Page database structure. Does not remove "
|
||||
" columns of removed languages or undeclared fields.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
translator.get_registered_models = get_page_model.__get__(translator)
|
||||
|
||||
try:
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
finally:
|
||||
translator.get_registered_models = old_get_registered_models
|
||||
|
|
@ -1,77 +1,9 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection
|
||||
from django.db.models import F, Q
|
||||
from modeltranslation.settings import DEFAULT_LANGUAGE
|
||||
from modeltranslation.translator import translator
|
||||
from modeltranslation.utils import build_localized_fieldname
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from modeltranslation.management.commands.update_translation_fields import Command as UpdateTranslationFieldsCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = ('Updates empty values of default translation fields using'
|
||||
' values from original fields (in all translated models).')
|
||||
class Command(UpdateTranslationFieldsCommand):
|
||||
pass
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
verbosity = int(options['verbosity'])
|
||||
if verbosity > 0:
|
||||
self.stdout.write(
|
||||
"Using default language: %s\n" % DEFAULT_LANGUAGE)
|
||||
models = translator.get_registered_models(abstract=False)
|
||||
for model in models:
|
||||
if verbosity > 0:
|
||||
self.stdout.write("Updating data of model '%s'\n" % model)
|
||||
opts = translator.get_options_for_model(model)
|
||||
for field_name in opts.fields.keys():
|
||||
def_lang_fieldname = build_localized_fieldname(
|
||||
field_name, DEFAULT_LANGUAGE)
|
||||
|
||||
# We'll only update fields which do not have an existing value
|
||||
q = Q(**{def_lang_fieldname: None})
|
||||
field = model._meta.get_field(field_name)
|
||||
if field.empty_strings_allowed:
|
||||
q |= Q(**{def_lang_fieldname: ''})
|
||||
|
||||
if issubclass(model, Page):
|
||||
for obj in model._default_manager.filter(q):
|
||||
# Get table description in order to know if field is
|
||||
# in child or parent table
|
||||
# TODO: Tested only on PostgreSQL engine
|
||||
db_table = model._meta.db_table
|
||||
db_table_desc = connection.introspection. \
|
||||
get_table_description(
|
||||
connection.cursor(), db_table)
|
||||
# original field in child class
|
||||
if field_name in [x.name for x in db_table_desc]:
|
||||
raw = model._default_manager.raw(
|
||||
'SELECT *, %s AS original_field FROM %s \
|
||||
WHERE page_ptr_id=%d LIMIT 1' % (
|
||||
field_name, db_table, obj.page_ptr_id))[0]
|
||||
setattr(
|
||||
obj, def_lang_fieldname, raw.original_field)
|
||||
# field is a foreign key
|
||||
elif (field_name + '_id') in \
|
||||
[x.name for x in db_table_desc]:
|
||||
raw = model._default_manager.raw(
|
||||
'SELECT *, %s AS original_field FROM %s \
|
||||
WHERE page_ptr_id=%d LIMIT 1' % (
|
||||
field_name + '_id',
|
||||
db_table,
|
||||
obj.page_ptr_id))[0]
|
||||
setattr(
|
||||
obj,
|
||||
def_lang_fieldname + '_id',
|
||||
raw.original_field)
|
||||
# original field parent class
|
||||
else:
|
||||
raw = Page._default_manager.raw(
|
||||
'SELECT *, %s AS original_field FROM \
|
||||
wagtailcore_page WHERE id=%d LIMIT 1' % (
|
||||
field_name, obj.page_ptr_id))[0]
|
||||
setattr(
|
||||
obj, def_lang_fieldname, raw.original_field)
|
||||
obj.save(update_fields=[def_lang_fieldname])
|
||||
else:
|
||||
model._default_manager.filter(q).rewrite(False).update(
|
||||
**{def_lang_fieldname: F(field_name)})
|
||||
|
|
|
|||
3
wagtail_modeltranslation/migrate/__init__.py
Normal file
3
wagtail_modeltranslation/migrate/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
default_app_config = 'wagtail_modeltranslation.migrate.apps.MigrateConfig'
|
||||
7
wagtail_modeltranslation/migrate/apps.py
Normal file
7
wagtail_modeltranslation/migrate/apps.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MigrateConfig(AppConfig):
|
||||
name = 'wagtail_modeltranslation.migrate'
|
||||
label = 'wagtail_modeltranslation_migrate'
|
||||
verbose_name = "Wagtail Modeltranslation migrate"
|
||||
0
wagtail_modeltranslation/migrate/management/__init__.py
Normal file
0
wagtail_modeltranslation/migrate/management/__init__.py
Normal file
0
wagtail_modeltranslation/migrate/management/commands/__init__.py
Executable file
0
wagtail_modeltranslation/migrate/management/commands/__init__.py
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
from wagtail_modeltranslation.management.commands.migrate_translation import Command as MigrateCommand
|
||||
|
||||
|
||||
class Command(MigrateCommand):
|
||||
pass
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from django.core.management.commands.migrate import Command as MigrateCommand
|
||||
|
||||
|
||||
class Command(MigrateCommand):
|
||||
pass
|
||||
|
|
@ -1,29 +1,51 @@
|
|||
# coding: utf-8
|
||||
import copy
|
||||
import logging
|
||||
import types
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
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
|
||||
from django.http import Http404
|
||||
from django.utils.translation import trans_real
|
||||
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
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, \
|
||||
MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel, RichTextFieldPanel
|
||||
from wagtail.wagtailcore.models import Page, Site
|
||||
from wagtail.wagtailcore.url_routing import RouteResult
|
||||
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
|
||||
|
||||
from wagtail_modeltranslation.settings import CUSTOM_SIMPLE_PANELS, CUSTOM_COMPOSED_PANELS
|
||||
try:
|
||||
from wagtail.contrib.routable_page.models import RoutablePageMixin
|
||||
from wagtail.admin.edit_handlers import FieldPanel, \
|
||||
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.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,\
|
||||
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.views.snippets import SNIPPET_EDIT_HANDLERS
|
||||
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')
|
||||
|
||||
|
|
@ -87,28 +109,43 @@ class WagtailTranslator(object):
|
|||
translated_field.field_name = build_localized_fieldname(field.field_name, language)
|
||||
model.search_fields = list(model.search_fields) + [translated_field]
|
||||
|
||||
# OVERRIDE PAGE METHODS
|
||||
# OVERRIDE FIELDS
|
||||
model_fields = model._meta.get_fields()
|
||||
for field in model_fields:
|
||||
if isinstance(field, StreamField) and field.name in translation_registered_fields:
|
||||
descriptor = getattr(model, field.name)
|
||||
_patch_stream_field_meaningful_value(descriptor)
|
||||
|
||||
model.move = _new_move
|
||||
model.set_url_path = _new_set_url_path
|
||||
model.route = _new_route
|
||||
model.get_site_root_paths = _new_get_site_root_paths
|
||||
model.relative_url = _new_relative_url
|
||||
model.url = _new_url
|
||||
_patch_clean(model)
|
||||
# OVERRIDE PAGE METHODS
|
||||
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))
|
||||
|
||||
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):
|
||||
"""
|
||||
|
|
@ -149,6 +186,14 @@ class WagtailTranslator(object):
|
|||
if not original_field.blank and language == mt_settings.DEFAULT_LANGUAGE:
|
||||
localized_field = model._meta.get_field(localized_field_name)
|
||||
localized_field.blank = False
|
||||
elif isinstance(original_field, StreamField):
|
||||
# otherwise the field is optional and
|
||||
# if it's a StreamField the stream_block need to be changed to non required
|
||||
localized_field = model._meta.get_field(localized_field_name)
|
||||
new_stream_block = copy.copy(localized_field.stream_block)
|
||||
new_stream_block.meta = copy.copy(localized_field.stream_block.meta)
|
||||
new_stream_block.meta.required = False
|
||||
localized_field.stream_block = new_stream_block
|
||||
|
||||
localized_panel = panel_class(localized_field_name)
|
||||
|
||||
|
|
@ -200,26 +245,33 @@ class WagtailTranslator(object):
|
|||
# patched, leaving the original untouched
|
||||
return panel
|
||||
|
||||
|
||||
# Overridden Page methods adapted to the translated fields
|
||||
|
||||
@transaction.atomic # only commit when all descendants are properly updated
|
||||
def _new_move(self, target, pos=None):
|
||||
"""
|
||||
Extension to the treebeard 'move' method to ensure that url_path is updated too.
|
||||
"""
|
||||
old_url_path = Page.objects.get(id=self.id).url_path
|
||||
super(Page, self).move(target, pos=pos)
|
||||
# treebeard's move method doesn't actually update the in-memory instance, so we need to work
|
||||
# with a freshly loaded one now
|
||||
# added .specific to use the most specific class so that url_paths are updated to all languages
|
||||
new_self = Page.objects.get(id=self.id).specific
|
||||
new_url_path = new_self.set_url_path(new_self.get_parent())
|
||||
new_self.save()
|
||||
new_self._update_descendant_url_paths(old_url_path, new_url_path)
|
||||
|
||||
# Log
|
||||
logger.info("Page moved: \"%s\" id=%d path=%s", self.title, self.id, new_url_path)
|
||||
def _localized_set_url_path(page, parent, language):
|
||||
"""
|
||||
Updates a localized url_path for a given language
|
||||
"""
|
||||
localized_slug_field = build_localized_fieldname('slug', language)
|
||||
default_localized_slug_field = build_localized_fieldname('slug', mt_settings.DEFAULT_LANGUAGE)
|
||||
localized_url_path_field = build_localized_fieldname('url_path', language)
|
||||
default_localized_url_path_field = build_localized_fieldname('url_path', mt_settings.DEFAULT_LANGUAGE)
|
||||
|
||||
if parent:
|
||||
# Emulate the default behavior of django-modeltranslation to get the slug and url path
|
||||
# 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
|
||||
parent_url_path = getattr(parent, localized_url_path_field, None) or \
|
||||
getattr(parent, default_localized_url_path_field, None) or parent.url_path
|
||||
|
||||
setattr(page, localized_url_path_field, parent_url_path + slug + '/')
|
||||
|
||||
else:
|
||||
# a page without a parent is the tree root,
|
||||
# which always has a url_path of '/'
|
||||
setattr(page, localized_url_path_field, '/')
|
||||
|
||||
|
||||
def _new_set_url_path(self, parent):
|
||||
|
|
@ -229,32 +281,7 @@ def _new_set_url_path(self, parent):
|
|||
by page slug.
|
||||
"""
|
||||
for language in mt_settings.AVAILABLE_LANGUAGES:
|
||||
localized_slug_field = build_localized_fieldname('slug', language)
|
||||
default_localized_slug_field = build_localized_fieldname('slug', mt_settings.DEFAULT_LANGUAGE)
|
||||
localized_url_path_field = build_localized_fieldname('url_path', language)
|
||||
default_localized_url_path_field = build_localized_fieldname('url_path', mt_settings.DEFAULT_LANGUAGE)
|
||||
|
||||
if parent:
|
||||
parent = parent.specific
|
||||
|
||||
# Emulate the default behavior of django-modeltranslation to get the slug and url path
|
||||
# for the current language. If the value for the current language is invalid we get the one
|
||||
# for the default fallback language
|
||||
slug = getattr(self, localized_slug_field, None) or getattr(self, default_localized_slug_field, self.slug)
|
||||
parent_url_path = getattr(parent, localized_url_path_field, None) or \
|
||||
getattr(parent, default_localized_url_path_field, parent.url_path)
|
||||
|
||||
setattr(self, localized_url_path_field, parent_url_path + slug + '/')
|
||||
|
||||
else:
|
||||
# a page without a parent is the tree root,
|
||||
# which always has a url_path of '/'
|
||||
setattr(self, localized_url_path_field, '/')
|
||||
|
||||
# update url_path for children pages
|
||||
for child in self.get_children().specific():
|
||||
child.set_url_path(self.specific)
|
||||
child.save()
|
||||
_localized_set_url_path(self, parent, language)
|
||||
|
||||
return self.url_path
|
||||
|
||||
|
|
@ -263,6 +290,20 @@ def _new_route(self, request, path_components):
|
|||
"""
|
||||
Rewrite route method in order to handle languages fallbacks
|
||||
"""
|
||||
# copied from wagtail/contrib/wagtailroutablepage/models.py mixin ##
|
||||
# Override route when Page is also RoutablePage
|
||||
if isinstance(self, RoutablePageMixin):
|
||||
if self.live:
|
||||
try:
|
||||
path = '/'
|
||||
if path_components:
|
||||
path += '/'.join(path_components) + '/'
|
||||
|
||||
view, args, kwargs = self.resolve_subpage(path)
|
||||
return RouteResult(self, args=(view, args, kwargs))
|
||||
except Http404:
|
||||
pass
|
||||
|
||||
if path_components:
|
||||
# request is for a child of this page
|
||||
child_slug = path_components[0]
|
||||
|
|
@ -270,7 +311,7 @@ def _new_route(self, request, path_components):
|
|||
|
||||
subpages = self.get_children()
|
||||
for page in subpages:
|
||||
if page.specific.slug == child_slug:
|
||||
if page.slug == child_slug:
|
||||
return page.specific.route(request, remaining_components)
|
||||
raise Http404
|
||||
|
||||
|
|
@ -282,61 +323,6 @@ def _new_route(self, request, path_components):
|
|||
raise Http404
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _new_get_site_root_paths():
|
||||
"""
|
||||
Return a list of (root_path, root_url) tuples, most specific path first -
|
||||
used to translate url_paths into actual URLs with hostnames
|
||||
|
||||
Same method as Site.get_site_root_paths() but without cache
|
||||
|
||||
TODO: remake this method with cache and think of his integration in
|
||||
Site.get_site_root_paths()
|
||||
"""
|
||||
result = [
|
||||
(site.id, site.root_page.specific.url_path, site.root_url)
|
||||
for site in Site.objects.select_related('root_page').order_by('-root_page__url_path')
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _new_relative_url(self, current_site):
|
||||
"""
|
||||
Return the 'most appropriate' URL for this page taking into account the site we're currently on;
|
||||
a local URL if the site matches, or a fully qualified one otherwise.
|
||||
Return None if the page is not routable.
|
||||
|
||||
Override for using custom get_site_root_paths() instead of
|
||||
Site.get_site_root_paths()
|
||||
"""
|
||||
for (id, root_path, root_url) in self.get_site_root_paths():
|
||||
if self.url_path.startswith(root_path):
|
||||
return ('' if current_site.id == id else root_url) + reverse('wagtail_serve',
|
||||
args=(self.url_path[len(root_path):],))
|
||||
|
||||
|
||||
@property
|
||||
def _new_url(self):
|
||||
"""
|
||||
Return the 'most appropriate' URL for referring to this page from the pages we serve,
|
||||
within the Wagtail backend and actual website templates;
|
||||
this is the local URL (starting with '/') if we're only running a single site
|
||||
(i.e. we know that whatever the current page is being served from, this link will be on the
|
||||
same domain), and the full URL (with domain) if not.
|
||||
Return None if the page is not routable.
|
||||
|
||||
Override for using custom get_site_root_paths() instead of
|
||||
Site.get_site_root_paths()
|
||||
"""
|
||||
root_paths = self.get_site_root_paths()
|
||||
|
||||
for (id, root_path, root_url) in root_paths:
|
||||
if self.url_path.startswith(root_path):
|
||||
return ('' if len(root_paths) == 1 else root_url) + reverse(
|
||||
'wagtail_serve', args=(self.url_path[len(root_path):],))
|
||||
|
||||
|
||||
def _validate_slugs(page):
|
||||
"""
|
||||
Determine whether the given slug is available for use on a child page of
|
||||
|
|
@ -351,7 +337,7 @@ def _validate_slugs(page):
|
|||
# Save the current active language
|
||||
current_language = get_language()
|
||||
|
||||
siblings = page.get_siblings(inclusive=False).specific()
|
||||
siblings = page.get_siblings(inclusive=False)
|
||||
|
||||
errors = {}
|
||||
|
||||
|
|
@ -364,7 +350,7 @@ def _validate_slugs(page):
|
|||
|
||||
siblings_slugs = [sibling.slug for sibling in siblings]
|
||||
|
||||
if page.specific.slug in siblings_slugs:
|
||||
if page.slug in siblings_slugs:
|
||||
errors[build_localized_fieldname('slug', language)] = _("This slug is already in use")
|
||||
|
||||
# Re-enable the original language
|
||||
|
|
@ -388,6 +374,195 @@ def _patch_clean(model):
|
|||
model.clean = clean
|
||||
|
||||
|
||||
def _new_update_descendant_url_paths(self, old_url_path, new_url_path):
|
||||
return _localized_update_descendant_url_paths(self, old_url_path, new_url_path)
|
||||
|
||||
|
||||
def _localized_update_descendant_url_paths(page, old_url_path, new_url_path, language=None):
|
||||
localized_url_path = 'url_path'
|
||||
if language:
|
||||
localized_url_path = build_localized_fieldname('url_path', language)
|
||||
|
||||
if connection.vendor in ('mssql', 'microsoft'):
|
||||
cursor = connection.cursor()
|
||||
update_statement = """
|
||||
UPDATE wagtailcore_page
|
||||
SET {localized_url_path}= CONCAT(%s, (SUBSTRING({localized_url_path}, 0, %s)))
|
||||
WHERE path LIKE %s AND id <> %s
|
||||
""".format(localized_url_path=localized_url_path)
|
||||
cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, page.path + '%', page.id])
|
||||
else:
|
||||
(Page.objects
|
||||
.rewrite(False)
|
||||
.filter(path__startswith=page.path)
|
||||
.exclude(**{localized_url_path: None}) # url_path_xx may not be set yet
|
||||
.exclude(pk=page.pk)
|
||||
.update(**{localized_url_path: Concat(
|
||||
Value(new_url_path),
|
||||
Substr(localized_url_path, len(old_url_path) + 1))}))
|
||||
|
||||
|
||||
def _localized_site_get_site_root_paths():
|
||||
"""
|
||||
Localized version of ``Site.get_site_root_paths()``
|
||||
"""
|
||||
current_language = get_language()
|
||||
cache_key = 'wagtail_site_root_paths_{}'.format(current_language)
|
||||
result = cache.get(cache_key)
|
||||
|
||||
if result is None:
|
||||
result = [
|
||||
(site.id, site.root_page.url_path, site.root_url)
|
||||
for site in Site.objects.select_related('root_page').order_by('-root_page__url_path')
|
||||
]
|
||||
cache.set(cache_key, result, 3600)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _new_get_site_root_paths(self, request=None):
|
||||
"""
|
||||
Return localized site_root_paths, using the cached copy on the
|
||||
request object if available and if language is the same.
|
||||
"""
|
||||
# if we have a request, use that to cache site_root_paths; otherwise, use self
|
||||
current_language = get_language()
|
||||
cache_object = request if request else self
|
||||
if not hasattr(cache_object, '_wagtail_cached_site_root_paths_language') or \
|
||||
cache_object._wagtail_cached_site_root_paths_language != current_language:
|
||||
cache_object._wagtail_cached_site_root_paths_language = current_language
|
||||
cache_object._wagtail_cached_site_root_paths = _localized_site_get_site_root_paths()
|
||||
|
||||
return cache_object._wagtail_cached_site_root_paths
|
||||
|
||||
|
||||
def _new_get_url_parts(self, request=None):
|
||||
"""
|
||||
For Wagtail<1.11 ``Page.get_url_parts()`` is patched so it uses ``self._get_site_root_paths(request)``
|
||||
"""
|
||||
for (site_id, root_path, root_url) in self._get_site_root_paths(request):
|
||||
if self.url_path.startswith(root_path):
|
||||
page_path = reverse('wagtail_serve', args=(self.url_path[len(root_path):],))
|
||||
|
||||
# Remove the trailing slash from the URL reverse generates if
|
||||
# WAGTAIL_APPEND_SLASH is False and we're not trying to serve
|
||||
# the root path
|
||||
if not WAGTAIL_APPEND_SLASH and page_path != '/':
|
||||
page_path = page_path.rstrip('/')
|
||||
|
||||
return (site_id, root_url, page_path)
|
||||
|
||||
|
||||
def _update_translation_descendant_url_paths(old_record, page):
|
||||
# update children paths, must be done for all languages to ensure fallbacks are applied
|
||||
languages_changed = []
|
||||
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) or ''
|
||||
new_url_path = getattr(page, localized_url_path) or getattr(page, default_localized_url_path)
|
||||
|
||||
if old_url_path == new_url_path:
|
||||
# nothing to do
|
||||
continue
|
||||
|
||||
languages_changed.append(language)
|
||||
_localized_update_descendant_url_paths(page, old_url_path, new_url_path, language)
|
||||
|
||||
_update_untranslated_descendants_url_paths(page, languages_changed)
|
||||
|
||||
|
||||
def _update_untranslated_descendants_url_paths(page, languages_changed):
|
||||
"""
|
||||
Updates localized URL Paths for child pages that don't have their localized URL Paths set yet
|
||||
"""
|
||||
if not languages_changed:
|
||||
return
|
||||
|
||||
condition = Q()
|
||||
update_fields = []
|
||||
for language in languages_changed:
|
||||
localized_url_path = build_localized_fieldname('url_path', language)
|
||||
condition |= Q(**{localized_url_path: None})
|
||||
update_fields.append(localized_url_path)
|
||||
|
||||
# let's restrict the query to children who don't have localized_url_path set yet
|
||||
children = page.get_children().filter(condition)
|
||||
for child in children:
|
||||
for language in languages_changed:
|
||||
_localized_set_url_path(child, page, language)
|
||||
child.save(update_fields=update_fields) # this will trigger any required saves downstream
|
||||
|
||||
|
||||
class LocalizedSaveDescriptor(object):
|
||||
def __init__(self, f):
|
||||
self.func = f
|
||||
self.__name__ = 'localized_{}'.format(f.__name__)
|
||||
|
||||
@transaction.atomic # only commit when all descendants are properly updated
|
||||
def __call__(self, instance, *args, **kwargs):
|
||||
# when updating, save doesn't check if slug_xx has changed so it can only detect changes in slug
|
||||
# from current language. We need to ensure that if a given localized slug changes we call set_url_path
|
||||
if not instance.id: # creating a record, wagtail will call set_url_path, nothing to do.
|
||||
return self.func(instance, *args, **kwargs)
|
||||
|
||||
old_record = None
|
||||
change_url_path = change_descendant_url_path = False
|
||||
for language in mt_settings.AVAILABLE_LANGUAGES:
|
||||
localized_slug = build_localized_fieldname('slug', language)
|
||||
# similar logic used in save
|
||||
if not ('update_fields' in kwargs and localized_slug not in kwargs['update_fields']):
|
||||
old_record = old_record or Page.objects.get(id=instance.id)
|
||||
if getattr(old_record, localized_slug) != getattr(instance, localized_slug):
|
||||
change_descendant_url_path = True
|
||||
if language != get_language():
|
||||
change_url_path = True
|
||||
break
|
||||
|
||||
# Pages may have have their url_path_xx changed upstream when a parent has its url_path changed.
|
||||
# If that's the case let's propagate the change to children
|
||||
if not change_descendant_url_path:
|
||||
localized_url_path = build_localized_fieldname('url_path', language)
|
||||
if not ('update_fields' in kwargs and localized_url_path not in kwargs['update_fields']):
|
||||
old_record = old_record or Page.objects.get(id=instance.id)
|
||||
if getattr(old_record, localized_url_path) != getattr(instance, localized_url_path):
|
||||
change_descendant_url_path = True
|
||||
|
||||
# if any language other than current language had it slug changed set_url_path will be executed
|
||||
if change_url_path:
|
||||
instance.set_url_path(instance.get_parent())
|
||||
|
||||
result = self.func(instance, *args, **kwargs)
|
||||
|
||||
# update children localized paths if any language had it slug changed
|
||||
if change_descendant_url_path:
|
||||
_update_translation_descendant_url_paths(old_record, instance)
|
||||
|
||||
# Check if this is a root page of any sites and clear the 'wagtail_site_root_paths_XX' key if so
|
||||
if Site.objects.filter(root_page=instance).exists():
|
||||
for language in mt_settings.AVAILABLE_LANGUAGES:
|
||||
cache.delete('wagtail_site_root_paths_{}'.format(language))
|
||||
|
||||
return result
|
||||
|
||||
def __get__(self, instance, owner=None):
|
||||
return types.MethodType(self, instance) if instance else self
|
||||
|
||||
|
||||
def _patch_stream_field_meaningful_value(field):
|
||||
old_meaningful_value = field.meaningful_value
|
||||
|
||||
def meaningful_value(self, val, undefined):
|
||||
"""
|
||||
Check if val is considered non-empty.
|
||||
"""
|
||||
if isinstance(val, StreamValue):
|
||||
return len(val.stream_data) != 0
|
||||
return old_meaningful_value(self, val, undefined)
|
||||
|
||||
field.meaningful_value = meaningful_value.__get__(field)
|
||||
|
||||
|
||||
def patch_wagtail_models():
|
||||
# After all models being registered the Page or BaseSetting subclasses and snippets are patched
|
||||
registered_models = translator.get_registered_models()
|
||||
|
|
@ -398,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)
|
||||
|
|
|
|||
107
wagtail_modeltranslation/patch_wagtailadmin_forms.py
Normal file
107
wagtail_modeltranslation/patch_wagtailadmin_forms.py
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,22 +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) {
|
||||
$('#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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
@ -3,10 +3,27 @@
|
|||
import re
|
||||
|
||||
from django import template
|
||||
from django.core.urlresolvers import resolve
|
||||
from django.utils.translation import activate, get_language
|
||||
|
||||
try:
|
||||
from django.urls import resolve
|
||||
except ImportError:
|
||||
from django.core.urlresolvers import resolve
|
||||
|
||||
from six import iteritems
|
||||
|
||||
try:
|
||||
from wagtail.core.models import Page
|
||||
from wagtail.core.templatetags.wagtailcore_tags import pageurl
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.wagtailcore.templatetags.wagtailcore_tags import pageurl
|
||||
|
||||
from modeltranslation import settings as mt_settings
|
||||
from modeltranslation.settings import DEFAULT_LANGUAGE
|
||||
|
||||
from ..contextlib import use_language
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
|
|
@ -38,8 +55,65 @@ def change_lang(context, lang=None, *args, **kwargs):
|
|||
translated_url = '/' + lang + '/' + path_components[0] + '/'
|
||||
if request.GET:
|
||||
translated_url += '?'
|
||||
for key, value in iteritems(request.GET):
|
||||
for count, (key, value) in enumerate(iteritems(request.GET)):
|
||||
if count != 0:
|
||||
translated_url += "&"
|
||||
translated_url += key + '=' + value
|
||||
return translated_url
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
class GetAvailableLanguagesNode(template.Node):
|
||||
"""Get available languages."""
|
||||
|
||||
def __init__(self, variable):
|
||||
self.variable = variable
|
||||
|
||||
def render(self, context):
|
||||
"""Rendering."""
|
||||
context[self.variable] = mt_settings.AVAILABLE_LANGUAGES
|
||||
return ''
|
||||
|
||||
# Alternative to slugurl which uses chosen or default language for language
|
||||
@register.simple_tag(takes_context=True)
|
||||
def slugurl_trans(context, slug, language=None):
|
||||
"""
|
||||
Examples:
|
||||
{% slugurl_trans 'default_lang_slug' %}
|
||||
{% slugurl_trans 'de_lang_slug' 'de' %}
|
||||
|
||||
Returns the URL for the page that has the given slug.
|
||||
"""
|
||||
language = language or DEFAULT_LANGUAGE
|
||||
|
||||
with use_language(language):
|
||||
page = Page.objects.filter(slug=slug).first()
|
||||
|
||||
if page:
|
||||
# call pageurl() instead of page.relative_url() here so we get the ``accepts_kwarg`` logic
|
||||
return pageurl(context, page)
|
||||
|
||||
|
||||
@register.tag('get_available_languages_wmt')
|
||||
def do_get_available_languages(unused_parser, token):
|
||||
"""
|
||||
Store a list of available languages in the context.
|
||||
|
||||
Usage::
|
||||
|
||||
{% get_available_languages_wmt as languages %}
|
||||
{% for language in languages %}
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
This will just pull the MODELTRANSLATION_LANGUAGES (or LANGUAGES) setting
|
||||
from your setting file (or the default settings) and
|
||||
put it into the named variable.
|
||||
"""
|
||||
args = token.contents.split()
|
||||
if len(args) != 3 or args[1] != 'as':
|
||||
raise template.TemplateSyntaxError(
|
||||
"'get_available_languages_wmt' requires 'as variable' "
|
||||
"(got %r)" % args)
|
||||
return GetAvailableLanguagesNode(args[2])
|
||||
|
|
|
|||
|
|
@ -1,13 +1,26 @@
|
|||
# coding: utf-8
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
from modelcluster.fields import ParentalKey
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel
|
||||
from wagtail.wagtailcore import blocks
|
||||
from wagtail.wagtailcore.fields import StreamField
|
||||
from wagtail.wagtailcore.models import Page as WagtailPage
|
||||
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
||||
from wagtail.wagtailsearch import index
|
||||
from wagtail.wagtailsnippets.models import register_snippet
|
||||
try:
|
||||
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
||||
from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, FieldRowPanel, InlinePanel, StreamFieldPanel
|
||||
from wagtail.core import blocks
|
||||
from wagtail.core.fields import StreamField
|
||||
from wagtail.core.models import Page as WagtailPage
|
||||
from wagtail.images.edit_handlers import ImageChooserPanel
|
||||
from wagtail.search import index
|
||||
from wagtail.snippets.models import register_snippet
|
||||
except ImportError:
|
||||
from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, FieldRowPanel, InlinePanel, \
|
||||
StreamFieldPanel
|
||||
from wagtail.wagtailcore import blocks
|
||||
from wagtail.wagtailcore.fields import StreamField
|
||||
from wagtail.wagtailcore.models import Page as WagtailPage
|
||||
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
||||
from wagtail.wagtailsearch import index
|
||||
from wagtail.wagtailsnippets.models import register_snippet
|
||||
|
||||
|
||||
# Wagtail Models
|
||||
|
|
@ -25,6 +38,10 @@ class TestSlugPage2(WagtailPage):
|
|||
pass
|
||||
|
||||
|
||||
class TestSlugPage1Subclass(TestSlugPage1):
|
||||
pass
|
||||
|
||||
|
||||
class PatchTestPage(WagtailPage):
|
||||
description = models.CharField(max_length=50)
|
||||
|
||||
|
|
@ -35,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')
|
||||
]
|
||||
|
|
@ -57,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 = [
|
||||
|
|
@ -105,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)
|
||||
|
||||
|
|
@ -141,7 +166,8 @@ class FieldPanelPage(WagtailPage):
|
|||
|
||||
class ImageChooserPanelPage(WagtailPage):
|
||||
image = models.ForeignKey(
|
||||
'wagtailimages.Image'
|
||||
'wagtailimages.Image',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
content_panels = [
|
||||
|
|
@ -162,7 +188,7 @@ class FieldRowPanelPage(WagtailPage):
|
|||
class StreamFieldPanelPage(WagtailPage):
|
||||
body = StreamField([
|
||||
('text', blocks.CharBlock(max_length=10))
|
||||
])
|
||||
], blank=False) # since wagtail 1.12 StreamField's blank defaults to False
|
||||
|
||||
content_panels = [
|
||||
StreamFieldPanel('body')
|
||||
|
|
@ -191,3 +217,13 @@ class InlinePanelPage(WagtailPage):
|
|||
content_panels = [
|
||||
InlinePanel('related_page_model')
|
||||
]
|
||||
|
||||
|
||||
class RoutablePageTest(RoutablePageMixin, WagtailPage):
|
||||
@route(r'^archive/year/1984/$')
|
||||
def archive_for_1984(self, request):
|
||||
return HttpResponse("we were always at war with eastasia")
|
||||
|
||||
@route(r'^archive/year/(\d+)/$')
|
||||
def archive_by_year(self, request, year):
|
||||
return HttpResponse("ARCHIVE BY YEAR: " + str(year))
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ USE_TZ = False
|
|||
MIDDLEWARE_CLASSES = ()
|
||||
|
||||
MODELTRANSLATION_AUTO_POPULATE = False
|
||||
MODELTRANSLATION_FALLBACK_LANGUAGES = ()
|
||||
MODELTRANSLATION_FALLBACK_LANGUAGES = {'default': (MODELTRANSLATION_DEFAULT_LANGUAGE,)}
|
||||
|
||||
ROOT_URLCONF = 'wagtail_modeltranslation.tests.urls'
|
||||
|
||||
TRANSLATE_SLUGS = True
|
||||
|
|
|
|||
|
|
@ -3,17 +3,26 @@ import imp
|
|||
|
||||
import django
|
||||
from django.apps import apps as django_apps
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.management import call_command
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.test.client import RequestFactory
|
||||
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.snippets.views.snippets import get_snippet_edit_handler
|
||||
except ImportError:
|
||||
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
|
||||
|
||||
models = translation = None
|
||||
|
||||
request_factory = RequestFactory()
|
||||
|
||||
class dummy_context_mgr():
|
||||
def __enter__(self):
|
||||
|
|
@ -39,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
|
||||
|
|
@ -51,9 +59,23 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
imp.reload(translator)
|
||||
|
||||
# reload the translation module to register the Page model
|
||||
from wagtail_modeltranslation import translation as wag_translation, translator as wag_translator
|
||||
# and also edit_handlers so any patches made to Page are reapplied
|
||||
import sys
|
||||
del cls.cache.all_models['wagtailcore']
|
||||
sys.modules.pop('wagtail_modeltranslation.translation.pagetr', None)
|
||||
from wagtail_modeltranslation import translation as wag_translation
|
||||
try:
|
||||
from wagtail.admin import edit_handlers
|
||||
sys.modules.pop('wagtail.core.models', None)
|
||||
except ImportError:
|
||||
from wagtail.wagtailadmin import edit_handlers
|
||||
sys.modules.pop('wagtail.wagtailcore.models', None)
|
||||
imp.reload(wag_translation)
|
||||
imp.reload(wag_translator)
|
||||
imp.reload(edit_handlers) # so Page can be repatched by edit_handlers
|
||||
wagtailcore_args = []
|
||||
if django.VERSION < (1, 11):
|
||||
wagtailcore_args = [cls.cache.all_models['wagtailcore']]
|
||||
cls.cache.get_app_config('wagtailcore').import_models(*wagtailcore_args)
|
||||
|
||||
# Reload the patching class to update the imported translator
|
||||
# in order to include the newly registered models
|
||||
|
|
@ -64,10 +86,12 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
# have translation fields, but for languages previously defined. We want
|
||||
# to be sure that 'de' and 'en' are available)
|
||||
del cls.cache.all_models['tests']
|
||||
import sys
|
||||
sys.modules.pop('wagtail_modeltranslation.tests.models', None)
|
||||
sys.modules.pop('wagtail_modeltranslation.tests.translation', None)
|
||||
cls.cache.get_app_config('tests').import_models(cls.cache.all_models['tests'])
|
||||
tests_args = []
|
||||
if django.VERSION < (1, 11):
|
||||
tests_args = [cls.cache.all_models['tests']]
|
||||
cls.cache.get_app_config('tests').import_models(*tests_args)
|
||||
|
||||
# 4. Autodiscover
|
||||
from modeltranslation.models import handle_translation_registrations
|
||||
|
|
@ -75,14 +99,16 @@ 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. patch wagtail models
|
||||
# 7. Make sure Page translation fields are created
|
||||
call_command('sync_page_translation_fields', interactive=False, verbosity=0)
|
||||
|
||||
# 8. patch wagtail models
|
||||
from wagtail_modeltranslation.patch_wagtailadmin import patch_wagtail_models
|
||||
patch_wagtail_models()
|
||||
|
||||
|
|
@ -90,12 +116,16 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
# tests app has been added into INSTALLED_APPS and loaded
|
||||
# (that's why this is not imported in normal import section)
|
||||
global models, translation
|
||||
from wagtail_modeltranslation.tests import models, translation
|
||||
from wagtail_modeltranslation.tests import models, translation # NOQA
|
||||
|
||||
def setUp(self):
|
||||
self._old_language = get_language()
|
||||
trans_real.activate('de')
|
||||
|
||||
# ensure we have a fresh site cache
|
||||
for language in mt_settings.AVAILABLE_LANGUAGES:
|
||||
cache.delete('wagtail_site_root_paths_{}'.format(language))
|
||||
|
||||
def tearDown(self):
|
||||
trans_real.activate(self._old_language)
|
||||
|
||||
|
|
@ -114,7 +144,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
super(WagtailModeltranslationTest, cls).setUpClass()
|
||||
|
||||
# Delete the default wagtail pages from db
|
||||
from wagtail.wagtailcore.models import Page
|
||||
try:
|
||||
from wagtail.core.models import Page
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Page
|
||||
Page.objects.delete()
|
||||
|
||||
def test_page_fields(self):
|
||||
|
|
@ -148,7 +181,11 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
self.assertEquals(len(panels), 2)
|
||||
|
||||
# Validate if the created panels are instances of FieldPanel
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
||||
try:
|
||||
from wagtail.admin.edit_handlers import FieldPanel
|
||||
except ImportError:
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
||||
|
||||
self.assertIsInstance(panels[0], FieldPanel)
|
||||
self.assertIsInstance(panels[1], FieldPanel)
|
||||
|
||||
|
|
@ -160,7 +197,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
# Check if there is one panel per language
|
||||
self.assertEquals(len(panels), 2)
|
||||
|
||||
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
||||
try:
|
||||
from wagtail.images.edit_handlers import ImageChooserPanel
|
||||
except ImportError:
|
||||
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
||||
self.assertIsInstance(panels[0], ImageChooserPanel)
|
||||
self.assertIsInstance(panels[1], ImageChooserPanel)
|
||||
|
||||
|
|
@ -172,7 +212,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
# Check if the fieldrowpanel still exists
|
||||
self.assertEqual(len(panels), 1)
|
||||
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldRowPanel
|
||||
try:
|
||||
from wagtail.admin.edit_handlers import FieldRowPanel
|
||||
except ImportError:
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldRowPanel
|
||||
self.assertIsInstance(panels[0], FieldRowPanel)
|
||||
|
||||
# Check if the children were correctly patched using the fieldpanel test
|
||||
|
|
@ -184,7 +227,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
# Check if there is one panel per language
|
||||
self.assertEquals(len(panels), 2)
|
||||
|
||||
from wagtail.wagtailadmin.edit_handlers import StreamFieldPanel
|
||||
try:
|
||||
from wagtail.admin.edit_handlers import StreamFieldPanel
|
||||
except ImportError:
|
||||
from wagtail.wagtailadmin.edit_handlers import StreamFieldPanel
|
||||
self.assertIsInstance(panels[0], StreamFieldPanel)
|
||||
self.assertIsInstance(panels[1], StreamFieldPanel)
|
||||
|
||||
|
|
@ -197,16 +243,33 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
|
||||
self.assertEquals(len(child_block), 1)
|
||||
|
||||
from wagtail.wagtailcore.blocks import CharBlock
|
||||
try:
|
||||
from wagtail.core.blocks import CharBlock
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.blocks import CharBlock
|
||||
self.assertEquals(child_block[0][0], 'text')
|
||||
self.assertIsInstance(child_block[0][1], CharBlock)
|
||||
|
||||
if VERSION >= (1, 12):
|
||||
# Original and Default language StreamFields are required
|
||||
self.assertFalse(models.StreamFieldPanelPage.body.field.blank)
|
||||
self.assertTrue(models.StreamFieldPanelPage.body.field.stream_block.required)
|
||||
self.assertFalse(models.StreamFieldPanelPage.body_de.field.blank)
|
||||
self.assertTrue(models.StreamFieldPanelPage.body_de.field.stream_block.required)
|
||||
|
||||
# Translated StreamField is optional
|
||||
self.assertTrue(models.StreamFieldPanelPage.body_en.field.blank)
|
||||
self.assertFalse(models.StreamFieldPanelPage.body_en.field.stream_block.required)
|
||||
|
||||
def check_multipanel_patching(self, panels):
|
||||
# There are three multifield panels, one for each of the available
|
||||
# children panels
|
||||
self.assertEquals(len(panels), 3)
|
||||
|
||||
from wagtail.wagtailadmin.edit_handlers import MultiFieldPanel
|
||||
try:
|
||||
from wagtail.admin.edit_handlers import MultiFieldPanel
|
||||
except ImportError:
|
||||
from wagtail.wagtailadmin.edit_handlers import MultiFieldPanel
|
||||
self.assertIsInstance(panels[0], MultiFieldPanel)
|
||||
self.assertIsInstance(panels[1], MultiFieldPanel)
|
||||
self.assertIsInstance(panels[2], MultiFieldPanel)
|
||||
|
|
@ -244,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)
|
||||
|
|
@ -253,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
|
||||
|
|
@ -261,7 +344,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
|
||||
page_edit_handler = models.InlinePanelPage.get_edit_handler()
|
||||
|
||||
form = page_edit_handler.get_form_class(models.InlinePanelPage)
|
||||
if VERSION < (2,):
|
||||
form = page_edit_handler.get_form_class(models.InlinePanelPage)
|
||||
else:
|
||||
form = page_edit_handler.get_form_class()
|
||||
|
||||
page_base_fields = ['slug_de', 'slug_en', 'seo_title_de', 'seo_title_en', 'search_description_de',
|
||||
'search_description_en', u'show_in_menus', u'go_live_at', u'expire_at']
|
||||
|
|
@ -291,10 +377,12 @@ 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
|
||||
"""
|
||||
from wagtail.wagtailsnippets.views.snippets import get_snippet_edit_handler
|
||||
snippet_edit_handler = get_snippet_edit_handler(models.InlinePanelSnippet)
|
||||
|
||||
form = snippet_edit_handler.get_form_class(models.InlinePanelSnippet)
|
||||
if VERSION < (2,):
|
||||
form = snippet_edit_handler.get_form_class(models.InlinePanelSnippet)
|
||||
else:
|
||||
form = snippet_edit_handler.get_form_class()
|
||||
|
||||
inline_model_fields = ['field_name_de', 'field_name_en', 'image_chooser_de', 'image_chooser_en',
|
||||
'fieldrow_name_de', 'fieldrow_name_en', 'name_de', 'name_en', 'image_de', 'image_en',
|
||||
|
|
@ -310,7 +398,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
self.assertItemsEqual(inline_model_fields, related_formset_form.base_fields.keys())
|
||||
|
||||
def test_duplicate_slug(self):
|
||||
from wagtail.wagtailcore.models import Site
|
||||
try:
|
||||
from wagtail.core.models import Site
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Site
|
||||
# Create a test Site with a root page
|
||||
root = models.TestRootPage(title='title', depth=1, path='0001', slug_en='slug_en', slug_de='slug_de')
|
||||
root.save()
|
||||
|
|
@ -333,8 +424,92 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
|
||||
# Make the slug equal to test if the duplicate is detected
|
||||
child2.slug_de = 'child'
|
||||
|
||||
self.assertRaises(ValidationError, child2.clean)
|
||||
child2.slug_de = 'child-2'
|
||||
|
||||
# Make the translated slug equal to test if the duplicate is detected
|
||||
child2.slug_en = 'child-en'
|
||||
self.assertRaises(ValidationError, child2.clean)
|
||||
|
||||
def test_slugurl_trans(self):
|
||||
"""
|
||||
Assert tag slugurl_trans is immune to user's current language
|
||||
"""
|
||||
from wagtail_modeltranslation.templatetags.wagtail_modeltranslation import slugurl_trans
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root slugurl', },
|
||||
'children': {
|
||||
'child': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child slugurl', 'slug': 'child-slugurl', 'slug_en': 'child-slugurl-en'},
|
||||
'children': {},
|
||||
},
|
||||
},
|
||||
}
|
||||
site = page_factory.create_page_tree(site_pages)
|
||||
|
||||
request_mock = request_factory.get('/')
|
||||
setattr(request_mock, 'site', site)
|
||||
context = {'request': request_mock}
|
||||
|
||||
self.assertEqual(slugurl_trans(context, 'root-slugurl'), '/de/')
|
||||
self.assertEqual(slugurl_trans(context, 'child-slugurl'), '/de/child-slugurl/')
|
||||
self.assertEqual(slugurl_trans(context, 'child-slugurl-en', 'en'), '/de/child-slugurl/')
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
self.assertEqual(slugurl_trans(context, 'root-slugurl'), '/en/')
|
||||
self.assertEqual(slugurl_trans(context, 'child-slugurl'), '/en/child-slugurl-en/')
|
||||
self.assertEqual(slugurl_trans(context, 'child-slugurl-en', 'en'), '/en/child-slugurl-en/')
|
||||
|
||||
def test_relative_url(self):
|
||||
try:
|
||||
from wagtail.core.models import Site
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Site
|
||||
# Create a test Site with a root page
|
||||
root = models.TestRootPage(title='title slugurl', depth=1, path='0004',
|
||||
slug_en='title_slugurl_en', slug_de='title_slugurl_de')
|
||||
root.save()
|
||||
site = Site(root_page=root)
|
||||
site.save()
|
||||
|
||||
# Add children to the root
|
||||
child = root.add_child(
|
||||
instance=models.TestSlugPage1(title='child1 slugurl',
|
||||
slug_en='child-slugurl-en', slug_de='child-slugurl-de',
|
||||
depth=2, path='00040001')
|
||||
)
|
||||
child.save_revision().publish()
|
||||
|
||||
url_1_de = child.relative_url(site)
|
||||
self.assertEqual(url_1_de, '/de/child-slugurl-de/',
|
||||
'When using the default language, slugurl produces the wrong url.')
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
url_1_en = child.relative_url(site)
|
||||
self.assertEqual(url_1_en, '/en/child-slugurl-en/',
|
||||
'When using non-default language, slugurl produces the wrong url.')
|
||||
|
||||
# Add children using non-default language
|
||||
child2 = root.add_child(
|
||||
instance=models.TestSlugPage2(title='child2 slugurl', title_de='child2 slugurl DE',
|
||||
slug_de='child2-slugurl-de', slug_en='child2-slugurl-en',
|
||||
depth=2, path='00040002')
|
||||
)
|
||||
child2.save_revision().publish()
|
||||
|
||||
url_2_en = child2.relative_url(site)
|
||||
self.assertEqual(url_2_en, '/en/child2-slugurl-en/',
|
||||
'When using non-default language, slugurl produces the wrong url.')
|
||||
|
||||
trans_real.activate('de')
|
||||
|
||||
url_2_de = child2.relative_url(site)
|
||||
self.assertEqual(url_2_de, '/de/child2-slugurl-de/',
|
||||
'When using non-default language, slugurl produces the wrong url.')
|
||||
|
||||
def test_searchfield_patching(self):
|
||||
# Check if the search fields have the original field plus the translated ones
|
||||
|
|
@ -348,3 +523,474 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
except AttributeError:
|
||||
# python 2.7
|
||||
self.assertItemsEqual(expected_fields, model_search_fields)
|
||||
|
||||
def test_streamfield_fallback(self):
|
||||
body_text = '[{"value": "Some text", "type": "text"}]'
|
||||
page = models.StreamFieldPanelPage(title='Streamfield Fallback', slug='streamfield_fallback',
|
||||
depth=1, path='0005', body=body_text)
|
||||
page.save()
|
||||
|
||||
self.assertEqual(str(page.body), '<div class="block-text">Some text</div>')
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
self.assertEqual(str(page.body), '<div class="block-text">Some text</div>',
|
||||
'page.body did not fallback to original language.')
|
||||
|
||||
def test_set_url_path(self):
|
||||
"""
|
||||
Assert translation URL Paths are correctly set in page and descendants for a slug change and
|
||||
page move operations
|
||||
"""
|
||||
try:
|
||||
from wagtail.core.models import Site
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Site
|
||||
# Create a test Site with a root page
|
||||
root = models.TestRootPage.objects.create(title='url paths', depth=1, path='0006', slug='url-path-slug')
|
||||
|
||||
Site.objects.create(root_page=root)
|
||||
|
||||
# Add children to the root
|
||||
child = root.add_child(
|
||||
instance=models.TestSlugPage1(title='child', slug='child', depth=2, path='00060001')
|
||||
)
|
||||
child.save()
|
||||
|
||||
# Add grandchildren to the root
|
||||
grandchild = child.add_child(
|
||||
instance=models.TestSlugPage1(title='grandchild', slug='grandchild', depth=2, path='000600010001')
|
||||
)
|
||||
grandchild.save()
|
||||
|
||||
# check everything is as expected
|
||||
self.assertEqual(child.url_path_de, '/child/')
|
||||
self.assertEqual(child.url_path_en, '/child/')
|
||||
self.assertEqual(grandchild.url_path_de, '/child/grandchild/')
|
||||
self.assertEqual(grandchild.url_path_en, '/child/grandchild/')
|
||||
|
||||
# PAGE SLUG CHANGE
|
||||
grandchild.slug_de = 'grandchild1'
|
||||
grandchild.save()
|
||||
|
||||
self.assertEqual(grandchild.url_path_de, '/child/grandchild1/')
|
||||
self.assertEqual(grandchild.url_path_en, '/child/grandchild1/')
|
||||
|
||||
grandchild.slug_en = 'grandchild1_en'
|
||||
grandchild.save()
|
||||
|
||||
self.assertEqual(grandchild.url_path_de, '/child/grandchild1/')
|
||||
self.assertEqual(grandchild.url_path_en, '/child/grandchild1_en/')
|
||||
|
||||
# Children url paths should update when parent changes
|
||||
child.slug_en = 'child_en'
|
||||
child.save()
|
||||
|
||||
self.assertEqual(child.url_path_de, '/child/')
|
||||
self.assertEqual(child.url_path_en, '/child_en/')
|
||||
|
||||
# Retrieve grandchild from DB:
|
||||
grandchild_new = models.TestSlugPage1.objects.get(id=grandchild.id)
|
||||
self.assertEqual(grandchild_new.url_path_en, '/child_en/grandchild1_en/')
|
||||
self.assertEqual(grandchild_new.url_path_de, '/child/grandchild1/')
|
||||
|
||||
# Add 2nd child to the root
|
||||
child2 = root.add_child(
|
||||
instance=models.TestSlugPage1(title='child2', slug='child2', depth=2, path='00060002')
|
||||
)
|
||||
child2.save()
|
||||
|
||||
# Add grandchildren
|
||||
grandchild2 = child2.add_child(
|
||||
instance=models.TestSlugPage1(title='grandchild2', slug='grandchild2', depth=3, path='000600020001')
|
||||
)
|
||||
grandchild2.save()
|
||||
|
||||
# PAGE MOVE
|
||||
child2.move(child, pos='last-child')
|
||||
|
||||
# re-fetch child2 to confirm db fields have been updated
|
||||
child2 = models.TestSlugPage1.objects.get(id=child2.id)
|
||||
|
||||
self.assertEqual(child2.depth, 3)
|
||||
self.assertEqual(child2.get_parent().id, child.id)
|
||||
self.assertEqual(child2.url_path_de, '/child/child2/')
|
||||
self.assertEqual(child2.url_path_en, '/child_en/child2/')
|
||||
|
||||
# children of child2 should also have been updated
|
||||
grandchild2 = child2.get_children().get(slug='grandchild2').specific
|
||||
self.assertEqual(grandchild2.depth, 4)
|
||||
self.assertEqual(grandchild2.url_path_de, '/child/child2/grandchild2/')
|
||||
self.assertEqual(grandchild2.url_path_en, '/child_en/child2/grandchild2/')
|
||||
|
||||
def test_set_url_path_non_translated_descendants(self):
|
||||
"""
|
||||
Assert set_url_path works correctly when a Page with untranslated children
|
||||
has its translated slug changed.
|
||||
"""
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root untranslated', },
|
||||
'children': {
|
||||
'child': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child untranslated'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 untranslated'},
|
||||
'children': {
|
||||
'grandgrandchild': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandgrandchild untranslated'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'grandchild2': {
|
||||
'model': models.TestSlugPage2,
|
||||
'kwargs': {'title': 'grandchild2 untranslated'},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
page_factory.create_page_tree(site_pages)
|
||||
|
||||
# Revert grandchild1 and grandgrandchild url_path_en to their initial untranslated states
|
||||
# to simulate pages that haven't been translated yet
|
||||
models.TestSlugPage1.objects.filter(slug_de__in=['grandchild1-untranslated', 'grandgrandchild-untranslated'])\
|
||||
.rewrite(False).update(slug_en=None, url_path_en=None)
|
||||
|
||||
# re-fetch to pick up latest from DB
|
||||
grandchild1 = models.TestSlugPage1.objects.get(slug_de='grandchild1-untranslated')
|
||||
self.assertEqual(grandchild1.url_path_de, '/root-untranslated/child-untranslated/grandchild1-untranslated/')
|
||||
self.assertEqual(grandchild1.slug_en, None)
|
||||
self.assertEqual(grandchild1.url_path_en, None)
|
||||
grandgrandchild = models.TestSlugPage1.objects.get(slug_de='grandgrandchild-untranslated')
|
||||
self.assertEqual(grandgrandchild.url_path_de,
|
||||
'/root-untranslated/child-untranslated/grandchild1-untranslated/grandgrandchild-untranslated/')
|
||||
self.assertEqual(grandgrandchild.slug_en, None)
|
||||
self.assertEqual(grandgrandchild.url_path_en, None)
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
child = site_pages['children']['child']['instance']
|
||||
child.slug_en = 'child-translated'
|
||||
child.save()
|
||||
|
||||
self.assertEqual(child.url_path_de, '/root-untranslated/child-untranslated/')
|
||||
self.assertEqual(child.url_path_en, '/root-untranslated/child-translated/')
|
||||
|
||||
grandchild1 = models.TestSlugPage1.objects.get(slug_de='grandchild1-untranslated')
|
||||
self.assertEqual(grandchild1.url_path_de, '/root-untranslated/child-untranslated/grandchild1-untranslated/')
|
||||
self.assertEqual(grandchild1.url_path_en, '/root-untranslated/child-translated/grandchild1-untranslated/')
|
||||
|
||||
grandgrandchild = models.TestSlugPage1.objects.get(slug_de='grandgrandchild-untranslated')
|
||||
self.assertEqual(grandgrandchild.url_path_de,
|
||||
'/root-untranslated/child-untranslated/grandchild1-untranslated/grandgrandchild-untranslated/')
|
||||
self.assertEqual(grandgrandchild.url_path_en,
|
||||
'/root-untranslated/child-translated/grandchild1-untranslated/grandgrandchild-untranslated/')
|
||||
|
||||
def test_fetch_translation_records(self):
|
||||
"""
|
||||
Assert that saved translation fields are retrieved correctly
|
||||
See: https://github.com/infoportugal/wagtail-modeltranslation/issues/103#issuecomment-352006610
|
||||
"""
|
||||
page = models.StreamFieldPanelPage.objects.create(title_de='Fetch DE', title_en='Fetch EN',
|
||||
slug_de='fetch_de', slug_en='fetch_en',
|
||||
body_de=[('text', 'fetch de')], body_en=[('text', 'fetch en')],
|
||||
depth=1, path='0007')
|
||||
page.save()
|
||||
|
||||
page_db = models.StreamFieldPanelPage.objects.get(id=page.id)
|
||||
|
||||
self.assertEqual(page_db.title_de, 'Fetch DE')
|
||||
self.assertEqual(page_db.slug_de, 'fetch_de')
|
||||
self.assertEqual(str(page_db.body_de), '<div class="block-text">fetch de</div>')
|
||||
self.assertEqual(page_db.title_en, 'Fetch EN')
|
||||
self.assertEqual(page_db.slug_en, 'fetch_en')
|
||||
self.assertEqual(str(page_db.body_en), '<div class="block-text">fetch en</div>')
|
||||
|
||||
def check_route_request(self, root_page, components, expected_page):
|
||||
request = HttpRequest()
|
||||
request.path = '/' + '/'.join(components) + '/'
|
||||
(found_page, args, kwargs) = root_page.route(request, components)
|
||||
self.assertEqual(found_page, expected_page)
|
||||
|
||||
def test_request_routing(self):
|
||||
"""
|
||||
Assert .route works for translated slugs
|
||||
"""
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root routing', },
|
||||
'children': {
|
||||
'child1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child1 routing', 'slug_de': 'routing-de-01', 'slug_en': 'routing-en-01'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 routing',
|
||||
'slug_de': 'routing-de-0101', 'slug_en': 'routing-en-0101'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'child2': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child2 routing', 'slug': 'routing-de-02'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 routing', 'slug': 'routing-de-0201'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'routable_page': {
|
||||
'model': models.RoutablePageTest,
|
||||
'kwargs': {'title': 'Routable Page', 'live': True,
|
||||
'slug_de': 'routing-de-03', 'slug_en': 'routing-en-03'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 routing',
|
||||
'slug_de': 'routing-de-0301', 'slug_en': 'routing-en-0301'},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
page_factory.create_page_tree(site_pages)
|
||||
|
||||
root_page = site_pages['instance']
|
||||
page_0101 = site_pages['children']['child1']['children']['grandchild1']['instance']
|
||||
page_0201 = site_pages['children']['child2']['children']['grandchild1']['instance']
|
||||
page_0301 = site_pages['children']['routable_page']['children']['grandchild1']['instance']
|
||||
|
||||
self.check_route_request(root_page, ['routing-de-01', 'routing-de-0101'], page_0101)
|
||||
self.check_route_request(root_page, ['routing-de-02', 'routing-de-0201'], page_0201)
|
||||
|
||||
# routable page test
|
||||
routable_page = site_pages['children']['routable_page']['instance']
|
||||
view, args, kwargs = routable_page.resolve_subpage('/archive/year/2014/')
|
||||
self.assertEqual(view, routable_page.archive_by_year)
|
||||
self.assertEqual(args, ('2014',))
|
||||
self.assertEqual(kwargs, {})
|
||||
self.check_route_request(root_page, ['routing-de-03', 'routing-de-0301'], page_0301)
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
# assert translated slugs fetch the correct page
|
||||
self.check_route_request(root_page, ['routing-en-01', 'routing-en-0101'], page_0101)
|
||||
# in the absence of translated slugs assert the default ones work
|
||||
self.check_route_request(root_page, ['routing-de-02', 'routing-de-0201'], page_0201)
|
||||
|
||||
view, args, kwargs = routable_page.resolve_subpage('/archive/year/2014/')
|
||||
self.assertEqual(view, routable_page.archive_by_year)
|
||||
self.assertEqual(args, ('2014',))
|
||||
self.assertEqual(kwargs, {})
|
||||
self.check_route_request(root_page, ['routing-en-03', 'routing-en-0301'], page_0301)
|
||||
|
||||
def test_get_url_parts(self):
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root URL parts', },
|
||||
'children': {
|
||||
'child1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child1 URL parts', 'slug_de': 'url-parts-de-01', 'slug_en': 'url-parts-en-01'},
|
||||
},
|
||||
'child2': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child2 URL parts', 'slug': 'url-parts-de-02'},
|
||||
},
|
||||
},
|
||||
}
|
||||
site = page_factory.create_page_tree(site_pages)
|
||||
|
||||
root_page = site_pages['instance']
|
||||
page_01 = site_pages['children']['child1']['instance']
|
||||
page_02 = site_pages['children']['child2']['instance']
|
||||
|
||||
self.assertEqual(root_page.relative_url(site), '/de/')
|
||||
self.assertEqual(page_01.relative_url(site), '/de/url-parts-de-01/')
|
||||
self.assertEqual(page_02.relative_url(site), '/de/url-parts-de-02/')
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
self.assertEqual(root_page.relative_url(site), '/en/')
|
||||
self.assertEqual(page_01.relative_url(site), '/en/url-parts-en-01/')
|
||||
self.assertEqual(page_02.relative_url(site), '/en/url-parts-de-02/')
|
||||
|
||||
def test_url(self):
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root URL', },
|
||||
'children': {
|
||||
'child1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child1 URL', 'slug_de': 'url-de-01', 'slug_en': 'url-en-01'},
|
||||
},
|
||||
'child2': {
|
||||
'model': models.TestSlugPage2,
|
||||
'kwargs': {'title': 'child2 URL', 'slug': 'url-de-02'},
|
||||
},
|
||||
},
|
||||
}
|
||||
page_factory.create_page_tree(site_pages)
|
||||
|
||||
root_page = site_pages['instance']
|
||||
page_01 = site_pages['children']['child1']['instance']
|
||||
page_02 = site_pages['children']['child2']['instance']
|
||||
|
||||
self.assertEqual(root_page.url, '/de/')
|
||||
self.assertEqual(page_01.url, '/de/url-de-01/')
|
||||
self.assertEqual(page_02.url, '/de/url-de-02/')
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
self.assertEqual(root_page.url, '/en/')
|
||||
self.assertEqual(page_01.url, '/en/url-en-01/')
|
||||
self.assertEqual(page_02.url, '/en/url-de-02/')
|
||||
|
||||
def test_root_page_slug(self):
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root URL', 'slug_de': 'root-de', 'slug_en': 'root-en'},
|
||||
'children': {
|
||||
'child1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child1 URL', 'slug_de': 'url-de-01', 'slug_en': 'url-en-01'},
|
||||
},
|
||||
'child2': {
|
||||
'model': models.TestSlugPage2,
|
||||
'kwargs': {'title': 'child2 URL', 'slug': 'url-de-02'},
|
||||
},
|
||||
'child3': {
|
||||
'model': models.TestSlugPage2,
|
||||
'kwargs': {'title': 'child3 URL', 'slug': 'url-de-03'},
|
||||
},
|
||||
},
|
||||
}
|
||||
page_factory.create_page_tree(site_pages)
|
||||
request = HttpRequest()
|
||||
|
||||
site_root_page = site_pages['instance']
|
||||
wagtail_page_01 = site_pages['children']['child1']['instance']
|
||||
wagtail_page_02 = site_pages['children']['child2']['instance']
|
||||
wagtail_page_03 = site_pages['children']['child3']['instance']
|
||||
|
||||
self.assertEqual(wagtail_page_01.url, '/de/url-de-01/')
|
||||
self.assertEqual(wagtail_page_01.url_path, '/root-de/url-de-01/')
|
||||
if VERSION >= (1, 11):
|
||||
self.assertEqual(wagtail_page_02.get_url(request=request), '/de/url-de-02/') # with request
|
||||
|
||||
trans_real.activate('en')
|
||||
|
||||
self.assertEqual(wagtail_page_01.url, '/en/url-en-01/')
|
||||
self.assertEqual(wagtail_page_01.url_path, '/root-en/url-en-01/')
|
||||
if VERSION >= (1, 11):
|
||||
self.assertEqual(wagtail_page_02.get_url(request=request), '/en/url-de-02/')
|
||||
|
||||
trans_real.activate('de')
|
||||
|
||||
# new request after changing language
|
||||
self.assertEqual(wagtail_page_03.url, '/de/url-de-03/')
|
||||
if VERSION >= (1, 11):
|
||||
self.assertEqual(wagtail_page_01.get_url(request=HttpRequest()), '/de/url-de-01/')
|
||||
|
||||
# URL should not be broken after updating the root_page (ensure the cache is evicted)
|
||||
self.assertEqual(wagtail_page_01.url, '/de/url-de-01/')
|
||||
site_root_page.slug = 'new-root-de'
|
||||
site_root_page.save()
|
||||
wagtail_page_01_new = site_root_page.get_children().get(id=wagtail_page_01.id)
|
||||
self.assertEqual(wagtail_page_01_new.url, '/de/url-de-01/')
|
||||
|
||||
def test_set_translation_url_paths_command(self):
|
||||
"""
|
||||
Assert set_translation_url_paths management command works correctly
|
||||
"""
|
||||
site_pages = {
|
||||
'model': models.TestRootPage,
|
||||
'kwargs': {'title': 'root untranslated', },
|
||||
'children': {
|
||||
'child': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child untranslated'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 untranslated'},
|
||||
'children': {
|
||||
'grandgrandchild': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandgrandchild untranslated'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'grandchild2': {
|
||||
'model': models.TestSlugPage2,
|
||||
'kwargs': {'title': 'grandchild2 untranslated'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'child2': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'child2 translated', 'slug_en': 'child2-translated-en'},
|
||||
'children': {
|
||||
'grandchild1': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandchild1 translated', 'slug_en': 'grandchild1-translated-en'},
|
||||
'children': {
|
||||
'grandgrandchild': {
|
||||
'model': models.TestSlugPage1,
|
||||
'kwargs': {'title': 'grandgrandchild1 translated',
|
||||
'slug_en': 'grandgrandchild1-translated-en'},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
page_factory.create_page_tree(site_pages)
|
||||
|
||||
# Revert grandchild1 and grandgrandchild url_path_en to their initial untranslated states
|
||||
# to simulate pages that haven't been translated yet
|
||||
models.TestSlugPage1.objects.filter(slug_de__in=['grandchild1-untranslated', 'grandgrandchild-untranslated']) \
|
||||
.rewrite(False).update(slug_en=None, url_path_en=None)
|
||||
|
||||
# re-fetch to pick up latest from DB
|
||||
grandchild1 = models.TestSlugPage1.objects.get(slug_de='grandchild1-untranslated')
|
||||
self.assertEqual(grandchild1.url_path_en, None)
|
||||
grandgrandchild = models.TestSlugPage1.objects.get(slug_de='grandgrandchild-untranslated')
|
||||
self.assertEqual(grandgrandchild.url_path_en, None)
|
||||
|
||||
# change grandchild2 url_path to corrupt it in order to simulate Wagtail's 0.7 corruption bug:
|
||||
# http://docs.wagtail.io/en/latest/releases/0.8.html#corrupted-url-paths-may-need-fixing
|
||||
models.TestSlugPage2.objects.filter(slug_de__in=['grandchild2-untranslated',]) \
|
||||
.rewrite(False).update(url_path='corrupted', url_path_de='corrupted')
|
||||
|
||||
grandchild2 = models.TestSlugPage2.objects.get(slug_de='grandchild2-untranslated')
|
||||
self.assertEqual(grandchild2.__dict__['url_path'], 'corrupted')
|
||||
|
||||
call_command('set_translation_url_paths', verbosity=0)
|
||||
|
||||
grandchild1 = models.TestSlugPage1.objects.get(slug_de='grandchild1-untranslated')
|
||||
self.assertEqual(grandchild1.url_path_de, '/root-untranslated/child-untranslated/grandchild1-untranslated/')
|
||||
self.assertEqual(grandchild1.url_path_en, '/root-untranslated/child-untranslated/grandchild1-untranslated/')
|
||||
grandgrandchild = models.TestSlugPage1.objects.get(slug_de='grandgrandchild-untranslated')
|
||||
self.assertEqual(grandgrandchild.url_path_de,
|
||||
'/root-untranslated/child-untranslated/grandchild1-untranslated/grandgrandchild-untranslated/')
|
||||
self.assertEqual(grandgrandchild.url_path_en,
|
||||
'/root-untranslated/child-untranslated/grandchild1-untranslated/grandgrandchild-untranslated/')
|
||||
grandchild2 = models.TestSlugPage2.objects.get(slug_de='grandchild2-untranslated')
|
||||
self.assertEqual(grandchild2.__dict__['url_path'], '/root-untranslated/child-untranslated/grandchild2-untranslated/')
|
||||
self.assertEqual(grandchild2.url_path_de, '/root-untranslated/child-untranslated/grandchild2-untranslated/')
|
||||
self.assertEqual(grandchild2.url_path_en, '/root-untranslated/child-untranslated/grandchild2-untranslated/')
|
||||
|
||||
grandgrandchild_translated = models.TestSlugPage1.objects.get(slug_de='grandgrandchild1-translated')
|
||||
self.assertEqual(grandgrandchild_translated.url_path_de,
|
||||
'/root-untranslated/child2-translated/grandchild1-translated/grandgrandchild1-translated/')
|
||||
self.assertEqual(grandgrandchild_translated.url_path_en,
|
||||
'/root-untranslated/child2-translated-en/grandchild1-translated-en/grandgrandchild1-translated-en/')
|
||||
|
|
|
|||
|
|
@ -1,45 +1,70 @@
|
|||
# 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
|
||||
from wagtail_modeltranslation.translator import WagtailTranslationOptions
|
||||
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
|
||||
|
||||
@register(TestRootPage)
|
||||
class TestRootPagePageTranslationOptions(WagtailTranslationOptions):
|
||||
class TestRootPagePageTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage1)
|
||||
class TestSlugPage1TranslationOptions(WagtailTranslationOptions):
|
||||
class TestSlugPage1TranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage2)
|
||||
class TestSlugPage2TranslationOptions(WagtailTranslationOptions):
|
||||
class TestSlugPage2TranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage1Subclass)
|
||||
class TestSlugPage1SubclassTranslationOptions(TranslationOptions):
|
||||
pass
|
||||
|
||||
|
||||
@register(PatchTestPage)
|
||||
class PatchTestPageTranslationOptions(WagtailTranslationOptions):
|
||||
class PatchTestPageTranslationOptions(TranslationOptions):
|
||||
fields = ('description',)
|
||||
|
||||
|
||||
class PatchTestSnippetTranslationOptions(WagtailTranslationOptions):
|
||||
@register(PatchTestSnippetNoPanels)
|
||||
class PatchTestSnippetNoPanelsTranslationOptions(TranslationOptions):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
translator.register(PatchTestSnippet, PatchTestSnippetTranslationOptions)
|
||||
@register(PatchTestSnippet)
|
||||
class PatchTestSnippetTranslationOptions(TranslationOptions):
|
||||
pass
|
||||
|
||||
|
||||
# Panel Patching Models
|
||||
|
||||
class FieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class FieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
|
|
@ -47,7 +72,7 @@ translator.register(FieldPanelPage, FieldPanelTranslationOptions)
|
|||
translator.register(FieldPanelSnippet, FieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class ImageChooserPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class ImageChooserPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('image',)
|
||||
|
||||
|
||||
|
|
@ -55,7 +80,7 @@ translator.register(ImageChooserPanelPage, ImageChooserPanelTranslationOptions)
|
|||
translator.register(ImageChooserPanelSnippet, ImageChooserPanelTranslationOptions)
|
||||
|
||||
|
||||
class FieldRowPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class FieldRowPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('other_name',)
|
||||
|
||||
|
||||
|
|
@ -63,7 +88,7 @@ translator.register(FieldRowPanelPage, FieldRowPanelTranslationOptions)
|
|||
translator.register(FieldRowPanelSnippet, FieldRowPanelTranslationOptions)
|
||||
|
||||
|
||||
class StreamFieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class StreamFieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
|
|
@ -71,7 +96,7 @@ translator.register(StreamFieldPanelPage, StreamFieldPanelTranslationOptions)
|
|||
translator.register(StreamFieldPanelSnippet, StreamFieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class MultiFieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class MultiFieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
|
|
@ -79,14 +104,14 @@ translator.register(MultiFieldPanelPage, MultiFieldPanelTranslationOptions)
|
|||
translator.register(MultiFieldPanelSnippet, MultiFieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class InlinePanelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelTranslationOptions(TranslationOptions):
|
||||
fields = ('field_name', 'image_chooser', 'fieldrow_name',)
|
||||
|
||||
|
||||
translator.register(BaseInlineModel, InlinePanelTranslationOptions)
|
||||
|
||||
|
||||
class InlinePanelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
|
|
@ -95,8 +120,13 @@ translator.register(SnippetInlineModel, InlinePanelTranslationOptions)
|
|||
|
||||
|
||||
@register(InlinePanelPage)
|
||||
class InlinePanelModelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelModelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
translator.register(InlinePanelSnippet, InlinePanelModelTranslationOptions)
|
||||
|
||||
|
||||
@register(RoutablePageTest)
|
||||
class RoutablePageTestTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.views.i18n import set_language
|
||||
from wagtail.wagtailcore import urls as wagtail_urls
|
||||
try:
|
||||
from wagtail.core import urls as wagtail_urls
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore import urls as wagtail_urls
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^set_language/$', set_language, {},
|
||||
|
|
|
|||
82
wagtail_modeltranslation/tests/util.py
Normal file
82
wagtail_modeltranslation/tests/util.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
class PageFactory(object):
|
||||
|
||||
def __init__(self, initial_path=0):
|
||||
self.root_path = initial_path
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
self.root_path += 1
|
||||
return self.root_path
|
||||
|
||||
def create_page_tree(self, nodes=None):
|
||||
"""
|
||||
Creates a page tree with a dict of page nodes following the below structure:
|
||||
{
|
||||
'model': Page,
|
||||
'args': [],
|
||||
'kwargs': {'title': 'root',},
|
||||
'children': {
|
||||
'child': {
|
||||
'model': Page,
|
||||
'args': [],
|
||||
'kwargs': {'title': 'child',},
|
||||
'children': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
:param nodes: representing a page tree
|
||||
:return: site
|
||||
"""
|
||||
if not nodes:
|
||||
return None
|
||||
|
||||
from .models import TestRootPage
|
||||
try:
|
||||
from wagtail.core.models import Site
|
||||
except ImportError:
|
||||
from wagtail.wagtailcore.models import Site
|
||||
|
||||
# add a top root node to mimic Wagtail's real behaviour
|
||||
all_nodes = {
|
||||
'model': TestRootPage,
|
||||
'kwargs': {'title': 'Root', 'slug': 'root', },
|
||||
'children': {
|
||||
'site_root': nodes,
|
||||
},
|
||||
}
|
||||
self.create_instance(all_nodes)
|
||||
|
||||
site_root_node = nodes['instance']
|
||||
site = Site.objects.create(root_page=site_root_node, hostname='localhost', port=80, is_default_site=True)
|
||||
return site
|
||||
|
||||
def create_instance(self, node, parent=None, order=None):
|
||||
if parent:
|
||||
path = "{}{}".format(parent.path, "%04d" % (order,))
|
||||
depth = parent.depth + 1
|
||||
else:
|
||||
path = "%04d" % (self.path,)
|
||||
depth = 1
|
||||
|
||||
args = node.get('args', [])
|
||||
kwargs = node.get('kwargs', {})
|
||||
kwargs['path'] = kwargs.get('path', path)
|
||||
kwargs['depth'] = kwargs.get('depth', depth)
|
||||
|
||||
if parent:
|
||||
node_page = parent.add_child(instance=node['model'](*args, **kwargs))
|
||||
node_page.save()
|
||||
else:
|
||||
node_page = node['model'].objects.create(*args, **kwargs)
|
||||
|
||||
node_page.save_revision().publish()
|
||||
node['instance'] = node_page
|
||||
|
||||
for n, child in enumerate(node.get('children', {}).values()):
|
||||
self.create_instance(child, node_page, n+1)
|
||||
|
||||
return node_page
|
||||
|
||||
|
||||
page_factory = PageFactory()
|
||||
|
|
@ -2,9 +2,23 @@
|
|||
|
||||
from modeltranslation.decorators import register
|
||||
from modeltranslation.translator import TranslationOptions
|
||||
from wagtail.wagtailcore.models import Page
|
||||
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):
|
||||
pass
|
||||
fields = (
|
||||
'title',
|
||||
'seo_title',
|
||||
'search_description',
|
||||
)
|
||||
if settings.TRANSLATE_SLUGS:
|
||||
fields += (
|
||||
'slug',
|
||||
'url_path',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
from modeltranslation.translator import TranslationOptions
|
||||
|
||||
|
||||
class WagtailTranslationOptions(TranslationOptions):
|
||||
def __init__(self, model):
|
||||
from wagtail.wagtailcore.models import Page
|
||||
if Page in model.__bases__:
|
||||
self.fields += (
|
||||
'title',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'search_description',
|
||||
'url_path',)
|
||||
|
||||
super(WagtailTranslationOptions, self).__init__(model)
|
||||
|
|
@ -2,16 +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.wagtailcore import hooks
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.wagtailcore.rich_text import PageLinkHandler
|
||||
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')
|
||||
|
|
@ -20,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
|
||||
|
||||
|
|
@ -54,13 +83,13 @@ def return_translation_target_field_rendered_html(request, page_id):
|
|||
# Patch field prefixes from origin field to target field
|
||||
target_field_patched = []
|
||||
for item in origin_field_serialized:
|
||||
patched_item = None
|
||||
patched_item = {'name': None, 'value': None}
|
||||
for att in iteritems(item):
|
||||
target_value = att[1]
|
||||
if att[0] == 'name':
|
||||
target_value = att[1].replace(
|
||||
origin_field_name, target_field_name)
|
||||
patched_item = {"name": target_value}
|
||||
patched_item["name"] = target_value
|
||||
else:
|
||||
patched_item["value"] = att[1]
|
||||
|
||||
|
|
@ -102,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')
|
||||
|
|
@ -142,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
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue