diff --git a/_layouts/page.html b/_layouts/page.html
index 0035fb4a739b69943d38db19036a5b7d83aad0bb..6607cab01409b2f0e19e88aa022e90da2c012040 100644
--- a/_layouts/page.html
+++ b/_layouts/page.html
@@ -13,7 +13,7 @@
         <div class="span10 offset1">
           <h1 class="title">$title$</h1>
           $if(subtitle)$<h2 class="subtitle">$subtitle$</h2>$endif$
-          $body$
+$body$
         </div>
       </div>
       $footer$
diff --git a/_layouts/slides.html b/_layouts/slides.html
index 581bc990d13dcd9f1a61e336f5cee60a9342aa43..61718627f9fcd65dbed88665a3cfaeecb9818242 100644
--- a/_layouts/slides.html
+++ b/_layouts/slides.html
@@ -33,7 +33,7 @@
   <div class="deck-container">
 
     <!-- Begin slides. Just make elements with a class of slide. -->
-    $body$
+$body$
     <!-- End slides. -->
 
     <!-- Begin extension snippets. Add or remove as needed. -->
diff --git a/pages/01-one.md b/pages/01-one.md
index 9857e506196cf33aa47efc55270ebbe129dc1e0e..ca4dc268d6b1539b9c418bc9c33993ebebd9c70e 100644
--- a/pages/01-one.md
+++ b/pages/01-one.md
@@ -4,7 +4,7 @@ title: Lesson Title
 subtitle: Topic Title One
 minutes: 10
 ---
-> ## Learning Objectives {.objectives}
+> ## Learning Objectives
 >
 > * Learning objective 1
 > * Learning objective 2
diff --git a/pages/02-two.md b/pages/02-two.md
index b3fdb641a980390f5adf514f8f38b8baaceafeb0..0412d1bb186e0dc2d7bba0c31f4d1b0c242f6441 100644
--- a/pages/02-two.md
+++ b/pages/02-two.md
@@ -4,7 +4,7 @@ title: Lesson Title
 subtitle: Topic Title Two
 minutes: 10
 ---
-> ## Learning Objectives {.objectives}
+> ## Learning Objectives
 >
 > * Learning objective 1
 > * Learning objective 2
diff --git a/tools/blockquote2div.py b/tools/blockquote2div.py
old mode 100644
new mode 100755
index a23109f076b77efe474ed58fceee748fdabb5fc1..6797c86472c92a6526d6f572d0d424f9b2a24638
--- a/tools/blockquote2div.py
+++ b/tools/blockquote2div.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 """Pandoc filter to convert Blockquotes with attributes into Div
 with attributes.
 
@@ -7,10 +8,11 @@ Usage:
 
 A blockquote will be converted if
 
-1. it begins with a header
-2. that header has attributes
-3. those attributes contain a single class
-4. that class is one of ['objectives', 'callout', 'challenge']
+1.  it begins with a header
+2.  that either
+    1.  matches "Prerequisites", "Objectives", "Callout" or "Challenge" OR
+    2.  has attributes containing a single class matching
+        one of ['prereq', 'objectives', 'callout', 'challenge']
 
 For example, this is a valid blockquote:
 
@@ -24,6 +26,18 @@ and it will be converted into this markdown:
     Let's do something.
     </div>
 
+This is also a valid blockquote:
+
+    > ## Prerequisites
+    > Breakfast!
+
+and it will be converted into this markdown:
+
+    <div class='prereq'>
+    ## Prerequisites
+    Breakfast!
+    </div>
+
 
 For debugging purposes you may find it useful to test the filter
 like this:
@@ -33,10 +47,21 @@ like this:
 import pandocfilters as pf
 
 
-valid_classes = ['objectives', 'callout', 'challenge']
+# These are classes that, if set on the title of a blockquote, will
+# trigger the blockquote to be converted to a div.
+SPECIAL_CLASSES = ['callout', 'challenge', 'prereq', 'objectives']
 
+# These are titles of blockquotes that will cause the blockquote to
+# be converted into a div. They are 'title': 'class' pairs, where the
+# 'title' will create a blockquote with the corresponding 'class'.
+SPECIAL_TITLES = {'prerequisites': 'prereq',
+                  'learning objectives': 'objectives',
+                  'objectives': 'objectives',
+                  'challenge': 'challenge',
+                  'callout': 'callout'}
 
-def find_attributes(blockquote):
+
+def find_header(blockquote):
     """Find attributes in a blockquote if they are defined on a
     header that is the first thing in the block quote.
 
@@ -45,7 +70,7 @@ def find_attributes(blockquote):
     """
     if blockquote[0]['t'] == 'Header':
         level, attr, inline = blockquote[0]['c']
-        return attr
+        return level, attr, inline
 
 
 def remove_attributes(blockquote):
@@ -70,10 +95,21 @@ def blockquote2div(key, value, format, meta):
     """
     if key == 'BlockQuote':
         blockquote = value
-        attr = find_attributes(blockquote)
-        if not attr:
+
+        header = find_header(blockquote)
+        if not header:
             return
-        elif len(attr[1]) == 1 and attr[1][0] in valid_classes:
+        else:
+            level, attr, inlines = header
+
+        id, classes, kvs = attr
+
+        ltitle = pf.stringify(inlines).lower()
+        if ltitle in SPECIAL_TITLES:
+            classes.append(SPECIAL_TITLES[ltitle])
+            return pf.Div(attr, blockquote)
+
+        elif len(classes) == 1 and classes[0] in SPECIAL_CLASSES:
             remove_attributes(blockquote)
             # a blockquote is just a list of blocks, so it can be
             # passed directly to Div, which expects Div(attr, blocks)
diff --git a/tools/check b/tools/check
index e750ec7633a97f41d1e3c440b2bc1f5d149f882a..5457d73910a65ded713ad7ff3599396928c6ecba 100755
--- a/tools/check
+++ b/tools/check
@@ -1,6 +1,213 @@
-#!/usr/bin/env bash
+#!/usr/bin/python
+#
+# Software Carpentry Lesson Validator
+#
+# Check for errors in lessons built using the Software Carpentry template
+# found at http://github.com/swcarpentry/lesson-template.
+#
+# Usage:
+#
+#     $ tools/check
 
-# Placeholder for actual conformance checking script (which will
-# probably be Python, not Bash).
+import sys
+import os
+import re
+import yaml
 
-grep -i -n 'FIX''ME' $*
+#----------------------------------------
+# Error reporting.
+
+def report_error(file_path, line_number, line, error_message):
+    """
+    Print information about general error.
+    """
+    ERR_MSG = "Error at line {} of {}:\n\t{}\n{}"
+    print(ERR_MSG.format(line_number, file_path, line, error_message))
+
+def report_missing(present, file_path, missing_element):
+    """
+    Print information about missing element.
+    """
+    ERR_MSG = "Error on {}: missing {}"
+    if not present:
+        print(ERR_MSG.format(file_path, missing_element))
+
+def report_missing_metadata(missing_element):
+    """
+    Print information about missing metadata at YAML header.
+    """
+    ERR_MSG = "Error on YAML header: missing {}"
+    print(ERR_MSG.format(missing_element))
+
+def report_broken_link(file_path, line_number, link):
+    """
+    Print information about broken link.
+    """
+    ERR_MSG = "Broken link at line {} of {}:\n\tCan't find {}."
+    print(ERR_MSG.format(line_number, file_path, link))
+
+#----------------------------------------
+# Checking.
+
+def check_yaml(metadata):
+    """
+    Check if all metadata are present at YAML header.
+    """
+    METADATA_REQUIRED = {"layout", "title", "minutes"}
+    for key in METADATA_REQUIRED - set(metadata.keys()):
+        report_missing_metadata(key)
+
+# TODO: Implement check_lesson
+def check_lesson(file_path):
+    """
+    Checks the file ``pages/[0-9]{2}-.*.md`` for:
+
+    - "layout: topic" in YAML header
+    - "title" as keyword in YAML header
+    - line "> ## Learning Objectives {.objectives}" after YAML header
+    - items in learning objectives begin with "*"
+    - items in learning objective following four-space indentation rule
+    - code samples be of type input, error, output, python, shell, r, matlab, or sql
+    - callout box style
+    - challenge box style
+    """
+    pass
+
+# TODO: Implement check_discussion
+def check_discussion(file_path):
+    """
+    Checks the file ``pages/discussion.md`` for:
+
+    FIXME: tell what need to check.
+    """
+    pass
+
+# TODO: Complete implementation of check_index
+# TODO: break check_index into pieces -- it's too long.
+def check_index(file_path):
+    """
+    Checks the file ``pages/index.md`` for:
+
+    - "layout: lesson" in YAML header
+    - "title" as keyword in YAML header
+    - introductory paragraph(s) right after YAML header
+    - line with "> ## Prerequisites"
+    - non-empty prerequisites
+    - title line with "## Topics"
+    - items at topic list begin with "*"
+    - items in topic list follow four-space indentation rule
+    - links at topic list are valid
+    - line with "## Other Resources"
+    - items at other resources list begin with "*"
+    - link at other resources list are valid
+    """
+    # State variables
+    in_yaml = False
+    yaml_metadata = []
+    has_prerequisites = False
+    has_topics = False
+    has_other_resources = False
+
+    # Load file and process it
+    with open(file_path, "r") as lines:
+        for line_number, line in enumerate(lines):
+            if re.match("---", line): # what if there are multiple YAML blocks??
+                in_yaml = not in_yaml
+            elif in_yaml:
+                yaml_metadata.append(line)
+            elif re.match("> ## Prerequisites", line): # check this in the Markdown or in the generated HTML?
+                has_prerequisites = True
+            elif re.match("## Topics", line): # as above?
+                has_topics = True
+            elif re.match("## Other Resources", line): # as above
+                has_other_resources = True
+            else:
+                ## Push this check into another function - this one is getting too long.
+                # Check if local links are valid
+                matches = re.search("\[.*\]\((?P<link>.*)\)", line)
+                if matches and not matches.group("link").startswith("http"):
+                    link = os.path.join(os.path.dirname(file_path), matches.group("link"))
+                    if link.endswith(".html"):
+                        link = link.replace("html", "md") # NO: what about "03-html-editing.html" ?
+                    if not os.path.exists(link):
+                        report_broken_link(file_path, line_number, link)
+
+    ## Again, this function is too long - break it into sub-functions.
+    # Check YAML
+    yaml_metadata = yaml.load("\n".join(yaml_metadata))
+    check_yaml(yaml_metadata)
+
+    # Check sections
+    ## Note the refactoring: replaces three conditionals with one.
+    report_missing(has_prerequisites, file_path, "Prerequisites")
+    report_missing(has_topics, file_path, "Topics")
+    report_missing(has_other_resources, file_path, "Other Resources")
+
+# TODO Implement check_intructors
+def check_intructors(file_path):
+    """
+    Checks the file ``pages/instructors.md`` for:
+
+    - "title: Instructor"s Guide" in YAML header
+    - line with "## Overall"
+    - line with "## General Points"
+    - lines with topics titles begin with "## "
+    - points begin with "*" and following four space rules.
+    """
+    pass
+
+# TODO Implement check_motivation
+def check_motivation(file_path):
+    """
+    Checks the file ``pages/motivation.md``.
+
+    FIXME: tell what need to check.
+    """
+    pass
+
+# TODO Implement check_reference
+def check_reference(file_path):
+    """
+    Checks the file ``pages/reference.md`` for:
+
+    -   ``layout: reference`` in YAML header
+    -   line with "## Glossary"
+    -   words definitions after at the "Glossary" as::
+
+        > **Key Word 1**: the definition
+        > relevant to the lesson.
+    """
+    pass
+
+def check_file(file_path):
+    """
+    Call the correctly check function based on the name of the file.
+    """
+    # Pair of regex and function to call
+    CONTROL = (
+        ("[0-9]{2}-.*", check_lesson),
+        ("discussion",  check_discussion),
+        ("index",       check_index),
+        ("instructors", check_intructors),
+        ("motivation",  check_motivation),
+        ("reference",   check_reference)
+    )
+    for (pattern, checker) in CONTROL:
+        if re.search(pattern, file_path):
+            checker(file_path)
+
+def main(list_of_files):
+    """
+    Call the check function for every file in ``list_of_files``.
+
+    If ``list_of_files`` is empty load all the files from ``pages`` directory.
+    """
+    if not list_of_files:
+        list_of_files = [os.path.join("pages", filename) for filename in os.listdir("pages")]
+
+    for filename in list_of_files:
+        if filename.endswith(".md"):
+            check_file(filename)
+
+if __name__ == "__main__":
+    main(sys.argv[1:])