Code

Ticket #16779: 16779.3.diff

File 16779.3.diff, 30.8 KB (added by timo, 20 months ago)

fixed typos

Line 
1diff --git a/docs/index.txt b/docs/index.txt
2index a6d9ed2..e6eb77c 100644
3--- a/docs/index.txt
4+++ b/docs/index.txt
5@@ -47,7 +47,8 @@ Are you new to Django or to programming? This is the place to start!
6   :doc:`Part 4 <intro/tutorial04>`
7 
8 * **Advanced Tutorials:**
9-  :doc:`How to write reusable apps <intro/reusable-apps>`
10+  :doc:`How to write reusable apps <intro/reusable-apps>` |
11+  :doc:`Writing your first patch for Django <intro/contributing>`
12 
13 The model layer
14 ===============
15diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
16index a828b06..71666a1 100644
17--- a/docs/internals/contributing/writing-code/unit-tests.txt
18+++ b/docs/internals/contributing/writing-code/unit-tests.txt
19@@ -38,6 +38,8 @@ with this sample ``settings`` module, ``cd`` into the Django
20 If you get an ``ImportError: No module named django.contrib`` error,
21 you need to add your install of Django to your ``PYTHONPATH``.
22 
23+.. _running-unit-tests-settings:
24+
25 Using another ``settings`` module
26 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 
28@@ -133,6 +135,8 @@ Then, run the tests normally, for example:
29 
30     ./runtests.py --settings=test_sqlite admin_inlines
31 
32+.. _running-unit-tests-dependencies:
33+
34 Running all the tests
35 ~~~~~~~~~~~~~~~~~~~~~
36 
37diff --git a/docs/intro/contributing.txt b/docs/intro/contributing.txt
38new file mode 100644
39index 0000000..ad91ac9
40--- /dev/null
41+++ b/docs/intro/contributing.txt
42@@ -0,0 +1,610 @@
43+===================================
44+Writing your first patch for Django
45+===================================
46+
47+Introduction
48+============
49+
50+Interested in giving back to the community a little? Maybe you've found a bug
51+in Django that you'd like to see fixed, or maybe there's a small feature you
52+want added.
53+
54+Contributing back to Django itself is the best way to see your own concerns
55+addressed. This may seem daunting at first, but it's really pretty simple.
56+We'll walk you through the entire process, so you can learn by example.
57+
58+Who's this tutorial for?
59+------------------------
60+
61+For this tutorial, we expect that you have at least a basic understanding of
62+how Django works. This means you should be comfortable going through the
63+existing tutorials on :doc:`writing your first Django app</intro/tutorial01>`.
64+In addition, you should have a good understanding of Python itself. But if you
65+don't, `Dive Into Python`__ is a fantastic (and free) online book for beginning
66+Python programmers.
67+
68+Those of you who are unfamiliar with version control systems and Trac will find
69+that this tutorial and its links include just enough information to get started.
70+However, you'll probably want to read some more about these different tools if
71+you plan on contributing to Django regularly.
72+
73+For the most part though, this tutorial tries to explain as much as possible,
74+so that it can be of use to the widest audience.
75+
76+.. admonition:: Where to get help:
77+
78+    If you're having trouble going through this tutorial, please post a message
79+    to `django-developers`__ or drop by `#django-dev on irc.freenode.net`__ to chat
80+    with other Django users who might be able to help.
81+
82+__ http://diveintopython.net/toc/index.html
83+__ http://groups.google.com/group/django-developers
84+__ irc://irc.freenode.net/django-dev
85+
86+What does this tutorial cover?
87+------------------------------
88+
89+We'll be walking you through contributing a patch to Django for the first time.
90+By the end of this tutorial, you should have a basic understanding of both the
91+tools and the processes involved. Specifically, we'll be covering the following:
92+
93+* Installing Git.
94+* How to download a development copy of Django.
95+* Running Django's test suite.
96+* Writing a test for your patch.
97+* Writing the code for your patch.
98+* Testing your patch.
99+* Generating a patch file for your changes.
100+* Where to look for more information.
101+
102+Once you're done with the tutorial, you can look through the rest of
103+:doc:`Django's documentation on contributing</internals/contributing/index>`.
104+It contains lots of great information and is a must read for anyone who'd like
105+to become a regular contributor to Django. If you've got questions, it's
106+probably got the answers.
107+
108+Installing Git
109+==============
110+
111+For this tutorial, you'll need Git installed to download the current
112+development version of Django and to generate patch files for the changes you
113+make.
114+
115+To check whether or not you have Git installed, enter ``git`` into the command
116+line. If you get messages saying that this command could be found, you'll have
117+to download and install it, see `Git's download page`__.
118+
119+If you're not that familiar with Git, you can always find out more about its
120+commands (once it's installed) by typing ``git help`` into the command line.
121+
122+__ http://git-scm.com/download
123+
124+Getting a copy of Django's development version
125+==============================================
126+
127+The first step to contributing to Django is to get a copy of the source code.
128+From the command line, use the ``cd`` command to navigate to the directory
129+where you'll want your local copy of Django to live.
130+
131+Download the Django source code repository using the following command::
132+
133+    git clone https://github.com/django/django.git
134+
135+.. note::
136+
137+    For advanced users who wish to use ``virtualenv``, you can use::
138+
139+        pip install -e git+https://github.com/django/django.git#egg=django
140+
141+    to install a live, editable source checkout into a virtual environment.
142+
143+Rolling back to a previous revision of Django
144+=============================================
145+
146+For this tutorial, we'll be using `ticket #17549`__ as a case study, so we'll
147+be using an older revision of Django from before that ticket's patch was
148+applied. This will allow us to go through all of the steps involved in writing
149+that patch from scratch, including running Django's test suite.
150+
151+**Keep in mind that while we'll be using an older revision of Django's trunk
152+for the purposes of the tutorial below, you should always use the current
153+development revision of Django when working on your own patch for a ticket!**
154+
155+.. note::
156+
157+    The patch for this ticket was written by Ulrich Petri, and it was applied
158+    to Django as `commit ac2052ebc84c45709ab5f0f25e685bf656ce79bc`__.
159+    Consequently, we'll be using the revision of Django just prior to that,
160+    `commit 39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac`__.
161+
162+__ https://code.djangoproject.com/ticket/17549
163+__ https://github.com/django/django/commit/ac2052ebc84c45709ab5f0f25e685bf656ce79bc
164+__ https://github.com/django/django/commit/39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac
165+
166+Navigate into Django's root directory (that's the one that contains ``django``,
167+``docs``, ``tests``, ``AUTHORS``, etc.). You can then check out the older
168+revision of Django that we'll be using in the tutorial below::
169+
170+    git checkout 39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac
171+
172+Running Django's test suite for the first time
173+==============================================
174+
175+When contributing to Django it's very important that your code changes don't
176+introduce bugs into other areas of Django.  One way to check that Django still
177+works after you make your changes is by running Django's test suite. If all
178+the tests still pass, then you can be reasonably sure that your changes
179+haven't completely broken Django. If you've never run Django's test suite
180+before, it's a good idea to run it once beforehand just to get familiar with
181+what its output is supposed to look like.
182+
183+Setting Django up to run the test suite
184+---------------------------------------
185+
186+.. note::
187+
188+    If you're using ``virtualenv``, you can skip this section. Using
189+    ``virtualenv`` with the ``--no-site-packages`` option isolates your
190+    copy of Django from the rest of your system and avoids potential conflicts.
191+
192+Before we can actually run the test suite, we need to make sure that your new
193+local copy of Django is on your ``PYTHONPATH``; otherwise, the test suite won't
194+run properly. We also need to make sure that there aren't any **other** copies
195+of Django installed somewhere else that are taking priority over your new
196+checkout (this happens more often than you might think). To check for these
197+problems, start up the Python interpreter and follow the code below::
198+
199+    >>> import django
200+    >>> django
201+    <module 'django' from '/.../django/__init__.pyc'>
202+
203+If you get an ``ImportError: No module named django`` after entering the first
204+line, then you'll need to add your new copy of Django to your ``PYTHONPATH``.
205+
206+If you didn't get any errors, then look at the path found in the third line
207+(abbreviated above as ``/.../django/__init__.pyc``). If that isn't the
208+directory that you put Django into earlier in this tutorial, then there is
209+**another** copy of Django on your ``PYTHONPATH`` that is taking priority over
210+the newer copy.  You'll either have to remove this older copy from your
211+``PYTHONPATH``, or add your new copy to the beginning of your ``PYTHONPATH``
212+so that it takes priority::
213+
214+    export PYTHONPATH=/path/to/django:$PYTHONPATH
215+
216+or on Windows::
217+
218+    set PYTHONPATH=C:\path\to\django;%PYTHONPATH%
219+
220+Running the full test suite
221+---------------------------
222+
223+Once Django is setup properly, we can actually run the test suite. Simply
224+``cd`` into the Django ``tests/`` directory and, if you're using GNU/Linux,
225+Mac OS X or some other flavor of Unix, run::
226+
227+    ./runtests.py --settings=test_sqlite
228+
229+If you're using Windows, you'll need to prefix ``python`` before
230+``runtests.py``. We're going to use the Unix style throughout the rest of the
231+tutorial, so just make this simple change wherever we are running tests, for
232+example::
233+
234+    python runtests.py --settings=test_sqlite
235+
236+If you get an ``ImportError: No module named django.contrib`` error, you still
237+need to add your current copy of Django to your ``PYTHONPATH`` (see above).
238+
239+Otherwise, sit back and relax. Django's entire test suite has over 4800
240+different tests, so it can take anywhere from 5 to 15 minutes to run,
241+depending on the speed of your computer.
242+
243+.. note::
244+
245+    While Django's test suite is running, you'll see a stream of characters
246+    representing the status of each test as it's run. ``E`` indicates that an
247+    error was raised during a test, and ``F`` indicates that a test's
248+    assertions failed. Both of these are considered to be test failures.
249+    Meanwhile, ``x`` and ``s`` indicated expected failures and skipped tests,
250+    respectively. Dots indicate passing tests.
251+
252+    Skipped tests are typically due to missing external libraries required to
253+    run the test; see :ref:`running-unit-tests-dependencies` for a list of
254+    dependencies and be sure to install any for tests related to the changes
255+    you are making (we won't need any for this tutorial).
256+
257+Once the tests complete, you should be greeted with a message informing you
258+whether the test suite passed or failed. Since you haven't yet made any changes
259+to Django's code, the entire test suite **should** pass. If you get failures or
260+errors make sure you've followed all of the previous steps properly. See
261+:ref:`running-unit-tests` for more information.
262+
263+Note that the latest Django trunk may not always be stable. When developing
264+against trunk, you can check `Django's continuous integration builds`__ to
265+determine if the failures are specific to your machine or if they are also
266+present in Django's official builds.
267+
268+__ http://ci.djangoproject.com/
269+
270+.. note::
271+
272+    For this tutorial and the ticket we're working on, testing against SQLite
273+    is sufficient, however, it's possible (and sometimes necessary) to
274+    :ref:`run the tests using a different database
275+    <running-unit-tests-settings>`.
276+
277+Writing some tests for your ticket
278+==================================
279+
280+In most cases, for a patch to be accepted into Django it has to include tests.
281+For bug fix patches, this means writing a regression test to ensure that the
282+bug is never reintroduced into Django later on. A regression test should be
283+written in such a way that it will fail while the bug still exists and pass
284+once the bug has been fixed. For patches containing new features, you'll need
285+to include tests which ensure that the new features are working correctly.
286+They too should fail when the new feature is not present, and then pass once it
287+has been implemented.
288+
289+A good way to do this is to write your new tests first, before making any
290+changes to the code. This style of development is called
291+`test-driven development`__ and can be applied to both entire projects and
292+single patches. After writing your tests, you then run them to make sure that
293+they do indeed fail (since you haven't fixed that bug or added that feature
294+yet). If your new tests don't fail, you'll need to fix them so that they do.
295+After all, a regression test that passes regardless of whether a bug is present
296+is not very helpful at preventing that bug from reoccurring down the road.
297+
298+Now for our hands-on example.
299+
300+__ http://en.wikipedia.org/wiki/Test-driven_development
301+
302+Writing some tests for ticket #17549
303+------------------------------------
304+
305+`Ticket #17549`__ describes the following, small feature addition:
306+
307+    It's useful for URLField to give you a way to open the URL; otherwise you
308+    might as well use a CharField.
309+
310+In order to resolve this ticket, we'll add a ``render`` method to the
311+``AdminURLFieldWidget`` in order to display a clickable link above the input
312+widget. Before we make those changes though, we're going to write a couple
313+tests to verify that our modification functions correctly and continues to
314+function correctly in the future.
315+
316+Navigate to Django's ``tests/regressiontests/admin_widgets/`` folder and
317+open the ``tests.py`` file. Add the following code on line 269 right before the
318+``AdminFileWidgetTest`` class::
319+
320+    class AdminURLWidgetTest(DjangoTestCase):
321+        def test_render(self):
322+            w = widgets.AdminURLFieldWidget()
323+            self.assertHTMLEqual(
324+                conditional_escape(w.render('test', '')),
325+                '<input class="vURLField" name="test" type="text" />'
326+            )
327+            self.assertHTMLEqual(
328+                conditional_escape(w.render('test', 'http://example.com')),
329+                '<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>'
330+            )
331+
332+        def test_render_idn(self):
333+            w = widgets.AdminURLFieldWidget()
334+            self.assertHTMLEqual(
335+                conditional_escape(w.render('test', 'http://example-äüö.com')),
336+                '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
337+            )
338+
339+        def test_render_quoting(self):
340+            w = widgets.AdminURLFieldWidget()
341+            self.assertHTMLEqual(
342+                conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
343+                '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
344+            )
345+            self.assertHTMLEqual(
346+                conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
347+                '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
348+            )
349+
350+The new tests check to see that the ``render`` method we'll be adding works
351+correctly in a couple different situations.
352+
353+.. admonition:: But this testing thing looks kinda hard...
354+
355+    If you've never had to deal with tests before, they can look a little hard
356+    to write at first glance. Fortunately, testing is a *very* big subject in
357+    computer programming, so there's lots of information out there:
358+
359+    * A good first look at writing tests for Django can be found in the
360+      documentation on :doc:`Testing Django applications</topics/testing/>`.
361+    * Dive Into Python (a free online book for beginning Python developers)
362+      includes a great `introduction to Unit Testing`__.
363+    * After reading those, if you want something a little meatier to sink
364+      your teeth into, there's always the `Python unittest documentation`__.
365+
366+__ https://code.djangoproject.com/ticket/17549
367+__ http://diveintopython.net/unit_testing/index.html
368+__ http://docs.python.org/library/unittest.html
369+
370+Running your new test
371+---------------------
372+
373+Remember that we haven't actually made any modifications to
374+``AdminURLFieldWidget`` yet, so our tests are going to fail. Let's run all the
375+tests in the ``model_forms_regress`` folder to make sure that's really what
376+happens. From the command line, ``cd`` into the Django ``tests/`` directory
377+and run::
378+
379+    ./runtests.py --settings=test_sqlite admin_widgets
380+
381+If the tests ran correctly, you should see three failures corresponding to each
382+of the test methods we added. If all of the tests passed, then you'll want to
383+make sure that you added the new test shown above to the appropriate folder and
384+class. It's also possible that you have a second copy of Django on your
385+``PYTHONPATH`` that is taking priority over this copy, and therefore it may be
386+that copy of Django whose tests are being run. To check if this might be the
387+problem, refer to the section `Setting Django up to run the test suite`_.
388+
389+Writing the code for your ticket
390+================================
391+
392+Next we'll be adding the functionality described in `ticket #17549`__ to Django.
393+
394+Writing the code for ticket #17549
395+----------------------------------
396+
397+Navigate to the ``django/django/contrib/admin/`` folder and open the
398+``widgets.py`` file. Find the ``AdminURLFieldWidget`` class on line 302 and add
399+the following ``render`` method after the existing ``__init__`` method::
400+
401+    def render(self, name, value, attrs=None):
402+        html = super(AdminURLFieldWidget, self).render(name, value, attrs)
403+        if value:
404+            value = force_text(self._format_value(value))
405+            final_attrs = {'href': mark_safe(smart_urlquote(value))}
406+            html = format_html(
407+                '<p class="url">{0} <a {1}>{2}</a><br />{3} {4}</p>',
408+                _('Currently:'), flatatt(final_attrs), value,
409+                _('Change:'), html
410+            )
411+        return html
412+
413+Verifying your test now passes
414+------------------------------
415+
416+Once you're done modifying Django, we need to make sure that the tests we wrote
417+earlier pass, so we can see whether the code we wrote above is working
418+correctly. To run the tests in the ``admin_widgets`` folder, ``cd`` into the
419+Django ``tests/`` directory and run::
420+
421+    ./runtests.py --settings=test_sqlite admin_widgets
422+
423+Oops, good thing we wrote those tests! You should still see 3 failures with
424+the following exception::
425+
426+    NameError: global name 'smart_urlquote' is not defined
427+
428+We forgot to add the import for that method.  Go ahead and add the
429+``smart_urlquote`` import at the end of line 13 of
430+``django/contrib/admin/widgets.py`` so it looks as follows::
431+
432+    from django.utils.html import escape, format_html, format_html_join, smart_urlquote
433+
434+Re-run the tests and everything should pass. If it doesn't, make sure you
435+correctly modified the ``AdminURLFieldWidget`` class as shown above and
436+copied the new tests correctly.
437+
438+__ https://code.djangoproject.com/ticket/17549
439+
440+Running Django's test suite for the second time
441+===============================================
442+
443+Once you've verified that your patch and your test are working correctly, it's
444+a good idea to run the entire Django test suite just to verify that your change
445+hasn't introduced any bugs into other areas of Django. While successfully
446+passing the entire test suite doesn't guarantee your code is bug free, it does
447+help identify many bugs and regressions that might otherwise go unnoticed.
448+
449+To run the entire Django test suite, ``cd`` into the Django ``tests/``
450+directory and run::
451+
452+    ./runtests.py --settings=test_sqlite
453+
454+As long as you don't see any failures, you're good to go. Note that this fix
455+also made a `small CSS change`__ to format the new widget. You can make the
456+change if you'd like, but we'll skip it for now in the interest of brevity.
457+
458+__ https://github.com/django/django/commit/ac2052ebc84c45709ab5f0f25e685bf656ce79bc#diff-0
459+
460+Writing Documentation
461+=====================
462+
463+This is a new feature, so it should be documented.  Add the following on line
464+925 of ``django/docs/ref/models/fields.txt`` beneath the existing docs for
465+``URLField``::
466+
467+    .. versionadded:: 1.5
468+
469+    The current value of the field will be displayed as a clickable link above the
470+    input widget.
471+
472+For more information on writing documentation, including an explanation of what
473+the ``versionadded`` bit is all about, see
474+:doc:`/internals/contributing/writing-documentation`. That page also includes
475+an explanation of how to build a copy of the documentation locally, so you can
476+preview the HTML that will be generated.
477+
478+Generating a patch for your changes
479+===================================
480+
481+Now it's time to generate a patch file that can be uploaded to Trac or applied
482+to another copy of Django. To get a look at the content of your patch, run the
483+following command::
484+
485+    git diff
486+
487+This will display the differences between your current copy of Django (with
488+your changes) and the revision that you initially checked out earlier in the
489+tutorial.
490+
491+Once you're done looking at the patch, hit the ``q`` key to exit back to the
492+command line.  If the patch's content looked okay, you can run the following
493+command to save the patch file to your current working directory::
494+
495+    git diff > 17549.diff
496+
497+You should now have a file in the root Django directory called ``17549.diff``.
498+This patch file contains all your changes and should look this:
499+
500+.. code-block:: diff
501+
502+    diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
503+    index 1e0bc2d..9e43a10 100644
504+    --- a/django/contrib/admin/widgets.py
505+    +++ b/django/contrib/admin/widgets.py
506+    @@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static
507+     from django.core.urlresolvers import reverse
508+     from django.forms.widgets import RadioFieldRenderer
509+     from django.forms.util import flatatt
510+    -from django.utils.html import escape, format_html, format_html_join
511+    +from django.utils.html import escape, format_html, format_html_join, smart_urlquote
512+     from django.utils.text import Truncator
513+     from django.utils.translation import ugettext as _
514+     from django.utils.safestring import mark_safe
515+    @@ -306,6 +306,18 @@ class AdminURLFieldWidget(forms.TextInput):
516+                 final_attrs.update(attrs)
517+             super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
518+
519+    +    def render(self, name, value, attrs=None):
520+    +        html = super(AdminURLFieldWidget, self).render(name, value, attrs)
521+    +        if value:
522+    +            value = force_text(self._format_value(value))
523+    +            final_attrs = {'href': mark_safe(smart_urlquote(value))}
524+    +            html = format_html(
525+    +                '<p class="url">{0} <a {1}>{2}</a><br />{3} {4}</p>',
526+    +                _('Currently:'), flatatt(final_attrs), value,
527+    +                _('Change:'), html
528+    +            )
529+    +        return html
530+    +
531+     class AdminIntegerFieldWidget(forms.TextInput):
532+         class_name = 'vIntegerField'
533+
534+    diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
535+    index 809d56e..d44f85f 100644
536+    --- a/docs/ref/models/fields.txt
537+    +++ b/docs/ref/models/fields.txt
538+    @@ -922,6 +922,10 @@ Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
539+     :attr:`~CharField.max_length`argument. If you don't specify
540+     :attr:`~CharField.max_length`, a default of 200 is used.
541+
542+    +.. versionadded:: 1.5
543+    +
544+    +The current value of the field will be displayed as a clickable link above the
545+    +input widget.
546+
547+     Relationship fields
548+     ===================
549+    diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
550+    index 4b11543..94acc6d 100644
551+    --- a/tests/regressiontests/admin_widgets/tests.py
552+    +++ b/tests/regressiontests/admin_widgets/tests.py
553+    @@ -265,6 +265,35 @@ class AdminSplitDateTimeWidgetTest(DjangoTestCase):
554+                         '<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>',
555+                     )
556+
557+    +class AdminURLWidgetTest(DjangoTestCase):
558+    +    def test_render(self):
559+    +        w = widgets.AdminURLFieldWidget()
560+    +        self.assertHTMLEqual(
561+    +            conditional_escape(w.render('test', '')),
562+    +            '<input class="vURLField" name="test" type="text" />'
563+    +        )
564+    +        self.assertHTMLEqual(
565+    +            conditional_escape(w.render('test', 'http://example.com')),
566+    +            '<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>'
567+    +        )
568+    +
569+    +    def test_render_idn(self):
570+    +        w = widgets.AdminURLFieldWidget()
571+    +        self.assertHTMLEqual(
572+    +            conditional_escape(w.render('test', 'http://example-äüö.com')),
573+    +            '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
574+    +        )
575+    +
576+    +    def test_render_quoting(self):
577+    +        w = widgets.AdminURLFieldWidget()
578+    +        self.assertHTMLEqual(
579+    +            conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
580+    +            '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
581+    +        )
582+    +        self.assertHTMLEqual(
583+    +            conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
584+    +            '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
585+    +        )
586+
587+     class AdminFileWidgetTest(DjangoTestCase):
588+         def test_render(self):
589+
590+So what do I do next?
591+=====================
592+
593+Congratulations, you've generated your very first Django patch! Now that you've
594+got that under your belt, you can put those skills to good use by helping to
595+improve Django's codebase. Generating patches and attaching them to Trac
596+tickets is useful, however, you can also :doc:`submit pull requests on Github
597+</internals/contributing/writing-code/working-with-git>`.
598+
599+More information for new contributors
600+-------------------------------------
601+
602+Before you get too into writing patches for Django, there's a little more
603+information on contributing that you should probably take a look at:
604+
605+* You should make sure to read Django's documentation on
606+  :doc:`claiming tickets and submitting patches
607+  </internals/contributing/writing-code/submitting-patches>`.
608+  It covers Trac etiquette, how to claim tickets for yourself, expected
609+  coding style for patches, and many other important details.
610+* First time contributors should also read Django's :doc:`documentation
611+  for first time contributors</internals/contributing/new-contributors/>`.
612+  It has lots of good advice for those of us who are new to helping out
613+  with Django.
614+* After those, if you're still hungry for more information about
615+  contributing, you can always browse through the rest of
616+  :doc:`Django's documentation on contributing</internals/contributing/index>`.
617+  It contains a ton of useful information and should be your first source
618+  for answering any questions you might have.
619+
620+Finding your first real ticket
621+------------------------------
622+
623+Once you've looked through some of that information, you'll be ready to go out
624+and find a ticket of your own to write a patch for. Pay special attention to
625+tickets with the "easy pickings" criterion. These tickets are often much
626+simpler in nature and are great for first time contributors.  Once you're
627+familiar with contributing to Django, you can move on to writing patches for
628+more difficult and complicated tickets.
629+
630+If you just want to get started already (and nobody would blame you!), try
631+taking a look at the list of `easy tickets that need patches`__ and the
632+`easy tickets that have patches which need improvement`__. If you're familiar
633+with writing tests, you can also look at the list of
634+`easy tickets that need tests`__. Just remember to follow the guidelines about
635+claiming tickets that were mentioned in the link to Django's documentation on
636+:doc:`claiming tickets and submitting patches
637+</internals/contributing/writing-code/submitting-patches>`.
638+
639+__ https://code.djangoproject.com/query?status=new&status=reopened&has_patch=0&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
640+__ https://code.djangoproject.com/query?status=new&status=reopened&needs_better_patch=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
641+__ https://code.djangoproject.com/query?status=new&status=reopened&needs_tests=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
642+
643+What's next?
644+------------
645+
646+After a ticket has a patch, it needs to be reviewed by a second set of eyes.
647+After uploading a patch or submitting a pull request, be sure to update the
648+ticket metadata by setting the flags on the ticket to say "has patch",
649+"doesn't need tests", etc, so others can find it for review. Contributing
650+doesn't necessarily always mean writing a patch from scratch. Reviewing
651+existing patches is also a very helpful contribution. See
652+:doc:`/internals/contributing/triaging-tickets` for details.
653diff --git a/docs/intro/index.txt b/docs/intro/index.txt
654index afb1825..bca2d77 100644
655--- a/docs/intro/index.txt
656+++ b/docs/intro/index.txt
657@@ -15,6 +15,7 @@ place: read this material to quickly get up and running.
658    tutorial04
659    reusable-apps
660    whatsnext
661+   contributing
662 
663 .. seealso::
664