mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	Merge from trunk
This commit is contained in:
		
						commit
						c9cb3eba48
					
				@ -17,6 +17,7 @@ from calibre.db.tests.base import BaseTest
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class WritingTest(BaseTest):
 | 
					class WritingTest(BaseTest):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Utils {{{
 | 
				
			||||||
    def create_getter(self, name, getter=None):
 | 
					    def create_getter(self, name, getter=None):
 | 
				
			||||||
        if getter is None:
 | 
					        if getter is None:
 | 
				
			||||||
            if name.endswith('_index'):
 | 
					            if name.endswith('_index'):
 | 
				
			||||||
@ -71,6 +72,7 @@ class WritingTest(BaseTest):
 | 
				
			|||||||
                        'Failed setting for %s, sqlite value not the same: %r != %r'%(
 | 
					                        'Failed setting for %s, sqlite value not the same: %r != %r'%(
 | 
				
			||||||
                            test.name, old_sqlite_res, sqlite_res))
 | 
					                            test.name, old_sqlite_res, sqlite_res))
 | 
				
			||||||
                del db
 | 
					                del db
 | 
				
			||||||
 | 
					    # }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_one_one(self):  # {{{
 | 
					    def test_one_one(self):  # {{{
 | 
				
			||||||
        'Test setting of values in one-one fields'
 | 
					        'Test setting of values in one-one fields'
 | 
				
			||||||
 | 
				
			|||||||
@ -276,7 +276,6 @@ class Worker(Thread): # Get details {{{
 | 
				
			|||||||
            self.log.exception('Error parsing authors for url: %r'%self.url)
 | 
					            self.log.exception('Error parsing authors for url: %r'%self.url)
 | 
				
			||||||
            authors = []
 | 
					            authors = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not title or not authors or not asin:
 | 
					        if not title or not authors or not asin:
 | 
				
			||||||
            self.log.error('Could not find title/authors/asin for %r'%self.url)
 | 
					            self.log.error('Could not find title/authors/asin for %r'%self.url)
 | 
				
			||||||
            self.log.error('ASIN: %r Title: %r Authors: %r'%(asin, title,
 | 
					            self.log.error('ASIN: %r Title: %r Authors: %r'%(asin, title,
 | 
				
			||||||
@ -431,7 +430,6 @@ class Worker(Thread): # Get details {{{
 | 
				
			|||||||
        desc = re.sub(r'(?s)<!--.*?-->', '', desc)
 | 
					        desc = re.sub(r'(?s)<!--.*?-->', '', desc)
 | 
				
			||||||
        return sanitize_comments_html(desc)
 | 
					        return sanitize_comments_html(desc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def parse_comments(self, root):
 | 
					    def parse_comments(self, root):
 | 
				
			||||||
        ans = ''
 | 
					        ans = ''
 | 
				
			||||||
        desc = root.xpath('//div[@id="ps-content"]/div[@class="content"]')
 | 
					        desc = root.xpath('//div[@id="ps-content"]/div[@class="content"]')
 | 
				
			||||||
@ -637,7 +635,6 @@ class Amazon(Source):
 | 
				
			|||||||
            mi.tags = list(map(fixcase, mi.tags))
 | 
					            mi.tags = list(map(fixcase, mi.tags))
 | 
				
			||||||
        mi.isbn = check_isbn(mi.isbn)
 | 
					        mi.isbn = check_isbn(mi.isbn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create_query(self, log, title=None, authors=None, identifiers={},  # {{{
 | 
					    def create_query(self, log, title=None, authors=None, identifiers={},  # {{{
 | 
				
			||||||
            domain=None):
 | 
					            domain=None):
 | 
				
			||||||
        if domain is None:
 | 
					        if domain is None:
 | 
				
			||||||
@ -724,7 +721,10 @@ class Amazon(Source):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        def title_ok(title):
 | 
					        def title_ok(title):
 | 
				
			||||||
            title = title.lower()
 | 
					            title = title.lower()
 | 
				
			||||||
            for x in ('bulk pack', '[audiobook]', '[audio cd]'):
 | 
					            bad = ['bulk pack', '[audiobook]', '[audio cd]']
 | 
				
			||||||
 | 
					            if self.domain == 'com':
 | 
				
			||||||
 | 
					                bad.append('(spanish edition)')
 | 
				
			||||||
 | 
					            for x in bad:
 | 
				
			||||||
                if x in title:
 | 
					                if x in title:
 | 
				
			||||||
                    return False
 | 
					                    return False
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
@ -751,7 +751,6 @@ class Amazon(Source):
 | 
				
			|||||||
                        matches.append(a.get('href'))
 | 
					                        matches.append(a.get('href'))
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Keep only the top 5 matches as the matches are sorted by relevance by
 | 
					        # Keep only the top 5 matches as the matches are sorted by relevance by
 | 
				
			||||||
        # Amazon so lower matches are not likely to be very relevant
 | 
					        # Amazon so lower matches are not likely to be very relevant
 | 
				
			||||||
        return matches[:5]
 | 
					        return matches[:5]
 | 
				
			||||||
@ -795,7 +794,6 @@ class Amazon(Source):
 | 
				
			|||||||
                log.exception(msg)
 | 
					                log.exception(msg)
 | 
				
			||||||
            return as_unicode(msg)
 | 
					            return as_unicode(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        raw = clean_ascii_chars(xml_to_unicode(raw,
 | 
					        raw = clean_ascii_chars(xml_to_unicode(raw,
 | 
				
			||||||
            strip_encoding_pats=True, resolve_entities=True)[0])
 | 
					            strip_encoding_pats=True, resolve_entities=True)[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -825,7 +823,6 @@ class Amazon(Source):
 | 
				
			|||||||
                    # The error is almost always a not found error
 | 
					                    # The error is almost always a not found error
 | 
				
			||||||
                    found = False
 | 
					                    found = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if found:
 | 
					        if found:
 | 
				
			||||||
            matches = self.parse_results_page(root)
 | 
					            matches = self.parse_results_page(root)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -907,6 +904,11 @@ if __name__ == '__main__': # tests {{{
 | 
				
			|||||||
            isbn_test, title_test, authors_test, comments_test, series_test)
 | 
					            isbn_test, title_test, authors_test, comments_test, series_test)
 | 
				
			||||||
    com_tests = [  # {{{
 | 
					    com_tests = [  # {{{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            (  # Has a spanish edition
 | 
				
			||||||
 | 
					             {'title':'11/22/63'},
 | 
				
			||||||
 | 
					             [title_test('11/22/63: A Novel', exact=True), authors_test(['Stephen King']),]
 | 
				
			||||||
 | 
					             ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            (  # + in title and uses id="main-image" for cover
 | 
					            (  # + in title and uses id="main-image" for cover
 | 
				
			||||||
             {'title':'C++ Concurrency in Action'},
 | 
					             {'title':'C++ Concurrency in Action'},
 | 
				
			||||||
             [title_test('C++ Concurrency in Action: Practical Multithreading',
 | 
					             [title_test('C++ Concurrency in Action: Practical Multithreading',
 | 
				
			||||||
@ -917,8 +919,8 @@ if __name__ == '__main__': # tests {{{
 | 
				
			|||||||
            (  # Series
 | 
					            (  # Series
 | 
				
			||||||
                {'identifiers':{'amazon':'0756407117'}},
 | 
					                {'identifiers':{'amazon':'0756407117'}},
 | 
				
			||||||
                [title_test(
 | 
					                [title_test(
 | 
				
			||||||
                "Throne of the Crescent Moon"
 | 
					                "Throne of the Crescent Moon",
 | 
				
			||||||
                , exact=True), series_test('Crescent Moon Kingdoms', 1),
 | 
					                exact=True), series_test('Crescent Moon Kingdoms', 1),
 | 
				
			||||||
                comments_test('Makhslood'),
 | 
					                comments_test('Makhslood'),
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
@ -926,8 +928,8 @@ if __name__ == '__main__': # tests {{{
 | 
				
			|||||||
            (  # Different comments markup, using Book Description section
 | 
					            (  # Different comments markup, using Book Description section
 | 
				
			||||||
                {'identifiers':{'amazon':'0982514506'}},
 | 
					                {'identifiers':{'amazon':'0982514506'}},
 | 
				
			||||||
                [title_test(
 | 
					                [title_test(
 | 
				
			||||||
                "Griffin's Destiny: Book Three: The Griffin's Daughter Trilogy"
 | 
					                "Griffin's Destiny: Book Three: The Griffin's Daughter Trilogy",
 | 
				
			||||||
                , exact=True),
 | 
					                exact=True),
 | 
				
			||||||
                comments_test('Jelena'), comments_test('Leslie'),
 | 
					                comments_test('Jelena'), comments_test('Leslie'),
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
				
			|||||||
@ -279,7 +279,7 @@ class EditMetadataAction(InterfaceAction):
 | 
				
			|||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Edit metadata of selected books in library in bulk.
 | 
					        Edit metadata of selected books in library in bulk.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        rows = [r.row() for r in \
 | 
					        rows = [r.row() for r in
 | 
				
			||||||
                self.gui.library_view.selectionModel().selectedRows()]
 | 
					                self.gui.library_view.selectionModel().selectedRows()]
 | 
				
			||||||
        m = self.gui.library_view.model()
 | 
					        m = self.gui.library_view.model()
 | 
				
			||||||
        ids = [m.id(r) for r in rows]
 | 
					        ids = [m.id(r) for r in rows]
 | 
				
			||||||
@ -470,38 +470,32 @@ class EditMetadataAction(InterfaceAction):
 | 
				
			|||||||
            db.set_cover(dest_id, dest_cover)
 | 
					            db.set_cover(dest_id, dest_cover)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in db.field_metadata:  # loop thru all defined fields
 | 
					        for key in db.field_metadata:  # loop thru all defined fields
 | 
				
			||||||
          if db.field_metadata[key]['is_custom']:
 | 
					            fm = db.field_metadata[key]
 | 
				
			||||||
            colnum = db.field_metadata[key]['colnum']
 | 
					            if not fm['is_custom']:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            dt = fm['datatype']
 | 
				
			||||||
 | 
					            colnum = fm['colnum']
 | 
				
			||||||
            # Get orig_dest_comments before it gets changed
 | 
					            # Get orig_dest_comments before it gets changed
 | 
				
			||||||
            if db.field_metadata[key]['datatype'] == 'comments':
 | 
					            if dt == 'comments':
 | 
				
			||||||
                orig_dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True)
 | 
					                orig_dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for src_id in src_ids:
 | 
					            for src_id in src_ids:
 | 
				
			||||||
                dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True)
 | 
					                dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True)
 | 
				
			||||||
                src_value = db.get_custom(src_id, num=colnum, index_is_id=True)
 | 
					                src_value = db.get_custom(src_id, num=colnum, index_is_id=True)
 | 
				
			||||||
              if db.field_metadata[key]['datatype'] == 'comments':
 | 
					                if (dt == 'comments' and src_value and src_value != orig_dest_value):
 | 
				
			||||||
                if src_value and src_value != orig_dest_value:
 | 
					 | 
				
			||||||
                    if not dest_value:
 | 
					                    if not dest_value:
 | 
				
			||||||
                        db.set_custom(dest_id, src_value, num=colnum)
 | 
					                        db.set_custom(dest_id, src_value, num=colnum)
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        dest_value = unicode(dest_value) + u'\n\n' + unicode(src_value)
 | 
					                        dest_value = unicode(dest_value) + u'\n\n' + unicode(src_value)
 | 
				
			||||||
                        db.set_custom(dest_id, dest_value, num=colnum)
 | 
					                        db.set_custom(dest_id, dest_value, num=colnum)
 | 
				
			||||||
              if db.field_metadata[key]['datatype'] in \
 | 
					                if (dt in {'bool', 'int', 'float', 'rating', 'datetime'} and dest_value is None):
 | 
				
			||||||
                ('bool', 'int', 'float', 'rating', 'datetime') \
 | 
					 | 
				
			||||||
                and dest_value is None:
 | 
					 | 
				
			||||||
                    db.set_custom(dest_id, src_value, num=colnum)
 | 
					                    db.set_custom(dest_id, src_value, num=colnum)
 | 
				
			||||||
              if db.field_metadata[key]['datatype'] == 'series' \
 | 
					                if (dt == 'series' and not dest_value and src_value):
 | 
				
			||||||
                and not dest_value:
 | 
					 | 
				
			||||||
                if src_value:
 | 
					 | 
				
			||||||
                    src_index = db.get_custom_extra(src_id, num=colnum, index_is_id=True)
 | 
					                    src_index = db.get_custom_extra(src_id, num=colnum, index_is_id=True)
 | 
				
			||||||
                    db.set_custom(dest_id, src_value, num=colnum, extra=src_index)
 | 
					                    db.set_custom(dest_id, src_value, num=colnum, extra=src_index)
 | 
				
			||||||
              if (db.field_metadata[key]['datatype'] == 'enumeration' or
 | 
					                if (dt == 'enumeration' or (dt == 'text' and not fm['is_multiple']) and not dest_value):
 | 
				
			||||||
                        (db.field_metadata[key]['datatype'] == 'text' and
 | 
					 | 
				
			||||||
                         not db.field_metadata[key]['is_multiple'])
 | 
					 | 
				
			||||||
                    and not dest_value):
 | 
					 | 
				
			||||||
                    db.set_custom(dest_id, src_value, num=colnum)
 | 
					                    db.set_custom(dest_id, src_value, num=colnum)
 | 
				
			||||||
              if db.field_metadata[key]['datatype'] == 'text' \
 | 
					                if (dt == 'text' and fm['is_multiple'] and src_value):
 | 
				
			||||||
                and db.field_metadata[key]['is_multiple']:
 | 
					 | 
				
			||||||
                if src_value:
 | 
					 | 
				
			||||||
                    if not dest_value:
 | 
					                    if not dest_value:
 | 
				
			||||||
                        dest_value = src_value
 | 
					                        dest_value = src_value
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
@ -585,7 +579,6 @@ class EditMetadataAction(InterfaceAction):
 | 
				
			|||||||
            self.apply_pd.value += 1
 | 
					            self.apply_pd.value += 1
 | 
				
			||||||
        QTimer.singleShot(50, self.do_one_apply)
 | 
					        QTimer.singleShot(50, self.do_one_apply)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def apply_mi(self, book_id, mi):
 | 
					    def apply_mi(self, book_id, mi):
 | 
				
			||||||
        db = self.gui.current_db
 | 
					        db = self.gui.current_db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,8 @@ from calibre.gui2.dialogs.message_box import ViewLog
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Question = namedtuple('Question', 'payload callback cancel_callback '
 | 
					Question = namedtuple('Question', 'payload callback cancel_callback '
 | 
				
			||||||
        'title msg html_log log_viewer_title log_is_file det_msg '
 | 
					        'title msg html_log log_viewer_title log_is_file det_msg '
 | 
				
			||||||
        'show_copy_button checkbox_msg checkbox_checked')
 | 
					        'show_copy_button checkbox_msg checkbox_checked action_callback '
 | 
				
			||||||
 | 
					        'action_label action_icon')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProceedQuestion(QDialog):
 | 
					class ProceedQuestion(QDialog):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -51,6 +52,8 @@ class ProceedQuestion(QDialog):
 | 
				
			|||||||
        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
 | 
					        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
 | 
				
			||||||
                self.bb.ActionRole)
 | 
					                self.bb.ActionRole)
 | 
				
			||||||
        self.copy_button.clicked.connect(self.copy_to_clipboard)
 | 
					        self.copy_button.clicked.connect(self.copy_to_clipboard)
 | 
				
			||||||
 | 
					        self.action_button = self.bb.addButton('', self.bb.ActionRole)
 | 
				
			||||||
 | 
					        self.action_button.clicked.connect(self.action_clicked)
 | 
				
			||||||
        self.show_det_msg = _('Show &details')
 | 
					        self.show_det_msg = _('Show &details')
 | 
				
			||||||
        self.hide_det_msg = _('Hide &details')
 | 
					        self.hide_det_msg = _('Hide &details')
 | 
				
			||||||
        self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
 | 
					        self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
 | 
				
			||||||
@ -81,6 +84,12 @@ class ProceedQuestion(QDialog):
 | 
				
			|||||||
                    unicode(self.det_msg.toPlainText())))
 | 
					                    unicode(self.det_msg.toPlainText())))
 | 
				
			||||||
        self.copy_button.setText(_('Copied'))
 | 
					        self.copy_button.setText(_('Copied'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def action_clicked(self):
 | 
				
			||||||
 | 
					        if self.questions:
 | 
				
			||||||
 | 
					            q = self.questions[0]
 | 
				
			||||||
 | 
					            self.questions[0] = q._replace(callback=q.action_callback)
 | 
				
			||||||
 | 
					        self.accept()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def accept(self):
 | 
					    def accept(self):
 | 
				
			||||||
        if self.questions:
 | 
					        if self.questions:
 | 
				
			||||||
            payload, callback, cancel_callback = self.questions[0][:3]
 | 
					            payload, callback, cancel_callback = self.questions[0][:3]
 | 
				
			||||||
@ -131,6 +140,11 @@ class ProceedQuestion(QDialog):
 | 
				
			|||||||
            self.setWindowTitle(question.title)
 | 
					            self.setWindowTitle(question.title)
 | 
				
			||||||
            self.log_button.setVisible(bool(question.html_log))
 | 
					            self.log_button.setVisible(bool(question.html_log))
 | 
				
			||||||
            self.copy_button.setVisible(bool(question.show_copy_button))
 | 
					            self.copy_button.setVisible(bool(question.show_copy_button))
 | 
				
			||||||
 | 
					            self.action_button.setVisible(question.action_callback is not None)
 | 
				
			||||||
 | 
					            if question.action_callback is not None:
 | 
				
			||||||
 | 
					                self.action_button.setText(question.action_label or '')
 | 
				
			||||||
 | 
					                self.action_button.setIcon(
 | 
				
			||||||
 | 
					                    QIcon() if question.action_icon is None else question.action_icon)
 | 
				
			||||||
            self.det_msg.setPlainText(question.det_msg or '')
 | 
					            self.det_msg.setPlainText(question.det_msg or '')
 | 
				
			||||||
            self.det_msg.setVisible(False)
 | 
					            self.det_msg.setVisible(False)
 | 
				
			||||||
            self.det_msg_toggle.setVisible(bool(question.det_msg))
 | 
					            self.det_msg_toggle.setVisible(bool(question.det_msg))
 | 
				
			||||||
@ -146,7 +160,8 @@ class ProceedQuestion(QDialog):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __call__(self, callback, payload, html_log, log_viewer_title, title,
 | 
					    def __call__(self, callback, payload, html_log, log_viewer_title, title,
 | 
				
			||||||
            msg, det_msg='', show_copy_button=False, cancel_callback=None,
 | 
					            msg, det_msg='', show_copy_button=False, cancel_callback=None,
 | 
				
			||||||
            log_is_file=False, checkbox_msg=None, checkbox_checked=False):
 | 
					            log_is_file=False, checkbox_msg=None, checkbox_checked=False,
 | 
				
			||||||
 | 
					            action_callback=None, action_label=None, action_icon=None):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        A non modal popup that notifies the user that a background task has
 | 
					        A non modal popup that notifies the user that a background task has
 | 
				
			||||||
        been completed. This class guarantees that only a single popup is
 | 
					        been completed. This class guarantees that only a single popup is
 | 
				
			||||||
@ -171,11 +186,19 @@ class ProceedQuestion(QDialog):
 | 
				
			|||||||
                             called with both the payload and the state of the
 | 
					                             called with both the payload and the state of the
 | 
				
			||||||
                             checkbox as arguments.
 | 
					                             checkbox as arguments.
 | 
				
			||||||
        :param checkbox_checked: If True the checkbox is checked by default.
 | 
					        :param checkbox_checked: If True the checkbox is checked by default.
 | 
				
			||||||
 | 
					        :param action_callback: If not None, an extra button is added, which
 | 
				
			||||||
 | 
					                                when clicked will cause action_callback to be called
 | 
				
			||||||
 | 
					                                instead of callback. action_callback is called in
 | 
				
			||||||
 | 
					                                exactly the same way as callback.
 | 
				
			||||||
 | 
					        :param action_label: The text on the action button
 | 
				
			||||||
 | 
					        :param action_icon: The icon for the action button, must be a QIcon object or None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        question = Question(payload, callback, cancel_callback, title, msg,
 | 
					        question = Question(
 | 
				
			||||||
                html_log, log_viewer_title, log_is_file, det_msg,
 | 
					            payload, callback, cancel_callback, title, msg, html_log,
 | 
				
			||||||
                show_copy_button, checkbox_msg, checkbox_checked)
 | 
					            log_viewer_title, log_is_file, det_msg, show_copy_button,
 | 
				
			||||||
 | 
					            checkbox_msg, checkbox_checked, action_callback, action_label,
 | 
				
			||||||
 | 
					            action_icon)
 | 
				
			||||||
        self.questions.append(question)
 | 
					        self.questions.append(question)
 | 
				
			||||||
        self.show_question()
 | 
					        self.show_question()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user