diff --git a/calibre-plugin/basicinihighlighter.py b/calibre-plugin/basicinihighlighter.py new file mode 100644 index 00000000..75165bb2 --- /dev/null +++ b/calibre-plugin/basicinihighlighter.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Jim Miller' +__docformat__ = 'restructuredtext en' + +import re + +try: + from PyQt5.Qt import (Qt, QSyntaxHighlighter, QTextCharFormat, QBrush) +except ImportError as e: + from PyQt4.Qt import (Qt, QSyntaxHighlighter, QTextCharFormat, QBrush) + +class BasicIniHighlighter(QSyntaxHighlighter): + ''' + QSyntaxHighlighter class for use with QTextEdit for highlighting + ini config files. + + I looked high and low to find a high lighter for basic ini config + format, so I'm leaving this in the project even though I'm not + using. + ''' + + def __init__( self, parent, theme ): + QSyntaxHighlighter.__init__( self, parent ) + self.parent = parent + + self.highlightingRules = [] + + # keyword + self.highlightingRules.append( HighlightingRule( r"^[^:=\s][^:=]*[:=]", + Qt.blue, + Qt.SolidPattern ) ) + + # section + self.highlightingRules.append( HighlightingRule( r"^\[[^\]]+\]", + Qt.darkBlue, + Qt.SolidPattern ) ) + + # comment + self.highlightingRules.append( HighlightingRule( r"#[^\n]*" , + Qt.darkYellow, + Qt.SolidPattern ) ) + + def highlightBlock( self, text ): + for rule in self.highlightingRules: + for match in rule.pattern.finditer(text): + self.setFormat( match.start(), match.end()-match.start(), rule.highlight ) + self.setCurrentBlockState( 0 ) + +class HighlightingRule(): + def __init__( self, pattern, color, style ): + if isinstance(pattern,basestring): + self.pattern = re.compile(pattern) + else: + self.pattern=pattern + charfmt = QTextCharFormat() + brush = QBrush(color, style) + charfmt.setForeground(brush) + self.highlight = charfmt + diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 7ced2c9b..57c8d1ea 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -13,8 +13,6 @@ logger = logging.getLogger(__name__) import traceback, copy, threading from collections import OrderedDict -from ConfigParser import ParsingError - try: from PyQt5.Qt import (QDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFont, QWidget, QTextEdit, QComboBox, @@ -75,7 +73,7 @@ no_trans = { 'pini':'personal.ini', from calibre_plugins.fanfictiondownloader_plugin.prefs import prefs, PREFS_NAMESPACE from calibre_plugins.fanfictiondownloader_plugin.dialogs \ import (UPDATE, UPDATEALWAYS, collision_order, save_collisions, RejectListDialog, - EditTextDialog, RejectUrlEntry, errors_dialog) + EditTextDialog, IniTextDialog, RejectUrlEntry, errors_dialog) from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.adapters \ import getConfigSections @@ -83,7 +81,8 @@ from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.adapters \ from calibre_plugins.fanfictiondownloader_plugin.common_utils \ import ( KeyboardConfigDialog, PrefsViewerDialog ) -from calibre_plugins.fanfictiondownloader_plugin.ffdl_util import (get_ffdl_config) +from calibre_plugins.fanfictiondownloader_plugin.ffdl_util \ + import (test_config) from calibre.gui2.complete2 import EditWithComplete #MultiCompleteLineEdit @@ -566,8 +565,7 @@ class BasicTab(QWidget): title=_("Reject Reasons"), label=_("Customize Reject List Reasons"), tooltip=_("Customize the Reasons presented when Rejecting URLs"), - save_size_name='ffdl:Reject List Reasons', - use_find=True) + save_size_name='ffdl:Reject List Reasons') d.exec_() if d.result() == d.Accepted: prefs['rejectreasons'] = d.get_plain_text() @@ -632,7 +630,7 @@ class PersonalIniTab(QWidget): # let edit box fill the space. def show_defaults(self): - EditTextDialog(self, + IniTextDialog(self, get_resources('plugin-defaults.ini'), icon=self.windowIcon(), title=_('Plugin Defaults'), @@ -643,7 +641,7 @@ class PersonalIniTab(QWidget): save_size_name='ffdl:defaults.ini').exec_() def add_ini_button(self): - d = EditTextDialog(self, + d = IniTextDialog(self, self.personalini, icon=self.windowIcon(), title=_("Edit personal.ini"), @@ -658,13 +656,7 @@ class PersonalIniTab(QWidget): if d.result() == d.Accepted: self.personalini = unicode(d.get_plain_text()) - try: - configini = get_ffdl_config("test1.com?sid=555", - personalini=self.personalini) - - errors = configini.test_config() - except ParsingError as pe: - errors = pe.errors + errors = test_config(self.personalini) if errors: error = not errors_dialog(self.plugin_action.gui, diff --git a/calibre-plugin/dialogs.py b/calibre-plugin/dialogs.py index a204632a..142ffe65 100644 --- a/calibre-plugin/dialogs.py +++ b/calibre-plugin/dialogs.py @@ -7,9 +7,6 @@ __license__ = 'GPL v3' __copyright__ = '2014, Jim Miller' __docformat__ = 'restructuredtext en' -import logging -logger = logging.getLogger(__name__) - import traceback, re from functools import partial @@ -28,8 +25,7 @@ try: QPushButton, QFont, QLabel, QCheckBox, QIcon, QLineEdit, QComboBox, QProgressDialog, QTimer, QDialogButtonBox, QPixmap, Qt, QAbstractItemView, QTextEdit, pyqtSignal, - QGroupBox, QFrame, QTextBrowser, QSize, QAction, - QSyntaxHighlighter, QTextCharFormat, QBrush ) + QGroupBox, QFrame, QTextBrowser, QSize, QAction) except ImportError as e: from PyQt4 import QtGui from PyQt4 import QtCore @@ -37,8 +33,7 @@ except ImportError as e: QPushButton, QFont, QLabel, QCheckBox, QIcon, QLineEdit, QComboBox, QProgressDialog, QTimer, QDialogButtonBox, QPixmap, Qt, QAbstractItemView, QTextEdit, pyqtSignal, - QGroupBox, QFrame, QTextBrowser, QSize, QAction, QtCore, - QSyntaxHighlighter, QTextCharFormat, QBrush ) + QGroupBox, QFrame, QTextBrowser, QSize, QAction) try: from calibre.gui2 import QVariant @@ -72,6 +67,12 @@ from calibre_plugins.fanfictiondownloader_plugin.common_utils \ from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.geturls import get_urls_from_html, get_urls_from_text from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.adapters import getNormalStoryURL +from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.configurable \ + import (get_valid_sections, get_valid_entries, + get_valid_keywords, get_valid_entry_keywords) + +from inihighlighter import IniHighlighter + SKIP=_('Skip') ADDNEW=_('Add New Book') UPDATE=_('Update EPUB if New Chapters') @@ -1120,15 +1121,73 @@ class EditTextDialog(SizePersistedDialog): def __init__(self, parent, text, icon=None, title=None, label=None, tooltip=None, rejectreasons=[],reasonslabel=None, + save_size_name='ffdl:edit text dialog', + ): + SizePersistedDialog.__init__(self, parent, save_size_name) + + self.l = QVBoxLayout() + self.setLayout(self.l) + self.label = QLabel(label) + if title: + self.setWindowTitle(title) + if icon: + self.setWindowIcon(icon) + self.l.addWidget(self.label) + + self.textedit = QTextEdit(self) + self.textedit.setLineWrapMode(QTextEdit.NoWrap) + self.textedit.setText(text) + self.l.addWidget(self.textedit) + + if tooltip: + self.label.setToolTip(tooltip) + self.textedit.setToolTip(tooltip) + + if rejectreasons or reasonslabel: + self.reason_edit = EditWithComplete(self,sort_func=lambda x:1) + + items = ['']+rejectreasons + self.reason_edit.update_items_cache(items) + self.reason_edit.show_initial_value('') + self.reason_edit.set_separator(None) + self.reason_edit.setToolTip(reasonslabel) + + if reasonslabel: + horz = QHBoxLayout() + label = QLabel(reasonslabel) + label.setToolTip(reasonslabel) + horz.addWidget(label) + horz.addWidget(self.reason_edit) + self.l.addLayout(horz) + else: + self.l.addWidget(self.reason_edit) + + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + self.l.addWidget(button_box) + + # Cause our dialog size to be restored from prefs or created on first usage + self.resize_dialog() + + def get_plain_text(self): + return unicode(self.textedit.toPlainText()) + + def get_reason_text(self): + return unicode(self.reason_edit.currentText()).strip() + +class IniTextDialog(SizePersistedDialog): + + def __init__(self, parent, text, + icon=None, title=None, label=None, tooltip=None, use_find=False, read_only=False, - save_size_name='ffdl:edit text dialog', + save_size_name='ffdl:ini text dialog', ): SizePersistedDialog.__init__(self, parent, save_size_name) self.keys=dict() - #self.resize(600, 500) self.l = QVBoxLayout() self.setLayout(self.l) self.label = QLabel(label) @@ -1140,7 +1199,12 @@ class EditTextDialog(SizePersistedDialog): self.textedit = QTextEdit(self) - highlighter = IniHighlighter(self.textedit, "Classic") + highlighter = IniHighlighter(self.textedit, + sections=get_valid_sections(), + keywords=get_valid_keywords(), + entries=get_valid_entries(), + entry_keywords=get_valid_entry_keywords(), + ) self.textedit.setLineWrapMode(QTextEdit.NoWrap) try: @@ -1193,25 +1257,6 @@ class EditTextDialog(SizePersistedDialog): self.label.setToolTip(tooltip) self.textedit.setToolTip(tooltip) - if rejectreasons or reasonslabel: - self.reason_edit = EditWithComplete(self,sort_func=lambda x:1) - - items = ['']+rejectreasons - self.reason_edit.update_items_cache(items) - self.reason_edit.show_initial_value('') - self.reason_edit.set_separator(None) - self.reason_edit.setToolTip(reasonslabel) - - if reasonslabel: - horz = QHBoxLayout() - label = QLabel(reasonslabel) - label.setToolTip(reasonslabel) - horz.addWidget(label) - horz.addWidget(self.reason_edit) - self.l.addLayout(horz) - else: - self.l.addWidget(self.reason_edit) - button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) @@ -1238,9 +1283,6 @@ class EditTextDialog(SizePersistedDialog): def get_plain_text(self): return unicode(self.textedit.toPlainText()) - def get_reason_text(self): - return unicode(self.reason_edit.currentText()).strip() - def findFocus(self): # print("findFocus called") self.findField.setFocus() @@ -1248,7 +1290,7 @@ class EditTextDialog(SizePersistedDialog): def find(self): - print("find self.lastStart:%s"%self.lastStart) + #print("find self.lastStart:%s"%self.lastStart) # Grab the parent's text text = self.textedit.toPlainText() @@ -1328,117 +1370,3 @@ class ViewLog(QDialog): txt = self.tb.toPlainText() QApplication.clipboard().setText(txt) -class IniHighlighter(QSyntaxHighlighter): - - def __init__( self, parent, theme ): - QSyntaxHighlighter.__init__( self, parent ) - self.parent = parent - keyword = QTextCharFormat() - reservedClasses = QTextCharFormat() - assignmentOperator = QTextCharFormat() - delimiter = QTextCharFormat() - specialConstant = QTextCharFormat() - boolean = QTextCharFormat() - number = QTextCharFormat() - comment = QTextCharFormat() - string = QTextCharFormat() - singleQuotedString = QTextCharFormat() - - self.highlightingRules = [] - - # # keyword - # brush = QBrush( Qt.darkBlue, Qt.SolidPattern ) - # keyword.setForeground( brush ) - # keyword.setFontWeight( QFont.Bold ) - # keywords = [ "break", "else", "for", "if", "in", - # "next", "repeat", "return", "switch", - # "try", "while" ] - # for word in keywords: - # pattern = "\\b" + word + "\\b" - # rule = HighlightingRule( pattern, keyword ) - # self.highlightingRules.append( rule ) - - # # reservedClasses - # reservedClasses.setForeground( brush ) - # reservedClasses.setFontWeight( QFont.Bold ) - # keywords = [ "array", "character", "complex", - # "data.frame", "double", "factor", - # "function", "integer", "list", - # "logical", "matrix", "numeric", - # "vector" ] - # for word in keywords: - # pattern = "\\b" + word + "\\b" - # rule = HighlightingRule( pattern, reservedClasses ) - # self.highlightingRules.append( rule ) - - # # assignmentOperator - # brush = QBrush( Qt.yellow, Qt.SolidPattern ) - # pattern = "(<){1,2}-" - # assignmentOperator.setForeground( brush ) - # assignmentOperator.setFontWeight( QFont.Bold ) - # rule = HighlightingRule( pattern, assignmentOperator ) - # self.highlightingRules.append( rule ) - - # section - pattern = r"^\[[^\]]+\]" - brush = QBrush( Qt.darkBlue, Qt.SolidPattern ) - delimiter.setForeground( brush ) - delimiter.setFontWeight( QFont.Bold ) - rule = HighlightingRule( pattern, delimiter ) - self.highlightingRules.append( rule ) - - # # specialConstant - # brush = QBrush( Qt.green, Qt.SolidPattern ) - # specialConstant.setForeground( brush ) - # keywords = [ "Inf", "NA", "NaN", "NULL" ] - # for word in keywords: - # pattern = "\\b" + word + "\\b" - # rule = HighlightingRule( pattern, specialConstant ) - # self.highlightingRules.append( rule ) - - # boolean, case insensitive - boolean.setForeground( brush ) - pattern = r"\b(true|false)\b" - rule = HighlightingRule( pattern, boolean ) - self.highlightingRules.append( rule ) - - # # number - # pattern = "[-+]?[0-9]*\.?[0-9]+?([eE][-+]?[0-9]+?)?" - # number.setForeground( brush ) - # rule = HighlightingRule( pattern, number ) - # self.highlightingRules.append( rule ) - - # comment - brush = QBrush( Qt.darkGray, Qt.SolidPattern ) - pattern = "#[^\n]*" - comment.setForeground( brush ) - rule = HighlightingRule( pattern, comment ) - self.highlightingRules.append( rule ) - - # string - brush = QBrush( Qt.red, Qt.SolidPattern ) - pattern = "\".*?\"" - string.setForeground( brush ) - rule = HighlightingRule( pattern, string ) - self.highlightingRules.append( rule ) - - # singleQuotedString - pattern = "\'.*?\'" - singleQuotedString.setForeground( brush ) - rule = HighlightingRule( pattern, singleQuotedString ) - self.highlightingRules.append( rule ) - - def highlightBlock( self, text ): - for rule in self.highlightingRules: - for match in rule.pattern.finditer(text): - self.setFormat( match.start(), match.end()-match.start(), rule.format ) - self.setCurrentBlockState( 0 ) - -class HighlightingRule(): - def __init__( self, pattern, format ): - if isinstance(pattern,basestring): - self.pattern = re.compile(pattern) - else: - self.pattern=pattern - self.format = format - diff --git a/calibre-plugin/ffdl_util.py b/calibre-plugin/ffdl_util.py index c169d80c..6f28eb30 100644 --- a/calibre-plugin/ffdl_util.py +++ b/calibre-plugin/ffdl_util.py @@ -8,10 +8,11 @@ __copyright__ = '2013, Jim Miller' __docformat__ = 'restructuredtext en' from StringIO import StringIO +from ConfigParser import ParsingError from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader import adapters, exceptions from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.configurable import Configuration -from calibre_plugins.fanfictiondownloader_plugin.prefs import (prefs) +from calibre_plugins.fanfictiondownloader_plugin.prefs import prefs def get_ffdl_personalini(): if prefs['includeimages']: @@ -40,4 +41,15 @@ def get_ffdl_config(url,fileform="epub",personalini=None): def get_ffdl_adapter(url,fileform="epub",personalini=None): return adapters.getAdapter(get_ffdl_config(url,fileform,personalini),url) + +def test_config(initext): + + configini = get_ffdl_config("test1.com?sid=555", + personalini=initext) + try: + errors = configini.test_config() + except ParsingError as pe: + errors = pe.errors + return errors + diff --git a/calibre-plugin/inihighlighter.py b/calibre-plugin/inihighlighter.py new file mode 100644 index 00000000..b78c5744 --- /dev/null +++ b/calibre-plugin/inihighlighter.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Jim Miller' +__docformat__ = 'restructuredtext en' + +import re + +try: + from PyQt5.Qt import (Qt, QSyntaxHighlighter, QTextCharFormat, QBrush, QFont) +except ImportError as e: + from PyQt4.Qt import (Qt, QSyntaxHighlighter, QTextCharFormat, QBrush, QFont) + +# r'add_to_+key + + + +class IniHighlighter(QSyntaxHighlighter): + ''' + QSyntaxHighlighter class for use with QTextEdit for highlighting + ini config files. + ''' + + def __init__( self, parent, sections=[], keywords=[], entries=[], entry_keywords=[] ): + QSyntaxHighlighter.__init__( self, parent ) + self.parent = parent + + self.highlightingRules = [] + + # *all* keywords -- change known later. + self.errorRule = HighlightingRule( r"^[^:=\s][^:=]*[:=]", Qt.red ) + self.highlightingRules.append( self.errorRule ) + + # *all* entry keywords -- change known later. + reentrykeywords = r'('+(r'|'.join([ e % r'[a-zA-Z0-9_]+' for e in entry_keywords ]))+r')' + self.highlightingRules.append( HighlightingRule( r"^(add_to_)?"+reentrykeywords+r"\s*[:=]", Qt.darkMagenta ) ) + + # true/false -- just to be nice. + self.highlightingRules.append( HighlightingRule( r"\b(true|false)\b", Qt.darkGreen ) ) + + if entries: + # *known* entries + reentries = r'('+(r'|'.join(entries))+r')' + self.highlightingRules.append( HighlightingRule( r"\b"+reentries+r"\b", Qt.darkGreen ) ) + + # *known* entry keywords + reentrykeywords = r'('+(r'|'.join([ e % reentries for e in entry_keywords ]))+r')' + self.highlightingRules.append( HighlightingRule( r"^(add_to_)?"+reentrykeywords+r"\s*[:=]", Qt.blue ) ) + + # *known* keywords + rekeywords = r'('+(r'|'.join(keywords))+r')' + self.highlightingRules.append( HighlightingRule( r"^(add_to_)?"+rekeywords+r"\s*[:=]", Qt.blue ) ) + + # *all* sections -- change known later. + self.highlightingRules.append( HighlightingRule( r"^\[[^\]]+\].*?$", Qt.red, QFont.Bold, blocknum=1 ) ) + + if sections: + # *known* sections + resections = r'('+(r'|'.join(sections))+r')' + resections = resections.replace('.','\.') #escape dots. + self.highlightingRules.append( HighlightingRule( r"^\["+resections+r"\]\s*$", Qt.darkBlue, QFont.Bold, blocknum=2 ) ) + + # test story sections + self.teststoryRule = HighlightingRule( r"^\[teststory:([0-9]+|defaults)\]", Qt.darkCyan, blocknum=3 ) + self.highlightingRules.append( self.teststoryRule ) + + # NOT comments -- but can be custom columns, so don't flag. + #self.highlightingRules.append( HighlightingRule( r"(? 0: + blocknum = rule.blocknum + + if not is_comment: + # unknown section, error all: + if blocknum == 1 and blocknum == self.previousBlockState(): + self.setFormat( 0, len(text), self.errorRule.highlight ) + + # teststory section rules: + if blocknum == 3: + self.setFormat( 0, len(text), self.teststoryRule.highlight ) + + self.setCurrentBlockState( blocknum ) + +class HighlightingRule(): + def __init__( self, pattern, color, + weight=QFont.Normal, + style=Qt.SolidPattern, + blocknum=0): + if isinstance(pattern,basestring): + self.pattern = re.compile(pattern) + else: + self.pattern=pattern + charfmt = QTextCharFormat() + brush = QBrush(color, style) + charfmt.setForeground(brush) + charfmt.setFontWeight(weight) + self.highlight = charfmt + self.blocknum=blocknum + diff --git a/fanficdownloader/configurable.py b/fanficdownloader/configurable.py index bbe8c1de..7dfe63ab 100644 --- a/fanficdownloader/configurable.py +++ b/fanficdownloader/configurable.py @@ -35,31 +35,30 @@ from ConfigParser import DEFAULTSECT, MissingSectionHeaderError, ParsingError import adapters -class Configuration(ConfigParser.SafeConfigParser): - - def __init__(self, site, fileform): - ConfigParser.SafeConfigParser.__init__(self) - - self.linenos=dict() # key by section or section,key -> lineno - - self.sectionslist = ['defaults'] - - if site.startswith("www."): - sitewith = site - sitewithout = site.replace("www.","") +def get_valid_sections(): + sites = adapters.getConfigSections() + sitesections = ['defaults','overrides'] + for section in sites: + sitesections.append(section) + if section.startswith('www.'): + # add w/o www if has www + sitesections.append(section[4:]) else: - sitewith = "www."+site - sitewithout = site - - self.addConfigSection(sitewith) - self.addConfigSection(sitewithout) - if fileform: - self.addConfigSection(fileform) - self.addConfigSection(sitewith+":"+fileform) - self.addConfigSection(sitewithout+":"+fileform) - self.addConfigSection("overrides") - - self.listTypeEntries = [ + # add w/ www if doesn't www + sitesections.append('www.%s'%section) + + allowedsections = [] + forms=['html','txt','epub','mobi'] + allowedsections.extend(forms) + + for section in sitesections: + allowedsections.append(section) + for f in forms: + allowedsections.append('%s:%s'%(section,f)) + return allowedsections + +def get_valid_list_entries(): + return list([ 'category', 'genre', 'characters', @@ -70,9 +69,10 @@ class Configuration(ConfigParser.SafeConfigParser): 'authorId', 'authorUrl', 'lastupdate', - ] - - self.validEntries = self.listTypeEntries + [ + ]) + +def get_valid_scalar_entries(): + return list([ 'series', 'seriesUrl', 'language', @@ -97,7 +97,151 @@ class Configuration(ConfigParser.SafeConfigParser): 'seriesHTML', 'langcode', 'output_css', - ] + ]) + +def get_valid_entries(): + return get_valid_list_entries() + get_valid_scalar_entries() + +def get_valid_keywords(): + return list(['add_chapter_numbers', + 'add_genre_when_multi_category', + 'allow_unsafe_filename', + 'always_overwrite', + 'anthology_tags', + 'anthology_title_pattern', + 'background_color', + 'bulk_load', + 'chapter_end', + 'chapter_start', + 'chapter_title_add_pattern', + 'chapter_title_strip_pattern', + 'check_next_chapter', + 'collect_series', + 'connect_timeout', + 'convert_images_to', + 'cover_content', + 'cover_exclusion_regexp', + 'custom_columns_settings', + 'dateCreated_format', + 'datePublished_format', + 'dateUpdated_format', + 'default_cover_image', + 'do_update_hook', + 'exclude_notes', + 'extra_logpage_entries', + 'extra_subject_tags', + 'extra_titlepage_entries', + 'extra_valid_entries', + 'extratags', + 'extracategories', + 'extragenres', + 'extracharacters', + 'extraships', + 'extrawarnings', + 'fail_on_password', + 'file_end', + 'file_start', + 'fileformat', + 'find_chapters', + 'fix_fimf_blockquotes', + 'force_login', + 'generate_cover_settings', + 'grayscale_images', + 'image_max_size', + 'include_images', + 'include_logpage', + 'include_subject_tags', + 'include_titlepage', + 'include_tocpage', + '(in|ex)clude_metadata_(pre|post)', + 'is_adult', + 'join_string_authorHTML', + 'keep_style_attr', + 'keep_summary_html', + 'logpage_end', + 'logpage_entries', + 'logpage_entry', + 'logpage_start', + 'logpage_update_end', + 'logpage_update_start', + 'make_directories', + 'make_firstimage_cover', + 'make_linkhtml_entries', + 'max_fg_sleep', + 'max_fg_sleep_at_downloads', + 'min_fg_sleep', + 'never_make_cover', + 'no_image_processing', + 'non_breaking_spaces', + 'nook_img_fix', + 'output_css', + 'output_filename', + 'output_filename_safepattern', + 'password', + 'post_process_cmd', + 'remove_transparency', + 'replace_br_with_p', + 'replace_hr', + 'replace_metadata', + 'slow_down_sleep_time', + 'sort_ships', + 'strip_chapter_numbers', + 'strip_chapter_numeral', + 'strip_text_links', + 'titlepage_end', + 'titlepage_entries', + 'titlepage_entry', + 'titlepage_no_title_entry', + 'titlepage_start', + 'titlepage_use_table', + 'titlepage_wide_entry', + 'tocpage_end', + 'tocpage_entry', + 'tocpage_start', + 'tweak_fg_sleep', + 'universe_as_series', + 'user_agent', + 'username', + 'website_encodings', + 'wide_titlepage_entries', + 'windows_eol', + 'wrap_width', + 'zip_filename', + 'zip_output', + ]) + +# *known* entry keywords -- or rather regexps for them. +def get_valid_entry_keywords(): + return list(['%s_label', + '(default_value|include_in|join_string|keep_in_order)_%s',]) + +class Configuration(ConfigParser.SafeConfigParser): + + def __init__(self, site, fileform): + ConfigParser.SafeConfigParser.__init__(self) + + self.linenos=dict() # key by section or section,key -> lineno + + self.sectionslist = ['defaults'] + + if site.startswith("www."): + sitewith = site + sitewithout = site.replace("www.","") + else: + sitewith = "www."+site + sitewithout = site + + self.addConfigSection(sitewith) + self.addConfigSection(sitewithout) + if fileform: + self.addConfigSection(fileform) + self.addConfigSection(sitewith+":"+fileform) + self.addConfigSection(sitewithout+":"+fileform) + self.addConfigSection("overrides") + + self.listTypeEntries = get_valid_list_entries() + + self.validEntries = get_valid_entries() def addConfigSection(self,section): self.sectionslist.insert(0,section) @@ -169,35 +313,6 @@ class Configuration(ConfigParser.SafeConfigParser): return self.get_config_list(self.sectionslist, key) - def test_config(self): - errors=[] - - sites = adapters.getConfigSections() - sitesections = ['defaults','overrides'] - for section in sites: - sitesections.append(section) - if section.startswith('www.'): - # add w/o www if has www - sitesections.append(section[4:]) - else: - # add w/ www if doesn't www - sitesections.append('www.%s'%section) - - allowedsections = [] - forms=['html','txt','epub','mobi'] - allowedsections.extend(forms) - - for section in sitesections: - allowedsections.append(section) - for f in forms: - allowedsections.append('%s:%s'%(section,f)) - - for section in self.sections(): - if section not in allowedsections and 'teststory:' not in section: - errors.append((self.get_lineno(section),"Bad Section Name: %s"%section)) - - return errors - def get_lineno(self,section,key=None): if key: return self.linenos.get(section+','+key,None) @@ -293,7 +408,17 @@ class Configuration(ConfigParser.SafeConfigParser): # if any parsing errors occurred, raise an exception if e: raise e - + + def test_config(self): + errors=[] + + allowedsections = get_valid_sections() + for section in self.sections(): + if section not in allowedsections and 'teststory:' not in section: + errors.append((self.get_lineno(section),"Bad Section Name: %s"%section)) + + return errors + # extended by adapter, writer and story for ease of calling configuration. class Configurable(object):