hsy77 4 vuotta sitten
vanhempi
commit
b2a843ec09
530 muutettua tiedostoa jossa 44706 lisäystä ja 2 poistoa
  1. +3
    -0
      .coveragerc
  2. +4
    -0
      .flake8
  3. +2
    -0
      .flaskenv
  4. +112
    -0
      .gitignore
  5. +21
    -0
      LICENSE
  6. +25
    -0
      Pipfile
  7. +369
    -0
      Pipfile.lock
  8. +1
    -0
      Procfile
  9. +1
    -0
      Procfile.windows
  10. +82
    -2
      README.md
  11. +223
    -0
      bluelog/__init__.py
  12. +0
    -0
      bluelog/blueprints/__init__.py
  13. +259
    -0
      bluelog/blueprints/admin.py
  14. +86
    -0
      bluelog/blueprints/auth.py
  15. +106
    -0
      bluelog/blueprints/blog.py
  16. +44
    -0
      bluelog/emails.py
  17. +38
    -0
      bluelog/extensions.py
  18. +216
    -0
      bluelog/fakes.py
  19. +77
    -0
      bluelog/forms.py
  20. +84
    -0
      bluelog/models.py
  21. +74
    -0
      bluelog/settings.py
  22. +1483
    -0
      bluelog/static/ckeditor/CHANGES.md
  23. +1420
    -0
      bluelog/static/ckeditor/LICENSE.md
  24. +39
    -0
      bluelog/static/ckeditor/README.md
  25. +10
    -0
      bluelog/static/ckeditor/adapters/jquery.js
  26. +165
    -0
      bluelog/static/ckeditor/build-config.js
  27. +1213
    -0
      bluelog/static/ckeditor/ckeditor.js
  28. +38
    -0
      bluelog/static/ckeditor/config.js
  29. +207
    -0
      bluelog/static/ckeditor/contents.css
  30. +5
    -0
      bluelog/static/ckeditor/lang/af.js
  31. +5
    -0
      bluelog/static/ckeditor/lang/ar.js
  32. +5
    -0
      bluelog/static/ckeditor/lang/az.js
  33. +5
    -0
      bluelog/static/ckeditor/lang/bg.js
  34. +5
    -0
      bluelog/static/ckeditor/lang/bn.js
  35. +5
    -0
      bluelog/static/ckeditor/lang/bs.js
  36. +5
    -0
      bluelog/static/ckeditor/lang/ca.js
  37. +5
    -0
      bluelog/static/ckeditor/lang/cs.js
  38. +5
    -0
      bluelog/static/ckeditor/lang/cy.js
  39. +5
    -0
      bluelog/static/ckeditor/lang/da.js
  40. +5
    -0
      bluelog/static/ckeditor/lang/de-ch.js
  41. +5
    -0
      bluelog/static/ckeditor/lang/de.js
  42. +5
    -0
      bluelog/static/ckeditor/lang/el.js
  43. +5
    -0
      bluelog/static/ckeditor/lang/en-au.js
  44. +5
    -0
      bluelog/static/ckeditor/lang/en-ca.js
  45. +5
    -0
      bluelog/static/ckeditor/lang/en-gb.js
  46. +5
    -0
      bluelog/static/ckeditor/lang/en.js
  47. +5
    -0
      bluelog/static/ckeditor/lang/eo.js
  48. +5
    -0
      bluelog/static/ckeditor/lang/es-mx.js
  49. +5
    -0
      bluelog/static/ckeditor/lang/es.js
  50. +5
    -0
      bluelog/static/ckeditor/lang/et.js
  51. +5
    -0
      bluelog/static/ckeditor/lang/eu.js
  52. +5
    -0
      bluelog/static/ckeditor/lang/fa.js
  53. +5
    -0
      bluelog/static/ckeditor/lang/fi.js
  54. +5
    -0
      bluelog/static/ckeditor/lang/fo.js
  55. +5
    -0
      bluelog/static/ckeditor/lang/fr-ca.js
  56. +5
    -0
      bluelog/static/ckeditor/lang/fr.js
  57. +5
    -0
      bluelog/static/ckeditor/lang/gl.js
  58. +5
    -0
      bluelog/static/ckeditor/lang/gu.js
  59. +5
    -0
      bluelog/static/ckeditor/lang/he.js
  60. +5
    -0
      bluelog/static/ckeditor/lang/hi.js
  61. +5
    -0
      bluelog/static/ckeditor/lang/hr.js
  62. +5
    -0
      bluelog/static/ckeditor/lang/hu.js
  63. +5
    -0
      bluelog/static/ckeditor/lang/id.js
  64. +5
    -0
      bluelog/static/ckeditor/lang/is.js
  65. +5
    -0
      bluelog/static/ckeditor/lang/it.js
  66. +5
    -0
      bluelog/static/ckeditor/lang/ja.js
  67. +5
    -0
      bluelog/static/ckeditor/lang/ka.js
  68. +5
    -0
      bluelog/static/ckeditor/lang/km.js
  69. +5
    -0
      bluelog/static/ckeditor/lang/ko.js
  70. +5
    -0
      bluelog/static/ckeditor/lang/ku.js
  71. +5
    -0
      bluelog/static/ckeditor/lang/lt.js
  72. +5
    -0
      bluelog/static/ckeditor/lang/lv.js
  73. +5
    -0
      bluelog/static/ckeditor/lang/mk.js
  74. +5
    -0
      bluelog/static/ckeditor/lang/mn.js
  75. +5
    -0
      bluelog/static/ckeditor/lang/ms.js
  76. +5
    -0
      bluelog/static/ckeditor/lang/nb.js
  77. +5
    -0
      bluelog/static/ckeditor/lang/nl.js
  78. +5
    -0
      bluelog/static/ckeditor/lang/no.js
  79. +5
    -0
      bluelog/static/ckeditor/lang/oc.js
  80. +5
    -0
      bluelog/static/ckeditor/lang/pl.js
  81. +5
    -0
      bluelog/static/ckeditor/lang/pt-br.js
  82. +5
    -0
      bluelog/static/ckeditor/lang/pt.js
  83. +5
    -0
      bluelog/static/ckeditor/lang/ro.js
  84. +5
    -0
      bluelog/static/ckeditor/lang/ru.js
  85. +5
    -0
      bluelog/static/ckeditor/lang/si.js
  86. +5
    -0
      bluelog/static/ckeditor/lang/sk.js
  87. +5
    -0
      bluelog/static/ckeditor/lang/sl.js
  88. +5
    -0
      bluelog/static/ckeditor/lang/sq.js
  89. +5
    -0
      bluelog/static/ckeditor/lang/sr-latn.js
  90. +5
    -0
      bluelog/static/ckeditor/lang/sr.js
  91. +5
    -0
      bluelog/static/ckeditor/lang/sv.js
  92. +5
    -0
      bluelog/static/ckeditor/lang/th.js
  93. +5
    -0
      bluelog/static/ckeditor/lang/tr.js
  94. +5
    -0
      bluelog/static/ckeditor/lang/tt.js
  95. +5
    -0
      bluelog/static/ckeditor/lang/ug.js
  96. +5
    -0
      bluelog/static/ckeditor/lang/uk.js
  97. +5
    -0
      bluelog/static/ckeditor/lang/vi.js
  98. +5
    -0
      bluelog/static/ckeditor/lang/zh-cn.js
  99. +5
    -0
      bluelog/static/ckeditor/lang/zh.js
  100. +10
    -0
      bluelog/static/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js

+ 3
- 0
.coveragerc Näytä tiedosto

@ -0,0 +1,3 @@
[run]
source = bluelog
branch = true

+ 4
- 0
.flake8 Näytä tiedosto

@ -0,0 +1,4 @@
[flake8]
exclude = .git,*migrations*
max-line-length = 119
max-complexity = 7

+ 2
- 0
.flaskenv Näytä tiedosto

@ -0,0 +1,2 @@
FLASK_APP=bluelog
FLASK_ENV=development

+ 112
- 0
.gitignore Näytä tiedosto

@ -0,0 +1,112 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# Development
*.db
.idea
logs/*
!logs/.gitkeep
uploads/*
!uploads/.gitkeep

+ 21
- 0
LICENSE Näytä tiedosto

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Grey Li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 25
- 0
Pipfile Näytä tiedosto

@ -0,0 +1,25 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
coverage = "*"
watchdog = "*"
"flake8" = "*"
faker = "*"
[packages]
flask = "*"
flask-ckeditor = "*"
flask-mail = "*"
flask-sqlalchemy = "*"
flask-wtf = "*"
flask-moment = "*"
python-dotenv = "*"
bootstrap-flask = "*"
flask-login = "*"
flask-debugtoolbar = "*"
gunicorn = "*"
"psycopg2" = "*"
flask-migrate = "*"

+ 369
- 0
Pipfile.lock Näytä tiedosto

@ -0,0 +1,369 @@
{
"_meta": {
"hash": {
"sha256": "b211c0ef54a1d431af2250e563c542fa0a957b3d009bd8005c0195ce56fd55e3"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"alembic": {
"hashes": [
"sha256:035ab00497217628bf5d0be82d664d8713ab13d37b630084da8e1f98facf4dbf"
],
"version": "==1.4.2"
},
"blinker": {
"hashes": [
"sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"
],
"version": "==1.4"
},
"bootstrap-flask": {
"hashes": [
"sha256:d03c738f5377dbe7ecce010e28afcc0fb8373c4c9d01f8590af7657b53342c1d",
"sha256:ed817e82acadac4c8b3fc2d1a310325b6ed395d13b0f6ca9d14e1742f880e685"
],
"index": "pypi",
"version": "==1.2.0"
},
"click": {
"hashes": [
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
],
"version": "==7.1.1"
},
"flask": {
"hashes": [
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
"sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
],
"index": "pypi",
"version": "==1.1.2"
},
"flask-ckeditor": {
"hashes": [
"sha256:327ad60c3787d96cdc579ce04c284d33343802d3b7422ba998aadbbdee6756f1",
"sha256:8d91ee747473dc4e6e0dc166fd71b3136b7d388de757731c019fad835d776c8e"
],
"index": "pypi",
"version": "==0.4.3"
},
"flask-debugtoolbar": {
"hashes": [
"sha256:0e9a80d4c599233c68376e81cc99976200b5ac5248cfb24f18935cc5b69ac5b3",
"sha256:3c4e79d354ede014e6657c545a536d4fb273cc89e3fd6b4835b02e346dd3aab4"
],
"index": "pypi",
"version": "==0.11.0"
},
"flask-login": {
"hashes": [
"sha256:6d33aef15b5bcead780acc339464aae8a6e28f13c90d8b1cf9de8b549d1c0b4b",
"sha256:7451b5001e17837ba58945aead261ba425fdf7b4f0448777e597ddab39f4fba0"
],
"index": "pypi",
"version": "==0.5.0"
},
"flask-mail": {
"hashes": [
"sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"
],
"index": "pypi",
"version": "==0.9.1"
},
"flask-migrate": {
"hashes": [
"sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732",
"sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee"
],
"index": "pypi",
"version": "==2.5.3"
},
"flask-moment": {
"hashes": [
"sha256:3c509afa25fd77459c9d799f292dcea384b1cd588ed1fd68f97f6fda6131299e",
"sha256:3e8b88f99af7cf75f2f29ef9d8c158eb92ca6f3c1ba6456ad70f715efa6eb7f7"
],
"index": "pypi",
"version": "==0.9.0"
},
"flask-sqlalchemy": {
"hashes": [
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327",
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"
],
"index": "pypi",
"version": "==2.4.1"
},
"flask-wtf": {
"hashes": [
"sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2",
"sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"
],
"index": "pypi",
"version": "==0.14.3"
},
"gunicorn": {
"hashes": [
"sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
"sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
],
"index": "pypi",
"version": "==20.0.4"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
],
"version": "==2.11.1"
},
"mako": {
"hashes": [
"sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d",
"sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9"
],
"version": "==1.1.2"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
"version": "==1.1.1"
},
"psycopg2": {
"hashes": [
"sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677",
"sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d",
"sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b",
"sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c",
"sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0",
"sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f",
"sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4",
"sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38",
"sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6",
"sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b",
"sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151",
"sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a",
"sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6"
],
"index": "pypi",
"version": "==2.8.4"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"version": "==2.8.1"
},
"python-dotenv": {
"hashes": [
"sha256:81822227f771e0cab235a2939f0f265954ac4763cafd806d845801c863bf372f",
"sha256:92b3123fb2d58a284f76cc92bfe4ee6c502c32ded73e8b051c4f6afc8b6751ed"
],
"index": "pypi",
"version": "==0.12.0"
},
"python-editor": {
"hashes": [
"sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
"sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b",
"sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"
],
"version": "==1.0.4"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"sqlalchemy": {
"hashes": [
"sha256:c4cca4aed606297afbe90d4306b49ad3a4cd36feb3f87e4bfd655c57fd9ef445"
],
"version": "==1.3.15"
},
"werkzeug": {
"hashes": [
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
],
"version": "==1.0.1"
},
"wtforms": {
"hashes": [
"sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61",
"sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1"
],
"version": "==2.2.1"
}
},
"develop": {
"coverage": {
"hashes": [
"sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0",
"sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30",
"sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b",
"sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0",
"sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823",
"sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe",
"sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037",
"sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6",
"sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31",
"sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd",
"sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892",
"sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1",
"sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78",
"sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac",
"sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006",
"sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014",
"sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2",
"sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7",
"sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8",
"sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7",
"sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9",
"sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1",
"sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307",
"sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a",
"sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435",
"sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0",
"sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5",
"sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441",
"sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732",
"sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de",
"sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"
],
"index": "pypi",
"version": "==5.0.4"
},
"entrypoints": {
"hashes": [
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
],
"version": "==0.3"
},
"faker": {
"hashes": [
"sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f",
"sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976"
],
"index": "pypi",
"version": "==4.0.2"
},
"flake8": {
"hashes": [
"sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb",
"sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"
],
"index": "pypi",
"version": "==3.7.9"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"pathtools": {
"hashes": [
"sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"
],
"version": "==0.1.2"
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
],
"version": "==2.5.0"
},
"pyflakes": {
"hashes": [
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"version": "==2.8.1"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"text-unidecode": {
"hashes": [
"sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
"sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
],
"version": "==1.3"
},
"watchdog": {
"hashes": [
"sha256:c560efb643faed5ef28784b2245cf8874f939569717a4a12826a173ac644456b"
],
"index": "pypi",
"version": "==0.10.2"
}
}
}

+ 1
- 0
Procfile Näytä tiedosto

@ -0,0 +1 @@
web: gunicorn wsgi:app --log-file -

+ 1
- 0
Procfile.windows Näytä tiedosto

@ -0,0 +1 @@
web: flask run

+ 82
- 2
README.md Näytä tiedosto

@ -1,3 +1,83 @@
# bluelog
# Bluelog 安装
博客系统
**使用镜像直接看第4步**
**云数据库以删除,需要新建**(防止花费过高)
## 1,安装过程
clone:
```
$ git clone https://github.com/hsy77/bluelog.git
$ cd bluelog
```
## 2,使用已有数据库
## (使用新建数据库先跳转至3,再做2)
在云主机中安装依赖:
```
(这两步可不做,即不用创建虚拟环境)
$ python3 -m venv env
$ source env/bin/activate
(需要在云主机上或者虚拟环境中运行,两个环境都可以,为了保证psycopg2的安装不报错)
$ sudo yum install postgresql-libs -y
$ sudo yum install postgresql-devel -y
$ sudo yum install gcc -y
(安装需要的包)
$ pip3 install -r requirements.txt
```
生成数据并且运行:
```
$ flask forge #将数据导入数据表中
$ flask run -h 0.0.0.0 -p 80 #在0.0.0.0 80端口运行,为了使得使用外网ip可以访问
* Running on http://0.0.0.0:80/
```
接下来就可以在网页上输入云主机外网ip访问
Test account:
* username: `admin`
* password: `helloflask`
## 3, 使用新建云数据库
- 将`bluelog\bluelog\settings.py` 中的以下四个参数改为自己数据库的设置
![数据库设置.jpg](http://ww1.sinaimg.cn/large/005ZSk16gy1gmqk9kv2iwj30cw03rwei.jpg)
# 4,从镜像中导入:
### - 先做步骤3,新建数据库
### - 然后
生成数据并且运行:
```
$ flask forge #将数据导入数据表中
$ flask run -h 0.0.0.0 -p 80 #在0.0.0.0 80端口运行,为了使得使用外网ip可以访问
* Running on http://0.0.0.0:80/
```
接下来就可以在网页上输入云主机外网ip访问
Test account:
* username: `admin`
* password: `helloflask`
## 安装成果
![安装成果.jpg](http://ww1.sinaimg.cn/large/005ZSk16gy1gmqk9ujm8xj30y30q0wi2.jpg)

+ 223
- 0
bluelog/__init__.py Näytä tiedosto

@ -0,0 +1,223 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
import logging
import os
from logging.handlers import SMTPHandler, RotatingFileHandler
import click
from flask import Flask, render_template, request
from flask_login import current_user
from flask_sqlalchemy import get_debug_queries
from flask_wtf.csrf import CSRFError
from bluelog.blueprints.admin import admin_bp
from bluelog.blueprints.auth import auth_bp
from bluelog.blueprints.blog import blog_bp
from bluelog.extensions import bootstrap, db, login_manager, csrf, ckeditor, mail, moment, toolbar, migrate
from bluelog.models import Admin, Post, Category, Comment, Link
from bluelog.settings import config
basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def create_app(config_name=None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development')
app = Flask('bluelog')
app.config.from_object(config[config_name])
register_logging(app)
register_extensions(app)
register_blueprints(app)
register_commands(app)
register_errors(app)
register_shell_context(app)
register_template_context(app)
register_request_handlers(app)
return app
def register_logging(app):
class RequestFormatter(logging.Formatter):
def format(self, record):
record.url = request.url
record.remote_addr = request.remote_addr
return super(RequestFormatter, self).format(record)
request_formatter = RequestFormatter(
'[%(asctime)s] %(remote_addr)s requested %(url)s\n'
'%(levelname)s in %(module)s: %(message)s'
)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler = RotatingFileHandler(os.path.join(basedir, 'logs/bluelog.log'),
maxBytes=10 * 1024 * 1024, backupCount=10)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
mail_handler = SMTPHandler(
mailhost=app.config['MAIL_SERVER'],
fromaddr=app.config['MAIL_USERNAME'],
toaddrs=['ADMIN_EMAIL'],
subject='Bluelog Application Error',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']))
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(request_formatter)
if not app.debug:
app.logger.addHandler(mail_handler)
app.logger.addHandler(file_handler)
def register_extensions(app):
bootstrap.init_app(app)
db.init_app(app)
login_manager.init_app(app)
csrf.init_app(app)
ckeditor.init_app(app)
mail.init_app(app)
moment.init_app(app)
toolbar.init_app(app)
migrate.init_app(app, db)
def register_blueprints(app):
app.register_blueprint(blog_bp)
app.register_blueprint(admin_bp, url_prefix='/admin')
app.register_blueprint(auth_bp, url_prefix='/auth')
def register_shell_context(app):
@app.shell_context_processor
def make_shell_context():
return dict(db=db, Admin=Admin, Post=Post, Category=Category, Comment=Comment)
def register_template_context(app):
@app.context_processor
def make_template_context():
admin = Admin.query.first()
categories = Category.query.order_by(Category.name).all()
links = Link.query.order_by(Link.name).all()
if current_user.is_authenticated:
unread_comments = Comment.query.filter_by(reviewed=False).count()
else:
unread_comments = None
return dict(
admin=admin, categories=categories,
links=links, unread_comments=unread_comments)
def register_errors(app):
@app.errorhandler(400)
def bad_request(e):
return render_template('errors/400.html'), 400
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('errors/500.html'), 500
@app.errorhandler(CSRFError)
def handle_csrf_error(e):
return render_template('errors/400.html', description=e.description), 400
def register_commands(app):
@app.cli.command()
@click.option('--drop', is_flag=True, help='Create after drop.')
def initdb(drop):
"""Initialize the database."""
if drop:
click.confirm('This operation will delete the database, do you want to continue?', abort=True)
db.drop_all()
click.echo('Drop tables.')
db.create_all()
click.echo('Initialized database.')
@app.cli.command()
@click.option('--username', prompt=True, help='The username used to login.')
@click.option('--password', prompt=True, hide_input=True,
confirmation_prompt=True, help='The password used to login.')
def init(username, password):
"""Building Bluelog, just for you."""
click.echo('Initializing the database...')
db.create_all()
admin = Admin.query.first()
if admin is not None:
click.echo('The administrator already exists, updating...')
admin.username = username
admin.set_password(password)
else:
click.echo('Creating the temporary administrator account...')
admin = Admin(
username=username,
blog_title='Bluelog',
blog_sub_title="No, I'm the real thing.",
name='Admin',
about='Anything about you.'
)
admin.set_password(password)
db.session.add(admin)
category = Category.query.first()
if category is None:
click.echo('Creating the default category...')
category = Category(name='Default')
db.session.add(category)
db.session.commit()
click.echo('Done.')
@app.cli.command()
@click.option('--category', default=10, help='Quantity of categories, default is 10.')
@click.option('--post', default=15, help='Quantity of posts, default is 50.')
@click.option('--comment', default=500, help='Quantity of comments, default is 500.')
def forge(category, post, comment):
"""Generate fake data."""
from bluelog.fakes import fake_admin, fake_categories, fake_posts, fake_comments, fake_links
db.drop_all()
db.create_all()
click.echo('Generating the administrator...')
fake_admin()
click.echo('Generating %d categories...' % category)
fake_categories(category)
click.echo('Generating %d posts...' % post)
fake_posts(post)
click.echo('Generating %d comments...' % comment)
fake_comments(comment)
click.echo('Generating links...')
fake_links()
click.echo('Done.')
def register_request_handlers(app):
@app.after_request
def query_profiler(response):
for q in get_debug_queries():
if q.duration >= app.config['BLUELOG_SLOW_QUERY_THRESHOLD']:
app.logger.warning(
'Slow query: Duration: %fs\n Context: %s\nQuery: %s\n '
% (q.duration, q.context, q.statement)
)
return response

+ 0
- 0
bluelog/blueprints/__init__.py Näytä tiedosto


+ 259
- 0
bluelog/blueprints/admin.py Näytä tiedosto

@ -0,0 +1,259 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
import os
from flask import render_template, flash, redirect, url_for, request, current_app, Blueprint, send_from_directory
from flask_login import login_required, current_user
from flask_ckeditor import upload_success, upload_fail
from bluelog.extensions import db
from bluelog.forms import SettingForm, PostForm, CategoryForm, LinkForm
from bluelog.models import Post, Category, Comment, Link
from bluelog.utils import redirect_back, allowed_file
admin_bp = Blueprint('admin', __name__)
@admin_bp.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
form = SettingForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.blog_title = form.blog_title.data
current_user.blog_sub_title = form.blog_sub_title.data
current_user.about = form.about.data
db.session.commit()
flash('Setting updated.', 'success')
return redirect(url_for('blog.index'))
form.name.data = current_user.name
form.blog_title.data = current_user.blog_title
form.blog_sub_title.data = current_user.blog_sub_title
form.about.data = current_user.about
return render_template('admin/settings.html', form=form)
@admin_bp.route('/post/manage')
@login_required
def manage_post():
page = request.args.get('page', 1, type=int)
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(
page, per_page=current_app.config['BLUELOG_MANAGE_POST_PER_PAGE'])
posts = pagination.items
return render_template('admin/manage_post.html', page=page, pagination=pagination, posts=posts)
@admin_bp.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
category = Category.query.get(form.category.data)
post = Post(title=title, body=body, category=category)
# same with:
# category_id = form.category.data
# post = Post(title=title, body=body, category_id=category_id)
db.session.add(post)
db.session.commit()
flash('Post created.', 'success')
return redirect(url_for('blog.show_post', post_id=post.id))
return render_template('admin/new_post.html', form=form)
@admin_bp.route('/post/<int:post_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
form = PostForm()
post = Post.query.get_or_404(post_id)
if form.validate_on_submit():
post.title = form.title.data
post.body = form.body.data
post.category = Category.query.get(form.category.data)
db.session.commit()
flash('Post updated.', 'success')
return redirect(url_for('blog.show_post', post_id=post.id))
form.title.data = post.title
form.body.data = post.body
form.category.data = post.category_id
return render_template('admin/edit_post.html', form=form)
@admin_bp.route('/post/<int:post_id>/delete', methods=['POST'])
@login_required
def delete_post(post_id):
post = Post.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
flash('Post deleted.', 'success')
return redirect_back()
@admin_bp.route('/post/<int:post_id>/set-comment', methods=['POST'])
@login_required
def set_comment(post_id):
post = Post.query.get_or_404(post_id)
if post.can_comment:
post.can_comment = False
flash('Comment disabled.', 'success')
else:
post.can_comment = True
flash('Comment enabled.', 'success')
db.session.commit()
return redirect_back()
@admin_bp.route('/comment/manage')
@login_required
def manage_comment():
filter_rule = request.args.get('filter', 'all') # 'all', 'unreviewed', 'admin'
page = request.args.get('page', 1, type=int)
per_page = current_app.config['BLUELOG_COMMENT_PER_PAGE']
if filter_rule == 'unread':
filtered_comments = Comment.query.filter_by(reviewed=False)
elif filter_rule == 'admin':
filtered_comments = Comment.query.filter_by(from_admin=True)
else:
filtered_comments = Comment.query
pagination = filtered_comments.order_by(Comment.timestamp.desc()).paginate(page, per_page=per_page)
comments = pagination.items
return render_template('admin/manage_comment.html', comments=comments, pagination=pagination)
@admin_bp.route('/comment/<int:comment_id>/approve', methods=['POST'])
@login_required
def approve_comment(comment_id):
comment = Comment.query.get_or_404(comment_id)
comment.reviewed = True
db.session.commit()
flash('Comment published.', 'success')
return redirect_back()
@admin_bp.route('/comment/<int:comment_id>/delete', methods=['POST'])
@login_required
def delete_comment(comment_id):
comment = Comment.query.get_or_404(comment_id)
db.session.delete(comment)
db.session.commit()
flash('Comment deleted.', 'success')
return redirect_back()
@admin_bp.route('/category/manage')
@login_required
def manage_category():
return render_template('admin/manage_category.html')
@admin_bp.route('/category/new', methods=['GET', 'POST'])
@login_required
def new_category():
form = CategoryForm()
if form.validate_on_submit():
name = form.name.data
category = Category(name=name)
db.session.add(category)
db.session.commit()
flash('Category created.', 'success')
return redirect(url_for('.manage_category'))
return render_template('admin/new_category.html', form=form)
@admin_bp.route('/category/<int:category_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_category(category_id):
form = CategoryForm()
category = Category.query.get_or_404(category_id)
if category.id == 1:
flash('You can not edit the default category.', 'warning')
return redirect(url_for('blog.index'))
if form.validate_on_submit():
category.name = form.name.data
db.session.commit()
flash('Category updated.', 'success')
return redirect(url_for('.manage_category'))
form.name.data = category.name
return render_template('admin/edit_category.html', form=form)
@admin_bp.route('/category/<int:category_id>/delete', methods=['POST'])
@login_required
def delete_category(category_id):
category = Category.query.get_or_404(category_id)
if category.id == 1:
flash('You can not delete the default category.', 'warning')
return redirect(url_for('blog.index'))
category.delete()
flash('Category deleted.', 'success')
return redirect(url_for('.manage_category'))
@admin_bp.route('/link/manage')
@login_required
def manage_link():
return render_template('admin/manage_link.html')
@admin_bp.route('/link/new', methods=['GET', 'POST'])
@login_required
def new_link():
form = LinkForm()
if form.validate_on_submit():
name = form.name.data
url = form.url.data
link = Link(name=name, url=url)
db.session.add(link)
db.session.commit()
flash('Link created.', 'success')
return redirect(url_for('.manage_link'))
return render_template('admin/new_link.html', form=form)
@admin_bp.route('/link/<int:link_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_link(link_id):
form = LinkForm()
link = Link.query.get_or_404(link_id)
if form.validate_on_submit():
link.name = form.name.data
link.url = form.url.data
db.session.commit()
flash('Link updated.', 'success')
return redirect(url_for('.manage_link'))
form.name.data = link.name
form.url.data = link.url
return render_template('admin/edit_link.html', form=form)
@admin_bp.route('/link/<int:link_id>/delete', methods=['POST'])
@login_required
def delete_link(link_id):
link = Link.query.get_or_404(link_id)
db.session.delete(link)
db.session.commit()
flash('Link deleted.', 'success')
return redirect(url_for('.manage_link'))
@admin_bp.route('/uploads/<path:filename>')
def get_image(filename):
return send_from_directory(current_app.config['BLUELOG_UPLOAD_PATH'], filename)
@admin_bp.route('/upload', methods=['POST'])
def upload_image():
f = request.files.get('upload')
if not allowed_file(f.filename):
return upload_fail('Image only!')
f.save(os.path.join(current_app.config['BLUELOG_UPLOAD_PATH'], f.filename))
url = url_for('.get_image', filename=f.filename)
return upload_success(url, f.filename)

+ 86
- 0
bluelog/blueprints/auth.py Näytä tiedosto

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from flask import render_template, flash, redirect, url_for, Blueprint
from flask_login import login_user, logout_user, login_required, current_user
from bluelog.forms import LoginForm, RegisterForm
from bluelog.models import Admin
from bluelog.utils import redirect_back
from bluelog.extensions import db
from sqlalchemy.exc import IntegrityError
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('blog.index'))
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remember = form.remember.data
admin = Admin.query.filter_by(username=username).first()
# admin = Admin.query.first()
if admin:
if username == admin.username and admin.validate_password(password):
login_user(admin, remember)
flash('Welcome back.', 'info')
return redirect_back()
flash('Invalid username or password.', 'warning')
else:
flash('No account.', 'warning')
return render_template('auth/login.html', form=form)
@auth_bp.route('/logout')
@login_required
def logout():
logout_user()
flash('Logout success.', 'info')
return redirect_back()
def create_user(username, password):
obj = Admin(
username=username,
blog_title='Bluelog',
blog_sub_title="No, I'm the real thing.",
name='Mima Kirigoe',
about='Um, l, Mima Kirigoe, had a fun time as a member of CHAM...'
)
obj.set_password(password)
db.session.add(obj)
code = 200
try:
db.session.commit()
return code
except:
code = 500
db.session.rollback()
return code
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remember = form.remember.data
code = create_user(username, password)
if code == 200:
flash('注册成功', 'info')
return redirect_back()
else:
flash('存在该用户名', 'warning')
return render_template('auth/register.html', form=form)

+ 106
- 0
bluelog/blueprints/blog.py Näytä tiedosto

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from flask import render_template, flash, redirect, url_for, request, current_app, Blueprint, abort, make_response
from flask_login import current_user
from bluelog.emails import send_new_comment_email, send_new_reply_email
from bluelog.extensions import db
from bluelog.forms import CommentForm, AdminCommentForm
from bluelog.models import Post, Category, Comment
from bluelog.utils import redirect_back
blog_bp = Blueprint('blog', __name__)
@blog_bp.route('/')
def index():
page = request.args.get('page', 1, type=int)
per_page = current_app.config['BLUELOG_POST_PER_PAGE']
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=per_page)
posts = pagination.items
return render_template('blog/index.html', pagination=pagination, posts=posts)
@blog_bp.route('/about')
def about():
return render_template('blog/about.html')
@blog_bp.route('/category/<int:category_id>')
def show_category(category_id):
category = Category.query.get_or_404(category_id)
page = request.args.get('page', 1, type=int)
per_page = current_app.config['BLUELOG_POST_PER_PAGE']
pagination = Post.query.with_parent(category).order_by(Post.timestamp.desc()).paginate(page, per_page)
posts = pagination.items
return render_template('blog/category.html', category=category, pagination=pagination, posts=posts)
@blog_bp.route('/post/<int:post_id>', methods=['GET', 'POST'])
def show_post(post_id):
post = Post.query.get_or_404(post_id)
page = request.args.get('page', 1, type=int)
per_page = current_app.config['BLUELOG_COMMENT_PER_PAGE']
pagination = Comment.query.with_parent(post).filter_by(reviewed=True).order_by(Comment.timestamp.asc()).paginate(
page, per_page)
comments = pagination.items
if current_user.is_authenticated:
form = AdminCommentForm()
form.author.data = current_user.name
form.email.data = current_app.config['BLUELOG_EMAIL']
form.site.data = url_for('.index')
from_admin = True
reviewed = True
else:
form = CommentForm()
from_admin = False
reviewed = False
if form.validate_on_submit():
author = form.author.data
email = form.email.data
site = form.site.data
body = form.body.data
comment = Comment(
author=author, email=email, site=site, body=body,
from_admin=from_admin, post=post, reviewed=reviewed)
replied_id = request.args.get('reply')
if replied_id:
replied_comment = Comment.query.get_or_404(replied_id)
comment.replied = replied_comment
send_new_reply_email(replied_comment)
db.session.add(comment)
db.session.commit()
if current_user.is_authenticated: # send message based on authentication status
flash('Comment published.', 'success')
else:
flash('Thanks, your comment will be published after reviewed.', 'info')
send_new_comment_email(post) # send notification email to admin
return redirect(url_for('.show_post', post_id=post_id))
return render_template('blog/post.html', post=post, pagination=pagination, form=form, comments=comments)
@blog_bp.route('/reply/comment/<int:comment_id>')
def reply_comment(comment_id):
comment = Comment.query.get_or_404(comment_id)
if not comment.post.can_comment:
flash('Comment is disabled.', 'warning')
return redirect(url_for('.show_post', post_id=comment.post.id))
return redirect(
url_for('.show_post', post_id=comment.post_id, reply=comment_id, author=comment.author) + '#comment-form')
@blog_bp.route('/change-theme/<theme_name>')
def change_theme(theme_name):
if theme_name not in current_app.config['BLUELOG_THEMES'].keys():
abort(404)
response = make_response(redirect_back())
response.set_cookie('theme', theme_name, max_age=30 * 24 * 60 * 60)
return response

+ 44
- 0
bluelog/emails.py Näytä tiedosto

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from threading import Thread
from flask import url_for, current_app
from flask_mail import Message
from bluelog.extensions import mail
def _send_async_mail(app, message):
with app.app_context():
mail.send(message)
def send_mail(subject, to, html):
app = current_app._get_current_object()
message = Message(subject, recipients=[to], html=html)
thr = Thread(target=_send_async_mail, args=[app, message])
thr.start()
return thr
def send_new_comment_email(post):
post_url = url_for('blog.show_post', post_id=post.id, _external=True) + '#comments'
send_mail(subject='New comment', to=current_app.config['BLUELOG_EMAIL'],
html='<p>New comment in post <i>%s</i>, click the link below to check:</p>'
'<p><a href="%s">%s</a></P>'
'<p><small style="color: #868e96">Do not reply this email.</small></p>'
% (post.title, post_url, post_url))
def send_new_reply_email(comment):
post_url = url_for('blog.show_post', post_id=comment.post_id, _external=True) + '#comments'
send_mail(subject='New reply', to=comment.email,
html='<p>New reply for the comment you left in post <i>%s</i>, click the link below to check: </p>'
'<p><a href="%s">%s</a></p>'
'<p><small style="color: #868e96">Do not reply this email.</small></p>'
% (comment.post.title, post_url, post_url))

+ 38
- 0
bluelog/extensions.py Näytä tiedosto

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from flask_bootstrap import Bootstrap
from flask_ckeditor import CKEditor
from flask_login import LoginManager
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from flask_debugtoolbar import DebugToolbarExtension
from flask_migrate import Migrate
bootstrap = Bootstrap()
db = SQLAlchemy()
login_manager = LoginManager()
csrf = CSRFProtect()
ckeditor = CKEditor()
mail = Mail()
moment = Moment()
toolbar = DebugToolbarExtension()
migrate = Migrate()
@login_manager.user_loader
def load_user(user_id):
from bluelog.models import Admin
user = Admin.query.get(int(user_id))
return user
login_manager.login_view = 'auth.login'
# login_manager.login_message = 'Your custom message'
login_manager.login_message_category = 'warning'

+ 216
- 0
bluelog/fakes.py Näytä tiedosto

@ -0,0 +1,216 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
import random
from faker import Faker
from sqlalchemy.exc import IntegrityError
from bluelog.extensions import db
from bluelog.models import Admin, Category, Post, Comment, Link
fake = Faker("zh_CN")
def fake_admin():
admin = Admin(
username='admin',
blog_title='博客',
blog_sub_title="写下你的感悟。",
name='思悟',
about='思世间之大,悟人生变换。'
)
admin.set_password('helloflask')
db.session.add(admin)
db.session.commit()
def fake_categories(count=10):
category1 = Category(name='新冠疫情')
db.session.add(category1)
category2 = Category(name='健康')
db.session.add(category2)
category3 = Category(name='体育')
db.session.add(category3)
category5 = Category(name='经济')
db.session.add(category5)
category6 = Category(name='军事')
db.session.add(category6)
category7 = Category(name='艺术')
db.session.add(category7)
try:
db.session.commit()
except IntegrityError:
db.session.rollback()
# path="D:/这学期/云计算/云计算项目/bluelog"
path=""
def fake_posts(count=15):
f1 = open(path+"/root/content/文本/content.txt", "r", encoding="utf-8")
content1 = f1.readlines()
f2 = open(path+"/root/content/文本/title.txt", "r", encoding="utf-8")
head1 = f2.readlines()
# f3 = open(path+"/root/content/健康/content.txt", "r", encoding="utf-8")
# content2 = f3.readlines()
# f4 = open(path+"/root/content/健康/title.txt", "r", encoding="utf-8")
# head2 = f4.readlines()
f5 = open(path+"/root/content/体育/content.txt", "r", encoding="utf-8")
content3 = f5.readlines()
f6 = open(path+"/root/content/体育/title.txt", "r", encoding="utf-8")
head3 = f6.readlines()
# f7 = open("C:/Users/12293/Desktop/程序员/content.txt", "r", encoding="utf-8")
# content4 = f7.readlines()
# f8 = open("C:/Users/12293/Desktop/程序员/title.txt", "r", encoding="utf-8")
# head4 = f8.readlines()
f9 = open(path+"/root/content/经济/content.txt", "r", encoding="utf-8")
content5 = f9.readlines()
f10 = open(path+"/root/content/经济/title.txt", "r", encoding="utf-8")
head5 = f10.readlines()
f11 = open(path+"/root/content/军事/content.txt", "r", encoding="utf-8")
content6 = f11.readlines()
f12 = open(path+"/root/content/军事/title.txt", "r", encoding="utf-8")
head6 = f12.readlines()
f13 = open(path+"/root/content/艺术/content.txt", "r", encoding="utf-8")
content7 = f13.readlines()
f14 = open(path+"/root/content/艺术/title.txt", "r", encoding="utf-8")
head7 = f14.readlines()
for i in range(count):
post1 = Post(
title=head1[i][:-19],
body=content1[i],
category=Category.query.get(1),
timestamp=fake.date_time_this_year()
)
# post2 = Post(
# title=head2[i],
# body=content2[i],
# category=Category.query.get(2),
# timestamp=fake.date_time_this_year()
# )
post3 = Post(
title=head3[i][:-19],
body=content3[i],
category=Category.query.get(3),
timestamp=fake.date_time_this_year()
)
# post4 = Post(
# title=head4[i],
# body=content4[i],
# category=Category.query.get(4),
# timestamp=fake.date_time_this_year()
# )
post5 = Post(
title=head5[i][:-19],
body=content5[i],
category=Category.query.get(4),
timestamp=fake.date_time_this_year()
)
post6 = Post(
title=head6[i][:-19],
body=content6[i],
category=Category.query.get(5),
timestamp=fake.date_time_this_year()
)
post7 = Post(
title=head7[i][:-19],
body=content7[i],
category=Category.query.get(6),
timestamp=fake.date_time_this_year()
)
db.session.add(post1)
# db.session.add(post2)
db.session.add(post3)
# db.session.add(post4)
db.session.add(post5)
db.session.add(post6)
db.session.add(post7)
db.session.commit()
# for i in range(count):
# post = Post(
# title=fake.sentence(),
# body=fake.text(2000),
# category=Category.query.get(random.randint(1, Category.query.count())),
# timestamp=fake.date_time_this_year()
# )
# print(type(fake.sentence()),type(fake.text(2000)))
# print(fake.sentence(),fake.text(2000))
#
# db.session.add(post)
# db.session.commit()
def fake_comments(count=500):
for i in range(count):
comment = Comment(
author=fake.name(),
email=fake.email(),
site=fake.url(),
body=fake.sentence(),
timestamp=fake.date_time_this_year(),
reviewed=True,
post=Post.query.get(random.randint(1, Post.query.count()))
)
db.session.add(comment)
salt = int(count * 0.1)
for i in range(salt):
# unreviewed comments
comment = Comment(
author=fake.name(),
email=fake.email(),
site=fake.url(),
body=fake.sentence(),
timestamp=fake.date_time_this_year(),
reviewed=False,
post=Post.query.get(random.randint(1, Post.query.count()))
)
db.session.add(comment)
# from admin
comment = Comment(
author='Mima Kirigoe',
email='mima@example.com',
site='example.com',
body=fake.sentence(),
timestamp=fake.date_time_this_year(),
from_admin=True,
reviewed=True,
post=Post.query.get(random.randint(1, Post.query.count()))
)
db.session.add(comment)
db.session.commit()
# replies
for i in range(salt):
comment = Comment(
author=fake.name(),
email=fake.email(),
site=fake.url(),
body=fake.sentence(),
timestamp=fake.date_time_this_year(),
reviewed=True,
replied=Comment.query.get(random.randint(1, Comment.query.count())),
post=Post.query.get(random.randint(1, Post.query.count()))
)
db.session.add(comment)
db.session.commit()
def fake_links():
twitter = Link(name='Twitter', url='#')
facebook = Link(name='Facebook', url='#')
linkedin = Link(name='LinkedIn', url='#')
google = Link(name='Google+', url='#')
db.session.add_all([twitter, facebook, linkedin, google])
db.session.commit()

+ 77
- 0
bluelog/forms.py Näytä tiedosto

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from flask_ckeditor import CKEditorField
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField, TextAreaField, ValidationError, HiddenField, \
BooleanField, PasswordField
from wtforms.validators import DataRequired, Email, Length, Optional, URL
from bluelog.models import Category
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
password = PasswordField('Password', validators=[DataRequired(), Length(1, 128)])
remember = BooleanField('Remember me')
submit = SubmitField('Log in')
class RegisterForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
password = PasswordField('Password', validators=[DataRequired(), Length(1, 128)])
remember = BooleanField('Remember me')
submit = SubmitField('Register')
class SettingForm(FlaskForm):
name = StringField('Name', validators=[DataRequired(), Length(1, 30)])
blog_title = StringField('Blog Title', validators=[DataRequired(), Length(1, 60)])
blog_sub_title = StringField('Blog Sub Title', validators=[DataRequired(), Length(1, 100)])
about = CKEditorField('About Page', validators=[DataRequired()])
submit = SubmitField()
class PostForm(FlaskForm):
title = StringField('Title', validators=[DataRequired(), Length(1, 60)])
category = SelectField('Category', coerce=int, default=1)
body = CKEditorField('Body', validators=[DataRequired()])
submit = SubmitField()
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
self.category.choices = [(category.id, category.name)
for category in Category.query.order_by(Category.name).all()]
class CategoryForm(FlaskForm):
name = StringField('Name', validators=[DataRequired(), Length(1, 30)])
submit = SubmitField()
def validate_name(self, field):
if Category.query.filter_by(name=field.data).first():
raise ValidationError('Name already in use.')
class CommentForm(FlaskForm):
author = StringField('Name', validators=[DataRequired(), Length(1, 30)])
email = StringField('Email', validators=[DataRequired(), Email(), Length(1, 254)])
site = StringField('Site', validators=[Optional(), URL(), Length(0, 255)])
body = TextAreaField('Comment', validators=[DataRequired()])
submit = SubmitField()
class AdminCommentForm(CommentForm):
author = HiddenField()
email = HiddenField()
site = HiddenField()
class LinkForm(FlaskForm):
name = StringField('Name', validators=[DataRequired(), Length(1, 30)])
url = StringField('URL', validators=[DataRequired(), URL(), Length(1, 255)])
submit = SubmitField()

+ 84
- 0
bluelog/models.py Näytä tiedosto

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
from datetime import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from bluelog.extensions import db
class Admin(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True)
password_hash = db.Column(db.String(128))
blog_title = db.Column(db.String(60))
blog_sub_title = db.Column(db.String(100))
name = db.Column(db.String(30))
about = db.Column(db.Text)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def validate_password(self, password):
return check_password_hash(self.password_hash, password)
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), unique=True)
posts = db.relationship('Post', back_populates='category')
def delete(self):
default_category = Category.query.get(1)
posts = self.posts[:]
for post in posts:
post.category = default_category
db.session.delete(self)
db.session.commit()
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(60))
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
can_comment = db.Column(db.Boolean, default=True)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category', back_populates='posts')
comments = db.relationship('Comment', back_populates='post', cascade='all, delete-orphan')
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
author = db.Column(db.String(30))
email = db.Column(db.String(254))
site = db.Column(db.String(255))
body = db.Column(db.Text)
from_admin = db.Column(db.Boolean, default=False)
reviewed = db.Column(db.Boolean, default=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
replied_id = db.Column(db.Integer, db.ForeignKey('comment.id'))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
post = db.relationship('Post', back_populates='comments')
replies = db.relationship('Comment', back_populates='replied', cascade='all, delete-orphan')
replied = db.relationship('Comment', back_populates='replies', remote_side=[id])
# Same with:
# replies = db.relationship('Comment', backref=db.backref('replied', remote_side=[id]),
# cascade='all,delete-orphan')
class Link(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
url = db.Column(db.String(255))

+ 74
- 0
bluelog/settings.py Näytä tiedosto

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
"""
:author: Grey Li ()
:url: http://greyli.com
:copyright: © 2018 Grey Li <withlihui@gmail.com>
:license: MIT, see LICENSE for more details.
"""
import os
import sys
basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
#等会记得改回去
myhost="localhost" #数据库内网地址
myroot="root"
mypwd="hsy19991231"
mydb="bluelog"
# SQLite URI compatible
WIN = sys.platform.startswith('win')
if WIN:
prefix = 'sqlite:///'
else:
prefix = 'sqlite:////'
class BaseConfig(object):
SECRET_KEY = os.getenv('SECRET_KEY', 'dev key')
DEBUG_TB_INTERCEPT_REDIRECTS = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_RECORD_QUERIES = True
CKEDITOR_ENABLE_CSRF = True
CKEDITOR_FILE_UPLOADER = 'admin.upload_image'
MAIL_SERVER = os.getenv('MAIL_SERVER')
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = os.getenv('MAIL_USERNAME')
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = ('Bluelog Admin', MAIL_USERNAME)
BLUELOG_EMAIL = os.getenv('BLUELOG_EMAIL')
BLUELOG_POST_PER_PAGE = 10
BLUELOG_MANAGE_POST_PER_PAGE = 15
BLUELOG_COMMENT_PER_PAGE = 15
# ('theme name', 'display name')
BLUELOG_THEMES = {'perfect_blue': 'Perfect Blue', 'black_swan': 'Black Swan'}
BLUELOG_SLOW_QUERY_THRESHOLD = 1
BLUELOG_UPLOAD_PATH = os.path.join(basedir, 'uploads')
BLUELOG_ALLOWED_IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif']
class DevelopmentConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://"+myroot+":"+mypwd+"@"+myhost+"/"+mydb #连接mysql数据库
class TestingConfig(BaseConfig):
TESTING = True
WTF_CSRF_ENABLED = False
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://"+myroot+":"+mypwd+"@"+myhost+"/"+mydb # in-memory database
class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://"+myroot+":"+mypwd+"@"+myhost+"/"+mydb
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig
}

+ 1483
- 0
bluelog/static/ckeditor/CHANGES.md
File diff suppressed because it is too large
Näytä tiedosto


+ 1420
- 0
bluelog/static/ckeditor/LICENSE.md
File diff suppressed because it is too large
Näytä tiedosto


+ 39
- 0
bluelog/static/ckeditor/README.md Näytä tiedosto

@ -0,0 +1,39 @@
CKEditor 4
==========
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
http://ckeditor.com - See LICENSE.md for license information.
CKEditor is a text editor to be used inside web pages. It's not a replacement
for desktop text editors like Word or OpenOffice, but a component to be used as
part of web applications and websites.
## Documentation
The full editor documentation is available online at the following address:
http://docs.ckeditor.com
## Installation
Installing CKEditor is an easy task. Just follow these simple steps:
1. **Download** the latest version from the CKEditor website:
http://ckeditor.com. You should have already completed this step, but be
sure you have the very latest version.
2. **Extract** (decompress) the downloaded file into the root of your website.
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
place the files in whichever you want though.
## Checking Your Installation
The editor comes with a few sample pages that can be used to verify that
installation proceeded properly. Take a look at the `samples` directory.
To test your installation, just call the following page at your website:
http://<your site>/<CKEditor installation path>/samples/index.html
For example:
http://www.example.com/ckeditor/samples/index.html

+ 10
- 0
bluelog/static/ckeditor/adapters/jquery.js Näytä tiedosto

@ -0,0 +1,10 @@
/*
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
(function(a){if("undefined"==typeof a)throw Error("jQuery should be loaded before CKEditor jQuery adapter.");if("undefined"==typeof CKEDITOR)throw Error("CKEditor should be loaded before CKEditor jQuery adapter.");CKEDITOR.config.jqueryOverrideVal="undefined"==typeof CKEDITOR.config.jqueryOverrideVal?!0:CKEDITOR.config.jqueryOverrideVal;a.extend(a.fn,{ckeditorGet:function(){var a=this.eq(0).data("ckeditorInstance");if(!a)throw"CKEditor is not initialized yet, use ckeditor() with a callback.";return a},
ckeditor:function(g,d){if(!CKEDITOR.env.isCompatible)throw Error("The environment is incompatible.");if(!a.isFunction(g)){var m=d;d=g;g=m}var k=[];d=d||{};this.each(function(){var b=a(this),c=b.data("ckeditorInstance"),f=b.data("_ckeditorInstanceLock"),h=this,l=new a.Deferred;k.push(l.promise());if(c&&!f)g&&g.apply(c,[this]),l.resolve();else if(f)c.once("instanceReady",function(){setTimeout(function(){c.element?(c.element.$==h&&g&&g.apply(c,[h]),l.resolve()):setTimeout(arguments.callee,100)},0)},
null,null,9999);else{if(d.autoUpdateElement||"undefined"==typeof d.autoUpdateElement&&CKEDITOR.config.autoUpdateElement)d.autoUpdateElementJquery=!0;d.autoUpdateElement=!1;b.data("_ckeditorInstanceLock",!0);c=a(this).is("textarea")?CKEDITOR.replace(h,d):CKEDITOR.inline(h,d);b.data("ckeditorInstance",c);c.on("instanceReady",function(d){var e=d.editor;setTimeout(function(){if(e.element){d.removeListener();e.on("dataReady",function(){b.trigger("dataReady.ckeditor",[e])});e.on("setData",function(a){b.trigger("setData.ckeditor",
[e,a.data])});e.on("getData",function(a){b.trigger("getData.ckeditor",[e,a.data])},999);e.on("destroy",function(){b.trigger("destroy.ckeditor",[e])});e.on("save",function(){a(h.form).submit();return!1},null,null,20);if(e.config.autoUpdateElementJquery&&b.is("textarea")&&a(h.form).length){var c=function(){b.ckeditor(function(){e.updateElement()})};a(h.form).submit(c);a(h.form).bind("form-pre-serialize",c);b.bind("destroy.ckeditor",function(){a(h.form).unbind("submit",c);a(h.form).unbind("form-pre-serialize",
c)})}e.on("destroy",function(){b.removeData("ckeditorInstance")});b.removeData("_ckeditorInstanceLock");b.trigger("instanceReady.ckeditor",[e]);g&&g.apply(e,[h]);l.resolve()}else setTimeout(arguments.callee,100)},0)},null,null,9999)}});var f=new a.Deferred;this.promise=f.promise();a.when.apply(this,k).then(function(){f.resolve()});this.editor=this.eq(0).data("ckeditorInstance");return this}});CKEDITOR.config.jqueryOverrideVal&&(a.fn.val=CKEDITOR.tools.override(a.fn.val,function(g){return function(d){if(arguments.length){var m=
this,k=[],f=this.each(function(){var b=a(this),c=b.data("ckeditorInstance");if(b.is("textarea")&&c){var f=new a.Deferred;c.setData(d,function(){f.resolve()});k.push(f.promise());return!0}return g.call(b,d)});if(k.length){var b=new a.Deferred;a.when.apply(this,k).done(function(){b.resolveWith(m)});return b.promise()}return f}var f=a(this).eq(0),c=f.data("ckeditorInstance");return f.is("textarea")&&c?c.getData():g.call(f)}}))})(window.jQuery);

+ 165
- 0
bluelog/static/ckeditor/build-config.js Näytä tiedosto

@ -0,0 +1,165 @@
/**
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* This file was added automatically by CKEditor builder.
* You may re-use it at any time to build CKEditor again.
*
* If you would like to build CKEditor online again
* (for example to upgrade), visit one the following links:
*
* (1) http://ckeditor.com/builder
* Visit online builder to build CKEditor from scratch.
*
* (2) http://ckeditor.com/builder/2f57fc244039d7273aeb5bb45ddb4866
* Visit online builder to build CKEditor, starting with the same setup as before.
*
* (3) http://ckeditor.com/builder/download/2f57fc244039d7273aeb5bb45ddb4866
* Straight download link to the latest version of CKEditor (Optimized) with the same setup as before.
*
* NOTE:
* This file is not used by CKEditor, you may remove it.
* Changing this file will not change your CKEditor configuration.
*/
var CKBUILDER_CONFIG = {
skin: 'moono-lisa',
preset: 'standard',
ignore: [
'.DS_Store',
'.bender',
'.editorconfig',
'.gitattributes',
'.gitignore',
'.idea',
'.jscsrc',
'.jshintignore',
'.jshintrc',
'.mailmap',
'.travis.yml',
'bender-err.log',
'bender-out.log',
'bender.ci.js',
'bender.js',
'dev',
'gruntfile.js',
'less',
'node_modules',
'package.json',
'tests'
],
plugins : {
'a11yhelp' : 1,
'about' : 1,
'basicstyles' : 1,
'blockquote' : 1,
'clipboard' : 1,
'contextmenu' : 1,
'elementspath' : 1,
'enterkey' : 1,
'entities' : 1,
'filebrowser' : 1,
'floatingspace' : 1,
'format' : 1,
'horizontalrule' : 1,
'htmlwriter' : 1,
'image' : 1,
'indentlist' : 1,
'link' : 1,
'list' : 1,
'magicline' : 1,
'maximize' : 1,
'pastefromword' : 1,
'pastetext' : 1,
'removeformat' : 1,
'resize' : 1,
'scayt' : 1,
'showborders' : 1,
'sourcearea' : 1,
'specialchar' : 1,
'stylescombo' : 1,
'tab' : 1,
'table' : 1,
'tableselection' : 1,
'tabletools' : 1,
'toolbar' : 1,
'undo' : 1,
'uploadimage' : 1,
'wsc' : 1,
'wysiwygarea' : 1
},
languages : {
'af' : 1,
'ar' : 1,
'az' : 1,
'bg' : 1,
'bn' : 1,
'bs' : 1,
'ca' : 1,
'cs' : 1,
'cy' : 1,
'da' : 1,
'de' : 1,
'de-ch' : 1,
'el' : 1,
'en' : 1,
'en-au' : 1,
'en-ca' : 1,
'en-gb' : 1,
'eo' : 1,
'es' : 1,
'es-mx' : 1,
'et' : 1,
'eu' : 1,
'fa' : 1,
'fi' : 1,
'fo' : 1,
'fr' : 1,
'fr-ca' : 1,
'gl' : 1,
'gu' : 1,
'he' : 1,
'hi' : 1,
'hr' : 1,
'hu' : 1,
'id' : 1,
'is' : 1,
'it' : 1,
'ja' : 1,
'ka' : 1,
'km' : 1,
'ko' : 1,
'ku' : 1,
'lt' : 1,
'lv' : 1,
'mk' : 1,
'mn' : 1,
'ms' : 1,
'nb' : 1,
'nl' : 1,
'no' : 1,
'oc' : 1,
'pl' : 1,
'pt' : 1,
'pt-br' : 1,
'ro' : 1,
'ru' : 1,
'si' : 1,
'sk' : 1,
'sl' : 1,
'sq' : 1,
'sr' : 1,
'sr-latn' : 1,
'sv' : 1,
'th' : 1,
'tr' : 1,
'tt' : 1,
'ug' : 1,
'uk' : 1,
'vi' : 1,
'zh' : 1,
'zh-cn' : 1
}
};

+ 1213
- 0
bluelog/static/ckeditor/ckeditor.js
File diff suppressed because it is too large
Näytä tiedosto


+ 38
- 0
bluelog/static/ckeditor/config.js Näytä tiedosto

@ -0,0 +1,38 @@
/**
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.editorConfig = function( config ) {
// Define changes to default configuration here.
// For complete reference see:
// http://docs.ckeditor.com/#!/api/CKEDITOR.config
// The toolbar groups arrangement, optimized for two toolbar rows.
config.toolbarGroups = [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
{ name: 'forms' },
{ name: 'tools' },
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
{ name: 'others' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' },
{ name: 'about' }
];
// Remove some buttons provided by the standard plugins, which are
// not needed in the Standard(s) toolbar.
config.removeButtons = 'Underline,Subscript,Superscript';
// Set the most common block elements.
config.format_tags = 'p;h1;h2;h3;pre';
// Simplify the dialog windows.
config.removeDialogTabs = 'image:advanced;link:advanced';
};

+ 207
- 0
bluelog/static/ckeditor/contents.css Näytä tiedosto

@ -0,0 +1,207 @@
/*
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
body
{
/* Font */
font-family: sans-serif, Arial, Verdana, "Trebuchet MS";
font-size: 12px;
/* Text color */
color: #333;
/* Remove the background color to make it transparent */
background-color: #fff;
margin: 20px;
}
.cke_editable
{
font-size: 13px;
line-height: 1.6;
/* Fix for missing scrollbars with RTL texts. (#10488) */
word-wrap: break-word;
}
blockquote
{
font-style: italic;
font-family: Georgia, Times, "Times New Roman", serif;
padding: 2px 0;
border-style: solid;
border-color: #ccc;
border-width: 0;
}
.cke_contents_ltr blockquote
{
padding-left: 20px;
padding-right: 8px;
border-left-width: 5px;
}
.cke_contents_rtl blockquote
{
padding-left: 8px;
padding-right: 20px;
border-right-width: 5px;
}
a
{
color: #0782C1;
}
ol,ul,dl
{
/* IE7: reset rtl list margin. (#7334) */
*margin-right: 0px;
/* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/
padding: 0 40px;
}
h1,h2,h3,h4,h5,h6
{
font-weight: normal;
line-height: 1.2;
}
hr
{
border: 0px;
border-top: 1px solid #ccc;
}
img.right
{
border: 1px solid #ccc;
float: right;
margin-left: 15px;
padding: 5px;
}
img.left
{
border: 1px solid #ccc;
float: left;
margin-right: 15px;
padding: 5px;
}
pre
{
white-space: pre-wrap; /* CSS 2.1 */
word-wrap: break-word; /* IE7 */
-moz-tab-size: 4;
tab-size: 4;
}
.marker
{
background-color: Yellow;
}
span[lang]
{
font-style: italic;
}
figure
{
text-align: center;
outline: solid 1px #ccc;
background: rgba(0,0,0,0.05);
padding: 10px;
margin: 10px 20px;
display: inline-block;
}
figure > figcaption
{
text-align: center;
display: block; /* For IE8 */
}
a > img {
padding: 1px;
margin: 1px;
border: none;
outline: 1px solid #0782C1;
}
/* Widget Styles */
.code-featured
{
border: 5px solid red;
}
.math-featured
{
padding: 20px;
box-shadow: 0 0 2px rgba(200, 0, 0, 1);
background-color: rgba(255, 0, 0, 0.05);
margin: 10px;
}
.image-clean
{
border: 0;
background: none;
padding: 0;
}
.image-clean > figcaption
{
font-size: .9em;
text-align: right;
}
.image-grayscale
{
background-color: white;
color: #666;
}
.image-grayscale img, img.image-grayscale
{
filter: grayscale(100%);
}
.embed-240p
{
max-width: 426px;
max-height: 240px;
margin:0 auto;
}
.embed-360p
{
max-width: 640px;
max-height: 360px;
margin:0 auto;
}
.embed-480p
{
max-width: 854px;
max-height: 480px;
margin:0 auto;
}
.embed-720p
{
max-width: 1280px;
max-height: 720px;
margin:0 auto;
}
.embed-1080p
{
max-width: 1920px;
max-height: 1080px;
margin:0 auto;
}

+ 5
- 0
bluelog/static/ckeditor/lang/af.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ar.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/az.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/bg.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/bn.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/bs.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ca.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/cs.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/cy.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/da.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/de-ch.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/de.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/el.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/en-au.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/en-ca.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/en-gb.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/en.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/eo.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/es-mx.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/es.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/et.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/eu.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/fa.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/fi.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/fo.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/fr-ca.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/fr.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/gl.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/gu.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/he.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/hi.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/hr.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/hu.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/id.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/is.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/it.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ja.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ka.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/km.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ko.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ku.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/lt.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/lv.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/mk.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/mn.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ms.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/nb.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/nl.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/no.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/oc.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/pl.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/pt-br.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/pt.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ro.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ru.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/si.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sk.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sl.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sq.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sr-latn.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sr.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/sv.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/th.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/tr.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/tt.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/ug.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/uk.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/vi.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/zh-cn.js
File diff suppressed because it is too large
Näytä tiedosto


+ 5
- 0
bluelog/static/ckeditor/lang/zh.js
File diff suppressed because it is too large
Näytä tiedosto


+ 10
- 0
bluelog/static/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js Näytä tiedosto

@ -0,0 +1,10 @@
/*
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.dialog.add("a11yHelp",function(e){var a=e.lang.a11yhelp,b=e.lang.common.keyboard,q=CKEDITOR.tools.getNextId(),d={8:b[8],9:a.tab,13:b[13],16:b[16],17:b[17],18:b[18],19:a.pause,20:a.capslock,27:a.escape,33:a.pageUp,34:a.pageDown,35:b[35],36:b[36],37:a.leftArrow,38:a.upArrow,39:a.rightArrow,40:a.downArrow,45:a.insert,46:b[46],91:a.leftWindowKey,92:a.rightWindowKey,93:a.selectKey,96:a.numpad0,97:a.numpad1,98:a.numpad2,99:a.numpad3,100:a.numpad4,101:a.numpad5,102:a.numpad6,103:a.numpad7,104:a.numpad8,
105:a.numpad9,106:a.multiply,107:a.add,109:a.subtract,110:a.decimalPoint,111:a.divide,112:a.f1,113:a.f2,114:a.f3,115:a.f4,116:a.f5,117:a.f6,118:a.f7,119:a.f8,120:a.f9,121:a.f10,122:a.f11,123:a.f12,144:a.numLock,145:a.scrollLock,186:a.semiColon,187:a.equalSign,188:a.comma,189:a.dash,190:a.period,191:a.forwardSlash,192:a.graveAccent,219:a.openBracket,220:a.backSlash,221:a.closeBracket,222:a.singleQuote};d[CKEDITOR.ALT]=b[18];d[CKEDITOR.SHIFT]=b[16];d[CKEDITOR.CTRL]=CKEDITOR.env.mac?b[224]:b[17];var k=
[CKEDITOR.ALT,CKEDITOR.SHIFT,CKEDITOR.CTRL],r=/\$\{(.*?)\}/g,t=function(a,b){var c=e.getCommandKeystroke(b);if(c){for(var l,f,h=[],g=0;g<k.length;g++)f=k[g],l=c/k[g],1<l&&2>=l&&(c-=f,h.push(d[f]));h.push(d[c]||String.fromCharCode(c));c=h.join("+")}else c=a;return c};return{title:a.title,minWidth:600,minHeight:400,contents:[{id:"info",label:e.lang.common.generalTab,expand:!0,elements:[{type:"html",id:"legends",style:"white-space:normal;",focus:function(){this.getElement().focus()},html:function(){for(var b=
'\x3cdiv class\x3d"cke_accessibility_legend" role\x3d"document" aria-labelledby\x3d"'+q+'_arialbl" tabIndex\x3d"-1"\x3e%1\x3c/div\x3e\x3cspan id\x3d"'+q+'_arialbl" class\x3d"cke_voice_label"\x3e'+a.contents+" \x3c/span\x3e",d=[],c=a.legend,l=c.length,f=0;f<l;f++){for(var h=c[f],g=[],e=h.items,k=e.length,p=0;p<k;p++){var m=e[p],n=CKEDITOR.env.edge&&m.legendEdge?m.legendEdge:m.legend,n=n.replace(r,t);n.match(r)||g.push("\x3cdt\x3e%1\x3c/dt\x3e\x3cdd\x3e%2\x3c/dd\x3e".replace("%1",m.name).replace("%2",
n))}d.push("\x3ch1\x3e%1\x3c/h1\x3e\x3cdl\x3e%2\x3c/dl\x3e".replace("%1",h.name).replace("%2",g.join("")))}return b.replace("%1",d.join(""))}()+'\x3cstyle type\x3d"text/css"\x3e.cke_accessibility_legend{width:600px;height:400px;padding-right:5px;overflow-y:auto;overflow-x:hidden;}.cke_browser_quirks .cke_accessibility_legend,{height:390px}.cke_accessibility_legend *{white-space:normal;}.cke_accessibility_legend h1{font-size: 20px;border-bottom: 1px solid #AAA;margin: 5px 0px 15px;}.cke_accessibility_legend dl{margin-left: 5px;}.cke_accessibility_legend dt{font-size: 13px;font-weight: bold;}.cke_accessibility_legend dd{margin:10px}\x3c/style\x3e'}]}],
buttons:[CKEDITOR.dialog.cancelButton]}});

Some files were not shown because too many files changed in this diff

Ladataan…
Peruuta
Tallenna