Esempio n. 1
0
 def __init__(self):
     self._directory_path: str = ''
     self._article_documents: Dict[str, WhitebearDocumentArticle] = {}
     self._menu_documents: Dict[str, WhitebearDocumentMenu] = {}
     self._index_document = None
     self._css_document = None
     # Prepare xml schemas
     try:
         self._xmlschema_index = etree.XMLSchema(etree.parse(Fetch.get_resource_path('schema_index.xsd')))
         self._xmlschema_article = etree.XMLSchema(etree.parse(Fetch.get_resource_path('schema_article.xsd')))
         self._xmlschema_menu = etree.XMLSchema(etree.parse(Fetch.get_resource_path('schema_menu.xsd')))
     except XMLSchemaParseError as ex:
         raise UnrecognizedFileException(Strings.exception_schema_syntax_error + ':\n' + str(ex))
    def test_self(self) -> bool:
        """
        SEO test self for caption, alt and link title. If the image and thumbnail is not accessible on disk, set a
        special warning image.
        :return: True if test is ok, False otherwise
        """
        # Clear all error before each retest
        self._caption_error_message: str = ''

        # First test the base class seo attributes
        result = super(AsideImage, self).test_self()

        # Check caption length must be at least 3 and must not be default
        if len(self._caption) < Numbers.article_image_caption_min or len(
                self._caption) > Numbers.article_image_caption_max:
            self._caption_error_message = Strings.seo_error_image_caption_length
            result = False

        if self._caption == Strings.label_article_image_caption:
            self._caption_error_message = Strings.seo_error_default_value
            result = False

        # Check thumbnail image disk path
        if not self._thumbnail_path or not os.path.exists(self._thumbnail_path):
            # The image has the same dimensions as the main image
            self._image = wx.Image(Fetch.get_resource_path('main_image_thumbnail_missing.png'), wx.BITMAP_TYPE_PNG)
            self._thumbnail_size = (0, 0)
            result = False
        else:
            image = wx.Image(Fetch.get_resource_path(self._thumbnail_path), wx.BITMAP_TYPE_ANY)
            self._thumbnail_size = image.GetSize()
            if self._thumbnail_size == (Numbers.main_image_width, Numbers.main_image_height):
                self._image = image
            else:
                self._image = wx.Image(Fetch.get_resource_path('main_image_thumbnail_wrong.png'), wx.BITMAP_TYPE_PNG)
                self._thumbnail_size = image.GetSize()
                result = False

            # Check full image disk path, size can be whatever the user likes
            if not self._original_image_path or not os.path.exists(self._original_image_path):
                self._image = wx.Image(Fetch.get_resource_path('main_image_missing.png'), wx.BITMAP_TYPE_PNG)
                result = False

        # Spell check
        if not self._spell_check(self._caption):
            self._caption_error_message = Strings.spelling_error
            result = False

        if not result:
            self._status_color = wx.RED
        return result
Esempio n. 3
0
 def validate(html_string: str, schema: str) -> (bool, List[str]):
     """
     Validate a document against a xml schema.
     :param html_string: Html document as string.
     :param schema: The name of the schema to use.
     :return: Tuple of boolean validation result and optional list of error messages.
     :raises UnrecognizedFileException if html parse fails.
     :raises UnrecognizedFileException if xml schema is incorrect.
     """
     errors = []
     try:
         xmlschema = etree.XMLSchema(
             etree.parse(Fetch.get_resource_path(schema)))
         xml_doc = html.fromstring(html_string)
         is_valid = xmlschema.validate(xml_doc)
     except XMLSchemaParseError as e:
         raise UnrecognizedFileException(
             Strings.exception_schema_syntax_error + ':\n' + str(e))
     except XMLSyntaxError as e:
         raise UnrecognizedFileException(
             Strings.exception_html_syntax_error + ':\n' + str(e))
     except ParserError as e:
         raise UnrecognizedFileException(
             Strings.exception_html_syntax_error + ':\n' + str(e))
     for error in xmlschema.error_log:
         errors.append(error.message)
     return is_valid, errors
    def test_self(self) -> bool:
        """
        SEO test self for alt and link title. If the image and thumbnail is not accessible on disk, set a
        special warning image.
        :return: True if test is ok, False otherwise
        """
        # First test the base class seo attributes
        result = super(ImageInText, self).test_self()

        # Check thumbnail image disk path
        if not self._thumbnail_path or not os.path.exists(
                self._thumbnail_path):
            # The image has generic text and can be reused.
            self._image = wx.Image(
                Fetch.get_resource_path('main_image_thumbnail_missing.png'),
                wx.BITMAP_TYPE_PNG)
            self._thumbnail_size = (0, 0)
            result = False
        else:
            # Image thumbnails in text must not be larger than 534 px.
            image = wx.Image(Fetch.get_resource_path(self._thumbnail_path),
                             wx.BITMAP_TYPE_ANY)
            self._thumbnail_size = image.GetSize()
            if self._thumbnail_size[0] <= Numbers.text_image_max_size \
                    and self._thumbnail_size[1] <= Numbers.text_image_max_size:
                self._image = image
            else:
                self._image = wx.Image(
                    Fetch.get_resource_path('main_image_thumbnail_wrong.png'),
                    wx.BITMAP_TYPE_PNG)
                self._thumbnail_size = self._image.GetSize()
                result = False

            # Check full image disk path, size can be whatever the user likes
            if not self._original_image_path or not os.path.exists(
                    self._original_image_path):
                self._image = wx.Image(
                    Fetch.get_resource_path('main_image_missing.png'),
                    wx.BITMAP_TYPE_PNG)
                result = False

        if not result:
            self._status_color = wx.RED
        return result
 def _button_to_cancel(self) -> None:
     """
     Change the upload button to the cancel upload state.
     :return: None
     """
     self._upload_button.SetBitmap(
         wx.Bitmap(Fetch.get_resource_path('upload_cancel.png'),
                   wx.BITMAP_TYPE_PNG))
     self._upload_button.SetBitmapPosition(wx.TOP)
     self._upload_button.SetLabelText(Strings.button_cancel)
 def get_original_size(self) -> (int, int):
     """
     Return a tuple of this image's original size (width, height).
     :return: Return a tuple of this image's original size (width, height).
     """
     if not self._original_image_path:
         return None
     if not os.path.exists(self._original_image_path):
         return None
     image = wx.Image(Fetch.get_resource_path(self._original_image_path),
                      wx.BITMAP_TYPE_ANY)
     self._original_size = image.GetSize()
     return self._original_size
Esempio n. 7
0
    def test_self(self, online: bool) -> bool:
        """
        SEO check self for correct title, url and dimensions.
        :param online: Do online url test.
        :return: True if no error is found.
        """
        # Disk paths have to be checked by the subclasses.
        # Clear all error before each retest
        self._link_title_error_message = ''
        self._url_error_message = ''
        self._size_error_message = ''
        self._status_color = wx.NullColour

        result = True
        self._image = wx.Image(Fetch.get_resource_path('video_placeholder.png'), wx.BITMAP_TYPE_PNG)
        # Check video link title
        if len(self._link_title) < Numbers.article_image_title_min or len(
                self._link_title) > Numbers.article_image_title_max:
            self._link_title_error_message = Strings.seo_error_link_title_length
            self._image = wx.Image(Fetch.get_resource_path('video_seo_error.png'), wx.BITMAP_TYPE_PNG)
            result = False

        # Check dimensions
        if self._width != Numbers.video_width or self._height != Numbers.video_height:
            self._size_error_message = Strings.seo_error_video_size_wrong
            self._image = wx.Image(Fetch.get_resource_path('video_size_incorrect.png'), wx.BITMAP_TYPE_PNG)
            result = False

        # Check url, if online test is not run on document switching this causes wrong results.
        if online:
            h = httplib2.Http(timeout=Numbers.online_test_timeout)
            try:
                resp = h.request(self._url, 'HEAD')
                if int(resp[0]['status']) >= 400:
                    self._url_error_message = Strings.seo_error_url_nonexistent
                    self._image = wx.Image(Fetch.get_resource_path('video_seo_error.png'), wx.BITMAP_TYPE_PNG)
                    result = False
            except KeyError as _:
                self._url_error_message = Strings.seo_error_url_malformed
                result = False
            except (httplib2.ServerNotFoundError, httplib2.RelativeURIError, SSLCertVerificationError) as _:
                self._url_error_message = Strings.seo_error_url_nonexistent
                self._image = wx.Image(Fetch.get_resource_path('video_seo_error.png'), wx.BITMAP_TYPE_PNG)
                result = False
            except ConnectionResetError as _:
                pass
            finally:
                h.close()

        # Spell check
        if not self._spell_check(self._link_title):
            self._link_title_error_message = Strings.spelling_error
            self._image = wx.Image(Fetch.get_resource_path('video_seo_error.png'), wx.BITMAP_TYPE_PNG)
            result = False

        if not result:
            self._status_color = wx.RED
        return result
Esempio n. 8
0
    def __init__(self, parent, work_dir: str):
        """
        This dialog helps with adding a new menu logo into the folder structure of the website.
        Display a dialog with information about the image where the user can edit it.
        :param parent: Parent frame.
        :param work_dir: The working directory of the editor.
        """
        wx.Dialog.__init__(self, parent, title=Strings.label_dialog_add_logo,
                           size=(Numbers.add_logo_dialog_width, Numbers.add_logo_dialog_height),
                           style=wx.DEFAULT_DIALOG_STYLE)

        self._main_vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        self._horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        self._information_sizer = wx.BoxSizer(wx.VERTICAL)
        self._image_path = None
        self._image_name = None
        self._menu_image = None
        self._logos_path = None
        self._working_directory = work_dir
        self._file_path = None
        self._config_manager = ConfigManager.get_instance()

        # Disk location
        self._original_disk_location_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._label_image_original_path = wx.StaticText(self, -1, Strings.label_image + ': ')
        self._content_image_original_path = wx.StaticText(self, -1, Strings.label_none,
                                                          style=wx.ST_ELLIPSIZE_MIDDLE | wx.ST_NO_AUTORESIZE)
        self._original_disk_location_sub_sizer.Add(self._label_image_original_path,
                                                   flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
        self._original_disk_location_sub_sizer.Add(self._content_image_original_path, 1, flag=wx.EXPAND)
        self._information_sizer.Add(self._original_disk_location_sub_sizer, flag=wx.EXPAND | wx.TOP,
                                    border=Numbers.widget_border_size)

        # Original size
        self._image_original_size_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._label_image_size = wx.StaticText(self, -1, Strings.label_size + ': ')
        self._content_image_size = wx.StaticText(self, -1, Strings.label_none,
                                                 style=wx.ST_ELLIPSIZE_MIDDLE | wx.ST_NO_AUTORESIZE)
        self._image_original_size_sub_sizer.Add(self._label_image_size,
                                                flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
        self._image_original_size_sub_sizer.Add((14, -1))
        self._image_original_size_sub_sizer.Add(self._content_image_size, 1, flag=wx.EXPAND)
        self._information_sizer.Add(self._image_original_size_sub_sizer, flag=wx.EXPAND | wx.TOP,
                                    border=Numbers.widget_border_size)

        # Image name sub sizer
        self._name_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._label_image_name = wx.StaticText(self, -1, Strings.label_name + ': ')
        self._field_image_name = wx.TextCtrl(self, -1)
        self._field_image_name.Disable()
        self._name_sub_sizer.Add(self._label_image_name, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
        self._name_sub_sizer.Add((5, -1))
        self._name_sub_sizer.Add(self._field_image_name, proportion=1)
        self._information_sizer.Add(self._name_sub_sizer, flag=wx.EXPAND | wx.TOP, border=Numbers.widget_border_size)
        self._field_image_name_tip = Tools.get_warning_tip(self._field_image_name, Strings.label_image_name)
        self._field_image_name_tip.SetMessage('')

        # Image preview
        self._image_sizer = wx.BoxSizer(wx.VERTICAL)
        self._bitmap = wx.StaticBitmap(self, -1, wx.Bitmap(wx.Image(Fetch.get_resource_path('menu_image_missing.png'),
                                                                    wx.BITMAP_TYPE_PNG)))
        self._image_sizer.Add(self._bitmap, flag=wx.ALL, border=1)

        # Buttons
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        grouping_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._cancel_button = wx.Button(self, wx.ID_CANCEL, Strings.button_cancel)
        self._save_button = wx.Button(self, wx.ID_OK, Strings.button_save)
        self._browse_button = wx.Button(self, wx.ID_OPEN, Strings.button_browse)
        self._save_button.Disable()
        self._browse_button.SetDefault()
        grouping_sizer.Add(self._save_button)
        grouping_sizer.Add((Numbers.widget_border_size, Numbers.widget_border_size))
        grouping_sizer.Add(self._cancel_button)
        grouping_sizer.Add((Numbers.widget_border_size, Numbers.widget_border_size))
        grouping_sizer.Add(self._browse_button)
        self._button_sizer.Add(grouping_sizer, flag=wx.ALIGN_CENTER_HORIZONTAL)

        # Putting the sizers together
        self._vertical_sizer.Add(self._information_sizer, 0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
                                 border=Numbers.widget_border_size)
        self._horizontal_sizer.Add(self._vertical_sizer, 1)
        self._horizontal_sizer.Add(self._image_sizer, flag=wx.TOP | wx.RIGHT, border=Numbers.widget_border_size)
        self._main_vertical_sizer.Add(self._horizontal_sizer, 1, flag=wx.EXPAND)
        self._main_vertical_sizer.Add(self._button_sizer, 0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                                      border=Numbers.widget_border_size)
        self.SetSizer(self._main_vertical_sizer)

        # Bind handlers
        self.Bind(wx.EVT_BUTTON, self._handle_buttons, self._save_button)
        self.Bind(wx.EVT_BUTTON, self._handle_buttons, self._cancel_button)
        self.Bind(wx.EVT_BUTTON, self._handle_buttons, self._browse_button)
        self.Bind(wx.EVT_TEXT, self._switch_default_button, self._field_image_name)
    def __init__(self, parent, menus: Dict[str, WhitebearDocumentMenu],
                 articles: Dict[str, WhitebearDocumentArticle],
                 css: WhitebearDocumentCSS, index: WhitebearDocumentIndex):
        """
        Display a dialog that allows editing additional data used in html generation.
        Default main title, author, contact, keywords, main page meta description. script, main page red/black text
        :param parent: The parent frame.
        :param menus: Currently loaded dictionary of menus.
        :param articles: Currently loaded dictionary of articles.
        :param css: Currently loaded css document.
        :param index: Currently loaded index document.
        """
        wx.Dialog.__init__(self,
                           parent,
                           title=Strings.label_dialog_new_document,
                           size=(Numbers.new_file_dialog_width,
                                 Numbers.new_file_dialog_height),
                           style=wx.DEFAULT_DIALOG_STYLE)

        self._config_manager: ConfigManager = ConfigManager.get_instance()
        self._doc = None
        self._menus = menus
        self._articles = articles
        self._css_document = css
        self._index = index
        self._article_image = None
        self._menu_item = None
        self._document_path = None

        self._main_vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        self._horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        self._information_sizer = wx.BoxSizer(wx.VERTICAL)

        # Name sub sizer
        self._name_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._label_file_name = wx.StaticText(self, -1,
                                              Strings.label_file_name + ': ')
        self._field_name = wx.TextCtrl(self, -1)
        self._name_sub_sizer.Add(self._label_file_name,
                                 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
        self._name_sub_sizer.Add(self._field_name, proportion=1)
        self._information_sizer.Add(self._name_sub_sizer,
                                    flag=wx.EXPAND | wx.TOP,
                                    border=Numbers.widget_border_size)
        self._field_name_tip = Tools.get_warning_tip(self._field_name,
                                                     Strings.label_file_name)
        self._field_name.SetBackgroundColour(Numbers.RED_COLOR)
        self._field_name_tip.SetMessage(Strings.warning_empty)

        choices: List[str] = [
            menu.get_page_name()[0] for menu in self._menus.values()
        ]
        choices.append('-')
        # Category sub sizer
        self._category_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._label_category = wx.StaticText(
            self, -1, Strings.label_target_section + ': ')
        self._box_menu = wx.ComboBox(self,
                                     -1,
                                     choices=choices,
                                     style=wx.CB_DROPDOWN | wx.CB_SORT
                                     | wx.CB_READONLY)
        self._box_menu.SetSelection(0)
        self._box_menu.SetBackgroundColour(Numbers.RED_COLOR)
        self._category_sub_sizer.Add(self._label_category,
                                     flag=wx.ALIGN_LEFT
                                     | wx.ALIGN_CENTER_VERTICAL)
        self._category_sub_sizer.Add(16, -1)
        self._category_sub_sizer.Add(self._box_menu, proportion=1)
        self._information_sizer.Add(self._category_sub_sizer,
                                    flag=wx.EXPAND | wx.TOP,
                                    border=Numbers.widget_border_size)

        # Image buttons
        self._image_buttons_sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._menu_logo_button = wx.Button(
            self,
            wx.ID_FILE1,
            style=wx.BU_EXACTFIT | wx.BORDER_NONE,
            size=wx.Size(Numbers.menu_logo_image_size,
                         Numbers.menu_logo_image_size))
        self._menu_logo_button.Disable()
        self._menu_logo_button.SetBitmap(
            wx.Bitmap(
                wx.Image(Fetch.get_resource_path('menu_image.png'),
                         wx.BITMAP_TYPE_PNG)))
        self._main_image_button = wx.Button(self,
                                            wx.ID_FILE2,
                                            style=wx.BU_EXACTFIT
                                            | wx.BORDER_NONE)
        self._main_image_button.Disable()
        self._main_image_button.SetBitmap(
            wx.Bitmap(
                wx.Image(Fetch.get_resource_path('article_image.png'),
                         wx.BITMAP_TYPE_PNG)))
        self._image_buttons_sub_sizer.Add(self._main_image_button,
                                          1,
                                          flag=wx.EXPAND)
        self._image_buttons_sub_sizer.Add(Numbers.widget_border_size,
                                          Numbers.widget_border_size)
        self._image_buttons_sub_sizer.Add(self._menu_logo_button,
                                          1,
                                          flag=wx.EXPAND)
        self._information_sizer.Add(self._image_buttons_sub_sizer,
                                    flag=wx.EXPAND | wx.TOP,
                                    border=Numbers.widget_border_size)

        # Buttons
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        grouping_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._cancel_button = wx.Button(self, wx.ID_CANCEL,
                                        Strings.button_cancel)
        self._ok_button = wx.Button(self, wx.ID_OK, Strings.button_ok)
        self._ok_button.SetDefault()
        self._ok_button.Disable()
        grouping_sizer.Add(self._ok_button)
        grouping_sizer.Add(
            (Numbers.widget_border_size, Numbers.widget_border_size))
        grouping_sizer.Add(self._cancel_button)
        grouping_sizer.Add(
            (Numbers.widget_border_size, Numbers.widget_border_size))
        self._button_sizer.Add(grouping_sizer, flag=wx.ALIGN_CENTER_HORIZONTAL)

        # Putting the sizers together
        self._vertical_sizer.Add(self._information_sizer,
                                 0,
                                 flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
                                 border=Numbers.widget_border_size)
        self._horizontal_sizer.Add(self._vertical_sizer, 1)
        self._main_vertical_sizer.Add(self._horizontal_sizer,
                                      1,
                                      flag=wx.EXPAND)
        self._main_vertical_sizer.Add(self._button_sizer,
                                      0,
                                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT
                                      | wx.BOTTOM,
                                      border=Numbers.widget_border_size)
        self.SetSizer(self._main_vertical_sizer)

        # Bind handlers
        self.Bind(wx.EVT_BUTTON, self._handle_buttons, self._ok_button)
        self.Bind(wx.EVT_BUTTON, self._handle_buttons, self._cancel_button)
        self.Bind(wx.EVT_TEXT, self._handle_fields, self._field_name)
        self.Bind(wx.EVT_BUTTON, self._handle_image_buttons,
                  self._main_image_button)
        self.Bind(wx.EVT_BUTTON, self._handle_image_buttons,
                  self._menu_logo_button)
        self.Bind(wx.EVT_COMBOBOX, self._handle_fields, self._box_menu)

        # If there are no menus, disable the dialog.
        if not choices:
            self._field_name.Disable()
            self._box_menu.Disable()
Esempio n. 10
0
    def convert_to_html(self) -> None:
        """
        Converts this document into a html white bear article page.
        :return: None
        :raise UnrecognizedFileException if template file can not be validated.
        :raise UnrecognizedFileException if html parse fails.
        :raise UnrecognizedFileException if generated html fails validation.
        :raises UnrecognizedFileException if xml schema is incorrect.
        """
        with open(Fetch.get_resource_path('article_template.html'),
                  'r') as template:
            template_string = template.read()
        is_valid, errors = Tools.validate(template_string,
                                          'schema_article_template.xsd')
        if not is_valid:
            raise UnrecognizedFileException(
                Strings.exception_html_syntax_error + '\n' +
                'article_template.html ' + str(errors))

        parsed_template = BeautifulSoup(template_string, 'html5lib')

        # Fill title.
        title: Tag = parsed_template.find(name='title')
        title.string = self._page_name + ' - ' + self._menu_section.get_section_name(
        ) + ' | ' + Strings.page_name

        # Fill description.
        description = parsed_template.find_all(name='meta',
                                               attrs={
                                                   'name': 'description',
                                                   'content': True
                                               })
        if len(description) == 1:
            description[0]['content'] = self._meta_description
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_descriptions)

        # Fill keywords.
        keywords = parsed_template.find_all(name='meta',
                                            attrs={
                                                'name': 'keywords',
                                                'content': True
                                            })
        if len(keywords) == 1:
            keywords[0]['content'] = ', '.join(self._meta_keywords)
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_keywords)

        # Fill author.
        author = parsed_template.find_all(name='meta',
                                          attrs={
                                              'name': 'author',
                                              'content': True
                                          })
        if len(author) == 1:
            author[0]['content'] = self._config_manager.get_author()
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_authors)

        # Fill script.
        script = parsed_template.find(name='script')
        script.string = self._config_manager.get_script()

        # Fill global title.
        figure = parsed_template.find(name='header').figure
        figure.figcaption.string = self._config_manager.get_global_title()
        heading = parsed_template.find(name='h1', attrs={'id': 'heading'})
        heading.string = self._config_manager.get_global_title()

        # Activate correct menu, generate menu items according to menus.
        menu_container = parsed_template.find(name='nav')
        for instance in sorted(self._menus.values(),
                               key=lambda x: x.get_section_name(),
                               reverse=True):
            new_item = parsed_template.new_tag('a',
                                               attrs={
                                                   'class':
                                                   'menu',
                                                   'href':
                                                   instance.get_filename(),
                                                   'title':
                                                   instance.get_page_name()[0]
                                               })
            new_item.string = instance.get_page_name()[0]
            if instance.get_filename() == self._menu_section.get_filename():
                new_item['id'] = 'active'
            menu_container.append(new_item)

        # Fill main page title.
        article = parsed_template.find(name='article',
                                       attrs={'class': 'textPage'})
        article.h2.string = self._page_name

        # Fill date.
        parsed_template.find(name='p', attrs={
            'id': 'date'
        }).string = self._date

        # Fill main image.
        main_image_figure = parsed_template.find(name='figure',
                                                 attrs={'id': 'articleImg'})
        main_image_figure.a['href'] = self.get_article_image(
        ).get_full_filename()
        main_image_figure.a['title'] = self.get_article_image().get_link_title(
        )[0]
        main_image_figure.img['src'] = self.get_article_image(
        ).get_thumbnail_filename()
        main_image_figure.img['alt'] = self.get_article_image().get_image_alt(
        )[0]
        main_image_figure.figcaption.string = self.get_article_image(
        ).get_caption()[0]

        # Fill main text.
        text_section = parsed_template.find(name='section', attrs='mainText')
        if not self._enabled:
            # Save the disabled state into the html, once the article is enabled, this special class will be removed.
            text_section['class'] = 'mainText disabled'
        for element in self.get_main_text_elements():
            if isinstance(element, Heading):
                size = 'h3' if element.get_size() == Heading.SIZE_H3 else 'h4'
                text = element.get_text().get_text()
                color = element.get_text().get_color()
                new_h = parsed_template.new_tag(size)
                new_h.string = text
                if color != Strings.color_black:
                    # Black text is default, so ignore it.
                    new_h['class'] = color
                text_section.append(new_h)
            elif isinstance(element, ImageInText):
                new_div = parsed_template.new_tag('div',
                                                  attrs={'class': 'center'})
                href = element.get_full_filename()
                title = element.get_link_title()[0]
                src = element.get_thumbnail_filename()
                alt = element.get_image_alt()[0]
                width = element.get_thumbnail_size()[0]
                height = element.get_thumbnail_size()[1]
                new_a = parsed_template.new_tag('a',
                                                attrs={
                                                    'href': href,
                                                    'target': Strings.blank,
                                                    'title': title
                                                })
                new_img = parsed_template.new_tag('img',
                                                  attrs={
                                                      'src': src,
                                                      'alt': alt,
                                                      'width': width,
                                                      'height': height
                                                  })
                new_a.append(new_img)
                new_div.append(new_a)
                text_section.append(new_div)
            elif isinstance(element, Video):
                new_div = parsed_template.new_tag('div',
                                                  attrs={'class': 'center'})
                title = element.get_title()[0]
                width = element.get_size()[0]
                height = element.get_size()[1]
                src = element.get_url()[0]
                new_iframe = parsed_template.new_tag('iframe',
                                                     attrs={
                                                         'title': title,
                                                         'height': height,
                                                         'width': width,
                                                         'src': src,
                                                         'allowfullscreen':
                                                         None
                                                     })
                new_div.append(new_iframe)
                text_section.append(new_div)
            elif isinstance(element, Paragraph):
                new_p = parsed_template.new_tag('p')
                new_p = self._convert_text_contents(new_p, element,
                                                    parsed_template)
                text_section.append(new_p)
            elif isinstance(element, UnorderedList):
                new_ul = parsed_template.new_tag('ul')
                for par in element.get_paragraphs():
                    new_li = parsed_template.new_tag('li')
                    self._convert_text_contents(new_li, par, parsed_template)
                    new_ul.append(new_li)
                text_section.append(new_ul)

        # Fill aside images.
        aside = parsed_template.find(name='aside')
        for img in self._aside_images:
            new_figure = parsed_template.new_tag('figure')
            new_figcaption = parsed_template.new_tag(
                'figcaption', attrs={'class': 'photoCaption'})
            href = img.get_full_filename()
            title = img.get_link_title()[0]
            src = img.get_thumbnail_filename()
            alt = img.get_image_alt()[0]
            text = img.get_caption()[0]
            new_figcaption.string = text
            new_a = parsed_template.new_tag('a',
                                            attrs={
                                                'href': href,
                                                'target': Strings.blank,
                                                'title': title
                                            })
            # Width and height are different from the thumbnail here because the image expands in the page.
            new_img = parsed_template.new_tag(
                'img',
                attrs={
                    'src': src,
                    'alt': alt,
                    'width': Numbers.aside_thumbnail_width,
                    'height': Numbers.aside_thumbnail_height,
                    'class': 'imgAside'
                })
            new_a.append(new_img)
            new_figure.append(new_a)
            new_figure.append(new_figcaption)
            aside.append(new_figure)

        output = str(parsed_template)
        is_valid, errors = Tools.validate(output, 'schema_article.xsd')
        if not is_valid:
            raise UnrecognizedFileException(Strings.exception_bug + '\n' +
                                            self.get_filename() + ' \n' +
                                            str(errors))

        self._html = output
        # Save the fact that this file is changed into the list of file that we need to upload. This survives editor
        # exit and can be restored on start. This list is cleared when a file is uploaded.
        self._config_manager.store_not_uploaded(self.get_filename())
    def convert_to_html(self) -> None:
        """
        Converts this document into a html white bear menu page.
        :return: None
        :raise UnrecognizedFileException if template file can not be validated.
        :raise UnrecognizedFileException if html parse fails.
        :raise UnrecognizedFileException if generated html fails validation.
        :raises UnrecognizedFileException if xml schema is incorrect.
        """
        with open(Fetch.get_resource_path('menu_template.html'), 'r') as template:
            template_string = template.read()
        is_valid, errors = Tools.validate(template_string, 'schema_menu_template.xsd')
        if not is_valid:
            raise UnrecognizedFileException(Strings.exception_html_syntax_error + '\n' + 'menu_template.html ' +
                                            str(errors))

        parsed_template = BeautifulSoup(template_string, 'html5lib')

        # Fill title.
        title: Tag = parsed_template.find(name='title')
        title.string = Strings.menu_title_stump + ' ' + self._page_name + ' | ' + Strings.page_name

        # Fill description.
        description = parsed_template.find_all(name='meta', attrs={'name': 'description', 'content': True})
        if len(description) == 1:
            description[0]['content'] = self._meta_description
        else:
            raise UnrecognizedFileException(Strings.exception_parse_multiple_descriptions)

        # Fill keywords.
        keywords = parsed_template.find_all(name='meta', attrs={'name': 'keywords', 'content': True})
        if len(keywords) == 1:
            keywords[0]['content'] = ', '.join(self._meta_keywords)
        else:
            raise UnrecognizedFileException(Strings.exception_parse_multiple_keywords)

        # Fill author.
        author = parsed_template.find_all(name='meta', attrs={'name': 'author', 'content': True})
        if len(author) == 1:
            author[0]['content'] = self._config_manager.get_author()
        else:
            raise UnrecognizedFileException(Strings.exception_parse_multiple_authors)

        # Fill script.
        script = parsed_template.find(name='script')
        script.string = self._config_manager.get_script()

        # Fill global title.
        figure = parsed_template.find(name='header').figure
        figure.figcaption.string = self._config_manager.get_global_title()
        heading = parsed_template.find(name='h1', attrs={'id': 'heading'})
        heading.string = self._config_manager.get_global_title()

        # Activate correct menu, generate menu items according to menus.
        menu_container = parsed_template.find(name='nav')
        for instance in sorted(self._menus.values(), key=lambda x: x.get_section_name(), reverse=True):
            new_item = parsed_template.new_tag('a', attrs={'class': 'menu', 'href': instance.get_filename(),
                                                           'title': instance.get_page_name()[0]})
            new_item.string = instance.get_page_name()[0]
            if instance.get_filename() == self._file_name:
                new_item['id'] = 'active'
            menu_container.append(new_item)

        # Fill main page title.
        article = parsed_template.find(name='article', attrs={'class': 'menuPage'})
        article.h2.string = self._page_name

        # Fill menu items.
        menu_container = parsed_template.find(name='nav', attrs={'class': 'sixItems'})
        for item in self._menu_items:
            item: MenuItem
            attrs = {'class': 'link'}
            if not item.get_article():
                # Skip deleted articles.
                continue
            if not item.get_article().test_self(self._config_manager.get_online_test()):
                # Hide this menu item because this article is not yet finished or is deleted. The item will be available
                # for future parsing though so the editor will load the menu item correctly for the unfinished article.
                attrs['class'] = 'link hidden'
            new_div = parsed_template.new_tag('div', attrs=attrs)
            href = item.get_link_href()
            title = item.get_link_title()[0]
            width = item.get_image_size()[0]
            height = item.get_image_size()[1]
            src = item.get_filename()
            alt = item.get_image_alt()[0]
            text = item.get_article_name()[0]
            new_a = parsed_template.new_tag('a', attrs={'href': href, 'title': title})
            new_img = parsed_template.new_tag('img', attrs={'width': width, 'height': height, 'src': src, 'alt': alt})
            new_p = parsed_template.new_tag('p')
            new_p.string = text

            new_a.append(new_img)
            new_div.append(new_a)
            new_div.append(new_p)
            menu_container.append(new_div)

        output = str(parsed_template)
        is_valid, errors = Tools.validate(output, 'schema_menu.xsd')
        if not is_valid:
            raise UnrecognizedFileException(Strings.exception_bug + '\n' + self.get_filename() + ' \n' + str(errors))

        self._html = output
Esempio n. 12
0
    def convert_to_html(self) -> None:
        """
        Converts this document into a html white bear article page.
        :return: None
        :raise UnrecognizedFileException if template file can not be validated.
        :raise UnrecognizedFileException if html parse fails.
        :raise UnrecognizedFileException if generated html fails validation.
        :raises UnrecognizedFileException if xml schema is incorrect.
        """
        self.update_content()
        with open(Fetch.get_resource_path('index_template.html'),
                  'r') as template:
            template_string = template.read()
        is_valid, errors = Tools.validate(template_string,
                                          'schema_index_template.xsd')
        if not is_valid:
            raise UnrecognizedFileException(
                Strings.exception_html_syntax_error + '\n' +
                'index_template.html ' + str(errors))

        parsed_template = BeautifulSoup(template_string, 'html5lib')

        # Fill title.
        title: Tag = parsed_template.find(name='title')
        title.string = self._page_name + ' | ' + self._global_title

        # Fill description.
        description = parsed_template.find_all(name='meta',
                                               attrs={
                                                   'name': 'description',
                                                   'content': True
                                               })
        if len(description) == 1:
            description[0]['content'] = self._meta_description
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_descriptions)

        # Fill keywords.
        keywords = parsed_template.find_all(name='meta',
                                            attrs={
                                                'name': 'keywords',
                                                'content': True
                                            })
        if len(keywords) == 1:
            keywords[0]['content'] = self._meta_keywords
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_authors)

        # Fill author.
        author = parsed_template.find_all(name='meta',
                                          attrs={
                                              'name': 'author',
                                              'content': True
                                          })
        if len(author) == 1:
            author[0]['content'] = self._author
        else:
            raise UnrecognizedFileException(
                Strings.exception_parse_multiple_descriptions)

        # Fill script.
        script = parsed_template.find(name='script')
        script.string = self._script

        # Fill global title.
        figure = parsed_template.find(name='header').figure
        figure.figcaption.string = self._global_title
        heading = parsed_template.find(name='h1', attrs={'id': 'heading'})
        heading.string = self._global_title

        # Activate correct menu, generate menu items according to menus.
        menu_container = parsed_template.find(name='nav')
        for instance in sorted(self._menus.values(),
                               key=lambda x: x.get_section_name(),
                               reverse=True):
            new_item = parsed_template.new_tag('a',
                                               attrs={
                                                   'class':
                                                   'menu',
                                                   'href':
                                                   instance.get_filename(),
                                                   'title':
                                                   instance.get_page_name()[0]
                                               })
            new_item.string = instance.get_page_name()[0]
            if instance.get_filename() == self.get_filename():
                new_item['id'] = 'active'
            menu_container.append(new_item)

        # Fill main page title.
        article = parsed_template.find(name='article',
                                       attrs={'class': 'indexPage'})
        article.h2.string = self._page_name

        # Fill text.
        new_black_p = parsed_template.new_tag('p')
        new_black_p.string = self._black_text

        new_red_p = parsed_template.new_tag('p')
        new_strong = parsed_template.new_tag('strong', attrs={'class': 'red'})
        new_strong.string = self._red_text
        new_red_p.append(new_strong)

        article.h2.insert_after(new_red_p)
        article.h2.insert_after(new_black_p)

        # Fill news.
        news = parsed_template.find(name='h3', attrs={'id': 'news'})
        # Sort all articles by date.
        sorted_articles = sorted(self._articles.values(),
                                 key=lambda x: x.get_computable_date(),
                                 reverse=True)
        new_ul = parsed_template.new_tag('ul')
        limit = self._number_of_news
        for index, item in enumerate(sorted_articles):
            if index >= limit:
                break
            if item.test_self(self._config_manager.get_online_test()):
                new_li = parsed_template.new_tag('li')
                href = item.get_filename()
                title = item.get_page_name()[0]
                date = item.get_date()[0] + ' '
                new_a = parsed_template.new_tag('a',
                                                attrs={
                                                    'href': href,
                                                    'title': title
                                                })
                new_a.string = title

                new_li.string = date
                new_li.append(new_a)
                new_ul.append(new_li)
            else:
                # Add the next article to news.
                limit = limit + 1
        news.insert_after(new_ul)

        # Fill contact.
        contact = parsed_template.find(name='h3', attrs={'id': 'contact'})
        # Insert the author's contact as an image.
        image: wx.Bitmap = Tools.create_image(self._contact)
        image_path = os.path.join(self._working_directory,
                                  Strings.folder_images, Strings.contact_file)
        image.SaveFile(image_path, wx.BITMAP_TYPE_PNG)
        src = os.path.join(Strings.folder_images, Strings.contact_file)
        new_img = parsed_template.new_tag('img',
                                          attrs={
                                              'width': image.GetWidth(),
                                              'height': image.GetHeight(),
                                              'src': src,
                                              'alt':
                                              Strings.contact_default_alt
                                          })
        contact.insert_after(new_img)

        # Fill design.
        new_p = parsed_template.new_tag('p')
        new_p.string = 'Web design: ' + self._author
        new_img.insert_after(new_p)

        # Fill aside images from the newest articles.
        latest_images = []
        for article in sorted_articles:
            if article.is_enabled():
                images = article.get_aside_images()
                if images:
                    latest_images.append(images[0])
                if len(latest_images) >= Numbers.max_index_images:
                    break

        aside = parsed_template.find(name='aside')
        for img in latest_images:
            new_figure = parsed_template.new_tag('figure')
            new_figcaption = parsed_template.new_tag(
                'figcaption', attrs={'class': 'photoCaption'})
            href = img.get_full_filename()
            title = img.get_link_title()[0]
            src = img.get_thumbnail_filename()
            alt = img.get_image_alt()[0]
            text = img.get_caption()[0]
            new_figcaption.string = text
            new_a = parsed_template.new_tag('a',
                                            attrs={
                                                'href': href,
                                                'target': Strings.blank,
                                                'title': title
                                            })
            # Width and height are different from the thumbnail here because the image expands in the page.
            new_img = parsed_template.new_tag(
                'img',
                attrs={
                    'src': src,
                    'alt': alt,
                    'width': Numbers.aside_thumbnail_width,
                    'height': Numbers.aside_thumbnail_height,
                    'class': 'imgAside'
                })
            new_a.append(new_img)
            new_figure.append(new_a)
            new_figure.append(new_figcaption)
            aside.append(new_figure)

        output = str(parsed_template)
        is_valid, errors = Tools.validate(output, 'schema_index.xsd')
        if not is_valid:
            raise UnrecognizedFileException(Strings.exception_bug + '\n' +
                                            self.get_filename() + ' \n' +
                                            str(errors))

        self._html = output
Esempio n. 13
0
    def test_self(self) -> bool:
        """
        SEO test self for page name, alt and link title. If the menu image is not accessible on disk, set a special
        warning image.
        :return: True if test is ok, False otherwise
        """
        # Clear all error before each retest
        self._article_name_error_message: str = ''
        self._link_title_error_message: str = ''
        self._image_alt_error_message: str = ''
        self._status_color = wx.NullColour

        result = True
        # Check page name length must be at least 3 and must not be default
        if len(self._article_name) < Numbers.menu_name_min_length or len(
                self._article_name) > Numbers.menu_name_max_length:
            self._article_name_error_message = Strings.seo_error_menu_name_length
            result = False

        if self._article_name == Strings.label_article_menu_logo_name_placeholder:
            self._article_name_error_message = Strings.seo_error_default_value
            result = False

        # Check menu image disk path
        if not self._menu_image_path:
            self._menu_image = wx.Image(Fetch.get_resource_path('menu_image_missing.png'), wx.BITMAP_TYPE_PNG)
            result = False
        else:
            try:
                image = wx.Image(Fetch.get_resource_path(self._menu_image_path), wx.BITMAP_TYPE_ANY)
                if image.GetSize() == (Numbers.menu_logo_image_size, Numbers.menu_logo_image_size):
                    self._menu_image = image
                else:
                    self._menu_image = wx.Image(Fetch.get_resource_path('menu_image_wrong.png'), wx.BITMAP_TYPE_PNG)
                    result = False
            except FileNotFoundError as _:
                self._menu_image = wx.Image(Fetch.get_resource_path('menu_image_missing.png'), wx.BITMAP_TYPE_PNG)
                result = False

        # Check article image link title
        if len(self._link_title) < Numbers.article_image_title_min or len(
                self._link_title) > Numbers.article_image_title_max:
            self._link_title_error_message = Strings.seo_error_link_title_length
            result = False

        if self._link_title == Strings.label_menu_logo_link_title_placeholder:
            self._link_title_error_message = Strings.seo_error_default_value
            result = False

        # Check article image alt
        if len(self._image_alt) < Numbers.article_image_alt_min or len(
                self._image_alt) > Numbers.article_image_alt_max:
            self._image_alt_error_message = Strings.seo_error_image_alt_length
            result = False

        if self._image_alt == Strings.label_article_image_alt:
            self._image_alt_error_message = Strings.seo_error_default_value
            result = False

        # Spellchecks
        if not self._spell_check(self._article_name):
            self._article_name_error_message = Strings.spelling_error
            result = False

        if not self._spell_check(self._link_title):
            self._link_title_error_message = Strings.spelling_error
            result = False

        if not self._spell_check(self._image_alt):
            self._image_alt_error_message = Strings.spelling_error
            result = False

        if not result:
            self._status_color = wx.RED
        return result