Merge lp:~widelands-dev/widelands-website/solitaire_html_documentation into lp:widelands-website

Proposed by kaputtnik on 2018-05-12
Status: Merged
Merged at revision: 496
Proposed branch: lp:~widelands-dev/widelands-website/solitaire_html_documentation
Merge into: lp:widelands-website
Diff against target: 2015 lines (+1308/-502)
24 files modified
README.txt (+9/-20)
documentation/conf.py (+171/-0)
documentation/management/commands/create_docs.py (+134/-0)
documentation/static/basic.css (+645/-0)
documentation/static/pygments.css (+78/-0)
documentation/static/widelands.css (+258/-0)
local_settings.py.sample (+1/-0)
local_urls.py.sample (+7/-0)
media/css/documentation.css (+0/-118)
settings.py (+1/-1)
sitemap_urls.py (+0/-2)
sphinxdoc/admin.py (+0/-14)
sphinxdoc/migrations/0001_initial.py (+0/-24)
sphinxdoc/models.py (+0/-23)
sphinxdoc/sitemap.py (+0/-26)
sphinxdoc/urls.py (+0/-28)
sphinxdoc/views.py (+0/-89)
templates/navigation.html (+1/-1)
templates/sphinxdoc/app_list.html (+0/-18)
templates/sphinxdoc/base.html (+0/-17)
templates/sphinxdoc/documentation.html (+0/-57)
templates/sphinxdoc/genindex.html (+0/-38)
templates/sphinxdoc/py-modindex.html (+0/-23)
urls.py (+3/-3)
To merge this branch: bzr merge lp:~widelands-dev/widelands-website/solitaire_html_documentation
Reviewer Review Type Date Requested Status
Widelands Developers 2018-05-12 Pending
Review via email: mp+345460@code.launchpad.net

Commit message

Serve sphinx documentation as html files; Removes sphinx-doc

Description of the change

Serve sphinx documentation as html files. This remove unmaintained sphinx-doc. But all features of the produced html could be used (e.g searching).

The main idea is to let sphinx build the html files directly in the media folder of the website and link it from the navigation. So no extra entries for nginx are needed.

To ease up the creation step of the documentation, a management command is implemented which uses an own conf.py for sphinx. To create the documentation one can just write:

./manage.py create_docs

To have a similar look and feel in the created html files, i have added also the needed css and background images to this branch and let sphinx use them, so the main source code for widelands isn't tainted. An example image: https://bugs.launchpad.net/widelands-website/+bug/893275/+attachment/5046332/+files/solitaire_documentation.jpg

The management command let sphinx create the doctrees in the WIDELANDS_SVN_DIR/doc/sphinx/build/doctrees/ but puts the produced html in the folder in MEDIA/documentation/'. On unix like systems the produced html are served from a symlink called 'html'. This makes it possible to have consistent links during creation of new a documentation:

1. sphinx produces html files in a folder called 'html_temp'
2. switch the link to 'html_temp'
3. remove folder 'current' (if exist)
4. copy 'html_temp' to 'current'
5. switch the link to 'current'
6. remove 'html_temp'

On windows systems (if there are ever some users creating the website in an windows environment...) the step to create the symlink is omitted and 'html_temp' is moved to 'html'

We can do similar with the doxygen created documentation.

If you think this approach is good to go, i will make it work on the alpha site for testing. Since heavy file action is involved (moving, deleting of folders), please read the code of documentation/management/commands/create_docs.py very carefully :-)

To post a comment you must log in.
505. By kaputtnik on 2018-05-12

cleanups

GunChleoc (gunchleoc) wrote :

That screenshot looks so good - finally, a table of contents!

Just 1 tiny nit for a comment.

506. By kaputtnik on 2018-05-13

adressed code review; found an unneeded call to os.path.join

507. By kaputtnik on 2018-05-13

merged trunk

kaputtnik (franku) wrote :

Ok, my approach will not work on the server. After some fiddling i will continue the work for this next week.

Just if one want's to play around with the alpha site: Be aware that it is currently in a WIP-state.

kaputtnik (franku) wrote :

The alpha site is functional now and running. So one can test the look and feel of the documentation.

The link shown in the users addressbar isn't that good now. May i have to adjust nginx for that. Also there is no favicon shown.

In regard to bug 1758515 it has to be modified again.

kaputtnik (franku) wrote :

The link shown in user browsers addressbar is also fixed now. I fixed it in the same manner as the links for 'media' and 'wlmedia'. Added a location block to the nginx file and made a symlink to 'wlmedia/documentation/html'

I would be glad if some one can have a look at the documentation.

In the meantime i will adjust the code at home accordingly.

kaputtnik (franku) wrote :

Ah, forgotton: I have sometimes trouble to access the alpha site, because my browser opens wl.widelands.org instead. I have to clean the browsers cache to get into alpha...

508. By kaputtnik on 2018-05-15

adjusted code to make it run on the server

509. By kaputtnik on 2018-05-22

give the sidebars text a really dark green

GunChleoc (gunchleoc) wrote :

The really dark green works for me. Ship it! :)

kaputtnik (franku) wrote :

Hmmm... currently i didn't get the cron job /etc/cron.hourly/update_widelands_bzr to work... Commenting all other rows (except the 'bzr pull' command) and applying the following do not work for some reason:

# Some magic from: http://stackoverflow.com/a/29969243/200945. If we are
# run as root (which we are from cron), we rerun the script under the user we
# actually want to execute all commands as
if [ $UID -eq 0 ]; then
  exec sudo -u www-data "$0" "$@"
fi

cd /var/www/django_projects/wlwebsite/
source bin/activate
cd code/widelands

/var/www/django_projects/wlwebsite/bin/python manage.py create_docs

510. By kaputtnik on 2018-05-24

make local url work again

511. By kaputtnik on 2018-05-24

add a redirect to fix old links

kaputtnik (franku) wrote :

Just found that a redirect from docs/wl/ to documentation was missing.

Will give it another try to get it into production next weekend (when i am not that tired from work ;) )

GunChleoc (gunchleoc) wrote :

Glad you found it :)

kaputtnik (franku) wrote :

Ok, got it. The problem was that merging this branch the folder media/documentation/ was owned by me, so the script (which runs as user www-data) could not write into it.

Fixed also another small issue.

Merged and deployed.

The django management command to update the documentation is now executed daily. Former it was executed hourly. Is this ok?
If we want to update the documentation hourly, i have to investigate if the permissions allow it.

Hopefully SirVer did not get too much errors due to my changes in the cron jobs...

GunChleoc (gunchleoc) wrote :

Daily is good enough :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.txt'
2--- README.txt 2017-04-11 05:30:40 +0000
3+++ README.txt 2018-05-24 19:17:56 +0000
4@@ -150,25 +150,14 @@
5 Setting up widelands source code documentation
6 ----------------------------------------------
7
8-Change to the directory doc/sphinx/ of the widelands source code and run
9-
10- $ ./extract_rst.py
11-
12-Now create the documentation as json files. You have to provide the path to
13-the sphinx-build executable of the website:
14-
15- $ ~/wlwebsite/bin/sphinx-build -b json -d build/doctrees source build/json
16-
17-A new directory containing json files is created in doc/sphinx/build/json. The full path
18-to this directory has to be set in the sphinxdoc-app of the website:
19-
20-Open http://localhost:8000/admin/sphinxdoc/app/ and add a new app with this values:
21-
22-Name: Widelands developer documentation
23-Slug: wl
24-Path: /your/path/to/widelands-src/doc/sphinx/build/json/
25-
26-Open http://localhost:8000/docs/wl/ to test if everything is working.
27+There is a small helper script to get the documenation. Be sure
28+you have set WIDELANDS_SVN_DIR set in local_settings.py. Run:
29+
30+ $ ./manage.py create_docs
31+
32+After finishing without errors, type localhost:8000/documentation/index.html
33+in your browsers addressbar or click on "Development -> Documentation".
34+
35
36 Uploading a map to the local website
37 ------------------------------------
38@@ -182,7 +171,7 @@
39 Contact
40 =======
41
42-Contact SirVer on the homepage for more information and problems.
43+Contact user 'kaputtnik' on the homepage for more information and problems.
44
45
46 -- vim:ft=rst:
47
48=== added directory 'documentation'
49=== added file 'documentation/__init__.py'
50=== added file 'documentation/conf.py'
51--- documentation/conf.py 1970-01-01 00:00:00 +0000
52+++ documentation/conf.py 2018-05-24 19:17:56 +0000
53@@ -0,0 +1,171 @@
54+# -*- coding: utf-8 -*-
55+#
56+# Widelands documentation build configuration file, created by
57+# sphinx-quickstart on Fri Jun 01 11:27:52 2012.
58+#
59+# This file is execfile()d with the current directory set to its containing dir.
60+#
61+# Note that not all possible configuration values are present in this
62+# autogenerated file.
63+#
64+# All configuration values have a default; values that are commented out
65+# serve to show the default.
66+# For a complete list of options see: http://www.sphinx-doc.org/en/stable/config.html
67+
68+# import sys, os
69+
70+# If extensions (or modules to document with autodoc) are in another directory,
71+# add these directories to sys.path here. If the directory is relative to the
72+# documentation root, use os.path.abspath to make it absolute, like shown here.
73+#sys.path.insert(0, os.path.abspath('.'))
74+
75+# -- General configuration -----------------------------------------------------
76+
77+# If your documentation needs a minimal Sphinx version, state it here.
78+#needs_sphinx = '1.0'
79+
80+# Add any Sphinx extension module names here, as strings. They can be extensions
81+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
82+extensions = ['sphinx.ext.mathjax']
83+
84+# Add any paths that contain templates here, relative to this directory.
85+templates_path = ['_templates']
86+
87+# The suffix of source filenames.
88+source_suffix = '.rst'
89+
90+# The encoding of source files.
91+#source_encoding = 'utf-8-sig'
92+
93+# The master toctree document.
94+master_doc = 'index'
95+
96+# General information about the project.
97+project = u'Widelands'
98+copyright = u'The Widelands Development Team'
99+
100+# The version info for the project you're documenting, acts as replacement for
101+# |version| and |release|, also used in various other places throughout the
102+# built documents.
103+#
104+# The short X.Y version.
105+version = 'trunk'
106+# The full version, including alpha/beta/rc tags.
107+release = 'trunk'
108+
109+# The language for content autogenerated by Sphinx. Refer to documentation
110+# for a list of supported languages.
111+#language = None
112+
113+# There are two options for replacing |today|: either, you set today to some
114+# non-false value, then it is used:
115+#today = ''
116+# Else, today_fmt is used as the format for a strftime call.
117+#today_fmt = '%B %d, %Y'
118+
119+# List of patterns, relative to source directory, that match files and
120+# directories to ignore when looking for source files.
121+exclude_patterns = []
122+
123+# The reST default role (used for this markup: `text`) to use for all documents.
124+#default_role = None
125+
126+# If true, '()' will be appended to :func: etc. cross-reference text.
127+#add_function_parentheses = True
128+
129+# If true, the current module name will be prepended to all description
130+# unit titles (such as .. function::).
131+#add_module_names = True
132+
133+# If true, sectionauthor and moduleauthor directives will be shown in the
134+# output. They are ignored by default.
135+#show_authors = False
136+
137+# The name of the Pygments (syntax highlighting) style to use.
138+pygments_style = 'sphinx'
139+
140+# A list of ignored prefixes for module index sorting.
141+#modindex_common_prefix = []
142+
143+highlight_language = 'lua'
144+
145+
146+# -- Options for HTML output ---------------------------------------------------
147+
148+# The theme to use for HTML and HTML Help pages. See the documentation for
149+# a list of builtin themes.
150+html_theme = 'basic'
151+
152+# Theme options are theme-specific and customize the look and feel of a theme
153+# further. For a list of options available for each theme, see the
154+# documentation.
155+html_theme_options = {}
156+
157+# Add any paths that contain custom themes here, relative to this directory.
158+#html_theme_path = []
159+
160+# The name for this set of Sphinx documents. If None, it defaults to
161+# "<project> v<release> documentation".
162+#html_title = None
163+
164+# A shorter title for the navigation bar. Default is the same as html_title.
165+#html_short_title = None
166+
167+# The name of an image file (relative to this directory) to place at the top
168+# of the sidebar.
169+#html_logo = None
170+
171+# The name of an image file (within the static path) to use as favicon of the
172+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
173+# pixels large.
174+#html_favicon = None
175+
176+# Add any paths that contain custom static files (such as style sheets) here,
177+# relative to this directory. They are copied after the builtin static files,
178+# so a file named "default.css" will overwrite the builtin "default.css".
179+html_static_path = ['static/']
180+
181+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
182+# using the given strftime format.
183+html_last_updated_fmt = '%b %d, %Y'
184+
185+# If true, SmartyPants will be used to convert quotes and dashes to
186+# typographically correct entities.
187+#html_use_smartypants = True
188+
189+# Custom sidebar templates, maps document names to template names.
190+#html_sidebars = {}
191+
192+# Additional templates that should be rendered to pages, maps page names to
193+# template names.
194+#html_additional_pages = {}
195+
196+# If false, no module index is generated.
197+html_domain_indices = False
198+
199+# If false, no index is generated.
200+html_use_index = True
201+
202+# If true, the index is split into individual pages for each letter.
203+#html_split_index = False
204+
205+# If true, links to the reST sources are added to the pages.
206+#html_show_sourcelink = True
207+
208+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
209+#html_show_sphinx = True
210+
211+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
212+#html_show_copyright = True
213+
214+# If true, an OpenSearch description file will be output, and all pages will
215+# contain a <link> tag referring to it. The value of this option must be the
216+# base URL from which the finished HTML is served.
217+#html_use_opensearch = ''
218+
219+# This is the file name suffix for HTML files (e.g. ".xhtml").
220+#html_file_suffix = None
221+
222+
223+# Output file base name for HTML help builder.
224+htmlhelp_basename = 'Widelandsdoc'
225
226=== added directory 'documentation/management'
227=== added file 'documentation/management/__init__.py'
228=== added directory 'documentation/management/commands'
229=== added file 'documentation/management/commands/__init__.py'
230=== added file 'documentation/management/commands/create_docs.py'
231--- documentation/management/commands/create_docs.py 1970-01-01 00:00:00 +0000
232+++ documentation/management/commands/create_docs.py 2018-05-24 19:17:56 +0000
233@@ -0,0 +1,134 @@
234+#!/usr/bin/env python
235+# -*- coding: utf-8 -*-
236+
237+"""Create the source code documenation.
238+
239+This script covers all needed steps to create or recreate the widelands
240+source code documentation.
241+
242+Needed dependency: Sphinx
243+
244+"""
245+
246+from __future__ import print_function
247+from django.core.management.base import BaseCommand, CommandError
248+from django.conf import settings
249+from subprocess import check_call, CalledProcessError
250+from documentation import conf
251+import os
252+import sys
253+import shutil
254+import glob
255+
256+
257+class Command(BaseCommand):
258+ help = 'Create the source code documenation.'
259+
260+ def __init__(self, *args, **kwargs):
261+ super(Command, self).__init__(*args, **kwargs)
262+
263+ # Define the main directories used in this class
264+ self.sphinx_dir = os.path.join(
265+ settings.WIDELANDS_SVN_DIR, 'doc/sphinx')
266+ self.build_dir = os.path.join(
267+ settings.MEDIA_ROOT, 'documentation/html_temp')
268+ self.sphinx_conf_dir = os.path.dirname(conf.__file__)
269+
270+ def move_docs(self):
271+ """Move the documentation created by sphinxdoc to the correct folder.
272+
273+ On unix systems the files were served from the symlink called
274+ 'html'. On Windows systems the files will only be copied in a
275+ folder called 'html'.
276+
277+ """
278+
279+ if os.name == 'posix':
280+ # Creating symlinks is only available on unix systems
281+ try:
282+ link_name = os.path.join(
283+ settings.MEDIA_ROOT, 'documentation/html')
284+ target_dir = os.path.join(
285+ settings.MEDIA_ROOT, 'documentation/current')
286+
287+ if not os.path.exists(target_dir):
288+ # only needed on first run
289+ os.mkdir(target_dir)
290+
291+ if os.path.exists(link_name):
292+ # only needed if this script has already run
293+ os.remove(link_name)
294+
295+ # Temporarily switch the symlink
296+ os.symlink(self.build_dir, link_name)
297+ # Remove current
298+ shutil.rmtree(target_dir)
299+ # Copy new build to current
300+ shutil.copytree(self.build_dir, target_dir)
301+ # Switch the link to current
302+ os.remove(link_name)
303+ os.symlink(target_dir, link_name)
304+ except:
305+ raise
306+ else:
307+ # Non unix OS: Copy docs
308+ try:
309+ target_dir = os.path.join(
310+ settings.MEDIA_ROOT, 'documentation/html')
311+ if os.path.exists(target_dir):
312+ shutil.rmtree(target_dir)
313+ shutil.copytree(self.build_dir, target_dir)
314+ except:
315+ raise
316+
317+ try:
318+ # The new build directory is no longer needed
319+ shutil.rmtree(self.build_dir)
320+ except:
321+ raise
322+
323+ def handle(self, *args, **options):
324+ """Create the widelands source code documentation.
325+
326+ The Documenatation is built by sphinxdoc in the directory
327+ 'settings/MEDIA/documentation/html_temp'.
328+
329+ """
330+
331+ if not os.path.exists(self.sphinx_dir):
332+ print(
333+ "Can't find the directory given by WIDELANDS_SVN_DIR in local_settings.py:\n", self.sphinx_dir)
334+ sys.exit(1)
335+
336+ if os.path.exists(os.path.join(self.sphinx_dir, 'build')):
337+ # Clean the autogen* files created by extract_rst.py
338+ # This has to be done because sometimes such a file remains after
339+ # removing it from extract_rst.
340+ try:
341+ for f in glob.glob(os.path.join(self.sphinx_dir, 'source/autogen*')):
342+ os.remove(f)
343+ except OSError:
344+ raise
345+
346+ # Locally 'dirhtml' do not work because the staticfiles view disallow
347+ # directory indexes, but 'dirhtml' gives nicer addresses in production
348+ builder = 'html'
349+ if not settings.DEBUG:
350+ # In production DEBUG is False
351+ builder = 'dirhtml'
352+
353+ try:
354+ check_call(['python', os.path.join(
355+ self.sphinx_dir, 'extract_rst.py')])
356+ check_call(['sphinx-build',
357+ '-b', builder,
358+ '-d', os.path.join(self.sphinx_dir, 'build/doctrees'),
359+ '-c', self.sphinx_conf_dir,
360+ os.path.join(self.sphinx_dir, 'source'),
361+ self.build_dir,
362+ ])
363+ except CalledProcessError as why:
364+ print('An error occured: {0}'.format(why))
365+ sys.exit(1)
366+
367+ self.move_docs()
368
369=== added directory 'documentation/static'
370=== added file 'documentation/static/basic.css'
371--- documentation/static/basic.css 1970-01-01 00:00:00 +0000
372+++ documentation/static/basic.css 2018-05-24 19:17:56 +0000
373@@ -0,0 +1,645 @@
374+/*
375+ * basic.css
376+ * ~~~~~~~~~
377+ *
378+ * Sphinx stylesheet -- basic theme.
379+ *
380+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
381+ * :license: BSD, see LICENSE for details.
382+ *
383+ */
384+
385+@import url("widelands.css");
386+
387+/* -- main layout ----------------------------------------------------------- */
388+
389+div.clearer {
390+ clear: both;
391+}
392+
393+/* -- relbar ---------------------------------------------------------------- */
394+
395+div.related {
396+ width: 100%;
397+ font-size: 90%;
398+}
399+
400+div.related h3 {
401+ display: none;
402+}
403+
404+div.related ul {
405+ margin: 0;
406+ padding: 0 0 0 10px;
407+ list-style: none;
408+}
409+
410+div.related li {
411+ display: inline;
412+}
413+
414+div.related li.right {
415+ float: right;
416+ margin-right: 5px;
417+}
418+
419+/* -- sidebar --------------------------------------------------------------- */
420+
421+div.sphinxsidebarwrapper {
422+ padding: 10px 5px 0 10px;
423+}
424+
425+div.sphinxsidebar {
426+ float: left;
427+ width: 230px;
428+ margin-left: -100%;
429+ font-size: 90%;
430+ word-wrap: break-word;
431+ overflow-wrap : break-word;
432+}
433+
434+div.sphinxsidebar ul {
435+ list-style: none;
436+}
437+
438+div.sphinxsidebar ul ul,
439+div.sphinxsidebar ul.want-points {
440+ margin-left: 20px;
441+ list-style: square;
442+}
443+
444+div.sphinxsidebar ul ul {
445+ margin-top: 0;
446+ margin-bottom: 0;
447+}
448+
449+div.sphinxsidebar form {
450+ margin-top: 10px;
451+}
452+
453+/*div.sphinxsidebar input {
454+ border: 1px solid #98dbcc;
455+ font-family: sans-serif;
456+ font-size: 1em;
457+}*/
458+
459+div.sphinxsidebar #searchbox input[type="text"] {
460+ width: 170px;
461+}
462+
463+img {
464+ border: 0;
465+ max-width: 100%;
466+}
467+
468+/* -- search page ----------------------------------------------------------- */
469+
470+ul.search {
471+ margin: 10px 0 0 20px;
472+ padding: 0;
473+}
474+
475+ul.search li {
476+ padding: 5px 0 5px 20px;
477+ background-image: url(file.png);
478+ background-repeat: no-repeat;
479+ background-position: 0 7px;
480+}
481+
482+ul.search li a {
483+ font-weight: bold;
484+}
485+
486+ul.search li div.context {
487+ color: #888;
488+ margin: 2px 0 0 30px;
489+ text-align: left;
490+}
491+
492+ul.keywordmatches li.goodmatch a {
493+ font-weight: bold;
494+}
495+
496+/* -- index page ------------------------------------------------------------ */
497+
498+table.contentstable {
499+ width: 90%;
500+ margin-left: auto;
501+ margin-right: auto;
502+}
503+
504+table.contentstable p.biglink {
505+ line-height: 150%;
506+}
507+
508+a.biglink {
509+ font-size: 1.3em;
510+}
511+
512+span.linkdescr {
513+ font-style: italic;
514+ padding-top: 5px;
515+ font-size: 90%;
516+}
517+
518+/* -- general index --------------------------------------------------------- */
519+
520+table.indextable {
521+ width: 100%;
522+}
523+
524+table.indextable td {
525+ text-align: left;
526+ vertical-align: top;
527+}
528+
529+table.indextable ul {
530+ margin-top: 0;
531+ margin-bottom: 0;
532+ list-style-type: none;
533+}
534+
535+table.indextable > tbody > tr > td > ul {
536+ padding-left: 0em;
537+}
538+
539+table.indextable tr.pcap {
540+ height: 10px;
541+}
542+/*
543+table.indextable tr.cap {
544+ margin-top: 10px;
545+ background-color: #f2f2f2;
546+}*/
547+
548+img.toggler {
549+ margin-right: 3px;
550+ margin-top: 3px;
551+ cursor: pointer;
552+}
553+
554+div.modindex-jumpbox {
555+ border-top: 1px solid #ddd;
556+ border-bottom: 1px solid #ddd;
557+ margin: 1em 0 1em 0;
558+ padding: 0.4em;
559+}
560+
561+div.genindex-jumpbox {
562+ border-top: 1px solid #ddd;
563+ border-bottom: 1px solid #ddd;
564+ margin: 1em 0 1em 0;
565+ padding: 0.4em;
566+}
567+
568+/* -- domain module index --------------------------------------------------- */
569+
570+table.modindextable td {
571+ padding: 2px;
572+ border-collapse: collapse;
573+}
574+
575+/* -- general body styles --------------------------------------------------- */
576+
577+div.body p, div.body dd, div.body li, div.body blockquote {
578+ -moz-hyphens: auto;
579+ -ms-hyphens: auto;
580+ -webkit-hyphens: auto;
581+ hyphens: auto;
582+}
583+
584+a.headerlink {
585+ visibility: hidden;
586+}
587+
588+h1:hover > a.headerlink,
589+h2:hover > a.headerlink,
590+h3:hover > a.headerlink,
591+h4:hover > a.headerlink,
592+h5:hover > a.headerlink,
593+h6:hover > a.headerlink,
594+dt:hover > a.headerlink,
595+caption:hover > a.headerlink,
596+p.caption:hover > a.headerlink,
597+div.code-block-caption:hover > a.headerlink {
598+ visibility: visible;
599+}
600+
601+div.body p.caption {
602+ text-align: inherit;
603+}
604+
605+div.body td {
606+ text-align: left;
607+}
608+
609+.first {
610+ margin-top: 0 !important;
611+}
612+
613+p.rubric {
614+ margin-top: 30px;
615+ font-weight: bold;
616+}
617+
618+img.align-left, .figure.align-left, object.align-left {
619+ clear: left;
620+ float: left;
621+ margin-right: 1em;
622+}
623+
624+img.align-right, .figure.align-right, object.align-right {
625+ clear: right;
626+ float: right;
627+ margin-left: 1em;
628+}
629+
630+img.align-center, .figure.align-center, object.align-center {
631+ display: block;
632+ margin-left: auto;
633+ margin-right: auto;
634+}
635+
636+.align-left {
637+ text-align: left;
638+}
639+
640+.align-center {
641+ text-align: center;
642+}
643+
644+.align-right {
645+ text-align: right;
646+}
647+
648+/* -- sidebars -------------------------------------------------------------- */
649+
650+div.sidebar {
651+ margin: 0 0 0.5em 1em;
652+ border: 1px solid #ddb;
653+ padding: 7px 7px 0 7px;
654+ background-color: #ffe;
655+ width: 40%;
656+ float: right;
657+}
658+
659+p.sidebar-title {
660+ font-weight: bold;
661+}
662+
663+/* -- topics ---------------------------------------------------------------- */
664+
665+div.topic {
666+ border: 1px solid #ccc;
667+ padding: 7px 7px 0 7px;
668+ margin: 10px 0 10px 0;
669+}
670+
671+p.topic-title {
672+ font-size: 1.1em;
673+ font-weight: bold;
674+ margin-top: 10px;
675+}
676+
677+/* -- admonitions ----------------------------------------------------------- */
678+
679+div.admonition {
680+ margin-top: 10px;
681+ margin-bottom: 10px;
682+ padding: 7px;
683+}
684+
685+div.admonition dt {
686+ font-weight: bold;
687+}
688+
689+div.admonition dl {
690+ margin-bottom: 0;
691+}
692+
693+p.admonition-title {
694+ margin: 0px 10px 5px 0px;
695+ font-weight: bold;
696+}
697+
698+div.body p.centered {
699+ text-align: center;
700+ margin-top: 25px;
701+}
702+
703+/* -- tables ---------------------------------------------------------------- */
704+
705+table.docutils {
706+ border: 0;
707+ border-collapse: collapse;
708+}
709+
710+table caption span.caption-number {
711+ font-style: italic;
712+}
713+
714+table caption span.caption-text {
715+}
716+
717+table.docutils td, table.docutils th {
718+ padding: 1px 8px 1px 5px;
719+ border-top: 0;
720+ border-left: 0;
721+ border-right: 0;
722+ border-bottom: 1px solid #aaa;
723+}
724+
725+table.footnote td, table.footnote th {
726+ border: 0 !important;
727+}
728+
729+th {
730+ text-align: left;
731+ padding-right: 5px;
732+}
733+
734+table.citation {
735+ border-left: solid 1px gray;
736+ margin-left: 1px;
737+}
738+
739+table.citation td {
740+ border-bottom: none;
741+}
742+
743+/* -- figures --------------------------------------------------------------- */
744+
745+div.figure {
746+ margin: 0.5em;
747+ padding: 0.5em;
748+}
749+
750+div.figure p.caption {
751+ padding: 0.3em;
752+}
753+
754+div.figure p.caption span.caption-number {
755+ font-style: italic;
756+}
757+
758+div.figure p.caption span.caption-text {
759+}
760+
761+/* -- field list styles ----------------------------------------------------- */
762+
763+table.field-list td, table.field-list th {
764+ border: 0 !important;
765+}
766+
767+.field-list ul {
768+ margin: 0;
769+ padding-left: 1em;
770+}
771+
772+.field-list p {
773+ margin: 0;
774+}
775+
776+.field-name {
777+ -moz-hyphens: manual;
778+ -ms-hyphens: manual;
779+ -webkit-hyphens: manual;
780+ hyphens: manual;
781+}
782+
783+/* -- other body styles ----------------------------------------------------- */
784+
785+ol.arabic {
786+ list-style: decimal;
787+}
788+
789+ol.loweralpha {
790+ list-style: lower-alpha;
791+}
792+
793+ol.upperalpha {
794+ list-style: upper-alpha;
795+}
796+
797+ol.lowerroman {
798+ list-style: lower-roman;
799+}
800+
801+ol.upperroman {
802+ list-style: upper-roman;
803+}
804+
805+dl {
806+ margin-bottom: 15px;
807+}
808+
809+dd p {
810+ margin-top: 0px;
811+}
812+
813+dd ul, dd table {
814+ margin-bottom: 10px;
815+}
816+
817+dd {
818+ margin-top: 3px;
819+ margin-bottom: 10px;
820+ margin-left: 30px;
821+}
822+
823+/*dt:target, span.highlighted {
824+ background-color: #fbe54e;
825+}*/
826+
827+rect.highlighted {
828+ fill: #C8BE93;
829+}
830+
831+dl.glossary dt {
832+ font-weight: bold;
833+ font-size: 1.1em;
834+}
835+
836+.optional {
837+ font-size: 1.3em;
838+}
839+
840+/*.sig-paren {
841+ font-size: larger;
842+}*/
843+
844+.versionmodified {
845+ font-style: italic;
846+}
847+
848+.system-message {
849+ background-color: #fda;
850+ padding: 5px;
851+ border: 3px solid red;
852+}
853+
854+.footnote:target {
855+ background-color: #ffa;
856+}
857+
858+.line-block {
859+ display: block;
860+ margin-top: 1em;
861+ margin-bottom: 1em;
862+}
863+
864+.line-block .line-block {
865+ margin-top: 0;
866+ margin-bottom: 0;
867+ margin-left: 1.5em;
868+}
869+
870+.guilabel, .menuselection {
871+ font-family: sans-serif;
872+}
873+
874+.accelerator {
875+ text-decoration: underline;
876+}
877+
878+.classifier {
879+ font-style: oblique;
880+}
881+
882+abbr, acronym {
883+ border-bottom: dotted 1px;
884+ cursor: help;
885+}
886+
887+/* -- code displays --------------------------------------------------------- */
888+
889+pre {
890+ overflow: auto;
891+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
892+}
893+
894+span.pre {
895+ -moz-hyphens: none;
896+ -ms-hyphens: none;
897+ -webkit-hyphens: none;
898+ hyphens: none;
899+}
900+
901+td.linenos pre {
902+ padding: 5px 0px;
903+ border: 0;
904+ background-color: transparent;
905+ color: #aaa;
906+}
907+
908+table.highlighttable {
909+ margin-left: 0.5em;
910+}
911+
912+table.highlighttable td {
913+ padding: 0 0.5em 0 0.5em;
914+}
915+
916+div.code-block-caption {
917+ padding: 2px 5px;
918+ font-size: small;
919+}
920+
921+div.code-block-caption code {
922+ background-color: transparent;
923+}
924+
925+div.code-block-caption + div > div.highlight > pre {
926+ margin-top: 0;
927+}
928+
929+div.code-block-caption span.caption-number {
930+ padding: 0.1em 0.3em;
931+ font-style: italic;
932+}
933+
934+div.code-block-caption span.caption-text {
935+}
936+
937+div.literal-block-wrapper {
938+ padding: 1em 1em 0;
939+}
940+
941+div.literal-block-wrapper div.highlight {
942+ margin: 0;
943+}
944+
945+code.descname {
946+ background-color: transparent;
947+ font-weight: bold;
948+ font-size: 1.2em;
949+}
950+
951+code.descclassname {
952+ background-color: transparent;
953+}
954+
955+code.xref, a code {
956+ background-color: transparent;
957+ font-weight: bold;
958+}
959+
960+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
961+ background-color: transparent;
962+}
963+
964+.viewcode-link {
965+ float: right;
966+}
967+
968+.viewcode-back {
969+ float: right;
970+ font-family: sans-serif;
971+}
972+
973+div.viewcode-block:target {
974+ margin: -1px -10px;
975+ padding: 0 10px;
976+}
977+
978+/* -- math display ---------------------------------------------------------- */
979+
980+img.math {
981+ vertical-align: middle;
982+}
983+
984+div.body div.math p {
985+ text-align: center;
986+}
987+
988+span.eqno {
989+ float: right;
990+}
991+
992+span.eqno a.headerlink {
993+ position: relative;
994+ left: 0px;
995+ z-index: 1;
996+}
997+
998+div.math:hover a.headerlink {
999+ visibility: visible;
1000+}
1001+
1002+/* -- printout stylesheet --------------------------------------------------- */
1003+
1004+@media print {
1005+ div.document,
1006+ div.documentwrapper,
1007+ div.bodywrapper {
1008+ margin: 0 !important;
1009+ width: 100%;
1010+ }
1011+
1012+ div.sphinxsidebar,
1013+ div.related,
1014+ div.footer,
1015+ #top-link {
1016+ display: none;
1017+ }
1018+}
1019
1020=== added file 'documentation/static/black20.png'
1021Binary files documentation/static/black20.png 1970-01-01 00:00:00 +0000 and documentation/static/black20.png 2018-05-24 19:17:56 +0000 differ
1022=== added file 'documentation/static/but1.png'
1023Binary files documentation/static/but1.png 1970-01-01 00:00:00 +0000 and documentation/static/but1.png 2018-05-24 19:17:56 +0000 differ
1024=== added file 'documentation/static/parchment.png'
1025Binary files documentation/static/parchment.png 1970-01-01 00:00:00 +0000 and documentation/static/parchment.png 2018-05-24 19:17:56 +0000 differ
1026=== added file 'documentation/static/pygments.css'
1027--- documentation/static/pygments.css 1970-01-01 00:00:00 +0000
1028+++ documentation/static/pygments.css 2018-05-24 19:17:56 +0000
1029@@ -0,0 +1,78 @@
1030+/*******************************/
1031+/* Pygment Syntax Highlighting */
1032+/*******************************/
1033+pre {
1034+ margin: 4px;
1035+ display: block; /*show black background over the whole width*/
1036+}
1037+.highlight {
1038+ /* text-shadow: none;
1039+ / * color: #000000;*/ *
1040+ padding: 4px;
1041+ border: 1px solid black;
1042+ background-image: url("black20.png");
1043+ margin: 4px 0px;
1044+ margin-left: 1em;
1045+}
1046+.highlight .hll { background-color: #ffffcc }
1047+/*.highlight { background: #f0f0f0; }*/
1048+.highlight .c { color: #60a0b0; font-style: italic } /* Comment */
1049+.highlight .err { border: 1px solid #FF0000 } /* Error */
1050+.highlight .k { color: #007020; font-weight: bold } /* Keyword */
1051+.highlight .o { color: #998 } /* Operator */
1052+.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
1053+.highlight .cp { color: #007020 } /* Comment.Preproc */
1054+.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
1055+.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
1056+.highlight .gd { color: #A00000 } /* Generic.Deleted */
1057+.highlight .ge { font-style: italic } /* Generic.Emph */
1058+.highlight .gr { color: #FF0000 } /* Generic.Error */
1059+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
1060+.highlight .gi { color: #00A000 } /* Generic.Inserted */
1061+.highlight .go { color: #808080 } /* Generic.Output */
1062+.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
1063+.highlight .gs { font-weight: bold } /* Generic.Strong */
1064+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
1065+.highlight .gt { color: #0040D0 } /* Generic.Traceback */
1066+.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
1067+.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
1068+.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
1069+.highlight .kp { color: #007020 } /* Keyword.Pseudo */
1070+.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
1071+.highlight .kt { color: #902000 } /* Keyword.Type */
1072+.highlight .m { color: #40a070 } /* Literal.Number */
1073+.highlight .s { color: #4070a0 } /* Literal.String */
1074+.highlight .na { color: #4070a0 } /* Name.Attribute */
1075+.highlight .nb { color: #007020 } /* Name.Builtin */
1076+.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
1077+.highlight .no { color: #60add5 } /* Name.Constant */
1078+.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
1079+.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
1080+.highlight .ne { color: #007020 } /* Name.Exception */
1081+.highlight .nf { color: #aaaa00 } /* Name.Function */
1082+.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
1083+.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
1084+.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
1085+.highlight .nv { color: #bb60d5 } /* Name.Variable */
1086+.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
1087+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
1088+.highlight .mf { color: #40a070 } /* Literal.Number.Float */
1089+.highlight .mh { color: #40a070 } /* Literal.Number.Hex */
1090+.highlight .mi { color: #40a070 } /* Literal.Number.Integer */
1091+.highlight .mo { color: #40a070 } /* Literal.Number.Oct */
1092+.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
1093+.highlight .sc { color: #4070a0 } /* Literal.String.Char */
1094+.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
1095+.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
1096+.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
1097+.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
1098+.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
1099+.highlight .sx { color: #c65d09 } /* Literal.String.Other */
1100+.highlight .sr { color: #235388 } /* Literal.String.Regex */
1101+.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
1102+.highlight .ss { color: #517918 } /* Literal.String.Symbol */
1103+.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
1104+.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
1105+.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
1106+.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
1107+.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
1108
1109=== added file 'documentation/static/widelands.css'
1110--- documentation/static/widelands.css 1970-01-01 00:00:00 +0000
1111+++ documentation/static/widelands.css 2018-05-24 19:17:56 +0000
1112@@ -0,0 +1,258 @@
1113+/*
1114+ * classic.css_t
1115+ * ~~~~~~~~~~~~~
1116+ *
1117+ * Sphinx stylesheet -- classic theme.
1118+ *
1119+ * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
1120+ * :license: BSD, see LICENSE for details.
1121+ *
1122+ */
1123+/* This was adjusted by the widelands development team to fit with the
1124+ * widelands website style.
1125+ */
1126+
1127+/* -- page layout ----------------------------------------------------------- */
1128+
1129+body {
1130+ font-family: sans-serif;
1131+ font-size: 100%;
1132+ background-image: url("parchment.png");
1133+ color: #ffffff;
1134+ margin: 0;
1135+ padding: 0;
1136+}
1137+
1138+div.documentwrapper {
1139+ float: left;
1140+ width: 100%;
1141+}
1142+
1143+div.bodywrapper {
1144+ margin: 0 0 0 230px;
1145+}
1146+
1147+div.body {
1148+ background-image: url("but1.png");
1149+ background-color: #000000;
1150+ padding: 0 20px 30px 20px;
1151+}
1152+
1153+div.footer {
1154+ color: #181;
1155+ width: 100%;
1156+ padding: 9px 0 9px 0;
1157+ text-align: center;
1158+ font-size: 75%;
1159+}
1160+
1161+div.footer a {
1162+ color: #181;
1163+}
1164+
1165+div.related {
1166+ background-color: #000000;
1167+ background-image: url("wood.png");
1168+ line-height: 30px;
1169+}
1170+
1171+div.related a {
1172+ font-weight: bold;
1173+ color: rgba(254, 234, 138, 1);
1174+}
1175+
1176+div.sphinxsidebar {
1177+ color: #072107;
1178+}
1179+
1180+div.sphinxsidebar h3 {
1181+ font-family: 'Trebuchet MS', sans-serif;
1182+ font-size: 1.4em;
1183+ margin: 0;
1184+ padding: 0;
1185+}
1186+
1187+div.sphinxsidebar h3 a {
1188+ border-bottom: 1px solid #0d440d;
1189+}
1190+
1191+div.sphinxsidebar h4 {
1192+ font-family: 'Trebuchet MS', sans-serif;
1193+ font-size: 1.3em;
1194+ margin: 5px 0 0 0;
1195+ padding: 0;
1196+}
1197+
1198+div.sphinxsidebar p.topless {
1199+ margin: 5px 10px 10px 10px;
1200+}
1201+
1202+div.sphinxsidebar ul {
1203+ padding: 0;
1204+}
1205+div.sphinxsidebar ul.this-page-menu {
1206+ margin: 5px 10px 10px 10px;
1207+}
1208+div.sphinxsidebar a {
1209+ color: #072107;
1210+}
1211+
1212+div.sphinxsidebar a:hover {
1213+ text-decoration: underline;
1214+}
1215+
1216+div.sphinxsidebar input {
1217+ font-family: sans-serif;
1218+ font-size: 1em;
1219+}
1220+
1221+/* -- hyperlink styles ------------------------------------------------------ */
1222+
1223+a {
1224+ color: rgba(254, 234, 138, 1);
1225+ text-decoration: none;
1226+}
1227+
1228+a:visited {
1229+ color: #C8BE93;
1230+ text-decoration: none;
1231+}
1232+
1233+a:hover {
1234+ color: #181;
1235+}
1236+
1237+/* -- body styles ----------------------------------------------------------- */
1238+
1239+div.body h1,
1240+div.body h2,
1241+div.body h3,
1242+div.body h4,
1243+div.body h5,
1244+div.body h6 {
1245+ font-family: 'Trebuchet MS', sans-serif;
1246+ background-image: url("black20.png");
1247+ font-weight: normal;
1248+ color: #181;
1249+ border-bottom: 1px solid #C8BE93;
1250+ margin: 20px -20px 10px -20px;
1251+ padding: 3px 0 3px 10px;
1252+}
1253+
1254+div.body h1 {
1255+ margin-top: 0;
1256+}
1257+
1258+a.headerlink {
1259+ color: #181;
1260+ font-size: 0.8em;
1261+ padding: 0 4px 0 4px;
1262+ text-decoration: none;
1263+}
1264+
1265+a.headerlink:hover {
1266+ background-color: #181;
1267+ color: white;
1268+}
1269+
1270+div.body p, div.body dd, div.body li, div.body blockquote {
1271+ line-height: 130%;
1272+ /*margin: 0;*/
1273+}
1274+
1275+div.admonition p.admonition-title + p {
1276+ display: inline;
1277+}
1278+
1279+div.admonition p {
1280+ margin-bottom: 5px;
1281+}
1282+
1283+div.admonition pre {
1284+ margin-bottom: 5px;
1285+}
1286+
1287+div.admonition ul, div.admonition ol {
1288+ margin-bottom: 5px;
1289+}
1290+
1291+div.note {
1292+ border: 1px solid #ccc;
1293+}
1294+
1295+div.seealso {
1296+ background-color: #ffc;
1297+ border: 1px solid #ff6;
1298+}
1299+
1300+div.topic {
1301+ background-color: #eee;
1302+}
1303+
1304+div.warning {
1305+ background-color: #ffe4e4;
1306+ border: 1px solid #f66;
1307+}
1308+
1309+p.admonition-title {
1310+ display: inline;
1311+}
1312+
1313+p.admonition-title:after {
1314+ content: ":";
1315+}
1316+
1317+pre {
1318+ padding: 5px;
1319+ margin-left: 1em;
1320+ /*line-height: 120%;*/
1321+ background-image: url("black20.png");
1322+ border-left: none;
1323+ border-right: none;
1324+}
1325+
1326+div.body code .pre {
1327+ background-image: url("black20.png");
1328+ padding: 0 1px 0 1px;
1329+}
1330+
1331+th {
1332+ background-image: url("black20.png");
1333+}
1334+
1335+.warning code {
1336+ background: #efc2c2;
1337+}
1338+
1339+/*.note code {
1340+ background: #d6d6d6;
1341+}*/
1342+
1343+.viewcode-back {
1344+ font-family: sans-serif;
1345+}
1346+
1347+div.viewcode-block:target {
1348+ background-color: #f4debf;
1349+ border-top: 1px solid #ac9;
1350+ border-bottom: 1px solid #ac9;
1351+}
1352+
1353+div.code-block-caption {
1354+ color: #efefef;
1355+ background-color: #1c4e63;
1356+}
1357+
1358+
1359+/* Overwritten classes from basic.css */
1360+code.descname {
1361+ color: #181;
1362+ font-size: larger;
1363+ padding-right: 0.5em;
1364+}
1365+
1366+table.indextable tr.cap {
1367+ margin-top: 10px;
1368+ background-image: url("black20.png");
1369+}
1370+
1371
1372=== added file 'documentation/static/wood.png'
1373Binary files documentation/static/wood.png 1970-01-01 00:00:00 +0000 and documentation/static/wood.png 2018-05-24 19:17:56 +0000 differ
1374=== modified file 'local_settings.py.sample'
1375--- local_settings.py.sample 2018-05-05 18:58:22 +0000
1376+++ local_settings.py.sample 2018-05-24 19:17:56 +0000
1377@@ -17,6 +17,7 @@
1378 # set WIDELANDS_SVN_DIR to the correct path. See also:
1379 # https://wl.widelands.org/wiki/BzrPrimer/
1380 WIDELANDS_SVN_DIR = "/path/to/widelands/trunk/"
1381+
1382 os.environ['PATH'] = WIDELANDS_SVN_DIR + ':' + os.environ['PATH']
1383
1384 DATABASES = {
1385
1386=== modified file 'local_urls.py.sample'
1387--- local_urls.py.sample 2018-04-03 18:57:40 +0000
1388+++ local_urls.py.sample 2018-05-24 19:17:56 +0000
1389@@ -1,6 +1,9 @@
1390 from django.conf.urls import *
1391 from django.conf import settings
1392 from django.views.static import serve
1393+from os import path
1394+
1395+# Don't use this file on the server!
1396
1397 local_urlpatterns = [
1398 url(r'^wlmedia/(?P<path>.*)$',
1399@@ -11,4 +14,8 @@
1400 serve,
1401 {'document_root': settings.STATIC_MEDIA_PATH},
1402 name='static_media_pybb'),
1403+ url(r'^documentation/(?P<path>.*)$',
1404+ serve,
1405+ {'document_root': path.join(settings.STATIC_MEDIA_PATH, 'documentation/html')},
1406+ name='documentation'),
1407 ]
1408
1409=== removed file 'media/css/documentation.css'
1410--- media/css/documentation.css 2017-12-12 11:48:37 +0000
1411+++ media/css/documentation.css 1970-01-01 00:00:00 +0000
1412@@ -1,118 +0,0 @@
1413-table.field-list{
1414- border-collapse: separate;
1415-}
1416-
1417-.sphinx a.headerlink {
1418- padding: 0 4px 0 4px;
1419- visibility: hidden;
1420-}
1421-
1422-.sphinx *:hover > a.headerlink {
1423- visibility: visible;
1424-}
1425-
1426-img.math{
1427- vertical-align: middle;
1428-}
1429-
1430-th {
1431- background-color: #4A4A4A;
1432-}
1433-
1434-/* Disable base style and set new ones */
1435-code {
1436- font-weight: bold;
1437- font-size: 1.3em;
1438-}
1439-
1440-/* Used for functions in first line */
1441-dt {
1442- margin-top: 0.5em;
1443- margin-bottom: 0.25em;
1444-}
1445-code.descname {
1446- padding: 0.25em;
1447- background-image: none;
1448- font-weight: bold;
1449- font-size: 1.4em;
1450- color: #181;
1451-}
1452-
1453-
1454-/*******************************/
1455-/* Pygment Syntax Highlighting */
1456-/*******************************/
1457-pre {
1458- margin: 0px;
1459- display: block; /*show black background over the whole width*/
1460-}
1461-.highlight {
1462-/* text-shadow: none;
1463-/* color: #000000;*/
1464- padding: 4px;
1465- border: 1px solid black;
1466- background-image: url("../img/black20.png");
1467- margin: 4px 0px;
1468-}
1469-.highlight .hll { background-color: #ffffcc }
1470-/*.highlight { background: #f0f0f0; }*/
1471-.highlight .c { color: #60a0b0; font-style: italic } /* Comment */
1472-.highlight .err { border: 1px solid #FF0000 } /* Error */
1473-.highlight .k { color: #007020; font-weight: bold } /* Keyword */
1474-.highlight .o { color: #998 } /* Operator */
1475-.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
1476-.highlight .cp { color: #007020 } /* Comment.Preproc */
1477-.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
1478-.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
1479-.highlight .gd { color: #A00000 } /* Generic.Deleted */
1480-.highlight .ge { font-style: italic } /* Generic.Emph */
1481-.highlight .gr { color: #FF0000 } /* Generic.Error */
1482-.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
1483-.highlight .gi { color: #00A000 } /* Generic.Inserted */
1484-.highlight .go { color: #808080 } /* Generic.Output */
1485-.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
1486-.highlight .gs { font-weight: bold } /* Generic.Strong */
1487-.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
1488-.highlight .gt { color: #0040D0 } /* Generic.Traceback */
1489-.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
1490-.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
1491-.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
1492-.highlight .kp { color: #007020 } /* Keyword.Pseudo */
1493-.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
1494-.highlight .kt { color: #902000 } /* Keyword.Type */
1495-.highlight .m { color: #40a070 } /* Literal.Number */
1496-.highlight .s { color: #4070a0 } /* Literal.String */
1497-.highlight .na { color: #4070a0 } /* Name.Attribute */
1498-.highlight .nb { color: #007020 } /* Name.Builtin */
1499-.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
1500-.highlight .no { color: #60add5 } /* Name.Constant */
1501-.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
1502-.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
1503-.highlight .ne { color: #007020 } /* Name.Exception */
1504-.highlight .nf { color: #aaaa00 } /* Name.Function */
1505-.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
1506-.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
1507-.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
1508-.highlight .nv { color: #bb60d5 } /* Name.Variable */
1509-.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
1510-.highlight .w { color: #bbbbbb } /* Text.Whitespace */
1511-.highlight .mf { color: #40a070 } /* Literal.Number.Float */
1512-.highlight .mh { color: #40a070 } /* Literal.Number.Hex */
1513-.highlight .mi { color: #40a070 } /* Literal.Number.Integer */
1514-.highlight .mo { color: #40a070 } /* Literal.Number.Oct */
1515-.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
1516-.highlight .sc { color: #4070a0 } /* Literal.String.Char */
1517-.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
1518-.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
1519-.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
1520-.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
1521-.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
1522-.highlight .sx { color: #c65d09 } /* Literal.String.Other */
1523-.highlight .sr { color: #235388 } /* Literal.String.Regex */
1524-.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
1525-.highlight .ss { color: #517918 } /* Literal.String.Symbol */
1526-.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
1527-.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
1528-.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
1529-.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
1530-.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
1531
1532=== added directory 'media/documentation'
1533=== modified file 'settings.py'
1534--- settings.py 2018-05-07 06:32:56 +0000
1535+++ settings.py 2018-05-24 19:17:56 +0000
1536@@ -94,6 +94,7 @@
1537 'wlggz',
1538 'wlscheduling',
1539 'check_input.apps.CheckInput',
1540+ 'documentation',
1541 'haystack', # search engine; see option HAYSTACK_CONNECTIONS
1542
1543 # Modified 3rd party apps
1544@@ -109,7 +110,6 @@
1545 'dj_pagination',
1546 'tagging',
1547 'djangoratings', # included as wlapp
1548- 'sphinxdoc', # included as wlapp
1549 ]
1550
1551 MIDDLEWARE = [
1552
1553=== modified file 'sitemap_urls.py'
1554--- sitemap_urls.py 2016-12-13 18:28:51 +0000
1555+++ sitemap_urls.py 2018-05-24 19:17:56 +0000
1556@@ -7,12 +7,10 @@
1557 from news.sitemap import *
1558 from pybb.sitemap import *
1559 from wlhelp.sitemap import *
1560-from sphinxdoc.sitemap import *
1561
1562
1563 sitemaps = {
1564 'static': StaticViewSitemap,
1565- 'docs': DocumentationSitemap,
1566 'news': NewsSitemap,
1567 'wiki': WikiSitemap,
1568 'forum': ForumSitemap,
1569
1570=== removed directory 'sphinxdoc'
1571=== removed file 'sphinxdoc/__init__.py'
1572=== removed file 'sphinxdoc/admin.py'
1573--- sphinxdoc/admin.py 2016-12-13 18:28:51 +0000
1574+++ sphinxdoc/admin.py 1970-01-01 00:00:00 +0000
1575@@ -1,14 +0,0 @@
1576-# encoding: utf-8
1577-"""Admin interface for the sphinxdoc app."""
1578-
1579-from django.contrib import admin
1580-
1581-from sphinxdoc.models import App
1582-
1583-
1584-class AppAdmin(admin.ModelAdmin):
1585- list_display = ('name', 'path',)
1586- prepopulated_fields = {'slug': ('name',)}
1587-
1588-
1589-admin.site.register(App, AppAdmin)
1590
1591=== removed directory 'sphinxdoc/migrations'
1592=== removed file 'sphinxdoc/migrations/0001_initial.py'
1593--- sphinxdoc/migrations/0001_initial.py 2016-12-13 18:28:51 +0000
1594+++ sphinxdoc/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
1595@@ -1,24 +0,0 @@
1596-# -*- coding: utf-8 -*-
1597-from __future__ import unicode_literals
1598-
1599-from django.db import models, migrations
1600-
1601-
1602-class Migration(migrations.Migration):
1603-
1604- dependencies = [
1605- ]
1606-
1607- operations = [
1608- migrations.CreateModel(
1609- name='App',
1610- fields=[
1611- ('id', models.AutoField(verbose_name='ID',
1612- serialize=False, auto_created=True, primary_key=True)),
1613- ('name', models.CharField(max_length=100)),
1614- ('slug', models.SlugField(
1615- help_text='Used in the URL for the app. Must be unique.', unique=True)),
1616- ('path', models.CharField(max_length=255)),
1617- ],
1618- ),
1619- ]
1620
1621=== removed file 'sphinxdoc/migrations/__init__.py'
1622=== removed file 'sphinxdoc/models.py'
1623--- sphinxdoc/models.py 2018-04-08 16:23:55 +0000
1624+++ sphinxdoc/models.py 1970-01-01 00:00:00 +0000
1625@@ -1,23 +0,0 @@
1626-# encoding: utf-8
1627-"""
1628-Models for django-sphinxdoc.
1629-"""
1630-
1631-from django.db import models
1632-from django.urls import reverse
1633-
1634-
1635-class App(models.Model):
1636- name = models.CharField(max_length=100)
1637- slug = models.SlugField(unique=True,
1638- help_text=u'Used in the URL for the app. Must be unique.')
1639- path = models.CharField(max_length=255)
1640-
1641- def __unicode__(self):
1642- return self.name
1643-
1644- def get_absolute_url(self):
1645- return reverse('doc-index', kwargs={'slug': self.slug})
1646-
1647- class Meta:
1648- app_label = 'sphinxdoc'
1649
1650=== removed file 'sphinxdoc/sitemap.py'
1651--- sphinxdoc/sitemap.py 2016-12-15 12:55:44 +0000
1652+++ sphinxdoc/sitemap.py 1970-01-01 00:00:00 +0000
1653@@ -1,26 +0,0 @@
1654-from django.contrib.sitemaps import Sitemap
1655-from sphinxdoc.models import App
1656-import datetime
1657-import os
1658-
1659-try:
1660- app = App.objects.get(slug='wl')
1661-except:
1662- pass
1663-
1664-
1665-class DocumentationSitemap(Sitemap):
1666- """This is just a dummy class to return the link to docs/wl/genindex."""
1667-
1668- changefreq = 'yearly'
1669- priority = 0.5
1670-
1671- def items(self):
1672- return ['']
1673-
1674- def location(self, item):
1675- return '/docs/wl/genindex/'
1676-
1677- def lastmod(self, item):
1678- return datetime.datetime.fromtimestamp(
1679- os.path.getmtime(os.path.join(app.path, 'last_build')))
1680
1681=== removed file 'sphinxdoc/urls.py'
1682--- sphinxdoc/urls.py 2016-11-07 13:26:04 +0000
1683+++ sphinxdoc/urls.py 1970-01-01 00:00:00 +0000
1684@@ -1,28 +0,0 @@
1685-# encoding: utf-8
1686-
1687-from django.conf.urls import *
1688-from django.views.generic.list import ListView
1689-from sphinxdoc import views
1690-from sphinxdoc import models
1691-
1692-app_info = {
1693- 'queryset': models.App.objects.all().order_by('name'),
1694- 'template_object_name': 'app',
1695-}
1696-
1697-
1698-urlpatterns = [
1699- url(r'^$', ListView.as_view(), app_info,),
1700- url(r'^(?P<slug>[\w-]+)/search/$',
1701- views.search, name='doc-search', ),
1702- url(r'^(?P<slug>[\w-]+)/_images/(?P<path>.*)$',
1703- views.images, ),
1704- url(r'^(?P<slug>[\w-]+)/_source/(?P<path>.*)$',
1705- views.source, ),
1706- url(r'^(?P<slug>[\w-]+)/_objects/$',
1707- views.objects_inventory, name='objects-inv', ),
1708- url(r'^(?P<slug>[\w-]+)/$',
1709- views.documentation, {'url': ''}, name='doc-index', ),
1710- url(r'^(?P<slug>[\w-]+)/(?P<url>(([\w-]+)/)+)$',
1711- views.documentation, name='doc-detail', ),
1712-]
1713
1714=== removed file 'sphinxdoc/views.py'
1715--- sphinxdoc/views.py 2018-04-07 15:35:47 +0000
1716+++ sphinxdoc/views.py 1970-01-01 00:00:00 +0000
1717@@ -1,89 +0,0 @@
1718-# encoding: utf-8
1719-
1720-import datetime
1721-import os.path
1722-
1723-from django.http import Http404
1724-from django.shortcuts import get_object_or_404, render
1725-#from django.template import RequestContext
1726-import json
1727-from django.views import static
1728-
1729-from sphinxdoc.models import App
1730-
1731-
1732-SPECIAL_TITLES = {
1733- 'genindex': 'General Index',
1734- 'py-modindex': 'Module Index',
1735- 'search': 'Search',
1736-}
1737-
1738-
1739-def documentation(request, slug, url):
1740- app = get_object_or_404(App, slug=slug)
1741- url = url.strip('/')
1742- page_name = os.path.basename(url)
1743-
1744- path = os.path.join(app.path, url, 'index.fjson')
1745- if not os.path.exists(path):
1746- path = os.path.dirname(path) + '.fjson'
1747- if not os.path.exists(path):
1748- raise Http404('"%s" does not exist' % path)
1749-
1750- templates = (
1751- 'sphinxdoc/%s.html' % page_name,
1752- 'sphinxdoc/documentation.html',
1753- )
1754-
1755- data = {
1756- 'app': app,
1757- 'doc': json.load(open(path, 'rb')),
1758- 'env': json.load(open(
1759- os.path.join(app.path, 'globalcontext.json'), 'rb')),
1760- 'version': app.name,
1761- 'docurl': url,
1762- 'update_date': datetime.datetime.fromtimestamp(
1763- os.path.getmtime(os.path.join(app.path, 'last_build'))),
1764- 'home': app.get_absolute_url(),
1765- # 'search': urlresolvers.reverse('document-search', kwargs={'lang':lang, 'version':version}),
1766- 'redirect_from': request.GET.get('from', None),
1767-
1768- }
1769- if 'title' not in data['doc']:
1770- data['doc']['title'] = SPECIAL_TITLES[page_name]
1771-
1772- return render(request, templates, data)
1773-
1774-
1775-def search(request, slug):
1776- from django.http import HttpResponse
1777- return HttpResponse('Not yet implemented.')
1778-
1779-
1780-def objects_inventory(request, slug):
1781- app = get_object_or_404(App, slug=slug)
1782- response = static.serve(
1783- request,
1784- document_root=app.path,
1785- path='objects.inv',
1786- )
1787- response['Content-Type'] = 'text/plain'
1788- return response
1789-
1790-
1791-def images(request, slug, path):
1792- app = get_object_or_404(App, slug=slug)
1793- return static.serve(
1794- request,
1795- document_root=os.path.join(app.path, '_images'),
1796- path=path,
1797- )
1798-
1799-
1800-def source(request, slug, path):
1801- app = get_object_or_404(App, slug=slug)
1802- return static.serve(
1803- request,
1804- document_root=os.path.join(app.path, '_sources'),
1805- path=path,
1806- )
1807
1808=== modified file 'templates/navigation.html'
1809--- templates/navigation.html 2018-02-17 11:22:34 +0000
1810+++ templates/navigation.html 2018-05-24 19:17:56 +0000
1811@@ -50,7 +50,7 @@
1812 <ul>
1813 <li><a href="{% url 'wiki_article' "Contribute" %}">Contribute</a></li>
1814 <li><a href="{% url 'developers' %}">Widelands Development Team</a></li>
1815- <li><a href="/docs/wl/" target="_blank">Documentation</a></li>
1816+ <li><a href="documentation/index.html" target="_blank">Documentation</a></li>
1817 <li><a href="https://bugs.launchpad.net/widelands" target="_blank">Widelands Bugtracker</a></li>
1818 <li><a href="https://bugs.launchpad.net/widelands-website" target="_blank">Website Bugtracker</a></li>
1819 </ul>
1820
1821=== removed directory 'templates/sphinxdoc'
1822=== removed file 'templates/sphinxdoc/app_list.html'
1823--- templates/sphinxdoc/app_list.html 2012-05-16 22:31:25 +0000
1824+++ templates/sphinxdoc/app_list.html 1970-01-01 00:00:00 +0000
1825@@ -1,18 +0,0 @@
1826-{% extends 'sphinxdoc/base.html' %}
1827-
1828-{% block title %}Overview - {{ block.super }}{% endblock %}
1829-
1830-{% block content %}
1831-<h1>Documentation Overview</h1>
1832-<div class="blogEntry">
1833- <a href="/docs/">Documentation</a>
1834-
1835- <br />
1836-
1837- <ul>
1838- {% for app in app_list %}
1839- <li><a href="{{ app.get_absolute_url }}">{{ app.name }}</a></li>
1840- {% endfor %}
1841- </ul>
1842-</div>
1843-{% endblock content %}
1844
1845=== removed file 'templates/sphinxdoc/base.html'
1846--- templates/sphinxdoc/base.html 2017-07-04 06:40:29 +0000
1847+++ templates/sphinxdoc/base.html 1970-01-01 00:00:00 +0000
1848@@ -1,17 +0,0 @@
1849-{% extends "base.html" %}
1850-
1851-{% block title %}
1852-Documentation - {{ block.super }}
1853-{% endblock %}
1854-
1855-{% block extra_head %}
1856-<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/documentation.css" />
1857-
1858-{# using mathjax for formulas #}
1859-<script type="text/javascript" async
1860- src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML">
1861-</script>
1862-
1863-{{ block.super}}
1864-{% endblock %}
1865-
1866
1867=== removed file 'templates/sphinxdoc/documentation.html'
1868--- templates/sphinxdoc/documentation.html 2016-11-07 13:26:04 +0000
1869+++ templates/sphinxdoc/documentation.html 1970-01-01 00:00:00 +0000
1870@@ -1,57 +0,0 @@
1871-{% extends 'sphinxdoc/base.html' %}
1872-
1873-{% block title %}
1874-{{ app.name }} - {{ block.super }}
1875-{% endblock %}
1876-
1877-{% block content %}
1878-<h1>Documentation: {{ app.name }}</h1>
1879-<div class="blogEntry">
1880- <a href="{{ app.get_absolute_url }}">{{ app.name }}</a>
1881- {% for p in doc.parents %}
1882- » <a href="{{ p.link }}">{{ p.title|safe }}</a>
1883- {% endfor %}
1884- » {{ doc.title|safe }}
1885- {% if doc.prev or doc.next %}
1886- <br /><br />
1887- <div class="posLeft">
1888- {% if doc.prev %}
1889- Prev: <a href="{{ doc.prev.link }}">{{ doc.prev.title|safe }}</a>
1890- {% endif %}
1891- </div>
1892- <div class="posRight">
1893- {% if doc.next %}
1894- Next: <a href="{{ doc.next.link }}">{{ doc.next.title|safe }}</a>
1895- {% endif %}
1896- </div>
1897- {% endif %}
1898-
1899- <br /><br />
1900-
1901- <div class="sphinx">
1902- {% block doc_body %}
1903- {{ doc.body|safe }}
1904- {% endblock %}
1905- </div>
1906-
1907- <br />
1908-
1909- <div class="pagination-bottom">
1910- {% if doc.prev or doc.next %}
1911- <div class="posLeft">
1912- {% if doc.prev %}
1913- Prev: <a href="{{ doc.prev.link }}">{{ doc.prev.title|safe }}</a>
1914- {% endif %}
1915- </div>
1916- <div class="posRight">
1917- {% if doc.next %}
1918- Next: <a href="{{ doc.next.link }}">{{ doc.next.title|safe }}</a>
1919- {% endif %}
1920- </div>
1921- <br />
1922- {% endif %}
1923- <hr />
1924- <span class="small">Last update: {{ update_date|date:"Y-m-d H:i" }} (<a href="http://www.timeanddate.com/worldclock/city.html?n=37">CET</a>)</span>
1925- </div>
1926-</div>
1927-{% endblock content %}
1928
1929=== removed file 'templates/sphinxdoc/genindex.html'
1930--- templates/sphinxdoc/genindex.html 2016-08-07 18:35:30 +0000
1931+++ templates/sphinxdoc/genindex.html 1970-01-01 00:00:00 +0000
1932@@ -1,38 +0,0 @@
1933-{% extends 'sphinxdoc/documentation.html' %}
1934-
1935-{% block doc_body %}
1936- <h1>General Index</h1>
1937- <p class="indexletters">
1938- {% for letter, _ in doc.genindexentries %}
1939- <a href="#{{ letter }}">{{ letter }}</a> {% if not forloop.last %} •{% endif %}
1940- {% endfor %}
1941- </p>
1942-
1943- {% for letter, entries in doc.genindexentries %}
1944- <br />
1945- <h2 id="{{ letter }}">{{ letter }}</h2>
1946- <dl class="index">
1947- {% for name, contents in entries %}
1948- <dt>
1949- {# contents.0 is a list of links for the item #}
1950- {% if contents.0 %}
1951- <a href="{{ contents.0.0.1 }}">{{ name }}</a>
1952- {% else %}
1953- {{ name }}
1954- {% endif %}
1955- </dt>
1956- {# contents.1 is a list of subitems #}
1957- {% if contents.1 %}
1958- {% for subname, sublinks in contents.1 %}
1959- <dd>
1960- <a href="{{ sublinks.0.1 }}">{{ subname }}</a>
1961- {% for link in sublinks|slice:"1:" %}, <a href="{{ link }}">[Link]</a>{% endfor %}
1962- </dd>
1963- {% endfor %}
1964- {% endif %}
1965- {% endfor %}
1966- </dl>
1967- {% endfor %}
1968- <br />
1969-{% endblock doc_body %}
1970-{% block doc_toc %}{% endblock %}
1971
1972=== removed file 'templates/sphinxdoc/py-modindex.html'
1973--- templates/sphinxdoc/py-modindex.html 2018-05-02 18:02:12 +0000
1974+++ templates/sphinxdoc/py-modindex.html 1970-01-01 00:00:00 +0000
1975@@ -1,23 +0,0 @@
1976-{% extends 'sphinxdoc/documentation.html' %}
1977-
1978-{% block doc_body %}
1979- <h1>Module Index</h1>
1980- <dl>
1981- {% for c in doc.content %}
1982- {% for modul, level, fname, mainmod, unknown1, unknown2, descr in c.1 %}
1983- {% if level < 2 %}
1984- <dt><a href="../{{ fname }}">{{ modul }}</a></dt>
1985- <dd>{{ descr }}
1986- {% else %}
1987- <dl>
1988- <dt><a href="../{{ fname }}">{{ modul }}</a></dt>
1989- <dd>{{ descr }}</dd>
1990- </dl>
1991- {% endif %}
1992- {% endfor %}
1993- </dd>
1994- {% endfor %}
1995- </dl>
1996- <br />
1997-{% endblock doc_body %}
1998-{% block doc_toc %}{% endblock %}
1999
2000=== modified file 'urls.py'
2001--- urls.py 2018-04-18 12:04:12 +0000
2002+++ urls.py 2018-05-24 19:17:56 +0000
2003@@ -38,9 +38,9 @@
2004
2005 url(r'^threadedcomments/', include('threadedcomments.urls')),
2006
2007- # Redirect old urls to docs to docs/wl
2008- url(r'^docs/$', RedirectView.as_view(url='/docs/wl', permanent=True), name='docs'),
2009- url(r'^docs/', include('sphinxdoc.urls')),
2010+ # Redirect old urls to new documentation
2011+ url(r'^docs/wl/(?P<path>.*)', RedirectView.as_view(url='/documentation/%(path)s', permanent=True), name='docs_wl'),
2012+ url(r'^docs/$', RedirectView.as_view(url='/documentation/index.html', permanent=True), name='docs'),
2013
2014 # 3rd party, modified for widelands
2015 url(r'^wiki/', include('wiki.urls')),

Subscribers

People subscribed via source and target branches

to status/vote changes: