James Bennett avatar James Bennett committed 1e2ce61

Initial Mercurial import

Comments (0)

Files changed (12)

+=========================
+Installation instructions
+=========================
+
+There are two ways to install this application for use by your
+projects; the easiest in most cases is to do a Subversion checkout
+into a directory that's on your Python path::
+
+    svn co http://django-contact-form.googlecode.com/svn/trunk/contact_form/
+
+The other method is to download a packaged version and use Python's
+``distutils`` to install it onto your Python path::
+
+    wget http://django-contact-form.googlecode.com/files/contact_form-0.3.tar.gz
+    tar zxvf contact_form-0.3.tar.gz
+    cd contact-form-0.3
+    python setup.py install
+
+Depending on your system configuration, you may need to prefix the
+last command with ``sudo`` and supply your password to perform a
+system-wide installation.
+Copyright (c) 2007, James Bennett
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+include INSTALL.txt
+include LICENSE.txt
+include README.txt
+include MANIFEST.in
+recursive-include docs *
+================================
+Generic contact forms for Django
+================================
+
+
+This is an application which provides generic, extensible contact-form
+functionality for use in Django_ projects; for installation
+instructions, see the file ``INSTALL.txt`` in this directory, and for
+documentation see the files in the ``docs/`` directory.
+
+The latest versions of these documents can always be viewed on the
+Google Code project web site for this application, which is located at
+http://code.google.com/p/django-contact/form/.
+
+.. _Django: http://www.djangoproject.com/
Add a comment to this file

contact_form/__init__.py

Empty file added.

contact_form/forms.py

+"""
+A base contact form for allowing users to send email messages through
+a web interface, and a subclass demonstrating useful functionality.
+
+"""
+
+
+from django import forms
+from django.conf import settings
+from django.core.mail import send_mail
+from django.template import loader, RequestContext
+from django.contrib.sites.models import Site
+
+
+# I put this on all required fields, because it's easier to pick up
+# on them with CSS or JavaScript if they have a class of "required"
+# in the HTML. Your mileage may vary.
+attrs_dict = { 'class': 'required' }
+
+
+class ContactForm(forms.Form):
+    """
+    Base contact form class from which all contact form classes should
+    inherit.
+    
+    If you don't need any custom functionality, you can simply use
+    this form to provide basic contact functionality; it will collect
+    name, email address and message.
+    
+    The ``contact_form`` view included in this application knows how
+    to work with this form and can handle many types of subclasses as
+    well (see below for a discussion of the important points), so in
+    many cases it will be all that you need. If you'd like to use this
+    form or a subclass of it from one of your own views, just do the
+    following:
+    
+        1. When you instantiate the form, pass the current
+           ``HttpRequest`` object to the constructor as the keyword
+           argument ``request``; this is used internally by the base
+           implementation, and also made available so that subclasses
+           can add functionality which relies on inspecting the
+           request.
+           
+        2. To send the message, call the form's ``save`` method, which
+           accepts the keyword argument ``fail_silently`` and defaults
+           it to ``False``. This argument is passed directly to
+           ``send_mail``, and allows you to suppress or raise
+           exceptions as needed for debugging. The ``save`` method has
+           no return value.
+           
+    Other than that, treat it like any other form; validity checks and
+    validated data are handled normally, through the ``is_valid``
+    method and the ``cleaned_data`` dictionary.
+    
+    
+    Base implementation
+    -------------------
+    
+    Under the hood, this form uses a somewhat abstracted interface in
+    order to make it easier to subclass and add functionality. There
+    are several important attributes subclasses may want to look at
+    overriding, all of which will work (in the base implementation) as
+    either plain attributes or as callable methods:
+    
+        * ``from_email`` -- used to get the address to use in the
+          ``From:`` header of the message. The base implementation
+          returns the value of the ``DEFAULT_FROM_EMAIL`` setting.
+          
+        * ``message`` -- used to get the message body as a string. The
+          base implementation renders a template using the form's
+          ``cleaned_data`` dictionary as context.
+          
+        * ``recipient_list`` -- used to generate the list of
+          recipients for the message. The base implementation returns
+          the email addresses specified in the ``MANAGERS`` setting.
+          
+        * ``subject`` -- used to generate the subject line for the
+          message. The base implementation returns the string 'Message
+          sent through the web site', with the name of the current
+          ``Site`` prepended.
+          
+        * ``template_name`` -- used by the base ``message`` method to
+          determine which template to use for rendering the
+          message. Default is ``contact_form/contact_form.txt``.
+          
+    Internally, the base implementation ``_get_message_dict`` method
+    collects ``from_email``, ``message``, ``recipient_list`` and
+    ``subject`` into a dictionary, which the ``save`` method then
+    passes directly to ``send_mail`` as keyword arguments.
+    
+    Particularly important is the ``message`` attribute, with its base
+    implementation as a method which renders a template; because it
+    passes ``cleaned_data`` as the template context, any additional
+    fields added by a subclass will automatically be available in the
+    template. This means that many useful subclasses can get by with
+    just adding a few fields and possibly overriding
+    ``template_name``.
+    
+    Much useful functionality can be achieved in subclasses without
+    having to override much of the above; adding additional validation
+    methods works the same as any other form, and typically only a few
+    items -- ``recipient_list`` and ``subject_line``, for example,
+    need to be overridden to achieve customized behavior.
+    
+    
+    Other notes for subclassing
+    ---------------------------
+    
+    Subclasses which want to inspect the current ``HttpRequest`` to
+    add functionality can access it via the attribute ``request``; the
+    base ``message`` takes advantage of this to use ``RequestContext``
+    when rendering its template. See the ``AkismetContactForm``
+    subclass in this file for an example of using the request to
+    perform additional validation.
+    
+    Subclasses which override ``__init__`` need to accept ``*args``
+    and ``**kwargs``, and pass them via ``super`` in order to ensure
+    proper behavior.
+    
+    Subclasses should be careful if overriding ``_get_message_dict``,
+    since that method **must** return a dictionary suitable for
+    passing directly to ``send_mail`` (unless ``save`` is overridden
+    as well).
+    
+    Overriding ``save`` is relatively safe, though remember that code
+    which uses your form will expect ``save`` to accept the
+    ``fail_silently`` keyword argument. In the base implementation,
+    that argument defaults to ``False``, on the assumption that it's
+    far better to notice errors than to silently not send mail from
+    the contact form (see also the Zen of Python: "Errors should never
+    pass silently, unless explicitly silenced").
+    
+    """
+    def __init__(self, data=None, files=None, request=None, *args, **kwargs):
+        if request is None:
+            raise TypeError("Keyword argument 'request' must be supplied")
+        super(ContactForm, self).__init__(data=data, files=files, *args, **kwargs)
+        self.request = request
+    
+    name = forms.CharField(max_length=100,
+                           widget=forms.TextInput(attrs=attrs_dict),
+                           label=u'Your name')
+    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
+                                                               maxlength=200)),
+                             label=u'Your email address')
+    body = forms.CharField(widget=forms.Textarea(attrs=attrs_dict),
+                              label=u'Your message')
+    
+    from_email = settings.DEFAULT_FROM_EMAIL
+    
+    recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]
+
+    subject_template_name = "contact_form/contact_form_subject.txt"
+    
+    template_name = 'contact_form/contact_form.txt'
+
+    _context = None
+    
+    def message(self):
+        """
+        Renders the body of the message to a string.
+        
+        """
+        if callable(self.template_name):
+            template_name = self.template_name()
+        else:
+            template_name = self.template_name
+        return loader.render_to_string(template_name,
+                                       self.get_context())
+    
+    def subject(self):
+        """
+        Renders the subject of the message to a string.
+        
+        """
+        subject = loader.render_to_string(self.subject_template_name,
+                                          self.get_context())
+        return ''.join(subject.splitlines())
+    
+    def get_context(self):
+        if not self.is_valid():
+            raise ValueError("Cannot generate Context from invalid contact form")
+        if self._context is None:
+            self._context = RequestContext(self.request,
+                                           dict(self.cleaned_data,
+                                                site=Site.objects.get_current()))
+        return self._context
+    
+    def get_message_dict(self):
+        if not self.is_valid():
+            raise ValueError("Message cannot be sent from invalid contact form")
+        message_dict = {}
+        for message_part in ('from_email', 'message', 'recipient_list', 'subject'):
+            attr = getattr(self, message_part)
+            message_dict[message_part] = callable(attr) and attr() or attr
+        return message_dict
+    
+    def save(self, fail_silently=False):
+        """
+        Builds and sends the email message.
+        
+        """
+        send_mail(fail_silently=fail_silently, **self.get_message_dict())
+
+
+class AkismetContactForm(ContactForm):
+    """
+    Contact form which doesn't add any extra fields, but does add an
+    Akismet spam check to the validation routine.
+    
+    Requires the setting ``AKISMET_API_KEY``, which should be a valid
+    Akismet API key.
+    
+    """
+    def clean_body(self):
+        if 'body' in self.cleaned_data and getattr(settings, 'AKISMET_API_KEY', ''):
+            from akismet import Akismet
+            from django.utils.encoding import smart_str
+            akismet_api = Akismet(key=settings.AKISMET_API_KEY,
+                                  blog_url='http://%s/' % Site.objects.get_current().domain)
+            if akismet_api.verify_key():
+                akismet_data = { 'comment_type': 'comment',
+                                 'referer': self.request.META.get('HTTP_REFERER', ''),
+                                 'user_ip': self.request.META.get('REMOTE_ADDR', ''),
+                                 'user_agent': self.request.META.get('HTTP_USER_AGENT', '') }
+                if akismet_api.comment_check(smart_str(self.cleaned_data['body']), data=akismet_data, build_data=True):
+                    raise forms.ValidationError(u"Akismet thinks this message is spam")
+        return self.cleaned_data['body']

contact_form/urls.py

+"""
+Example URLConf for a contact form.
+
+Because the ``contact_form`` view takes configurable arguments, it's
+recommended that you manually place it somewhere in your URL
+configuration with the arguments you want. If you just prefer the
+default, however, you can hang this URLConf somewhere in your URL
+hierarchy (for best results with the defaults, include it under
+``/contact/``).
+
+"""
+
+
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+from contact_form.views import contact_form
+
+
+urlpatterns = patterns('',
+                       url(r'^$',
+                           contact_form,
+                           name='contact_form'),
+                       url(r'^sent/$',
+                           direct_to_template,
+                           { 'template': 'contact_form/contact_form_sent.html' },
+                           name='contact_form_sent'),
+                       )

contact_form/views.py

+"""
+View which can render and send email from a contact form.
+
+"""
+
+
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.contrib.auth.views import redirect_to_login
+
+from contact_form.forms import ContactForm
+
+
+def contact_form(request, form_class=ContactForm,
+                 template_name='contact_form/contact_form.html',
+                 success_url='/contact/sent/', login_required=False,
+                 fail_silently=False):
+    """
+    Renders a contact form, validates its input and sends an email
+    from it.
+    
+    To specify the form class to use, pass the ``form_class`` keyword
+    argument; if no ``form_class`` is specified, the base
+    ``ContactForm`` class will be used.
+    
+    To specify the template to use for rendering the form (*not* the
+    template used to render the email message sent from the form,
+    which is handled by the form class), pass the ``template_name``
+    keyword argument; if not supplied, this will default to
+    ``contact_form/contact_form.html``.
+    
+    To specify a URL to redirect to after a successfully-sent message,
+    pass the ``success_url`` keyword argument; if not supplied, this
+    will default to ``/contact/sent/``.
+    
+    To allow only registered users to use the form, pass a ``True``
+    value for the ``login_required`` keyword argument.
+    
+    To suppress exceptions raised during sending of the email, pass a
+    ``True`` value for the ``fail_silently`` keyword argument. This is
+    **not** recommended.
+    
+    Template::
+    
+        Passed in the ``template_name`` argument.
+        
+    Context::
+    
+        form
+            The form instance.
+    
+    """
+    if login_required and not request.user.is_authenticated():
+        return redirect_to_login(request.path)
+    
+    if request.method == 'POST':
+        form = form_class(data=request.POST, files=request.FILES, request=request)
+        if form.is_valid():
+            form.save(fail_silently=fail_silently)
+            return HttpResponseRedirect(success_url)
+    else:
+        form = form_class(request=request)
+    return render_to_response(template_name,
+                              { 'form': form },
+                              context_instance=RequestContext(request))
+=====================
+Included form classes
+=====================
+
+
+Two form classes are included with this application; one,
+``contact_form.forms.ContactForm`` implements the necessary base
+functionality of a contact form, and other contact-form classes should
+inherit from it to receive that functionality. The other,
+contact_form.forms.AkismetContactForm``, is a subclass of
+``ContactForm`` provided both because it is useful and as a
+demonstration of subclassing ``ContactForm``.
+
+
+``contact_form.forms.ContactForm``
+==================================
+
+Base contact form class from which all contact form classes should
+inherit.
+
+If you don't need any custom functionality, you can simply use this
+form to provide basic contact functionality; it will collect name,
+email address and message.
+
+The ``contact_form`` view included in this application knows how to
+work with this form and can handle many types of subclasses as well
+(see below for a discussion of the important points), so in many cases
+it will be all that you need. If you'd like to use this form or a
+subclass of it from one of your own views, just do the following:
+
+    1. When you instantiate the form, pass the current ``HttpRequest``
+       object to the constructor as the keyword argument ``request``;
+       this is used internally by the base implementation, and also
+       made available so that subclasses can add functionality which
+       relies on inspecting the request.
+       
+    2. To send the message, call the form's ``save`` method, which
+       accepts the keyword argument ``fail_silently`` and defaults it
+       to ``False``. This argument is passed directly to
+       ``send_mail``, and allows you to suppress or raise exceptions
+       as needed for debugging. The ``save`` method has no return
+       value.
+       
+Other than that, treat it like any other form; validity checks and
+validated data are handled normally, through the ``is_valid`` method
+and the ``cleaned_data`` dictionary.
+
+
+Base implementation
+-------------------
+
+Under the hood, this form uses a somewhat abstracted interface in
+order to make it easier to subclass and add functionality. There are
+several important attributes subclasses may want to look at
+overriding, all of which will work (in the base implementation) as
+either plain attributes or as callable methods:
+
+    * ``from_email`` -- used to get the address to use in the
+      ``From:`` header of the message. The base implementation returns
+      the value of the ``DEFAULT_FROM_EMAIL`` setting.
+      
+    * ``message`` -- used to get the message body as a string. The
+      base implementation renders a template using the form's
+      ``cleaned_data`` dictionary as context.
+      
+    * ``recipient_list`` -- used to generate the list of recipients
+      for the message. The base implementation returns the email
+      addresses specified in the ``MANAGERS`` setting.
+      
+    * ``subject`` -- used to generate the subject line for the
+      message. The base implementation returns the string 'Message
+      sent through the web site', with the name of the current
+      ``Site`` prepended.
+      
+    * ``template_name`` -- used by the base ``ContactForm`` class to
+      determine which template to use for rendering the
+      message. Default is ``contact_form/contact_form.txt``.
+    
+    * ``subject_template_name`` -- used by the base ``ContactForm``
+      class to determine which template to use for rendering the
+      message's subject line. Regardless of the output of rendering
+      this template, it will be condensed to a single line of text;
+      multi-line subjects are not permitted. Default is
+      ``contact_form/contact_form_subject.txt``.
+      
+Internally, the base implementation ``_get_message_dict`` method
+collects ``from_email``, ``message``, ``recipient_list`` and
+``subject`` into a dictionary, which the ``save`` method then passes
+directly to ``send_mail`` as keyword arguments.
+
+Particularly important is the ``message`` attribute, with its base
+implementation as a method which renders a template, and the
+``subject`` attribute which defaults to a similar method; because they
+pass ``cleaned_data`` as the template context, any additional fields
+added by a subclass will automatically be available in the
+template. This means that many useful subclasses can get by with just
+adding a few fields and possibly overriding ``template_name`` and/or
+``subject_template_name``.
+
+Much useful functionality can be achieved in subclasses without having
+to override much of the above; adding additional validation methods
+works the same as any other form, and typically only a few items --
+``recipient_list`` and ``subject_line``, for example, need to be
+overridden to achieve customized behavior.
+
+
+Other notes for subclassing
+---------------------------
+
+Subclasses which want to inspect the current ``HttpRequest`` to add
+functionality can access it via the attribute ``request``; the base
+``message`` and ``subject`` take advantage of this to use
+``RequestContext`` when rendering their templates. See the
+``AkismetContactForm`` subclass in this file for an example of using
+the request to perform additional validation.
+
+Subclasses which override ``__init__`` need to accept ``*args`` and
+``**kwargs``, and pass them via ``super`` in order to ensure proper
+behavior.
+
+Subclasses should be careful if overriding ``_get_message_dict``,
+since that method **must** return a dictionary suitable for passing
+directly to ``send_mail`` (unless ``save`` is overridden as well).
+
+Overriding ``save`` is relatively safe, though remember that code
+which uses your form will expect ``save`` to accept the
+``fail_silently`` keyword argument. In the base implementation, that
+argument defaults to ``False``, on the assumption that it's far better
+to notice errors than to silently not send mail from the contact form
+(see also the Zen of Python: "Errors should never pass silently,
+unless explicitly silenced").
+
+
+``contact_form.forms.AkismetContactForm``
+=========================================
+
+Contact form which doesn't add any extra fields, but does add an
+Akismet spam check to the validation routine.
+
+Requires the setting ``AKISMET_API_KEY``, which should be a valid
+Akismet API key.

docs/overview.txt

+================================
+Generic contact forms for Django
+================================
+
+
+Providing some sort of contact or feedback form for soliciting
+information from site visitors is a common need in web development,
+and writing a contact form and associated handler view, while
+relatively straightforward to do with Django, can be a tedious and
+repetitive task. This application aims to remove or reduce that tedium
+and repetition by providing generic contact-form functionality.
+
+
+Requirements
+============
+
+This application makes heavy use of Django's newforms library, and was
+written *after* a backwards-incompatible change on Django trunk
+altered the way newforms stores valid data; as a result, this
+application requires a Subversion checkout of Django, revision 5237 or
+later.
+
+Because a recent trunk checkout of Django was already required, the
+default ``urls.py`` supplied with this application also takes
+advantage of named URL patterns, a feature which was not present in
+the Django 0.96 release.
+
+If you will be using the included ``AkismetContactForm`` class, which
+performs an Akismet spam check as part of its validation, you will
+need the `Python Akismet module`_ and a valid Akismet API key; you can
+obtain an Akismet API by following the instructions at `the Akismet
+web site`_.
+
+.. _Python Akismet module: http://www.voidspace.org.uk/python/modules.shtml#akismet
+.. _the Akismet web site: http://akismet.com/
+
+
+High-level overview
+===================
+
+This application contains a (newforms) form class called
+``ContactForm``, which implements a useful baseline for contact forms
+-- collecting a name, email address and message, and emailing them to
+site staff -- and which is also designed so as to allow additional
+functionality to be added easily in subclasses. For specifics of how
+``ContactForm`` works and what to look at when subclassing it, see the
+`forms documentation`_ included with this application.
+
+Also provided is a generic-style view called ``contact_form``, which
+is designed to work out-of-the-box with ``ContactForm`` and subclasses
+of ``ContactForm``, and has a number of configurable parameters to
+allow specification of the form class to use, whether to require a
+user to log in before using the form, etc. For full details on this
+view and the options it provides, see the `views_documentation`_
+included with this application.
+
+A sample URLConf is included, and can be used "as-is" if the default
+``ContactForm`` and default parameters of the ``contact_form`` view
+are all that's required, or studied as an example.
+
+.. _forms documentation: forms.html
+.. _views documentation: views.html
+
+
+Basic usage
+===========
+
+To get up and running immediately using the default setup, do the
+following:
+
+    * Add ``contact_form`` to your project's ``INSTALLED_APPS``
+      setting. You will *not* need to run ``manage.py syncdb``, since
+      this application provides no models.
+
+    * In your root URLConf, add the following URL pattern::
+          
+          (r'^contact/', include('contact_form.urls'),
+    
+    * Create four templates: ``contact_form/contact_form_subject.txt``
+      and ``contact_form/contact_form.txt``, which will be used to
+      render the email messages sent by the form;
+      ``contact_form/contact_form.html``, which will be used to
+      display the form to users; and
+      ``contact_form/contact_form_sent.html``, which will be used
+      after the form is successfully submitted.  See the forms and
+      views documentation, respectively, for details on the contexts
+      available to the first three templates; the fourth is rendered
+      from the ``direct_to_template`` generic view, and has no
+      context.
+
+Once this is done, visiting the URL ``/contact/`` on your site will
+display the contact form, and submitting it will send an email to each
+address in the ``MANAGERS`` setting for your project.
+==============
+Included views
+==============
+
+
+One view is included with this application:
+``contact_form.views.contact_form``, which knows how to work with
+``contact_form.forms.ContactForm`` and its subclasses, and takes
+several useful keyword arguments for specifying behavior.
+
+
+``contact_form.views.contact_form``
+====================================
+
+Renders a contact form, validates its input and sends an email from
+it.
+
+To specify the form class to use, pass the ``form_class`` keyword
+argument; if no ``form_class`` is specified, the base ``ContactForm``
+class will be used.
+
+To specify the template to use for rendering the form (*not* the
+template used to render the email message sent from the form, which is
+handled by the form class), pass the ``template_name`` keyword
+argument; if not supplied, this will default to
+``contact/contact_form.html``.
+
+To specify a URL to redirect to after a successfully-sent message,
+pass the ``success_url`` keyword argument; if not supplied, this will
+default to ``/contact/sent/``.
+
+To allow only registered users to use the form, pass a ``True`` value
+for the ``login_required`` keyword argument.
+
+To suppress exceptions raised during sending of the email, pass a
+``True`` value for the ``fail_silently`` keyword argument. This is
+**not** recommended.
+    
+Template::
+
+    Passed in the ``template_name`` argument.
+    
+Context::
+
+    form
+        The form instance.
+from distutils.core import setup
+
+setup(name='contact_form',
+      version='0.3',
+      description='Generic contact-form application for Django',
+      author='James Bennett',
+      author_email='james@b-list.org',
+      url='http://code.google.com/p/django-contact-form/',
+      packages=['contact_form'],
+      classifiers=['Development Status :: 4 - Beta',
+                   'Environment :: Web Environment',
+                   'Intended Audience :: Developers',
+                   'License :: OSI Approved :: BSD License',
+                   'Operating System :: OS Independent',
+                   'Programming Language :: Python',
+                   'Topic :: Utilities'],
+      )
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.