def publish(self, filename):
        '''Write the review to a file.

        Arguments:
        :filename: filename to write to.

        Write the review out to a file.
        '''

        if not self.checklist.properties.requirementsMet:
            # Popup a dialog to finish entering properties
            requireDialog = gtk.MessageDialog(None,
                    gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_WARNING,
                    gtk.BUTTONS_CLOSE,
                    'There are several properties in this checklist that are'
                    ' required.  A review cannot be printed until those'
                    ' entries are filled in.  Please be sure you have entered'
                    ' each property which is displayed in italics in the'
                    ' Edit::Properties menu and then try to publish again.')
            requireDialog.set_title('Enter All Required Properties')
            requireDialog.set_default_response(gtk.RESPONSE_CLOSE)
            requireDialog.run()
            requireDialog.destroy()
            return
        
        outBuf = self.checklist.functions.header() + '\n'
        # Loop through the review areas:
        for box in ('Pass', 'Fail', 'Non-Blocker', 'Notes'):
            reviewBox = self.reviewBoxes[box]
            tempOutBuf = '\n'
            # Add items from this review category to the output buffer.
            for entryLabel in reviewBox.get_children():
                value = entryLabel.get_text()
                if value:
                    value = self.checklist.unpangoize_output(value)
                    tempOutBuf += self.textwrap.fill(value) + '\n'
            if tempOutBuf:
                outBuf += self.reviewTitles[box].get_text() + tempOutBuf + '\n'
        # Add the footer
        outBuf += self.checklist.functions.footer() + '\n'

        # Sign the review if the user has decided to
        key = GCONFPREFIX + '/user/use-gpg'
        try:
            sign = self.gconfClient.get_bool(key)
        except gobject.GError:
            sign = self.gconfClient.get_default_from_schema(key).get_bool()
        if sign:
            key = GCONFPREFIX + '/user/gpg-identity'
            try:
                gpgId = self.gconfClient.get_string(key)
            except gobject.GError:
                # If no ID is set, we'll use whatever gpg considers the
                # default.
                gpgId = None

            key = GCONFPREFIX + '/files/gpg-path'
            try:
                gpgPath = self.gconfClient.get_string(key)
            except gobject.GError:
                gpgPath = self.gconfClient.get_default_from_schema(key).get_string()
            reenterPassphrase = False
            while True:
                passphrase = gpg.get_passphrase(gpgId, reenterPassphrase)
                if passphrase == None:
                    break
                try:
                    outBuf = gpg.sign_buffer(outBuf, gpgId, gpgPath, passphrase)
                except error.BadPassphrase:
                    reenterPassphrase = True
                except error.NoSecretKey:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'No secret key was found for that user ID.  Please'
                            ' go to the Edit::Preferences Menu.  Select'
                            ' another id to sign with in the Preferences'
                            ' Dialog.')
                    errorDialog.set_title('No Signing Key')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                except error.NoOut:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'No output was created by GPG when trying to'
                            ' sign the review.  This is likely a problem'
                            ' with the way gpg is setup.  Please take a look'
                            ' in the Edit::Preferences menu and see if there'
                            ' are any errors there.  If not, you may want to'
                            ' disable automatic gpg signing and publish your'
                            ' review.  Then sign it manually using your gpg'
                            ' number.')
                    errorDialog.set_title('No Output Created')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                except error.NotGPGCompatible:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'The program specified in the Preferences'
                            ' dialog does not appear to take the same'
                            ' commandline arguments as gpg.  Please specify'
                            ' a new program in the Preferences that is either'
                            ' gpg or something that is a dropin replacement'
                            ' for it.')
                    errorDialog.set_title('Invalid GPG program specified')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                else:
                    # Success!
                    break

        # Output review
        outfile = file(filename, 'w')
        outfile.writelines(outBuf)
        outfile.close()
    def publish(self, filename):
        '''Write the review to a file.

        Arguments:
        :filename: filename to write to.

        Write the review out to a file.
        '''

        ## FIXME: Get header information from the checklist
        # I want to make these available through some sort of header method
        # on the checklist but I haven't designed that yet.

        ### FIXME: This is probably going to change in favor of a checklist
        # header method.  As it currently stands, it still needs to be
        # translated into PUBLISH +1, NEEDSWORK, etc.
        outBuf = self.checklist.resolution + '\n'
        # Loop through the review areas:
        for box in ('Pass', 'Fail', 'Non-Blocker', 'Notes'):
            reviewBox = self.reviewBoxes[box]
            tempOutBuf = ''
            # Add items from this review category to the output buffer.
            for entryLabel in reviewBox.get_children():
                value = entryLabel.get_text()
                if value:
                    value = self.checklist.unpangoize_output(value)
                    tempOutBuf += self.textwrap.fill(value) + '\n'
            if tempOutBuf:
                outBuf += self.reviewTitles[box].get_text() + '\n' + tempOutBuf

        # Sign the review if the user has decided to
        key = GCONFPREFIX + '/user/use-gpg'
        try:
            sign = self.gconfClient.get_bool(key)
        except gobject.GError:
            sign = self.gconfClient.get_default_from_schema(key).get_bool()
        if sign:
            key = GCONFPREFIX + '/user/gpg-identity'
            try:
                gpgId = self.gconfClient.get_string(key)
            except gobject.GError:
                # If no ID is set, we'll use whatever gpg considers the
                # default.
                gpgId = None

            key = GCONFPREFIX + '/files/gpg-path'
            try:
                gpgPath = self.gconfClient.get_string(key)
            except gobject.GError:
                gpgPath = self.gconfClient.get_default_from_schema(key).get_string()
            reenterPassphrase = False
            while True:
                passphrase = gpg.get_passphrase(gpgId, reenterPassphrase)
                if passphrase == None:
                    break
                try:
                    outBuf = gpg.sign_buffer(outBuf, gpgId, gpgPath, passphrase)
                except error.BadPassphrase:
                    reenterPassphrase = True
                except error.NoSecretKey:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'No secret key was found for that user ID.  Please'
                            ' go to the Edit::Preferences Menu.  Select'
                            ' another id to sign with in the Preferences'
                            ' Dialog.')
                    errorDialog.set_title('No Signing Key')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                except error.NoOut:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'No output was created by GPG when trying to'
                            ' sign the review.  This is likely a problem'
                            ' with the way gpg is setup.  Please take a look'
                            ' in the Edit::Preferences menu and see if there'
                            ' are any errors there.  If not, you may want to'
                            ' disable automatic gpg signing and publish your'
                            ' review.  Then sign it manually using your gpg'
                            ' number.')
                    errorDialog.set_title('No Output Created')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                except error.NotGPGCompatible:
                    errorDialog = gtk.MessageDialog(None,
                            gtk.DIALOG_DESTROY_WITH_PARENT,
                            gtk.MESSAGE_WARNING,
                            gtk.BUTTONS_CLOSE,
                            'The program specified in the Preferences'
                            ' dialog does not appear to take the same'
                            ' commandline arguments as gpg.  Please specify'
                            ' a new program in the Preferences that is either'
                            ' gpg or something that is a dropin replacement'
                            ' for it.')
                    errorDialog.set_title('Invalid GPG program specified')
                    errorDialog.set_default_response(gtk.RESPONSE_CLOSE)
                    response = errorDialog.run()
                    errorDialog.destroy()
                    return
                else:
                    # Success!
                    break

        # Output review
        outfile = file(filename, 'w')
        outfile.writelines(outBuf)
        outfile.close()