Пример #1
0
    def opds(self, version=0):
        version = int(version)
        if version not in BASE_HREFS:
            raise cherrypy.HTTPError(404, "Not found")
        categories = self.categories_cache(self.get_opds_allowed_ids_for_version(version))
        category_meta = self.db.field_metadata
        cats = [(_("Newest"), _("Date"), "Onewest"), (_("Title"), _("Title"), "Otitle")]

        def getter(x):
            try:
                return category_meta[x]["name"].lower()
            except KeyError:
                return x

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ("formats", "identifiers"):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and category not in custom_fields_to_display(self.db):
                continue
            cats.append((meta["name"], meta["name"], "N" + category))
        updated = self.db.last_modified()

        cherrypy.response.headers["Last-Modified"] = self.last_modified(updated)
        cherrypy.response.headers["Content-Type"] = "application/atom+xml"

        feed = TopLevel(updated, cats, version)

        return str(feed)
Пример #2
0
 def __init__(self,
              updated,
              id_,
              items,
              offsets,
              page_url,
              up_url,
              version,
              db,
              prefix,
              title=None):
     NavFeed.__init__(self,
                      id_,
                      updated,
                      version,
                      offsets,
                      page_url,
                      up_url,
                      title=title)
     CFM = db.field_metadata
     CKEYS = [
         key for key in sorted(custom_fields_to_display(db),
                               key=lambda x: sort_key(CFM[x]['name']))
     ]
     for item in items:
         self.root.append(
             ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS,
                               prefix))
Пример #3
0
    def browse_toplevel(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
                (_('Newest'), 'newest', 'forward.png'),
                (_('All books'), 'allbooks', 'book.png'),
                ]

        def getter(x):
            return category_meta[x]['name'].lower()

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if meta['is_custom'] and category not in displayed_custom_fields:
                continue
            # get the icon files
            if category in self.icon_map:
                icon = '_'+quote(self.icon_map[category])
            elif category in category_icon_map:
                icon = category_icon_map[category]
            elif meta['is_custom']:
                icon = category_icon_map['custom:']
            elif meta['kind'] == 'user':
                icon = category_icon_map['user:'******'blank.png'

            if meta['kind'] == 'user':
                dot = category.find('.')
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta['name'][:dot-1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta['name'], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta['name'], category, icon))

        cats = [(u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">&nbsp;</a>'
                 u'<img src="{3}{src}" alt="{0}" />'
                 u'<span class="label">{0}</span>'
                 u'</li>')
                .format(xml(x, True), xml(quote(y)), xml(_('Browse books by')),
                    self.opts.url_prefix, src='/browse/icon/'+z)
                for x, y, z in cats]

        main = u'<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
                .format(_('Choose a category to browse by:'), u'\n\n'.join(cats))
        return self.browse_template('name').format(title='',
                    script='toplevel();', main=main)
Пример #4
0
    def opds(self, version=0):
        version = int(version)
        if version not in BASE_HREFS:
            raise cherrypy.HTTPError(404, 'Not found')
        categories = self.categories_cache(
                self.get_opds_allowed_ids_for_version(version))
        category_meta = self.db.field_metadata
        cats = [
                (_('Newest'), _('Date'), 'Onewest'),
                (_('Title'), _('Title'), 'Otitle'),
                ]
        def getter(x):
            return category_meta[x]['name'].lower()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                                category not in custom_fields_to_display(self.db):
                continue
            cats.append((meta['name'], meta['name'], 'N'+category))
        updated = self.db.last_modified()

        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
        cherrypy.response.headers['Content-Type'] = 'application/atom+xml'

        feed = TopLevel(updated, cats, version)

        return str(feed)
Пример #5
0
    def opds(self):
        categories = self.db.get_categories()
        category_meta = self.db.field_metadata
        cats = [
            (_('Newest'), _('Date'), 'Onewest'),
            (_('Title'), _('Title'), 'Otitle'),
        ]

        def getter(x):
            try:
                return category_meta[x]['name'].lower()
            except KeyError:
                return x

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                                category not in custom_fields_to_display(self.db):
                continue
            cats.append((meta['name'], meta['name'], 'N' + category))

        updated = self.db.last_modified()
        self.set_header('Last-Modified', self.last_modified(updated))
        self.set_header('Content-Type', 'application/atom+xml; charset=UTF-8')

        feed = TopLevel(updated, cats)

        return str(feed)
Пример #6
0
 def __init__(self, updated, id_, items, offsets, page_url, up_url, version,
         db, prefix):
     NavFeed.__init__(self, id_, updated, version, offsets, page_url, up_url)
     CFM = db.field_metadata
     CKEYS = [key for key in sorted(custom_fields_to_display(db),
                                    key=lambda x: sort_key(CFM[x]['name']))]
     for item in items:
         self.root.append(ACQUISITION_ENTRY(item, version, db, updated,
                                            CFM, CKEYS, prefix))
Пример #7
0
    def browse_template(self, sort, category=True, initial_search=''):

        if not hasattr(self, '__browse_template__') or \
                self.opts.develop:
            self.__browse_template__ = \
                P('content_server/browse/browse.html', data=True).decode('utf-8')

        ans = self.__browse_template__
        scn = 'calibre_browse_server_sort_'

        if category:
            sort_opts = [('rating', _('Average rating')), ('name', _('Name')),
                         ('popularity', _('Popularity'))]
            scn += 'category'
        else:
            scn += 'list'
            fm = self.db.field_metadata
            sort_opts, added = [], set([])
            displayed_custom_fields = custom_fields_to_display(self.db)
            for x in fm.sortable_field_keys():
                if x in ('ondevice', 'formats', 'sort'):
                    continue
                if fm.is_ignorable_field(
                        x) and x not in displayed_custom_fields:
                    continue
                if x == 'comments' or fm[x]['datatype'] == 'comments':
                    continue
                n = fm[x]['name']
                if n not in added:
                    added.add(n)
                    sort_opts.append((x, n))

        ans = ans.replace('{sort_select_label}', xml(_('Sort by') + ':'))
        ans = ans.replace('{sort_cookie_name}', scn)
        ans = ans.replace('{prefix}', self.opts.url_prefix)
        ans = ans.replace('{library}', _('library'))
        ans = ans.replace('{home}', _('home'))
        ans = ans.replace('{Search}', _('Search'))
        opts = [
            '<option %svalue="%s">%s</option>' % (
                'selected="selected" ' if k == sort else '',
                xml(k),
                xml(nl),
            ) for k, nl in sorted(
                sort_opts, key=lambda x: sort_key(operator.itemgetter(1)(x)))
            if k and nl
        ]
        ans = ans.replace('{sort_select_options}',
                          ('\n' + ' ' * 20).join(opts))
        lp = self.db.library_path
        if isbytestring(lp):
            lp = force_unicode(lp, filesystem_encoding)
        ans = ans.replace('{library_name}', xml(os.path.basename(lp)))
        ans = ans.replace('{library_path}', xml(lp, True))
        ans = ans.replace('{initial_search}',
                          xml(initial_search, attribute=True))
        return ans
Пример #8
0
    def ajax_categories(self):
        '''
        Return the list of top-level categories as a list of dictionaries. Each
        dictionary is of the form::
            {
            'name': Display Name,
            'url':URL that gives the JSON object corresponding to all entries in this category,
            'icon': URL to icon of this category,
            'is_category': False for the All Books and Newest categories, True for everything else
            }

        '''
        ans = {}
        categories = self.categories_cache()
        category_meta = self.db.field_metadata

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(self.db)


        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{'url':k, 'name':v[0], 'icon':v[1], 'is_category':True}
                for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in  [
                (_('All books'), 'allbooks', 'book.png'),
                (_('Newest'), 'newest', 'forward.png'),
                ]:
            ans.insert(0, {'name':name, 'url':url, 'icon':icon,
                'is_category':False})

        for c in ans:
            c['url'] = category_url(self.opts.url_prefix, c['url'])
            c['icon'] = icon_url(self.opts.url_prefix, c['icon'])

        return ans
Пример #9
0
    def browse_render_details(self, id_):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _('This book has been deleted')
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(mi, id_,
                    add_category_links=True)
            args['formats'] = ''
            if fmts:
                ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
                        .format(fmt, fname, id_, fmt.upper(),
                            self.opts.url_prefix) for fmt in
                        fmts]
                ofmts = ', '.join(ofmts)
                args['formats'] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + \
                    list(mi.get_all_user_metadata(False).items()):
                if m['is_custom'] and field not in displayed_custom_fields:
                    continue
                if m['datatype'] == 'comments' or field == 'comments' or (
                        m['datatype'] == 'composite' and \
                            m['display'].get('contains_html', False)):
                    val = mi.get(field, '')
                    if val and val.strip():
                        comments.append((m['name'], comments_to_html(val)))
                    continue
                if field in ('title', 'formats') or not args.get(field, False) \
                        or not m['name']:
                    continue
                if m['datatype'] == 'rating':
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                            render_rating(mi.get(field)/2.0, self.opts.url_prefix,
                                    prefix=m['name'])[0]
                else:
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                                args[field]
                fields.append((m['name'], r))

            fields.sort(key=lambda x: sort_key(x[0]))
            fields = [u'<div class="field">{0}</div>'.format(f[1]) for f in
                    fields]
            fields = u'<div class="fields">%s</div>'%('\n\n'.join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [(u'<div class="field"><strong>%s: </strong>'
                         u'<div class="comment">%s</div></div>') % (xml(c[0]),
                             c[1]) for c in comments]
            comments = u'<div class="comments">%s</div>'%('\n\n'.join(comments))

            return self.browse_details_template.format(id=id_,
                    title=xml(mi.title, True), fields=fields,
                    formats=args['formats'], comments=comments)
Пример #10
0
    def browse_template(self, sort, category=True, initial_search=''):

        if not hasattr(self, '__browse_template__') or \
                self.opts.develop:
            self.__browse_template__ = \
                P('content_server/browse/browse.html', data=True).decode('utf-8')

        ans = self.__browse_template__
        scn = 'calibre_browse_server_sort_'

        if category:
            sort_opts = [('rating', _('Average rating')), ('name',
                _('Name')), ('popularity', _('Popularity'))]
            scn += 'category'
        else:
            scn += 'list'
            fm = self.db.field_metadata
            sort_opts, added = [], set([])
            displayed_custom_fields = custom_fields_to_display(self.db)
            for x in fm.sortable_field_keys():
                if x in ('ondevice', 'formats', 'sort'):
                    continue
                if fm.is_ignorable_field(x) and x not in displayed_custom_fields:
                    continue
                if x == 'comments' or fm[x]['datatype'] == 'comments':
                    continue
                n = fm[x]['name']
                if n not in added:
                    added.add(n)
                    sort_opts.append((x, n))

        ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
        ans = ans.replace('{sort_cookie_name}', scn)
        ans = ans.replace('{prefix}', self.opts.url_prefix)
        ans = ans.replace('{library}', _('library'))
        ans = ans.replace('{home}', _('home'))
        ans = ans.replace('{Search}', _('Search'))
        ans = ans.replace('{nav}', self.nav)
        opts = ['<option %svalue="%s">%s</option>' % (
            'selected="selected" ' if k==sort else '',
            xml(k), xml(nl), ) for k, nl in
                sorted(sort_opts, key=lambda x: sort_key(operator.itemgetter(1)(x))) if k and nl]
        ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts))
        lp = self.db.library_path
        if isbytestring(lp):
            lp = force_unicode(lp, filesystem_encoding)
        ans = ans.replace('{library_name}', xml(os.path.basename(lp)))
        ans = ans.replace('{library_path}', xml(lp, True))
        ans = ans.replace('{initial_search}', xml(initial_search, attribute=True))
        return ans
Пример #11
0
    def browse_template(self, sort, category=True, initial_search=""):

        if not hasattr(self, "__browse_template__") or self.opts.develop:
            self.__browse_template__ = P("content_server/browse/browse.html", data=True).decode("utf-8")

        ans = self.__browse_template__
        scn = "calibre_browse_server_sort_"

        if category:
            sort_opts = [("rating", _("Average rating")), ("name", _("Name")), ("popularity", _("Popularity"))]
            scn += "category"
        else:
            scn += "list"
            fm = self.db.field_metadata
            sort_opts, added = [], set([])
            displayed_custom_fields = custom_fields_to_display(self.db)
            for x in fm.sortable_field_keys():
                if x in ("ondevice", "formats", "sort"):
                    continue
                if fm.is_ignorable_field(x) and x not in displayed_custom_fields:
                    continue
                if x == "comments" or fm[x]["datatype"] == "comments":
                    continue
                n = fm[x]["name"]
                if n not in added:
                    added.add(n)
                    sort_opts.append((x, n))

        ans = ans.replace("{sort_select_label}", xml(_("Sort by") + ":"))
        ans = ans.replace("{sort_cookie_name}", scn)
        ans = ans.replace("{prefix}", self.opts.url_prefix)
        ans = ans.replace("{library}", _("library"))
        ans = ans.replace("{home}", _("home"))
        ans = ans.replace("{Search}", _("Search"))
        opts = [
            '<option %svalue="%s">%s</option>' % ('selected="selected" ' if k == sort else "", xml(k), xml(nl))
            for k, nl in sorted(sort_opts, key=lambda x: sort_key(operator.itemgetter(1)(x)))
            if k and nl
        ]
        ans = ans.replace("{sort_select_options}", ("\n" + " " * 20).join(opts))
        lp = self.db.library_path
        if isbytestring(lp):
            lp = force_unicode(lp, filesystem_encoding)
        ans = ans.replace("{library_name}", xml(os.path.basename(lp)))
        ans = ans.replace("{library_path}", xml(lp, True))
        ans = ans.replace("{initial_search}", xml(initial_search, attribute=True))
        return ans
Пример #12
0
    def mobile(self, start='1', num='25', sort='date', search='',
                _=None, order='descending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, 'start: %s is not an integer'%start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)
        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')
        ids = self.db.search_getting_ids(search.strip(), self.search_restriction)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == 'ascending'))

        CFM = self.db.field_metadata
        CKEYS = [key for key in sorted(custom_fields_to_display(self.db),
                                       key=lambda x:sort_key(CFM[x]['name']))]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start-1):(start-1)+num]:
            book = {'formats':record[FM['formats']], 'size':record[FM['size']]}
            if not book['formats']:
                book['formats'] = ''
            if not book['size']:
                book['size'] = 0
            book['size'] = human_readable(book['size'])

            aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
            aut_is = CFM['authors']['is_multiple']
            authors = aut_is['list_to_ui'].join([i.replace('|', ',') for i in aus.split(',')])
            book['authors'] = authors
            book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
            book['series'] = record[FM['series']]
            book['tags'] = format_tag_string(record[FM['tags']], ',',
                                             no_tag_count=True)
            book['title'] = record[FM['title']]
            for x in ('timestamp', 'pubdate'):
                book[x] = strftime('%d %b, %Y', record[FM[x]])
            book['id'] = record[FM['id']]
            books.append(book)
            for key in CKEYS:
                def concat(name, val):
                    return '%s:#:%s'%(name, unicode(val))
                mi = self.db.get_metadata(record[CFM['id']['rec_index']], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                if datatype == 'text' and CFM[key]['is_multiple']:
                    book[key] = concat(name,
                                       format_tag_string(val,
                                           CFM[key]['is_multiple']['ui_to_list'],
                                           no_tag_count=True,
                                           joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)

        url_base = "/mobile?search=" + search+";order="+order+";sort="+sort+";num="+str(num)

        raw = html.tostring(build_index(books, num, search, sort, order,
                             start, len(ids), url_base, CKEYS,
                             self.opts.url_prefix),
                             encoding='utf-8',
                             pretty_print=True)
        # tostring's include_meta_content_type is broken
        raw = raw.replace('<head>', '<head>\n'
                '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">')
        return raw
Пример #13
0
    def browse_toplevel(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
            (_('Newest'), 'newest', 'forward.png'),
            (_('All books'), 'allbooks', 'book.png'),
            (_('Random book'), 'randombook', 'random.png'),
        ]
        virt_libs = self.db.prefs.get('virtual_libraries', {})
        if virt_libs:
            cats.append((_('Virtual Libs.'), 'virt_libs', 'lt.png'))

        def getter(x):
            try:
                return category_meta[x]['name'].lower()
            except KeyError:
                return x

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if self.db.field_metadata.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            # get the icon files
            main_cat = (category.partition('.')[0]) if hasattr(
                category, 'partition') else category
            if main_cat in self.icon_map:
                icon = '_' + quote(self.icon_map[main_cat])
            elif category in category_icon_map:
                icon = category_icon_map[category]
            elif meta['is_custom']:
                icon = category_icon_map['custom:']
            elif meta['kind'] == 'user':
                icon = category_icon_map['user:'******'blank.png'

            if meta['kind'] == 'user':
                dot = category.find('.')
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta['name'][:dot - 1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta['name'], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta['name'], category, icon))

        cats = [(
            u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">&nbsp;</a>'
            u'<img src="{3}{src}" alt="{0}" />'
            u'<span class="label">{0}</span>'
            u'</li>').format(xml(x, True),
                             xml(quote(y)),
                             xml(_('Browse books by')),
                             self.opts.url_prefix,
                             src='/browse/icon/' + z) for x, y, z in cats]

        main = u'<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
                .format(_('Choose a category to browse by:'), u'\n\n'.join(cats))
        return self.browse_template('name').format(title='',
                                                   script='toplevel();',
                                                   main=main)
Пример #14
0
    def browse_render_details(self,
                              id_,
                              add_random_button=False,
                              add_title=False):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _('This book has been deleted')
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(
                mi, id_, add_category_links=True)
            args['fmt'] = fmt
            if fmt:
                args['get_url'] = xml(
                    self.opts.url_prefix + '/get/%s/%s_%d.%s' %
                    (fmt, fname, id_, fmt), True)
            else:
                args['get_url'] = 'javascript:alert(\'%s\')' % xml(
                    _('This book has no available formats to view'), True)
            args['formats'] = ''
            if fmts:
                ofmts = [
                    u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'.
                    format(xfmt, fname, id_, xfmt.upper(),
                           self.opts.url_prefix) for xfmt in fmts
                ]
                ofmts = ', '.join(ofmts)
                args['formats'] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + \
                    list(mi.get_all_user_metadata(False).items()):
                if self.db.field_metadata.is_ignorable_field(field) and \
                                field not in displayed_custom_fields:
                    continue
                if m['datatype'] == 'comments' or field == 'comments' or (
                        m['datatype'] == 'composite'
                        and m['display'].get('contains_html', False)):
                    val = mi.get(field, '')
                    if val and val.strip():
                        comments.append((m['name'], comments_to_html(val)))
                    continue
                if field in ('title', 'formats') or not args.get(field, False) \
                        or not m['name']:
                    continue
                if field == 'identifiers':
                    urls = urls_from_identifiers(mi.get(field, {}))
                    links = [
                        u'<a class="details_category_link" target="_new" href="%s" title="%s:%s">%s</a>'
                        % (url, id_typ, id_val, name)
                        for name, id_typ, id_val, url in urls
                    ]
                    links = u', '.join(links)
                    if links:
                        fields.append(
                            (field, m['name'],
                             u'<strong>%s: </strong>%s' % (_('Ids'), links)))
                        continue

                if m['datatype'] == 'rating':
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                            render_rating(mi.get(field)/2.0, self.opts.url_prefix,
                                    prefix=m['name'])[0]
                else:
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                                args[field]
                fields.append((field, m['name'], r))

            def fsort(x):
                num = {'authors': 0, 'series': 1, 'tags': 2}.get(x[0], 100)
                return (num, sort_key(x[-1]))

            fields.sort(key=fsort)
            if add_title:
                fields.insert(0,
                              ('title', 'Title', u'<strong>%s: </strong>%s' %
                               (xml(_('Title')), xml(mi.title))))
            fields = [
                u'<div class="field">{0}</div>'.format(f[-1]) for f in fields
            ]
            fields = u'<div class="fields">%s</div>' % ('\n\n'.join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [
                (u'<div class="field"><strong>%s: </strong>'
                 u'<div class="comment">%s</div></div>') % (xml(c[0]), c[1])
                for c in comments
            ]
            comments = u'<div class="comments">%s</div>' % (
                '\n\n'.join(comments))
            random = ''
            if add_random_button:
                href = '%s/browse/random?v=%s' % (self.opts.url_prefix,
                                                  time.time())
                random = '<a href="%s" id="random_button" title="%s">%s</a>' % (
                    xml(href, True), xml(_('Choose another random book'),
                                         True), xml(_('Another random book')))

            return self.browse_details_template.format(id=id_,
                                                       title=xml(
                                                           mi.title, True),
                                                       fields=fields,
                                                       get_url=args['get_url'],
                                                       fmt=args['fmt'],
                                                       formats=args['formats'],
                                                       comments=comments,
                                                       random=random)
Пример #15
0
    def mobile(self, start="1", num="25", sort="date", search="", _=None, order="descending"):
        """
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        """
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, "start: %s is not an integer" % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, "num: %s is not an integer" % num)
        if not search:
            search = ""
        if isbytestring(search):
            search = search.decode("UTF-8")
        ids = self.search_for_books(search)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM["id"]] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == "ascending"))

        CFM = self.db.field_metadata
        CKEYS = [key for key in sorted(custom_fields_to_display(self.db), key=lambda x: sort_key(CFM[x]["name"]))]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start - 1) : (start - 1) + num]:
            book = {"formats": record[FM["formats"]], "size": record[FM["size"]]}
            if not book["formats"]:
                book["formats"] = ""
            if not book["size"]:
                book["size"] = 0
            book["size"] = human_readable(book["size"])

            aus = record[FM["authors"]] if record[FM["authors"]] else __builtin__._("Unknown")
            aut_is = CFM["authors"]["is_multiple"]
            authors = aut_is["list_to_ui"].join([i.replace("|", ",") for i in aus.split(",")])
            book["authors"] = authors
            book["series_index"] = fmt_sidx(float(record[FM["series_index"]]))
            book["series"] = record[FM["series"]]
            book["tags"] = format_tag_string(record[FM["tags"]], ",", no_tag_count=True)
            book["title"] = record[FM["title"]]
            for x in ("timestamp", "pubdate"):
                book[x] = strftime("%d %b, %Y", as_local_time(record[FM[x]]))
            book["id"] = record[FM["id"]]
            books.append(book)
            for key in CKEYS:

                def concat(name, val):
                    return "%s:#:%s" % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM["id"]["rec_index"]], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]["datatype"]
                if datatype in ["comments"]:
                    continue
                if datatype == "text" and CFM[key]["is_multiple"]:
                    book[key] = concat(
                        name,
                        format_tag_string(
                            val,
                            CFM[key]["is_multiple"]["ui_to_list"],
                            no_tag_count=True,
                            joinval=CFM[key]["is_multiple"]["list_to_ui"],
                        ),
                    )
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers["Content-Type"] = "text/html; charset=utf-8"
        cherrypy.response.headers["Last-Modified"] = self.last_modified(updated)

        q = {
            b"search": search.encode("utf-8"),
            b"order": order.encode("utf-8"),
            b"sort": sort.encode("utf-8"),
            b"num": str(num).encode("utf-8"),
        }
        url_base = "/mobile?" + urlencode(q)
        ua = cherrypy.request.headers.get("User-Agent", "").strip()
        have_kobo_browser = self.is_kobo_browser(ua)

        raw = html.tostring(
            build_index(
                books,
                num,
                search,
                sort,
                order,
                start,
                len(ids),
                url_base,
                CKEYS,
                self.opts.url_prefix,
                have_kobo_browser=have_kobo_browser,
            ),
            encoding="utf-8",
            pretty_print=True,
        )
        # tostring's include_meta_content_type is broken
        raw = raw.replace("<head>", "<head>\n" '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">')
        return raw
Пример #16
0
    def browse_nav(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
                (_('Newest'), 'newest', 'whatshot'),
                (_('All books'), 'allbooks', 'library_books'),
                (_('Random book'), 'randombook', 'shuffle'),
                ]
        virt_libs = self.db.prefs.get('virtual_libraries', {})
        if virt_libs:
            cats.append((_('Virtual Libs.'), 'virt_libs', 'graphic_eq'))

        def getter(x):
            try:
                return category_meta[x]['name'].lower()
            except KeyError:
                return x

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if self.db.field_metadata.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue

            # get the icon files
            main_cat = (category.partition('.')[0]) if hasattr(category,
                                                    'partition') else category
            if main_cat in self.icon_map:
                icon = self.icon_map[main_cat]
            elif category in category_icon_map:
                icon = category_icon_map[category]
            elif meta['is_custom']:
                icon = category_icon_map['custom:']
            elif meta['kind'] == 'user':
                icon = category_icon_map['user:'******'check_box_outline_blank'

            if meta['kind'] == 'user':
                dot = category.find('.')
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta['name'][:dot-1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta['name'], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta['name'], category, icon))

        cats = [(u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">'
                 u'<i class="material-icons">{icon}</i>'
                 u'<span>{0}</span></a>'
                 u'</li>')
                .format(xml(name, True), xml(quote(cat)), xml(_('Browse books by')),
                    self.opts.url_prefix, icon=icon)
                for name, cat, icon in cats]

        return '\n'.join(cats)
Пример #17
0
    def browse_render_details(self, id_, add_random_button=False):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _('This book has been deleted')
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(mi, id_,
                    add_category_links=True)
            args['fmt'] = fmt
            if fmt:
                args['get_url'] = xml(self.opts.url_prefix + '/get/%s/%s_%d.%s'%(
                    fmt, fname, id_, fmt), True)
            else:
                args['get_url'] = ''
            args['formats'] = ''
            if fmts:
                ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'
                        .format(xfmt, fname, id_, xfmt.upper(),
                            self.opts.url_prefix) for xfmt in fmts]
                ofmts = ', '.join(ofmts)
                args['formats'] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + \
                    list(mi.get_all_user_metadata(False).items()):
                if self.db.field_metadata.is_ignorable_field(field) and \
                                field not in displayed_custom_fields:
                    continue
                if m['datatype'] == 'comments' or field == 'comments' or (
                        m['datatype'] == 'composite' and
                            m['display'].get('contains_html', False)):
                    val = mi.get(field, '')
                    if val and val.strip():
                        comments.append((m['name'], comments_to_html(val)))
                    continue
                if field in ('title', 'formats') or not args.get(field, False) \
                        or not m['name']:
                    continue
                if field == 'identifiers':
                    urls = urls_from_identifiers(mi.get(field, {}))
                    links = [u'<a class="details_category_link" target="_new" href="%s" title="%s:%s">%s</a>' % (url, id_typ, id_val, name)
                            for name, id_typ, id_val, url in urls]
                    links = u', '.join(links)
                    if links:
                        fields.append((m['name'], u'<strong>%s: </strong>%s'%(
                            _('Ids'), links)))
                        continue

                if m['datatype'] == 'rating':
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                            render_rating(mi.get(field)/2.0, self.opts.url_prefix,
                                    prefix=m['name'])[0]
                else:
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                                args[field]
                fields.append((m['name'], r))

            fields.sort(key=lambda x: sort_key(x[0]))
            fields = [u'<div class="field">{0}</div>'.format(f[1]) for f in
                    fields]
            fields = u'<div class="fields">%s</div>'%('\n\n'.join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [(u'<div class="field"><strong>%s: </strong>'
                         u'<div class="comment">%s</div></div>') % (xml(c[0]),
                             c[1]) for c in comments]
            comments = u'<div class="comments">%s</div>'%('\n\n'.join(comments))
            random = ''
            if add_random_button:
                href = '%s/browse/random?v=%s'%(
                    self.opts.url_prefix, time.time())
                random = '<a href="%s" id="random_button" title="%s">%s</a>' % (
                    xml(href, True), xml(_('Choose another random book'), True),
                    xml(_('Another random book')))

            return self.browse_details_template.format(
                id=id_, title=xml(mi.title, True), fields=fields,
                get_url=args['get_url'], fmt=args['fmt'],
                formats=args['formats'], comments=comments, random=random)
Пример #18
0
    def browse_render_details(self, id_):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _('This book has been deleted')
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(
                mi, id_, add_category_links=True)
            args['formats'] = ''
            if fmts:
                ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
                        .format(fmt, fname, id_, fmt.upper(),
                            self.opts.url_prefix) for fmt in
                        fmts]
                ofmts = ', '.join(ofmts)
                args['formats'] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + \
                    list(mi.get_all_user_metadata(False).items()):
                if m['is_custom'] and field not in displayed_custom_fields:
                    continue
                if m['datatype'] == 'comments' or field == 'comments' or (
                        m['datatype'] == 'composite' and \
                            m['display'].get('contains_html', False)):
                    val = mi.get(field, '')
                    if val and val.strip():
                        comments.append((m['name'], comments_to_html(val)))
                    continue
                if field in ('title', 'formats') or not args.get(field, False) \
                        or not m['name']:
                    continue
                if m['datatype'] == 'rating':
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                            render_rating(mi.get(field)/2.0, self.opts.url_prefix,
                                    prefix=m['name'])[0]
                else:
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                                args[field]
                fields.append((m['name'], r))

            fields.sort(key=lambda x: sort_key(x[0]))
            fields = [
                u'<div class="field">{0}</div>'.format(f[1]) for f in fields
            ]
            fields = u'<div class="fields">%s</div>' % ('\n\n'.join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [
                (u'<div class="field"><strong>%s: </strong>'
                 u'<div class="comment">%s</div></div>') % (xml(c[0]), c[1])
                for c in comments
            ]
            comments = u'<div class="comments">%s</div>' % (
                '\n\n'.join(comments))

            return self.browse_details_template.format(id=id_,
                                                       title=xml(
                                                           mi.title, True),
                                                       fields=fields,
                                                       formats=args['formats'],
                                                       comments=comments)
Пример #19
0
    def mobile(self,
               start='1',
               num='25',
               sort='date',
               search='',
               _=None,
               order='descending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400,
                                     'start: %s is not an integer' % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer' % num)
        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')
        ids = self.db.search_getting_ids(search.strip(),
                                         self.search_restriction)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == 'ascending'))

        CFM = self.db.field_metadata
        CKEYS = [
            key for key in sorted(custom_fields_to_display(self.db),
                                  key=lambda x: sort_key(CFM[x]['name']))
        ]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start - 1):(start - 1) + num]:
            book = {
                'formats': record[FM['formats']],
                'size': record[FM['size']]
            }
            if not book['formats']:
                book['formats'] = ''
            if not book['size']:
                book['size'] = 0
            book['size'] = human_readable(book['size'])

            aus = record[FM['authors']] if record[
                FM['authors']] else __builtin__._('Unknown')
            aut_is = CFM['authors']['is_multiple']
            authors = aut_is['list_to_ui'].join(
                [i.replace('|', ',') for i in aus.split(',')])
            book['authors'] = authors
            book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
            book['series'] = record[FM['series']]
            book['tags'] = format_tag_string(record[FM['tags']],
                                             ',',
                                             no_tag_count=True)
            book['title'] = record[FM['title']]
            for x in ('timestamp', 'pubdate'):
                book[x] = strftime('%d %b, %Y', record[FM[x]])
            book['id'] = record[FM['id']]
            books.append(book)
            for key in CKEYS:

                def concat(name, val):
                    return '%s:#:%s' % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM['id']['rec_index']],
                                          index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                if datatype == 'text' and CFM[key]['is_multiple']:
                    book[key] = concat(
                        name,
                        format_tag_string(
                            val,
                            CFM[key]['is_multiple']['ui_to_list'],
                            no_tag_count=True,
                            joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(
            updated)

        url_base = "/mobile?search=" + search + ";order=" + order + ";sort=" + sort + ";num=" + str(
            num)

        raw = html.tostring(build_index(books, num, search, sort, order, start,
                                        len(ids), url_base, CKEYS,
                                        self.opts.url_prefix),
                            encoding='utf-8',
                            pretty_print=True)
        # tostring's include_meta_content_type is broken
        raw = raw.replace(
            '<head>', '<head>\n'
            '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
        )
        return raw
Пример #20
0
    def xml(self, start='0', num='50', sort=None, search=None,
                _=None, order='ascending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, 'start: %s is not an integer'%start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)

        order = order.lower().strip() == 'ascending'

        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')

        ids = self.search_for_books(search)

        FM = self.db.FIELD_MAP

        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, order)

        books = []

        def serialize(x):
            if isinstance(x, unicode):
                return x
            if isbytestring(x):
                return x.decode(preferred_encoding, 'replace')
            return unicode(x)

        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        for record in items[start:start+num]:
            kwargs = {}
            aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
            authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
            kwargs['authors'] = authors

            kwargs['series_index'] = \
                fmt_sidx(float(record[FM['series_index']]))

            for x in ('timestamp', 'pubdate'):
                kwargs[x] = strftime('%Y/%m/%d %H:%M:%S', record[FM[x]])

            for x in ('id', 'title', 'sort', 'author_sort', 'rating', 'size'):
                kwargs[x] = serialize(record[FM[x]])

            for x in ('formats', 'series', 'tags', 'publisher',
                    'comments', 'identifiers'):
                y = record[FM[x]]
                if x == 'tags':
                    y = format_tag_string(y, ',', ignore_max=True)
                kwargs[x] = serialize(y) if y else ''

            isbn = self.db.isbn(record[FM['id']], index_is_id=True)
            kwargs['isbn'] = serialize(isbn if isbn else '')

            kwargs['safe_title'] = ascii_filename(kwargs['title'])

            c = kwargs.pop('comments')

            CFM = self.db.field_metadata
            CKEYS = [key for key in sorted(custom_fields_to_display(self.db),
                                           key=lambda x: sort_key(CFM[x]['name']))]
            custcols = []
            for key in CKEYS:
                def concat(name, val):
                    return '%s:#:%s'%(name, unicode(val))
                mi = self.db.get_metadata(record[CFM['id']['rec_index']], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                k = str('CF_'+key[1:])
                name = CFM[key]['name']
                custcols.append(k)
                if datatype == 'text' and CFM[key]['is_multiple']:
                    kwargs[k] = \
                        concat('#T#'+name,
                               format_tag_string(val,
                                   CFM[key]['is_multiple']['ui_to_list'],
                                   ignore_max=True,
                                   joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    kwargs[k] = concat(name, val)
            kwargs['custcols'] = ','.join(custcols)
            books.append(E.book(c, **kwargs))

        updated = self.db.last_modified()
        kwargs = dict(
                start=str(start),
                updated=updated.strftime('%Y-%m-%dT%H:%M:%S+00:00'),
                total=str(len(ids)),
                num=str(len(books)))
        ans = E.library(*books, **kwargs)

        cherrypy.response.headers['Content-Type'] = 'text/xml'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)

        return etree.tostring(ans, encoding='utf-8', pretty_print=True,
                xml_declaration=True)
Пример #21
0
    def browse_toplevel(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
            (_("Newest"), "newest", "forward.png"),
            (_("All books"), "allbooks", "book.png"),
            (_("Random book"), "randombook", "random.png"),
        ]
        virt_libs = self.db.prefs.get("virtual_libraries", {})
        if virt_libs:
            cats.append((_("Virtual Libs."), "virt_libs", "lt.png"))

        def getter(x):
            try:
                return category_meta[x]["name"].lower()
            except KeyError:
                return x

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ("formats", "identifiers"):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if self.db.field_metadata.is_ignorable_field(category) and category not in displayed_custom_fields:
                continue
            # get the icon files
            main_cat = (category.partition(".")[0]) if hasattr(category, "partition") else category
            if main_cat in self.icon_map:
                icon = "_" + quote(self.icon_map[main_cat])
            elif category in category_icon_map:
                icon = category_icon_map[category]
            elif meta["is_custom"]:
                icon = category_icon_map["custom:"]
            elif meta["kind"] == "user":
                icon = category_icon_map["user:"******"blank.png"

            if meta["kind"] == "user":
                dot = category.find(".")
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta["name"][: dot - 1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta["name"], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta["name"], category, icon))

        cats = [
            (
                u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">&nbsp;</a>'
                u'<img src="{3}{src}" alt="{0}" />'
                u'<span class="label">{0}</span>'
                u"</li>"
            ).format(
                xml(x, True), xml(quote(y)), xml(_("Browse books by")), self.opts.url_prefix, src="/browse/icon/" + z
            )
            for x, y, z in cats
        ]

        main = u'<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'.format(
            _("Choose a category to browse by:"), u"\n\n".join(cats)
        )
        return self.browse_template("name").format(title="", script="toplevel();", main=main)
Пример #22
0
    def browse_render_details(self, id_, add_random_button=False, add_title=False):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _("This book has been deleted")
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(mi, id_, add_category_links=True)
            args["fmt"] = fmt
            if fmt:
                args["get_url"] = xml(self.opts.url_prefix + "/get/%s/%s_%d.%s" % (fmt, fname, id_, fmt), True)
            else:
                args["get_url"] = "javascript:alert('%s')" % xml(_("This book has no available formats to view"), True)
            args["formats"] = ""
            if fmts:
                ofmts = [
                    u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'.format(
                        xfmt, fname, id_, xfmt.upper(), self.opts.url_prefix
                    )
                    for xfmt in fmts
                ]
                ofmts = ", ".join(ofmts)
                args["formats"] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + list(
                mi.get_all_user_metadata(False).items()
            ):
                if self.db.field_metadata.is_ignorable_field(field) and field not in displayed_custom_fields:
                    continue
                if (
                    m["datatype"] == "comments"
                    or field == "comments"
                    or (m["datatype"] == "composite" and m["display"].get("contains_html", False))
                ):
                    val = mi.get(field, "")
                    if val and val.strip():
                        comments.append((m["name"], comments_to_html(val)))
                    continue
                if field in ("title", "formats") or not args.get(field, False) or not m["name"]:
                    continue
                if field == "identifiers":
                    urls = urls_from_identifiers(mi.get(field, {}))
                    links = [
                        u'<a class="details_category_link" target="_new" href="%s" title="%s:%s">%s</a>'
                        % (url, id_typ, id_val, name)
                        for name, id_typ, id_val, url in urls
                    ]
                    links = u", ".join(links)
                    if links:
                        fields.append((field, m["name"], u"<strong>%s: </strong>%s" % (_("Ids"), links)))
                        continue

                if m["datatype"] == "rating":
                    r = (
                        u"<strong>%s: </strong>" % xml(m["name"])
                        + render_rating(mi.get(field) / 2.0, self.opts.url_prefix, prefix=m["name"])[0]
                    )
                else:
                    r = u"<strong>%s: </strong>" % xml(m["name"]) + args[field]
                fields.append((field, m["name"], r))

            def fsort(x):
                num = {"authors": 0, "series": 1, "tags": 2}.get(x[0], 100)
                return (num, sort_key(x[-1]))

            fields.sort(key=fsort)
            if add_title:
                fields.insert(0, ("title", "Title", u"<strong>%s: </strong>%s" % (xml(_("Title")), xml(mi.title))))
            fields = [u'<div class="field">{0}</div>'.format(f[-1]) for f in fields]
            fields = u'<div class="fields">%s</div>' % ("\n\n".join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [
                (u'<div class="field"><strong>%s: </strong>' u'<div class="comment">%s</div></div>') % (xml(c[0]), c[1])
                for c in comments
            ]
            comments = u'<div class="comments">%s</div>' % ("\n\n".join(comments))
            random = ""
            if add_random_button:
                href = "%s/browse/random?v=%s" % (self.opts.url_prefix, time.time())
                random = '<a href="%s" id="random_button" title="%s">%s</a>' % (
                    xml(href, True),
                    xml(_("Choose another random book"), True),
                    xml(_("Another random book")),
                )

            return self.browse_details_template.format(
                id=id_,
                title=xml(mi.title, True),
                fields=fields,
                get_url=args["get_url"],
                fmt=args["fmt"],
                formats=args["formats"],
                comments=comments,
                random=random,
            )
Пример #23
0
    def xml(self,
            start='0',
            num='50',
            sort=None,
            search=None,
            _=None,
            order='ascending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400,
                                     'start: %s is not an integer' % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer' % num)

        order = order.lower().strip() == 'ascending'

        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')

        ids = self.search_for_books(search)

        FM = self.db.FIELD_MAP

        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, order)

        books = []

        def serialize(x):
            if isinstance(x, unicode):
                return x
            if isbytestring(x):
                return x.decode(preferred_encoding, 'replace')
            return unicode(x)

        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        for record in items[start:start + num]:
            kwargs = {}
            aus = record[FM['authors']] if record[
                FM['authors']] else __builtin__._('Unknown')
            authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
            kwargs['authors'] = authors

            kwargs['series_index'] = \
                fmt_sidx(float(record[FM['series_index']]))

            for x in ('timestamp', 'pubdate'):
                kwargs[x] = strftime('%Y/%m/%d %H:%M:%S', record[FM[x]])

            for x in ('id', 'title', 'sort', 'author_sort', 'rating', 'size'):
                kwargs[x] = serialize(record[FM[x]])

            for x in ('formats', 'series', 'tags', 'publisher', 'comments',
                      'identifiers'):
                y = record[FM[x]]
                if x == 'tags':
                    y = format_tag_string(y, ',', ignore_max=True)
                kwargs[x] = serialize(y) if y else ''

            isbn = self.db.isbn(record[FM['id']], index_is_id=True)
            kwargs['isbn'] = serialize(isbn if isbn else '')

            kwargs['safe_title'] = ascii_filename(kwargs['title'])

            c = kwargs.pop('comments')

            CFM = self.db.field_metadata
            CKEYS = [
                key for key in sorted(custom_fields_to_display(self.db),
                                      key=lambda x: sort_key(CFM[x]['name']))
            ]
            custcols = []
            for key in CKEYS:

                def concat(name, val):
                    return '%s:#:%s' % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM['id']['rec_index']],
                                          index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                k = str('CF_' + key[1:])
                name = CFM[key]['name']
                custcols.append(k)
                if datatype == 'text' and CFM[key]['is_multiple']:
                    kwargs[k] = \
                        concat('#T#'+name,
                               format_tag_string(val,
                                   CFM[key]['is_multiple']['ui_to_list'],
                                   ignore_max=True,
                                   joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    kwargs[k] = concat(name, val)
            kwargs['custcols'] = ','.join(custcols)
            books.append(E.book(c, **kwargs))

        updated = self.db.last_modified()
        kwargs = dict(start=str(start),
                      updated=updated.strftime('%Y-%m-%dT%H:%M:%S+00:00'),
                      total=str(len(ids)),
                      num=str(len(books)))
        ans = E.library(*books, **kwargs)

        cherrypy.response.headers['Content-Type'] = 'text/xml'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(
            updated)

        return etree.tostring(ans,
                              encoding='utf-8',
                              pretty_print=True,
                              xml_declaration=True)